@kolisachint/hoocode-agent 0.5.29 → 0.5.31
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 +127 -0
- package/dist/core/agent-selection-eval.d.ts +91 -0
- package/dist/core/agent-selection-eval.d.ts.map +1 -0
- package/dist/core/agent-selection-eval.js +186 -0
- package/dist/core/agent-selection-eval.js.map +1 -0
- package/dist/core/builtin-skills.d.ts +61 -0
- package/dist/core/builtin-skills.d.ts.map +1 -0
- package/dist/core/builtin-skills.js +106 -0
- package/dist/core/builtin-skills.js.map +1 -0
- package/dist/core/extensions/plugins/default-marketplace/.agents-plugin/marketplace.json +5 -1
- package/dist/core/extensions/plugins/trigger-judge.d.ts +42 -0
- package/dist/core/extensions/plugins/trigger-judge.d.ts.map +1 -0
- package/dist/core/extensions/plugins/trigger-judge.js +121 -0
- package/dist/core/extensions/plugins/trigger-judge.js.map +1 -0
- package/dist/core/external-tools.d.ts +67 -0
- package/dist/core/external-tools.d.ts.map +1 -0
- package/dist/core/external-tools.js +120 -0
- package/dist/core/external-tools.js.map +1 -0
- package/dist/core/light.d.ts +9 -0
- package/dist/core/light.d.ts.map +1 -1
- package/dist/core/light.js +12 -4
- package/dist/core/light.js.map +1 -1
- package/dist/core/mode-prompts.d.ts +15 -3
- package/dist/core/mode-prompts.d.ts.map +1 -1
- package/dist/core/mode-prompts.js +17 -29
- package/dist/core/mode-prompts.js.map +1 -1
- package/dist/core/tools/propose-plugin.d.ts.map +1 -1
- package/dist/core/tools/propose-plugin.js +7 -7
- package/dist/core/tools/propose-plugin.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +7 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +12 -24
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/extensions/core/modes.d.ts.map +1 -1
- package/dist/extensions/core/modes.js +21 -23
- package/dist/extensions/core/modes.js.map +1 -1
- package/dist/extensions/core/scaffold.d.ts.map +1 -1
- package/dist/extensions/core/scaffold.js +28 -24
- package/dist/extensions/core/scaffold.js.map +1 -1
- package/dist/init-templates.generated.d.ts +3 -0
- package/dist/init-templates.generated.d.ts.map +1 -1
- package/dist/init-templates.generated.js +12 -0
- package/dist/init-templates.generated.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +14 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +27 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +256 -45
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +26 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +30 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +35 -0
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/modes.md +4 -0
- package/docs/plugins.md +10 -0
- package/docs/settings.md +63 -0
- package/docs/skills.md +30 -0
- package/docs/usage.md +3 -3
- 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 +5 -4
- package/templates/prompts/grill-bridge.md +1 -0
- package/templates/prompts/grill-me.md +7 -0
- package/templates/prompts/grill-plan.md +9 -0
- package/templates/prompts/task-background-agents.md +2 -0
- package/templates/prompts/task-background-none.md +1 -0
- package/templates/prompts/task-main.md +19 -0
- package/templates/skills/plugin-authoring/SKILL.md +81 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propose-plugin.js","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tpromoteDraft,\n\tremoveFromPlugin,\n\tresolvePluginPlatforms,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { PluginPlatform } from \"../extensions/plugins/formats/platform-targets.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport { formatGateFindings, runStaticGates, withFindings } from \"../extensions/plugins/gates.js\";\nimport { discardDraftDir } from \"../extensions/plugins/locations.js\";\nimport { runSmokeGate } from \"../extensions/plugins/smoke.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nconst skillSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Skill name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line trigger description (kept lazy in context).\" })),\n\t\tbody: Type.String({ description: \"SKILL.md instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst commandSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Command name (invoked as /name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line description.\" })),\n\t\tbody: Type.String({ description: \"Prompt template body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst subagentSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Subagent name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"When to dispatch this subagent.\" })),\n\t\ttools: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(): PluginPlatform[] {\n\t// No model-facing platform selection: the human picks the target ecosystem\n\t// with --platform, defaulting to claude. A plugin is a distribution unit, so\n\t// there is no vendor-neutral option here. See resolvePluginPlatforms.\n\treturn resolvePluginPlatforms();\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\ninterface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n\tevidence?: string,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\t// The gate results go in the prompt, not just the tool result. A human shown a\n\t// bare shell command has no basis to approve it; \"ran the checks, here is what\n\t// they found\" is what makes the confirmation a decision rather than a formality.\n\tconst review = evidence ? `${buildReview(id, params)}\\n\\n${evidence}` : buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW portable, reusable plugin to fill a capability gap when no marketplace plugin fits. Accepts any \" +\n\t\t\t\"capability mix — skills, slash commands, subagents, hooks, MCP servers. Authored as one self-contained \" +\n\t\t\t\"artifact usable across sessions and projects. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new portable, reusable plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: when you complete a multi-step recipe you'd plausibly repeat (or repeat the same pattern twice in one session) and SearchPlugins finds nothing that covers it, author it with ProposePlugin. Name and describe it by the capability, not the one-off task that prompted it, so it triggers again in other contexts. Passive skills/commands activate immediately and are reversible with UninstallPlugin — announce what you created and why.\",\n\t\t\t\"Author for portability: write self-contained content — no absolute or machine-specific paths, no embedded secrets or environment-specific values, no assumptions about the current repo unless that is the capability's point. Prefer relative paths and runtime discovery, and state any prerequisites in the body. The layout is the session's target platform; you never choose it.\",\n\t\t\t\"One tool for the whole plugin: put skills + a hook in a single call. The risk gate is computed from content — you don't pre-classify. Read-only subagents and skills/commands go straight through; hooks, MCP servers, or a subagent needing Bash/Write/Edit/MCP or tools:* pause for human confirmation.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing a proven-useful plugin to a marketplace stays a human action — do not do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tconst platforms = resolvePlatforms();\n\t\t\t// Scoped to the target platform's home: the same id on another platform is\n\t\t\t// a separate ecosystem artifact, not a duplicate.\n\t\t\tif (pluginExists(params.id, platforms)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A ${platforms.join(\"/\")} plugin named \"${params.id}\" already exists. ` +\n\t\t\t\t\t\t\"Use UpdatePlugin to change it, or pick another id.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Emit to an ephemeral draft first: the gates need something real to run\n\t\t\t// against, and a production home is live (Claude Code loads\n\t\t\t// ~/.claude/skills on its next session), so nothing may land there until\n\t\t\t// the checks pass and — for executable content — the human agrees.\n\t\t\tconst draft = writePluginDraft(draftFrom(params.id, params, platforms), platforms, { promote: false });\n\t\t\tlet evaluation = runStaticGates(draft.dest, { platform: platforms[0] });\n\t\t\t// G3 only for executable content, and only once the cheap gates are\n\t\t\t// happy: no point running a hook whose command G2 already rejected.\n\t\t\tif (evaluation.ok && evaluation.plugin && hasExecutable(params)) {\n\t\t\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin));\n\t\t\t}\n\t\t\tif (!evaluation.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored — it failed its checks.\\n` +\n\t\t\t\t\t\t`${formatGateFindings(evaluation)}\\nFix the issues and try again.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx, formatGateFindings(evaluation));\n\t\t\tif (!gate.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn gate.result;\n\t\t\t}\n\n\t\t\tconst dest = promoteDraft(draft.dest, params.id, platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(dest);\n\t\t\tconst text =\n\t\t\t\t`${summarizeWrite(params.id, platforms, draft.files, dest, \"Authored\")}\\n` +\n\t\t\t\t`${formatGateFindings(evaluation)}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Keep additions as portable as \" +\n\t\t\t\"the original, and in its existing layout. Passive additions apply autonomously; executable additions (hooks, MCP \" +\n\t\t\t\"servers, mutating subagents) require human confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a portable plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use UpdatePlugin to grow a plugin you already authored — e.g. add a skill to it, or attach a hook. Supply only the delta; existing capabilities are preserved (a matching name replaces just that one). It cannot remove a capability — use RemovePluginCapability for that.\",\n\t\t\t\"Keep additions portable: same content rules as ProposePlugin — no absolute paths, no secrets, capability-not-task naming.\",\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Only executable *additions* trigger confirmation — adding a passive skill to an already-executable plugin does not re-prompt.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\t// No model-facing platform selection: a merge keeps the plugin's existing\n\t\t\t// layout (mergePluginDraft defaults to existing.supportPlatform).\n\t\t\tconst result = mergePluginDraft(ctx.cwd, params.id, draftFrom(params.id, params, existing.supportPlatform));\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\ninterface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"propose-plugin.js","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tpromoteDraft,\n\tremoveFromPlugin,\n\tresolvePluginPlatforms,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { PluginPlatform } from \"../extensions/plugins/formats/platform-targets.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport { formatGateFindings, runStaticGates, withFindings } from \"../extensions/plugins/gates.js\";\nimport { discardDraftDir } from \"../extensions/plugins/locations.js\";\nimport { runSmokeGate } from \"../extensions/plugins/smoke.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nconst skillSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Skill name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line trigger description (kept lazy in context).\" })),\n\t\tbody: Type.String({ description: \"SKILL.md instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst commandSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Command name (invoked as /name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"One-line description.\" })),\n\t\tbody: Type.String({ description: \"Prompt template body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst subagentSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"Subagent name.\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"When to dispatch this subagent.\" })),\n\t\ttools: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(): PluginPlatform[] {\n\t// No model-facing platform selection: the human picks the target ecosystem\n\t// with --platform, defaulting to claude. A plugin is a distribution unit, so\n\t// there is no vendor-neutral option here. See resolvePluginPlatforms.\n\treturn resolvePluginPlatforms();\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\ninterface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n\tevidence?: string,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\t// The gate results go in the prompt, not just the tool result. A human shown a\n\t// bare shell command has no basis to approve it; \"ran the checks, here is what\n\t// they found\" is what makes the confirmation a decision rather than a formality.\n\tconst review = evidence ? `${buildReview(id, params)}\\n\\n${evidence}` : buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW portable, reusable plugin to fill a capability gap when no marketplace plugin fits. Accepts any \" +\n\t\t\t\"capability mix — skills, slash commands, subagents, hooks, MCP servers. Authored as one self-contained \" +\n\t\t\t\"artifact usable across sessions and projects. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new portable, reusable plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\t// Trimmed to the trigger and the two hard prohibitions. The craft guidance —\n\t\t// portability rules, capability-not-task naming, the one-call shape — was\n\t\t// ~600 tok/turn of always-on prose restating a how-to. It is the\n\t\t// `plugin-authoring` skill in the bundled marketplace now, read on demand.\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: a multi-step recipe you'd plausibly repeat (or repeated twice in one session) that SearchPlugins does not already cover is worth authoring. Read the plugin-authoring skill first if it is available; it decides naming, portability and shape.\",\n\t\t\t\"Announce what you authored and why — passive content activates without asking, so the user learns it exists only if you say so.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing to a marketplace stays a human action — never do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tconst platforms = resolvePlatforms();\n\t\t\t// Scoped to the target platform's home: the same id on another platform is\n\t\t\t// a separate ecosystem artifact, not a duplicate.\n\t\t\tif (pluginExists(params.id, platforms)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A ${platforms.join(\"/\")} plugin named \"${params.id}\" already exists. ` +\n\t\t\t\t\t\t\"Use UpdatePlugin to change it, or pick another id.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Emit to an ephemeral draft first: the gates need something real to run\n\t\t\t// against, and a production home is live (Claude Code loads\n\t\t\t// ~/.claude/skills on its next session), so nothing may land there until\n\t\t\t// the checks pass and — for executable content — the human agrees.\n\t\t\tconst draft = writePluginDraft(draftFrom(params.id, params, platforms), platforms, { promote: false });\n\t\t\tlet evaluation = runStaticGates(draft.dest, { platform: platforms[0] });\n\t\t\t// G3 only for executable content, and only once the cheap gates are\n\t\t\t// happy: no point running a hook whose command G2 already rejected.\n\t\t\tif (evaluation.ok && evaluation.plugin && hasExecutable(params)) {\n\t\t\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin));\n\t\t\t}\n\t\t\tif (!evaluation.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored — it failed its checks.\\n` +\n\t\t\t\t\t\t`${formatGateFindings(evaluation)}\\nFix the issues and try again.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx, formatGateFindings(evaluation));\n\t\t\tif (!gate.ok) {\n\t\t\t\tdiscardDraftDir(draft.dest);\n\t\t\t\treturn gate.result;\n\t\t\t}\n\n\t\t\tconst dest = promoteDraft(draft.dest, params.id, platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(dest);\n\t\t\tconst text =\n\t\t\t\t`${summarizeWrite(params.id, platforms, draft.files, dest, \"Authored\")}\\n` +\n\t\t\t\t`${formatGateFindings(evaluation)}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Keep additions as portable as \" +\n\t\t\t\"the original, and in its existing layout. Passive additions apply autonomously; executable additions (hooks, MCP \" +\n\t\t\t\"servers, mutating subagents) require human confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a portable plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\t// No model-facing platform selection: a merge keeps the plugin's existing\n\t\t\t// layout (mergePluginDraft defaults to existing.supportPlatform).\n\t\t\tconst result = mergePluginDraft(ctx.cwd, params.id, draftFrom(params.id, params, existing.supportPlatform));\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\ninterface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|
|
@@ -25,7 +25,13 @@ export { summarizeAgentDescription } from "../agent-registry.js";
|
|
|
25
25
|
/**
|
|
26
26
|
* Build the main-session subagent instructions appended to the system prompt.
|
|
27
27
|
*
|
|
28
|
-
* The
|
|
28
|
+
* The prose lives in `templates/prompts/task-*.md` and reaches here through the
|
|
29
|
+
* build-time embed. It is the largest block of always-on text hoocode emits
|
|
30
|
+
* (~600 tok/turn once the Task tool is on) and it had no interpolation beyond
|
|
31
|
+
* the single background slot, so keeping it as a TypeScript string constant
|
|
32
|
+
* bought nothing and made it awkward to edit as the prose it is.
|
|
33
|
+
*
|
|
34
|
+
* The detailed background/barrier guidance (the two heaviest bullets) is only
|
|
29
35
|
* emitted when the project actually has background-capable agents; otherwise a
|
|
30
36
|
* single concise line covers the per-call `background: true` escape hatch. This
|
|
31
37
|
* keeps the always-on cost down for the common case where nothing runs in the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../../src/core/tools/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,yBAAyB,CAAC;AAG/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAkB7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;;;6EAK6E;AAC7E;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CA2BvE;AAmCD,MAAM,WAAW,eAAe;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IACjC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAeD,gFAAgF;AAChF,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAsB,GAAG,cAAc,CAsUpF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,EAClC,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,GAAG,EAAE,MAAM,GACT,MAAM,GAAG,SAAS,CAQpB;AAwRD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,IAAI,cAAc,CAwL/D","sourcesContent":["/**\n * Task tool: delegate a focused task to a specialized subagent.\n *\n * Mirrors the Claude Code `Task` tool. The parent agent decides *when* to\n * delegate based on each agent's `description` (there is no deterministic gate)\n * and selects *which* agent via `subagent_type`. The chosen agent runs in a\n * fresh, isolated child process (SubagentPool) and only its final answer is\n * returned to the parent.\n *\n * Enabled by default (the `enableSubagent` setting defaults to true); disable\n * with `enableSubagent: false`. The `--enable-subagents` flag still force-enables\n * it. Nesting is bounded by the tree-wide depth cap (maxSubagentDepth, default 2:\n * a spawned subagent may itself delegate one more level, and depth-2 grandchildren\n * cannot). See buildSessionOptions in main.ts.\n */\n\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type Static, Type } from \"typebox\";\nimport { agentColorFor } from \"../../modes/interactive/theme/theme.js\";\nimport { type AgentDefinition, TASK_TOOL_NAME } from \"../agent-frontmatter.js\";\nimport { agentLog } from \"../agent-log.js\";\nimport { loadAgentRegistry } from \"../agent-registry.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { defineTool } from \"../extensions/types.js\";\nimport { formatDurationSecs } from \"../format-duration.js\";\nimport { formatTokens } from \"../format-tokens.js\";\nimport { getProviderExhaustion } from \"../provider-health.js\";\nimport { SessionManager } from \"../session-manager.js\";\nimport { delegateAllowList, isDelegateAllowed } from \"../subagent-depth.js\";\nimport { type InboxRecord, subagentInbox } from \"../subagent-inbox.js\";\nimport type { SubagentResult, TaskResult } from \"../subagent-pool.js\";\nimport { getSubagentPool } from \"../subagent-pool-instance.js\";\nimport type { SubagentResultFile, SubagentTaskNode } from \"../subagent-result.js\";\nimport { taskStore } from \"../task-store.js\";\nimport { type WarmRunResult, WarmWorkerError } from \"../warm-subagent-pool.js\";\nimport { getWarmSubagentPool, warmSubagentsEnabled } from \"../warm-subagent-pool-instance.js\";\n\n// Re-exported from its home in agent-registry (where formatAgentsForPrompt uses\n// it to render the roster) so existing importers keep working without creating a\n// tools -> registry -> tools cycle.\nexport { summarizeAgentDescription } from \"../agent-registry.js\";\n\n/** System prompt appendix for the main session when the Task tool is enabled.\n * Instructs the parent agent on when and how to delegate effectively. The\n * available agents themselves are listed once, authoritatively, in the\n * `<available_agents>` block the system prompt emits whenever the Task tool is\n * active (see agent-session `_rebuildSystemPrompt`); this appendix references\n * that list rather than re-rendering the roster and paying for it twice. */\n/**\n * Build the main-session subagent instructions appended to the system prompt.\n *\n * The detailed background/barrier guidance (the three heaviest bullets) is only\n * emitted when the project actually has background-capable agents; otherwise a\n * single concise line covers the per-call `background: true` escape hatch. This\n * keeps the always-on cost down for the common case where nothing runs in the\n * background. `cwd` is used only to detect those agents.\n */\nexport function buildTaskMainPrompt(cwd: string = process.cwd()): string {\n\tconst hasBackgroundAgents = collectBackgroundAgentNames(cwd).size > 0;\n\n\tconst backgroundGuidance = hasBackgroundAgents\n\t\t? `- Background agents run non-blocking (or force per call with \\`background: true\\`): you get a short \"explore#1 finished\" notification and pull the full result with \\`TaskOutput\\` (e.g. \\`TaskOutput(\"explore#1\")\\`); \\`TaskOutput(list: true)\\` shows what's running.\n- After dispatching, don't idle — keep doing independent work (read/edit unrelated files, draft, dispatch more); barrier with \\`TaskOutput(wait: true)\\` (a named task, or all outstanding when no id) only when you genuinely can't proceed.`\n\t\t: `- You can force any task to run in the background with \\`background: true\\` (non-blocking): keep working, then pull its result with \\`TaskOutput\\` (e.g. \\`TaskOutput(\"explore#1\")\\`) or block on it with \\`TaskOutput(wait: true)\\`.`;\n\n\treturn `You have access to the **Task** tool. Use it to delegate self-contained tasks to specialized subagents that run in their own isolated context and return only their final answer. Pick an agent by name from the <available_agents> list in this prompt and pass it as \\`subagent_type\\`.\n\nWhen to delegate:\n1. The work is self-contained and you only need the final result, not intermediate steps.\n2. You want to investigate or edit something in parallel without losing your current context or reasoning chain.\n3. The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug).\n4. You need to run a long command or test suite and wait for its output without blocking your own reasoning.\n\nModel tier (optional \\`complexity\\`): set \\`fast\\` for quick reads/lookups, \\`standard\\` for multi-file edits, \\`capable\\` for deep architecture work. It maps to a model from \\`settings.modelCategories\\`. Omit it to use the agent's default; an agent that pins its own model ignores \\`complexity\\`.\n\nGuidelines:\n- Choose the agent whose description best matches the task.\n- Make every task specific and self-contained. The subagent cannot see this conversation; pass all necessary context (files, constraints, prior findings) in \\`prompt\\`.\n- Do NOT delegate tasks that require tight back-and-forth with your current reasoning, or edits to files you are actively reasoning about.\n- The subagent returns ONLY its final answer. Its intermediate reasoning, tool calls, and output are hidden from you.\n- Delegate proactively when work is self-contained or parallelizable: multi-step investigation, read-only exploration (use \\`explore\\`), research before changes (use \\`plan\\`), drafting a standalone file/section, or running a long command/test suite. Dispatch independent subtasks in the same turn. Handle only trivial single-step edits or tightly interactive back-and-forth inline.\n${backgroundGuidance}\n- When working through a TodoWrite plan, mark the plan item in_progress BEFORE dispatching subagents for it: each dispatch is attributed to the current in_progress item in the user's task panel, so dispatching first (or with several items in_progress) leaves the run unattributed.\n- To continue a previous subagent (for example one that returned partial results), call Task again with \\`resume_task_id\\` set to its task_id; it resumes with its full prior transcript and \\`prompt\\` is your follow-up.`;\n}\n\nconst taskParams = Type.Object({\n\tdescription: Type.String({\n\t\tdescription: \"A short (3-5 word) description of the task, shown in the task panel.\",\n\t}),\n\tprompt: Type.String({\n\t\tdescription:\n\t\t\t\"The full, self-contained task for the subagent. It cannot see this conversation, so include all needed context, files, and constraints.\",\n\t}),\n\tsubagent_type: Type.String({\n\t\tdescription: \"The name of the specialized agent to delegate to. Must be one of the available agents.\",\n\t}),\n\tcomplexity: Type.Optional(\n\t\tType.Union([Type.Literal(\"fast\"), Type.Literal(\"standard\"), Type.Literal(\"capable\")], {\n\t\t\tdescription:\n\t\t\t\t\"Model tier for this dispatch: fast (quick reads/lookups), standard (multi-file edits), capable (deep architecture). Maps to settings.modelCategories. Ignored if the chosen agent pins its own model; omit to use the agent's default.\",\n\t\t}),\n\t),\n\tbackground: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Set true to run non-blocking: you get a short notification when it finishes and pull the full result with TaskOutput; set false to wait and get the answer inline. Defaults to the agent's own background setting.\",\n\t\t}),\n\t),\n\tresume_task_id: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional. To continue a previous subagent run, pass its task_id (returned by an earlier Task or TaskOutput call). The subagent resumes with its full prior transcript and `prompt` is your follow-up instruction.\",\n\t\t}),\n\t),\n});\n\ntype TaskParams = Static<typeof taskParams>;\n\nexport interface TaskToolDetails {\n\tsubagent_type: string;\n\tok: boolean;\n\terror?: string;\n\ttaskId: number;\n\t/** Pool-level task id usable for resume/polling. */\n\tpoolTaskId?: string;\n\t/** True when dispatched as a non-blocking background task. */\n\tbackground?: boolean;\n}\n\nexport interface TaskOutputDetails {\n\t/** The handle queried, when a specific task was named. */\n\ttask_id?: string;\n\t/** running | done | collected | failed | stalled | timeout | cancelled | list | empty | unknown */\n\tstatus: string;\n\tok: boolean;\n\t/** Number of subagents still running, included on roster/list responses. */\n\toutstanding?: number;\n}\n\n/**\n * A short, human-readable task name for the task panel: the first line limited\n * to ~8 words so it stays glanceable. A character cap guards a single long word.\n */\nfunction summarize(task: string): string {\n\tconst firstLine = (task.trim().split(\"\\n\")[0] ?? \"\").trim();\n\tif (!firstLine) return \"(task)\";\n\tconst words = firstLine.split(/\\s+/);\n\tlet name = words.length > 8 ? `${words.slice(0, 8).join(\" \")}…` : firstLine;\n\tif (name.length > 60) name = `${name.slice(0, 59)}…`;\n\treturn name;\n}\n\n/** Create the Task tool definition. Registered as a customTool when enabled. */\nexport function createTaskToolDefinition(cwd: string = process.cwd()): ToolDefinition {\n\t// Agents whose definitions opt into background execution. The agent loop reads\n\t// the tool's `background` flag per call and, for these, runs the dispatch\n\t// detached: the parent keeps reasoning and the subagent's answer is injected as\n\t// a follow-up message when it finishes (no polling needed). A per-call\n\t// `background` argument overrides the agent's default in either direction.\n\tconst backgroundAgents = collectBackgroundAgentNames(cwd);\n\treturn defineTool<typeof taskParams, TaskToolDetails>({\n\t\tname: TASK_TOOL_NAME,\n\t\tlabel: TASK_TOOL_NAME,\n\t\tbackground: (toolCall) => {\n\t\t\tconst override = toolCall.arguments?.background;\n\t\t\tif (typeof override === \"boolean\") return override;\n\t\t\treturn backgroundAgents.has(String(toolCall.arguments?.subagent_type ?? \"\"));\n\t\t},\n\t\t// Kept to mechanics only: the when-to-use / when-not-to guidance lives once in\n\t\t// the system-prompt block (buildTaskMainPrompt), the available agents are\n\t\t// listed there too, and the `complexity`/`background`/`resume_task_id`\n\t\t// semantics live in their parameter descriptions. Repeating any of that here\n\t\t// would re-spend those tokens on every turn.\n\t\tdescription:\n\t\t\t\"Delegate a focused task to a specialized subagent that runs in a fresh, isolated context (it cannot see this conversation). Choose one of the available agents (listed in the system prompt) via `subagent_type` and pass everything it needs via `prompt`; the subagent returns only its final answer.\",\n\t\tpromptSnippet: \"delegate a self-contained task to a specialized subagent (choose via subagent_type)\",\n\t\tparameters: taskParams,\n\n\t\tasync execute(_toolCallId, params: TaskParams, signal, _onUpdate, ctx) {\n\t\t\t// Snapshot the caller's available models so unconfigured model-category\n\t\t\t// tiers (fast/standard/capable) resolve to a derived default instead of a\n\t\t\t// no-op. Explicit settings.modelCategories still win inside the pool. The\n\t\t\t// registry can be absent in some tool contexts; an empty list just keeps\n\t\t\t// category derivation a no-op (inherit parent), so guard the access.\n\t\t\tconst availableModels = ctx.modelRegistry?.getAvailable() ?? [];\n\t\t\tconst pool = getSubagentPool(ctx.cwd, availableModels);\n\n\t\t\t// User-initiated cancel (Esc/abort): kill the dispatched run's whole\n\t\t\t// process tree and let the dispatch settle with status \"cancelled\" —\n\t\t\t// distinct from \"failed\" everywhere it surfaces (panel, inbox, result).\n\t\t\t// Cold dispatches only: a warm worker has no cancel surface (it is\n\t\t\t// reused), so warm runs still complete server-side and are discarded.\n\t\t\tconst cancelOnAbort = async (poolRunId: string, run: () => Promise<TaskResult>): Promise<TaskResult> => {\n\t\t\t\tif (!signal) return run();\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tpool.cancel?.(poolRunId);\n\t\t\t\t};\n\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\ttry {\n\t\t\t\t\treturn await run();\n\t\t\t\t} finally {\n\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Pre-flight: if the inherited provider recently exhausted its quota (the\n\t\t\t// parent's own turn failed with a usage/rate-limit error that did not\n\t\t\t// recover), skip the spawn. Subagents run on the same provider, so this\n\t\t\t// would only burn another failed attempt. The signal self-expires and is\n\t\t\t// cleared on the next successful response.\n\t\t\tconst provider = ctx.model?.provider;\n\t\t\tconst exhaustion = provider ? getProviderExhaustion(provider) : undefined;\n\t\t\tif (exhaustion) {\n\t\t\t\tconst skippedRunId = newDispatchTaskId();\n\t\t\t\tconst skipped = taskStore.create(params.description?.trim() || summarize(params.prompt), {\n\t\t\t\t\tsource: \"subagent\",\n\t\t\t\t\tsubagentMode: params.subagent_type,\n\t\t\t\t\tagent: skippedRunId,\n\t\t\t\t\tlinkedTaskId: linkedTodoId(),\n\t\t\t\t});\n\t\t\t\tregisterSubagentDispatch(skippedRunId, subagentInbox.nextLabel(params.subagent_type));\n\t\t\t\ttaskStore.update(skipped.id, { status: \"failed\", note: `${provider} exhausted` });\n\t\t\t\ttaskStore.patchAgent(skippedRunId, { state: \"failed\" });\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t`Did not dispatch subagent \"${params.subagent_type}\": the \"${provider}\" provider appears ` +\n\t\t\t\t\t\t\t\t`exhausted or rate-limited (this session just failed with: ${exhaustion.message}). ` +\n\t\t\t\t\t\t\t\t`Subagents run on the same provider, so dispatching would fail too. Wait for the quota to ` +\n\t\t\t\t\t\t\t\t`reset or switch model/provider, then retry — or complete the work directly in this session.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { subagent_type: params.subagent_type, ok: false, taskId: skipped.id },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Scoped delegation: a delegating agent may be restricted to certain subagent\n\t\t\t// types (its `delegate: <types>` frontmatter). The root is unrestricted.\n\t\t\tif (!isDelegateAllowed(params.subagent_type)) {\n\t\t\t\tconst allowed = delegateAllowList()?.join(\", \") ?? \"\";\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`This agent may not delegate to \"${params.subagent_type}\". Allowed subagent types: ${allowed || \"(none)\"}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Resume path: continue a previously dispatched subagent with a follow-up\n\t\t\t// prompt, reusing its persisted session (full prior transcript).\n\t\t\tconst resumeId = params.resume_task_id?.trim();\n\t\t\tif (resumeId) {\n\t\t\t\tconst summary = params.description?.trim() || summarize(params.prompt);\n\t\t\t\tconst runId = newDispatchTaskId();\n\t\t\t\tconst label = subagentInbox.nextLabel(params.subagent_type);\n\t\t\t\tconst task = taskStore.create(summary, {\n\t\t\t\t\tsource: \"subagent\",\n\t\t\t\t\tsubagentMode: params.subagent_type,\n\t\t\t\t\tagent: runId,\n\t\t\t\t\tlinkedTaskId: linkedTodoId(),\n\t\t\t\t});\n\t\t\t\tregisterSubagentDispatch(runId, label);\n\t\t\t\ttaskStore.update(task.id, { status: \"in_progress\" });\n\t\t\t\ttry {\n\t\t\t\t\tconst dispatchResult = await cancelOnAbort(runId, () =>\n\t\t\t\t\t\tpool.resume(resumeId, params.prompt, {\n\t\t\t\t\t\t\tmodel: ctx.model?.id,\n\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\ttaskId: runId,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\t// The session lives under the original task id; keep it as the resume handle.\n\t\t\t\t\treturn finalizeDispatchResult(dispatchResult, params.subagent_type, runId, task.id, resumeId);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tmarkDispatchFailed(task.id, runId);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// The model has already decided to delegate and which agent to use; honor\n\t\t\t// it. Validate the requested agent against the registry (no routing gate).\n\t\t\tconst registry = loadAgentRegistry({ cwd: ctx.cwd });\n\t\t\tconst def = registry.get(params.subagent_type);\n\t\t\tif (!def) {\n\t\t\t\tconst available = registry\n\t\t\t\t\t.list()\n\t\t\t\t\t.map((a) => a.name)\n\t\t\t\t\t.join(\", \");\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Unknown subagent_type: \"${params.subagent_type}\". Available agents: ${available || \"(none)\"}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst summary = params.description?.trim() || summarize(params.prompt);\n\t\t\t// One roster row per dispatch, keyed by the (pre-allocated) pool task id.\n\t\t\t// Keying by agent TYPE made concurrent same-type runs share a single row:\n\t\t\t// their activity, state, and stats collided last-writer-wins. The inbox\n\t\t\t// already keys by task id; this aligns the panel to the same model. The\n\t\t\t// pool's task_progress events carry the task id, so patching by this id\n\t\t\t// routes live activity to the right row.\n\t\t\tconst poolTaskId = newDispatchTaskId();\n\t\t\tconst label = subagentInbox.nextLabel(params.subagent_type);\n\t\t\tconst task = taskStore.create(summary, {\n\t\t\t\tsource: \"subagent\",\n\t\t\t\tsubagentMode: params.subagent_type,\n\t\t\t\tagent: poolTaskId,\n\t\t\t\t// Tie this run to the plan item it executes (the single in_progress\n\t\t\t\t// TodoWrite task, when unambiguous) so the panel can nest it there.\n\t\t\t\tlinkedTaskId: linkedTodoId(),\n\t\t\t});\n\t\t\tregisterSubagentDispatch(poolTaskId, label);\n\t\t\ttaskStore.update(task.id, { status: \"in_progress\" });\n\t\t\t// Fork agents inherit the parent's conversation via a forked session.\n\t\t\tconst forkSessionFile = def.fork\n\t\t\t\t? resolveForkSessionFile(def, ctx.sessionManager?.getSessionFile(), ctx.cwd)\n\t\t\t\t: undefined;\n\n\t\t\t// `complexity` is passed as the model: the pool's spawn() already lets a\n\t\t\t// non-`inherit` agent model win, then resolves a category string (fast/\n\t\t\t// standard/capable) via settings.modelCategories. So a pinned-model agent\n\t\t\t// ignores complexity, and an `inherit` agent picks up the requested tier —\n\t\t\t// no settings lookup needed here.\n\t\t\tconst dispatchModel = params.complexity ?? ctx.model?.id;\n\n\t\t\t// Whether this call runs detached. The agent loop reads the tool's\n\t\t\t// `background` flag (the same predicate) to run execute() detached; we\n\t\t\t// recompute it here to choose the notify-and-pull return shape.\n\t\t\tconst isBackground = params.background ?? backgroundAgents.has(params.subagent_type);\n\n\t\t\tif (isBackground) {\n\t\t\t\t// Notify-and-pull: register the dispatch in the inbox under the\n\t\t\t\t// pre-allocated id, await it, retain the body in the inbox, and return a\n\t\t\t\t// compact notification (not the body). The model pulls it with TaskOutput.\n\t\t\t\tsubagentInbox.observe(pool);\n\t\t\t\tsubagentInbox.start(poolTaskId, label, params.subagent_type);\n\n\t\t\t\t// Warm path (opt-in): run on a reused RPC worker, retain the body in the\n\t\t\t\t// inbox, and return the same notify-and-pull shape as the cold path. An\n\t\t\t\t// infra failure falls through to the cold dispatch below.\n\t\t\t\tif (warmSubagentsEnabled() && !forkSessionFile) {\n\t\t\t\t\tconst warm = getWarmSubagentPool(ctx.cwd, availableModels);\n\t\t\t\t\tif (warm.isPoolable(params.subagent_type)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst warmResult = await warm.dispatch(\n\t\t\t\t\t\t\t\tparams.prompt,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tagentType: params.subagent_type,\n\t\t\t\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t(activity) => taskStore.patchAgent(poolTaskId, { activity }),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst dispatchResult = warmResultToTaskResult(warmResult, params.subagent_type, task.id);\n\t\t\t\t\t\t\tsubagentInbox.finish(poolTaskId, dispatchResult);\n\t\t\t\t\t\t\treturn finalizeDispatchResult(\n\t\t\t\t\t\t\t\tdispatchResult,\n\t\t\t\t\t\t\t\tparams.subagent_type,\n\t\t\t\t\t\t\t\tpoolTaskId,\n\t\t\t\t\t\t\t\ttask.id,\n\t\t\t\t\t\t\t\tpoolTaskId,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t\t\t\t\tlabel,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tif (!(error instanceof WarmWorkerError)) throw error;\n\t\t\t\t\t\t\tagentLog(`[WARM] ${params.subagent_type} fell back to cold spawn: ${error.message}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst dispatchResult = await cancelOnAbort(poolTaskId, () =>\n\t\t\t\t\t\tpool.dispatch(params.prompt, {\n\t\t\t\t\t\t\tforceAgent: params.subagent_type,\n\t\t\t\t\t\t\tcontext: \"\",\n\t\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\tsessionFile: forkSessionFile,\n\t\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tsubagentInbox.finish(poolTaskId, dispatchResult);\n\t\t\t\t\treturn finalizeDispatchResult(dispatchResult, params.subagent_type, poolTaskId, task.id, poolTaskId, {\n\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tmarkDispatchFailed(task.id, poolTaskId);\n\t\t\t\t\tsubagentInbox.fail(poolTaskId, reason);\n\t\t\t\t\t// A background dispatch reports failure as a compact notification, not a\n\t\t\t\t\t// thrown tool error — the call was already answered by a placeholder.\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `${label} failed ✗ — ${reason}` }],\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tsubagent_type: params.subagent_type,\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\terror: reason,\n\t\t\t\t\t\t\ttaskId: task.id,\n\t\t\t\t\t\t\tpoolTaskId,\n\t\t\t\t\t\t\tbackground: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Warm path (opt-in): for an eligible foreground dispatch, run on a reused\n\t\t\t// RPC worker to skip the cold-boot. Fork agents (forkSessionFile set) and\n\t\t\t// non-poolable types are excluded. Any infra failure falls through to the\n\t\t\t// cold pool below, so enabling this can only change latency, never whether\n\t\t\t// the task can run.\n\t\t\tif (warmSubagentsEnabled() && !forkSessionFile) {\n\t\t\t\tconst warm = getWarmSubagentPool(ctx.cwd, availableModels);\n\t\t\t\tif (warm.isPoolable(params.subagent_type)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst warmResult = await warm.dispatch(\n\t\t\t\t\t\t\tparams.prompt,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tagentType: params.subagent_type,\n\t\t\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t// Mirror the cold pool's live progress on the task panel roster so a\n\t\t\t\t\t\t\t// warm dispatch reads as busy (⋯ grep), not stuck.\n\t\t\t\t\t\t\t(activity) => taskStore.patchAgent(poolTaskId, { activity }),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst dispatchResult = warmResultToTaskResult(warmResult, params.subagent_type, task.id);\n\t\t\t\t\t\treturn finalizeDispatchResult(dispatchResult, params.subagent_type, poolTaskId, task.id, undefined);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// A genuine infra failure (worker crash/timeout) retries cold; any other\n\t\t\t\t\t\t// error is a real dispatch failure and propagates.\n\t\t\t\t\t\tif (!(error instanceof WarmWorkerError)) {\n\t\t\t\t\t\t\ttaskStore.update(task.id, { status: \"failed\" });\n\t\t\t\t\t\t\ttaskStore.patchAgent(poolTaskId, { state: \"failed\", activity: \"\" });\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tagentLog(`[WARM] ${params.subagent_type} fell back to cold spawn: ${error.message}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Foreground: block the turn and return the subagent's full answer inline.\n\t\t\ttry {\n\t\t\t\tconst dispatchResult = await cancelOnAbort(poolTaskId, () =>\n\t\t\t\t\tpool.dispatch(params.prompt, {\n\t\t\t\t\t\tforceAgent: params.subagent_type,\n\t\t\t\t\t\tcontext: \"\",\n\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\tsessionFile: forkSessionFile,\n\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn finalizeDispatchResult(\n\t\t\t\t\tdispatchResult,\n\t\t\t\t\tparams.subagent_type,\n\t\t\t\t\tpoolTaskId,\n\t\t\t\t\ttask.id,\n\t\t\t\t\tdispatchResult.task_id,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tmarkDispatchFailed(task.id, poolTaskId);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme) {\n\t\t\tconst type = args.subagent_type ?? \"agent\";\n\t\t\t// The [type] tag carries the agent's identity color — the same hue this\n\t\t\t// agent has in the task panel and TaskOutput — so a transcript full of\n\t\t\t// concurrent dispatches is scannable by color.\n\t\t\tconst text = theme.fg(\"toolTitle\", theme.bold(\"Agent \")) + theme.fg(agentColorFor(type), `[${type}]`);\n\t\t\treturn new Text(text, 0, 0);\n\t\t},\n\t});\n}\n\n/**\n * For a `fork: true` agent, fork the parent's session so the subagent inherits the\n * full parent conversation (and its prompt cache) instead of starting fresh. Returns\n * the forked session file to dispatch the child with, or undefined to fall back to a\n * fresh session (non-fork agent, no parent session, or an empty/invalid source).\n */\nexport function resolveForkSessionFile(\n\tdef: Pick<AgentDefinition, \"fork\">,\n\tparentSessionPath: string | undefined,\n\tcwd: string,\n): string | undefined {\n\tif (!def.fork || !parentSessionPath) return undefined;\n\ttry {\n\t\treturn SessionManager.forkFrom(parentSessionPath, cwd).getSessionFile();\n\t} catch {\n\t\t// Empty/invalid parent session: fall back to a fresh subagent session.\n\t\treturn undefined;\n\t}\n}\n\n/** Pre-allocated pool task id for a dispatch (matches the pool's own format). */\nfunction newDispatchTaskId(): string {\n\treturn `dispatch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * The TodoWrite plan item this dispatch is (most plausibly) working on: the\n * single in_progress main-agent task. TodoWrite discipline keeps exactly one\n * item in_progress, so when that holds the link is unambiguous; with zero or\n * several in_progress items no link is recorded rather than guessing. Used to\n * nest the run under its plan item in the task panel's flat lens.\n */\nfunction linkedTodoId(): number | undefined {\n\tconst inProgress = taskStore\n\t\t.list()\n\t\t.filter(\n\t\t\t(t) =>\n\t\t\t\tt.source === undefined &&\n\t\t\t\tt.agent === undefined &&\n\t\t\t\tt.parentTaskId === undefined &&\n\t\t\t\tt.status === \"in_progress\",\n\t\t);\n\treturn inProgress.length === 1 ? inProgress[0]?.id : undefined;\n}\n\n/**\n * Register this dispatch in the task store's roster, keyed by its run id (the\n * pool task id) so concurrent runs of the same agent type each get their own\n * row, state, activity, and stats. The friendly label (\"explore#1\") names the\n * row the same way the inbox names background tasks.\n */\nfunction registerSubagentDispatch(runId: string, label: string): void {\n\ttaskStore.upsertAgent({ id: runId, name: label, role: \"subagent\", kind: \"subagent\", state: \"running\" });\n}\n\n/**\n * Catch-path bookkeeping: mark the dispatch failed unless it already settled.\n * finalizeDispatchResult records a terminal status (failed/cancelled) *before*\n * throwing, so a blanket \"failed\" here would clobber a cancellation with a\n * failure the moment the thrown error passed through.\n */\nfunction markDispatchFailed(taskStoreId: number, runId: string): void {\n\tconst current = taskStore.list().find((t) => t.id === taskStoreId);\n\tif (current && current.status !== \"pending\" && current.status !== \"in_progress\") return;\n\ttaskStore.update(taskStoreId, { status: \"failed\" });\n\ttaskStore.patchAgent(runId, { state: \"failed\", activity: \"\" });\n}\n\n/** Names of agents configured to run in the background (non-blocking). */\nfunction collectBackgroundAgentNames(cwd: string): Set<string> {\n\tconst names = new Set<string>();\n\tfor (const agent of loadAgentRegistry({ cwd }).list()) {\n\t\tif (agent.background) names.add(agent.name);\n\t}\n\treturn names;\n}\n\n/**\n * Merge a child subagent's task subtree into the parent's task store, rooting\n * each top-level node under the dispatching task (`parentTaskId`). Recurses so a\n * subagent that itself delegated shows its nested work — the subtree the child\n * could not surface across the process boundary on its own. Each node is its own\n * task (no key-by-type collapse), preserving the order the child created them.\n */\nfunction mergeChildTaskTree(nodes: readonly SubagentTaskNode[] | undefined, parentTaskId: number): void {\n\tif (!nodes) return;\n\ttaskStore.batch(() => {\n\t\tfor (const node of nodes) {\n\t\t\tconst created = taskStore.create(node.title, {\n\t\t\t\tsource: node.source,\n\t\t\t\tsubagentMode: node.subagentMode,\n\t\t\t\tparentTaskId,\n\t\t\t});\n\t\t\ttaskStore.update(created.id, { status: node.status, usage: node.usage });\n\t\t\tmergeChildTaskTree(node.children, created.id);\n\t\t}\n\t});\n}\n\n/**\n * Adapt a warm-worker run into the TaskResult shape finalizeDispatchResult\n * consumes, so the warm and cold paths share one finish/render path. A warm run\n * returns its answer inline (no result.json), so we synthesize an equivalent\n * SubagentResult with the answer as the summary and the pulled usage. The warm\n * path is clean (no persisted session), so there is no task_tree to merge and no\n * resume handle.\n */\nfunction warmResultToTaskResult(warm: WarmRunResult, agentType: string, taskStoreId: number): TaskResult {\n\tconst resultData: SubagentResultFile = {\n\t\tsummary: warm.summary || \"(subagent returned no output)\",\n\t\tfiles_changed: [],\n\t\tconfidence: warm.ok ? 1 : 0,\n\t\tstatus: warm.status,\n\t\tusage: warm.usage,\n\t};\n\tconst result: SubagentResult = {\n\t\ttask_id: String(taskStoreId),\n\t\tok: warm.ok,\n\t\tstdout: \"\",\n\t\tstderr: \"\",\n\t\texit_code: warm.ok ? 0 : 1,\n\t\tstatus: warm.status,\n\t\terror: warm.error,\n\t\tresult_data: resultData as unknown as Record<string, unknown>,\n\t};\n\treturn { handled_inline: false, agent_type: agentType, result };\n}\n\n/**\n * Update the task panel from a finished dispatch and shape the tool result.\n *\n * Foreground calls return the subagent's full answer inline and signal a hard\n * failure by throwing (the agent loop derives a tool's error state from a thrown\n * error). A background call passes `background`: the body already lives in the\n * inbox, so it returns a compact, self-contained notification (success or\n * failure) and never throws — the call was already answered by a placeholder.\n */\nfunction finalizeDispatchResult(\n\tdispatchResult: TaskResult,\n\tsubagentType: string,\n\trunAgentId: string,\n\ttaskStoreId: number,\n\tresumeHandle: string | undefined,\n\tbackground?: { taskId: string; label: string },\n): { content: Array<{ type: \"text\"; text: string }>; details: TaskToolDetails } {\n\tconst result = dispatchResult.result;\n\tconst resultData = result?.result_data as SubagentResultFile | undefined;\n\tconst usage = resultData?.usage;\n\n\t// Merge the child's own task subtree under the dispatching task so nested\n\t// delegation (depth >= 2) is visible in the subagents lens's task tree.\n\tmergeChildTaskTree(resultData?.task_tree, taskStoreId);\n\n\t// Roll this run's usage into its own roster row (rows are per-dispatch).\n\tif (usage) {\n\t\ttaskStore.addAgentStats(runAgentId, { input: usage.input, output: usage.output, cost: usage.cost });\n\t}\n\n\tif (!result || !result.ok) {\n\t\t// A user-initiated cancel is not a failure: it gets its own terminal\n\t\t// status so the panel shows ⊘ cancelled (dim) instead of ✗ failed (red).\n\t\tconst cancelled = result?.status === \"cancelled\";\n\t\tconst failNote = result?.usedInheritedModelFallback ? \"inherited-model retry failed\" : undefined;\n\t\ttaskStore.update(taskStoreId, { status: cancelled ? \"cancelled\" : \"failed\", usage, note: failNote });\n\t\ttaskStore.patchAgent(runAgentId, { state: cancelled ? \"cancelled\" : \"failed\", activity: \"\" });\n\t\tconst reason = result?.error ?? (result?.status ? `subagent ${result.status}` : \"unknown error\");\n\t\tif (background) {\n\t\t\tconst verdict = cancelled ? \"cancelled ⊘\" : \"failed ✗\";\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `${background.label} ${verdict} — ${reason}` }],\n\t\t\t\tdetails: {\n\t\t\t\t\tsubagent_type: subagentType,\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: reason,\n\t\t\t\t\ttaskId: taskStoreId,\n\t\t\t\t\tpoolTaskId: background.taskId,\n\t\t\t\t\tbackground: true,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (cancelled) {\n\t\t\tthrow new Error(`Subagent (${subagentType}) cancelled by user.`);\n\t\t}\n\t\tconst stderr = result?.stderr?.trim();\n\t\tthrow new Error(`Subagent (${subagentType}) failed: ${reason}${stderr ? `\\nstderr: ${stderr.slice(-500)}` : \"\"}`);\n\t}\n\n\t// Leave the task in the store with its final status; it stays visible in the\n\t// task panel until the next user message arrives. Surface a ⚠ cue when the run\n\t// fell back to the inherited model rather than emitting a chat message — and\n\t// clear any stale note otherwise (note is clearable via an explicit undefined).\n\tconst fallbackNote = dispatchResult.result?.usedInheritedModelFallback ? \"ran on inherited model\" : undefined;\n\ttaskStore.update(taskStoreId, { status: \"done\", usage, note: fallbackNote });\n\t// Rows are per-run, so this run settles to done regardless of siblings.\n\ttaskStore.patchAgent(runAgentId, { state: \"done\", activity: \"\" });\n\tlet answer = resultData?.summary || \"(subagent returned no output)\";\n\t// Partial results are resumable; surface the handle so the parent can continue.\n\tif (result.status === \"partial\" && resumeHandle) {\n\t\tanswer += `\\n\\n[Partial result. To continue this subagent, call Task again with resume_task_id=\"${resumeHandle}\".]`;\n\t}\n\n\tif (background) {\n\t\t// Compact notification: the body is retained in the inbox; the model pulls it\n\t\t// with TaskOutput. Keeps a wide swarm from flooding the parent's context.\n\t\tconst partial = result.status === \"partial\" ? \" (partial — resume to continue)\" : \"\";\n\t\tconst outstanding = subagentInbox.outstanding().length;\n\t\tconst tail = outstanding > 0 ? ` ${outstanding} still running.` : \"\";\n\t\tconst text =\n\t\t\t`${background.label} finished ✓${partial} — ${summarize(answer)}.${tail}\\n` +\n\t\t\t`Read the full result with TaskOutput(\"${background.label}\").`;\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails: {\n\t\t\t\tsubagent_type: subagentType,\n\t\t\t\tok: true,\n\t\t\t\ttaskId: taskStoreId,\n\t\t\t\tpoolTaskId: background.taskId,\n\t\t\t\tbackground: true,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tcontent: [{ type: \"text\", text: answer }],\n\t\tdetails: { subagent_type: subagentType, ok: true, taskId: taskStoreId, poolTaskId: resumeHandle },\n\t};\n}\n\nconst taskOutputParams = Type.Object({\n\ttask_id: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t'Handle of a background subagent — its task_id or friendly label (e.g. \"explore#1\") from a Task notification. Omit (or set list:true) to see every background task.',\n\t\t}),\n\t),\n\tlist: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"List all background subagents with their status (running/done/failed/cancelled) and current activity. No result bodies are returned.\",\n\t\t}),\n\t),\n\twait: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Block until the named task finishes — or, with no task_id, until all outstanding subagents finish (a swarm barrier) — before returning. Bounded by timeout_ms.\",\n\t\t}),\n\t),\n\ttimeout_ms: Type.Optional(\n\t\tType.Number({ description: \"Maximum time to block in wait mode, in milliseconds (default 120000).\" }),\n\t),\n});\n\ntype TaskOutputParams = Static<typeof taskOutputParams>;\n\nconst TASK_OUTPUT_DEFAULT_TIMEOUT_MS = 120_000;\n\n/**\n * Elapsed time a record has run (so far, or until it settled), in the same\n * format the task panel uses so the two surfaces always agree.\n */\nfunction recordElapsed(rec: InboxRecord): string {\n\tconst end = rec.endedAt ?? Date.now();\n\treturn formatDurationSecs((end - rec.startedAt) / 1000);\n}\n\n/** A compact roster of every known background subagent — status + activity, no bodies. */\nfunction formatTaskRoster(): { content: Array<{ type: \"text\"; text: string }>; details: TaskOutputDetails } {\n\tconst all = subagentInbox.list();\n\tconst outstanding = subagentInbox.outstanding().length;\n\tif (all.length === 0) {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: \"No background subagents have been dispatched.\" }],\n\t\t\tdetails: { status: \"empty\", ok: true, outstanding: 0 },\n\t\t};\n\t}\n\tconst lines = all.map((r) => {\n\t\tconst when = recordElapsed(r);\n\t\tswitch (r.lifecycle) {\n\t\t\tcase \"running\":\n\t\t\t\treturn `- ${r.label} running ${when}${r.lastActivity ? ` · ${r.lastActivity}` : \"\"}`;\n\t\t\tcase \"done\":\n\t\t\t\treturn `- ${r.label} done (uncollected) ${when} — ${r.summaryLine ?? \"\"}`;\n\t\t\tcase \"collected\":\n\t\t\t\treturn `- ${r.label} collected ${when} — ${r.summaryLine ?? \"\"}`;\n\t\t\tcase \"cancelled\":\n\t\t\t\treturn `- ${r.label} cancelled ⊘ — ${r.error ?? \"cancelled by user\"}`;\n\t\t\tdefault:\n\t\t\t\treturn `- ${r.label} ${r.lifecycle} ✗ — ${r.error ?? \"unknown error\"}`;\n\t\t}\n\t});\n\tconst header = `${all.length} background subagent${all.length === 1 ? \"\" : \"s\"} (${outstanding} running):`;\n\tconst hint = all.some((r) => r.lifecycle === \"done\") ? '\\nRead a finished one with TaskOutput(\"<label>\").' : \"\";\n\treturn {\n\t\tcontent: [{ type: \"text\", text: `${header}\\n${lines.join(\"\\n\")}${hint}` }],\n\t\tdetails: { status: \"list\", ok: true, outstanding },\n\t};\n}\n\n/**\n * TaskOutput tool: check on background subagents and pull their results.\n *\n * Background `Task` calls don't push their body into the conversation — they\n * leave it in the inbox and post a compact notification. TaskOutput is how the\n * model pulls a body, checks liveness, or waits. It never throws on a valid\n * handle (an error tool result would only confuse the loop): it reports status\n * instead. Modes: `list` (roster), a `task_id` to read/check one, and `wait` to\n * block until one task — or all outstanding tasks — finish.\n */\nexport function createTaskOutputToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof taskOutputParams, TaskOutputDetails>({\n\t\tname: \"TaskOutput\",\n\t\tlabel: \"TaskOutput\",\n\t\tdescription: [\n\t\t\t\"Check on background subagents dispatched via Task, and pull their results.\",\n\t\t\t'Pass a task_id/label (e.g. \"explore#1\") to read a finished subagent\\'s full result, or to see its status while it runs.',\n\t\t\t\"Set list:true (or omit task_id) to list every background subagent with its status and current activity.\",\n\t\t\t\"Set wait:true to block until that task finishes — or, with no task_id, until all outstanding subagents finish (a swarm barrier).\",\n\t\t\t\"It never errors on a valid handle: a running task reports status, a finished one returns its result, an already-read one says so.\",\n\t\t].join(\"\\n\"),\n\t\tpromptSnippet: \"check status / list / collect the results of background subagents\",\n\t\tparameters: taskOutputParams,\n\n\t\tasync execute(_toolCallId, params: TaskOutputParams, _signal, _onUpdate, ctx) {\n\t\t\t// Touch the pool so the inbox is wired to its progress stream for activity.\n\t\t\t// The pool is normally already created by a prior dispatch; pass available\n\t\t\t// models so a first-touch here still seeds category derivation.\n\t\t\tsubagentInbox.observe(getSubagentPool(ctx.cwd, ctx.modelRegistry?.getAvailable() ?? []));\n\t\t\tconst handle = params.task_id?.trim();\n\n\t\t\t// Barrier: wait for the target (or all outstanding) to settle first.\n\t\t\tif (params.wait) {\n\t\t\t\tconst timeout = params.timeout_ms ?? TASK_OUTPUT_DEFAULT_TIMEOUT_MS;\n\t\t\t\tif (handle) await subagentInbox.waitFor(handle, timeout);\n\t\t\t\telse await subagentInbox.waitForAll(timeout);\n\t\t\t}\n\n\t\t\t// Roster when asked, or when no specific task was named.\n\t\t\tif (params.list || !handle) {\n\t\t\t\treturn formatTaskRoster();\n\t\t\t}\n\n\t\t\tconst rec = subagentInbox.get(handle);\n\t\t\tif (!rec) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `No background task \"${handle}\". Call TaskOutput with list:true to see active tasks.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task_id: handle, status: \"unknown\", ok: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rec.lifecycle === \"running\") {\n\t\t\t\tconst activity = rec.lastActivity ? ` (currently: ${rec.lastActivity})` : \"\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `${rec.label} is still running — ${recordElapsed(rec)} elapsed${activity}. Call TaskOutput again, or with wait:true to block until it finishes.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task_id: handle, status: \"running\", ok: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rec.lifecycle === \"done\") {\n\t\t\t\tconst collected = subagentInbox.collect(handle);\n\t\t\t\tconst body = collected?.body ?? rec.summaryLine ?? \"(subagent returned no output)\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: body }],\n\t\t\t\t\tdetails: { task_id: handle, status: \"done\", ok: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rec.lifecycle === \"collected\") {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `${rec.label} was already delivered — ${rec.summaryLine ?? \"(no summary kept)\"}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task_id: handle, status: \"collected\", ok: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// failed / stalled / timeout / cancelled\n\t\t\tconst glyph = rec.lifecycle === \"cancelled\" ? \"⊘\" : \"✗\";\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\ttext: `${rec.label} ${rec.lifecycle} ${glyph} — ${rec.error ?? \"unknown error\"}.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { task_id: handle, status: rec.lifecycle, ok: false },\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme) {\n\t\t\tconst target = args.list ? \"list\" : String(args.task_id ?? \"\");\n\t\t\t// A friendly label (\"explore#1\") gets its agent's identity color so the\n\t\t\t// pull visually pairs with the dispatch that produced it; raw ids and\n\t\t\t// \"list\" stay dim.\n\t\t\tconst hashIdx = target.indexOf(\"#\");\n\t\t\tconst styledTarget =\n\t\t\t\thashIdx > 0 ? theme.fg(agentColorFor(target.slice(0, hashIdx)), target) : theme.fg(\"dim\", target);\n\t\t\tconst text =\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"TaskOutput \")) +\n\t\t\t\tstyledTarget +\n\t\t\t\t(args.wait ? theme.fg(\"dim\", \" (wait)\") : \"\");\n\t\t\treturn new Text(text, 0, 0);\n\t\t},\n\n\t\trenderResult(result, _options, theme) {\n\t\t\t// Display-only framing/colorization (the text sent to the model stays\n\t\t\t// plain). A single-task pull renders as a result CARD — a status-stamped\n\t\t\t// header, a left spine down the body, and a closing corner — so a\n\t\t\t// collected result reads as a discrete artifact instead of blending into\n\t\t\t// the surrounding tool chatter. Roster/list responses keep the line\n\t\t\t// colorization.\n\t\t\tconst text = result.content\n\t\t\t\t.map((c) => (c.type === \"text\" ? c.text : \"\"))\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t\tif (!text) return new Text(\"\", 0, 0);\n\n\t\t\tconst details = result.details as TaskOutputDetails | undefined;\n\t\t\tconst cardStatus = details?.task_id ? details.status : undefined;\n\t\t\tif (cardStatus && cardStatus !== \"list\" && cardStatus !== \"empty\") {\n\t\t\t\tconst presentation: Record<\n\t\t\t\t\tstring,\n\t\t\t\t\t{ glyph: string; color: \"success\" | \"warning\" | \"error\" | \"muted\" | \"dim\" }\n\t\t\t\t> = {\n\t\t\t\t\tdone: { glyph: \"✓\", color: \"success\" },\n\t\t\t\t\trunning: { glyph: \"◐\", color: \"warning\" },\n\t\t\t\t\tcollected: { glyph: \"✓\", color: \"muted\" },\n\t\t\t\t\tcancelled: { glyph: \"⊘\", color: \"dim\" },\n\t\t\t\t\tunknown: { glyph: \"?\", color: \"dim\" },\n\t\t\t\t};\n\t\t\t\tconst { glyph, color } = presentation[cardStatus] ?? { glyph: \"✗\", color: \"error\" as const };\n\t\t\t\tconst label = details?.task_id ?? \"\";\n\t\t\t\tconst hashIdx = label.indexOf(\"#\");\n\t\t\t\tconst labelColor = hashIdx > 0 ? agentColorFor(label.slice(0, hashIdx)) : \"accent\";\n\n\t\t\t\t// Look up inbox record for elapsed time\n\t\t\t\tconst inboxRecord = subagentInbox.get(label);\n\t\t\t\tconst elapsed = inboxRecord ? (inboxRecord.endedAt ?? Date.now()) - inboxRecord.startedAt : undefined;\n\t\t\t\tconst elapsedText = elapsed ? ` ${formatDurationSecs(elapsed / 1000)}` : \"\";\n\n\t\t\t\t// Look up task for token count\n\t\t\t\tconst task = inboxRecord ? taskStore.list().find((t) => t.agent === inboxRecord.taskId) : undefined;\n\t\t\t\tconst totalTokens = task?.usage ? task.usage.input + task.usage.output : undefined;\n\t\t\t\tconst tokenText = totalTokens !== undefined ? ` ${formatTokens(totalTokens)}` : \"\";\n\n\t\t\t\tconst spine = (s: string) => theme.fg(\"borderMuted\", s);\n\t\t\t\tconst header =\n\t\t\t\t\t`${spine(\"╭\")} ${theme.fg(color, glyph)} ` +\n\t\t\t\t\t`${theme.bold(theme.fg(color, cardStatus))} ${theme.fg(labelColor, label)}${tokenText}${elapsedText}`;\n\t\t\t\tconst body = text\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.map((line) => `${spine(\"│\")} ${theme.fg(\"toolOutput\", line)}`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn new Text(`${header}\\n${body}\\n${spine(\"╰\")}`, 0, 0);\n\t\t\t}\n\n\t\t\tconst rosterLine =\n\t\t\t\t/^- (\\S+)\\s{2}(running|done \\(uncollected\\)|collected|failed|stalled|timeout|cancelled)(.*)$/;\n\t\t\tconst styled = text\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((line) => {\n\t\t\t\t\tconst match = rosterLine.exec(line);\n\t\t\t\t\tif (!match) return theme.fg(\"toolOutput\", line);\n\t\t\t\t\tconst [, label, status, rest] = match as unknown as [string, string, string, string];\n\t\t\t\t\tconst hashIdx = label.indexOf(\"#\");\n\t\t\t\t\tconst labelColor = hashIdx > 0 ? agentColorFor(label.slice(0, hashIdx)) : \"accent\";\n\t\t\t\t\tconst statusColor =\n\t\t\t\t\t\tstatus === \"running\"\n\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t: status.startsWith(\"done\")\n\t\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t\t: status === \"collected\" || status === \"cancelled\"\n\t\t\t\t\t\t\t\t\t? \"muted\"\n\t\t\t\t\t\t\t\t\t: \"error\";\n\t\t\t\t\treturn `- ${theme.fg(labelColor, label)} ${theme.fg(statusColor, status)}${theme.fg(\"dim\", rest)}`;\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\treturn new Text(styled, 0, 0);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../../src/core/tools/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,yBAAyB,CAAC;AAG/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAkB7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;;;6EAK6E;AAC7E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CASvE;AAmCD,MAAM,WAAW,eAAe;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IACjC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAeD,gFAAgF;AAChF,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAsB,GAAG,cAAc,CAsUpF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACrC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,EAClC,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,GAAG,EAAE,MAAM,GACT,MAAM,GAAG,SAAS,CAQpB;AAwRD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,IAAI,cAAc,CAwL/D","sourcesContent":["/**\n * Task tool: delegate a focused task to a specialized subagent.\n *\n * Mirrors the Claude Code `Task` tool. The parent agent decides *when* to\n * delegate based on each agent's `description` (there is no deterministic gate)\n * and selects *which* agent via `subagent_type`. The chosen agent runs in a\n * fresh, isolated child process (SubagentPool) and only its final answer is\n * returned to the parent.\n *\n * Enabled by default (the `enableSubagent` setting defaults to true); disable\n * with `enableSubagent: false`. The `--enable-subagents` flag still force-enables\n * it. Nesting is bounded by the tree-wide depth cap (maxSubagentDepth, default 2:\n * a spawned subagent may itself delegate one more level, and depth-2 grandchildren\n * cannot). See buildSessionOptions in main.ts.\n */\n\nimport { Text } from \"@kolisachint/hoocode-tui\";\nimport { type Static, Type } from \"typebox\";\nimport { EMBEDDED_PROMPTS } from \"../../init-templates.generated.js\";\nimport { agentColorFor } from \"../../modes/interactive/theme/theme.js\";\nimport { type AgentDefinition, TASK_TOOL_NAME } from \"../agent-frontmatter.js\";\nimport { agentLog } from \"../agent-log.js\";\nimport { loadAgentRegistry } from \"../agent-registry.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { defineTool } from \"../extensions/types.js\";\nimport { formatDurationSecs } from \"../format-duration.js\";\nimport { formatTokens } from \"../format-tokens.js\";\nimport { getProviderExhaustion } from \"../provider-health.js\";\nimport { SessionManager } from \"../session-manager.js\";\nimport { delegateAllowList, isDelegateAllowed } from \"../subagent-depth.js\";\nimport { type InboxRecord, subagentInbox } from \"../subagent-inbox.js\";\nimport type { SubagentResult, TaskResult } from \"../subagent-pool.js\";\nimport { getSubagentPool } from \"../subagent-pool-instance.js\";\nimport type { SubagentResultFile, SubagentTaskNode } from \"../subagent-result.js\";\nimport { taskStore } from \"../task-store.js\";\nimport { type WarmRunResult, WarmWorkerError } from \"../warm-subagent-pool.js\";\nimport { getWarmSubagentPool, warmSubagentsEnabled } from \"../warm-subagent-pool-instance.js\";\n\n// Re-exported from its home in agent-registry (where formatAgentsForPrompt uses\n// it to render the roster) so existing importers keep working without creating a\n// tools -> registry -> tools cycle.\nexport { summarizeAgentDescription } from \"../agent-registry.js\";\n\n/** System prompt appendix for the main session when the Task tool is enabled.\n * Instructs the parent agent on when and how to delegate effectively. The\n * available agents themselves are listed once, authoritatively, in the\n * `<available_agents>` block the system prompt emits whenever the Task tool is\n * active (see agent-session `_rebuildSystemPrompt`); this appendix references\n * that list rather than re-rendering the roster and paying for it twice. */\n/**\n * Build the main-session subagent instructions appended to the system prompt.\n *\n * The prose lives in `templates/prompts/task-*.md` and reaches here through the\n * build-time embed. It is the largest block of always-on text hoocode emits\n * (~600 tok/turn once the Task tool is on) and it had no interpolation beyond\n * the single background slot, so keeping it as a TypeScript string constant\n * bought nothing and made it awkward to edit as the prose it is.\n *\n * The detailed background/barrier guidance (the two heaviest bullets) is only\n * emitted when the project actually has background-capable agents; otherwise a\n * single concise line covers the per-call `background: true` escape hatch. This\n * keeps the always-on cost down for the common case where nothing runs in the\n * background. `cwd` is used only to detect those agents.\n */\nexport function buildTaskMainPrompt(cwd: string = process.cwd()): string {\n\tconst hasBackgroundAgents = collectBackgroundAgentNames(cwd).size > 0;\n\tconst backgroundGuidance = (\n\t\tEMBEDDED_PROMPTS[hasBackgroundAgents ? \"task-background-agents\" : \"task-background-none\"] ?? \"\"\n\t).trim();\n\n\t// Trimmed at both ends: the template file ends with a newline, and this is\n\t// appended to a system prompt that manages its own separators.\n\treturn (EMBEDDED_PROMPTS[\"task-main\"] ?? \"\").replace(\"{{BACKGROUND_GUIDANCE}}\", backgroundGuidance).trim();\n}\n\nconst taskParams = Type.Object({\n\tdescription: Type.String({\n\t\tdescription: \"A short (3-5 word) description of the task, shown in the task panel.\",\n\t}),\n\tprompt: Type.String({\n\t\tdescription:\n\t\t\t\"The full, self-contained task for the subagent. It cannot see this conversation, so include all needed context, files, and constraints.\",\n\t}),\n\tsubagent_type: Type.String({\n\t\tdescription: \"The name of the specialized agent to delegate to. Must be one of the available agents.\",\n\t}),\n\tcomplexity: Type.Optional(\n\t\tType.Union([Type.Literal(\"fast\"), Type.Literal(\"standard\"), Type.Literal(\"capable\")], {\n\t\t\tdescription:\n\t\t\t\t\"Model tier for this dispatch: fast (quick reads/lookups), standard (multi-file edits), capable (deep architecture). Maps to settings.modelCategories. Ignored if the chosen agent pins its own model; omit to use the agent's default.\",\n\t\t}),\n\t),\n\tbackground: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Set true to run non-blocking: you get a short notification when it finishes and pull the full result with TaskOutput; set false to wait and get the answer inline. Defaults to the agent's own background setting.\",\n\t\t}),\n\t),\n\tresume_task_id: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional. To continue a previous subagent run, pass its task_id (returned by an earlier Task or TaskOutput call). The subagent resumes with its full prior transcript and `prompt` is your follow-up instruction.\",\n\t\t}),\n\t),\n});\n\ntype TaskParams = Static<typeof taskParams>;\n\nexport interface TaskToolDetails {\n\tsubagent_type: string;\n\tok: boolean;\n\terror?: string;\n\ttaskId: number;\n\t/** Pool-level task id usable for resume/polling. */\n\tpoolTaskId?: string;\n\t/** True when dispatched as a non-blocking background task. */\n\tbackground?: boolean;\n}\n\nexport interface TaskOutputDetails {\n\t/** The handle queried, when a specific task was named. */\n\ttask_id?: string;\n\t/** running | done | collected | failed | stalled | timeout | cancelled | list | empty | unknown */\n\tstatus: string;\n\tok: boolean;\n\t/** Number of subagents still running, included on roster/list responses. */\n\toutstanding?: number;\n}\n\n/**\n * A short, human-readable task name for the task panel: the first line limited\n * to ~8 words so it stays glanceable. A character cap guards a single long word.\n */\nfunction summarize(task: string): string {\n\tconst firstLine = (task.trim().split(\"\\n\")[0] ?? \"\").trim();\n\tif (!firstLine) return \"(task)\";\n\tconst words = firstLine.split(/\\s+/);\n\tlet name = words.length > 8 ? `${words.slice(0, 8).join(\" \")}…` : firstLine;\n\tif (name.length > 60) name = `${name.slice(0, 59)}…`;\n\treturn name;\n}\n\n/** Create the Task tool definition. Registered as a customTool when enabled. */\nexport function createTaskToolDefinition(cwd: string = process.cwd()): ToolDefinition {\n\t// Agents whose definitions opt into background execution. The agent loop reads\n\t// the tool's `background` flag per call and, for these, runs the dispatch\n\t// detached: the parent keeps reasoning and the subagent's answer is injected as\n\t// a follow-up message when it finishes (no polling needed). A per-call\n\t// `background` argument overrides the agent's default in either direction.\n\tconst backgroundAgents = collectBackgroundAgentNames(cwd);\n\treturn defineTool<typeof taskParams, TaskToolDetails>({\n\t\tname: TASK_TOOL_NAME,\n\t\tlabel: TASK_TOOL_NAME,\n\t\tbackground: (toolCall) => {\n\t\t\tconst override = toolCall.arguments?.background;\n\t\t\tif (typeof override === \"boolean\") return override;\n\t\t\treturn backgroundAgents.has(String(toolCall.arguments?.subagent_type ?? \"\"));\n\t\t},\n\t\t// Kept to mechanics only: the when-to-use / when-not-to guidance lives once in\n\t\t// the system-prompt block (buildTaskMainPrompt), the available agents are\n\t\t// listed there too, and the `complexity`/`background`/`resume_task_id`\n\t\t// semantics live in their parameter descriptions. Repeating any of that here\n\t\t// would re-spend those tokens on every turn.\n\t\tdescription:\n\t\t\t\"Delegate a focused task to a specialized subagent that runs in a fresh, isolated context (it cannot see this conversation). Choose one of the available agents (listed in the system prompt) via `subagent_type` and pass everything it needs via `prompt`; the subagent returns only its final answer.\",\n\t\tpromptSnippet: \"delegate a self-contained task to a specialized subagent (choose via subagent_type)\",\n\t\tparameters: taskParams,\n\n\t\tasync execute(_toolCallId, params: TaskParams, signal, _onUpdate, ctx) {\n\t\t\t// Snapshot the caller's available models so unconfigured model-category\n\t\t\t// tiers (fast/standard/capable) resolve to a derived default instead of a\n\t\t\t// no-op. Explicit settings.modelCategories still win inside the pool. The\n\t\t\t// registry can be absent in some tool contexts; an empty list just keeps\n\t\t\t// category derivation a no-op (inherit parent), so guard the access.\n\t\t\tconst availableModels = ctx.modelRegistry?.getAvailable() ?? [];\n\t\t\tconst pool = getSubagentPool(ctx.cwd, availableModels);\n\n\t\t\t// User-initiated cancel (Esc/abort): kill the dispatched run's whole\n\t\t\t// process tree and let the dispatch settle with status \"cancelled\" —\n\t\t\t// distinct from \"failed\" everywhere it surfaces (panel, inbox, result).\n\t\t\t// Cold dispatches only: a warm worker has no cancel surface (it is\n\t\t\t// reused), so warm runs still complete server-side and are discarded.\n\t\t\tconst cancelOnAbort = async (poolRunId: string, run: () => Promise<TaskResult>): Promise<TaskResult> => {\n\t\t\t\tif (!signal) return run();\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tpool.cancel?.(poolRunId);\n\t\t\t\t};\n\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\ttry {\n\t\t\t\t\treturn await run();\n\t\t\t\t} finally {\n\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Pre-flight: if the inherited provider recently exhausted its quota (the\n\t\t\t// parent's own turn failed with a usage/rate-limit error that did not\n\t\t\t// recover), skip the spawn. Subagents run on the same provider, so this\n\t\t\t// would only burn another failed attempt. The signal self-expires and is\n\t\t\t// cleared on the next successful response.\n\t\t\tconst provider = ctx.model?.provider;\n\t\t\tconst exhaustion = provider ? getProviderExhaustion(provider) : undefined;\n\t\t\tif (exhaustion) {\n\t\t\t\tconst skippedRunId = newDispatchTaskId();\n\t\t\t\tconst skipped = taskStore.create(params.description?.trim() || summarize(params.prompt), {\n\t\t\t\t\tsource: \"subagent\",\n\t\t\t\t\tsubagentMode: params.subagent_type,\n\t\t\t\t\tagent: skippedRunId,\n\t\t\t\t\tlinkedTaskId: linkedTodoId(),\n\t\t\t\t});\n\t\t\t\tregisterSubagentDispatch(skippedRunId, subagentInbox.nextLabel(params.subagent_type));\n\t\t\t\ttaskStore.update(skipped.id, { status: \"failed\", note: `${provider} exhausted` });\n\t\t\t\ttaskStore.patchAgent(skippedRunId, { state: \"failed\" });\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t`Did not dispatch subagent \"${params.subagent_type}\": the \"${provider}\" provider appears ` +\n\t\t\t\t\t\t\t\t`exhausted or rate-limited (this session just failed with: ${exhaustion.message}). ` +\n\t\t\t\t\t\t\t\t`Subagents run on the same provider, so dispatching would fail too. Wait for the quota to ` +\n\t\t\t\t\t\t\t\t`reset or switch model/provider, then retry — or complete the work directly in this session.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { subagent_type: params.subagent_type, ok: false, taskId: skipped.id },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Scoped delegation: a delegating agent may be restricted to certain subagent\n\t\t\t// types (its `delegate: <types>` frontmatter). The root is unrestricted.\n\t\t\tif (!isDelegateAllowed(params.subagent_type)) {\n\t\t\t\tconst allowed = delegateAllowList()?.join(\", \") ?? \"\";\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`This agent may not delegate to \"${params.subagent_type}\". Allowed subagent types: ${allowed || \"(none)\"}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Resume path: continue a previously dispatched subagent with a follow-up\n\t\t\t// prompt, reusing its persisted session (full prior transcript).\n\t\t\tconst resumeId = params.resume_task_id?.trim();\n\t\t\tif (resumeId) {\n\t\t\t\tconst summary = params.description?.trim() || summarize(params.prompt);\n\t\t\t\tconst runId = newDispatchTaskId();\n\t\t\t\tconst label = subagentInbox.nextLabel(params.subagent_type);\n\t\t\t\tconst task = taskStore.create(summary, {\n\t\t\t\t\tsource: \"subagent\",\n\t\t\t\t\tsubagentMode: params.subagent_type,\n\t\t\t\t\tagent: runId,\n\t\t\t\t\tlinkedTaskId: linkedTodoId(),\n\t\t\t\t});\n\t\t\t\tregisterSubagentDispatch(runId, label);\n\t\t\t\ttaskStore.update(task.id, { status: \"in_progress\" });\n\t\t\t\ttry {\n\t\t\t\t\tconst dispatchResult = await cancelOnAbort(runId, () =>\n\t\t\t\t\t\tpool.resume(resumeId, params.prompt, {\n\t\t\t\t\t\t\tmodel: ctx.model?.id,\n\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\ttaskId: runId,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\t// The session lives under the original task id; keep it as the resume handle.\n\t\t\t\t\treturn finalizeDispatchResult(dispatchResult, params.subagent_type, runId, task.id, resumeId);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tmarkDispatchFailed(task.id, runId);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// The model has already decided to delegate and which agent to use; honor\n\t\t\t// it. Validate the requested agent against the registry (no routing gate).\n\t\t\tconst registry = loadAgentRegistry({ cwd: ctx.cwd });\n\t\t\tconst def = registry.get(params.subagent_type);\n\t\t\tif (!def) {\n\t\t\t\tconst available = registry\n\t\t\t\t\t.list()\n\t\t\t\t\t.map((a) => a.name)\n\t\t\t\t\t.join(\", \");\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Unknown subagent_type: \"${params.subagent_type}\". Available agents: ${available || \"(none)\"}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst summary = params.description?.trim() || summarize(params.prompt);\n\t\t\t// One roster row per dispatch, keyed by the (pre-allocated) pool task id.\n\t\t\t// Keying by agent TYPE made concurrent same-type runs share a single row:\n\t\t\t// their activity, state, and stats collided last-writer-wins. The inbox\n\t\t\t// already keys by task id; this aligns the panel to the same model. The\n\t\t\t// pool's task_progress events carry the task id, so patching by this id\n\t\t\t// routes live activity to the right row.\n\t\t\tconst poolTaskId = newDispatchTaskId();\n\t\t\tconst label = subagentInbox.nextLabel(params.subagent_type);\n\t\t\tconst task = taskStore.create(summary, {\n\t\t\t\tsource: \"subagent\",\n\t\t\t\tsubagentMode: params.subagent_type,\n\t\t\t\tagent: poolTaskId,\n\t\t\t\t// Tie this run to the plan item it executes (the single in_progress\n\t\t\t\t// TodoWrite task, when unambiguous) so the panel can nest it there.\n\t\t\t\tlinkedTaskId: linkedTodoId(),\n\t\t\t});\n\t\t\tregisterSubagentDispatch(poolTaskId, label);\n\t\t\ttaskStore.update(task.id, { status: \"in_progress\" });\n\t\t\t// Fork agents inherit the parent's conversation via a forked session.\n\t\t\tconst forkSessionFile = def.fork\n\t\t\t\t? resolveForkSessionFile(def, ctx.sessionManager?.getSessionFile(), ctx.cwd)\n\t\t\t\t: undefined;\n\n\t\t\t// `complexity` is passed as the model: the pool's spawn() already lets a\n\t\t\t// non-`inherit` agent model win, then resolves a category string (fast/\n\t\t\t// standard/capable) via settings.modelCategories. So a pinned-model agent\n\t\t\t// ignores complexity, and an `inherit` agent picks up the requested tier —\n\t\t\t// no settings lookup needed here.\n\t\t\tconst dispatchModel = params.complexity ?? ctx.model?.id;\n\n\t\t\t// Whether this call runs detached. The agent loop reads the tool's\n\t\t\t// `background` flag (the same predicate) to run execute() detached; we\n\t\t\t// recompute it here to choose the notify-and-pull return shape.\n\t\t\tconst isBackground = params.background ?? backgroundAgents.has(params.subagent_type);\n\n\t\t\tif (isBackground) {\n\t\t\t\t// Notify-and-pull: register the dispatch in the inbox under the\n\t\t\t\t// pre-allocated id, await it, retain the body in the inbox, and return a\n\t\t\t\t// compact notification (not the body). The model pulls it with TaskOutput.\n\t\t\t\tsubagentInbox.observe(pool);\n\t\t\t\tsubagentInbox.start(poolTaskId, label, params.subagent_type);\n\n\t\t\t\t// Warm path (opt-in): run on a reused RPC worker, retain the body in the\n\t\t\t\t// inbox, and return the same notify-and-pull shape as the cold path. An\n\t\t\t\t// infra failure falls through to the cold dispatch below.\n\t\t\t\tif (warmSubagentsEnabled() && !forkSessionFile) {\n\t\t\t\t\tconst warm = getWarmSubagentPool(ctx.cwd, availableModels);\n\t\t\t\t\tif (warm.isPoolable(params.subagent_type)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst warmResult = await warm.dispatch(\n\t\t\t\t\t\t\t\tparams.prompt,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tagentType: params.subagent_type,\n\t\t\t\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t(activity) => taskStore.patchAgent(poolTaskId, { activity }),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst dispatchResult = warmResultToTaskResult(warmResult, params.subagent_type, task.id);\n\t\t\t\t\t\t\tsubagentInbox.finish(poolTaskId, dispatchResult);\n\t\t\t\t\t\t\treturn finalizeDispatchResult(\n\t\t\t\t\t\t\t\tdispatchResult,\n\t\t\t\t\t\t\t\tparams.subagent_type,\n\t\t\t\t\t\t\t\tpoolTaskId,\n\t\t\t\t\t\t\t\ttask.id,\n\t\t\t\t\t\t\t\tpoolTaskId,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t\t\t\t\tlabel,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tif (!(error instanceof WarmWorkerError)) throw error;\n\t\t\t\t\t\t\tagentLog(`[WARM] ${params.subagent_type} fell back to cold spawn: ${error.message}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst dispatchResult = await cancelOnAbort(poolTaskId, () =>\n\t\t\t\t\t\tpool.dispatch(params.prompt, {\n\t\t\t\t\t\t\tforceAgent: params.subagent_type,\n\t\t\t\t\t\t\tcontext: \"\",\n\t\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\tsessionFile: forkSessionFile,\n\t\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tsubagentInbox.finish(poolTaskId, dispatchResult);\n\t\t\t\t\treturn finalizeDispatchResult(dispatchResult, params.subagent_type, poolTaskId, task.id, poolTaskId, {\n\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\t\t\tmarkDispatchFailed(task.id, poolTaskId);\n\t\t\t\t\tsubagentInbox.fail(poolTaskId, reason);\n\t\t\t\t\t// A background dispatch reports failure as a compact notification, not a\n\t\t\t\t\t// thrown tool error — the call was already answered by a placeholder.\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `${label} failed ✗ — ${reason}` }],\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tsubagent_type: params.subagent_type,\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\terror: reason,\n\t\t\t\t\t\t\ttaskId: task.id,\n\t\t\t\t\t\t\tpoolTaskId,\n\t\t\t\t\t\t\tbackground: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Warm path (opt-in): for an eligible foreground dispatch, run on a reused\n\t\t\t// RPC worker to skip the cold-boot. Fork agents (forkSessionFile set) and\n\t\t\t// non-poolable types are excluded. Any infra failure falls through to the\n\t\t\t// cold pool below, so enabling this can only change latency, never whether\n\t\t\t// the task can run.\n\t\t\tif (warmSubagentsEnabled() && !forkSessionFile) {\n\t\t\t\tconst warm = getWarmSubagentPool(ctx.cwd, availableModels);\n\t\t\t\tif (warm.isPoolable(params.subagent_type)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst warmResult = await warm.dispatch(\n\t\t\t\t\t\t\tparams.prompt,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tagentType: params.subagent_type,\n\t\t\t\t\t\t\t\tcwd: ctx.cwd,\n\t\t\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t// Mirror the cold pool's live progress on the task panel roster so a\n\t\t\t\t\t\t\t// warm dispatch reads as busy (⋯ grep), not stuck.\n\t\t\t\t\t\t\t(activity) => taskStore.patchAgent(poolTaskId, { activity }),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst dispatchResult = warmResultToTaskResult(warmResult, params.subagent_type, task.id);\n\t\t\t\t\t\treturn finalizeDispatchResult(dispatchResult, params.subagent_type, poolTaskId, task.id, undefined);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// A genuine infra failure (worker crash/timeout) retries cold; any other\n\t\t\t\t\t\t// error is a real dispatch failure and propagates.\n\t\t\t\t\t\tif (!(error instanceof WarmWorkerError)) {\n\t\t\t\t\t\t\ttaskStore.update(task.id, { status: \"failed\" });\n\t\t\t\t\t\t\ttaskStore.patchAgent(poolTaskId, { state: \"failed\", activity: \"\" });\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tagentLog(`[WARM] ${params.subagent_type} fell back to cold spawn: ${error.message}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Foreground: block the turn and return the subagent's full answer inline.\n\t\t\ttry {\n\t\t\t\tconst dispatchResult = await cancelOnAbort(poolTaskId, () =>\n\t\t\t\t\tpool.dispatch(params.prompt, {\n\t\t\t\t\t\tforceAgent: params.subagent_type,\n\t\t\t\t\t\tcontext: \"\",\n\t\t\t\t\t\tmodel: dispatchModel,\n\t\t\t\t\t\tprovider: ctx.model?.provider,\n\t\t\t\t\t\tsessionFile: forkSessionFile,\n\t\t\t\t\t\ttaskId: poolTaskId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn finalizeDispatchResult(\n\t\t\t\t\tdispatchResult,\n\t\t\t\t\tparams.subagent_type,\n\t\t\t\t\tpoolTaskId,\n\t\t\t\t\ttask.id,\n\t\t\t\t\tdispatchResult.task_id,\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tmarkDispatchFailed(task.id, poolTaskId);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme) {\n\t\t\tconst type = args.subagent_type ?? \"agent\";\n\t\t\t// The [type] tag carries the agent's identity color — the same hue this\n\t\t\t// agent has in the task panel and TaskOutput — so a transcript full of\n\t\t\t// concurrent dispatches is scannable by color.\n\t\t\tconst text = theme.fg(\"toolTitle\", theme.bold(\"Agent \")) + theme.fg(agentColorFor(type), `[${type}]`);\n\t\t\treturn new Text(text, 0, 0);\n\t\t},\n\t});\n}\n\n/**\n * For a `fork: true` agent, fork the parent's session so the subagent inherits the\n * full parent conversation (and its prompt cache) instead of starting fresh. Returns\n * the forked session file to dispatch the child with, or undefined to fall back to a\n * fresh session (non-fork agent, no parent session, or an empty/invalid source).\n */\nexport function resolveForkSessionFile(\n\tdef: Pick<AgentDefinition, \"fork\">,\n\tparentSessionPath: string | undefined,\n\tcwd: string,\n): string | undefined {\n\tif (!def.fork || !parentSessionPath) return undefined;\n\ttry {\n\t\treturn SessionManager.forkFrom(parentSessionPath, cwd).getSessionFile();\n\t} catch {\n\t\t// Empty/invalid parent session: fall back to a fresh subagent session.\n\t\treturn undefined;\n\t}\n}\n\n/** Pre-allocated pool task id for a dispatch (matches the pool's own format). */\nfunction newDispatchTaskId(): string {\n\treturn `dispatch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * The TodoWrite plan item this dispatch is (most plausibly) working on: the\n * single in_progress main-agent task. TodoWrite discipline keeps exactly one\n * item in_progress, so when that holds the link is unambiguous; with zero or\n * several in_progress items no link is recorded rather than guessing. Used to\n * nest the run under its plan item in the task panel's flat lens.\n */\nfunction linkedTodoId(): number | undefined {\n\tconst inProgress = taskStore\n\t\t.list()\n\t\t.filter(\n\t\t\t(t) =>\n\t\t\t\tt.source === undefined &&\n\t\t\t\tt.agent === undefined &&\n\t\t\t\tt.parentTaskId === undefined &&\n\t\t\t\tt.status === \"in_progress\",\n\t\t);\n\treturn inProgress.length === 1 ? inProgress[0]?.id : undefined;\n}\n\n/**\n * Register this dispatch in the task store's roster, keyed by its run id (the\n * pool task id) so concurrent runs of the same agent type each get their own\n * row, state, activity, and stats. The friendly label (\"explore#1\") names the\n * row the same way the inbox names background tasks.\n */\nfunction registerSubagentDispatch(runId: string, label: string): void {\n\ttaskStore.upsertAgent({ id: runId, name: label, role: \"subagent\", kind: \"subagent\", state: \"running\" });\n}\n\n/**\n * Catch-path bookkeeping: mark the dispatch failed unless it already settled.\n * finalizeDispatchResult records a terminal status (failed/cancelled) *before*\n * throwing, so a blanket \"failed\" here would clobber a cancellation with a\n * failure the moment the thrown error passed through.\n */\nfunction markDispatchFailed(taskStoreId: number, runId: string): void {\n\tconst current = taskStore.list().find((t) => t.id === taskStoreId);\n\tif (current && current.status !== \"pending\" && current.status !== \"in_progress\") return;\n\ttaskStore.update(taskStoreId, { status: \"failed\" });\n\ttaskStore.patchAgent(runId, { state: \"failed\", activity: \"\" });\n}\n\n/** Names of agents configured to run in the background (non-blocking). */\nfunction collectBackgroundAgentNames(cwd: string): Set<string> {\n\tconst names = new Set<string>();\n\tfor (const agent of loadAgentRegistry({ cwd }).list()) {\n\t\tif (agent.background) names.add(agent.name);\n\t}\n\treturn names;\n}\n\n/**\n * Merge a child subagent's task subtree into the parent's task store, rooting\n * each top-level node under the dispatching task (`parentTaskId`). Recurses so a\n * subagent that itself delegated shows its nested work — the subtree the child\n * could not surface across the process boundary on its own. Each node is its own\n * task (no key-by-type collapse), preserving the order the child created them.\n */\nfunction mergeChildTaskTree(nodes: readonly SubagentTaskNode[] | undefined, parentTaskId: number): void {\n\tif (!nodes) return;\n\ttaskStore.batch(() => {\n\t\tfor (const node of nodes) {\n\t\t\tconst created = taskStore.create(node.title, {\n\t\t\t\tsource: node.source,\n\t\t\t\tsubagentMode: node.subagentMode,\n\t\t\t\tparentTaskId,\n\t\t\t});\n\t\t\ttaskStore.update(created.id, { status: node.status, usage: node.usage });\n\t\t\tmergeChildTaskTree(node.children, created.id);\n\t\t}\n\t});\n}\n\n/**\n * Adapt a warm-worker run into the TaskResult shape finalizeDispatchResult\n * consumes, so the warm and cold paths share one finish/render path. A warm run\n * returns its answer inline (no result.json), so we synthesize an equivalent\n * SubagentResult with the answer as the summary and the pulled usage. The warm\n * path is clean (no persisted session), so there is no task_tree to merge and no\n * resume handle.\n */\nfunction warmResultToTaskResult(warm: WarmRunResult, agentType: string, taskStoreId: number): TaskResult {\n\tconst resultData: SubagentResultFile = {\n\t\tsummary: warm.summary || \"(subagent returned no output)\",\n\t\tfiles_changed: [],\n\t\tconfidence: warm.ok ? 1 : 0,\n\t\tstatus: warm.status,\n\t\tusage: warm.usage,\n\t};\n\tconst result: SubagentResult = {\n\t\ttask_id: String(taskStoreId),\n\t\tok: warm.ok,\n\t\tstdout: \"\",\n\t\tstderr: \"\",\n\t\texit_code: warm.ok ? 0 : 1,\n\t\tstatus: warm.status,\n\t\terror: warm.error,\n\t\tresult_data: resultData as unknown as Record<string, unknown>,\n\t};\n\treturn { handled_inline: false, agent_type: agentType, result };\n}\n\n/**\n * Update the task panel from a finished dispatch and shape the tool result.\n *\n * Foreground calls return the subagent's full answer inline and signal a hard\n * failure by throwing (the agent loop derives a tool's error state from a thrown\n * error). A background call passes `background`: the body already lives in the\n * inbox, so it returns a compact, self-contained notification (success or\n * failure) and never throws — the call was already answered by a placeholder.\n */\nfunction finalizeDispatchResult(\n\tdispatchResult: TaskResult,\n\tsubagentType: string,\n\trunAgentId: string,\n\ttaskStoreId: number,\n\tresumeHandle: string | undefined,\n\tbackground?: { taskId: string; label: string },\n): { content: Array<{ type: \"text\"; text: string }>; details: TaskToolDetails } {\n\tconst result = dispatchResult.result;\n\tconst resultData = result?.result_data as SubagentResultFile | undefined;\n\tconst usage = resultData?.usage;\n\n\t// Merge the child's own task subtree under the dispatching task so nested\n\t// delegation (depth >= 2) is visible in the subagents lens's task tree.\n\tmergeChildTaskTree(resultData?.task_tree, taskStoreId);\n\n\t// Roll this run's usage into its own roster row (rows are per-dispatch).\n\tif (usage) {\n\t\ttaskStore.addAgentStats(runAgentId, { input: usage.input, output: usage.output, cost: usage.cost });\n\t}\n\n\tif (!result || !result.ok) {\n\t\t// A user-initiated cancel is not a failure: it gets its own terminal\n\t\t// status so the panel shows ⊘ cancelled (dim) instead of ✗ failed (red).\n\t\tconst cancelled = result?.status === \"cancelled\";\n\t\tconst failNote = result?.usedInheritedModelFallback ? \"inherited-model retry failed\" : undefined;\n\t\ttaskStore.update(taskStoreId, { status: cancelled ? \"cancelled\" : \"failed\", usage, note: failNote });\n\t\ttaskStore.patchAgent(runAgentId, { state: cancelled ? \"cancelled\" : \"failed\", activity: \"\" });\n\t\tconst reason = result?.error ?? (result?.status ? `subagent ${result.status}` : \"unknown error\");\n\t\tif (background) {\n\t\t\tconst verdict = cancelled ? \"cancelled ⊘\" : \"failed ✗\";\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `${background.label} ${verdict} — ${reason}` }],\n\t\t\t\tdetails: {\n\t\t\t\t\tsubagent_type: subagentType,\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: reason,\n\t\t\t\t\ttaskId: taskStoreId,\n\t\t\t\t\tpoolTaskId: background.taskId,\n\t\t\t\t\tbackground: true,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (cancelled) {\n\t\t\tthrow new Error(`Subagent (${subagentType}) cancelled by user.`);\n\t\t}\n\t\tconst stderr = result?.stderr?.trim();\n\t\tthrow new Error(`Subagent (${subagentType}) failed: ${reason}${stderr ? `\\nstderr: ${stderr.slice(-500)}` : \"\"}`);\n\t}\n\n\t// Leave the task in the store with its final status; it stays visible in the\n\t// task panel until the next user message arrives. Surface a ⚠ cue when the run\n\t// fell back to the inherited model rather than emitting a chat message — and\n\t// clear any stale note otherwise (note is clearable via an explicit undefined).\n\tconst fallbackNote = dispatchResult.result?.usedInheritedModelFallback ? \"ran on inherited model\" : undefined;\n\ttaskStore.update(taskStoreId, { status: \"done\", usage, note: fallbackNote });\n\t// Rows are per-run, so this run settles to done regardless of siblings.\n\ttaskStore.patchAgent(runAgentId, { state: \"done\", activity: \"\" });\n\tlet answer = resultData?.summary || \"(subagent returned no output)\";\n\t// Partial results are resumable; surface the handle so the parent can continue.\n\tif (result.status === \"partial\" && resumeHandle) {\n\t\tanswer += `\\n\\n[Partial result. To continue this subagent, call Task again with resume_task_id=\"${resumeHandle}\".]`;\n\t}\n\n\tif (background) {\n\t\t// Compact notification: the body is retained in the inbox; the model pulls it\n\t\t// with TaskOutput. Keeps a wide swarm from flooding the parent's context.\n\t\tconst partial = result.status === \"partial\" ? \" (partial — resume to continue)\" : \"\";\n\t\tconst outstanding = subagentInbox.outstanding().length;\n\t\tconst tail = outstanding > 0 ? ` ${outstanding} still running.` : \"\";\n\t\tconst text =\n\t\t\t`${background.label} finished ✓${partial} — ${summarize(answer)}.${tail}\\n` +\n\t\t\t`Read the full result with TaskOutput(\"${background.label}\").`;\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails: {\n\t\t\t\tsubagent_type: subagentType,\n\t\t\t\tok: true,\n\t\t\t\ttaskId: taskStoreId,\n\t\t\t\tpoolTaskId: background.taskId,\n\t\t\t\tbackground: true,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tcontent: [{ type: \"text\", text: answer }],\n\t\tdetails: { subagent_type: subagentType, ok: true, taskId: taskStoreId, poolTaskId: resumeHandle },\n\t};\n}\n\nconst taskOutputParams = Type.Object({\n\ttask_id: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t'Handle of a background subagent — its task_id or friendly label (e.g. \"explore#1\") from a Task notification. Omit (or set list:true) to see every background task.',\n\t\t}),\n\t),\n\tlist: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"List all background subagents with their status (running/done/failed/cancelled) and current activity. No result bodies are returned.\",\n\t\t}),\n\t),\n\twait: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Block until the named task finishes — or, with no task_id, until all outstanding subagents finish (a swarm barrier) — before returning. Bounded by timeout_ms.\",\n\t\t}),\n\t),\n\ttimeout_ms: Type.Optional(\n\t\tType.Number({ description: \"Maximum time to block in wait mode, in milliseconds (default 120000).\" }),\n\t),\n});\n\ntype TaskOutputParams = Static<typeof taskOutputParams>;\n\nconst TASK_OUTPUT_DEFAULT_TIMEOUT_MS = 120_000;\n\n/**\n * Elapsed time a record has run (so far, or until it settled), in the same\n * format the task panel uses so the two surfaces always agree.\n */\nfunction recordElapsed(rec: InboxRecord): string {\n\tconst end = rec.endedAt ?? Date.now();\n\treturn formatDurationSecs((end - rec.startedAt) / 1000);\n}\n\n/** A compact roster of every known background subagent — status + activity, no bodies. */\nfunction formatTaskRoster(): { content: Array<{ type: \"text\"; text: string }>; details: TaskOutputDetails } {\n\tconst all = subagentInbox.list();\n\tconst outstanding = subagentInbox.outstanding().length;\n\tif (all.length === 0) {\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: \"No background subagents have been dispatched.\" }],\n\t\t\tdetails: { status: \"empty\", ok: true, outstanding: 0 },\n\t\t};\n\t}\n\tconst lines = all.map((r) => {\n\t\tconst when = recordElapsed(r);\n\t\tswitch (r.lifecycle) {\n\t\t\tcase \"running\":\n\t\t\t\treturn `- ${r.label} running ${when}${r.lastActivity ? ` · ${r.lastActivity}` : \"\"}`;\n\t\t\tcase \"done\":\n\t\t\t\treturn `- ${r.label} done (uncollected) ${when} — ${r.summaryLine ?? \"\"}`;\n\t\t\tcase \"collected\":\n\t\t\t\treturn `- ${r.label} collected ${when} — ${r.summaryLine ?? \"\"}`;\n\t\t\tcase \"cancelled\":\n\t\t\t\treturn `- ${r.label} cancelled ⊘ — ${r.error ?? \"cancelled by user\"}`;\n\t\t\tdefault:\n\t\t\t\treturn `- ${r.label} ${r.lifecycle} ✗ — ${r.error ?? \"unknown error\"}`;\n\t\t}\n\t});\n\tconst header = `${all.length} background subagent${all.length === 1 ? \"\" : \"s\"} (${outstanding} running):`;\n\tconst hint = all.some((r) => r.lifecycle === \"done\") ? '\\nRead a finished one with TaskOutput(\"<label>\").' : \"\";\n\treturn {\n\t\tcontent: [{ type: \"text\", text: `${header}\\n${lines.join(\"\\n\")}${hint}` }],\n\t\tdetails: { status: \"list\", ok: true, outstanding },\n\t};\n}\n\n/**\n * TaskOutput tool: check on background subagents and pull their results.\n *\n * Background `Task` calls don't push their body into the conversation — they\n * leave it in the inbox and post a compact notification. TaskOutput is how the\n * model pulls a body, checks liveness, or waits. It never throws on a valid\n * handle (an error tool result would only confuse the loop): it reports status\n * instead. Modes: `list` (roster), a `task_id` to read/check one, and `wait` to\n * block until one task — or all outstanding tasks — finish.\n */\nexport function createTaskOutputToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof taskOutputParams, TaskOutputDetails>({\n\t\tname: \"TaskOutput\",\n\t\tlabel: \"TaskOutput\",\n\t\tdescription: [\n\t\t\t\"Check on background subagents dispatched via Task, and pull their results.\",\n\t\t\t'Pass a task_id/label (e.g. \"explore#1\") to read a finished subagent\\'s full result, or to see its status while it runs.',\n\t\t\t\"Set list:true (or omit task_id) to list every background subagent with its status and current activity.\",\n\t\t\t\"Set wait:true to block until that task finishes — or, with no task_id, until all outstanding subagents finish (a swarm barrier).\",\n\t\t\t\"It never errors on a valid handle: a running task reports status, a finished one returns its result, an already-read one says so.\",\n\t\t].join(\"\\n\"),\n\t\tpromptSnippet: \"check status / list / collect the results of background subagents\",\n\t\tparameters: taskOutputParams,\n\n\t\tasync execute(_toolCallId, params: TaskOutputParams, _signal, _onUpdate, ctx) {\n\t\t\t// Touch the pool so the inbox is wired to its progress stream for activity.\n\t\t\t// The pool is normally already created by a prior dispatch; pass available\n\t\t\t// models so a first-touch here still seeds category derivation.\n\t\t\tsubagentInbox.observe(getSubagentPool(ctx.cwd, ctx.modelRegistry?.getAvailable() ?? []));\n\t\t\tconst handle = params.task_id?.trim();\n\n\t\t\t// Barrier: wait for the target (or all outstanding) to settle first.\n\t\t\tif (params.wait) {\n\t\t\t\tconst timeout = params.timeout_ms ?? TASK_OUTPUT_DEFAULT_TIMEOUT_MS;\n\t\t\t\tif (handle) await subagentInbox.waitFor(handle, timeout);\n\t\t\t\telse await subagentInbox.waitForAll(timeout);\n\t\t\t}\n\n\t\t\t// Roster when asked, or when no specific task was named.\n\t\t\tif (params.list || !handle) {\n\t\t\t\treturn formatTaskRoster();\n\t\t\t}\n\n\t\t\tconst rec = subagentInbox.get(handle);\n\t\t\tif (!rec) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `No background task \"${handle}\". Call TaskOutput with list:true to see active tasks.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task_id: handle, status: \"unknown\", ok: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rec.lifecycle === \"running\") {\n\t\t\t\tconst activity = rec.lastActivity ? ` (currently: ${rec.lastActivity})` : \"\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `${rec.label} is still running — ${recordElapsed(rec)} elapsed${activity}. Call TaskOutput again, or with wait:true to block until it finishes.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task_id: handle, status: \"running\", ok: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rec.lifecycle === \"done\") {\n\t\t\t\tconst collected = subagentInbox.collect(handle);\n\t\t\t\tconst body = collected?.body ?? rec.summaryLine ?? \"(subagent returned no output)\";\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: body }],\n\t\t\t\t\tdetails: { task_id: handle, status: \"done\", ok: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (rec.lifecycle === \"collected\") {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `${rec.label} was already delivered — ${rec.summaryLine ?? \"(no summary kept)\"}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { task_id: handle, status: \"collected\", ok: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// failed / stalled / timeout / cancelled\n\t\t\tconst glyph = rec.lifecycle === \"cancelled\" ? \"⊘\" : \"✗\";\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\ttext: `${rec.label} ${rec.lifecycle} ${glyph} — ${rec.error ?? \"unknown error\"}.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { task_id: handle, status: rec.lifecycle, ok: false },\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme) {\n\t\t\tconst target = args.list ? \"list\" : String(args.task_id ?? \"\");\n\t\t\t// A friendly label (\"explore#1\") gets its agent's identity color so the\n\t\t\t// pull visually pairs with the dispatch that produced it; raw ids and\n\t\t\t// \"list\" stay dim.\n\t\t\tconst hashIdx = target.indexOf(\"#\");\n\t\t\tconst styledTarget =\n\t\t\t\thashIdx > 0 ? theme.fg(agentColorFor(target.slice(0, hashIdx)), target) : theme.fg(\"dim\", target);\n\t\t\tconst text =\n\t\t\t\ttheme.fg(\"toolTitle\", theme.bold(\"TaskOutput \")) +\n\t\t\t\tstyledTarget +\n\t\t\t\t(args.wait ? theme.fg(\"dim\", \" (wait)\") : \"\");\n\t\t\treturn new Text(text, 0, 0);\n\t\t},\n\n\t\trenderResult(result, _options, theme) {\n\t\t\t// Display-only framing/colorization (the text sent to the model stays\n\t\t\t// plain). A single-task pull renders as a result CARD — a status-stamped\n\t\t\t// header, a left spine down the body, and a closing corner — so a\n\t\t\t// collected result reads as a discrete artifact instead of blending into\n\t\t\t// the surrounding tool chatter. Roster/list responses keep the line\n\t\t\t// colorization.\n\t\t\tconst text = result.content\n\t\t\t\t.map((c) => (c.type === \"text\" ? c.text : \"\"))\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t\tif (!text) return new Text(\"\", 0, 0);\n\n\t\t\tconst details = result.details as TaskOutputDetails | undefined;\n\t\t\tconst cardStatus = details?.task_id ? details.status : undefined;\n\t\t\tif (cardStatus && cardStatus !== \"list\" && cardStatus !== \"empty\") {\n\t\t\t\tconst presentation: Record<\n\t\t\t\t\tstring,\n\t\t\t\t\t{ glyph: string; color: \"success\" | \"warning\" | \"error\" | \"muted\" | \"dim\" }\n\t\t\t\t> = {\n\t\t\t\t\tdone: { glyph: \"✓\", color: \"success\" },\n\t\t\t\t\trunning: { glyph: \"◐\", color: \"warning\" },\n\t\t\t\t\tcollected: { glyph: \"✓\", color: \"muted\" },\n\t\t\t\t\tcancelled: { glyph: \"⊘\", color: \"dim\" },\n\t\t\t\t\tunknown: { glyph: \"?\", color: \"dim\" },\n\t\t\t\t};\n\t\t\t\tconst { glyph, color } = presentation[cardStatus] ?? { glyph: \"✗\", color: \"error\" as const };\n\t\t\t\tconst label = details?.task_id ?? \"\";\n\t\t\t\tconst hashIdx = label.indexOf(\"#\");\n\t\t\t\tconst labelColor = hashIdx > 0 ? agentColorFor(label.slice(0, hashIdx)) : \"accent\";\n\n\t\t\t\t// Look up inbox record for elapsed time\n\t\t\t\tconst inboxRecord = subagentInbox.get(label);\n\t\t\t\tconst elapsed = inboxRecord ? (inboxRecord.endedAt ?? Date.now()) - inboxRecord.startedAt : undefined;\n\t\t\t\tconst elapsedText = elapsed ? ` ${formatDurationSecs(elapsed / 1000)}` : \"\";\n\n\t\t\t\t// Look up task for token count\n\t\t\t\tconst task = inboxRecord ? taskStore.list().find((t) => t.agent === inboxRecord.taskId) : undefined;\n\t\t\t\tconst totalTokens = task?.usage ? task.usage.input + task.usage.output : undefined;\n\t\t\t\tconst tokenText = totalTokens !== undefined ? ` ${formatTokens(totalTokens)}` : \"\";\n\n\t\t\t\tconst spine = (s: string) => theme.fg(\"borderMuted\", s);\n\t\t\t\tconst header =\n\t\t\t\t\t`${spine(\"╭\")} ${theme.fg(color, glyph)} ` +\n\t\t\t\t\t`${theme.bold(theme.fg(color, cardStatus))} ${theme.fg(labelColor, label)}${tokenText}${elapsedText}`;\n\t\t\t\tconst body = text\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.map((line) => `${spine(\"│\")} ${theme.fg(\"toolOutput\", line)}`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn new Text(`${header}\\n${body}\\n${spine(\"╰\")}`, 0, 0);\n\t\t\t}\n\n\t\t\tconst rosterLine =\n\t\t\t\t/^- (\\S+)\\s{2}(running|done \\(uncollected\\)|collected|failed|stalled|timeout|cancelled)(.*)$/;\n\t\t\tconst styled = text\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((line) => {\n\t\t\t\t\tconst match = rosterLine.exec(line);\n\t\t\t\t\tif (!match) return theme.fg(\"toolOutput\", line);\n\t\t\t\t\tconst [, label, status, rest] = match as unknown as [string, string, string, string];\n\t\t\t\t\tconst hashIdx = label.indexOf(\"#\");\n\t\t\t\t\tconst labelColor = hashIdx > 0 ? agentColorFor(label.slice(0, hashIdx)) : \"accent\";\n\t\t\t\t\tconst statusColor =\n\t\t\t\t\t\tstatus === \"running\"\n\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t: status.startsWith(\"done\")\n\t\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t\t: status === \"collected\" || status === \"cancelled\"\n\t\t\t\t\t\t\t\t\t? \"muted\"\n\t\t\t\t\t\t\t\t\t: \"error\";\n\t\t\t\t\treturn `- ${theme.fg(labelColor, label)} ${theme.fg(statusColor, status)}${theme.fg(\"dim\", rest)}`;\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\treturn new Text(styled, 0, 0);\n\t\t},\n\t});\n}\n"]}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { Text } from "@kolisachint/hoocode-tui";
|
|
17
17
|
import { Type } from "typebox";
|
|
18
|
+
import { EMBEDDED_PROMPTS } from "../../init-templates.generated.js";
|
|
18
19
|
import { agentColorFor } from "../../modes/interactive/theme/theme.js";
|
|
19
20
|
import { TASK_TOOL_NAME } from "../agent-frontmatter.js";
|
|
20
21
|
import { agentLog } from "../agent-log.js";
|
|
@@ -43,7 +44,13 @@ export { summarizeAgentDescription } from "../agent-registry.js";
|
|
|
43
44
|
/**
|
|
44
45
|
* Build the main-session subagent instructions appended to the system prompt.
|
|
45
46
|
*
|
|
46
|
-
* The
|
|
47
|
+
* The prose lives in `templates/prompts/task-*.md` and reaches here through the
|
|
48
|
+
* build-time embed. It is the largest block of always-on text hoocode emits
|
|
49
|
+
* (~600 tok/turn once the Task tool is on) and it had no interpolation beyond
|
|
50
|
+
* the single background slot, so keeping it as a TypeScript string constant
|
|
51
|
+
* bought nothing and made it awkward to edit as the prose it is.
|
|
52
|
+
*
|
|
53
|
+
* The detailed background/barrier guidance (the two heaviest bullets) is only
|
|
47
54
|
* emitted when the project actually has background-capable agents; otherwise a
|
|
48
55
|
* single concise line covers the per-call `background: true` escape hatch. This
|
|
49
56
|
* keeps the always-on cost down for the common case where nothing runs in the
|
|
@@ -51,29 +58,10 @@ export { summarizeAgentDescription } from "../agent-registry.js";
|
|
|
51
58
|
*/
|
|
52
59
|
export function buildTaskMainPrompt(cwd = process.cwd()) {
|
|
53
60
|
const hasBackgroundAgents = collectBackgroundAgentNames(cwd).size > 0;
|
|
54
|
-
const backgroundGuidance = hasBackgroundAgents
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return `You have access to the **Task** tool. Use it to delegate self-contained tasks to specialized subagents that run in their own isolated context and return only their final answer. Pick an agent by name from the <available_agents> list in this prompt and pass it as \`subagent_type\`.
|
|
59
|
-
|
|
60
|
-
When to delegate:
|
|
61
|
-
1. The work is self-contained and you only need the final result, not intermediate steps.
|
|
62
|
-
2. You want to investigate or edit something in parallel without losing your current context or reasoning chain.
|
|
63
|
-
3. The task is a discrete unit (explore one module, run one test file, review one PR, fix one isolated bug).
|
|
64
|
-
4. You need to run a long command or test suite and wait for its output without blocking your own reasoning.
|
|
65
|
-
|
|
66
|
-
Model tier (optional \`complexity\`): set \`fast\` for quick reads/lookups, \`standard\` for multi-file edits, \`capable\` for deep architecture work. It maps to a model from \`settings.modelCategories\`. Omit it to use the agent's default; an agent that pins its own model ignores \`complexity\`.
|
|
67
|
-
|
|
68
|
-
Guidelines:
|
|
69
|
-
- Choose the agent whose description best matches the task.
|
|
70
|
-
- Make every task specific and self-contained. The subagent cannot see this conversation; pass all necessary context (files, constraints, prior findings) in \`prompt\`.
|
|
71
|
-
- Do NOT delegate tasks that require tight back-and-forth with your current reasoning, or edits to files you are actively reasoning about.
|
|
72
|
-
- The subagent returns ONLY its final answer. Its intermediate reasoning, tool calls, and output are hidden from you.
|
|
73
|
-
- Delegate proactively when work is self-contained or parallelizable: multi-step investigation, read-only exploration (use \`explore\`), research before changes (use \`plan\`), drafting a standalone file/section, or running a long command/test suite. Dispatch independent subtasks in the same turn. Handle only trivial single-step edits or tightly interactive back-and-forth inline.
|
|
74
|
-
${backgroundGuidance}
|
|
75
|
-
- When working through a TodoWrite plan, mark the plan item in_progress BEFORE dispatching subagents for it: each dispatch is attributed to the current in_progress item in the user's task panel, so dispatching first (or with several items in_progress) leaves the run unattributed.
|
|
76
|
-
- To continue a previous subagent (for example one that returned partial results), call Task again with \`resume_task_id\` set to its task_id; it resumes with its full prior transcript and \`prompt\` is your follow-up.`;
|
|
61
|
+
const backgroundGuidance = (EMBEDDED_PROMPTS[hasBackgroundAgents ? "task-background-agents" : "task-background-none"] ?? "").trim();
|
|
62
|
+
// Trimmed at both ends: the template file ends with a newline, and this is
|
|
63
|
+
// appended to a system prompt that manages its own separators.
|
|
64
|
+
return (EMBEDDED_PROMPTS["task-main"] ?? "").replace("{{BACKGROUND_GUIDANCE}}", backgroundGuidance).trim();
|
|
77
65
|
}
|
|
78
66
|
const taskParams = Type.Object({
|
|
79
67
|
description: Type.String({
|