@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.
- package/CHANGELOG.md +71 -0
- package/dist/core/agent-registry.d.ts +11 -0
- package/dist/core/agent-registry.d.ts.map +1 -1
- package/dist/core/agent-registry.js +36 -1
- package/dist/core/agent-registry.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +24 -6
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-categories.d.ts +14 -8
- package/dist/core/model-categories.d.ts.map +1 -1
- package/dist/core/model-categories.js +13 -24
- package/dist/core/model-categories.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-inbox.d.ts +92 -0
- package/dist/core/subagent-inbox.d.ts.map +1 -0
- package/dist/core/subagent-inbox.js +245 -0
- package/dist/core/subagent-inbox.js.map +1 -0
- package/dist/core/subagent-pool.d.ts +3 -0
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +4 -2
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/task-store.d.ts +7 -0
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +19 -0
- package/dist/core/task-store.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +20 -17
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +250 -105
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/core/tools/todo.d.ts.map +1 -1
- package/dist/core/tools/todo.js +30 -18
- package/dist/core/tools/todo.js.map +1 -1
- package/dist/init-templates.generated.d.ts.map +1 -1
- package/dist/init-templates.generated.js +3 -3
- package/dist/init-templates.generated.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- 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, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\")\n\t\t.replace(/'/g, \"'\");\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, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\")\n\t\t.replace(/'/g, \"'\");\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
|
-
|
|
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, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\")\n\t\t.replace(/'/g, \"'\");\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, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\")\n\t\t.replace(/'/g, \"'\");\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"]}
|
package/dist/core/messages.js
CHANGED
|
@@ -105,11 +105,15 @@ export function describeBackgroundTool(toolCall) {
|
|
|
105
105
|
*/
|
|
106
106
|
export function createBackgroundPlaceholderText(toolCall) {
|
|
107
107
|
const info = describeBackgroundTool(toolCall);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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:
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
|
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
|
|
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"]}
|