Repository navigation
feat(appkit): Agent Skills (v1) — SKILL.md progressive disclosure for agents - #532
MarioCadenas wants to merge 4 commits into
Conversation
0bc20d6 to
3dc4e7f
Compare
📦 Bundle size reportCompared against
|
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 929 KB (+37 KB) | 326 KB (+14 KB) |
| Type declarations | 351 KB (+11 KB) | 122 KB (+4.3 KB) |
| Source maps | 1.8 MB (+68 KB) | 611 KB (+24 KB) |
| Other | 11 KB | 3.7 KB |
| Total | 3.1 MB (+116 KB) | 1.0 MB (+43 KB) |
Per-entry composition (own code — deps external (as shipped))
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
. |
88 KB | 2.5 KB | 91 KB | external | 288 KB |
./beta |
56 KB (+7.1 KB) | 457 B | 56 KB (+7.1 KB) | external | 164 KB (+21 KB) |
./testing |
17 KB | 0 B | 17 KB | external | 50 KB |
./tsdown |
520 B | 0 B | 520 B | external | 813 B |
./type-generator |
21 KB | 0 B | 21 KB | external | 61 KB |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
. |
index.js |
initial | 84 KB |
. |
utils.js |
initial | 4.0 KB |
. |
remote-tunnel-manager.js |
lazy | 2.5 KB |
./beta |
beta.js |
initial | 40 KB |
./beta |
stream-manager.js |
initial | 5.8 KB |
./beta |
wide-event-emitter.js |
initial | 3.2 KB |
./beta |
databricks.js |
initial | 3.0 KB |
./beta |
configuration.js |
initial | 2.1 KB |
./beta |
service-context.js |
initial | 1.3 KB |
./beta |
client.js |
initial | 434 B |
./beta |
client-options.js |
initial | 220 B |
./beta |
supervisor-api.js |
lazy | 192 B |
./beta |
databricks.js |
lazy | 142 B |
./beta |
index.js |
lazy | 123 B |
./testing |
index.js |
initial | 17 KB |
./tsdown |
index.js |
initial | 520 B |
./type-generator |
index.js |
initial | 21 KB |
@databricks/appkit-ui
npm tarball (packed): 349 KB (+751 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 394 KB (+316 B) | 132 KB (+158 B) |
| Type declarations | 229 KB (+300 B) | 84 KB (+130 B) |
| Source maps | 765 KB (+1.2 KB) | 253 KB (+492 B) |
| CSS | 16 KB | 3.2 KB |
| Total | 1.4 MB (+1.8 KB) | 471 KB (+780 B) |
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
./js |
5.3 KB | 49 KB | 55 KB | 208 KB | 14 KB |
./js/beta |
20 B | 0 B | 20 B | 0 B | 0 B |
./react |
432 KB (+120 B) | 49 KB | 481 KB (+120 B) | 1.3 MB | 177 KB (+168 B) |
./react/beta |
1.0 KB | 0 B | 1.0 KB | 0 B | 1.9 KB |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
./js |
index.js |
initial | 5.2 KB |
./js |
chunk |
initial | 120 B |
./js |
apache-arrow |
lazy | 49 KB |
./js/beta |
beta.js |
initial | 20 B |
./react |
index.js |
initial | 430 KB |
./react |
tslib |
initial | 2.1 KB |
./react |
apache-arrow |
lazy | 49 KB |
./react/beta |
beta.js |
initial | 1.0 KB |
🤖 AppKit PR bot🔬 Run evalsStart an eval for this PR from the evals-monitor app: Go to Evals Monitor → 📦 Try this PR's app templateScaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh run download 32470045125 -R databricks/appkit -n appkit-template-0.63.0-pr.2ca1159-feat-agent-skills-532 -D appkit-pr-532 \
&& unzip -o "appkit-pr-532/appkit-template-0.63.0-pr.2ca1159-feat-agent-skills-532.zip" -d "appkit-pr-532" \
&& databricks apps init --template "appkit-pr-532"The template pins |
3dc4e7f to
baad708
Compare
4b886ca to
c3f6872
Compare
c1ac42c to
b1ae87f
Compare
…ents) Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…xtures Exercise the previously-untested skill sources/paths in the reference app: - global `bullet-brief` skill; helper opts into it (multi-entry menu on a code agent) - per-agent `query/skills/routing-brief` (bundle-agent source + bundled reference.md) - per-agent `query/skills/haiku` collides with global `haiku` (query opts in), forcing qualified agent:haiku / bundle:haiku addressing - add `query` to the /agent page picker so its skills surface in the input Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Reconciles with the rebased base: agents({ dir }) was removed (server/agents
is now a fixed convention), so the skill-client tests construct the plugin
with {} instead of { dir: false }.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
b1ae87f to
5a33e95
Compare
Ponytail review cuts: one-line query picker entry (shorter label), and a single worked example in routing-brief/reference.md. No behavior change. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
|
Split into two stacked PRs for easier review — this PR is superseded:
Both are rebased onto current |
Playground/template half of #532 (split 2/2), stacked on the SDK PR (1/2). Demo skills exercising every source/case: global bullet-brief (+ helper opt-in), per-agent query/skills/routing-brief, the agent:haiku/bundle:haiku collision, and the template tracer-bullets skill. Auto-retargets to main once 1/2 merges. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Playground/template half of #532 (split 2/2), stacked on the SDK PR (1/2). Demo skills exercising every source/case: global bullet-brief (+ helper opt-in), per-agent query/skills/routing-brief, the agent:haiku/bundle:haiku collision, and the template tracer-bullets skill. Auto-retargets to main once 1/2 merges. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
#532) (#544) * feat(appkit): agent skills v1 — SKILL.md progressive disclosure (1/2) SDK half of #532 (split 1/2). Skills engine (parse/load/resolve/render/read), agent-definition skills: wiring, agents-plugin integration (load_skill + read_skill_file tools, catalog resolution, clientConfig), the appkit-ui useAgentChat /skill surface, and docs. Playground/template fixtures follow in 2/2. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * chore(appkit): sync template manifest with agents skills volume resource template/appkit.plugins.json is generated from the plugin manifests; it must travel with the agents manifest.json change (skills volume resource) or CI's sync:template check fails. Was mis-bucketed into the 2/2 fixtures PR. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): tidy skill loading + drop dead biome-ignore comments Review follow-ups on #543: - parallelize UC-volume skill reads (network-bound) via Promise.all; per-skill failures still skip individually and sorted order is preserved - extract the useAgentChat slash-command parse into a named resolveSkill helper (appkit-ui) - delete 18 stale biome-ignore comments — post-oxlint migration, and no-explicit-any is off in .oxlintrc.json, so they suppressed nothing Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * test(playground): agent skills fixtures + template demo (2/2) Playground/template half of #532 (split 2/2), stacked on the SDK PR (1/2). Demo skills exercising every source/case: global bullet-brief (+ helper opt-in), per-agent query/skills/routing-brief, the agent:haiku/bundle:haiku collision, and the template tracer-bullets skill. Auto-retargets to main once 1/2 merges. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(playground): drop dead biome-ignore comments in agent.route.tsx react/exhaustive-deps is off in .oxlintrc.json and oxlint doesn't parse biome directives, so these two suppressions were inert post-migration. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * fix(appkit-ui): gate /skill-prefix parsing on the skill catalog The leading-/token sugar in useAgentChat stripped any /word off the message and sent it as a skill, so ordinary messages like '/tmp is full' or '/usr/bin/python needs upgrading' were mangled into a bogus skill with the first word cut out. Parse the leading token as a skill only when it matches a name in the agent's skill catalog; otherwise send the message verbatim. useAgentChat gains an optional 'skills' option (the known names); the template passes its catalog through, and the playground route applies the same guard to its inline parser. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> --------- Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> Co-authored-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
* refactor(appkit): extract pure agents-plugin helpers into sibling modules Step 1 of splitting the ~2.5k-line agents plugin. Moves module-scope pure functions/constants out of agents.ts verbatim (behavior-preserving): - approval.ts requiresApproval - prompt.ts composePromptForAgent - builtin-tools.ts LOAD_SKILL_TOOL_DEF, READ_SKILL_FILE_TOOL_DEF - adapter-extensions.ts buildAdapterExtensions, supervisorToolDescription, warnOnCapabilityMismatch agents.ts: 2512 -> 2331 lines. typecheck + 394 agent tests green. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): extract agents skill loading/dispatch into skill-loader Step 2 of splitting the agents plugin. Moves skill discovery, per-agent catalog resolution, and the load_skill/read_skill_file dispatch into skill-loader.ts as free functions; the class keeps thin delegators (call sites unchanged) and skillWorkspaceClient() as the OBO credential seam. agents.ts: 2331 -> 2152 lines. typecheck + 394 agent tests green. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): extract agents registry assembly into registry.ts Step 3 of splitting the agents plugin. Moves the decoupled boot-time assembly helpers into registry.ts: loadCodeAgents, hasCodeAgentSources (now internal), resolveDefaultAgent, and the AgentSource type. buildAgentRegistry stays as the orchestrator that wires them. Also merges a duplicate import in skill-loader.ts. agents.ts: 2152 -> 2088 lines. typecheck + 394 agent tests green. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): extract agents tool-dispatch engine into tool-dispatch.ts Step 4 (final) of splitting the agents plugin. Moves dispatchToolCall + runSubAgent — the tool-call budget, approval gate, and sub-agent recursion — into tool-dispatch.ts as free functions over RunState + a ToolDispatchDeps object. The plugin builds deps via toolDispatchDeps(); the two executeTool closures call the free function. RunState moves with them. Tests updated to invoke the free functions (deps built from the plugin's own builder). agents.ts: 2088 -> 1839 lines (2512 -> 1839 across all four steps). typecheck + 394 agent tests green. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): extract agents config resolution into resolve-config.ts Moves resolvedApprovalPolicy / resolvedLimits defaulting into pure functions over AgentsPluginConfig. The getters keep the approval-policy memo cache and delegate. Config-only interface; both now unit-testable in isolation. agents.ts: 1839 -> 1812 lines. typecheck + agent tests green. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): extract stream tracking into StreamRegistry Moves the active-stream map + per-user counter (the O(1) concurrency-limit check) into a StreamRegistry class. The plugin holds one instance and keeps trackStream/untrackStream/countUserStreams as delegators; cancel/approve read via streams.get(). Tests inject via trackStream and assert via the registry. agents.ts: 1812 -> 1784 lines. typecheck + 394 agent tests green. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * refactor(appkit): rename agents stream tracker to ActiveStreamTracker The previous commit named it StreamRegistry, colliding with the existing SSE-layer StreamRegistry in src/stream/ (connection/event-buffer tracking used by StreamManager). They're different concepts; renamed the agents-plugin one to ActiveStreamTracker (tracks active streams + per-user counts for the O(1) concurrency limit) to avoid the name clash. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * fix(appkit): finish ActiveStreamTracker rename (add file + agents wiring) The prior commit's git add hit the already-deleted stream-registry.ts path, aborted, and recorded only the deletion — leaving the pushed tip non-compiling (agents.ts imported the removed file; active-stream-tracker.ts was uncommitted). This adds the new module and the agents.ts import/usage so the tree builds. Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> * docs(appkit): note volume skills list resource files one level only Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> --------- Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com> Co-authored-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Agent Skills (v1)
Runtime Agent Skills for the
agentsplugin — theSKILL.mdformat Claude Code / Cursor use, brought to AppKit agents. Only each skill'sname+descriptionsit in the system prompt (always-on, cheap); the full body loads on demand. Works on any Databricks-served model — AppKit implements the progressive disclosure itself, so it doesn't depend on a provider-native skills feature. Fills the seam the loader already reserved (RESERVED_DIRS = new Set(["skills"])).Not to be confused with the dev-time "Databricks Agent Skills" product (Claude Code skills for building apps) — this is a runtime capability of deployed agents.
What a skill is
A directory with a
SKILL.md(frontmattername+description, Markdown body) plus optional bundled reference files. Frontmatter is an Anthropic-format superset (also tolerateslicense,allowed-tools,metadata); unknown keys warn, not error — so skills authored elsewhere drop in.How it works
name+descriptionis injected into the agent's system prompt.load_skill(skill)returns the body + a manifest of bundled files;read_skill_file(skill, path)reads one of those files (through a directory-containment guard)./skill-namein chat (oruseAgentChat'ssend(msg, { skill })).Sources & visibility
config/agents/skills/, per-agentconfig/agents/<id>/skills/, and a catalog UC Volume (skillsVolume/DATABRICKS_VOLUME_AGENT_SKILLS), read as the service principal.skills: [...]frontmatter (orautoInheritSkills).<scope>:name; the bare name errors as ambiguous.Commits (phased, independently reviewable)
feat(appkit): load and resolve agent skills from bundle sources— model, parser, loader, per-agent catalog resolutionfeat(appkit): expose skills to agents via prompt catalog and load_skill— disclosure + built-in tools (bundle e2e)feat(appkit): source agent skills from a Unity Catalog volume— catalog source, SP-readfeat(appkit): let users load skills from chat (/skill-name + picker)— client UX + forced loadfeat(appkit): document agent skills, wire sub-agents, ship example skilldev-playgroundcommits (ahaikudemo skill + a/-triggered skill menu) for hands-on testing.Try it in dev-playground
pnpm --filter=dev-playground dev, open/agent(Helper agent):/→ a menu of the agent's skills (/haiku); ↑/↓ + Enter/Tab to insert./haiku what's the weather in Paris?forces the skill; "give me a haiku about NYC taxi trips" triggers auto-load (watch theload_skilltool call).Tests & verification
New coverage across
skills.test.ts,dispatch-tool-call,skill-volume,skill-client, and theuse-agent-chathook. Full appkit (3075) and appkit-ui (366) suites pass; all packages typecheck; docs build succeeds.Deferred to v2 (non-goals here)
Script/code execution from skills;
allowed-toolsenforcement (advisory only in v1); per-user (OBO) skill volumes; TTL refresh of volume listings; marketplace / end-user-uploaded skills; standalonerunAgentskill parity.