@kolisachint/hoocode-agent 0.4.77 → 0.4.79

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 (42) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/dist/core/agent-registry.d.ts +11 -0
  3. package/dist/core/agent-registry.d.ts.map +1 -1
  4. package/dist/core/agent-registry.js +36 -1
  5. package/dist/core/agent-registry.js.map +1 -1
  6. package/dist/core/messages.d.ts.map +1 -1
  7. package/dist/core/messages.js +24 -6
  8. package/dist/core/messages.js.map +1 -1
  9. package/dist/core/model-categories.d.ts +14 -8
  10. package/dist/core/model-categories.d.ts.map +1 -1
  11. package/dist/core/model-categories.js +13 -24
  12. package/dist/core/model-categories.js.map +1 -1
  13. package/dist/core/settings-manager.d.ts +3 -2
  14. package/dist/core/settings-manager.d.ts.map +1 -1
  15. package/dist/core/settings-manager.js.map +1 -1
  16. package/dist/core/subagent-inbox.d.ts +92 -0
  17. package/dist/core/subagent-inbox.d.ts.map +1 -0
  18. package/dist/core/subagent-inbox.js +245 -0
  19. package/dist/core/subagent-inbox.js.map +1 -0
  20. package/dist/core/subagent-pool.d.ts +3 -0
  21. package/dist/core/subagent-pool.d.ts.map +1 -1
  22. package/dist/core/subagent-pool.js +4 -2
  23. package/dist/core/subagent-pool.js.map +1 -1
  24. package/dist/core/task-store.d.ts +7 -0
  25. package/dist/core/task-store.d.ts.map +1 -1
  26. package/dist/core/task-store.js +19 -0
  27. package/dist/core/task-store.js.map +1 -1
  28. package/dist/core/tools/subagent.d.ts +20 -17
  29. package/dist/core/tools/subagent.d.ts.map +1 -1
  30. package/dist/core/tools/subagent.js +250 -105
  31. package/dist/core/tools/subagent.js.map +1 -1
  32. package/dist/core/tools/todo.d.ts.map +1 -1
  33. package/dist/core/tools/todo.js +30 -18
  34. package/dist/core/tools/todo.js.map +1 -1
  35. package/dist/init-templates.generated.d.ts.map +1 -1
  36. package/dist/init-templates.generated.js +3 -3
  37. package/dist/init-templates.generated.js.map +1 -1
  38. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  39. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  40. package/examples/extensions/sandbox/package.json +1 -1
  41. package/examples/extensions/with-deps/package.json +1 -1
  42. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.79] - 2026-06-22
4
+
5
+ ### Changed
6
+
7
+ - **Reverted the `Task` → `ExecuteTask` rename from 0.4.78.** The subagent
8
+ delegation tool is `Task` again (no deprecated alias), keeping parity with
9
+ Claude Code. The never-wired `item_id` parameter and the duplicate `complexity`
10
+ field on TodoWrite (both added in 0.4.78) are removed.
11
+ - **Background subagents are now notify-and-pull** instead of forced-background.
12
+ A background `Task` posts a compact one-line notification and retains the body
13
+ in a new subagent inbox; the model pulls the full result with `TaskOutput`.
14
+ Background is opt-in per agent with a per-call `background: true|false`
15
+ override. This keeps a wide swarm of subagents from flooding the parent's
16
+ context with N full summaries.
17
+ - **`TaskOutput` reworked into a status-aware probe + swarm barrier** (replacing
18
+ 0.4.78's `wait_for_completion` approach). It never errors on a valid handle: a
19
+ running task reports its status/activity, a finished one returns its body, an
20
+ already-read one says so. New modes: `list: true` lists every background
21
+ subagent, and `wait: true` blocks until a named task — or, with no `task_id`,
22
+ all outstanding subagents — finish. Tasks are addressable by a friendly label
23
+ (`explore#1`) or their task id.
24
+ - **`Task` keeps the optional `complexity` tier** (`"fast"`/`"standard"`/
25
+ `"capable"`), now passed straight through as the dispatch model so the pool's
26
+ precedence applies it only when the agent's model is `inherit`; a pinned-model
27
+ agent ignores it.
28
+ - **Model categories are provider-neutral**: the tiers resolve only from
29
+ `settings.modelCategories` with no hardcoded fallback, and an unconfigured tier
30
+ is a no-op (keep the agent's or parent's default model). Built-in agent
31
+ templates select by category (`explore: fast`, `general-purpose`/`plan:
32
+ standard`).
33
+ - **Lower per-turn prompt cost**: the available-agents roster renders once (was
34
+ up to three times — the `<available_agents>` block, the buildTaskMainPrompt
35
+ appendix, and the Task tool description) and as a one-line summary per agent
36
+ instead of the full description; the Task tool description no longer re-embeds
37
+ the roster or re-explains parameters covered by their schemas.
38
+ - The on-spawn placeholder is a single compact line instead of a multi-line
39
+ explainer, so dispatching several subagents at once no longer floods the TUI.
40
+
41
+ ## [0.4.78] - 2026-06-21
42
+
43
+ ### Breaking Changes
44
+
45
+ - **Task tool renamed to ExecuteTask**. The subagent delegation tool is now
46
+ called `ExecuteTask` instead of `Task`. Agent definitions and prompts that
47
+ reference "Task tool" must be updated. The old name is kept as a deprecated
48
+ alias for backward compatibility.
49
+ - **TodoWrite schema extended with `complexity` field**. Each todo item can now
50
+ carry a `complexity` parameter (`"fast"`, `"standard"`, `"capable"`) that
51
+ maps to a model category via `settings.modelCategories`. This is optional;
52
+ omitting it uses the agent's default model.
53
+ - **ExecuteTask schema extended with `complexity` and `item_id` fields**. The
54
+ new `complexity` parameter selects a model category from config. The new
55
+ `item_id` parameter links the dispatch to a TodoWrite item for tracking.
56
+
57
+ ### Fixed
58
+
59
+ - TodoWrite reconciliation now filters to root main-agent tasks only, excluding
60
+ MCP-sourced and delegated tasks that `taskOwnerId()` folded under "main". This
61
+ prevented silent data corruption of MCP task rows when the TodoWrite list was
62
+ shorter than the combined main+MCP task count.
63
+ - TaskOutput now waits for a running/queued subagent to finish (up to 120s)
64
+ instead of returning "call again later" and requiring an extra LLM round-trip
65
+ per poll. Foreground Task completions are also visible to TaskOutput via a new
66
+ `wait_for_completion` API on the subagent pool.
67
+
68
+ ### Changed
69
+
70
+ - TaskStore gains a `batch(fn)` method that defers listener notifications until
71
+ the batch callback completes. TodoWrite reconciliation and child-task-tree
72
+ merging now emit a single render invalidation instead of one per item.
73
+
3
74
  ## [0.4.77] - 2026-06-21
4
75
 
5
76
  ## [0.4.76] - 2026-06-21
@@ -62,5 +62,16 @@ export declare function loadAgentRegistry(options: LoadAgentRegistryOptions): Ag
62
62
  * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.
63
63
  * Only intended for display when the Task tool is active.
64
64
  */
65
+ /**
66
+ * Condense a (possibly multi-line, bulleted) agent description into a single
67
+ * useful one-liner.
68
+ *
69
+ * Built-in agent descriptions open with a boilerplate header ("Use this
70
+ * subagent ONLY when:") followed by "when to use" bullets and a "DO NOT use"
71
+ * section. Taking the first line alone yields that identical header for every
72
+ * agent, so instead surface the first meaningful bullets (or the first prose
73
+ * line) from the positive "when to use" region.
74
+ */
75
+ export declare function summarizeAgentDescription(description: string): string;
65
76
  export declare function formatAgentsForPrompt(agents: AgentDefinition[]): string;
66
77
  //# sourceMappingURL=agent-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-registry.d.ts","sourceRoot":"","sources":["../../src/core/agent-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,EAAE,KAAK,eAAe,EAA0C,MAAM,wBAAwB,CAAC;AAEtG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,mDAAmD;AACnD,qBAAa,aAAa;IACzB,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,WAAW,CAA4B;IAE/C;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAkBnC;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE7C;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,IAAI,IAAI,eAAe,EAAE,CAExB;IAED,2DAA2D;IAC3D,cAAc,IAAI,kBAAkB,EAAE,CAErC;IAED,uEAAuE;IACvE,cAAc,CAAC,WAAW,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAEtD;CACD;AA8ED,MAAM,WAAW,wBAAwB;IACxC,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mGAAmG;IACnG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAYD,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CA0DlF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CA0BvE","sourcesContent":["/**\n * Agent registry: data-driven loading of subagent definitions.\n *\n * Replaces the hardcoded `SubagentMode` enum + `MODE_TOOLS` map with frontmatter\n * `.md` files (see agent-frontmatter.ts). Definitions are discovered from, in\n * increasing order of precedence:\n *\n * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)\n * 2. package-manifest paths from hoocode.agents in package.json\n * 3. claude-user ~/.claude/agents/*.md (D7 native import)\n * 4. user ~/.hoocode/agents/*.md\n * 5. ancestor-walk <git-root..cwd>/.agents/agents/*.md\n * 6. claude-project <cwd>/.claude/agents/*.md (D7 native import)\n * 7. project <cwd>/.hoocode/agents/*.md\n * 8. cli paths injected via --agent <path>\n *\n * Higher-precedence sources override lower ones by name. Overrides are recorded\n * as collision diagnostics. Loading never throws; problems surface as\n * diagnostics, matching skills.ts.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport { EMBEDDED_AGENT_PROMPTS } from \"../init-templates.generated.js\";\nimport { type AgentDefinition, type AgentSource, parseAgentDefinition } from \"./agent-frontmatter.js\";\nimport { getAgentCliPaths, getAgentManifestPaths } from \"./agent-manifest-paths.js\";\nimport type { ResourceDiagnostic } from \"./diagnostics.js\";\n\n/** Registry of agent definitions keyed by name. */\nexport class AgentRegistry {\n\tprivate agents = new Map<string, AgentDefinition>();\n\tprivate diagnostics: ResourceDiagnostic[] = [];\n\n\t/**\n\t * Add or override a definition. Later registrations win (used both by the\n\t * loader for precedence and as an escape hatch for programmatic agents).\n\t * Overriding an existing name records a collision diagnostic.\n\t */\n\tregister(def: AgentDefinition): void {\n\t\tconst existing = this.agents.get(def.name);\n\t\tif (existing) {\n\t\t\tthis.diagnostics.push({\n\t\t\t\ttype: \"collision\",\n\t\t\t\tmessage: `agent \"${def.name}\" from ${def.source} overrides ${existing.source}`,\n\t\t\t\tpath: def.filePath,\n\t\t\t\tcollision: {\n\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\tname: def.name,\n\t\t\t\t\twinnerPath: def.filePath ?? `<${def.source}>`,\n\t\t\t\t\tloserPath: existing.filePath ?? `<${existing.source}>`,\n\t\t\t\t\twinnerSource: def.source,\n\t\t\t\t\tloserSource: existing.source,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tthis.agents.set(def.name, def);\n\t}\n\n\tget(name: string): AgentDefinition | undefined {\n\t\treturn this.agents.get(name);\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.agents.has(name);\n\t}\n\n\tlist(): AgentDefinition[] {\n\t\treturn Array.from(this.agents.values());\n\t}\n\n\t/** Diagnostics accumulated during loading/registration. */\n\tgetDiagnostics(): ResourceDiagnostic[] {\n\t\treturn this.diagnostics;\n\t}\n\n\t/** Append externally-produced diagnostics (e.g. from a parse step). */\n\taddDiagnostics(diagnostics: ResourceDiagnostic[]): void {\n\t\tthis.diagnostics.push(...diagnostics);\n\t}\n}\n\n/** Load and register every built-in (embedded) agent definition. */\nfunction registerBuiltins(registry: AgentRegistry): void {\n\tfor (const [key, raw] of Object.entries(EMBEDDED_AGENT_PROMPTS)) {\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source: \"builtin\", fallbackName: key });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t}\n}\n\n/** Load flat `*.md` agent files from a directory. Non-`.md` entries and\n * subdirectories are skipped (so runtime dispatch dirs are ignored). */\nfunction registerDir(registry: AgentRegistry, dir: string, source: AgentSource): void {\n\tif (!existsSync(dir)) return;\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") || !entry.endsWith(\".md\")) continue;\n\t\tconst filePath = join(dir, entry);\n\t\ttry {\n\t\t\tif (!statSync(filePath).isFile()) continue;\n\t\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\t\tregistry.addDiagnostics(diagnostics);\n\t\t\tif (agent) registry.register(agent);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t\t}\n\t}\n}\n\nfunction findGitRepoRoot(startDir: string): string | null {\n\tlet dir = resolve(startDir);\n\twhile (true) {\n\t\tif (existsSync(join(dir, \".git\"))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Collect `.agents/agents/` dirs from cwd up to the git root (cwd-first order). */\nfunction collectAncestorAgentsDirs(startDir: string): string[] {\n\tconst dirs: string[] = [];\n\tconst resolvedStart = resolve(startDir);\n\tconst gitRoot = findGitRepoRoot(resolvedStart);\n\tlet dir = resolvedStart;\n\twhile (true) {\n\t\tdirs.push(join(dir, \".agents\", \"agents\"));\n\t\tif (gitRoot && dir === gitRoot) break;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn dirs;\n}\n\n/** Register a single file as an agent. */\nfunction registerFile(registry: AgentRegistry, filePath: string, source: AgentSource): void {\n\tif (!existsSync(filePath)) return;\n\ttry {\n\t\tif (!statSync(filePath).isFile()) return;\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t}\n}\n\nexport interface LoadAgentRegistryOptions {\n\t/** Working directory for project-local agents. */\n\tcwd: string;\n\t/** User agent config directory (contains `agents/` subdir). Defaults to getAgentDir(). */\n\tagentDir?: string;\n\t/** Include embedded built-in agents. Defaults to true. */\n\tincludeBuiltins?: boolean;\n\t/** Discover `.claude/agents/` directories for native Claude Code import (D7). Defaults to true. */\n\tincludeClaude?: boolean;\n\t/**\n\t * Explicit agent definition paths (files or directories), resolved relative\n\t * to `cwd` (with `~` expansion). Mirrors `skillPaths`/`promptPaths` on the\n\t * skills and prompt-template loaders. These override all discovered sources\n\t * by name but yield to CLI-injected `--agent` paths.\n\t */\n\tagentPaths?: string[];\n}\n\n/** Resolve an explicit path: expand a leading `~` and resolve against `cwd`. */\nfunction normalizeAgentPath(input: string, cwd: string): string {\n\tconst trimmed = input.trim();\n\tlet expanded = trimmed;\n\tif (trimmed === \"~\") expanded = homedir();\n\telse if (trimmed.startsWith(\"~/\")) expanded = join(homedir(), trimmed.slice(2));\n\telse if (trimmed.startsWith(\"~\")) expanded = join(homedir(), trimmed.slice(1));\n\treturn resolve(cwd, expanded);\n}\n\n/** Build an AgentRegistry from all configured locations, applying precedence. */\nexport function loadAgentRegistry(options: LoadAgentRegistryOptions): AgentRegistry {\n\tconst { cwd, includeBuiltins = true, includeClaude = true } = options;\n\tconst userAgentDir = options.agentDir ?? getAgentDir();\n\tconst registry = new AgentRegistry();\n\n\t// Lowest precedence first; later sources override earlier ones by name.\n\tif (includeBuiltins) {\n\t\tregisterBuiltins(registry);\n\t}\n\n\t// Package-manifest agents (declared via `hoocode.agents` in package.json).\n\tfor (const filePath of getAgentManifestPaths()) {\n\t\tregisterFile(registry, filePath, \"user\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, join(homedir(), \".claude\", \"agents\"), \"claude-user\");\n\t}\n\tregisterDir(registry, join(userAgentDir, \"agents\"), \"user\");\n\n\t// Ancestor-walk .agents/agents/ dirs (git-root first so cwd-level overrides ancestors).\n\tfor (const dir of collectAncestorAgentsDirs(cwd).reverse()) {\n\t\tregisterDir(registry, dir, \"project\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, resolve(cwd, \".claude\", \"agents\"), \"claude-project\");\n\t}\n\tregisterDir(registry, resolve(cwd, CONFIG_DIR_NAME, \"agents\"), \"project\");\n\n\t// Explicit caller-provided paths override discovered sources (files or dirs).\n\tfor (const rawPath of options.agentPaths ?? []) {\n\t\tconst p = normalizeAgentPath(rawPath, cwd);\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"project\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"project\");\n\t\t}\n\t}\n\n\t// CLI-injected paths have highest precedence (support both files and dirs).\n\tfor (const p of getAgentCliPaths()) {\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"user\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"user\");\n\t\t}\n\t}\n\n\treturn registry;\n}\n\n/**\n * Format a list of agent definitions as an XML block for inclusion in a system\n * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.\n * Only intended for display when the Task tool is active.\n */\nexport function formatAgentsForPrompt(agents: AgentDefinition[]): string {\n\tif (agents.length === 0) return \"\";\n\n\tconst lines = [\n\t\t\"\\n\\nThe following specialized agents are available for delegation via the Task tool.\",\n\t\t\"Choose the agent whose description best matches the task and pass it as `subagent_type`.\",\n\t\t\"\",\n\t\t\"<available_agents>\",\n\t];\n\n\tfor (const agent of agents) {\n\t\tlines.push(\" <agent>\");\n\t\tlines.push(` <name>${escapeXml(agent.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(agent.description)}</description>`);\n\t\tif (agent.tools && agent.tools.length > 0) {\n\t\t\tlines.push(` <tools>${escapeXml(agent.tools.join(\", \"))}</tools>`);\n\t\t}\n\t\tif (agent.model) {\n\t\t\tlines.push(` <model>${escapeXml(agent.model)}</model>`);\n\t\t}\n\t\tlines.push(\" </agent>\");\n\t}\n\n\tlines.push(\"</available_agents>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}
1
+ {"version":3,"file":"agent-registry.d.ts","sourceRoot":"","sources":["../../src/core/agent-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,EAAE,KAAK,eAAe,EAA0C,MAAM,wBAAwB,CAAC;AAEtG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,mDAAmD;AACnD,qBAAa,aAAa;IACzB,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,WAAW,CAA4B;IAE/C;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAkBnC;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAE7C;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,IAAI,IAAI,eAAe,EAAE,CAExB;IAED,2DAA2D;IAC3D,cAAc,IAAI,kBAAkB,EAAE,CAErC;IAED,uEAAuE;IACvE,cAAc,CAAC,WAAW,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAEtD;CACD;AA8ED,MAAM,WAAW,wBAAwB;IACxC,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mGAAmG;IACnG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAYD,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CA0DlF;AAED;;;;GAIG;AACH;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAwBrE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CA8BvE","sourcesContent":["/**\n * Agent registry: data-driven loading of subagent definitions.\n *\n * Replaces the hardcoded `SubagentMode` enum + `MODE_TOOLS` map with frontmatter\n * `.md` files (see agent-frontmatter.ts). Definitions are discovered from, in\n * increasing order of precedence:\n *\n * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)\n * 2. package-manifest paths from hoocode.agents in package.json\n * 3. claude-user ~/.claude/agents/*.md (D7 native import)\n * 4. user ~/.hoocode/agents/*.md\n * 5. ancestor-walk <git-root..cwd>/.agents/agents/*.md\n * 6. claude-project <cwd>/.claude/agents/*.md (D7 native import)\n * 7. project <cwd>/.hoocode/agents/*.md\n * 8. cli paths injected via --agent <path>\n *\n * Higher-precedence sources override lower ones by name. Overrides are recorded\n * as collision diagnostics. Loading never throws; problems surface as\n * diagnostics, matching skills.ts.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport { EMBEDDED_AGENT_PROMPTS } from \"../init-templates.generated.js\";\nimport { type AgentDefinition, type AgentSource, parseAgentDefinition } from \"./agent-frontmatter.js\";\nimport { getAgentCliPaths, getAgentManifestPaths } from \"./agent-manifest-paths.js\";\nimport type { ResourceDiagnostic } from \"./diagnostics.js\";\n\n/** Registry of agent definitions keyed by name. */\nexport class AgentRegistry {\n\tprivate agents = new Map<string, AgentDefinition>();\n\tprivate diagnostics: ResourceDiagnostic[] = [];\n\n\t/**\n\t * Add or override a definition. Later registrations win (used both by the\n\t * loader for precedence and as an escape hatch for programmatic agents).\n\t * Overriding an existing name records a collision diagnostic.\n\t */\n\tregister(def: AgentDefinition): void {\n\t\tconst existing = this.agents.get(def.name);\n\t\tif (existing) {\n\t\t\tthis.diagnostics.push({\n\t\t\t\ttype: \"collision\",\n\t\t\t\tmessage: `agent \"${def.name}\" from ${def.source} overrides ${existing.source}`,\n\t\t\t\tpath: def.filePath,\n\t\t\t\tcollision: {\n\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\tname: def.name,\n\t\t\t\t\twinnerPath: def.filePath ?? `<${def.source}>`,\n\t\t\t\t\tloserPath: existing.filePath ?? `<${existing.source}>`,\n\t\t\t\t\twinnerSource: def.source,\n\t\t\t\t\tloserSource: existing.source,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tthis.agents.set(def.name, def);\n\t}\n\n\tget(name: string): AgentDefinition | undefined {\n\t\treturn this.agents.get(name);\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.agents.has(name);\n\t}\n\n\tlist(): AgentDefinition[] {\n\t\treturn Array.from(this.agents.values());\n\t}\n\n\t/** Diagnostics accumulated during loading/registration. */\n\tgetDiagnostics(): ResourceDiagnostic[] {\n\t\treturn this.diagnostics;\n\t}\n\n\t/** Append externally-produced diagnostics (e.g. from a parse step). */\n\taddDiagnostics(diagnostics: ResourceDiagnostic[]): void {\n\t\tthis.diagnostics.push(...diagnostics);\n\t}\n}\n\n/** Load and register every built-in (embedded) agent definition. */\nfunction registerBuiltins(registry: AgentRegistry): void {\n\tfor (const [key, raw] of Object.entries(EMBEDDED_AGENT_PROMPTS)) {\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source: \"builtin\", fallbackName: key });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t}\n}\n\n/** Load flat `*.md` agent files from a directory. Non-`.md` entries and\n * subdirectories are skipped (so runtime dispatch dirs are ignored). */\nfunction registerDir(registry: AgentRegistry, dir: string, source: AgentSource): void {\n\tif (!existsSync(dir)) return;\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") || !entry.endsWith(\".md\")) continue;\n\t\tconst filePath = join(dir, entry);\n\t\ttry {\n\t\t\tif (!statSync(filePath).isFile()) continue;\n\t\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\t\tregistry.addDiagnostics(diagnostics);\n\t\t\tif (agent) registry.register(agent);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t\t}\n\t}\n}\n\nfunction findGitRepoRoot(startDir: string): string | null {\n\tlet dir = resolve(startDir);\n\twhile (true) {\n\t\tif (existsSync(join(dir, \".git\"))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Collect `.agents/agents/` dirs from cwd up to the git root (cwd-first order). */\nfunction collectAncestorAgentsDirs(startDir: string): string[] {\n\tconst dirs: string[] = [];\n\tconst resolvedStart = resolve(startDir);\n\tconst gitRoot = findGitRepoRoot(resolvedStart);\n\tlet dir = resolvedStart;\n\twhile (true) {\n\t\tdirs.push(join(dir, \".agents\", \"agents\"));\n\t\tif (gitRoot && dir === gitRoot) break;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn dirs;\n}\n\n/** Register a single file as an agent. */\nfunction registerFile(registry: AgentRegistry, filePath: string, source: AgentSource): void {\n\tif (!existsSync(filePath)) return;\n\ttry {\n\t\tif (!statSync(filePath).isFile()) return;\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t}\n}\n\nexport interface LoadAgentRegistryOptions {\n\t/** Working directory for project-local agents. */\n\tcwd: string;\n\t/** User agent config directory (contains `agents/` subdir). Defaults to getAgentDir(). */\n\tagentDir?: string;\n\t/** Include embedded built-in agents. Defaults to true. */\n\tincludeBuiltins?: boolean;\n\t/** Discover `.claude/agents/` directories for native Claude Code import (D7). Defaults to true. */\n\tincludeClaude?: boolean;\n\t/**\n\t * Explicit agent definition paths (files or directories), resolved relative\n\t * to `cwd` (with `~` expansion). Mirrors `skillPaths`/`promptPaths` on the\n\t * skills and prompt-template loaders. These override all discovered sources\n\t * by name but yield to CLI-injected `--agent` paths.\n\t */\n\tagentPaths?: string[];\n}\n\n/** Resolve an explicit path: expand a leading `~` and resolve against `cwd`. */\nfunction normalizeAgentPath(input: string, cwd: string): string {\n\tconst trimmed = input.trim();\n\tlet expanded = trimmed;\n\tif (trimmed === \"~\") expanded = homedir();\n\telse if (trimmed.startsWith(\"~/\")) expanded = join(homedir(), trimmed.slice(2));\n\telse if (trimmed.startsWith(\"~\")) expanded = join(homedir(), trimmed.slice(1));\n\treturn resolve(cwd, expanded);\n}\n\n/** Build an AgentRegistry from all configured locations, applying precedence. */\nexport function loadAgentRegistry(options: LoadAgentRegistryOptions): AgentRegistry {\n\tconst { cwd, includeBuiltins = true, includeClaude = true } = options;\n\tconst userAgentDir = options.agentDir ?? getAgentDir();\n\tconst registry = new AgentRegistry();\n\n\t// Lowest precedence first; later sources override earlier ones by name.\n\tif (includeBuiltins) {\n\t\tregisterBuiltins(registry);\n\t}\n\n\t// Package-manifest agents (declared via `hoocode.agents` in package.json).\n\tfor (const filePath of getAgentManifestPaths()) {\n\t\tregisterFile(registry, filePath, \"user\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, join(homedir(), \".claude\", \"agents\"), \"claude-user\");\n\t}\n\tregisterDir(registry, join(userAgentDir, \"agents\"), \"user\");\n\n\t// Ancestor-walk .agents/agents/ dirs (git-root first so cwd-level overrides ancestors).\n\tfor (const dir of collectAncestorAgentsDirs(cwd).reverse()) {\n\t\tregisterDir(registry, dir, \"project\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, resolve(cwd, \".claude\", \"agents\"), \"claude-project\");\n\t}\n\tregisterDir(registry, resolve(cwd, CONFIG_DIR_NAME, \"agents\"), \"project\");\n\n\t// Explicit caller-provided paths override discovered sources (files or dirs).\n\tfor (const rawPath of options.agentPaths ?? []) {\n\t\tconst p = normalizeAgentPath(rawPath, cwd);\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"project\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"project\");\n\t\t}\n\t}\n\n\t// CLI-injected paths have highest precedence (support both files and dirs).\n\tfor (const p of getAgentCliPaths()) {\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"user\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"user\");\n\t\t}\n\t}\n\n\treturn registry;\n}\n\n/**\n * Format a list of agent definitions as an XML block for inclusion in a system\n * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.\n * Only intended for display when the Task tool is active.\n */\n/**\n * Condense a (possibly multi-line, bulleted) agent description into a single\n * useful one-liner.\n *\n * Built-in agent descriptions open with a boilerplate header (\"Use this\n * subagent ONLY when:\") followed by \"when to use\" bullets and a \"DO NOT use\"\n * section. Taking the first line alone yields that identical header for every\n * agent, so instead surface the first meaningful bullets (or the first prose\n * line) from the positive \"when to use\" region.\n */\nexport function summarizeAgentDescription(description: string): string {\n\tconst lines = description\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter((line) => line.length > 0);\n\tif (lines.length === 0) return \"\";\n\n\t// Keep only the positive region: everything before a \"DO NOT use\" section.\n\tconst stop = lines.findIndex((line) => /^(do\\s*not|don'?t|avoid)\\b/i.test(line));\n\tconst region = stop === -1 ? lines : lines.slice(0, stop);\n\n\t// Drop a leading header line (e.g. \"Use this subagent ONLY when:\").\n\tconst body = region.length > 1 && region[0]!.endsWith(\":\") ? region.slice(1) : region;\n\n\tconst stripBullet = (line: string) => line.replace(/^[-*•]\\s+/, \"\").trim();\n\tconst bullets = body\n\t\t.filter((line) => /^[-*•]\\s+/.test(line))\n\t\t.map(stripBullet)\n\t\t.filter((line) => line.length > 0);\n\n\tconst summary = bullets.length > 0 ? bullets.slice(0, 3).join(\"; \") : (body[0] ?? lines[0] ?? \"\").replace(/:$/, \"\");\n\n\tconst MAX = 200;\n\treturn summary.length > MAX ? `${summary.slice(0, MAX - 1).trimEnd()}…` : summary;\n}\n\nexport function formatAgentsForPrompt(agents: AgentDefinition[]): string {\n\tif (agents.length === 0) return \"\";\n\n\tconst lines = [\n\t\t\"\\n\\nThe following specialized agents are available for delegation via the Task tool.\",\n\t\t\"Choose the agent whose description best matches the task and pass it as `subagent_type`.\",\n\t\t\"\",\n\t\t\"<available_agents>\",\n\t];\n\n\tfor (const agent of agents) {\n\t\tlines.push(\" <agent>\");\n\t\tlines.push(` <name>${escapeXml(agent.name)}</name>`);\n\t\t// Summarized (one positive \"when to use\" line) rather than the full\n\t\t// description: the roster is rendered every turn and the full text — with its\n\t\t// \"DO NOT use\"/Cost/Isolation metadata — costs ~2x the tokens for routing\n\t\t// detail the parent rarely needs once it has picked an agent.\n\t\tlines.push(` <description>${escapeXml(summarizeAgentDescription(agent.description))}</description>`);\n\t\tif (agent.tools && agent.tools.length > 0) {\n\t\t\tlines.push(` <tools>${escapeXml(agent.tools.join(\", \"))}</tools>`);\n\t\t}\n\t\tif (agent.model) {\n\t\t\tlines.push(` <model>${escapeXml(agent.model)}</model>`);\n\t\t}\n\t\tlines.push(\" </agent>\");\n\t}\n\n\tlines.push(\"</available_agents>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}
@@ -228,6 +228,37 @@ export function loadAgentRegistry(options) {
228
228
  * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.
229
229
  * Only intended for display when the Task tool is active.
230
230
  */
231
+ /**
232
+ * Condense a (possibly multi-line, bulleted) agent description into a single
233
+ * useful one-liner.
234
+ *
235
+ * Built-in agent descriptions open with a boilerplate header ("Use this
236
+ * subagent ONLY when:") followed by "when to use" bullets and a "DO NOT use"
237
+ * section. Taking the first line alone yields that identical header for every
238
+ * agent, so instead surface the first meaningful bullets (or the first prose
239
+ * line) from the positive "when to use" region.
240
+ */
241
+ export function summarizeAgentDescription(description) {
242
+ const lines = description
243
+ .split("\n")
244
+ .map((line) => line.trim())
245
+ .filter((line) => line.length > 0);
246
+ if (lines.length === 0)
247
+ return "";
248
+ // Keep only the positive region: everything before a "DO NOT use" section.
249
+ const stop = lines.findIndex((line) => /^(do\s*not|don'?t|avoid)\b/i.test(line));
250
+ const region = stop === -1 ? lines : lines.slice(0, stop);
251
+ // Drop a leading header line (e.g. "Use this subagent ONLY when:").
252
+ const body = region.length > 1 && region[0].endsWith(":") ? region.slice(1) : region;
253
+ const stripBullet = (line) => line.replace(/^[-*•]\s+/, "").trim();
254
+ const bullets = body
255
+ .filter((line) => /^[-*•]\s+/.test(line))
256
+ .map(stripBullet)
257
+ .filter((line) => line.length > 0);
258
+ const summary = bullets.length > 0 ? bullets.slice(0, 3).join("; ") : (body[0] ?? lines[0] ?? "").replace(/:$/, "");
259
+ const MAX = 200;
260
+ return summary.length > MAX ? `${summary.slice(0, MAX - 1).trimEnd()}…` : summary;
261
+ }
231
262
  export function formatAgentsForPrompt(agents) {
232
263
  if (agents.length === 0)
233
264
  return "";
@@ -240,7 +271,11 @@ export function formatAgentsForPrompt(agents) {
240
271
  for (const agent of agents) {
241
272
  lines.push(" <agent>");
242
273
  lines.push(` <name>${escapeXml(agent.name)}</name>`);
243
- lines.push(` <description>${escapeXml(agent.description)}</description>`);
274
+ // Summarized (one positive "when to use" line) rather than the full
275
+ // description: the roster is rendered every turn and the full text — with its
276
+ // "DO NOT use"/Cost/Isolation metadata — costs ~2x the tokens for routing
277
+ // detail the parent rarely needs once it has picked an agent.
278
+ lines.push(` <description>${escapeXml(summarizeAgentDescription(agent.description))}</description>`);
244
279
  if (agent.tools && agent.tools.length > 0) {
245
280
  lines.push(` <tools>${escapeXml(agent.tools.join(", "))}</tools>`);
246
281
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-registry.js","sourceRoot":"","sources":["../../src/core/agent-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent registry: data-driven loading of subagent definitions.\n *\n * Replaces the hardcoded `SubagentMode` enum + `MODE_TOOLS` map with frontmatter\n * `.md` files (see agent-frontmatter.ts). Definitions are discovered from, in\n * increasing order of precedence:\n *\n * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)\n * 2. package-manifest paths from hoocode.agents in package.json\n * 3. claude-user ~/.claude/agents/*.md (D7 native import)\n * 4. user ~/.hoocode/agents/*.md\n * 5. ancestor-walk <git-root..cwd>/.agents/agents/*.md\n * 6. claude-project <cwd>/.claude/agents/*.md (D7 native import)\n * 7. project <cwd>/.hoocode/agents/*.md\n * 8. cli paths injected via --agent <path>\n *\n * Higher-precedence sources override lower ones by name. Overrides are recorded\n * as collision diagnostics. Loading never throws; problems surface as\n * diagnostics, matching skills.ts.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport { EMBEDDED_AGENT_PROMPTS } from \"../init-templates.generated.js\";\nimport { type AgentDefinition, type AgentSource, parseAgentDefinition } from \"./agent-frontmatter.js\";\nimport { getAgentCliPaths, getAgentManifestPaths } from \"./agent-manifest-paths.js\";\nimport type { ResourceDiagnostic } from \"./diagnostics.js\";\n\n/** Registry of agent definitions keyed by name. */\nexport class AgentRegistry {\n\tprivate agents = new Map<string, AgentDefinition>();\n\tprivate diagnostics: ResourceDiagnostic[] = [];\n\n\t/**\n\t * Add or override a definition. Later registrations win (used both by the\n\t * loader for precedence and as an escape hatch for programmatic agents).\n\t * Overriding an existing name records a collision diagnostic.\n\t */\n\tregister(def: AgentDefinition): void {\n\t\tconst existing = this.agents.get(def.name);\n\t\tif (existing) {\n\t\t\tthis.diagnostics.push({\n\t\t\t\ttype: \"collision\",\n\t\t\t\tmessage: `agent \"${def.name}\" from ${def.source} overrides ${existing.source}`,\n\t\t\t\tpath: def.filePath,\n\t\t\t\tcollision: {\n\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\tname: def.name,\n\t\t\t\t\twinnerPath: def.filePath ?? `<${def.source}>`,\n\t\t\t\t\tloserPath: existing.filePath ?? `<${existing.source}>`,\n\t\t\t\t\twinnerSource: def.source,\n\t\t\t\t\tloserSource: existing.source,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tthis.agents.set(def.name, def);\n\t}\n\n\tget(name: string): AgentDefinition | undefined {\n\t\treturn this.agents.get(name);\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.agents.has(name);\n\t}\n\n\tlist(): AgentDefinition[] {\n\t\treturn Array.from(this.agents.values());\n\t}\n\n\t/** Diagnostics accumulated during loading/registration. */\n\tgetDiagnostics(): ResourceDiagnostic[] {\n\t\treturn this.diagnostics;\n\t}\n\n\t/** Append externally-produced diagnostics (e.g. from a parse step). */\n\taddDiagnostics(diagnostics: ResourceDiagnostic[]): void {\n\t\tthis.diagnostics.push(...diagnostics);\n\t}\n}\n\n/** Load and register every built-in (embedded) agent definition. */\nfunction registerBuiltins(registry: AgentRegistry): void {\n\tfor (const [key, raw] of Object.entries(EMBEDDED_AGENT_PROMPTS)) {\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source: \"builtin\", fallbackName: key });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t}\n}\n\n/** Load flat `*.md` agent files from a directory. Non-`.md` entries and\n * subdirectories are skipped (so runtime dispatch dirs are ignored). */\nfunction registerDir(registry: AgentRegistry, dir: string, source: AgentSource): void {\n\tif (!existsSync(dir)) return;\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") || !entry.endsWith(\".md\")) continue;\n\t\tconst filePath = join(dir, entry);\n\t\ttry {\n\t\t\tif (!statSync(filePath).isFile()) continue;\n\t\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\t\tregistry.addDiagnostics(diagnostics);\n\t\t\tif (agent) registry.register(agent);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t\t}\n\t}\n}\n\nfunction findGitRepoRoot(startDir: string): string | null {\n\tlet dir = resolve(startDir);\n\twhile (true) {\n\t\tif (existsSync(join(dir, \".git\"))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Collect `.agents/agents/` dirs from cwd up to the git root (cwd-first order). */\nfunction collectAncestorAgentsDirs(startDir: string): string[] {\n\tconst dirs: string[] = [];\n\tconst resolvedStart = resolve(startDir);\n\tconst gitRoot = findGitRepoRoot(resolvedStart);\n\tlet dir = resolvedStart;\n\twhile (true) {\n\t\tdirs.push(join(dir, \".agents\", \"agents\"));\n\t\tif (gitRoot && dir === gitRoot) break;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn dirs;\n}\n\n/** Register a single file as an agent. */\nfunction registerFile(registry: AgentRegistry, filePath: string, source: AgentSource): void {\n\tif (!existsSync(filePath)) return;\n\ttry {\n\t\tif (!statSync(filePath).isFile()) return;\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t}\n}\n\nexport interface LoadAgentRegistryOptions {\n\t/** Working directory for project-local agents. */\n\tcwd: string;\n\t/** User agent config directory (contains `agents/` subdir). Defaults to getAgentDir(). */\n\tagentDir?: string;\n\t/** Include embedded built-in agents. Defaults to true. */\n\tincludeBuiltins?: boolean;\n\t/** Discover `.claude/agents/` directories for native Claude Code import (D7). Defaults to true. */\n\tincludeClaude?: boolean;\n\t/**\n\t * Explicit agent definition paths (files or directories), resolved relative\n\t * to `cwd` (with `~` expansion). Mirrors `skillPaths`/`promptPaths` on the\n\t * skills and prompt-template loaders. These override all discovered sources\n\t * by name but yield to CLI-injected `--agent` paths.\n\t */\n\tagentPaths?: string[];\n}\n\n/** Resolve an explicit path: expand a leading `~` and resolve against `cwd`. */\nfunction normalizeAgentPath(input: string, cwd: string): string {\n\tconst trimmed = input.trim();\n\tlet expanded = trimmed;\n\tif (trimmed === \"~\") expanded = homedir();\n\telse if (trimmed.startsWith(\"~/\")) expanded = join(homedir(), trimmed.slice(2));\n\telse if (trimmed.startsWith(\"~\")) expanded = join(homedir(), trimmed.slice(1));\n\treturn resolve(cwd, expanded);\n}\n\n/** Build an AgentRegistry from all configured locations, applying precedence. */\nexport function loadAgentRegistry(options: LoadAgentRegistryOptions): AgentRegistry {\n\tconst { cwd, includeBuiltins = true, includeClaude = true } = options;\n\tconst userAgentDir = options.agentDir ?? getAgentDir();\n\tconst registry = new AgentRegistry();\n\n\t// Lowest precedence first; later sources override earlier ones by name.\n\tif (includeBuiltins) {\n\t\tregisterBuiltins(registry);\n\t}\n\n\t// Package-manifest agents (declared via `hoocode.agents` in package.json).\n\tfor (const filePath of getAgentManifestPaths()) {\n\t\tregisterFile(registry, filePath, \"user\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, join(homedir(), \".claude\", \"agents\"), \"claude-user\");\n\t}\n\tregisterDir(registry, join(userAgentDir, \"agents\"), \"user\");\n\n\t// Ancestor-walk .agents/agents/ dirs (git-root first so cwd-level overrides ancestors).\n\tfor (const dir of collectAncestorAgentsDirs(cwd).reverse()) {\n\t\tregisterDir(registry, dir, \"project\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, resolve(cwd, \".claude\", \"agents\"), \"claude-project\");\n\t}\n\tregisterDir(registry, resolve(cwd, CONFIG_DIR_NAME, \"agents\"), \"project\");\n\n\t// Explicit caller-provided paths override discovered sources (files or dirs).\n\tfor (const rawPath of options.agentPaths ?? []) {\n\t\tconst p = normalizeAgentPath(rawPath, cwd);\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"project\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"project\");\n\t\t}\n\t}\n\n\t// CLI-injected paths have highest precedence (support both files and dirs).\n\tfor (const p of getAgentCliPaths()) {\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"user\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"user\");\n\t\t}\n\t}\n\n\treturn registry;\n}\n\n/**\n * Format a list of agent definitions as an XML block for inclusion in a system\n * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.\n * Only intended for display when the Task tool is active.\n */\nexport function formatAgentsForPrompt(agents: AgentDefinition[]): string {\n\tif (agents.length === 0) return \"\";\n\n\tconst lines = [\n\t\t\"\\n\\nThe following specialized agents are available for delegation via the Task tool.\",\n\t\t\"Choose the agent whose description best matches the task and pass it as `subagent_type`.\",\n\t\t\"\",\n\t\t\"<available_agents>\",\n\t];\n\n\tfor (const agent of agents) {\n\t\tlines.push(\" <agent>\");\n\t\tlines.push(` <name>${escapeXml(agent.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(agent.description)}</description>`);\n\t\tif (agent.tools && agent.tools.length > 0) {\n\t\t\tlines.push(` <tools>${escapeXml(agent.tools.join(\", \"))}</tools>`);\n\t\t}\n\t\tif (agent.model) {\n\t\t\tlines.push(` <model>${escapeXml(agent.model)}</model>`);\n\t\t}\n\t\tlines.push(\" </agent>\");\n\t}\n\n\tlines.push(\"</available_agents>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}
1
+ {"version":3,"file":"agent-registry.js","sourceRoot":"","sources":["../../src/core/agent-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent registry: data-driven loading of subagent definitions.\n *\n * Replaces the hardcoded `SubagentMode` enum + `MODE_TOOLS` map with frontmatter\n * `.md` files (see agent-frontmatter.ts). Definitions are discovered from, in\n * increasing order of precedence:\n *\n * 1. builtin embedded templates (EMBEDDED_AGENT_PROMPTS)\n * 2. package-manifest paths from hoocode.agents in package.json\n * 3. claude-user ~/.claude/agents/*.md (D7 native import)\n * 4. user ~/.hoocode/agents/*.md\n * 5. ancestor-walk <git-root..cwd>/.agents/agents/*.md\n * 6. claude-project <cwd>/.claude/agents/*.md (D7 native import)\n * 7. project <cwd>/.hoocode/agents/*.md\n * 8. cli paths injected via --agent <path>\n *\n * Higher-precedence sources override lower ones by name. Overrides are recorded\n * as collision diagnostics. Loading never throws; problems surface as\n * diagnostics, matching skills.ts.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport { EMBEDDED_AGENT_PROMPTS } from \"../init-templates.generated.js\";\nimport { type AgentDefinition, type AgentSource, parseAgentDefinition } from \"./agent-frontmatter.js\";\nimport { getAgentCliPaths, getAgentManifestPaths } from \"./agent-manifest-paths.js\";\nimport type { ResourceDiagnostic } from \"./diagnostics.js\";\n\n/** Registry of agent definitions keyed by name. */\nexport class AgentRegistry {\n\tprivate agents = new Map<string, AgentDefinition>();\n\tprivate diagnostics: ResourceDiagnostic[] = [];\n\n\t/**\n\t * Add or override a definition. Later registrations win (used both by the\n\t * loader for precedence and as an escape hatch for programmatic agents).\n\t * Overriding an existing name records a collision diagnostic.\n\t */\n\tregister(def: AgentDefinition): void {\n\t\tconst existing = this.agents.get(def.name);\n\t\tif (existing) {\n\t\t\tthis.diagnostics.push({\n\t\t\t\ttype: \"collision\",\n\t\t\t\tmessage: `agent \"${def.name}\" from ${def.source} overrides ${existing.source}`,\n\t\t\t\tpath: def.filePath,\n\t\t\t\tcollision: {\n\t\t\t\t\tresourceType: \"skill\",\n\t\t\t\t\tname: def.name,\n\t\t\t\t\twinnerPath: def.filePath ?? `<${def.source}>`,\n\t\t\t\t\tloserPath: existing.filePath ?? `<${existing.source}>`,\n\t\t\t\t\twinnerSource: def.source,\n\t\t\t\t\tloserSource: existing.source,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\tthis.agents.set(def.name, def);\n\t}\n\n\tget(name: string): AgentDefinition | undefined {\n\t\treturn this.agents.get(name);\n\t}\n\n\thas(name: string): boolean {\n\t\treturn this.agents.has(name);\n\t}\n\n\tlist(): AgentDefinition[] {\n\t\treturn Array.from(this.agents.values());\n\t}\n\n\t/** Diagnostics accumulated during loading/registration. */\n\tgetDiagnostics(): ResourceDiagnostic[] {\n\t\treturn this.diagnostics;\n\t}\n\n\t/** Append externally-produced diagnostics (e.g. from a parse step). */\n\taddDiagnostics(diagnostics: ResourceDiagnostic[]): void {\n\t\tthis.diagnostics.push(...diagnostics);\n\t}\n}\n\n/** Load and register every built-in (embedded) agent definition. */\nfunction registerBuiltins(registry: AgentRegistry): void {\n\tfor (const [key, raw] of Object.entries(EMBEDDED_AGENT_PROMPTS)) {\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source: \"builtin\", fallbackName: key });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t}\n}\n\n/** Load flat `*.md` agent files from a directory. Non-`.md` entries and\n * subdirectories are skipped (so runtime dispatch dirs are ignored). */\nfunction registerDir(registry: AgentRegistry, dir: string, source: AgentSource): void {\n\tif (!existsSync(dir)) return;\n\tlet entries: string[];\n\ttry {\n\t\tentries = readdirSync(dir);\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const entry of entries) {\n\t\tif (entry.startsWith(\".\") || !entry.endsWith(\".md\")) continue;\n\t\tconst filePath = join(dir, entry);\n\t\ttry {\n\t\t\tif (!statSync(filePath).isFile()) continue;\n\t\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\t\tregistry.addDiagnostics(diagnostics);\n\t\t\tif (agent) registry.register(agent);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t\t}\n\t}\n}\n\nfunction findGitRepoRoot(startDir: string): string | null {\n\tlet dir = resolve(startDir);\n\twhile (true) {\n\t\tif (existsSync(join(dir, \".git\"))) return dir;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Collect `.agents/agents/` dirs from cwd up to the git root (cwd-first order). */\nfunction collectAncestorAgentsDirs(startDir: string): string[] {\n\tconst dirs: string[] = [];\n\tconst resolvedStart = resolve(startDir);\n\tconst gitRoot = findGitRepoRoot(resolvedStart);\n\tlet dir = resolvedStart;\n\twhile (true) {\n\t\tdirs.push(join(dir, \".agents\", \"agents\"));\n\t\tif (gitRoot && dir === gitRoot) break;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn dirs;\n}\n\n/** Register a single file as an agent. */\nfunction registerFile(registry: AgentRegistry, filePath: string, source: AgentSource): void {\n\tif (!existsSync(filePath)) return;\n\ttry {\n\t\tif (!statSync(filePath).isFile()) return;\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\tconst { agent, diagnostics } = parseAgentDefinition(raw, { source, filePath });\n\t\tregistry.addDiagnostics(diagnostics);\n\t\tif (agent) registry.register(agent);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"failed to read agent file\";\n\t\tregistry.addDiagnostics([{ type: \"warning\", message, path: filePath }]);\n\t}\n}\n\nexport interface LoadAgentRegistryOptions {\n\t/** Working directory for project-local agents. */\n\tcwd: string;\n\t/** User agent config directory (contains `agents/` subdir). Defaults to getAgentDir(). */\n\tagentDir?: string;\n\t/** Include embedded built-in agents. Defaults to true. */\n\tincludeBuiltins?: boolean;\n\t/** Discover `.claude/agents/` directories for native Claude Code import (D7). Defaults to true. */\n\tincludeClaude?: boolean;\n\t/**\n\t * Explicit agent definition paths (files or directories), resolved relative\n\t * to `cwd` (with `~` expansion). Mirrors `skillPaths`/`promptPaths` on the\n\t * skills and prompt-template loaders. These override all discovered sources\n\t * by name but yield to CLI-injected `--agent` paths.\n\t */\n\tagentPaths?: string[];\n}\n\n/** Resolve an explicit path: expand a leading `~` and resolve against `cwd`. */\nfunction normalizeAgentPath(input: string, cwd: string): string {\n\tconst trimmed = input.trim();\n\tlet expanded = trimmed;\n\tif (trimmed === \"~\") expanded = homedir();\n\telse if (trimmed.startsWith(\"~/\")) expanded = join(homedir(), trimmed.slice(2));\n\telse if (trimmed.startsWith(\"~\")) expanded = join(homedir(), trimmed.slice(1));\n\treturn resolve(cwd, expanded);\n}\n\n/** Build an AgentRegistry from all configured locations, applying precedence. */\nexport function loadAgentRegistry(options: LoadAgentRegistryOptions): AgentRegistry {\n\tconst { cwd, includeBuiltins = true, includeClaude = true } = options;\n\tconst userAgentDir = options.agentDir ?? getAgentDir();\n\tconst registry = new AgentRegistry();\n\n\t// Lowest precedence first; later sources override earlier ones by name.\n\tif (includeBuiltins) {\n\t\tregisterBuiltins(registry);\n\t}\n\n\t// Package-manifest agents (declared via `hoocode.agents` in package.json).\n\tfor (const filePath of getAgentManifestPaths()) {\n\t\tregisterFile(registry, filePath, \"user\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, join(homedir(), \".claude\", \"agents\"), \"claude-user\");\n\t}\n\tregisterDir(registry, join(userAgentDir, \"agents\"), \"user\");\n\n\t// Ancestor-walk .agents/agents/ dirs (git-root first so cwd-level overrides ancestors).\n\tfor (const dir of collectAncestorAgentsDirs(cwd).reverse()) {\n\t\tregisterDir(registry, dir, \"project\");\n\t}\n\n\tif (includeClaude) {\n\t\tregisterDir(registry, resolve(cwd, \".claude\", \"agents\"), \"claude-project\");\n\t}\n\tregisterDir(registry, resolve(cwd, CONFIG_DIR_NAME, \"agents\"), \"project\");\n\n\t// Explicit caller-provided paths override discovered sources (files or dirs).\n\tfor (const rawPath of options.agentPaths ?? []) {\n\t\tconst p = normalizeAgentPath(rawPath, cwd);\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"project\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"project\");\n\t\t}\n\t}\n\n\t// CLI-injected paths have highest precedence (support both files and dirs).\n\tfor (const p of getAgentCliPaths()) {\n\t\tif (!existsSync(p)) {\n\t\t\tregistry.addDiagnostics([{ type: \"warning\", message: `Agent path does not exist: ${p}`, path: p }]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (statSync(p).isDirectory()) {\n\t\t\tregisterDir(registry, p, \"user\");\n\t\t} else {\n\t\t\tregisterFile(registry, p, \"user\");\n\t\t}\n\t}\n\n\treturn registry;\n}\n\n/**\n * Format a list of agent definitions as an XML block for inclusion in a system\n * prompt, mirroring the `<available_skills>` format used by formatSkillsForPrompt.\n * Only intended for display when the Task tool is active.\n */\n/**\n * Condense a (possibly multi-line, bulleted) agent description into a single\n * useful one-liner.\n *\n * Built-in agent descriptions open with a boilerplate header (\"Use this\n * subagent ONLY when:\") followed by \"when to use\" bullets and a \"DO NOT use\"\n * section. Taking the first line alone yields that identical header for every\n * agent, so instead surface the first meaningful bullets (or the first prose\n * line) from the positive \"when to use\" region.\n */\nexport function summarizeAgentDescription(description: string): string {\n\tconst lines = description\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter((line) => line.length > 0);\n\tif (lines.length === 0) return \"\";\n\n\t// Keep only the positive region: everything before a \"DO NOT use\" section.\n\tconst stop = lines.findIndex((line) => /^(do\\s*not|don'?t|avoid)\\b/i.test(line));\n\tconst region = stop === -1 ? lines : lines.slice(0, stop);\n\n\t// Drop a leading header line (e.g. \"Use this subagent ONLY when:\").\n\tconst body = region.length > 1 && region[0]!.endsWith(\":\") ? region.slice(1) : region;\n\n\tconst stripBullet = (line: string) => line.replace(/^[-*•]\\s+/, \"\").trim();\n\tconst bullets = body\n\t\t.filter((line) => /^[-*•]\\s+/.test(line))\n\t\t.map(stripBullet)\n\t\t.filter((line) => line.length > 0);\n\n\tconst summary = bullets.length > 0 ? bullets.slice(0, 3).join(\"; \") : (body[0] ?? lines[0] ?? \"\").replace(/:$/, \"\");\n\n\tconst MAX = 200;\n\treturn summary.length > MAX ? `${summary.slice(0, MAX - 1).trimEnd()}…` : summary;\n}\n\nexport function formatAgentsForPrompt(agents: AgentDefinition[]): string {\n\tif (agents.length === 0) return \"\";\n\n\tconst lines = [\n\t\t\"\\n\\nThe following specialized agents are available for delegation via the Task tool.\",\n\t\t\"Choose the agent whose description best matches the task and pass it as `subagent_type`.\",\n\t\t\"\",\n\t\t\"<available_agents>\",\n\t];\n\n\tfor (const agent of agents) {\n\t\tlines.push(\" <agent>\");\n\t\tlines.push(` <name>${escapeXml(agent.name)}</name>`);\n\t\t// Summarized (one positive \"when to use\" line) rather than the full\n\t\t// description: the roster is rendered every turn and the full text — with its\n\t\t// \"DO NOT use\"/Cost/Isolation metadata — costs ~2x the tokens for routing\n\t\t// detail the parent rarely needs once it has picked an agent.\n\t\tlines.push(` <description>${escapeXml(summarizeAgentDescription(agent.description))}</description>`);\n\t\tif (agent.tools && agent.tools.length > 0) {\n\t\t\tlines.push(` <tools>${escapeXml(agent.tools.join(\", \"))}</tools>`);\n\t\t}\n\t\tif (agent.model) {\n\t\t\tlines.push(` <model>${escapeXml(agent.model)}</model>`);\n\t\t}\n\t\tlines.push(\" </agent>\");\n\t}\n\n\tlines.push(\"</available_agents>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/core/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom message types and transformers for the coding agent.\n *\n * Extends the base AgentMessage type with coding-agent specific message types,\n * and provides a transformer to convert them to LLM-compatible messages.\n */\n\nimport type { AgentMessage, BackgroundToolResult } from \"@kolisachint/hoocode-agent-core\";\nimport type { ImageContent, Message, TextContent } from \"@kolisachint/hoocode-ai\";\n\nexport const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:\n\n<summary>\n`;\n\nexport const COMPACTION_SUMMARY_SUFFIX = `\n</summary>`;\n\nexport const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:\n\n<summary>\n`;\n\nexport const BRANCH_SUMMARY_SUFFIX = `</summary>`;\n\n/**\n * Message type for bash executions via the ! command.\n */\nexport interface BashExecutionMessage {\n\trole: \"bashExecution\";\n\tcommand: string;\n\toutput: string;\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\tfullOutputPath?: string;\n\ttimestamp: number;\n\t/** If true, this message is excluded from LLM context (!! prefix) */\n\texcludeFromContext?: boolean;\n}\n\n/**\n * Message type for extension-injected messages via sendMessage().\n * These are custom messages that extensions can inject into the conversation.\n */\nexport interface CustomMessage<T = unknown> {\n\trole: \"custom\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdisplay: boolean;\n\tdetails?: T;\n\ttimestamp: number;\n}\n\nexport interface BranchSummaryMessage {\n\trole: \"branchSummary\";\n\tsummary: string;\n\tfromId: string;\n\ttimestamp: number;\n}\n\nexport interface CompactionSummaryMessage {\n\trole: \"compactionSummary\";\n\tsummary: string;\n\ttokensBefore: number;\n\t/** Estimated context tokens after compaction; absent on entries written before this field existed. */\n\ttokensAfter?: number;\n\ttimestamp: number;\n}\n\n// Extend CustomAgentMessages via declaration merging\ndeclare module \"@kolisachint/hoocode-agent-core\" {\n\tinterface CustomAgentMessages {\n\t\tbashExecution: BashExecutionMessage;\n\t\tcustom: CustomMessage;\n\t\tbranchSummary: BranchSummaryMessage;\n\t\tcompactionSummary: CompactionSummaryMessage;\n\t}\n}\n\n/**\n * Convert a BashExecutionMessage to user message text for LLM context.\n */\nexport function bashExecutionToText(msg: BashExecutionMessage): string {\n\tlet text = `Ran \\`${msg.command}\\`\\n`;\n\tif (msg.output) {\n\t\ttext += `\\`\\`\\`\\n${msg.output}\\n\\`\\`\\``;\n\t} else {\n\t\ttext += \"(no output)\";\n\t}\n\tif (msg.cancelled) {\n\t\ttext += \"\\n\\n(command cancelled)\";\n\t} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {\n\t\ttext += `\\n\\nCommand exited with code ${msg.exitCode}`;\n\t}\n\tif (msg.truncated && msg.fullOutputPath) {\n\t\ttext += `\\n\\n[Output truncated. Full output: ${msg.fullOutputPath}]`;\n\t}\n\treturn text;\n}\n\nexport function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage {\n\treturn {\n\t\trole: \"branchSummary\",\n\t\tsummary,\n\t\tfromId,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\nexport function createCompactionSummaryMessage(\n\tsummary: string,\n\ttokensBefore: number,\n\ttimestamp: string,\n\ttokensAfter?: number,\n): CompactionSummaryMessage {\n\treturn {\n\t\trole: \"compactionSummary\",\n\t\tsummary: summary,\n\t\ttokensBefore,\n\t\ttokensAfter,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/** customType used for the follow-up message a finished background tool injects. */\nexport const BACKGROUND_TASK_CUSTOM_TYPE = \"backgroundTask\";\n\n/** Minimal shape of a tool call, shared by the placeholder and follow-up builders. */\ninterface BackgroundToolCall {\n\tname: string;\n\targuments?: Record<string, unknown>;\n}\n\n/** A consistent, human-readable description of a background tool call. */\nexport interface BackgroundToolInfo {\n\t/** True for MCP server tools (registered as `mcp_<server>_<tool>`). */\n\tisMcpTool: boolean;\n\t/** The subagent type for `Task` calls; the tool name otherwise. */\n\tsubagentType: string;\n\t/** Short label used verbatim in both the start and finish messages (kept in sync). */\n\tlabel: string;\n\t/** One-line summary of what the call is doing, derived from its arguments. */\n\tsummary?: string;\n}\n\n/** First non-empty line of a string, trimmed and capped for one-line display. */\nfunction firstLine(text: string, max = 120): string {\n\tconst line = (text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\").trim();\n\treturn line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;\n}\n\n/** Summarize a tool's arguments as up to three `key: value` pairs for a one-liner. */\nexport function summarizeArgs(args: Record<string, unknown>): string | undefined {\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(args)) {\n\t\tif (value === undefined || value === null || value === \"\") continue;\n\t\tconst rendered = typeof value === \"string\" ? value : JSON.stringify(value);\n\t\tparts.push(`${key}: ${firstLine(rendered, 48)}`);\n\t\tif (parts.length === 3) break;\n\t}\n\treturn parts.length > 0 ? parts.join(\", \") : undefined;\n}\n\n/**\n * Describe a background tool call consistently, so the \"started\" placeholder, the\n * \"finished\" follow-up message, and any UI all read the same way. Subagent `Task`\n * calls are keyed off their `subagent_type`; MCP server tools off their `mcp_`\n * prefix.\n */\nexport function describeBackgroundTool(toolCall: BackgroundToolCall): BackgroundToolInfo {\n\tconst args = toolCall.arguments ?? {};\n\tconst isMcpTool = toolCall.name.startsWith(\"mcp_\");\n\n\tif (isMcpTool) {\n\t\t// Registered name is `mcp_<server>_<tool>`; drop the prefix for display.\n\t\tconst pretty = toolCall.name.replace(/^mcp_/, \"\");\n\t\treturn { isMcpTool, subagentType: toolCall.name, label: `MCP tool \\`${pretty}\\``, summary: summarizeArgs(args) };\n\t}\n\n\tconst subagentType = typeof args.subagent_type === \"string\" ? args.subagent_type : toolCall.name;\n\tconst summary =\n\t\ttypeof args.description === \"string\" && args.description.trim()\n\t\t\t? args.description.trim()\n\t\t\t: typeof args.prompt === \"string\"\n\t\t\t\t? firstLine(args.prompt)\n\t\t\t\t: undefined;\n\treturn { isMcpTool, subagentType, label: `subagent \\`${subagentType}\\``, summary };\n}\n\n/**\n * Verbose, human-readable placeholder shown the moment a background tool is\n * dispatched (before its result is known). Wired into the agent loop via\n * `createBackgroundPlaceholder`, it replaces the generic \"Started X in the\n * background\" line so the demo legibly explains what each background agent / MCP\n * tool is doing. Kept in sync with the finish message via {@link describeBackgroundTool}.\n */\nexport function createBackgroundPlaceholderText(toolCall: BackgroundToolCall): string {\n\tconst info = describeBackgroundTool(toolCall);\n\tconst lead = info.isMcpTool\n\t\t? `Started ${info.label} in the background — it runs on an external MCP server and may take a while.`\n\t\t: `Delegated to ${info.label} in the background — it runs in its own isolated context and cannot see this conversation.`;\n\tconst what = info.summary ? `\\nTask: ${info.summary}` : \"\";\n\treturn `${lead}${what}\\nIts result will arrive here as a follow-up message once it finishes — keep working in the meantime; you do not need to poll for it.`;\n}\n\n/**\n * Build the follow-up message injected when a background tool finishes.\n *\n * Handles both subagent `Task` calls and MCP tools, described consistently with\n * the start placeholder via {@link describeBackgroundTool}. Rendered as a distinct\n * custom message rather than a plain user message, and converted to a user message\n * for the LLM.\n */\nexport function createBackgroundTaskMessage(result: BackgroundToolResult): CustomMessage {\n\tconst info = describeBackgroundTool(result.toolCall);\n\tconst verb = result.isError ? \"failed\" : \"finished\";\n\tconst summary = info.summary ? ` (${info.summary})` : \"\";\n\tconst header = `Background ${info.label}${summary} ${verb}:`;\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType: BACKGROUND_TASK_CUSTOM_TYPE,\n\t\tcontent: [{ type: \"text\", text: header }, ...result.result.content],\n\t\tdisplay: true,\n\t\tdetails: { subagentType: info.subagentType, isMcpTool: info.isMcpTool, isError: result.isError },\n\t\ttimestamp: Date.now(),\n\t};\n}\n\n/** Convert CustomMessageEntry to AgentMessage format */\nexport function createCustomMessage(\n\tcustomType: string,\n\tcontent: string | (TextContent | ImageContent)[],\n\tdisplay: boolean,\n\tdetails: unknown | undefined,\n\ttimestamp: string,\n): CustomMessage {\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType,\n\t\tcontent,\n\t\tdisplay,\n\t\tdetails,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/**\n * Transform AgentMessages (including custom types) to LLM-compatible Messages.\n *\n * This is used by:\n * - Agent's transormToLlm option (for prompt calls and queued messages)\n * - Compaction's generateSummary (for summarization)\n * - Custom extensions and tools\n */\nexport function convertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages\n\t\t.map((m): Message | undefined => {\n\t\t\tswitch (m.role) {\n\t\t\t\tcase \"bashExecution\":\n\t\t\t\t\t// Skip messages excluded from context (!! prefix)\n\t\t\t\t\tif (m.excludeFromContext) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: bashExecutionToText(m) }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"custom\": {\n\t\t\t\t\tconst content = typeof m.content === \"string\" ? [{ type: \"text\" as const, text: m.content }] : m.content;\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase \"branchSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"compactionSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\" as const, text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },\n\t\t\t\t\t\t],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"user\":\n\t\t\t\tcase \"assistant\":\n\t\t\t\tcase \"toolResult\":\n\t\t\t\t\treturn m;\n\t\t\t\tdefault:\n\t\t\t\t\t// biome-ignore lint/correctness/noSwitchDeclarations: fine\n\t\t\t\t\tconst _exhaustiveCheck: never = m;\n\t\t\t\t\treturn undefined;\n\t\t\t}\n\t\t})\n\t\t.filter((m) => m !== undefined);\n}\n"]}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/core/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom message types and transformers for the coding agent.\n *\n * Extends the base AgentMessage type with coding-agent specific message types,\n * and provides a transformer to convert them to LLM-compatible messages.\n */\n\nimport type { AgentMessage, BackgroundToolResult } from \"@kolisachint/hoocode-agent-core\";\nimport type { ImageContent, Message, TextContent } from \"@kolisachint/hoocode-ai\";\n\nexport const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:\n\n<summary>\n`;\n\nexport const COMPACTION_SUMMARY_SUFFIX = `\n</summary>`;\n\nexport const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:\n\n<summary>\n`;\n\nexport const BRANCH_SUMMARY_SUFFIX = `</summary>`;\n\n/**\n * Message type for bash executions via the ! command.\n */\nexport interface BashExecutionMessage {\n\trole: \"bashExecution\";\n\tcommand: string;\n\toutput: string;\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\tfullOutputPath?: string;\n\ttimestamp: number;\n\t/** If true, this message is excluded from LLM context (!! prefix) */\n\texcludeFromContext?: boolean;\n}\n\n/**\n * Message type for extension-injected messages via sendMessage().\n * These are custom messages that extensions can inject into the conversation.\n */\nexport interface CustomMessage<T = unknown> {\n\trole: \"custom\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdisplay: boolean;\n\tdetails?: T;\n\ttimestamp: number;\n}\n\nexport interface BranchSummaryMessage {\n\trole: \"branchSummary\";\n\tsummary: string;\n\tfromId: string;\n\ttimestamp: number;\n}\n\nexport interface CompactionSummaryMessage {\n\trole: \"compactionSummary\";\n\tsummary: string;\n\ttokensBefore: number;\n\t/** Estimated context tokens after compaction; absent on entries written before this field existed. */\n\ttokensAfter?: number;\n\ttimestamp: number;\n}\n\n// Extend CustomAgentMessages via declaration merging\ndeclare module \"@kolisachint/hoocode-agent-core\" {\n\tinterface CustomAgentMessages {\n\t\tbashExecution: BashExecutionMessage;\n\t\tcustom: CustomMessage;\n\t\tbranchSummary: BranchSummaryMessage;\n\t\tcompactionSummary: CompactionSummaryMessage;\n\t}\n}\n\n/**\n * Convert a BashExecutionMessage to user message text for LLM context.\n */\nexport function bashExecutionToText(msg: BashExecutionMessage): string {\n\tlet text = `Ran \\`${msg.command}\\`\\n`;\n\tif (msg.output) {\n\t\ttext += `\\`\\`\\`\\n${msg.output}\\n\\`\\`\\``;\n\t} else {\n\t\ttext += \"(no output)\";\n\t}\n\tif (msg.cancelled) {\n\t\ttext += \"\\n\\n(command cancelled)\";\n\t} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {\n\t\ttext += `\\n\\nCommand exited with code ${msg.exitCode}`;\n\t}\n\tif (msg.truncated && msg.fullOutputPath) {\n\t\ttext += `\\n\\n[Output truncated. Full output: ${msg.fullOutputPath}]`;\n\t}\n\treturn text;\n}\n\nexport function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage {\n\treturn {\n\t\trole: \"branchSummary\",\n\t\tsummary,\n\t\tfromId,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\nexport function createCompactionSummaryMessage(\n\tsummary: string,\n\ttokensBefore: number,\n\ttimestamp: string,\n\ttokensAfter?: number,\n): CompactionSummaryMessage {\n\treturn {\n\t\trole: \"compactionSummary\",\n\t\tsummary: summary,\n\t\ttokensBefore,\n\t\ttokensAfter,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/** customType used for the follow-up message a finished background tool injects. */\nexport const BACKGROUND_TASK_CUSTOM_TYPE = \"backgroundTask\";\n\n/** Minimal shape of a tool call, shared by the placeholder and follow-up builders. */\ninterface BackgroundToolCall {\n\tname: string;\n\targuments?: Record<string, unknown>;\n}\n\n/** A consistent, human-readable description of a background tool call. */\nexport interface BackgroundToolInfo {\n\t/** True for MCP server tools (registered as `mcp_<server>_<tool>`). */\n\tisMcpTool: boolean;\n\t/** The subagent type for `Task` calls; the tool name otherwise. */\n\tsubagentType: string;\n\t/** Short label used verbatim in both the start and finish messages (kept in sync). */\n\tlabel: string;\n\t/** One-line summary of what the call is doing, derived from its arguments. */\n\tsummary?: string;\n}\n\n/** First non-empty line of a string, trimmed and capped for one-line display. */\nfunction firstLine(text: string, max = 120): string {\n\tconst line = (text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\").trim();\n\treturn line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;\n}\n\n/** Summarize a tool's arguments as up to three `key: value` pairs for a one-liner. */\nexport function summarizeArgs(args: Record<string, unknown>): string | undefined {\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(args)) {\n\t\tif (value === undefined || value === null || value === \"\") continue;\n\t\tconst rendered = typeof value === \"string\" ? value : JSON.stringify(value);\n\t\tparts.push(`${key}: ${firstLine(rendered, 48)}`);\n\t\tif (parts.length === 3) break;\n\t}\n\treturn parts.length > 0 ? parts.join(\", \") : undefined;\n}\n\n/**\n * Describe a background tool call consistently, so the \"started\" placeholder, the\n * \"finished\" follow-up message, and any UI all read the same way. Subagent `Task`\n * calls are keyed off their `subagent_type`; MCP server tools off their `mcp_`\n * prefix.\n */\nexport function describeBackgroundTool(toolCall: BackgroundToolCall): BackgroundToolInfo {\n\tconst args = toolCall.arguments ?? {};\n\tconst isMcpTool = toolCall.name.startsWith(\"mcp_\");\n\n\tif (isMcpTool) {\n\t\t// Registered name is `mcp_<server>_<tool>`; drop the prefix for display.\n\t\tconst pretty = toolCall.name.replace(/^mcp_/, \"\");\n\t\treturn { isMcpTool, subagentType: toolCall.name, label: `MCP tool \\`${pretty}\\``, summary: summarizeArgs(args) };\n\t}\n\n\tconst subagentType = typeof args.subagent_type === \"string\" ? args.subagent_type : toolCall.name;\n\tconst summary =\n\t\ttypeof args.description === \"string\" && args.description.trim()\n\t\t\t? args.description.trim()\n\t\t\t: typeof args.prompt === \"string\"\n\t\t\t\t? firstLine(args.prompt)\n\t\t\t\t: undefined;\n\treturn { isMcpTool, subagentType, label: `subagent \\`${subagentType}\\``, summary };\n}\n\n/**\n * Verbose, human-readable placeholder shown the moment a background tool is\n * dispatched (before its result is known). Wired into the agent loop via\n * `createBackgroundPlaceholder`, it replaces the generic \"Started X in the\n * background\" line so the demo legibly explains what each background agent / MCP\n * tool is doing. Kept in sync with the finish message via {@link describeBackgroundTool}.\n */\nexport function createBackgroundPlaceholderText(toolCall: BackgroundToolCall): string {\n\tconst info = describeBackgroundTool(toolCall);\n\tconst what = info.summary ? ` — ${firstLine(info.summary, 80)}` : \"\";\n\tif (info.isMcpTool) {\n\t\treturn `Started ${info.label} in the background${what}. Its result arrives as a follow-up; keep working.`;\n\t}\n\t// One compact line: a verbose multi-line placeholder per dispatch floods the\n\t// transcript when several subagents spin up at once. The finish notification\n\t// names the task and the body is pulled with TaskOutput; TaskOutput(list) shows\n\t// progress meanwhile.\n\treturn `Delegated to ${info.label} in the background${what}. I'll be notified when it finishes; use TaskOutput to check progress or read the result.`;\n}\n\n/**\n * Build the follow-up message injected when a background tool finishes.\n *\n * Handles both subagent `Task` calls and MCP tools, described consistently with\n * the start placeholder via {@link describeBackgroundTool}. Rendered as a distinct\n * custom message rather than a plain user message, and converted to a user message\n * for the LLM.\n */\nexport function createBackgroundTaskMessage(result: BackgroundToolResult): CustomMessage {\n\tconst info = describeBackgroundTool(result.toolCall);\n\t// Subagent `Task`: the tool already returns a compact, self-contained\n\t// notification (the full body is retained in the inbox for TaskOutput to pull),\n\t// so the follow-up is exactly that line — no second header, no inlined body.\n\tif (!info.isMcpTool) {\n\t\treturn {\n\t\t\trole: \"custom\",\n\t\t\tcustomType: BACKGROUND_TASK_CUSTOM_TYPE,\n\t\t\tcontent: result.result.content,\n\t\t\tdisplay: true,\n\t\t\tdetails: { subagentType: info.subagentType, isMcpTool: false, isError: result.isError },\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\t}\n\t// MCP background tool: no inbox, so deliver the header + full result body.\n\tconst verb = result.isError ? \"failed\" : \"finished\";\n\tconst summary = info.summary ? ` (${info.summary})` : \"\";\n\tconst header = `Background ${info.label}${summary} ${verb}:`;\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType: BACKGROUND_TASK_CUSTOM_TYPE,\n\t\tcontent: [{ type: \"text\", text: header }, ...result.result.content],\n\t\tdisplay: true,\n\t\tdetails: { subagentType: info.subagentType, isMcpTool: true, isError: result.isError },\n\t\ttimestamp: Date.now(),\n\t};\n}\n\n/** Convert CustomMessageEntry to AgentMessage format */\nexport function createCustomMessage(\n\tcustomType: string,\n\tcontent: string | (TextContent | ImageContent)[],\n\tdisplay: boolean,\n\tdetails: unknown | undefined,\n\ttimestamp: string,\n): CustomMessage {\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType,\n\t\tcontent,\n\t\tdisplay,\n\t\tdetails,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/**\n * Transform AgentMessages (including custom types) to LLM-compatible Messages.\n *\n * This is used by:\n * - Agent's transormToLlm option (for prompt calls and queued messages)\n * - Compaction's generateSummary (for summarization)\n * - Custom extensions and tools\n */\nexport function convertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages\n\t\t.map((m): Message | undefined => {\n\t\t\tswitch (m.role) {\n\t\t\t\tcase \"bashExecution\":\n\t\t\t\t\t// Skip messages excluded from context (!! prefix)\n\t\t\t\t\tif (m.excludeFromContext) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: bashExecutionToText(m) }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"custom\": {\n\t\t\t\t\tconst content = typeof m.content === \"string\" ? [{ type: \"text\" as const, text: m.content }] : m.content;\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase \"branchSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"compactionSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\" as const, text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },\n\t\t\t\t\t\t],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"user\":\n\t\t\t\tcase \"assistant\":\n\t\t\t\tcase \"toolResult\":\n\t\t\t\t\treturn m;\n\t\t\t\tdefault:\n\t\t\t\t\t// biome-ignore lint/correctness/noSwitchDeclarations: fine\n\t\t\t\t\tconst _exhaustiveCheck: never = m;\n\t\t\t\t\treturn undefined;\n\t\t\t}\n\t\t})\n\t\t.filter((m) => m !== undefined);\n}\n"]}
@@ -105,11 +105,15 @@ export function describeBackgroundTool(toolCall) {
105
105
  */
106
106
  export function createBackgroundPlaceholderText(toolCall) {
107
107
  const info = describeBackgroundTool(toolCall);
108
- const lead = info.isMcpTool
109
- ? `Started ${info.label} in the background — it runs on an external MCP server and may take a while.`
110
- : `Delegated to ${info.label} in the background it runs in its own isolated context and cannot see this conversation.`;
111
- const what = info.summary ? `\nTask: ${info.summary}` : "";
112
- return `${lead}${what}\nIts result will arrive here as a follow-up message once it finishes — keep working in the meantime; you do not need to poll for it.`;
108
+ const what = info.summary ? ` — ${firstLine(info.summary, 80)}` : "";
109
+ if (info.isMcpTool) {
110
+ return `Started ${info.label} in the background${what}. Its result arrives as a follow-up; keep working.`;
111
+ }
112
+ // One compact line: a verbose multi-line placeholder per dispatch floods the
113
+ // transcript when several subagents spin up at once. The finish notification
114
+ // names the task and the body is pulled with TaskOutput; TaskOutput(list) shows
115
+ // progress meanwhile.
116
+ return `Delegated to ${info.label} in the background${what}. I'll be notified when it finishes; use TaskOutput to check progress or read the result.`;
113
117
  }
114
118
  /**
115
119
  * Build the follow-up message injected when a background tool finishes.
@@ -121,6 +125,20 @@ export function createBackgroundPlaceholderText(toolCall) {
121
125
  */
122
126
  export function createBackgroundTaskMessage(result) {
123
127
  const info = describeBackgroundTool(result.toolCall);
128
+ // Subagent `Task`: the tool already returns a compact, self-contained
129
+ // notification (the full body is retained in the inbox for TaskOutput to pull),
130
+ // so the follow-up is exactly that line — no second header, no inlined body.
131
+ if (!info.isMcpTool) {
132
+ return {
133
+ role: "custom",
134
+ customType: BACKGROUND_TASK_CUSTOM_TYPE,
135
+ content: result.result.content,
136
+ display: true,
137
+ details: { subagentType: info.subagentType, isMcpTool: false, isError: result.isError },
138
+ timestamp: Date.now(),
139
+ };
140
+ }
141
+ // MCP background tool: no inbox, so deliver the header + full result body.
124
142
  const verb = result.isError ? "failed" : "finished";
125
143
  const summary = info.summary ? ` (${info.summary})` : "";
126
144
  const header = `Background ${info.label}${summary} ${verb}:`;
@@ -129,7 +147,7 @@ export function createBackgroundTaskMessage(result) {
129
147
  customType: BACKGROUND_TASK_CUSTOM_TYPE,
130
148
  content: [{ type: "text", text: header }, ...result.result.content],
131
149
  display: true,
132
- details: { subagentType: info.subagentType, isMcpTool: info.isMcpTool, isError: result.isError },
150
+ details: { subagentType: info.subagentType, isMcpTool: true, isError: result.isError },
133
151
  timestamp: Date.now(),
134
152
  };
135
153
  }
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/core/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom message types and transformers for the coding agent.\n *\n * Extends the base AgentMessage type with coding-agent specific message types,\n * and provides a transformer to convert them to LLM-compatible messages.\n */\n\nimport type { AgentMessage, BackgroundToolResult } from \"@kolisachint/hoocode-agent-core\";\nimport type { ImageContent, Message, TextContent } from \"@kolisachint/hoocode-ai\";\n\nexport const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:\n\n<summary>\n`;\n\nexport const COMPACTION_SUMMARY_SUFFIX = `\n</summary>`;\n\nexport const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:\n\n<summary>\n`;\n\nexport const BRANCH_SUMMARY_SUFFIX = `</summary>`;\n\n/**\n * Message type for bash executions via the ! command.\n */\nexport interface BashExecutionMessage {\n\trole: \"bashExecution\";\n\tcommand: string;\n\toutput: string;\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\tfullOutputPath?: string;\n\ttimestamp: number;\n\t/** If true, this message is excluded from LLM context (!! prefix) */\n\texcludeFromContext?: boolean;\n}\n\n/**\n * Message type for extension-injected messages via sendMessage().\n * These are custom messages that extensions can inject into the conversation.\n */\nexport interface CustomMessage<T = unknown> {\n\trole: \"custom\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdisplay: boolean;\n\tdetails?: T;\n\ttimestamp: number;\n}\n\nexport interface BranchSummaryMessage {\n\trole: \"branchSummary\";\n\tsummary: string;\n\tfromId: string;\n\ttimestamp: number;\n}\n\nexport interface CompactionSummaryMessage {\n\trole: \"compactionSummary\";\n\tsummary: string;\n\ttokensBefore: number;\n\t/** Estimated context tokens after compaction; absent on entries written before this field existed. */\n\ttokensAfter?: number;\n\ttimestamp: number;\n}\n\n// Extend CustomAgentMessages via declaration merging\ndeclare module \"@kolisachint/hoocode-agent-core\" {\n\tinterface CustomAgentMessages {\n\t\tbashExecution: BashExecutionMessage;\n\t\tcustom: CustomMessage;\n\t\tbranchSummary: BranchSummaryMessage;\n\t\tcompactionSummary: CompactionSummaryMessage;\n\t}\n}\n\n/**\n * Convert a BashExecutionMessage to user message text for LLM context.\n */\nexport function bashExecutionToText(msg: BashExecutionMessage): string {\n\tlet text = `Ran \\`${msg.command}\\`\\n`;\n\tif (msg.output) {\n\t\ttext += `\\`\\`\\`\\n${msg.output}\\n\\`\\`\\``;\n\t} else {\n\t\ttext += \"(no output)\";\n\t}\n\tif (msg.cancelled) {\n\t\ttext += \"\\n\\n(command cancelled)\";\n\t} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {\n\t\ttext += `\\n\\nCommand exited with code ${msg.exitCode}`;\n\t}\n\tif (msg.truncated && msg.fullOutputPath) {\n\t\ttext += `\\n\\n[Output truncated. Full output: ${msg.fullOutputPath}]`;\n\t}\n\treturn text;\n}\n\nexport function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage {\n\treturn {\n\t\trole: \"branchSummary\",\n\t\tsummary,\n\t\tfromId,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\nexport function createCompactionSummaryMessage(\n\tsummary: string,\n\ttokensBefore: number,\n\ttimestamp: string,\n\ttokensAfter?: number,\n): CompactionSummaryMessage {\n\treturn {\n\t\trole: \"compactionSummary\",\n\t\tsummary: summary,\n\t\ttokensBefore,\n\t\ttokensAfter,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/** customType used for the follow-up message a finished background tool injects. */\nexport const BACKGROUND_TASK_CUSTOM_TYPE = \"backgroundTask\";\n\n/** Minimal shape of a tool call, shared by the placeholder and follow-up builders. */\ninterface BackgroundToolCall {\n\tname: string;\n\targuments?: Record<string, unknown>;\n}\n\n/** A consistent, human-readable description of a background tool call. */\nexport interface BackgroundToolInfo {\n\t/** True for MCP server tools (registered as `mcp_<server>_<tool>`). */\n\tisMcpTool: boolean;\n\t/** The subagent type for `Task` calls; the tool name otherwise. */\n\tsubagentType: string;\n\t/** Short label used verbatim in both the start and finish messages (kept in sync). */\n\tlabel: string;\n\t/** One-line summary of what the call is doing, derived from its arguments. */\n\tsummary?: string;\n}\n\n/** First non-empty line of a string, trimmed and capped for one-line display. */\nfunction firstLine(text: string, max = 120): string {\n\tconst line = (text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\").trim();\n\treturn line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;\n}\n\n/** Summarize a tool's arguments as up to three `key: value` pairs for a one-liner. */\nexport function summarizeArgs(args: Record<string, unknown>): string | undefined {\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(args)) {\n\t\tif (value === undefined || value === null || value === \"\") continue;\n\t\tconst rendered = typeof value === \"string\" ? value : JSON.stringify(value);\n\t\tparts.push(`${key}: ${firstLine(rendered, 48)}`);\n\t\tif (parts.length === 3) break;\n\t}\n\treturn parts.length > 0 ? parts.join(\", \") : undefined;\n}\n\n/**\n * Describe a background tool call consistently, so the \"started\" placeholder, the\n * \"finished\" follow-up message, and any UI all read the same way. Subagent `Task`\n * calls are keyed off their `subagent_type`; MCP server tools off their `mcp_`\n * prefix.\n */\nexport function describeBackgroundTool(toolCall: BackgroundToolCall): BackgroundToolInfo {\n\tconst args = toolCall.arguments ?? {};\n\tconst isMcpTool = toolCall.name.startsWith(\"mcp_\");\n\n\tif (isMcpTool) {\n\t\t// Registered name is `mcp_<server>_<tool>`; drop the prefix for display.\n\t\tconst pretty = toolCall.name.replace(/^mcp_/, \"\");\n\t\treturn { isMcpTool, subagentType: toolCall.name, label: `MCP tool \\`${pretty}\\``, summary: summarizeArgs(args) };\n\t}\n\n\tconst subagentType = typeof args.subagent_type === \"string\" ? args.subagent_type : toolCall.name;\n\tconst summary =\n\t\ttypeof args.description === \"string\" && args.description.trim()\n\t\t\t? args.description.trim()\n\t\t\t: typeof args.prompt === \"string\"\n\t\t\t\t? firstLine(args.prompt)\n\t\t\t\t: undefined;\n\treturn { isMcpTool, subagentType, label: `subagent \\`${subagentType}\\``, summary };\n}\n\n/**\n * Verbose, human-readable placeholder shown the moment a background tool is\n * dispatched (before its result is known). Wired into the agent loop via\n * `createBackgroundPlaceholder`, it replaces the generic \"Started X in the\n * background\" line so the demo legibly explains what each background agent / MCP\n * tool is doing. Kept in sync with the finish message via {@link describeBackgroundTool}.\n */\nexport function createBackgroundPlaceholderText(toolCall: BackgroundToolCall): string {\n\tconst info = describeBackgroundTool(toolCall);\n\tconst lead = info.isMcpTool\n\t\t? `Started ${info.label} in the background — it runs on an external MCP server and may take a while.`\n\t\t: `Delegated to ${info.label} in the background — it runs in its own isolated context and cannot see this conversation.`;\n\tconst what = info.summary ? `\\nTask: ${info.summary}` : \"\";\n\treturn `${lead}${what}\\nIts result will arrive here as a follow-up message once it finishes — keep working in the meantime; you do not need to poll for it.`;\n}\n\n/**\n * Build the follow-up message injected when a background tool finishes.\n *\n * Handles both subagent `Task` calls and MCP tools, described consistently with\n * the start placeholder via {@link describeBackgroundTool}. Rendered as a distinct\n * custom message rather than a plain user message, and converted to a user message\n * for the LLM.\n */\nexport function createBackgroundTaskMessage(result: BackgroundToolResult): CustomMessage {\n\tconst info = describeBackgroundTool(result.toolCall);\n\tconst verb = result.isError ? \"failed\" : \"finished\";\n\tconst summary = info.summary ? ` (${info.summary})` : \"\";\n\tconst header = `Background ${info.label}${summary} ${verb}:`;\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType: BACKGROUND_TASK_CUSTOM_TYPE,\n\t\tcontent: [{ type: \"text\", text: header }, ...result.result.content],\n\t\tdisplay: true,\n\t\tdetails: { subagentType: info.subagentType, isMcpTool: info.isMcpTool, isError: result.isError },\n\t\ttimestamp: Date.now(),\n\t};\n}\n\n/** Convert CustomMessageEntry to AgentMessage format */\nexport function createCustomMessage(\n\tcustomType: string,\n\tcontent: string | (TextContent | ImageContent)[],\n\tdisplay: boolean,\n\tdetails: unknown | undefined,\n\ttimestamp: string,\n): CustomMessage {\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType,\n\t\tcontent,\n\t\tdisplay,\n\t\tdetails,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/**\n * Transform AgentMessages (including custom types) to LLM-compatible Messages.\n *\n * This is used by:\n * - Agent's transormToLlm option (for prompt calls and queued messages)\n * - Compaction's generateSummary (for summarization)\n * - Custom extensions and tools\n */\nexport function convertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages\n\t\t.map((m): Message | undefined => {\n\t\t\tswitch (m.role) {\n\t\t\t\tcase \"bashExecution\":\n\t\t\t\t\t// Skip messages excluded from context (!! prefix)\n\t\t\t\t\tif (m.excludeFromContext) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: bashExecutionToText(m) }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"custom\": {\n\t\t\t\t\tconst content = typeof m.content === \"string\" ? [{ type: \"text\" as const, text: m.content }] : m.content;\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase \"branchSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"compactionSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\" as const, text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },\n\t\t\t\t\t\t],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"user\":\n\t\t\t\tcase \"assistant\":\n\t\t\t\tcase \"toolResult\":\n\t\t\t\t\treturn m;\n\t\t\t\tdefault:\n\t\t\t\t\t// biome-ignore lint/correctness/noSwitchDeclarations: fine\n\t\t\t\t\tconst _exhaustiveCheck: never = m;\n\t\t\t\t\treturn undefined;\n\t\t\t}\n\t\t})\n\t\t.filter((m) => m !== undefined);\n}\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/core/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Custom message types and transformers for the coding agent.\n *\n * Extends the base AgentMessage type with coding-agent specific message types,\n * and provides a transformer to convert them to LLM-compatible messages.\n */\n\nimport type { AgentMessage, BackgroundToolResult } from \"@kolisachint/hoocode-agent-core\";\nimport type { ImageContent, Message, TextContent } from \"@kolisachint/hoocode-ai\";\n\nexport const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary:\n\n<summary>\n`;\n\nexport const COMPACTION_SUMMARY_SUFFIX = `\n</summary>`;\n\nexport const BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:\n\n<summary>\n`;\n\nexport const BRANCH_SUMMARY_SUFFIX = `</summary>`;\n\n/**\n * Message type for bash executions via the ! command.\n */\nexport interface BashExecutionMessage {\n\trole: \"bashExecution\";\n\tcommand: string;\n\toutput: string;\n\texitCode: number | undefined;\n\tcancelled: boolean;\n\ttruncated: boolean;\n\tfullOutputPath?: string;\n\ttimestamp: number;\n\t/** If true, this message is excluded from LLM context (!! prefix) */\n\texcludeFromContext?: boolean;\n}\n\n/**\n * Message type for extension-injected messages via sendMessage().\n * These are custom messages that extensions can inject into the conversation.\n */\nexport interface CustomMessage<T = unknown> {\n\trole: \"custom\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdisplay: boolean;\n\tdetails?: T;\n\ttimestamp: number;\n}\n\nexport interface BranchSummaryMessage {\n\trole: \"branchSummary\";\n\tsummary: string;\n\tfromId: string;\n\ttimestamp: number;\n}\n\nexport interface CompactionSummaryMessage {\n\trole: \"compactionSummary\";\n\tsummary: string;\n\ttokensBefore: number;\n\t/** Estimated context tokens after compaction; absent on entries written before this field existed. */\n\ttokensAfter?: number;\n\ttimestamp: number;\n}\n\n// Extend CustomAgentMessages via declaration merging\ndeclare module \"@kolisachint/hoocode-agent-core\" {\n\tinterface CustomAgentMessages {\n\t\tbashExecution: BashExecutionMessage;\n\t\tcustom: CustomMessage;\n\t\tbranchSummary: BranchSummaryMessage;\n\t\tcompactionSummary: CompactionSummaryMessage;\n\t}\n}\n\n/**\n * Convert a BashExecutionMessage to user message text for LLM context.\n */\nexport function bashExecutionToText(msg: BashExecutionMessage): string {\n\tlet text = `Ran \\`${msg.command}\\`\\n`;\n\tif (msg.output) {\n\t\ttext += `\\`\\`\\`\\n${msg.output}\\n\\`\\`\\``;\n\t} else {\n\t\ttext += \"(no output)\";\n\t}\n\tif (msg.cancelled) {\n\t\ttext += \"\\n\\n(command cancelled)\";\n\t} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {\n\t\ttext += `\\n\\nCommand exited with code ${msg.exitCode}`;\n\t}\n\tif (msg.truncated && msg.fullOutputPath) {\n\t\ttext += `\\n\\n[Output truncated. Full output: ${msg.fullOutputPath}]`;\n\t}\n\treturn text;\n}\n\nexport function createBranchSummaryMessage(summary: string, fromId: string, timestamp: string): BranchSummaryMessage {\n\treturn {\n\t\trole: \"branchSummary\",\n\t\tsummary,\n\t\tfromId,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\nexport function createCompactionSummaryMessage(\n\tsummary: string,\n\ttokensBefore: number,\n\ttimestamp: string,\n\ttokensAfter?: number,\n): CompactionSummaryMessage {\n\treturn {\n\t\trole: \"compactionSummary\",\n\t\tsummary: summary,\n\t\ttokensBefore,\n\t\ttokensAfter,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/** customType used for the follow-up message a finished background tool injects. */\nexport const BACKGROUND_TASK_CUSTOM_TYPE = \"backgroundTask\";\n\n/** Minimal shape of a tool call, shared by the placeholder and follow-up builders. */\ninterface BackgroundToolCall {\n\tname: string;\n\targuments?: Record<string, unknown>;\n}\n\n/** A consistent, human-readable description of a background tool call. */\nexport interface BackgroundToolInfo {\n\t/** True for MCP server tools (registered as `mcp_<server>_<tool>`). */\n\tisMcpTool: boolean;\n\t/** The subagent type for `Task` calls; the tool name otherwise. */\n\tsubagentType: string;\n\t/** Short label used verbatim in both the start and finish messages (kept in sync). */\n\tlabel: string;\n\t/** One-line summary of what the call is doing, derived from its arguments. */\n\tsummary?: string;\n}\n\n/** First non-empty line of a string, trimmed and capped for one-line display. */\nfunction firstLine(text: string, max = 120): string {\n\tconst line = (text.split(\"\\n\").find((l) => l.trim().length > 0) ?? \"\").trim();\n\treturn line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;\n}\n\n/** Summarize a tool's arguments as up to three `key: value` pairs for a one-liner. */\nexport function summarizeArgs(args: Record<string, unknown>): string | undefined {\n\tconst parts: string[] = [];\n\tfor (const [key, value] of Object.entries(args)) {\n\t\tif (value === undefined || value === null || value === \"\") continue;\n\t\tconst rendered = typeof value === \"string\" ? value : JSON.stringify(value);\n\t\tparts.push(`${key}: ${firstLine(rendered, 48)}`);\n\t\tif (parts.length === 3) break;\n\t}\n\treturn parts.length > 0 ? parts.join(\", \") : undefined;\n}\n\n/**\n * Describe a background tool call consistently, so the \"started\" placeholder, the\n * \"finished\" follow-up message, and any UI all read the same way. Subagent `Task`\n * calls are keyed off their `subagent_type`; MCP server tools off their `mcp_`\n * prefix.\n */\nexport function describeBackgroundTool(toolCall: BackgroundToolCall): BackgroundToolInfo {\n\tconst args = toolCall.arguments ?? {};\n\tconst isMcpTool = toolCall.name.startsWith(\"mcp_\");\n\n\tif (isMcpTool) {\n\t\t// Registered name is `mcp_<server>_<tool>`; drop the prefix for display.\n\t\tconst pretty = toolCall.name.replace(/^mcp_/, \"\");\n\t\treturn { isMcpTool, subagentType: toolCall.name, label: `MCP tool \\`${pretty}\\``, summary: summarizeArgs(args) };\n\t}\n\n\tconst subagentType = typeof args.subagent_type === \"string\" ? args.subagent_type : toolCall.name;\n\tconst summary =\n\t\ttypeof args.description === \"string\" && args.description.trim()\n\t\t\t? args.description.trim()\n\t\t\t: typeof args.prompt === \"string\"\n\t\t\t\t? firstLine(args.prompt)\n\t\t\t\t: undefined;\n\treturn { isMcpTool, subagentType, label: `subagent \\`${subagentType}\\``, summary };\n}\n\n/**\n * Verbose, human-readable placeholder shown the moment a background tool is\n * dispatched (before its result is known). Wired into the agent loop via\n * `createBackgroundPlaceholder`, it replaces the generic \"Started X in the\n * background\" line so the demo legibly explains what each background agent / MCP\n * tool is doing. Kept in sync with the finish message via {@link describeBackgroundTool}.\n */\nexport function createBackgroundPlaceholderText(toolCall: BackgroundToolCall): string {\n\tconst info = describeBackgroundTool(toolCall);\n\tconst what = info.summary ? ` — ${firstLine(info.summary, 80)}` : \"\";\n\tif (info.isMcpTool) {\n\t\treturn `Started ${info.label} in the background${what}. Its result arrives as a follow-up; keep working.`;\n\t}\n\t// One compact line: a verbose multi-line placeholder per dispatch floods the\n\t// transcript when several subagents spin up at once. The finish notification\n\t// names the task and the body is pulled with TaskOutput; TaskOutput(list) shows\n\t// progress meanwhile.\n\treturn `Delegated to ${info.label} in the background${what}. I'll be notified when it finishes; use TaskOutput to check progress or read the result.`;\n}\n\n/**\n * Build the follow-up message injected when a background tool finishes.\n *\n * Handles both subagent `Task` calls and MCP tools, described consistently with\n * the start placeholder via {@link describeBackgroundTool}. Rendered as a distinct\n * custom message rather than a plain user message, and converted to a user message\n * for the LLM.\n */\nexport function createBackgroundTaskMessage(result: BackgroundToolResult): CustomMessage {\n\tconst info = describeBackgroundTool(result.toolCall);\n\t// Subagent `Task`: the tool already returns a compact, self-contained\n\t// notification (the full body is retained in the inbox for TaskOutput to pull),\n\t// so the follow-up is exactly that line — no second header, no inlined body.\n\tif (!info.isMcpTool) {\n\t\treturn {\n\t\t\trole: \"custom\",\n\t\t\tcustomType: BACKGROUND_TASK_CUSTOM_TYPE,\n\t\t\tcontent: result.result.content,\n\t\t\tdisplay: true,\n\t\t\tdetails: { subagentType: info.subagentType, isMcpTool: false, isError: result.isError },\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\t}\n\t// MCP background tool: no inbox, so deliver the header + full result body.\n\tconst verb = result.isError ? \"failed\" : \"finished\";\n\tconst summary = info.summary ? ` (${info.summary})` : \"\";\n\tconst header = `Background ${info.label}${summary} ${verb}:`;\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType: BACKGROUND_TASK_CUSTOM_TYPE,\n\t\tcontent: [{ type: \"text\", text: header }, ...result.result.content],\n\t\tdisplay: true,\n\t\tdetails: { subagentType: info.subagentType, isMcpTool: true, isError: result.isError },\n\t\ttimestamp: Date.now(),\n\t};\n}\n\n/** Convert CustomMessageEntry to AgentMessage format */\nexport function createCustomMessage(\n\tcustomType: string,\n\tcontent: string | (TextContent | ImageContent)[],\n\tdisplay: boolean,\n\tdetails: unknown | undefined,\n\ttimestamp: string,\n): CustomMessage {\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType,\n\t\tcontent,\n\t\tdisplay,\n\t\tdetails,\n\t\ttimestamp: new Date(timestamp).getTime(),\n\t};\n}\n\n/**\n * Transform AgentMessages (including custom types) to LLM-compatible Messages.\n *\n * This is used by:\n * - Agent's transormToLlm option (for prompt calls and queued messages)\n * - Compaction's generateSummary (for summarization)\n * - Custom extensions and tools\n */\nexport function convertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages\n\t\t.map((m): Message | undefined => {\n\t\t\tswitch (m.role) {\n\t\t\t\tcase \"bashExecution\":\n\t\t\t\t\t// Skip messages excluded from context (!! prefix)\n\t\t\t\t\tif (m.excludeFromContext) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: bashExecutionToText(m) }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"custom\": {\n\t\t\t\t\tconst content = typeof m.content === \"string\" ? [{ type: \"text\" as const, text: m.content }] : m.content;\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase \"branchSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"compactionSummary\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\" as const, text: COMPACTION_SUMMARY_PREFIX + m.summary + COMPACTION_SUMMARY_SUFFIX },\n\t\t\t\t\t\t],\n\t\t\t\t\t\ttimestamp: m.timestamp,\n\t\t\t\t\t};\n\t\t\t\tcase \"user\":\n\t\t\t\tcase \"assistant\":\n\t\t\t\tcase \"toolResult\":\n\t\t\t\t\treturn m;\n\t\t\t\tdefault:\n\t\t\t\t\t// biome-ignore lint/correctness/noSwitchDeclarations: fine\n\t\t\t\t\tconst _exhaustiveCheck: never = m;\n\t\t\t\t\treturn undefined;\n\t\t\t}\n\t\t})\n\t\t.filter((m) => m !== undefined);\n}\n"]}
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Model categories for subagent model selection.
3
3
  *
4
- * Categories map to explicit model IDs configured in settings.
5
- * When a category is not configured, fallback defaults are used.
4
+ * A category (`fast` | `standard` | `capable`) is a provider-neutral indirection
5
+ * that maps to an explicit model id via `settings.modelCategories`. No concrete
6
+ * model names are baked in here, so the feature never assumes a particular
7
+ * provider. When a category is not configured, it resolves to `undefined` —
8
+ * callers treat that as "no override" and fall back to the agent's or parent's
9
+ * default model.
6
10
  */
7
11
  import type { Settings } from "./settings-manager.js";
8
12
  /** Valid model category names */
@@ -10,19 +14,21 @@ export type ModelCategory = "fast" | "standard" | "capable";
10
14
  /** Check if a string is a valid model category */
11
15
  export declare function isModelCategory(value: string): value is ModelCategory;
12
16
  /**
13
- * Resolve a model category to an actual model ID.
17
+ * Resolve a model category to the model id configured for it in settings, or
18
+ * `undefined` when the category is not configured. There is no built-in default:
19
+ * an unconfigured category is a no-op so the caller keeps its existing model.
14
20
  *
15
21
  * @param category - The model category (fast, standard, capable)
16
22
  * @param settings - The current settings (may contain modelCategories config)
17
- * @returns The resolved model ID or pattern
18
23
  */
19
- export declare function resolveModelCategory(category: ModelCategory, settings?: Settings): string;
24
+ export declare function resolveModelCategory(category: ModelCategory, settings?: Settings): string | undefined;
20
25
  /**
21
- * Resolve a model string that might be a category reference.
26
+ * Resolve a model string that might be a category reference. A category resolves
27
+ * to its configured model id (or `undefined` when unconfigured); any other string
28
+ * is already a concrete model id or alias and is returned as-is.
22
29
  *
23
30
  * @param model - The model string (could be a category, alias, or full model ID)
24
31
  * @param settings - The current settings (may contain modelCategories config)
25
- * @returns The resolved model ID or pattern
26
32
  */
27
- export declare function resolveModelReference(model: string, settings?: Settings): string;
33
+ export declare function resolveModelReference(model: string, settings?: Settings): string | undefined;
28
34
  //# sourceMappingURL=model-categories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-categories.d.ts","sourceRoot":"","sources":["../../src/core/model-categories.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,iCAAiC;AACjC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAE5D,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAErE;AAYD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CASzF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQhF","sourcesContent":["/**\n * Model categories for subagent model selection.\n *\n * Categories map to explicit model IDs configured in settings.\n * When a category is not configured, fallback defaults are used.\n */\n\nimport type { Settings } from \"./settings-manager.js\";\n\n/** Valid model category names */\nexport type ModelCategory = \"fast\" | \"standard\" | \"capable\";\n\n/** Check if a string is a valid model category */\nexport function isModelCategory(value: string): value is ModelCategory {\n\treturn value === \"fast\" || value === \"standard\" || value === \"capable\";\n}\n\n/**\n * Fallback default models for each category when not configured.\n * These are provider-agnostic patterns that will be resolved via model matching.\n */\nconst CATEGORY_FALLBACKS: Record<ModelCategory, string> = {\n\tfast: \"haiku\",\n\tstandard: \"sonnet\",\n\tcapable: \"opus\",\n};\n\n/**\n * Resolve a model category to an actual model ID.\n *\n * @param category - The model category (fast, standard, capable)\n * @param settings - The current settings (may contain modelCategories config)\n * @returns The resolved model ID or pattern\n */\nexport function resolveModelCategory(category: ModelCategory, settings?: Settings): string {\n\t// Check if the category is configured in settings\n\tconst configured = settings?.modelCategories?.[category];\n\tif (configured) {\n\t\treturn configured;\n\t}\n\n\t// Use fallback default\n\treturn CATEGORY_FALLBACKS[category];\n}\n\n/**\n * Resolve a model string that might be a category reference.\n *\n * @param model - The model string (could be a category, alias, or full model ID)\n * @param settings - The current settings (may contain modelCategories config)\n * @returns The resolved model ID or pattern\n */\nexport function resolveModelReference(model: string, settings?: Settings): string {\n\t// If it's a category, resolve it\n\tif (isModelCategory(model)) {\n\t\treturn resolveModelCategory(model, settings);\n\t}\n\n\t// Otherwise return as-is (it's already a model ID or alias)\n\treturn model;\n}\n"]}
1
+ {"version":3,"file":"model-categories.d.ts","sourceRoot":"","sources":["../../src/core/model-categories.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,iCAAiC;AACjC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAE5D,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAErE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAErG;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAK5F","sourcesContent":["/**\n * Model categories for subagent model selection.\n *\n * A category (`fast` | `standard` | `capable`) is a provider-neutral indirection\n * that maps to an explicit model id via `settings.modelCategories`. No concrete\n * model names are baked in here, so the feature never assumes a particular\n * provider. When a category is not configured, it resolves to `undefined` —\n * callers treat that as \"no override\" and fall back to the agent's or parent's\n * default model.\n */\n\nimport type { Settings } from \"./settings-manager.js\";\n\n/** Valid model category names */\nexport type ModelCategory = \"fast\" | \"standard\" | \"capable\";\n\n/** Check if a string is a valid model category */\nexport function isModelCategory(value: string): value is ModelCategory {\n\treturn value === \"fast\" || value === \"standard\" || value === \"capable\";\n}\n\n/**\n * Resolve a model category to the model id configured for it in settings, or\n * `undefined` when the category is not configured. There is no built-in default:\n * an unconfigured category is a no-op so the caller keeps its existing model.\n *\n * @param category - The model category (fast, standard, capable)\n * @param settings - The current settings (may contain modelCategories config)\n */\nexport function resolveModelCategory(category: ModelCategory, settings?: Settings): string | undefined {\n\treturn settings?.modelCategories?.[category];\n}\n\n/**\n * Resolve a model string that might be a category reference. A category resolves\n * to its configured model id (or `undefined` when unconfigured); any other string\n * is already a concrete model id or alias and is returned as-is.\n *\n * @param model - The model string (could be a category, alias, or full model ID)\n * @param settings - The current settings (may contain modelCategories config)\n */\nexport function resolveModelReference(model: string, settings?: Settings): string | undefined {\n\tif (isModelCategory(model)) {\n\t\treturn resolveModelCategory(model, settings);\n\t}\n\treturn model;\n}\n"]}