Skip to content

feat(appkit): auto-discover code agents from server/agents/ - #533

Draft
MarioCadenas wants to merge 5 commits into
mainfrom
agents-discovery-dx
Draft

feat(appkit): auto-discover code agents from server/agents/#533
MarioCadenas wants to merge 5 commits into
mainfrom
agents-discovery-dx

Conversation

@MarioCadenas

Copy link
Copy Markdown
Collaborator

What

Code agents are now auto-discovered from server/agents/, symmetric with markdown agents in config/agents/. A file that export default createAgent(...) is discovered at startup — its id is the filename — so the plugin call collapses to agents() with no agent map and no import.

- // server/agents/helper.ts
- export const helper = createAgent({ name: 'helper', instructions, tools });
- // server/server.ts
- import { helper } from './agents/helper';
- agents({ agents: { helper } })          // id restated; hand-built map
+ // server/agents/helper.ts   ← id IS the filename
+ export default createAgent({ instructions, tools });
+ // server/server.ts
+ agents()                                 // no import, no map

How

  • Runtime scan, resolved by NODE_ENV. Dev scans server/agents/*.ts under tsx; a bundled server scans the compiled dist/agents (or build/agents) *.js. The NODE_ENV guard keeps a stale build dir from shadowing live sources in dev.
  • Branded detection. createAgent stamps a non-enumerable Symbol.for("appkit.agent") on its result; the loader keeps branded exports and skips helpers / bundler chunks. One agent per file.
  • Prod bundling (the trap this avoids). A runtime scan of a dynamic path is dropped by the bundler. Instead, the template's tsdown config lists server/agents/*.ts as build entries, so the compiled dist/agents/*.js exist for the scan. Static bundling was the alternative considered and rejected (import.meta.glob crashes tsx in dev).

Backward compatibility

  • agents({ agents: { ... } }) still works and emits a one-time deprecation warning.
  • createAgent({ name }) is still honored.
  • Markdown discovery (config/agents/) is unchanged — it stays a runtime data scan.

Also in this PR

  • Migrates the dev-playground reference app to the new pattern (4 code agents → server/agents/*.ts; build emits build/agents/*.mjs).

Verification

  • pnpm -r typecheck clean · pnpm check (biome) 0 errors · appkit + shared 3388 tests pass · pnpm build + pnpm docs:build succeed.
  • E2E: discovery verified in both npm run dev (tsx, .ts) and a bundled node dist/agents/*.js run.
  • dev-playground tsc error count unchanged (2046 → 2046 — pre-existing client/*.tsx noise, none in the new files); its server builds and prod-discovers all four agents.

Not verified here

  • dev-playground Playwright integration tests + a live deploy (both need a Databricks workspace).

Open follow-ups

  • Changelog entry (release-it derives it from the commit).
  • Optional: unify markdown agents under server/agents/ (breaking; deferred — the config/agents/ convention was kept to avoid breaking existing apps).

Draft — opening for early review.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 850 KB (+10 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 879 KB (+10 KB) 307 KB (+3.9 KB)
Type declarations 318 KB (+2.7 KB) 110 KB (+937 B)
Source maps 1.7 MB (+20 KB) 573 KB (+7.0 KB)
Other 11 KB 3.7 KB
Total 2.9 MB (+33 KB) 993 KB (+12 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 50 KB (+1.7 KB) 457 B 51 KB (+1.7 KB) external 148 KB (+4.6 KB)
./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 34 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
./type-generator index.js initial 21 KB

@databricks/appkit-ui

npm tarball (packed): 342 KB — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 390 KB 130 KB
Type declarations 228 KB 83 KB
Source maps 753 KB 248 KB
CSS 16 KB 3.3 KB
Total 1.4 MB 465 KB
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 49 KB 480 KB 1.3 MB 175 KB
./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

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 32042203358 -R databricks/appkit -n appkit-template-0.61.1-pr.4ec510a-agents-discovery-dx-533 -D appkit-pr-533 \
  && unzip -o "appkit-pr-533/appkit-template-0.61.1-pr.4ec510a-agents-discovery-dx-533.zip" -d "appkit-pr-533" \
  && databricks apps init --template "appkit-pr-533"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

Drop-in code agents: a file `server/agents/<id>.ts` that
`export default createAgent(...)` is discovered at startup — the agent id
is the filename, so there is no agent map and no import at the call site
(`agents()`). Symmetric with markdown agents in `config/agents/`.

Discovery is a runtime scan resolved by NODE_ENV: dev scans
`server/agents/*.ts` under tsx; a bundled server scans the compiled
`dist/agents` (or `build/agents`) `*.js`. `createAgent` brands its result
(`Symbol.for("appkit.agent")`) so the loader picks the agent out of a
module's exports and skips helpers and bundler chunks. The template's
tsdown config lists `server/agents/*.ts` as build entries so the compiled
files exist for the production scan (a runtime scan of a dynamic path
would be dropped by the bundler — the trap this avoids).

Backward compatible: `agents({ agents: { ... } })` still works and emits a
one-time deprecation warning; `createAgent({ name })` is still honored;
markdown discovery is unchanged.

Also migrates the dev-playground reference app to the new pattern.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
- Resolve the code-agents dir built-first and NODE_ENV-independently
  (prefer dist/agents|build/agents .js; fall back to server/agents .ts
  only when no built dir exists). Fixes a boot crash where a compiled
  server run without NODE_ENV=production imported .ts under plain Node.
- Loader gracefully skips (warns) when it cannot import a module for lack
  of a TS loader, instead of crashing setup.
- Drop the phantom `appkit generate-agents` command from the deprecation
  JSDoc and the scaffold template — discovery is a runtime scan.
- Discovered id colliding with the deprecated agents({ agents }) map now
  warns and lets discovery win, rather than throwing at boot on upgrade.
- Document that createAgent brands (mutates) its input; de-noise the
  prod zero-agents warning when the deprecated map is in use; template
  tsdown clean:true so a deleted agent can't linger in dist/agents.
- Extract resolveCodeAgentsDir as a pure function and unit-test the
  built-first ordering, override, and fallback; add map-only and
  defaultAgent-not-registered regression tests.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
- Derive the first-registered default from the agent Map instead of
  tracking a separate variable + param.
- Collapse the isIgnored test/spec cross-product to one regex; derive the
  agent id via path.parse (drops a duplicated extension list).
- Reuse the loader's isIgnored in hasCodeAgentSources so the "sources exist
  but nothing discovered" warning no longer fires for index/test-only dirs.
- Trim verbose docblocks/comments to their load-bearing facts and keep the
  brand rationale in one place.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Every agent is a folder under server/agents/<id>/ holding agent.md
(markdown) or agent.ts (code); the folder name is the id.

- Code loader scans <id>/agent.{ts,tsx,js,mjs}, built-first (dist/build
  over source); folders without an entry file are skipped.
- Markdown loader skips folders lacking agent.md (was a throw) so code
  folders and asset dirs coexist; drop the RESERVED_DIRS list.
- One dir knob (default server/agents) feeds both loaders; retire
  codeAgentsDir. config/agents is read as a deprecated fallback
  (per-agent merge, new location wins, one-time warning).
- Cross-kind sub-agent references resolve by folder id.
- Migrate template, dev-playground, docs, and test fixtures.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
- resolveCodeAgentsDir: a relative `dir` is now built-first (dist/<name>,
  build/<name> probed before source); only an absolute path is verbatim.
  Fixes prod discovering zero code agents when `dir` was a relative path
  (e.g. the documented default typed explicitly) — dev worked, prod empty.
- Cross-location sub-agent refs resolve mid-migration: config/agents loads
  first and its defs feed the server/agents resolution.
- hasCodeAgentSources + its warning track the configured dir; drop the
  duplicate DEFAULT_AGENTS_DIR constant.
- Discover symlinked agent folders in both loaders.
- Template AgentChat.tsx: correct the scaffold's shown agent paths.
- Docs: dir:false also disables the config/agents fallback; note stale-build
  shadowing in dev; fix AgentDefinition.name tsdoc.
- Tests: cover the config/agents fallback (merge, precedence, one-time
  warning), cross-dir refs, dir:false, and built-first for a relative dir.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant