@databricks/appkit 0.62.0 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CLAUDE.md +2 -2
  2. package/dist/appkit/package.js +1 -1
  3. package/dist/core/agent/agent-dirs.js +14 -0
  4. package/dist/core/agent/agent-dirs.js.map +1 -0
  5. package/dist/core/agent/create-agent.d.ts +5 -7
  6. package/dist/core/agent/create-agent.d.ts.map +1 -1
  7. package/dist/core/agent/create-agent.js +28 -7
  8. package/dist/core/agent/create-agent.js.map +1 -1
  9. package/dist/core/agent/load-agents.d.ts.map +1 -1
  10. package/dist/core/agent/load-agents.js +4 -4
  11. package/dist/core/agent/load-agents.js.map +1 -1
  12. package/dist/core/agent/load-code-agents.js +115 -0
  13. package/dist/core/agent/load-code-agents.js.map +1 -0
  14. package/dist/core/agent/types.d.ts +20 -6
  15. package/dist/core/agent/types.d.ts.map +1 -1
  16. package/dist/core/agent/types.js.map +1 -1
  17. package/dist/plugins/agents/agents.d.ts +35 -4
  18. package/dist/plugins/agents/agents.d.ts.map +1 -1
  19. package/dist/plugins/agents/agents.js +144 -38
  20. package/dist/plugins/agents/agents.js.map +1 -1
  21. package/dist/plugins/agents/tool-approval-gate.js.map +1 -1
  22. package/dist/shared/src/agent.d.ts +1 -1
  23. package/dist/tsdown/index.d.ts +58 -0
  24. package/dist/tsdown/index.d.ts.map +1 -0
  25. package/dist/tsdown/index.js +71 -0
  26. package/dist/tsdown/index.js.map +1 -0
  27. package/docs/api/appkit/Function.createAgent.md +1 -3
  28. package/docs/api/appkit/Interface.AgentDefinition.md +12 -1
  29. package/docs/api/appkit/Interface.AgentsPluginConfig.md +6 -15
  30. package/docs/api/appkit/TypeAlias.AgentEvent.md +1 -1
  31. package/docs/api/appkit/Variable.agents.md +1 -1
  32. package/docs/api/appkit.md +46 -46
  33. package/docs/plugins/agents.md +85 -21
  34. package/docs/plugins/testing.md +2 -2
  35. package/llms.txt +2 -2
  36. package/package.json +5 -1
  37. package/sbom.cdx.json +1 -1
package/CLAUDE.md CHANGED
@@ -83,7 +83,7 @@ npx @databricks/appkit docs <query>
83
83
  - [Function: agentIdFromMarkdownPath()](./docs/api/appkit/Function.agentIdFromMarkdownPath.md): Derives the logical agent id from a markdown path. When the file is named
84
84
  - [Function: appKitServingTypesPlugin()](./docs/api/appkit/Function.appKitServingTypesPlugin.md): Vite plugin to generate TypeScript types for AppKit serving endpoints.
85
85
  - [Function: appKitTypesPlugin()](./docs/api/appkit/Function.appKitTypesPlugin.md): Vite plugin to generate types for AppKit queries.
86
- - [Function: createAgent()](./docs/api/appkit/Function.createAgent.md): Pure factory for agent definitions. Returns the passed-in definition after
86
+ - [Function: createAgent()](./docs/api/appkit/Function.createAgent.md): Pure factory for agent definitions: cycle-detects the sub-agent graph and
87
87
  - [Function: createApp()](./docs/api/appkit/Function.createApp.md): Bootstraps AppKit with the provided configuration.
88
88
  - [Function: createLakebasePool()](./docs/api/appkit/Function.createLakebasePool.md): Create a Lakebase pool with appkit's logger integration.
89
89
  - [Function: createLakebasePoolManager()](./docs/api/appkit/Function.createLakebasePoolManager.md): Create a pool manager that maintains per-key Lakebase connection pools.
@@ -198,7 +198,7 @@ npx @databricks/appkit docs <query>
198
198
  - [Type Alias: SupervisorTool](./docs/api/appkit/TypeAlias.SupervisorTool.md): Tools supported by the Databricks AI Gateway Responses API. The shapes match
199
199
  - [Type Alias: ToolRegistry](./docs/api/appkit/TypeAlias.ToolRegistry.md)
200
200
  - [Type Alias: ToPlugin()<T, U, N>](./docs/api/appkit/TypeAlias.ToPlugin.md): Factory function type returned by toPlugin(). Accepts optional config and returns a PluginData tuple.
201
- - [Variable: agents](./docs/api/appkit/Variable.agents.md): Plugin factory for the agents plugin. Reads config/agents/*.md by default,
201
+ - [Variable: agents](./docs/api/appkit/Variable.agents.md): Plugin factory for the agents plugin. Discovers agents from
202
202
  - [Variable: aiSearch](./docs/api/appkit/Variable.aiSearch.md)
203
203
  - [Variable: READ_ACTIONS](./docs/api/appkit/Variable.READ_ACTIONS.md): Actions that only read data.
204
204
  - [Variable: sql](./docs/api/appkit/Variable.sql.md): SQL helper namespace
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "@databricks/appkit";
3
- var version = "0.62.0";
3
+ var version = "0.64.0";
4
4
 
5
5
  //#endregion
6
6
  export { name, version };
@@ -0,0 +1,14 @@
1
+ //#region src/core/agent/agent-dirs.ts
2
+ /**
3
+ * Agent-folder names from a directory listing, sorted. An agent lives in a
4
+ * subfolder, so only real directories count — symlinks are not followed, so a
5
+ * link planted in the agents dir can't pull code from outside it. Single-sourced
6
+ * so the markdown and code loaders keep the same folder-selection policy.
7
+ */
8
+ function agentDirNames(entries) {
9
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
10
+ }
11
+
12
+ //#endregion
13
+ export { agentDirNames };
14
+ //# sourceMappingURL=agent-dirs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-dirs.js","names":[],"sources":["../../../src/core/agent/agent-dirs.ts"],"sourcesContent":["import type { Dirent } from \"node:fs\";\n\n/**\n * Agent-folder names from a directory listing, sorted. An agent lives in a\n * subfolder, so only real directories count — symlinks are not followed, so a\n * link planted in the agents dir can't pull code from outside it. Single-sourced\n * so the markdown and code loaders keep the same folder-selection policy.\n */\nexport function agentDirNames(entries: Dirent[]): string[] {\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,cAAc,SAA6B;AACzD,QAAO,QACJ,QAAQ,MAAM,EAAE,aAAa,CAAC,CAC9B,KAAK,MAAM,EAAE,KAAK,CAClB,MAAM"}
@@ -2,13 +2,11 @@ import { AgentDefinition } from "./types.js";
2
2
 
3
3
  //#region src/core/agent/create-agent.d.ts
4
4
  /**
5
- * Pure factory for agent definitions. Returns the passed-in definition after
6
- * cycle-detecting the sub-agent graph. Accepts the full `AgentDefinition` shape
7
- * and is safe to call at module top-level.
8
- *
9
- * The returned value is a plain `AgentDefinition` — no adapter construction,
10
- * no side effects. Register it with `agents({ agents: { name: def } })` or run
11
- * it standalone via `runAgent(def, input)`.
5
+ * Pure factory for agent definitions: cycle-detects the sub-agent graph and
6
+ * returns the same object, stamped with a non-enumerable {@link AGENT_BRAND}
7
+ * so discovery recognizes it. Safe at module top-level; no adapter is built.
8
+ * Don't `Object.freeze` the definition before passing it in — the brand is
9
+ * written onto the argument.
12
10
  *
13
11
  * @example
14
12
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"create-agent.d.ts","names":[],"sources":["../../../src/core/agent/create-agent.ts"],"mappings":";;;;;AAuBA;;;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,GAAA,EAAK,eAAA,GAAkB,eAAA"}
1
+ {"version":3,"file":"create-agent.d.ts","names":[],"sources":["../../../src/core/agent/create-agent.ts"],"mappings":";;;;;AAiCA;;;;;;;;;;;;;;;;iBAAgB,WAAA,CAAY,GAAA,EAAK,eAAA,GAAkB,eAAA"}
@@ -3,13 +3,22 @@ import "../../errors/index.js";
3
3
 
4
4
  //#region src/core/agent/create-agent.ts
5
5
  /**
6
- * Pure factory for agent definitions. Returns the passed-in definition after
7
- * cycle-detecting the sub-agent graph. Accepts the full `AgentDefinition` shape
8
- * and is safe to call at module top-level.
6
+ * Non-enumerable brand stamped on every {@link createAgent} result. The
7
+ * code-agent loader ({@link loadCodeAgentsFromDir}) uses it to tell a real
8
+ * agent export from any other value a module in `server/agents/` might
9
+ * export, without duck-typing or guessing from the filename.
9
10
  *
10
- * The returned value is a plain `AgentDefinition` — no adapter construction,
11
- * no side effects. Register it with `agents({ agents: { name: def } })` or run
12
- * it standalone via `runAgent(def, input)`.
11
+ * A registered (`Symbol.for`) symbol so the check still holds if two copies
12
+ * of the package end up loaded in one process — the app's agent files and
13
+ * the plugin can resolve `@databricks/appkit` independently.
14
+ */
15
+ const AGENT_BRAND = Symbol.for("appkit.agent");
16
+ /**
17
+ * Pure factory for agent definitions: cycle-detects the sub-agent graph and
18
+ * returns the same object, stamped with a non-enumerable {@link AGENT_BRAND}
19
+ * so discovery recognizes it. Safe at module top-level; no adapter is built.
20
+ * Don't `Object.freeze` the definition before passing it in — the brand is
21
+ * written onto the argument.
13
22
  *
14
23
  * @example
15
24
  * ```ts
@@ -24,9 +33,21 @@ import "../../errors/index.js";
24
33
  */
25
34
  function createAgent(def) {
26
35
  detectCycles(def);
36
+ Object.defineProperty(def, AGENT_BRAND, {
37
+ value: true,
38
+ enumerable: false,
39
+ configurable: true
40
+ });
27
41
  return def;
28
42
  }
29
43
  /**
44
+ * Type guard: true when `value` was produced by {@link createAgent}. Used by
45
+ * the code-agent loader to pick the agent export out of a discovered module.
46
+ */
47
+ function isCreatedAgent(value) {
48
+ return typeof value === "object" && value !== null && value[AGENT_BRAND] === true;
49
+ }
50
+ /**
30
51
  * Walks the `agents: { ... }` sub-agent tree via DFS and throws if a cycle is
31
52
  * found. Cycles would cause infinite recursion at tool-invocation time.
32
53
  */
@@ -45,5 +66,5 @@ function detectCycles(def) {
45
66
  }
46
67
 
47
68
  //#endregion
48
- export { createAgent };
69
+ export { createAgent, isCreatedAgent };
49
70
  //# sourceMappingURL=create-agent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-agent.js","names":[],"sources":["../../../src/core/agent/create-agent.ts"],"sourcesContent":["import { ConfigurationError } from \"../../errors\";\nimport type { AgentDefinition } from \"./types\";\n\n/**\n * Pure factory for agent definitions. Returns the passed-in definition after\n * cycle-detecting the sub-agent graph. Accepts the full `AgentDefinition` shape\n * and is safe to call at module top-level.\n *\n * The returned value is a plain `AgentDefinition` — no adapter construction,\n * no side effects. Register it with `agents({ agents: { name: def } })` or run\n * it standalone via `runAgent(def, input)`.\n *\n * @example\n * ```ts\n * const support = createAgent({\n * instructions: \"You help customers.\",\n * model: \"databricks-claude-sonnet-4-5\",\n * tools: {\n * get_weather: tool({ ... }),\n * },\n * });\n * ```\n */\nexport function createAgent(def: AgentDefinition): AgentDefinition {\n detectCycles(def);\n return def;\n}\n\n/**\n * Walks the `agents: { ... }` sub-agent tree via DFS and throws if a cycle is\n * found. Cycles would cause infinite recursion at tool-invocation time.\n */\nfunction detectCycles(def: AgentDefinition): void {\n const visiting = new Set<AgentDefinition>();\n const visited = new Set<AgentDefinition>();\n\n const walk = (current: AgentDefinition, path: string[]): void => {\n if (visited.has(current)) return;\n if (visiting.has(current)) {\n throw new ConfigurationError(\n `Agent sub-agent cycle detected: ${path.join(\" -> \")}`,\n );\n }\n visiting.add(current);\n for (const [childKey, child] of Object.entries(current.agents ?? {})) {\n walk(child, [...path, childKey]);\n }\n visiting.delete(current);\n visited.add(current);\n };\n\n walk(def, [def.name ?? \"(root)\"]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,YAAY,KAAuC;AACjE,cAAa,IAAI;AACjB,QAAO;;;;;;AAOT,SAAS,aAAa,KAA4B;CAChD,MAAM,2BAAW,IAAI,KAAsB;CAC3C,MAAM,0BAAU,IAAI,KAAsB;CAE1C,MAAM,QAAQ,SAA0B,SAAyB;AAC/D,MAAI,QAAQ,IAAI,QAAQ,CAAE;AAC1B,MAAI,SAAS,IAAI,QAAQ,CACvB,OAAM,IAAI,mBACR,mCAAmC,KAAK,KAAK,OAAO,GACrD;AAEH,WAAS,IAAI,QAAQ;AACrB,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,QAAQ,UAAU,EAAE,CAAC,CAClE,MAAK,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC;AAElC,WAAS,OAAO,QAAQ;AACxB,UAAQ,IAAI,QAAQ;;AAGtB,MAAK,KAAK,CAAC,IAAI,QAAQ,SAAS,CAAC"}
1
+ {"version":3,"file":"create-agent.js","names":[],"sources":["../../../src/core/agent/create-agent.ts"],"sourcesContent":["import { ConfigurationError } from \"../../errors\";\nimport type { AgentDefinition } from \"./types\";\n\n/**\n * Non-enumerable brand stamped on every {@link createAgent} result. The\n * code-agent loader ({@link loadCodeAgentsFromDir}) uses it to tell a real\n * agent export from any other value a module in `server/agents/` might\n * export, without duck-typing or guessing from the filename.\n *\n * A registered (`Symbol.for`) symbol so the check still holds if two copies\n * of the package end up loaded in one process — the app's agent files and\n * the plugin can resolve `@databricks/appkit` independently.\n */\nconst AGENT_BRAND: unique symbol = Symbol.for(\"appkit.agent\");\n\n/**\n * Pure factory for agent definitions: cycle-detects the sub-agent graph and\n * returns the same object, stamped with a non-enumerable {@link AGENT_BRAND}\n * so discovery recognizes it. Safe at module top-level; no adapter is built.\n * Don't `Object.freeze` the definition before passing it in — the brand is\n * written onto the argument.\n *\n * @example\n * ```ts\n * const support = createAgent({\n * instructions: \"You help customers.\",\n * model: \"databricks-claude-sonnet-4-5\",\n * tools: {\n * get_weather: tool({ ... }),\n * },\n * });\n * ```\n */\nexport function createAgent(def: AgentDefinition): AgentDefinition {\n detectCycles(def);\n // Non-enumerable + in-place: identity, JSON, and spread are unaffected.\n Object.defineProperty(def, AGENT_BRAND, {\n value: true,\n enumerable: false,\n configurable: true,\n });\n return def;\n}\n\n/**\n * Type guard: true when `value` was produced by {@link createAgent}. Used by\n * the code-agent loader to pick the agent export out of a discovered module.\n */\nexport function isCreatedAgent(value: unknown): value is AgentDefinition {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as Record<PropertyKey, unknown>)[AGENT_BRAND] === true\n );\n}\n\n/**\n * Walks the `agents: { ... }` sub-agent tree via DFS and throws if a cycle is\n * found. Cycles would cause infinite recursion at tool-invocation time.\n */\nfunction detectCycles(def: AgentDefinition): void {\n const visiting = new Set<AgentDefinition>();\n const visited = new Set<AgentDefinition>();\n\n const walk = (current: AgentDefinition, path: string[]): void => {\n if (visited.has(current)) return;\n if (visiting.has(current)) {\n throw new ConfigurationError(\n `Agent sub-agent cycle detected: ${path.join(\" -> \")}`,\n );\n }\n visiting.add(current);\n for (const [childKey, child] of Object.entries(current.agents ?? {})) {\n walk(child, [...path, childKey]);\n }\n visiting.delete(current);\n visited.add(current);\n };\n\n walk(def, [def.name ?? \"(root)\"]);\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAM,cAA6B,OAAO,IAAI,eAAe;;;;;;;;;;;;;;;;;;;AAoB7D,SAAgB,YAAY,KAAuC;AACjE,cAAa,IAAI;AAEjB,QAAO,eAAe,KAAK,aAAa;EACtC,OAAO;EACP,YAAY;EACZ,cAAc;EACf,CAAC;AACF,QAAO;;;;;;AAOT,SAAgB,eAAe,OAA0C;AACvE,QACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAuC,iBAAiB;;;;;;AAQ7D,SAAS,aAAa,KAA4B;CAChD,MAAM,2BAAW,IAAI,KAAsB;CAC3C,MAAM,0BAAU,IAAI,KAAsB;CAE1C,MAAM,QAAQ,SAA0B,SAAyB;AAC/D,MAAI,QAAQ,IAAI,QAAQ,CAAE;AAC1B,MAAI,SAAS,IAAI,QAAQ,CACvB,OAAM,IAAI,mBACR,mCAAmC,KAAK,KAAK,OAAO,GACrD;AAEH,WAAS,IAAI,QAAQ;AACrB,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,QAAQ,UAAU,EAAE,CAAC,CAClE,MAAK,OAAO,CAAC,GAAG,MAAM,SAAS,CAAC;AAElC,WAAS,OAAO,QAAQ;AACxB,UAAQ,IAAI,QAAQ;;AAGtB,MAAK,KAAK,CAAC,IAAI,QAAQ,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"load-agents.d.ts","names":[],"sources":["../../../src/core/agent/load-agents.ts"],"mappings":";;;;;UAoBU,eAAA;EACR,OAAA,GAAU,IAAA,GAAO,cAAA,KAAmB,MAAA;AAAA;AAAA,UAGrB,WAAA;EAJQ;EAMvB,YAAA,GAAe,YAAA,GAAe,OAAA,CAAQ,YAAA;EALI;EAO1C,cAAA,GAAiB,MAAA,SAAe,SAAA;EAPf;;;;;AAGnB;EAWE,OAAA,GAAU,GAAA,SAAY,eAAA;;;;;;;;EAQtB,UAAA,GAAa,MAAA,SAAe,eAAA;AAAA;AAAA,UAGb,UAAA;EAHI;EAKnB,IAAA,EAAM,MAAA,SAAe,eAAA;EAtBrB;EAwBA,YAAA;AAAA;;;;;;iBAgEc,uBAAA,CAAwB,QAAA;;;;;;;;;iBA+BlB,iBAAA,CACpB,QAAA,UACA,GAAA,EAAK,WAAA,GACJ,OAAA,CAAQ,eAAA;;;;;;;;;;;;;;;;;;;iBAgCW,iBAAA,CACpB,GAAA,UACA,GAAA,EAAK,WAAA,GACJ,OAAA,CAAQ,UAAA"}
1
+ {"version":3,"file":"load-agents.d.ts","names":[],"sources":["../../../src/core/agent/load-agents.ts"],"mappings":";;;;;UAqBU,eAAA;EACR,OAAA,GAAU,IAAA,GAAO,cAAA,KAAmB,MAAA;AAAA;AAAA,UAGrB,WAAA;EAJQ;EAMvB,YAAA,GAAe,YAAA,GAAe,OAAA,CAAQ,YAAA;EALI;EAO1C,cAAA,GAAiB,MAAA,SAAe,SAAA;EAPf;;;;;AAGnB;EAWE,OAAA,GAAU,GAAA,SAAY,eAAA;;;;;;;;EAQtB,UAAA,GAAa,MAAA,SAAe,eAAA;AAAA;AAAA,UAGb,UAAA;EAHI;EAKnB,IAAA,EAAM,MAAA,SAAe,eAAA;EAtBrB;EAwBA,YAAA;AAAA;;;;;;iBAgEc,uBAAA,CAAwB,QAAA;;;;;;;;;iBA+BlB,iBAAA,CACpB,QAAA,UACA,GAAA,EAAK,WAAA,GACJ,OAAA,CAAQ,eAAA;;;;;;;;;;;;;;;;;;;iBAgCW,iBAAA,CACpB,GAAA,UACA,GAAA,EAAK,WAAA,GACJ,OAAA,CAAQ,UAAA"}
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from "../../logging/logger.js";
2
2
  import { isToolkitEntry } from "./types.js";
3
+ import { agentDirNames } from "./agent-dirs.js";
3
4
  import fs from "node:fs/promises";
4
5
  import path from "node:path";
5
6
  import yaml from "js-yaml";
@@ -80,9 +81,7 @@ async function loadAgentsFromDir(dir, ctx) {
80
81
  const hint = orphanMd.map((f) => `${path.basename(f, ".md")}/agent.md`).join(", ");
81
82
  throw new Error(`Agents directory contains unsupported top-level markdown file(s): ${orphanMd.join(", ")}. Use one folder per agent with a fixed entry file, e.g. ${hint}.`);
82
83
  }
83
- /** Reserved folder name until per-agent skills land; not an agent package. */
84
- const RESERVED_DIRS = new Set(["skills"]);
85
- const agentIds = entries.filter((e) => e.isDirectory()).map((e) => e.name).filter((name) => !RESERVED_DIRS.has(name)).sort();
84
+ const agentIds = agentDirNames(entries);
86
85
  const defs = {};
87
86
  const subAgentRefs = {};
88
87
  let defaultAgent = null;
@@ -92,7 +91,8 @@ async function loadAgentsFromDir(dir, ctx) {
92
91
  try {
93
92
  raw = await fs.readFile(agentPath, "utf-8");
94
93
  } catch (err) {
95
- if (err.code === "ENOENT") throw new Error(`Agents subdirectory '${path.join(dir, id)}' must contain agent.md.`);
94
+ const code = err.code;
95
+ if (code === "ENOENT" || code === "ENOTDIR") continue;
96
96
  throw err;
97
97
  }
98
98
  defs[id] = buildDefinition(id, raw, agentPath, ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"load-agents.js","names":[],"sources":["../../../src/core/agent/load-agents.ts"],"sourcesContent":["import type { Dirent } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport yaml from \"js-yaml\";\nimport type { AgentAdapter } from \"shared\";\n\nimport type { GenerationParams } from \"../../agents/databricks\";\nimport type {\n AgentDefinition,\n AgentTool,\n BaseSystemPromptOption,\n ToolkitEntry,\n ToolkitOptions,\n} from \"../../core/agent/types\";\nimport { isToolkitEntry } from \"../../core/agent/types\";\nimport { createLogger } from \"../../logging/logger\";\n\nconst logger = createLogger(\"agents:loader\");\n\ninterface ToolkitProvider {\n toolkit: (opts?: ToolkitOptions) => Record<string, unknown>;\n}\n\nexport interface LoadContext {\n /** Default model when frontmatter has no `endpoint` and the def has no `model`. */\n defaultModel?: AgentAdapter | Promise<AgentAdapter> | string;\n /** Ambient tool library referenced by frontmatter `tools: [key1, key2]`. */\n availableTools?: Record<string, AgentTool>;\n /**\n * Registered plugin toolkits referenced by `plugin:NAME` entries in the\n * unified `tools:` frontmatter list. Keyed by plugin name; each value\n * exposes the same `toolkit(opts?)` surface as the `plugins` argument to\n * `tools(plugins) => Record<...>` in the code form.\n */\n plugins?: Map<string, ToolkitProvider>;\n /**\n * Code-defined agents contributed by `agents({ agents: { ... } })`. The\n * directory loader resolves `agents:` frontmatter references against\n * these alongside sibling markdown files, so a markdown parent can\n * delegate to a code-defined child. Code-defined names win on collision\n * with markdown names, matching the plugin's top-level merge precedence.\n */\n codeAgents?: Record<string, AgentDefinition>;\n}\n\nexport interface LoadResult {\n /** Agent definitions keyed by agent id (directory name under `dir`). */\n defs: Record<string, AgentDefinition>;\n /** First agent with `default: true` frontmatter (sorted id order), or `null`. */\n defaultAgent: string | null;\n}\n\ninterface Frontmatter {\n endpoint?: string;\n model?: string;\n /**\n * Unified tool list. Each entry is one of:\n *\n * - **`plugin:<name>`** (string) — pull every tool from the named plugin.\n * - **`plugin:<name>: [tool1, tool2]`** — pull only the listed tools\n * (shorthand for `{ only: [...] }`).\n * - **`plugin:<name>: { ...ToolkitOptions }`** — pass full\n * `prefix` / `only` / `except` / `rename` options.\n * - **`<key>`** (string, no `plugin:` prefix) — ambient tool name\n * resolved against the `agents({ tools: { ... } })` config.\n *\n * Mirrors the TS function form `tools(plugins) { ... }` where plugin\n * tools and inline tools live in the same record.\n */\n tools?: FrontmatterToolEntry[];\n /**\n * Other agent ids to expose as sub-agents. Each becomes an `agent-<id>`\n * tool at runtime. Resolution happens at directory-load time in\n * {@link loadAgentsFromDir}; the single-file {@link loadAgentFromFile} path\n * rejects non-empty values since there are no siblings to resolve against.\n */\n agents?: string[];\n maxSteps?: number;\n maxTokens?: number;\n /**\n * Optional OpenAI-compatible generation params forwarded to the serving\n * request body (`temperature`, `top_p`, `stop`, `frequency_penalty`,\n * `presence_penalty`). Parsed defensively in {@link buildDefinition}.\n */\n generationParams?: Record<string, unknown>;\n default?: boolean;\n baseSystemPrompt?: false | string;\n ephemeral?: boolean;\n}\n\n/**\n * Each item in {@link Frontmatter.tools}. Strings are either ambient tool\n * names (no prefix) or bare plugin references (`plugin:NAME`). Objects are\n * single-key mappings whose key is `plugin:NAME` and whose value is either\n * an array of local tool names (sugar for `{ only: [...] }`) or a full\n * `ToolkitOptions` record.\n *\n * Named `FrontmatterToolEntry` to avoid colliding with the exported\n * `ToolEntry` from `tools/define-tool.ts` — that is the plugin-author API\n * surface (`defineTool({ ... }) : ToolEntry`); this is the frontmatter\n * parse type. They are unrelated and live in different layers.\n */\ntype FrontmatterToolEntry =\n | string\n | { [key: string]: ToolkitOptions | string[] };\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\n/**\n * Derives the logical agent id from a markdown path. When the file is named\n * `agent.md`, the id is the parent directory name (folder-based layout);\n * otherwise the id is the file stem (e.g. legacy single-file paths).\n */\nexport function agentIdFromMarkdownPath(filePath: string): string {\n const normalized = path.normalize(filePath);\n const base = path.basename(normalized);\n const parent = path.basename(path.dirname(normalized));\n if (base === \"agent.md\" && parent && parent !== \".\" && parent !== \"..\") {\n return parent;\n }\n return path.basename(normalized, \".md\");\n}\n\nconst ALLOWED_KEYS = new Set([\n \"endpoint\",\n \"model\",\n \"tools\",\n \"agents\",\n \"maxSteps\",\n \"maxTokens\",\n \"generationParams\",\n \"default\",\n \"baseSystemPrompt\",\n \"ephemeral\",\n]);\n\n/**\n * Loads a single markdown agent file and resolves its frontmatter against\n * registered plugin toolkits + ambient tool library.\n *\n * Rejects non-empty `agents:` frontmatter because single-file loads have\n * no siblings to resolve sub-agent references against — callers must use\n * {@link loadAgentsFromDir} when markdown agents delegate to one another.\n */\nexport async function loadAgentFromFile(\n filePath: string,\n ctx: LoadContext,\n): Promise<AgentDefinition> {\n const raw = await fs.readFile(filePath, \"utf-8\");\n const name = agentIdFromMarkdownPath(filePath);\n const { data } = parseFrontmatter(raw, filePath);\n if (Array.isArray(data?.agents) && data.agents.length > 0) {\n throw new Error(\n `Agent '${name}' (${filePath}) declares 'agents:' in frontmatter, ` +\n `which requires loadAgentsFromDir to resolve sibling references. ` +\n `Use loadAgentsFromDir, or wire sub-agents in code via createAgent({ agents: { ... } }).`,\n );\n }\n return buildDefinition(name, raw, filePath, ctx);\n}\n\n/**\n * Scans a directory for one subdirectory per agent, each containing\n * `agent.md` (frontmatter + body). Produces an `AgentDefinition` record keyed\n * by agent id (folder name). Throws on frontmatter errors or unresolved\n * references. Returns an empty map if the directory does not exist.\n *\n * Legacy top-level `*.md` files are rejected with an error — migrate each to\n * `<id>/agent.md` under a sibling folder named for the agent id.\n *\n * Runs in two passes so sub-agent references in frontmatter (`agents: [...]`)\n * can be resolved regardless of directory iteration order:\n *\n * 1. Build every agent's definition from its own `agent.md`.\n * 2. Walk `agents:` references and wire `def.agents = { child: childDef }`\n * by looking them up in the complete map. Dangling names and\n * self-references fail loudly; mutual delegation is allowed and bounded\n * at runtime by `limits.maxSubAgentDepth`.\n */\nexport async function loadAgentsFromDir(\n dir: string,\n ctx: LoadContext,\n): Promise<LoadResult> {\n let entries: Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { defs: {}, defaultAgent: null };\n }\n throw err;\n }\n const orphanMd = entries\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => e.name)\n .sort();\n\n if (orphanMd.length > 0) {\n const hint = orphanMd\n .map((f) => `${path.basename(f, \".md\")}/agent.md`)\n .join(\", \");\n throw new Error(\n `Agents directory contains unsupported top-level markdown file(s): ${orphanMd.join(\", \")}. ` +\n `Use one folder per agent with a fixed entry file, e.g. ${hint}.`,\n );\n }\n\n /** Reserved folder name until per-agent skills land; not an agent package. */\n const RESERVED_DIRS = new Set([\"skills\"]);\n\n const agentIds = entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .filter((name) => !RESERVED_DIRS.has(name))\n .sort();\n\n const defs: Record<string, AgentDefinition> = {};\n const subAgentRefs: Record<string, string[]> = {};\n let defaultAgent: string | null = null;\n\n // Pass 1: build every agent's definition; collect sub-agent refs.\n for (const id of agentIds) {\n const agentPath = path.join(dir, id, \"agent.md\");\n let raw: string;\n try {\n raw = await fs.readFile(agentPath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new Error(\n `Agents subdirectory '${path.join(dir, id)}' must contain agent.md.`,\n );\n }\n throw err;\n }\n defs[id] = buildDefinition(id, raw, agentPath, ctx);\n const { data } = parseFrontmatter(raw, agentPath);\n if (data?.agents !== undefined) {\n subAgentRefs[id] = normalizeAgentsFrontmatter(data.agents, id, agentPath);\n }\n if (data?.default === true && !defaultAgent) {\n defaultAgent = id;\n }\n }\n\n // Pass 2: resolve sub-agent references against the complete defs map.\n // Code-defined agents (ctx.codeAgents) take precedence over markdown ones\n // with the same name, matching the plugin's top-level merge behaviour.\n for (const [name, refs] of Object.entries(subAgentRefs)) {\n if (refs.length === 0) continue;\n const children: Record<string, AgentDefinition> = {};\n const missing: string[] = [];\n for (const ref of refs) {\n if (ref === name) {\n throw new Error(\n `Agent '${name}' (${path.join(dir, name, \"agent.md\")}) cannot reference itself in 'agents:'.`,\n );\n }\n const sibling = ctx.codeAgents?.[ref] ?? defs[ref];\n if (!sibling) {\n missing.push(ref);\n continue;\n }\n children[ref] = sibling;\n }\n if (missing.length > 0) {\n const available =\n [...Object.keys(ctx.codeAgents ?? {}), ...Object.keys(defs)]\n .sort()\n .join(\", \") || \"<none>\";\n throw new Error(\n `Agent '${name}' references sub-agent(s) '${missing.join(\", \")}' in 'agents:', ` +\n `but no markdown or code agent(s) with those names exist. ` +\n `Available: ${available}.`,\n );\n }\n defs[name].agents = children;\n }\n\n return { defs, defaultAgent };\n}\n\n/**\n * Validates that `agents:` frontmatter is an array of non-empty strings and\n * returns it with duplicates removed. Throws with a clear per-file message\n * on malformed input rather than silently ignoring.\n */\nfunction normalizeAgentsFrontmatter(\n value: unknown,\n agentName: string,\n filePath: string,\n): string[] {\n if (!Array.isArray(value)) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has invalid 'agents:' frontmatter: ` +\n `expected an array of sibling agent ids, got ${typeof value}.`,\n );\n }\n const out: string[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n if (typeof item !== \"string\" || item.trim() === \"\") {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has invalid 'agents:' entry: ` +\n `expected non-empty string, got ${JSON.stringify(item)}.`,\n );\n }\n if (seen.has(item)) continue;\n seen.add(item);\n out.push(item);\n }\n return out;\n}\n\n/** Exposed for tests. Parses `--- yaml ---\\nbody` and validates frontmatter keys. */\nexport function parseFrontmatter(\n raw: string,\n sourcePath?: string,\n): { data: Frontmatter | null; content: string } {\n const match = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!match) {\n return { data: null, content: raw.trim() };\n }\n let parsed: unknown;\n try {\n parsed = yaml.load(match[1]);\n } catch (err) {\n const src = sourcePath ? ` (${sourcePath})` : \"\";\n throw new Error(\n `Invalid YAML frontmatter${src}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (parsed === null || parsed === undefined) {\n return { data: {}, content: match[2].trim() };\n }\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n const src = sourcePath ? ` (${sourcePath})` : \"\";\n throw new Error(`Frontmatter must be a YAML object${src}`);\n }\n const data = parsed as Record<string, unknown>;\n for (const key of Object.keys(data)) {\n if (!ALLOWED_KEYS.has(key)) {\n logger.warn(\n \"Ignoring unknown frontmatter key '%s' in %s\",\n key,\n sourcePath ?? \"<inline>\",\n );\n }\n }\n return { data: data as Frontmatter, content: match[2].trim() };\n}\n\nconst isNumber = (v: unknown): v is number => typeof v === \"number\";\n\n/**\n * Per-key validators for {@link GenerationParams} frontmatter. Keyed by wire\n * name and typed as `Record<keyof GenerationParams, ...>`, so a new param on\n * the interface is a compile error until it gets an entry here — parsing,\n * unknown-key detection, and warnings all derive from this one table.\n */\nconst GENERATION_PARAM_SPECS: Record<\n keyof GenerationParams,\n { label: string; valid: (v: unknown) => boolean }\n> = {\n temperature: { label: \"number\", valid: isNumber },\n top_p: { label: \"number\", valid: isNumber },\n frequency_penalty: { label: \"number\", valid: isNumber },\n presence_penalty: { label: \"number\", valid: isNumber },\n stop: {\n label: \"string or string[]\",\n valid: (v) =>\n typeof v === \"string\" ||\n (Array.isArray(v) && v.every((s) => typeof s === \"string\")),\n },\n};\n\n/**\n * Defensively maps a frontmatter `generationParams` map to {@link GenerationParams}.\n * Picks only known keys with the expected wire types. A key present with the\n * wrong type (e.g. `temperature: \"0.5\"`) or an unknown key (e.g. `top-p`) is\n * dropped and logged at warn level, so a silently-ignored param is visible\n * rather than mistaken for \"applied\". Returns `undefined` when no valid key is\n * present.\n */\nfunction parseGenerationParams(\n value: unknown,\n sourcePath?: string,\n): GenerationParams | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return undefined;\n }\n const raw = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n const where = sourcePath ?? \"<inline>\";\n\n for (const [key, v] of Object.entries(raw)) {\n const spec = GENERATION_PARAM_SPECS[key as keyof GenerationParams];\n if (!spec) {\n logger.warn(\n \"Ignoring unknown generationParams key '%s' in %s\",\n key,\n where,\n );\n } else if (spec.valid(v)) {\n out[key] = v;\n } else {\n logger.warn(\n \"Ignoring generationParams.%s in %s: expected %s, got %s\",\n key,\n where,\n spec.label,\n typeof v,\n );\n }\n }\n\n return Object.keys(out).length > 0 ? (out as GenerationParams) : undefined;\n}\n\nfunction buildDefinition(\n name: string,\n raw: string,\n filePath: string,\n ctx: LoadContext,\n): AgentDefinition {\n const { data, content } = parseFrontmatter(raw, filePath);\n const fm: Frontmatter = data ?? {};\n\n const tools = resolveFrontmatterTools(name, fm, filePath, ctx);\n const model = fm.model ?? fm.endpoint ?? ctx.defaultModel;\n\n let baseSystemPrompt: BaseSystemPromptOption | undefined;\n if (fm.baseSystemPrompt === false) baseSystemPrompt = false;\n else if (typeof fm.baseSystemPrompt === \"string\")\n baseSystemPrompt = fm.baseSystemPrompt;\n\n return {\n name,\n instructions: content,\n model,\n tools: Object.keys(tools).length > 0 ? tools : undefined,\n maxSteps: typeof fm.maxSteps === \"number\" ? fm.maxSteps : undefined,\n maxTokens: typeof fm.maxTokens === \"number\" ? fm.maxTokens : undefined,\n generationParams: parseGenerationParams(fm.generationParams, filePath),\n baseSystemPrompt,\n ephemeral: typeof fm.ephemeral === \"boolean\" ? fm.ephemeral : undefined,\n };\n}\n\nfunction resolveFrontmatterTools(\n agentName: string,\n fm: Frontmatter,\n filePath: string,\n ctx: LoadContext,\n): Record<string, AgentTool> {\n const out: Record<string, AgentTool> = {};\n const pluginIdx = ctx.plugins ?? new Map<string, ToolkitProvider>();\n\n for (const entry of fm.tools ?? []) {\n const parsed = parseToolEntry(entry, filePath, agentName);\n if (parsed.kind === \"plugin\") {\n const provider = pluginIdx.get(parsed.pluginName);\n if (!provider) {\n const available =\n pluginIdx.size > 0\n ? Array.from(pluginIdx.keys()).join(\", \")\n : \"<none>\";\n throw new Error(\n `Agent '${agentName}' (${filePath}) references 'plugin:${parsed.pluginName}', but plugin '${parsed.pluginName}' is not registered. Available: ${available}`,\n );\n }\n const entries = provider.toolkit(parsed.opts) as Record<string, unknown>;\n for (const [key, value] of Object.entries(entries)) {\n if (!isToolkitEntry(value)) {\n throw new Error(\n `Plugin '${parsed.pluginName}'.toolkit() returned a value at key '${key}' that is not a ToolkitEntry`,\n );\n }\n out[key] = value as ToolkitEntry;\n }\n } else {\n const tool = ctx.availableTools?.[parsed.toolName];\n if (!tool) {\n const available = ctx.availableTools\n ? Object.keys(ctx.availableTools).join(\", \")\n : \"<none>\";\n throw new Error(\n `Agent '${agentName}' (${filePath}) references ambient tool '${parsed.toolName}', which is not in the agents() plugin's tools field. Available: ${available}. ` +\n \"If you meant to reference a plugin, use the 'plugin:NAME' prefix.\",\n );\n }\n out[parsed.toolName] = tool;\n }\n }\n\n return out;\n}\n\ntype ParsedToolEntry =\n | { kind: \"plugin\"; pluginName: string; opts: ToolkitOptions | undefined }\n | { kind: \"ambient\"; toolName: string };\n\n/**\n * Classify one item in the `tools:` frontmatter list into either a plugin\n * reference (with optional ToolkitOptions) or an ambient tool lookup.\n *\n * Strings starting with `plugin:` are bare plugin references. Strings\n * without the prefix are ambient tool names. Object entries are\n * single-key mappings keyed by `plugin:NAME`; the value is either an\n * array (sugar for `{ only: [...] }`) or a full `ToolkitOptions` record.\n */\nfunction parseToolEntry(\n entry: FrontmatterToolEntry,\n filePath: string,\n agentName: string,\n): ParsedToolEntry {\n if (typeof entry === \"string\") {\n if (entry.startsWith(PLUGIN_PREFIX)) {\n const pluginName = entry.slice(PLUGIN_PREFIX.length);\n if (pluginName.length === 0) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty plugin name in 'plugin:'.`,\n );\n }\n return { kind: \"plugin\", pluginName, opts: undefined };\n }\n if (entry.length === 0) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty string in 'tools:'.`,\n );\n }\n return { kind: \"ambient\", toolName: entry };\n }\n if (typeof entry !== \"object\" || entry === null) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has invalid 'tools:' entry: ${JSON.stringify(entry)}`,\n );\n }\n const keys = Object.keys(entry);\n if (keys.length !== 1) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) 'tools:' object entry must have exactly one key, got: ${keys.join(\", \")}`,\n );\n }\n const key = keys[0];\n // Bare `- plugin:` (no name after the colon) parses as a mapping with the\n // key `\"plugin\"`. Catch that as a friendly error rather than dumping it\n // through the generic \"expected key 'plugin:NAME'\" branch.\n if (key === \"plugin\") {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty plugin name in 'plugin:'.`,\n );\n }\n if (!key.startsWith(PLUGIN_PREFIX)) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) 'tools:' object entries are reserved for plugin references; expected key 'plugin:NAME', got '${key}'. ` +\n \"Use a bare string for ambient tools (e.g. `- get_weather`).\",\n );\n }\n const pluginName = key.slice(PLUGIN_PREFIX.length);\n if (pluginName.length === 0) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty plugin name in 'plugin:'.`,\n );\n }\n const value = entry[key];\n if (Array.isArray(value)) {\n return { kind: \"plugin\", pluginName, opts: { only: value } };\n }\n if (typeof value === \"object\" && value !== null) {\n return {\n kind: \"plugin\",\n pluginName,\n opts: value as ToolkitOptions,\n };\n }\n throw new Error(\n `Agent '${agentName}' (${filePath}) 'plugin:${pluginName}' options must be an array of tool names or a ToolkitOptions object.`,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"load-agents.js","names":[],"sources":["../../../src/core/agent/load-agents.ts"],"sourcesContent":["import type { Dirent } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport yaml from \"js-yaml\";\nimport type { AgentAdapter } from \"shared\";\n\nimport type { GenerationParams } from \"../../agents/databricks\";\nimport type {\n AgentDefinition,\n AgentTool,\n BaseSystemPromptOption,\n ToolkitEntry,\n ToolkitOptions,\n} from \"../../core/agent/types\";\nimport { isToolkitEntry } from \"../../core/agent/types\";\nimport { createLogger } from \"../../logging/logger\";\nimport { agentDirNames } from \"./agent-dirs\";\n\nconst logger = createLogger(\"agents:loader\");\n\ninterface ToolkitProvider {\n toolkit: (opts?: ToolkitOptions) => Record<string, unknown>;\n}\n\nexport interface LoadContext {\n /** Default model when frontmatter has no `endpoint` and the def has no `model`. */\n defaultModel?: AgentAdapter | Promise<AgentAdapter> | string;\n /** Ambient tool library referenced by frontmatter `tools: [key1, key2]`. */\n availableTools?: Record<string, AgentTool>;\n /**\n * Registered plugin toolkits referenced by `plugin:NAME` entries in the\n * unified `tools:` frontmatter list. Keyed by plugin name; each value\n * exposes the same `toolkit(opts?)` surface as the `plugins` argument to\n * `tools(plugins) => Record<...>` in the code form.\n */\n plugins?: Map<string, ToolkitProvider>;\n /**\n * Code-defined agents contributed by `agents({ agents: { ... } })`. The\n * directory loader resolves `agents:` frontmatter references against\n * these alongside sibling markdown files, so a markdown parent can\n * delegate to a code-defined child. Code-defined names win on collision\n * with markdown names, matching the plugin's top-level merge precedence.\n */\n codeAgents?: Record<string, AgentDefinition>;\n}\n\nexport interface LoadResult {\n /** Agent definitions keyed by agent id (directory name under `dir`). */\n defs: Record<string, AgentDefinition>;\n /** First agent with `default: true` frontmatter (sorted id order), or `null`. */\n defaultAgent: string | null;\n}\n\ninterface Frontmatter {\n endpoint?: string;\n model?: string;\n /**\n * Unified tool list. Each entry is one of:\n *\n * - **`plugin:<name>`** (string) — pull every tool from the named plugin.\n * - **`plugin:<name>: [tool1, tool2]`** — pull only the listed tools\n * (shorthand for `{ only: [...] }`).\n * - **`plugin:<name>: { ...ToolkitOptions }`** — pass full\n * `prefix` / `only` / `except` / `rename` options.\n * - **`<key>`** (string, no `plugin:` prefix) — ambient tool name\n * resolved against the `agents({ tools: { ... } })` config.\n *\n * Mirrors the TS function form `tools(plugins) { ... }` where plugin\n * tools and inline tools live in the same record.\n */\n tools?: FrontmatterToolEntry[];\n /**\n * Other agent ids to expose as sub-agents. Each becomes an `agent-<id>`\n * tool at runtime. Resolution happens at directory-load time in\n * {@link loadAgentsFromDir}; the single-file {@link loadAgentFromFile} path\n * rejects non-empty values since there are no siblings to resolve against.\n */\n agents?: string[];\n maxSteps?: number;\n maxTokens?: number;\n /**\n * Optional OpenAI-compatible generation params forwarded to the serving\n * request body (`temperature`, `top_p`, `stop`, `frequency_penalty`,\n * `presence_penalty`). Parsed defensively in {@link buildDefinition}.\n */\n generationParams?: Record<string, unknown>;\n default?: boolean;\n baseSystemPrompt?: false | string;\n ephemeral?: boolean;\n}\n\n/**\n * Each item in {@link Frontmatter.tools}. Strings are either ambient tool\n * names (no prefix) or bare plugin references (`plugin:NAME`). Objects are\n * single-key mappings whose key is `plugin:NAME` and whose value is either\n * an array of local tool names (sugar for `{ only: [...] }`) or a full\n * `ToolkitOptions` record.\n *\n * Named `FrontmatterToolEntry` to avoid colliding with the exported\n * `ToolEntry` from `tools/define-tool.ts` — that is the plugin-author API\n * surface (`defineTool({ ... }) : ToolEntry`); this is the frontmatter\n * parse type. They are unrelated and live in different layers.\n */\ntype FrontmatterToolEntry =\n | string\n | { [key: string]: ToolkitOptions | string[] };\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\n/**\n * Derives the logical agent id from a markdown path. When the file is named\n * `agent.md`, the id is the parent directory name (folder-based layout);\n * otherwise the id is the file stem (e.g. legacy single-file paths).\n */\nexport function agentIdFromMarkdownPath(filePath: string): string {\n const normalized = path.normalize(filePath);\n const base = path.basename(normalized);\n const parent = path.basename(path.dirname(normalized));\n if (base === \"agent.md\" && parent && parent !== \".\" && parent !== \"..\") {\n return parent;\n }\n return path.basename(normalized, \".md\");\n}\n\nconst ALLOWED_KEYS = new Set([\n \"endpoint\",\n \"model\",\n \"tools\",\n \"agents\",\n \"maxSteps\",\n \"maxTokens\",\n \"generationParams\",\n \"default\",\n \"baseSystemPrompt\",\n \"ephemeral\",\n]);\n\n/**\n * Loads a single markdown agent file and resolves its frontmatter against\n * registered plugin toolkits + ambient tool library.\n *\n * Rejects non-empty `agents:` frontmatter because single-file loads have\n * no siblings to resolve sub-agent references against — callers must use\n * {@link loadAgentsFromDir} when markdown agents delegate to one another.\n */\nexport async function loadAgentFromFile(\n filePath: string,\n ctx: LoadContext,\n): Promise<AgentDefinition> {\n const raw = await fs.readFile(filePath, \"utf-8\");\n const name = agentIdFromMarkdownPath(filePath);\n const { data } = parseFrontmatter(raw, filePath);\n if (Array.isArray(data?.agents) && data.agents.length > 0) {\n throw new Error(\n `Agent '${name}' (${filePath}) declares 'agents:' in frontmatter, ` +\n `which requires loadAgentsFromDir to resolve sibling references. ` +\n `Use loadAgentsFromDir, or wire sub-agents in code via createAgent({ agents: { ... } }).`,\n );\n }\n return buildDefinition(name, raw, filePath, ctx);\n}\n\n/**\n * Scans a directory for one subdirectory per agent, each containing\n * `agent.md` (frontmatter + body). Produces an `AgentDefinition` record keyed\n * by agent id (folder name). Throws on frontmatter errors or unresolved\n * references. Returns an empty map if the directory does not exist.\n *\n * Legacy top-level `*.md` files are rejected with an error — migrate each to\n * `<id>/agent.md` under a sibling folder named for the agent id.\n *\n * Runs in two passes so sub-agent references in frontmatter (`agents: [...]`)\n * can be resolved regardless of directory iteration order:\n *\n * 1. Build every agent's definition from its own `agent.md`.\n * 2. Walk `agents:` references and wire `def.agents = { child: childDef }`\n * by looking them up in the complete map. Dangling names and\n * self-references fail loudly; mutual delegation is allowed and bounded\n * at runtime by `limits.maxSubAgentDepth`.\n */\nexport async function loadAgentsFromDir(\n dir: string,\n ctx: LoadContext,\n): Promise<LoadResult> {\n let entries: Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { defs: {}, defaultAgent: null };\n }\n throw err;\n }\n const orphanMd = entries\n .filter((e) => e.isFile() && e.name.endsWith(\".md\"))\n .map((e) => e.name)\n .sort();\n\n if (orphanMd.length > 0) {\n const hint = orphanMd\n .map((f) => `${path.basename(f, \".md\")}/agent.md`)\n .join(\", \");\n throw new Error(\n `Agents directory contains unsupported top-level markdown file(s): ${orphanMd.join(\", \")}. ` +\n `Use one folder per agent with a fixed entry file, e.g. ${hint}.`,\n );\n }\n\n // A symlink to a file is filtered out below when reading agent.md (ENOTDIR).\n const agentIds = agentDirNames(entries);\n\n const defs: Record<string, AgentDefinition> = {};\n const subAgentRefs: Record<string, string[]> = {};\n let defaultAgent: string | null = null;\n\n // Pass 1: build every agent's definition; collect sub-agent refs.\n for (const id of agentIds) {\n const agentPath = path.join(dir, id, \"agent.md\");\n let raw: string;\n try {\n raw = await fs.readFile(agentPath, \"utf-8\");\n } catch (err) {\n // No agent.md → a code-agent folder (agent.ts) or an asset dir (skills/);\n // ENOTDIR → the entry is a symlink to a file, not an agent folder.\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") continue;\n throw err;\n }\n defs[id] = buildDefinition(id, raw, agentPath, ctx);\n const { data } = parseFrontmatter(raw, agentPath);\n if (data?.agents !== undefined) {\n subAgentRefs[id] = normalizeAgentsFrontmatter(data.agents, id, agentPath);\n }\n if (data?.default === true && !defaultAgent) {\n defaultAgent = id;\n }\n }\n\n // Pass 2: resolve sub-agent references against the complete defs map.\n // Code-defined agents (ctx.codeAgents) take precedence over markdown ones\n // with the same name, matching the plugin's top-level merge behaviour.\n for (const [name, refs] of Object.entries(subAgentRefs)) {\n if (refs.length === 0) continue;\n const children: Record<string, AgentDefinition> = {};\n const missing: string[] = [];\n for (const ref of refs) {\n if (ref === name) {\n throw new Error(\n `Agent '${name}' (${path.join(dir, name, \"agent.md\")}) cannot reference itself in 'agents:'.`,\n );\n }\n const sibling = ctx.codeAgents?.[ref] ?? defs[ref];\n if (!sibling) {\n missing.push(ref);\n continue;\n }\n children[ref] = sibling;\n }\n if (missing.length > 0) {\n const available =\n [...Object.keys(ctx.codeAgents ?? {}), ...Object.keys(defs)]\n .sort()\n .join(\", \") || \"<none>\";\n throw new Error(\n `Agent '${name}' references sub-agent(s) '${missing.join(\", \")}' in 'agents:', ` +\n `but no markdown or code agent(s) with those names exist. ` +\n `Available: ${available}.`,\n );\n }\n defs[name].agents = children;\n }\n\n return { defs, defaultAgent };\n}\n\n/**\n * Validates that `agents:` frontmatter is an array of non-empty strings and\n * returns it with duplicates removed. Throws with a clear per-file message\n * on malformed input rather than silently ignoring.\n */\nfunction normalizeAgentsFrontmatter(\n value: unknown,\n agentName: string,\n filePath: string,\n): string[] {\n if (!Array.isArray(value)) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has invalid 'agents:' frontmatter: ` +\n `expected an array of sibling agent ids, got ${typeof value}.`,\n );\n }\n const out: string[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n if (typeof item !== \"string\" || item.trim() === \"\") {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has invalid 'agents:' entry: ` +\n `expected non-empty string, got ${JSON.stringify(item)}.`,\n );\n }\n if (seen.has(item)) continue;\n seen.add(item);\n out.push(item);\n }\n return out;\n}\n\n/** Exposed for tests. Parses `--- yaml ---\\nbody` and validates frontmatter keys. */\nexport function parseFrontmatter(\n raw: string,\n sourcePath?: string,\n): { data: Frontmatter | null; content: string } {\n const match = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!match) {\n return { data: null, content: raw.trim() };\n }\n let parsed: unknown;\n try {\n parsed = yaml.load(match[1]);\n } catch (err) {\n const src = sourcePath ? ` (${sourcePath})` : \"\";\n throw new Error(\n `Invalid YAML frontmatter${src}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (parsed === null || parsed === undefined) {\n return { data: {}, content: match[2].trim() };\n }\n if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n const src = sourcePath ? ` (${sourcePath})` : \"\";\n throw new Error(`Frontmatter must be a YAML object${src}`);\n }\n const data = parsed as Record<string, unknown>;\n for (const key of Object.keys(data)) {\n if (!ALLOWED_KEYS.has(key)) {\n logger.warn(\n \"Ignoring unknown frontmatter key '%s' in %s\",\n key,\n sourcePath ?? \"<inline>\",\n );\n }\n }\n return { data: data as Frontmatter, content: match[2].trim() };\n}\n\nconst isNumber = (v: unknown): v is number => typeof v === \"number\";\n\n/**\n * Per-key validators for {@link GenerationParams} frontmatter. Keyed by wire\n * name and typed as `Record<keyof GenerationParams, ...>`, so a new param on\n * the interface is a compile error until it gets an entry here — parsing,\n * unknown-key detection, and warnings all derive from this one table.\n */\nconst GENERATION_PARAM_SPECS: Record<\n keyof GenerationParams,\n { label: string; valid: (v: unknown) => boolean }\n> = {\n temperature: { label: \"number\", valid: isNumber },\n top_p: { label: \"number\", valid: isNumber },\n frequency_penalty: { label: \"number\", valid: isNumber },\n presence_penalty: { label: \"number\", valid: isNumber },\n stop: {\n label: \"string or string[]\",\n valid: (v) =>\n typeof v === \"string\" ||\n (Array.isArray(v) && v.every((s) => typeof s === \"string\")),\n },\n};\n\n/**\n * Defensively maps a frontmatter `generationParams` map to {@link GenerationParams}.\n * Picks only known keys with the expected wire types. A key present with the\n * wrong type (e.g. `temperature: \"0.5\"`) or an unknown key (e.g. `top-p`) is\n * dropped and logged at warn level, so a silently-ignored param is visible\n * rather than mistaken for \"applied\". Returns `undefined` when no valid key is\n * present.\n */\nfunction parseGenerationParams(\n value: unknown,\n sourcePath?: string,\n): GenerationParams | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return undefined;\n }\n const raw = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n const where = sourcePath ?? \"<inline>\";\n\n for (const [key, v] of Object.entries(raw)) {\n const spec = GENERATION_PARAM_SPECS[key as keyof GenerationParams];\n if (!spec) {\n logger.warn(\n \"Ignoring unknown generationParams key '%s' in %s\",\n key,\n where,\n );\n } else if (spec.valid(v)) {\n out[key] = v;\n } else {\n logger.warn(\n \"Ignoring generationParams.%s in %s: expected %s, got %s\",\n key,\n where,\n spec.label,\n typeof v,\n );\n }\n }\n\n return Object.keys(out).length > 0 ? (out as GenerationParams) : undefined;\n}\n\nfunction buildDefinition(\n name: string,\n raw: string,\n filePath: string,\n ctx: LoadContext,\n): AgentDefinition {\n const { data, content } = parseFrontmatter(raw, filePath);\n const fm: Frontmatter = data ?? {};\n\n const tools = resolveFrontmatterTools(name, fm, filePath, ctx);\n const model = fm.model ?? fm.endpoint ?? ctx.defaultModel;\n\n let baseSystemPrompt: BaseSystemPromptOption | undefined;\n if (fm.baseSystemPrompt === false) baseSystemPrompt = false;\n else if (typeof fm.baseSystemPrompt === \"string\")\n baseSystemPrompt = fm.baseSystemPrompt;\n\n return {\n name,\n instructions: content,\n model,\n tools: Object.keys(tools).length > 0 ? tools : undefined,\n maxSteps: typeof fm.maxSteps === \"number\" ? fm.maxSteps : undefined,\n maxTokens: typeof fm.maxTokens === \"number\" ? fm.maxTokens : undefined,\n generationParams: parseGenerationParams(fm.generationParams, filePath),\n baseSystemPrompt,\n ephemeral: typeof fm.ephemeral === \"boolean\" ? fm.ephemeral : undefined,\n };\n}\n\nfunction resolveFrontmatterTools(\n agentName: string,\n fm: Frontmatter,\n filePath: string,\n ctx: LoadContext,\n): Record<string, AgentTool> {\n const out: Record<string, AgentTool> = {};\n const pluginIdx = ctx.plugins ?? new Map<string, ToolkitProvider>();\n\n for (const entry of fm.tools ?? []) {\n const parsed = parseToolEntry(entry, filePath, agentName);\n if (parsed.kind === \"plugin\") {\n const provider = pluginIdx.get(parsed.pluginName);\n if (!provider) {\n const available =\n pluginIdx.size > 0\n ? Array.from(pluginIdx.keys()).join(\", \")\n : \"<none>\";\n throw new Error(\n `Agent '${agentName}' (${filePath}) references 'plugin:${parsed.pluginName}', but plugin '${parsed.pluginName}' is not registered. Available: ${available}`,\n );\n }\n const entries = provider.toolkit(parsed.opts) as Record<string, unknown>;\n for (const [key, value] of Object.entries(entries)) {\n if (!isToolkitEntry(value)) {\n throw new Error(\n `Plugin '${parsed.pluginName}'.toolkit() returned a value at key '${key}' that is not a ToolkitEntry`,\n );\n }\n out[key] = value as ToolkitEntry;\n }\n } else {\n const tool = ctx.availableTools?.[parsed.toolName];\n if (!tool) {\n const available = ctx.availableTools\n ? Object.keys(ctx.availableTools).join(\", \")\n : \"<none>\";\n throw new Error(\n `Agent '${agentName}' (${filePath}) references ambient tool '${parsed.toolName}', which is not in the agents() plugin's tools field. Available: ${available}. ` +\n \"If you meant to reference a plugin, use the 'plugin:NAME' prefix.\",\n );\n }\n out[parsed.toolName] = tool;\n }\n }\n\n return out;\n}\n\ntype ParsedToolEntry =\n | { kind: \"plugin\"; pluginName: string; opts: ToolkitOptions | undefined }\n | { kind: \"ambient\"; toolName: string };\n\n/**\n * Classify one item in the `tools:` frontmatter list into either a plugin\n * reference (with optional ToolkitOptions) or an ambient tool lookup.\n *\n * Strings starting with `plugin:` are bare plugin references. Strings\n * without the prefix are ambient tool names. Object entries are\n * single-key mappings keyed by `plugin:NAME`; the value is either an\n * array (sugar for `{ only: [...] }`) or a full `ToolkitOptions` record.\n */\nfunction parseToolEntry(\n entry: FrontmatterToolEntry,\n filePath: string,\n agentName: string,\n): ParsedToolEntry {\n if (typeof entry === \"string\") {\n if (entry.startsWith(PLUGIN_PREFIX)) {\n const pluginName = entry.slice(PLUGIN_PREFIX.length);\n if (pluginName.length === 0) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty plugin name in 'plugin:'.`,\n );\n }\n return { kind: \"plugin\", pluginName, opts: undefined };\n }\n if (entry.length === 0) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty string in 'tools:'.`,\n );\n }\n return { kind: \"ambient\", toolName: entry };\n }\n if (typeof entry !== \"object\" || entry === null) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has invalid 'tools:' entry: ${JSON.stringify(entry)}`,\n );\n }\n const keys = Object.keys(entry);\n if (keys.length !== 1) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) 'tools:' object entry must have exactly one key, got: ${keys.join(\", \")}`,\n );\n }\n const key = keys[0];\n // Bare `- plugin:` (no name after the colon) parses as a mapping with the\n // key `\"plugin\"`. Catch that as a friendly error rather than dumping it\n // through the generic \"expected key 'plugin:NAME'\" branch.\n if (key === \"plugin\") {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty plugin name in 'plugin:'.`,\n );\n }\n if (!key.startsWith(PLUGIN_PREFIX)) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) 'tools:' object entries are reserved for plugin references; expected key 'plugin:NAME', got '${key}'. ` +\n \"Use a bare string for ambient tools (e.g. `- get_weather`).\",\n );\n }\n const pluginName = key.slice(PLUGIN_PREFIX.length);\n if (pluginName.length === 0) {\n throw new Error(\n `Agent '${agentName}' (${filePath}) has an empty plugin name in 'plugin:'.`,\n );\n }\n const value = entry[key];\n if (Array.isArray(value)) {\n return { kind: \"plugin\", pluginName, opts: { only: value } };\n }\n if (typeof value === \"object\" && value !== null) {\n return {\n kind: \"plugin\",\n pluginName,\n opts: value as ToolkitOptions,\n };\n }\n throw new Error(\n `Agent '${agentName}' (${filePath}) 'plugin:${pluginName}' options must be an array of tool names or a ToolkitOptions object.`,\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ import { createLogger } from "../../logging/logger.js";
2
+ import { isCreatedAgent } from "./create-agent.js";
3
+ import { agentDirNames } from "./agent-dirs.js";
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+
8
+ //#region src/core/agent/load-code-agents.ts
9
+ const logger = createLogger("agents:code-loader");
10
+ /** Where code agents live in source (a `tsx` dev run imports the `.ts`). */
11
+ const CODE_AGENTS_SOURCE_DIR = "server/agents";
12
+ /** Compiled-output roots probed before source (tsdown emits into `dist`/`build`). */
13
+ const CODE_AGENTS_BUILT_ROOTS = ["dist", "build"];
14
+ /** Per-agent entry file, mirroring markdown's `agent.md`. */
15
+ const ENTRY_BASENAME = "agent";
16
+ /**
17
+ * Resolves which directory to scan for code agents. Compiled output wins over
18
+ * source unconditionally, so a bundled server never `import()`s a `.ts` (plain
19
+ * Node can't load one): the matching `dist/agents` / `build/agents` is probed
20
+ * first, with the source `server/agents` `.ts` dir as fallback.
21
+ */
22
+ function resolveCodeAgentsDir(opts) {
23
+ const name = path.basename(CODE_AGENTS_SOURCE_DIR);
24
+ const source = {
25
+ dir: path.resolve(opts.cwd, CODE_AGENTS_SOURCE_DIR),
26
+ extensions: [".ts"]
27
+ };
28
+ const built = CODE_AGENTS_BUILT_ROOTS.map((root) => ({
29
+ dir: path.resolve(opts.cwd, root, name),
30
+ extensions: [".js", ".mjs"]
31
+ }));
32
+ for (const candidate of [...built, source]) if (opts.exists(candidate.dir)) return candidate;
33
+ return source;
34
+ }
35
+ /**
36
+ * The `agent.<ext>` entry file inside an agent folder, or `null` if none —
37
+ * a markdown agent (`agent.md`) or a non-agent asset dir (`skills/`).
38
+ */
39
+ async function findEntryFile(agentDir, extensions) {
40
+ let files;
41
+ try {
42
+ files = await fs.readdir(agentDir);
43
+ } catch (err) {
44
+ const code = err.code;
45
+ if (code === "ENOENT" || code === "ENOTDIR") return null;
46
+ throw err;
47
+ }
48
+ for (const ext of extensions) {
49
+ const name = `${ENTRY_BASENAME}${ext}`;
50
+ if (files.includes(name)) return path.join(agentDir, name);
51
+ }
52
+ return null;
53
+ }
54
+ /**
55
+ * The single created agent a module exports — the default export, else the one
56
+ * branded named export. `undefined` if none (a helper or bundler chunk); throws
57
+ * if the entry file exports more than one (the folder name is the id).
58
+ */
59
+ function pickAgentExport(mod, filePath) {
60
+ if (isCreatedAgent(mod.default)) return mod.default;
61
+ const named = Object.entries(mod).filter(([key, value]) => key !== "default" && isCreatedAgent(value));
62
+ if (named.length === 0) return void 0;
63
+ if (named.length > 1) throw new Error(`Agent file '${filePath}' exports ${named.length} created agents (${named.map(([k]) => k).join(", ")}); expected exactly one. Export a single agent per folder (the folder name is its id).`);
64
+ return named[0][1];
65
+ }
66
+ /**
67
+ * Discovers code agents by importing each `<id>/agent.<ext>` under `dir`; the
68
+ * agent's id is its folder name. Folders with no `agent.<ext>` (markdown
69
+ * agents, asset dirs) are skipped. Returns `{}` when `dir` is absent.
70
+ *
71
+ * Imports key on the plain `file://` URL, so `reload()` picks up added/removed
72
+ * folders but not edits to an already-imported one (that needs a restart).
73
+ */
74
+ async function loadCodeAgentsFromDir(dir, opts) {
75
+ let entries;
76
+ try {
77
+ entries = await fs.readdir(dir, { withFileTypes: true });
78
+ } catch (err) {
79
+ if (err.code === "ENOENT") return {};
80
+ throw err;
81
+ }
82
+ const folders = agentDirNames(entries);
83
+ const NO_LOADER = Symbol("no-ts-loader");
84
+ const loaded = await Promise.all(folders.map(async (id) => {
85
+ const entryFile = await findEntryFile(path.join(dir, id), opts.extensions);
86
+ if (!entryFile) return null;
87
+ let mod;
88
+ try {
89
+ mod = await import(pathToFileURL(entryFile).href);
90
+ } catch (err) {
91
+ if (err.code === "ERR_UNKNOWN_FILE_EXTENSION") return NO_LOADER;
92
+ throw new Error(`Failed to import code agent '${entryFile}': ${err instanceof Error ? err.message : String(err)}`, { cause: err instanceof Error ? err : void 0 });
93
+ }
94
+ const agent = pickAgentExport(mod, entryFile);
95
+ if (!agent) {
96
+ logger.debug("Skipping %s — no createAgent export (not a code agent).", entryFile);
97
+ return null;
98
+ }
99
+ return {
100
+ id,
101
+ agent
102
+ };
103
+ }));
104
+ if (loaded.includes(NO_LOADER)) {
105
+ logger.warn("Cannot import code agents from %s under this runtime (no TypeScript loader). A production build must compile server/agents/ to JS — check the `server/agents/*/agent.ts` entry glob in the tsdown config. Discovered no code agents.", dir);
106
+ return {};
107
+ }
108
+ const agents = {};
109
+ for (const entry of loaded) if (entry && entry !== NO_LOADER) agents[entry.id] = entry.agent;
110
+ return agents;
111
+ }
112
+
113
+ //#endregion
114
+ export { CODE_AGENTS_SOURCE_DIR, loadCodeAgentsFromDir, resolveCodeAgentsDir };
115
+ //# sourceMappingURL=load-code-agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-code-agents.js","names":[],"sources":["../../../src/core/agent/load-code-agents.ts"],"sourcesContent":["import type { Dirent } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { createLogger } from \"../../logging/logger\";\nimport { agentDirNames } from \"./agent-dirs\";\nimport { isCreatedAgent } from \"./create-agent\";\nimport type { AgentDefinition } from \"./types\";\n\nconst logger = createLogger(\"agents:code-loader\");\n\n/** Where code agents live in source (a `tsx` dev run imports the `.ts`). */\nexport const CODE_AGENTS_SOURCE_DIR = \"server/agents\";\n/** Compiled-output roots probed before source (tsdown emits into `dist`/`build`). */\nconst CODE_AGENTS_BUILT_ROOTS = [\"dist\", \"build\"];\n/** Per-agent entry file, mirroring markdown's `agent.md`. */\nconst ENTRY_BASENAME = \"agent\";\n\ninterface ResolvedCodeAgentsDir {\n dir: string;\n extensions: string[];\n}\n\n/**\n * Resolves which directory to scan for code agents. Compiled output wins over\n * source unconditionally, so a bundled server never `import()`s a `.ts` (plain\n * Node can't load one): the matching `dist/agents` / `build/agents` is probed\n * first, with the source `server/agents` `.ts` dir as fallback.\n */\nexport function resolveCodeAgentsDir(opts: {\n cwd: string;\n exists: (dir: string) => boolean;\n}): ResolvedCodeAgentsDir {\n const name = path.basename(CODE_AGENTS_SOURCE_DIR);\n const source: ResolvedCodeAgentsDir = {\n dir: path.resolve(opts.cwd, CODE_AGENTS_SOURCE_DIR),\n // `.ts` only: the build entry glob is `<dir>/*/agent.ts`, so an `agent.tsx`\n // would load in dev but never be emitted for a prod bundle.\n extensions: [\".ts\"],\n };\n const built: ResolvedCodeAgentsDir[] = CODE_AGENTS_BUILT_ROOTS.map(\n (root) => ({\n dir: path.resolve(opts.cwd, root, name),\n extensions: [\".js\", \".mjs\"],\n }),\n );\n\n for (const candidate of [...built, source]) {\n if (opts.exists(candidate.dir)) return candidate;\n }\n return source;\n}\n\n/**\n * The `agent.<ext>` entry file inside an agent folder, or `null` if none —\n * a markdown agent (`agent.md`) or a non-agent asset dir (`skills/`).\n */\nasync function findEntryFile(\n agentDir: string,\n extensions: string[],\n): Promise<string | null> {\n let files: string[];\n try {\n files = await fs.readdir(agentDir);\n } catch (err) {\n // Absent (ENOENT) or a symlink to a file (ENOTDIR) → not an agent folder.\n // Surface anything else (e.g. EACCES) rather than silently dropping it.\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") return null;\n throw err;\n }\n for (const ext of extensions) {\n const name = `${ENTRY_BASENAME}${ext}`;\n if (files.includes(name)) return path.join(agentDir, name);\n }\n return null;\n}\n\n/**\n * The single created agent a module exports — the default export, else the one\n * branded named export. `undefined` if none (a helper or bundler chunk); throws\n * if the entry file exports more than one (the folder name is the id).\n */\nfunction pickAgentExport(\n mod: Record<string, unknown>,\n filePath: string,\n): AgentDefinition | undefined {\n if (isCreatedAgent(mod.default)) return mod.default;\n\n const named = Object.entries(mod).filter(\n ([key, value]) => key !== \"default\" && isCreatedAgent(value),\n );\n if (named.length === 0) return undefined;\n if (named.length > 1) {\n throw new Error(\n `Agent file '${filePath}' exports ${named.length} created agents (${named\n .map(([k]) => k)\n .join(\", \")}); expected exactly one. ` +\n \"Export a single agent per folder (the folder name is its id).\",\n );\n }\n return named[0][1] as AgentDefinition;\n}\n\n/**\n * Discovers code agents by importing each `<id>/agent.<ext>` under `dir`; the\n * agent's id is its folder name. Folders with no `agent.<ext>` (markdown\n * agents, asset dirs) are skipped. Returns `{}` when `dir` is absent.\n *\n * Imports key on the plain `file://` URL, so `reload()` picks up added/removed\n * folders but not edits to an already-imported one (that needs a restart).\n */\nexport async function loadCodeAgentsFromDir(\n dir: string,\n opts: { extensions: string[] },\n): Promise<Record<string, AgentDefinition>> {\n let entries: Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw err;\n }\n\n const folders = agentDirNames(entries);\n\n // Each agent is an independent read + import, so load them concurrently —\n // startup no longer scales linearly with the agent count. Results stay in\n // sorted-folder order (the default-agent fallback registers the first).\n const NO_LOADER = Symbol(\"no-ts-loader\");\n const loaded = await Promise.all(\n folders.map(async (id) => {\n const entryFile = await findEntryFile(\n path.join(dir, id),\n opts.extensions,\n );\n if (!entryFile) return null;\n\n let mod: Record<string, unknown>;\n try {\n mod = (await import(pathToFileURL(entryFile).href)) as Record<\n string,\n unknown\n >;\n } catch (err) {\n if (\n (err as NodeJS.ErrnoException).code === \"ERR_UNKNOWN_FILE_EXTENSION\"\n ) {\n return NO_LOADER;\n }\n throw new Error(\n `Failed to import code agent '${entryFile}': ${\n err instanceof Error ? err.message : String(err)\n }`,\n { cause: err instanceof Error ? err : undefined },\n );\n }\n\n const agent = pickAgentExport(mod, entryFile);\n if (!agent) {\n logger.debug(\n \"Skipping %s — no createAgent export (not a code agent).\",\n entryFile,\n );\n return null;\n }\n return { id, agent };\n }),\n );\n\n // No TS loader for these `.ts` modules — warn once and bail rather than crash\n // boot (every folder fails the same way).\n if (loaded.includes(NO_LOADER)) {\n logger.warn(\n \"Cannot import code agents from %s under this runtime (no TypeScript loader). \" +\n \"A production build must compile server/agents/ to JS — check the `server/agents/*/agent.ts` entry glob in the tsdown config. Discovered no code agents.\",\n dir,\n );\n return {};\n }\n\n const agents: Record<string, AgentDefinition> = {};\n for (const entry of loaded) {\n if (entry && entry !== NO_LOADER) agents[entry.id] = entry.agent;\n }\n return agents;\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,SAAS,aAAa,qBAAqB;;AAGjD,MAAa,yBAAyB;;AAEtC,MAAM,0BAA0B,CAAC,QAAQ,QAAQ;;AAEjD,MAAM,iBAAiB;;;;;;;AAavB,SAAgB,qBAAqB,MAGX;CACxB,MAAM,OAAO,KAAK,SAAS,uBAAuB;CAClD,MAAM,SAAgC;EACpC,KAAK,KAAK,QAAQ,KAAK,KAAK,uBAAuB;EAGnD,YAAY,CAAC,MAAM;EACpB;CACD,MAAM,QAAiC,wBAAwB,KAC5D,UAAU;EACT,KAAK,KAAK,QAAQ,KAAK,KAAK,MAAM,KAAK;EACvC,YAAY,CAAC,OAAO,OAAO;EAC5B,EACF;AAED,MAAK,MAAM,aAAa,CAAC,GAAG,OAAO,OAAO,CACxC,KAAI,KAAK,OAAO,UAAU,IAAI,CAAE,QAAO;AAEzC,QAAO;;;;;;AAOT,eAAe,cACb,UACA,YACwB;CACxB,IAAI;AACJ,KAAI;AACF,UAAQ,MAAM,GAAG,QAAQ,SAAS;UAC3B,KAAK;EAGZ,MAAM,OAAQ,IAA8B;AAC5C,MAAI,SAAS,YAAY,SAAS,UAAW,QAAO;AACpD,QAAM;;AAER,MAAK,MAAM,OAAO,YAAY;EAC5B,MAAM,OAAO,GAAG,iBAAiB;AACjC,MAAI,MAAM,SAAS,KAAK,CAAE,QAAO,KAAK,KAAK,UAAU,KAAK;;AAE5D,QAAO;;;;;;;AAQT,SAAS,gBACP,KACA,UAC6B;AAC7B,KAAI,eAAe,IAAI,QAAQ,CAAE,QAAO,IAAI;CAE5C,MAAM,QAAQ,OAAO,QAAQ,IAAI,CAAC,QAC/B,CAAC,KAAK,WAAW,QAAQ,aAAa,eAAe,MAAM,CAC7D;AACD,KAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,KAAI,MAAM,SAAS,EACjB,OAAM,IAAI,MACR,eAAe,SAAS,YAAY,MAAM,OAAO,mBAAmB,MACjE,KAAK,CAAC,OAAO,EAAE,CACf,KAAK,KAAK,CAAC,wFAEf;AAEH,QAAO,MAAM,GAAG;;;;;;;;;;AAWlB,eAAsB,sBACpB,KACA,MAC0C;CAC1C,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,GAAG,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;UACjD,KAAK;AACZ,MAAK,IAA8B,SAAS,SAAU,QAAO,EAAE;AAC/D,QAAM;;CAGR,MAAM,UAAU,cAAc,QAAQ;CAKtC,MAAM,YAAY,OAAO,eAAe;CACxC,MAAM,SAAS,MAAM,QAAQ,IAC3B,QAAQ,IAAI,OAAO,OAAO;EACxB,MAAM,YAAY,MAAM,cACtB,KAAK,KAAK,KAAK,GAAG,EAClB,KAAK,WACN;AACD,MAAI,CAAC,UAAW,QAAO;EAEvB,IAAI;AACJ,MAAI;AACF,SAAO,MAAM,OAAO,cAAc,UAAU,CAAC;WAItC,KAAK;AACZ,OACG,IAA8B,SAAS,6BAExC,QAAO;AAET,SAAM,IAAI,MACR,gCAAgC,UAAU,KACxC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,IAElD,EAAE,OAAO,eAAe,QAAQ,MAAM,QAAW,CAClD;;EAGH,MAAM,QAAQ,gBAAgB,KAAK,UAAU;AAC7C,MAAI,CAAC,OAAO;AACV,UAAO,MACL,2DACA,UACD;AACD,UAAO;;AAET,SAAO;GAAE;GAAI;GAAO;GACpB,CACH;AAID,KAAI,OAAO,SAAS,UAAU,EAAE;AAC9B,SAAO,KACL,wOAEA,IACD;AACD,SAAO,EAAE;;CAGX,MAAM,SAA0C,EAAE;AAClD,MAAK,MAAM,SAAS,OAClB,KAAI,SAAS,UAAU,UAAW,QAAO,MAAM,MAAM,MAAM;AAE7D,QAAO"}
@@ -109,7 +109,7 @@ interface AgentDefinition {
109
109
  /**
110
110
  * Stable identifier for the agent. **Optional and informational** —
111
111
  * when the definition is registered via `agents: { foo: def }` (code) or
112
- * lives at `config/agents/<id>/agent.md` (markdown), the **registry key
112
+ * lives at `server/agents/<id>/agent.md` (markdown), the **registry key
113
113
  * always wins** and `name` is ignored. The agent will be reachable as
114
114
  * `foo` (or `<id>`) regardless of what this field contains.
115
115
  *
@@ -125,6 +125,14 @@ interface AgentDefinition {
125
125
  * entirely.
126
126
  */
127
127
  name?: string;
128
+ /**
129
+ * Marks this agent as the default one chosen when a client doesn't name an
130
+ * agent. Mirrors markdown frontmatter `default: true`. When several agents
131
+ * set it, a code (discovered) agent wins over a markdown one, then the
132
+ * lowest id; an explicit `agents({ defaultAgent })` always overrides it.
133
+ * Defaults to `false`.
134
+ */
135
+ default?: boolean;
128
136
  /** System prompt body. For markdown-loaded agents this is the file body. */
129
137
  instructions: string;
130
138
  /**
@@ -187,11 +195,17 @@ interface AutoInheritToolsConfig {
187
195
  code?: boolean;
188
196
  }
189
197
  interface AgentsPluginConfig extends BasePluginConfig {
190
- /** Directory of agent packages (`<id>/agent.md` each). Default `./config/agents`. Set to `false` to disable. */
191
- dir?: string | false;
192
- /** Code-defined agents, merged with file-loaded ones (code wins on key collision). */
198
+ /**
199
+ * @deprecated Put each code agent in its own folder under
200
+ * `server/agents/<id>/agent.ts` (`export default createAgent({ ... })`); it is
201
+ * discovered automatically at startup and the call collapses to
202
+ * `agents({ ... })` with no map. Still honored for backward compatibility
203
+ * (emits a one-time deprecation warning) but will be removed in a future
204
+ * minor. If both discovery and this map define the same id, discovery wins
205
+ * and the map entry is ignored.
206
+ */
193
207
  agents?: Record<string, AgentDefinition>;
194
- /** Agent used when clients don't specify one. Defaults to the first-registered agent or the file with `default: true` frontmatter. */
208
+ /** Agent used when clients don't specify one. Precedence: this value, else a code agent with `default: true`, else a markdown agent with `default: true`, else the first-registered agent. */
195
209
  defaultAgent?: string;
196
210
  /** Default model for agents that don't specify their own (in code or frontmatter). */
197
211
  defaultModel?: AgentAdapter | Promise<AgentAdapter> | string;
@@ -215,7 +229,7 @@ interface AgentsPluginConfig extends BasePluginConfig {
215
229
  * (the default), the agents plugin emits an `appkit.approval_pending` SSE
216
230
  * event before executing any tool whose annotation flags it as mutating —
217
231
  * `effect: "write" | "update" | "destructive"` (preferred) or the legacy
218
- * `destructive: true` boolean — and waits for a `POST /chat/approve`
232
+ * `destructive: true` boolean — and waits for a `POST /api/agents/approve`
219
233
  * decision from the same user who initiated the stream. A missing decision
220
234
  * after `timeoutMs` auto-denies the call.
221
235
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/agent/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/agent/types.ts"],"mappings":"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"}