feat(appkit): reference agent-app, dev-playground chat UI, docs, and template#306
Open
MarioCadenas wants to merge 19 commits intoagent/v2/5-fromplugin-runagentfrom
Open
feat(appkit): reference agent-app, dev-playground chat UI, docs, and template#306MarioCadenas wants to merge 19 commits intoagent/v2/5-fromplugin-runagentfrom
MarioCadenas wants to merge 19 commits intoagent/v2/5-fromplugin-runagentfrom
Conversation
This was referenced Apr 21, 2026
162e970 to
29e3534
Compare
57cc1e4 to
4a441d2
Compare
7 tasks
29e3534 to
b462716
Compare
d16cdd5 to
e81d8bb
Compare
539487e to
dac73b5
Compare
e81d8bb to
829ad13
Compare
dac73b5 to
624f2a0
Compare
3386200 to
263f587
Compare
caa6fcf to
2f53d52
Compare
8f65c78 to
15dd8a1
Compare
2f53d52 to
4518195
Compare
15dd8a1 to
ebc7c11
Compare
4518195 to
f3e5fce
Compare
ebc7c11 to
873e70d
Compare
f3e5fce to
7f71a62
Compare
873e70d to
61ee6a7
Compare
ff610f8 to
4dbcbe3
Compare
…template
Final layer of the agents feature stack. Everything needed to
exercise, demonstrate, and learn the feature.
`apps/agent-app/` — a standalone app purpose-built around the agents
feature. Ships with:
- `server.ts` — full example of code-defined agents via `fromPlugin`:
```ts
const support = createAgent({
instructions: "…",
tools: {
...fromPlugin(analytics),
...fromPlugin(files),
get_weather,
"mcp.vector-search": mcpServer("vector-search", "https://…"),
},
});
await createApp({
plugins: [server({ port }), analytics(), files(), agents({ agents: { support } })],
});
```
- `config/agents/assistant.md` — markdown-driven agent alongside the
code-defined one, showing the asymmetric auto-inherit default.
- Vite + React 19 + TailwindCSS frontend with a chat UI.
- Databricks deployment config (`databricks.yml`, `app.yaml`) and
deploy scripts.
`apps/dev-playground/client/src/routes/agent.route.tsx` — chat UI with
inline autocomplete (hits the `autocomplete` markdown agent) and a
full threaded conversation panel (hits the default agent).
`apps/dev-playground/server/index.ts` — adds a code-defined `helper`
agent using `fromPlugin(analytics)` alongside the markdown-driven
`autocomplete` agent in `config/agents/`. Exercises the mixed-style
setup (markdown + code) against the same plugin list.
`apps/dev-playground/config/agents/*.md` — both agents defined with
valid YAML frontmatter.
`docs/docs/plugins/agents.md` — progressive five-level guide:
1. Drop a markdown file → it just works.
2. Scope tools via `toolkits:` / `tools:` frontmatter.
3. Code-defined agents with `fromPlugin()`.
4. Sub-agents.
5. Standalone `runAgent()` (no `createApp` or HTTP).
Plus a configuration reference, runtime API reference, and frontmatter
schema table.
`docs/docs/api/appkit/` — regenerated typedoc for the new public
surface (fromPlugin, runAgent, AgentDefinition, AgentsPluginConfig,
ToolkitEntry, ToolkitOptions, all adapter types, and the agents
plugin factory).
`template/appkit.plugins.json` — adds the `agent` plugin entry so
`npx @databricks/appkit init --features agent` scaffolds the plugin
correctly.
- Full appkit vitest suite: 1311 tests passing
- Typecheck clean across all 8 workspace projects
- `pnpm docs:build` clean (no broken links)
- `pnpm --filter=@databricks/appkit build:package` clean, publint
clean
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Documents the new `mcp` configuration block and the rules it enforces:
same-origin-only by default, explicit `trustedHosts` for external MCP
servers, plaintext `http://` refused outside localhost-in-dev, and
DNS-level blocking of private / link-local IP ranges (covers cloud
metadata services). See PR #302 for the policy implementation and
PR #304 for the `AgentsPluginConfig.mcp` wiring.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
- `docs/docs/plugins/agents.md`: new "SQL agent tools" subsection
covering `analytics.query` readOnly enforcement, `lakebase.query`
opt-in via `exposeAsAgentTool`, and the approval flow. New
"Human-in-the-loop approval for destructive tools" subsection
documents the config, SSE event shape, and `POST /chat/approve`
contract.
- `apps/agent-app`: approval-card component rendered inline in the
chat stream whenever an `appkit.approval_pending` event arrives.
Destructive badge + Approve/Deny buttons POST to
`/api/agent/approve` with the carried `streamId`/`approvalId`.
- `apps/dev-playground/client`: matching approval-card on the agent
route, using the existing appkit-ui `Button` component and
Tailwind utility classes.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Updates `docs/docs/plugins/agents.md` to document the new
two-key auto-inherit model introduced in PR #302 (per-tool
`autoInheritable` flag) and PR #304 (safe-by-default
`autoInheritTools: { file: false, code: false }`). Adds an
"Auto-inherit posture" subsection explaining that the developer
must opt into `autoInheritTools` AND the plugin author must mark
each tool `autoInheritable: true` for a tool to spread without
explicit wiring.
Includes a table documenting the `autoInheritable` marking on each
core plugin tool, plus an example of the setup-time audit log so
operators can see exactly what's inherited vs. skipped.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
- **Reference app no longer ships hardcoded dogfood URLs.** The three
`https://e2-dogfood.staging.cloud.databricks.com/...` and
`https://mario-mcp-hello-*.staging.aws.databricksapps.com/...` MCP
URLs in `apps/agent-app/server.ts` are replaced with optional
env-driven `VECTOR_SEARCH_MCP_URL` / `CUSTOM_MCP_URL` config. When
set, their hostnames are auto-added to `agents({ mcp: { trustedHosts
} })`. `.env.example` uses placeholder values the reader can replace
instead of another team's workspace.
- **`appkit.agent` → `appkit.agents` in the reference app.** The
prior `appkit.agent as { list, getDefault }` cast papered over the
plugin-name mismatch fixed in PR #304. The runtime key now matches
the docs, the manifest, and the factory name; the cast is gone.
- **Auto-inherit opt-in added to the reference config.** Since the
defaults flipped to `{ file: false, code: false }` (PR #304, S-3),
the reference now explicitly enables `autoInheritTools: { file:
true }` so the markdown agents that ship alongside the code-defined
one still pick up the analytics / files read-only tools. This is the
pattern a real deployment should follow — opt in deliberately.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
- `apps/dev-playground/config/agents/autocomplete.md` sets
`ephemeral: true`. Each debounced autocomplete keystroke no longer
leaves an orphan thread in `InMemoryThreadStore` — the server now
deletes the thread in the stream's `finally` (PR #304). Closes R1
from the MVP re-review.
- `docs/docs/plugins/agents.md` documents the new `ephemeral`
frontmatter key alongside the other AgentDefinition knobs.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Documents the MVP resource caps landed in PR #304: the static
request-body caps (enforced by the Zod schemas) and the three
configurable runtime limits (`maxConcurrentStreamsPerUser`,
`maxToolCalls`, `maxSubAgentDepth`). Includes the config-block
shape in the main reference and a new "Resource limits" subsection
under the Configuration section explaining the intent and per-user
semantics of each cap.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
The agents plugin's manifest `name` is `agents` (plural), so routes mount
at `/api/agents/*` and its client config is keyed as `agents` — but three
call sites still referenced the old singular `agent`:
- apps/agent-app/src/App.tsx: /api/agent/{info,chat,approve} returned an
Express 404 HTML page, which the client then tried to JSON.parse,
producing "Unexpected token '<', <!DOCTYPE ...". Swap to /api/agents/*.
- apps/dev-playground/client/src/routes/agent.route.tsx: same three
paths, plus getPluginClientConfig("agent") returned {} so
hasAutocomplete was false and the autocomplete hook short-circuited
before ever firing a request. Swap the lookup key to "agents".
- template/appkit.plugins.json: the scaffolded plugin descriptor still
used the singular name/key, which would have broken fresh apps the
same way. Align with the plugin's real manifest name.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…ersions Pin exact versions instead of ^ for reproducible manifests. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Move reference apps to config/agents/<id>/agent.md; document migration and reserved skills folder; align generated API snippets and CHANGELOG.
typedoc picked up JSDoc changes from agent/v2/4-agents-plugin: - New public export `agentIdFromMarkdownPath` (helper for path-based id resolution used by `loadAgentFromFile`). - `loadAgentsFromDir` description/body now reflects the folder layout (`<id>/agent.md`, orphan `*.md` rejected, reserved `skills/` dir). Generated by docusaurus-plugin-typedoc during pnpm --filter=docs build.
… retire agent-app
Stage 0 of the smart-dashboard-demo plan. Ports the prototype Smart
Dashboard (NYC Taxi analytics) from the p3ju worktree into dev-playground
as a new route, migrates its markdown agents to the folder layout, and
deletes apps/agent-app — which is superseded by this demo as the
integration test of the entire v2 agents stack.
Client:
- New route at client/src/routes/smart-dashboard.route.tsx with
its own subdirectory for components/ and hooks/.
- Ported 8 components (ActiveFilters, AgentSidebar, AnomalyCard,
FareChart, InsightCard, KPICards, QuerySection, TripChart) and
4 hooks (useActionDispatcher, useAgentStream, useChartColors,
useDashboardData) as-is. Relative imports preserved.
- Nav link added in __root.tsx.
- TanStack routeTree.gen.ts auto-regenerated.
Server:
- Ports apply_filter and highlight_period inline tools.
- Adds sql_analyst (code-defined: fromPlugin(analytics)) and
dashboard_pilot (code-defined: apply_filter + highlight_period)
per the plan's Q2 = option B decision.
- Adds query markdown dispatcher in config/agents/query/agent.md
delegating to both specialists via the agents: frontmatter.
- Ports insights and anomaly ephemeral markdown agents.
Config:
- Ports 4 SQL queries into config/queries/dashboard_*.sql.
- Note: shared/appkit-types/analytics.d.ts not regenerated in this
commit; useAnalyticsQuery("dashboard_*", ...) uses explicit as
casts and works at runtime. Regenerate with
'npx @databricks/appkit generate-types' locally when convenient.
Cleanup:
- apps/agent-app/ removed in full. No references outside
pnpm-lock.yaml (regenerated).
- plans/smart-dashboard-demo.md added with the full staged plan.
Verification:
- pnpm --filter=dev-playground client typecheck: clean.
- pnpm --filter=dev-playground client vite build: clean.
- Server typecheck: same pre-existing errors as main (files plugin
union type, telemetry CacheManager, playwright DOM lib) — no new
regressions.
Next stages (1-6, per the plan): dispatcher integration verified,
save_view + approval card, dashboard-context injection + focus_chart,
Stream Inspector, polish, demo script.
Stage 0 ported the dashboard shell verbatim from the prototype; this commit layers every v2-stack feature on top, moves the feature dir out of routes/ (TanStack was flagging files as stray routes), rewrites the agent -> UI action pipeline for correctness, and adds discoverability for the HITL flow. Server (apps/dev-playground/server/index.ts) - Split the polymorphic apply_filter into four narrower tools: filter_by_date_range, filter_by_pickup_zip, filter_by_fare, clear_filters. Each has exactly one client-side effect; removes the whole class of 'agent said it worked but nothing moved' bugs. - Add clear_highlights, focus_chart, save_view (destructive; triggers the approval gate). - dashboard_pilot instructions rewritten with a compact verb-per-line reference so the LLM picks the right single tool for each intent. Client - moved out of routes/ - Feature code relocates to client/src/features/smart-dashboard/ (components/, hooks/, lib/). TanStack Router was warning that every non-route file under routes/ 'does not contain any route piece.' - smart-dashboard.route.tsx uses @/features/ aliases; the route file is now the only thing under routes/. Client - correctness fixes in the action dispatcher - Act only on response.output_item.done (never .added, which fires with partial arguments and caused double-applied highlights plus silent JSON-parse races). - Dedupe by call_id with a bounded LRU; reset on appkit.metadata (new-run signal). - Use updater callbacks (onFilterUpdate(prev => ...)) instead of a currentFilters prop to eliminate stale-closure bugs when the agent fires multiple tool calls in one render cycle. - Validate arg shapes per tool; anything malformed or unrecognized surfaces through onUnknownTool (route renders as a red banner + console.warn). Silent failure was the worst failure mode. - Emit a human-readable summary for every applied action (onAction). Client - discoverability / HITL - New QuickActionsBar with Save view... (inline name input), Clear filters, Clear highlights. Each dispatches through the chat pipeline so the agent still reasons and the approval gate still fires for destructive actions - the bar just saves typing. - ActionToast (bottom-left) confirms every dispatcher-applied action for ~3s. Answers 'did it work?' without opening the inspector. - QuerySection refactored into a view: content/isLoading/onSend come from the route. Lifting useAgentStream one level up lets the Quick Actions bar and the chat input share a single agent stream. - QuerySection example queries refreshed to cover the new tools. Client - stream-inspector wiring - SSEEvent extended with approval_pending payload fields. - use-stream-inspector threaded through so every run's events flow into the inspector's module-level store. - FocusableChart renamed its 'id' prop to 'chartId' (logical registry key, not a DOM id - biome was right to complain). Verification - pnpm --filter=dev-playground client tsc --noEmit: clean. - pnpm --filter=dev-playground client vite build: clean. - Server typecheck: same pre-existing errors as main; no new regressions. - apps/dev-playground/shared/appkit-types/analytics.d.ts regenerated by vite build to register the four dashboard_* queries; kept in the commit so CI and downstream consumers have typed useAnalyticsQuery access out of the box.
…iews panel + floating chat Four feature layers on top of the Smart Dashboard demo. Each is independently useful; all land together because they share route plumbing. Phase A - Approval gate for sub-agent tool calls (packages/appkit) The earlier event-forwarding fix for runSubAgent surfaced a second gap: destructive tools invoked through a sub-agent (save_view via dashboard_pilot) bypassed the approval gate entirely. The gate lived only in _streamAgent.executeTool; childExecute called dispatchToolCall directly. Extract the approval flow (emit appkit.approval_pending, await gate, handle deny) into a closure the parent stream builds once and passes down to runSubAgent. runSubAgent propagates it to nested sub-agents too. Now every destructive tool fires the gate regardless of which agent in the delegation chain invoked it. No public API change; runSubAgent's new checkApproval parameter is private. Phase B - save_view uploads a dashboard snapshot to a UC volume Previously the save_view tool was a stub that logged to the console. Now it actually persists: - Add 'files' volume binding backed by DATABRICKS_VOLUME_FILES. - Client captures the dashboard body via html2canvas at scale 0.6, JPEG quality 0.75 - sized to fit under express.json's default 100kb limit. - ApprovalCard intercepts 'approve' for save_view: capture -> POST to /api/dashboard/save-view with name + description + filters + highlights + pngBase64 -> then POST to /api/agents/approve. - Server writes <timestamp>_<slug>.png and sidecar .json into the 'files' volume under saved-views/. Uses appkit.files(...).asUser(req) so OBO scoping works. - Approval card shows a preview thumbnail + the final volume path. Phase C - Saved views panel + load_view tool - New /api/dashboard/saved-views route lists paired .png/.json entries in the volume, parses metadata sidecars. - New /api/dashboard/saved-view-png streams PNG bytes so thumbnail <img src> tags work without a general file-download endpoint. - New 'load_view' tool on dashboard_pilot. Tool arguments carry the resolved filters + highlights; use-action-dispatcher applies them in one shot. No extra round trip needed since state is in the tool-call JSON. - SavedViewsPanel renders a horizontal strip of thumbnail cards; clicking a card dispatches 'Load the saved view X' through the chat so the agent loop stays consistent. Phase D - Floating chat drawer with multi-turn history The previous QuerySection wiped the UI on every send. Replaced with ChatDrawer: - Multi-turn messages accumulate in route state; previous user + assistant turns stay visible as the user iterates. - Streaming assistant content updates the in-progress turn in place. - Toggled by CMD+J, opens automatically when a new approval arrives so users don't miss destructive gates. - Approval cards render inline in the conversation, pinned to the user turn that triggered them. Extras / hygiene - query dispatcher markdown prompt expanded: 'save' was missing from the pilot verb list, which caused the LLM to respond directly from its empty toolset. Added save/clear + explicit delegation rule. - dashboard_pilot tool list includes save_view + load_view. - QuerySection component removed (superseded by ChatDrawer). Verification - pnpm --filter=appkit typecheck clean. - pnpm --filter=appkit test suite: 264 tests pass. - dev-playground client tsc + vite build: clean.
Fresh UC volumes don't have a saved-views/ subdirectory until the first
save; the SDK throws FILES_API_DIRECTORY_IS_NOT_FOUND on list. The
route was propagating that as a 500 which rendered as a red error
banner in the SavedViewsPanel on first load.
Catch the error explicitly, return { views: [] }, let the panel render
its 'no saved views yet' empty state cleanly. Uploads still work the
first time because the SDK auto-creates parent dirs on upload.
Previously forwardSubAgentToolEvent allow-listed only tool_call and tool_result. That was overcautious: it meant the sub-agent's message_delta / message / thinking / status events never reached the client, so users stared at 'thinking…' for the entire sub-agent run and only saw text once the whole run completed. Now forward everything except metadata. Metadata carries the sub-agent's own threadId and would overwrite the parent thread on the client, breaking multi-turn continuity — that's the one thing still worth filtering. Also update the query dispatcher prompt so the parent no longer echoes what the specialist already said. The specialist text streams through as-is; the dispatcher speaks only when it needs to route, combine, or add context.
html2canvas 1.x throws on `oklch()` color values, which Tailwind v4 emits everywhere in computed styles. Swap to the maintained html2canvas-pro fork (drop-in API) so dashboard captures render without "Attempting to parse an unsupported color function 'oklch'" errors in the approval card. Keeps html2canvas pinned so types still resolve.
Databricks SDK `volume.download(path)` returns a wrapper
`{ contents: ReadableStream, "content-type": string }`, not the stream
itself. The previous handler tried to write the wrapper directly, which
produced an empty body and broke thumbnails in the saved-views panel.
Now we read `.contents`, drain the stream, and respond with the
server-reported content-type (falling back to `image/png`).
Also drops a couple of noisy console.logs left over from the debugging
session.
… click Clicking a saved-view thumbnail was sending a chat prompt like "Load the saved view 'january'" and letting the agent reconstruct filters from the view name. That dropped the highlights (agent had no tool to fetch the stored metadata) so January-with-focus-on-week-1 came back as just January-wide. Since the client already holds the full authoritative metadata for the clicked thumbnail, bypass the agent and apply `meta.filters` and `meta.highlights` directly to local state, with a toast summarising what was restored. Also hardens the `appkit.approval_pending` handler: it now accepts both snake_case and camelCase fields and validates that approval_id/tool_name/stream_id are non-empty strings before enqueuing, so a malformed event can't push a broken approval card.
Picks up the new `annotations?: ToolAnnotations` field on `ToolConfig` and `FunctionTool` introduced upstream in the annotations-propagation fix.
…nable agent feed Reshapes the Smart Dashboard demo from a sparse 2-chart layout into a 2x2 chart grid with a right-rail agent feed, and turns the previously read-only insights/anomaly cards into clickable actions that drive the dashboard directly. New visualisations: - HourlyHeatmap: day-of-week × hour-of-day grid, click a cell to ask the agent to investigate that slot. - TopZonesChart: hand-rolled horizontal bar leaderboard with click-to- filter and a `highlight_zone` ring driven by the agent. - KPI sparklines: inline 7-day micro-charts with windowed trend deltas baked into each KPI card. Agent feed becomes interactive: - `feed-actions.ts` defines a structured action schema (filter_date, filter_zip, filter_fare, highlight_period, highlight_zone, focus_chart, ask) and a parser. The `insights` and `anomaly` ephemeral agents now emit JSON matching that schema. - `ActionableCard` renders insights/anomalies with action chips that invoke `useActionDispatcher.dispatch` directly — same code path the SSE function-call handler uses, so UI clicks and agent tool calls behave identically. - The feed re-runs (debounced) whenever filters or highlights change. Server-side wiring: - Adds `highlight_zone` and `clear_zone_highlights` tools. - Extends the `focus_chart` enum with `hourly_heatmap` and `top_zones`. - Updates `dashboard_pilot` instructions to prefer `highlight_zone` over `filter_by_pickup_zip` when calling out a single ZIP. - Adds three SQL queries: `dashboard_hourly_heatmap`, `dashboard_top_zones`, `dashboard_kpi_sparklines`. The top-zones query casts `pickup_zip` (an INT in samples.nyctaxi.trips) to STRING so the client's highlight Map keys, the agent's `highlight_zone` arg, and the filter parameter all speak the same type. Polish & defensive fixes: - Defensive `Number()` coercion in `kpi-cards.tsx` for sparkline values so trend math doesn't render `NaN%` or string-concatenated revenue totals if a driver hands back DECIMAL-as-string. - `Sparkline` reserves vertical space for intentionally-empty series (e.g. the categorical "Top Pickup Zone" KPI) instead of rendering a loading-style placeholder. - 2x2 chart grid uses `items-start` + `auto-rows-min content-start` so the rail no longer stretches the chart column and creates dead space. - `ChatDrawer` becomes a controlled component (`open` + `onOpenChange`) so any agent-triggering UI action can auto-open the chat — the user always sees the agent's response without manual disclosure.
The playground header was unscalable: 14 demo links rendered as side-by-side buttons that overflowed on narrow screens, and the home page maintained a parallel hand-curated grid that had already drifted (missing Smart Dashboard, Chart Inference, Vector Search, Policy Matrix, and Serving — ~30% of the catalog). Introduces `client/src/lib/nav.ts` as the single source of truth: each demo declares its label, one-line description, lucide icon, and category group. Both surfaces now read from the same list, so adding a demo is a one-line change and they can no longer drift. Header (`__root.tsx`): - Replaces the button wall with a single "Menu" hamburger dropdown grouping demos by purpose (Data / AI / Platform). - Active route is highlighted inside the dropdown and shown breadcrumb- style next to the brand, so the user always knows where they are. - Caps dropdown height at viewport-minus-header with overflow scroll, so adding more demos won't break the layout. Home page (`index.tsx`): - Restrained hero with a soft dual-radial gradient wash (~6-8% opacity, primary + accent) — depth without saturation. - Featured card for the Smart Dashboard flagship demo: gradient accent, icon tile, eyebrow badge, animated CTA. The featured demo also appears in its category grid, de-emphasised with a "Featured above" note. - Three category sections with one-line taglines, rendered as a 1/2/3-col responsive grid of icon + title + description cards. Each card is a real `<Link>` (not a button inside a decorative `<Card>`), so the whole surface is keyboard-accessible. - Footer shows live demo and category counts driven by the catalog.
…tive Retag save_view as effect: "write" (it creates a PNG; it doesn't delete anything) and teach the approval card to render three distinct tiers. Capturing a screenshot no longer masquerades as deletion: writes get a calm blue card with a plus-circle icon, updates get a warning-amber card with a pencil, and real destructive actions retain the red shield-alert. Legacy destructive: true still maps to the red tier, so tools that haven't migrated keep their current look.
61ee6a7 to
8d7ed49
Compare
2cc80cd to
398b881
Compare
Tailwind v4 compiles `bg-blue-50/50` to a two-layer rule: an sRGB hex fallback plus an `@supports (color-mix)` override that mixes the oklch palette token with transparent in oklab. Browsers with color-mix support (recent Chrome/Arc) take the oklab path; older embedded Chromiums (e.g. Cursor's built-in browser) fall through to the sRGB hex. Those two paths produce visibly different tints against the dark `--card` token, which is why the agent-feed cards rendered inconsistently across Chrome, Arc, and Cursor's browser. Pin the four insight/anomaly-tier backgrounds to arbitrary 8-digit hex (`bg-[#eff6ff80]` etc.) so every browser lands on the same sRGB path. Values taken from Tailwind's own fallback output to preserve the intended look on color-mix-capable browsers.
appkit-ui's globals.css already defines dark-theme tokens via two paths — an explicit `.dark` class on <html>, and `@media (prefers-color-scheme: dark)` guarded by `:root:not(.light)` so an explicit `.light` class wins. Tailwind v4's default `dark:` variant, however, is purely media driven. That mismatch shows up when the user forces light via the playground's theme selector while their OS is in dark mode: the bootstrap script sets `<html class="light">`, --card/--background correctly resolve to light, but every `dark:*` utility keeps firing under the media query — cards end up painted with dark-mode backgrounds layered under light-mode chrome. Declare a playground-local `@custom-variant dark` that mirrors the token logic exactly: fire when the element is (or descends from) `.dark`, or when `prefers-color-scheme: dark` matches and no `.light` ancestor is present. This rebinds every `dark:*` utility to respect the theme selector's forced choice, keeping the rest of appkit-ui's consumers — which don't ship the bootstrap script — on the existing media-only behaviour.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Final layer of the agents feature stack. Everything needed to
exercise, demonstrate, and learn the feature.
Reference application: agent-app
apps/agent-app/— a standalone app purpose-built around the agentsfeature. Demonstrates every major capability in one place:
config/agents/assistant.md, default)with destructive file tools (upload, delete) for HITL demo, and
agents: [support, researcher]delegating to both a markdownsibling and a code-defined specialist.
config/agents/support.md) — full analyticsget_weather.researcherinserver.ts) — definedin code specifically because its MCP tool set is conditional on
runtime env vars, which markdown frontmatter can't express.
Referenced from
assistant.mdvia the markdown → code cross-reference.server.ts— concise: ambienttool()factories, conditional MCPwiring, zero-trust host allowlist derived from the same env vars,
agents()plugin config withautoInheritToolsandmcp.trustedHosts.streaming tokens, tool calls, and an approval card that approves or
denies destructive tool requests over
/api/agents/approve.databricks.yml,app.yaml) and.env.examplefor local dev.dev-playground chat UI + autocomplete agent
apps/dev-playground/client/src/routes/agent.route.tsx— chat UIwith inline autocomplete (hits the
autocompletemarkdown agentconfigured with
ephemeral: true) and a full threaded conversationpanel (hits the default agent).
apps/dev-playground/server/index.ts— code-definedhelperagentusing
fromPlugin(analytics)alongside the markdown-drivenautocompleteagent inconfig/agents/, demonstrating the mixedsetup against the same plugin list. Route tree (
routeTree.gen.ts)regenerated to include the new
/agentroute.Docs
docs/docs/plugins/agents.md— progressive guide covering:toolkits:/tools:frontmatter.fromPlugin().runAgent()(nocreateAppor HTTP).Plus configuration reference (including
approval,limits,mcpkeys), runtime API reference, and a full frontmatter schema table.
docs/docs/api/appkit/— typedoc regenerated for the full agentspublic surface including
AgentDefinition.ephemeral,AgentsPluginConfig.{approval, limits, mcp}, updatedloadAgentFromFile/loadAgentsFromDirsignatures, expandedAgentEventunion, andToolkitEntryannotations.Template
template/appkit.plugins.json— adds theagentsplugin entry sonpx @databricks/appkit init --features agentsscaffolds the plugincorrectly.
Test plan
pnpm docs:buildclean (no broken links)pnpm --filter=@databricks/appkit build:packageclean, publint cleanSigned-off-by: MarioCadenas MarioCadenas@users.noreply.github.com
PR Stack
agents()plugin +createAgent(def)+ markdown-driven agents — feat(appkit): agents() plugin, createAgent(def), and markdown-driven agents #304fromPlugin()DX +runAgentplugins arg + toolkit-resolver — feat(appkit): fromPlugin() DX, runAgent plugins arg, shared toolkit-resolver #305Demo
agent-demo.mp4