Skip to content

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

Open
MarioCadenas wants to merge 6 commits into
mainfrom
agents-discovery-dx
Open

feat(appkit): auto-discover code agents from server/agents/#533
MarioCadenas wants to merge 6 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): 855 KB (+15 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 883 KB (+14 KB) 308 KB (+5.4 KB)
Type declarations 320 KB (+5.1 KB) 111 KB (+2.1 KB)
Source maps 1.7 MB (+27 KB) 576 KB (+10 KB)
Other 11 KB 3.7 KB
Total 2.9 MB (+46 KB) 999 KB (+18 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.8 KB) 457 B 51 KB (+1.8 KB) external 148 KB (+4.7 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 (-291 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 390 KB 130 KB (+1 B)
Type declarations 228 KB 83 KB
Source maps 752 KB (-334 B) 247 KB (-197 B)
CSS 16 KB (-462 B) 3.2 KB (-90 B)
Total 1.4 MB (-796 B) 464 KB (-286 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 (+127 B) 49 KB 480 KB (+127 B) 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 32238360627 -R databricks/appkit -n appkit-template-0.61.1-pr.ef8afac-agents-discovery-dx-533 -D appkit-pr-533 \
  && unzip -o "appkit-pr-533/appkit-template-0.61.1-pr.ef8afac-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.

…er/agents

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: a relative dir
  resolves dist/<name>|build/<name> before source, an absolute dir is verbatim,
  so a bundled server never imports .ts under plain Node.
- Markdown loader skips folders without agent.md so code + asset dirs coexist;
  drop the RESERVED_DIRS list.
- One `dir` knob (default server/agents) feeds both loaders; codeAgentsDir
  retired. config/agents is read as a deprecated fallback (per-agent merge, new
  location wins, one-time warning); cross-location sub-agent refs resolve.
- Cross-kind sub-agent references resolve by folder id.
- Migrate template, dev-playground, docs, and tests to the folder layout; add
  fallback / built-first / cross-dir test coverage.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas
MarioCadenas marked this pull request as ready for review August 18, 2026 15:34
@MarioCadenas
MarioCadenas requested a review from a team as a code owner August 18, 2026 15:34
@MarioCadenas
MarioCadenas requested a review from ditadi August 18, 2026 15:34
Follow-up to the review + inconsistency fan-out:
- findEntryFile now rethrows non-ENOENT/ENOTDIR errors (an unreadable agent
  folder no longer silently vanishes in prod).
- Clearer discovered-vs-markdown collision message (covers the cross-root
  config/agents fallback case, not just one folder).
- Template tsdown: scope clean:true to the agents case so a non-agents
  scaffold's build config is unchanged.
- Docs: fix DATABRICKS_SERVING_ENDPOINT_NAME, the auto-inherit default
  (off for both), cycle-rejection scope, /api/agents/approve path,
  defaultAgent precedence, dir:false wording, stale-dist note, and add
  the agents/generationParams frontmatter keys + toolCallTimeoutMs limit.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Ships the server build wiring from the package so a scaffolded app's
tsdown.server.config.ts is a one-liner instead of hand-maintained config:

  import { appkitServerConfig } from '@databricks/appkit/tsdown';
  export default appkitServerConfig();

- appkitServerConfig(overrides?, opts?) auto-detects server/agents/<id>/agent.ts
  and adds the entry glob + clean only when code agents exist.
- Object overrides merge with intent (entry unioned so the agent glob can't be
  clobbered, external composed, other keys win); a function override receives
  the computed base for full control.
- Dependency-free (node: builtins only) so it stays lean at build time.
- New ./tsdown export subpath (attw + publint clean); template drops its
  {{if .plugins.agents}} conditional.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Markdown agents need no build change; code agents require the server build
to emit them (dev via tsx hides this — only a bundled build breaks). Points
at the appkitServerConfig() preset as the one-line fix, notes the manual
entry-glob alternative, and the startup warning that catches a forgotten
build change.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…ion .ts-only

From the /simplify pass:
- Extract agentDirNames() so both loaders share one folder-selection policy
  (dir + symlink) instead of duplicating the subtle filter + comment.
- Code-agent source detection is .ts-only (resolveCodeAgentsDir source exts +
  hasCodeAgentSources), matching the build entry glob — an agent.tsx would
  otherwise load in dev but never be emitted for a prod bundle.

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