@kolisachint/hoocode-agent 0.4.113 → 0.4.114
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 +14 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/plugins/authoring.d.ts +51 -0
- package/dist/core/extensions/plugins/authoring.d.ts.map +1 -0
- package/dist/core/extensions/plugins/authoring.js +91 -0
- package/dist/core/extensions/plugins/authoring.js.map +1 -0
- package/dist/core/extensions/plugins/default-marketplace/.agents-plugin/marketplace.json +12 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/.agents-plugin/plugin.json +6 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/commands/hello.md +5 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/skills/greet/SKILL.md +7 -0
- package/dist/core/extensions/plugins/formats/agents.d.ts +7 -0
- package/dist/core/extensions/plugins/formats/agents.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/agents.js +14 -0
- package/dist/core/extensions/plugins/formats/agents.js.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts +10 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.js +17 -0
- package/dist/core/extensions/plugins/formats/claude.js.map +1 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts +23 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/copilot.js +130 -0
- package/dist/core/extensions/plugins/formats/copilot.js.map +1 -0
- package/dist/core/extensions/plugins/formats/index.d.ts +41 -0
- package/dist/core/extensions/plugins/formats/index.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/index.js +76 -0
- package/dist/core/extensions/plugins/formats/index.js.map +1 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts +21 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.js +97 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.js.map +1 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts +44 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/shared.js +107 -0
- package/dist/core/extensions/plugins/formats/shared.js.map +1 -0
- package/dist/core/extensions/plugins/formats/types.d.ts +118 -0
- package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/types.js +19 -0
- package/dist/core/extensions/plugins/formats/types.js.map +1 -0
- package/dist/core/extensions/plugins/install.d.ts +73 -0
- package/dist/core/extensions/plugins/install.d.ts.map +1 -0
- package/dist/core/extensions/plugins/install.js +173 -0
- package/dist/core/extensions/plugins/install.js.map +1 -0
- package/dist/core/extensions/plugins/manifest.d.ts +25 -7
- package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
- package/dist/core/extensions/plugins/manifest.js +16 -91
- package/dist/core/extensions/plugins/manifest.js.map +1 -1
- package/dist/core/extensions/plugins/marketplace.d.ts +5 -4
- package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
- package/dist/core/extensions/plugins/marketplace.js +8 -13
- package/dist/core/extensions/plugins/marketplace.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +2 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +2 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +16 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +2 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/subagent-depth.d.ts +13 -0
- package/dist/core/subagent-depth.d.ts.map +1 -1
- package/dist/core/subagent-depth.js +15 -0
- package/dist/core/subagent-depth.js.map +1 -1
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +4 -1
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/tools/find.d.ts +8 -3
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +189 -122
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/index.d.ts +0 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/plugin-tool-names.d.ts +21 -0
- package/dist/core/tools/plugin-tool-names.d.ts.map +1 -0
- package/dist/core/tools/plugin-tool-names.js +31 -0
- package/dist/core/tools/plugin-tool-names.js.map +1 -0
- package/dist/core/tools/plugins.d.ts +49 -0
- package/dist/core/tools/plugins.d.ts.map +1 -0
- package/dist/core/tools/plugins.js +186 -0
- package/dist/core/tools/plugins.js.map +1 -0
- package/dist/core/tools/propose-plugin.d.ts +36 -0
- package/dist/core/tools/propose-plugin.d.ts.map +1 -0
- package/dist/core/tools/propose-plugin.js +219 -0
- package/dist/core/tools/propose-plugin.js.map +1 -0
- package/dist/core/warm-subagent-pool.d.ts.map +1 -1
- package/dist/core/warm-subagent-pool.js +4 -1
- package/dist/core/warm-subagent-pool.js.map +1 -1
- package/dist/extensions/core/marketplace.d.ts +7 -3
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +23 -67
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/extensions/core/mcp-deferred.d.ts +28 -0
- package/dist/extensions/core/mcp-deferred.d.ts.map +1 -0
- package/dist/extensions/core/mcp-deferred.js +53 -0
- package/dist/extensions/core/mcp-deferred.js.map +1 -0
- package/dist/extensions/core/mcp-loader.d.ts.map +1 -1
- package/dist/extensions/core/mcp-loader.js +132 -78
- package/dist/extensions/core/mcp-loader.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -1
- package/dist/main.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -5
- package/dist/core/tools/glob.d.ts +0 -42
- package/dist/core/tools/glob.d.ts.map +0 -1
- package/dist/core/tools/glob.js +0 -432
- package/dist/core/tools/glob.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propose-plugin.d.ts","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGzE,OAAO,EAAE,mCAAmC,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAwFvG,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iCAAiC,IAAI,cAAc,CA0DlE;AAqBD,MAAM,WAAW,8BAA8B;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACnB;AAiBD,wBAAgB,2CAA2C,IAAI,cAAc,CAkF5E;AAsBD,gFAAgF;AAChF,wBAAgB,kCAAkC,IAAI,cAAc,EAAE,CAErE","sourcesContent":["/**\n * Capability authoring tools (spec §3). The two escalating-risk build paths are\n * kept as *separate tools* by design — not collapsed into one:\n *\n * ProposePlugin scaffold passive/low-risk capabilities — skills,\n * commands, and read-only subagents. Autonomous +\n * transparent (near-zero risk, reversible).\n * ProposeExecutablePlugin the risk-bearing path — hooks, MCP servers, and\n * mutating/high-privilege subagents. Draft → display\n * the code and the tool grant → human confirms →\n * activate. Bar >= install.\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\tDEFAULT_AUTHORING_PLATFORMS,\n\tpluginExists,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport { PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME, PROPOSE_PLUGIN_TOOL_NAME } from \"./plugin-tool-names.js\";\n\nexport { PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME, PROPOSE_PLUGIN_TOOL_NAME } from \"./plugin-tool-names.js\";\n\nconst platformSchema = Type.Union([Type.Literal(\"claude\"), Type.Literal(\"github\"), Type.Literal(\"agents\")], {\n\tdescription: \"Target format: claude (Claude Code), github (GitHub Copilot), or agents (native).\",\n});\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({ description: \"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Omit for none.\" }),\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\nfunction resolvePlatforms(input: string[] | undefined): MarketplacePlatform[] {\n\treturn (input && input.length > 0 ? (input as MarketplacePlatform[]) : DEFAULT_AUTHORING_PLATFORMS).slice();\n}\n\nfunction summarizeWrite(id: string, platforms: MarketplacePlatform[], files: string[], dest: string): string {\n\treturn (\n\t\t`Authored plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nActive after the next reload; remove it with UninstallPlugin.`\n\t);\n}\n\n// ── ProposePlugin (scaffold path) ─────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Plugin id (directory + manifest name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\t\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\t\tplatforms: Type.Optional(\n\t\t\tType.Array(platformSchema, { description: \"Formats to scaffold into. Default: claude + github.\" }),\n\t\t),\n\t\tskills: Type.Optional(Type.Array(skillSchema)),\n\t\tcommands: Type.Optional(Type.Array(commandSchema)),\n\t\tsubagents: Type.Optional(\n\t\t\tType.Array(subagentSchema, { description: \"Read-only subagents only (mutating grants are rejected here).\" }),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport interface ProposePluginDetails {\n\tid: string;\n\tauthored: boolean;\n}\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, ProposePluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a new plugin from passive, low-risk capabilities — skills, slash commands, and READ-ONLY subagents — when no marketplace plugin fits a gap. Scaffolds a proper plugin (Claude Code + GitHub Copilot layouts by default). Autonomous and reversible. For hooks, MCP servers, or mutating/high-privilege subagents, use ProposeExecutablePlugin instead.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a skill/command/read-only-subagent plugin to fill a capability gap (scaffold; reversible).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use ProposePlugin only for passive capabilities: skills, commands, and subagents whose tools are read-only (read, grep, glob, webfetch).\",\n\t\t\t\"A subagent that needs Bash/Write/Edit/MCP or tools:* is mutating — author it with ProposeExecutablePlugin (human confirmation), not 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: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst platforms = resolvePlatforms(params.platforms);\n\n\t\t\t// Guardrail + risk gate on each subagent before writing anything.\n\t\t\tfor (const sa of params.subagents ?? []) {\n\t\t\t\tconst cls = classifyAllowlist(sa.tools);\n\t\t\t\tif (cls.pluginTools.length > 0) {\n\t\t\t\t\treturn reject(\n\t\t\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\",\n\t\t\t\t\t\tparams.id,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (cls.risk === \"mutating\") {\n\t\t\t\t\treturn reject(\n\t\t\t\t\t\t`Subagent \"${sa.name}\" has a mutating allowlist (${cls.reason}). ` +\n\t\t\t\t\t\t\t\"Use ProposeExecutablePlugin so the human can review and confirm the tool grant.\",\n\t\t\t\t\t\tparams.id,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (pluginExists(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\t`A plugin named \"${params.id}\" already exists. Uninstall it first or pick another id.`,\n\t\t\t\t\tparams.id,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst draft: PluginDraft = {\n\t\t\t\tid: params.id,\n\t\t\t\tversion: params.version,\n\t\t\t\tdescription: params.description,\n\t\t\t\tsupportPlatform: platforms,\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tagents: params.subagents,\n\t\t\t};\n\t\t\tconst result = writePluginDraft(ctx.cwd, draft, platforms);\n\t\t\tconst text = summarizeWrite(params.id, platforms, result.files, result.dest);\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn { content: [{ type: \"text\" as const, text }], details: { id: params.id, authored: true } };\n\t\t},\n\t});\n}\n\n// ── ProposeExecutablePlugin (risk-bearing path) ───────────────────────────────\n\nconst proposeExecParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Plugin id (directory + manifest name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\t\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\t\tplatforms: Type.Optional(\n\t\t\tType.Array(platformSchema, { description: \"Formats to scaffold into. Default: claude + github.\" }),\n\t\t),\n\t\thooks: Type.Optional(Type.Array(hookSchema)),\n\t\tmcpServers: Type.Optional(Type.Array(mcpServerSchema)),\n\t\tsubagents: Type.Optional(\n\t\t\tType.Array(subagentSchema, { description: \"Subagents with mutating/exec/network or tools:* allowlists.\" }),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport interface ProposeExecutablePluginDetails {\n\tid: string;\n\tauthored: boolean;\n\tconfirmed: boolean;\n}\n\n/** Build the human-facing review text: the executable code and every tool grant. */\nfunction buildReview(params: Static<typeof proposeExecParams>): string {\n\tconst lines: string[] = [`Plugin \"${params.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 params.subagents ?? []) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function createProposeExecutablePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeExecParams, ProposeExecutablePluginDetails>({\n\t\tname: PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a plugin that includes EXECUTABLE or high-privilege capabilities — hooks (run on tool events), MCP servers, or subagents with mutating/exec/network tool grants. The code and the exact tool grant are shown to the human, who must confirm before anything is activated. Use ProposePlugin for passive skills/commands/read-only subagents.\",\n\t\tpromptSnippet: \"Author a hook/MCP/high-privilege-subagent plugin (shows the code; requires human confirmation).\",\n\t\tpromptGuidelines: [\n\t\t\t\"ProposeExecutablePlugin always shows the code and tool grant and requires explicit human confirmation before activating.\",\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: proposeExecParams,\n\t\tasync execute(_id, params: Static<typeof proposeExecParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst platforms = resolvePlatforms(params.platforms);\n\n\t\t\t// Guardrail: authored subagents may never carry plugin-system tools.\n\t\t\tfor (const sa of params.subagents ?? []) {\n\t\t\t\tconst cls = classifyAllowlist(sa.tools);\n\t\t\t\tif (cls.pluginTools.length > 0) {\n\t\t\t\t\treturn rejectExec(\n\t\t\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\",\n\t\t\t\t\t\tparams.id,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst hasExecutable =\n\t\t\t\t(params.hooks?.length ?? 0) > 0 ||\n\t\t\t\t(params.mcpServers?.length ?? 0) > 0 ||\n\t\t\t\t(params.subagents?.length ?? 0) > 0;\n\t\t\tif (!hasExecutable) {\n\t\t\t\treturn rejectExec(\"Nothing to author. Provide hooks, mcpServers, or subagents.\", params.id);\n\t\t\t}\n\n\t\t\tif (pluginExists(ctx.cwd, params.id)) {\n\t\t\t\treturn rejectExec(\n\t\t\t\t\t`A plugin named \"${params.id}\" already exists. Uninstall it first or pick another id.`,\n\t\t\t\t\tparams.id,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Draft → display → confirm → activate. Fail closed without a UI to confirm on.\n\t\t\tconst review = buildReview(params);\n\t\t\tctx.ui.notify(review, \"warning\");\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\treturn rejectExec(\n\t\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t\t\tparams.id,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t`Author executable plugin \"${params.id}\"?`,\n\t\t\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t\t\t);\n\t\t\tif (!confirmed) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${params.id}\" was not authored.` }],\n\t\t\t\t\tdetails: { id: params.id, authored: false, confirmed: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst draft: PluginDraft = {\n\t\t\t\tid: params.id,\n\t\t\t\tversion: params.version,\n\t\t\t\tdescription: params.description,\n\t\t\t\tsupportPlatform: platforms,\n\t\t\t\thooks: params.hooks,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\tagents: params.subagents,\n\t\t\t};\n\t\t\tconst result = writePluginDraft(ctx.cwd, draft, platforms);\n\t\t\tconst text = summarizeWrite(params.id, platforms, result.files, result.dest);\n\t\t\tctx.ui.notify(`Authored executable 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: true },\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction reject(\n\tmessage: string,\n\tid: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: ProposePluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\nfunction rejectExec(\n\tmessage: string,\n\tid: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: ProposeExecutablePluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false, confirmed: false } };\n}\n\n/** Both authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [createProposePluginToolDefinition(), createProposeExecutablePluginToolDefinition()];\n}\n"]}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability authoring tools (spec §3). The two escalating-risk build paths are
|
|
3
|
+
* kept as *separate tools* by design — not collapsed into one:
|
|
4
|
+
*
|
|
5
|
+
* ProposePlugin scaffold passive/low-risk capabilities — skills,
|
|
6
|
+
* commands, and read-only subagents. Autonomous +
|
|
7
|
+
* transparent (near-zero risk, reversible).
|
|
8
|
+
* ProposeExecutablePlugin the risk-bearing path — hooks, MCP servers, and
|
|
9
|
+
* mutating/high-privilege subagents. Draft → display
|
|
10
|
+
* the code and the tool grant → human confirms →
|
|
11
|
+
* activate. Bar >= install.
|
|
12
|
+
*
|
|
13
|
+
* Both author into `.agents/plugins/<id>/` in the requested vendor layouts
|
|
14
|
+
* (Claude Code + GitHub Copilot by default) via the format registry, so results
|
|
15
|
+
* are proper, publishable plugins that round-trip through parsePluginDir.
|
|
16
|
+
*
|
|
17
|
+
* Privilege-amplification guardrail: an authored subagent may never carry a
|
|
18
|
+
* plugin-system (capability-acquisition) tool in its allowlist — enforced in
|
|
19
|
+
* both tools — so a low-trust authored agent cannot bootstrap privilege.
|
|
20
|
+
*/
|
|
21
|
+
import { Type } from "typebox";
|
|
22
|
+
import { classifyAllowlist, DEFAULT_AUTHORING_PLATFORMS, pluginExists, writePluginDraft, } from "../extensions/plugins/authoring.js";
|
|
23
|
+
import { defineTool } from "../extensions/types.js";
|
|
24
|
+
import { PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME, PROPOSE_PLUGIN_TOOL_NAME } from "./plugin-tool-names.js";
|
|
25
|
+
export { PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME, PROPOSE_PLUGIN_TOOL_NAME } from "./plugin-tool-names.js";
|
|
26
|
+
const platformSchema = Type.Union([Type.Literal("claude"), Type.Literal("github"), Type.Literal("agents")], {
|
|
27
|
+
description: "Target format: claude (Claude Code), github (GitHub Copilot), or agents (native).",
|
|
28
|
+
});
|
|
29
|
+
const skillSchema = Type.Object({
|
|
30
|
+
name: Type.String({ description: "Skill name." }),
|
|
31
|
+
description: Type.Optional(Type.String({ description: "One-line trigger description (kept lazy in context)." })),
|
|
32
|
+
body: Type.String({ description: "SKILL.md instruction body (markdown)." }),
|
|
33
|
+
}, { additionalProperties: false });
|
|
34
|
+
const commandSchema = Type.Object({
|
|
35
|
+
name: Type.String({ description: "Command name (invoked as /name)." }),
|
|
36
|
+
description: Type.Optional(Type.String({ description: "One-line description." })),
|
|
37
|
+
body: Type.String({ description: "Prompt template body (markdown)." }),
|
|
38
|
+
}, { additionalProperties: false });
|
|
39
|
+
const subagentSchema = Type.Object({
|
|
40
|
+
name: Type.String({ description: "Subagent name." }),
|
|
41
|
+
description: Type.Optional(Type.String({ description: "When to dispatch this subagent." })),
|
|
42
|
+
tools: Type.Optional(Type.String({ description: "Comma-separated allowed-tools, e.g. 'read, grep, glob'. Omit for none." })),
|
|
43
|
+
model: Type.Optional(Type.String({ description: "Model override, or 'inherit'." })),
|
|
44
|
+
body: Type.String({ description: "System-prompt / instruction body (markdown)." }),
|
|
45
|
+
}, { additionalProperties: false });
|
|
46
|
+
const hookSchema = Type.Object({
|
|
47
|
+
event: Type.String({ description: "Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ..." }),
|
|
48
|
+
matcher: Type.Optional(Type.String({ description: "Regex matched against the tool name. Empty/'*' = all." })),
|
|
49
|
+
command: Type.String({ description: "Shell command to run on the event." }),
|
|
50
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds." })),
|
|
51
|
+
}, { additionalProperties: false });
|
|
52
|
+
const mcpServerSchema = Type.Object({
|
|
53
|
+
name: Type.String({ description: "MCP server name." }),
|
|
54
|
+
command: Type.String({ description: "Executable to launch the server." }),
|
|
55
|
+
args: Type.Optional(Type.Array(Type.String(), { description: "Command arguments." })),
|
|
56
|
+
env: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Environment variables." })),
|
|
57
|
+
}, { additionalProperties: false });
|
|
58
|
+
function resolvePlatforms(input) {
|
|
59
|
+
return (input && input.length > 0 ? input : DEFAULT_AUTHORING_PLATFORMS).slice();
|
|
60
|
+
}
|
|
61
|
+
function summarizeWrite(id, platforms, files, dest) {
|
|
62
|
+
return (`Authored plugin "${id}" (${platforms.join(", ")}) with ${files.length} file(s) at ${dest}:\n` +
|
|
63
|
+
files.map((f) => ` ${f}`).join("\n") +
|
|
64
|
+
`\nActive after the next reload; remove it with UninstallPlugin.`);
|
|
65
|
+
}
|
|
66
|
+
// ── ProposePlugin (scaffold path) ─────────────────────────────────────────────
|
|
67
|
+
const proposeParams = Type.Object({
|
|
68
|
+
id: Type.String({ description: "Plugin id (directory + manifest name)." }),
|
|
69
|
+
description: Type.Optional(Type.String({ description: "Plugin description." })),
|
|
70
|
+
version: Type.Optional(Type.String({ description: "Plugin version, e.g. '0.1.0'." })),
|
|
71
|
+
platforms: Type.Optional(Type.Array(platformSchema, { description: "Formats to scaffold into. Default: claude + github." })),
|
|
72
|
+
skills: Type.Optional(Type.Array(skillSchema)),
|
|
73
|
+
commands: Type.Optional(Type.Array(commandSchema)),
|
|
74
|
+
subagents: Type.Optional(Type.Array(subagentSchema, { description: "Read-only subagents only (mutating grants are rejected here)." })),
|
|
75
|
+
}, { additionalProperties: false });
|
|
76
|
+
export function createProposePluginToolDefinition() {
|
|
77
|
+
return defineTool({
|
|
78
|
+
name: PROPOSE_PLUGIN_TOOL_NAME,
|
|
79
|
+
label: PROPOSE_PLUGIN_TOOL_NAME,
|
|
80
|
+
description: "Author a new plugin from passive, low-risk capabilities — skills, slash commands, and READ-ONLY subagents — when no marketplace plugin fits a gap. Scaffolds a proper plugin (Claude Code + GitHub Copilot layouts by default). Autonomous and reversible. For hooks, MCP servers, or mutating/high-privilege subagents, use ProposeExecutablePlugin instead.",
|
|
81
|
+
promptSnippet: "Author a skill/command/read-only-subagent plugin to fill a capability gap (scaffold; reversible).",
|
|
82
|
+
promptGuidelines: [
|
|
83
|
+
"Use ProposePlugin only for passive capabilities: skills, commands, and subagents whose tools are read-only (read, grep, glob, webfetch).",
|
|
84
|
+
"A subagent that needs Bash/Write/Edit/MCP or tools:* is mutating — author it with ProposeExecutablePlugin (human confirmation), not here.",
|
|
85
|
+
"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.",
|
|
86
|
+
],
|
|
87
|
+
parameters: proposeParams,
|
|
88
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
89
|
+
const platforms = resolvePlatforms(params.platforms);
|
|
90
|
+
// Guardrail + risk gate on each subagent before writing anything.
|
|
91
|
+
for (const sa of params.subagents ?? []) {
|
|
92
|
+
const cls = classifyAllowlist(sa.tools);
|
|
93
|
+
if (cls.pluginTools.length > 0) {
|
|
94
|
+
return reject(`Subagent "${sa.name}" requests plugin-system tools (${cls.pluginTools.join(", ")}). ` +
|
|
95
|
+
"Authored subagents may never carry capability-acquisition tools.", params.id);
|
|
96
|
+
}
|
|
97
|
+
if (cls.risk === "mutating") {
|
|
98
|
+
return reject(`Subagent "${sa.name}" has a mutating allowlist (${cls.reason}). ` +
|
|
99
|
+
"Use ProposeExecutablePlugin so the human can review and confirm the tool grant.", params.id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (pluginExists(ctx.cwd, params.id)) {
|
|
103
|
+
return reject(`A plugin named "${params.id}" already exists. Uninstall it first or pick another id.`, params.id);
|
|
104
|
+
}
|
|
105
|
+
const draft = {
|
|
106
|
+
id: params.id,
|
|
107
|
+
version: params.version,
|
|
108
|
+
description: params.description,
|
|
109
|
+
supportPlatform: platforms,
|
|
110
|
+
skills: params.skills,
|
|
111
|
+
commands: params.commands,
|
|
112
|
+
agents: params.subagents,
|
|
113
|
+
};
|
|
114
|
+
const result = writePluginDraft(ctx.cwd, draft, platforms);
|
|
115
|
+
const text = summarizeWrite(params.id, platforms, result.files, result.dest);
|
|
116
|
+
ctx.ui.notify(`Authored plugin "${params.id}" (${platforms.join(", ")}).`, "info");
|
|
117
|
+
return { content: [{ type: "text", text }], details: { id: params.id, authored: true } };
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// ── ProposeExecutablePlugin (risk-bearing path) ───────────────────────────────
|
|
122
|
+
const proposeExecParams = Type.Object({
|
|
123
|
+
id: Type.String({ description: "Plugin id (directory + manifest name)." }),
|
|
124
|
+
description: Type.Optional(Type.String({ description: "Plugin description." })),
|
|
125
|
+
version: Type.Optional(Type.String({ description: "Plugin version, e.g. '0.1.0'." })),
|
|
126
|
+
platforms: Type.Optional(Type.Array(platformSchema, { description: "Formats to scaffold into. Default: claude + github." })),
|
|
127
|
+
hooks: Type.Optional(Type.Array(hookSchema)),
|
|
128
|
+
mcpServers: Type.Optional(Type.Array(mcpServerSchema)),
|
|
129
|
+
subagents: Type.Optional(Type.Array(subagentSchema, { description: "Subagents with mutating/exec/network or tools:* allowlists." })),
|
|
130
|
+
}, { additionalProperties: false });
|
|
131
|
+
/** Build the human-facing review text: the executable code and every tool grant. */
|
|
132
|
+
function buildReview(params) {
|
|
133
|
+
const lines = [`Plugin "${params.id}" wants to install executable capabilities:`];
|
|
134
|
+
for (const h of params.hooks ?? []) {
|
|
135
|
+
lines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : ""}]: ${h.command}`);
|
|
136
|
+
}
|
|
137
|
+
for (const s of params.mcpServers ?? []) {
|
|
138
|
+
lines.push(` mcp server "${s.name}": ${s.command}${s.args?.length ? ` ${s.args.join(" ")}` : ""}`);
|
|
139
|
+
}
|
|
140
|
+
for (const sa of params.subagents ?? []) {
|
|
141
|
+
lines.push(` subagent "${sa.name}" tools: ${sa.tools ?? "(none)"}`);
|
|
142
|
+
}
|
|
143
|
+
return lines.join("\n");
|
|
144
|
+
}
|
|
145
|
+
export function createProposeExecutablePluginToolDefinition() {
|
|
146
|
+
return defineTool({
|
|
147
|
+
name: PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME,
|
|
148
|
+
label: PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME,
|
|
149
|
+
description: "Author a plugin that includes EXECUTABLE or high-privilege capabilities — hooks (run on tool events), MCP servers, or subagents with mutating/exec/network tool grants. The code and the exact tool grant are shown to the human, who must confirm before anything is activated. Use ProposePlugin for passive skills/commands/read-only subagents.",
|
|
150
|
+
promptSnippet: "Author a hook/MCP/high-privilege-subagent plugin (shows the code; requires human confirmation).",
|
|
151
|
+
promptGuidelines: [
|
|
152
|
+
"ProposeExecutablePlugin always shows the code and tool grant and requires explicit human confirmation before activating.",
|
|
153
|
+
"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.",
|
|
154
|
+
"Publishing a proven-useful plugin to a marketplace stays a human action — do not do it autonomously.",
|
|
155
|
+
],
|
|
156
|
+
parameters: proposeExecParams,
|
|
157
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
158
|
+
const platforms = resolvePlatforms(params.platforms);
|
|
159
|
+
// Guardrail: authored subagents may never carry plugin-system tools.
|
|
160
|
+
for (const sa of params.subagents ?? []) {
|
|
161
|
+
const cls = classifyAllowlist(sa.tools);
|
|
162
|
+
if (cls.pluginTools.length > 0) {
|
|
163
|
+
return rejectExec(`Subagent "${sa.name}" requests plugin-system tools (${cls.pluginTools.join(", ")}). ` +
|
|
164
|
+
"Authored subagents may never carry capability-acquisition tools.", params.id);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const hasExecutable = (params.hooks?.length ?? 0) > 0 ||
|
|
168
|
+
(params.mcpServers?.length ?? 0) > 0 ||
|
|
169
|
+
(params.subagents?.length ?? 0) > 0;
|
|
170
|
+
if (!hasExecutable) {
|
|
171
|
+
return rejectExec("Nothing to author. Provide hooks, mcpServers, or subagents.", params.id);
|
|
172
|
+
}
|
|
173
|
+
if (pluginExists(ctx.cwd, params.id)) {
|
|
174
|
+
return rejectExec(`A plugin named "${params.id}" already exists. Uninstall it first or pick another id.`, params.id);
|
|
175
|
+
}
|
|
176
|
+
// Draft → display → confirm → activate. Fail closed without a UI to confirm on.
|
|
177
|
+
const review = buildReview(params);
|
|
178
|
+
ctx.ui.notify(review, "warning");
|
|
179
|
+
if (!ctx.hasUI) {
|
|
180
|
+
return rejectExec("Authoring executable capabilities requires human confirmation, which is unavailable in this mode. " +
|
|
181
|
+
`Not activated.\n${review}`, params.id);
|
|
182
|
+
}
|
|
183
|
+
const confirmed = await ctx.ui.confirm(`Author executable plugin "${params.id}"?`, `${review}\n\nThis installs and can run the code above. Activate it?`);
|
|
184
|
+
if (!confirmed) {
|
|
185
|
+
return {
|
|
186
|
+
content: [{ type: "text", text: `Declined — plugin "${params.id}" was not authored.` }],
|
|
187
|
+
details: { id: params.id, authored: false, confirmed: false },
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const draft = {
|
|
191
|
+
id: params.id,
|
|
192
|
+
version: params.version,
|
|
193
|
+
description: params.description,
|
|
194
|
+
supportPlatform: platforms,
|
|
195
|
+
hooks: params.hooks,
|
|
196
|
+
mcpServers: params.mcpServers,
|
|
197
|
+
agents: params.subagents,
|
|
198
|
+
};
|
|
199
|
+
const result = writePluginDraft(ctx.cwd, draft, platforms);
|
|
200
|
+
const text = summarizeWrite(params.id, platforms, result.files, result.dest);
|
|
201
|
+
ctx.ui.notify(`Authored executable plugin "${params.id}" (${platforms.join(", ")}).`, "info");
|
|
202
|
+
return {
|
|
203
|
+
content: [{ type: "text", text }],
|
|
204
|
+
details: { id: params.id, authored: true, confirmed: true },
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function reject(message, id) {
|
|
210
|
+
return { content: [{ type: "text", text: message }], details: { id, authored: false } };
|
|
211
|
+
}
|
|
212
|
+
function rejectExec(message, id) {
|
|
213
|
+
return { content: [{ type: "text", text: message }], details: { id, authored: false, confirmed: false } };
|
|
214
|
+
}
|
|
215
|
+
/** Both authoring tool definitions, for registration on the top-level agent. */
|
|
216
|
+
export function createProposePluginToolDefinitions() {
|
|
217
|
+
return [createProposePluginToolDefinition(), createProposeExecutablePluginToolDefinition()];
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=propose-plugin.js.map
|
|
@@ -0,0 +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). The two escalating-risk build paths are\n * kept as *separate tools* by design — not collapsed into one:\n *\n * ProposePlugin scaffold passive/low-risk capabilities — skills,\n * commands, and read-only subagents. Autonomous +\n * transparent (near-zero risk, reversible).\n * ProposeExecutablePlugin the risk-bearing path — hooks, MCP servers, and\n * mutating/high-privilege subagents. Draft → display\n * the code and the tool grant → human confirms →\n * activate. Bar >= install.\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\tDEFAULT_AUTHORING_PLATFORMS,\n\tpluginExists,\n\twritePluginDraft,\n} from \"../extensions/plugins/authoring.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"../extensions/plugins/formats/types.js\";\nimport type { ExtensionContext } from \"../extensions/types.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\nimport { PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME, PROPOSE_PLUGIN_TOOL_NAME } from \"./plugin-tool-names.js\";\n\nexport { PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME, PROPOSE_PLUGIN_TOOL_NAME } from \"./plugin-tool-names.js\";\n\nconst platformSchema = Type.Union([Type.Literal(\"claude\"), Type.Literal(\"github\"), Type.Literal(\"agents\")], {\n\tdescription: \"Target format: claude (Claude Code), github (GitHub Copilot), or agents (native).\",\n});\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({ description: \"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Omit for none.\" }),\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\nfunction resolvePlatforms(input: string[] | undefined): MarketplacePlatform[] {\n\treturn (input && input.length > 0 ? (input as MarketplacePlatform[]) : DEFAULT_AUTHORING_PLATFORMS).slice();\n}\n\nfunction summarizeWrite(id: string, platforms: MarketplacePlatform[], files: string[], dest: string): string {\n\treturn (\n\t\t`Authored plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nActive after the next reload; remove it with UninstallPlugin.`\n\t);\n}\n\n// ── ProposePlugin (scaffold path) ─────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Plugin id (directory + manifest name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\t\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\t\tplatforms: Type.Optional(\n\t\t\tType.Array(platformSchema, { description: \"Formats to scaffold into. Default: claude + github.\" }),\n\t\t),\n\t\tskills: Type.Optional(Type.Array(skillSchema)),\n\t\tcommands: Type.Optional(Type.Array(commandSchema)),\n\t\tsubagents: Type.Optional(\n\t\t\tType.Array(subagentSchema, { description: \"Read-only subagents only (mutating grants are rejected here).\" }),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport interface ProposePluginDetails {\n\tid: string;\n\tauthored: boolean;\n}\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, ProposePluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a new plugin from passive, low-risk capabilities — skills, slash commands, and READ-ONLY subagents — when no marketplace plugin fits a gap. Scaffolds a proper plugin (Claude Code + GitHub Copilot layouts by default). Autonomous and reversible. For hooks, MCP servers, or mutating/high-privilege subagents, use ProposeExecutablePlugin instead.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a skill/command/read-only-subagent plugin to fill a capability gap (scaffold; reversible).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use ProposePlugin only for passive capabilities: skills, commands, and subagents whose tools are read-only (read, grep, glob, webfetch).\",\n\t\t\t\"A subagent that needs Bash/Write/Edit/MCP or tools:* is mutating — author it with ProposeExecutablePlugin (human confirmation), not 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: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst platforms = resolvePlatforms(params.platforms);\n\n\t\t\t// Guardrail + risk gate on each subagent before writing anything.\n\t\t\tfor (const sa of params.subagents ?? []) {\n\t\t\t\tconst cls = classifyAllowlist(sa.tools);\n\t\t\t\tif (cls.pluginTools.length > 0) {\n\t\t\t\t\treturn reject(\n\t\t\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\",\n\t\t\t\t\t\tparams.id,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (cls.risk === \"mutating\") {\n\t\t\t\t\treturn reject(\n\t\t\t\t\t\t`Subagent \"${sa.name}\" has a mutating allowlist (${cls.reason}). ` +\n\t\t\t\t\t\t\t\"Use ProposeExecutablePlugin so the human can review and confirm the tool grant.\",\n\t\t\t\t\t\tparams.id,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (pluginExists(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\t`A plugin named \"${params.id}\" already exists. Uninstall it first or pick another id.`,\n\t\t\t\t\tparams.id,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst draft: PluginDraft = {\n\t\t\t\tid: params.id,\n\t\t\t\tversion: params.version,\n\t\t\t\tdescription: params.description,\n\t\t\t\tsupportPlatform: platforms,\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tagents: params.subagents,\n\t\t\t};\n\t\t\tconst result = writePluginDraft(ctx.cwd, draft, platforms);\n\t\t\tconst text = summarizeWrite(params.id, platforms, result.files, result.dest);\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn { content: [{ type: \"text\" as const, text }], details: { id: params.id, authored: true } };\n\t\t},\n\t});\n}\n\n// ── ProposeExecutablePlugin (risk-bearing path) ───────────────────────────────\n\nconst proposeExecParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Plugin id (directory + manifest name).\" }),\n\t\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\t\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\t\tplatforms: Type.Optional(\n\t\t\tType.Array(platformSchema, { description: \"Formats to scaffold into. Default: claude + github.\" }),\n\t\t),\n\t\thooks: Type.Optional(Type.Array(hookSchema)),\n\t\tmcpServers: Type.Optional(Type.Array(mcpServerSchema)),\n\t\tsubagents: Type.Optional(\n\t\t\tType.Array(subagentSchema, { description: \"Subagents with mutating/exec/network or tools:* allowlists.\" }),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport interface ProposeExecutablePluginDetails {\n\tid: string;\n\tauthored: boolean;\n\tconfirmed: boolean;\n}\n\n/** Build the human-facing review text: the executable code and every tool grant. */\nfunction buildReview(params: Static<typeof proposeExecParams>): string {\n\tconst lines: string[] = [`Plugin \"${params.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 params.subagents ?? []) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"}`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function createProposeExecutablePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeExecParams, ProposeExecutablePluginDetails>({\n\t\tname: PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_EXECUTABLE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a plugin that includes EXECUTABLE or high-privilege capabilities — hooks (run on tool events), MCP servers, or subagents with mutating/exec/network tool grants. The code and the exact tool grant are shown to the human, who must confirm before anything is activated. Use ProposePlugin for passive skills/commands/read-only subagents.\",\n\t\tpromptSnippet: \"Author a hook/MCP/high-privilege-subagent plugin (shows the code; requires human confirmation).\",\n\t\tpromptGuidelines: [\n\t\t\t\"ProposeExecutablePlugin always shows the code and tool grant and requires explicit human confirmation before activating.\",\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: proposeExecParams,\n\t\tasync execute(_id, params: Static<typeof proposeExecParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst platforms = resolvePlatforms(params.platforms);\n\n\t\t\t// Guardrail: authored subagents may never carry plugin-system tools.\n\t\t\tfor (const sa of params.subagents ?? []) {\n\t\t\t\tconst cls = classifyAllowlist(sa.tools);\n\t\t\t\tif (cls.pluginTools.length > 0) {\n\t\t\t\t\treturn rejectExec(\n\t\t\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\",\n\t\t\t\t\t\tparams.id,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst hasExecutable =\n\t\t\t\t(params.hooks?.length ?? 0) > 0 ||\n\t\t\t\t(params.mcpServers?.length ?? 0) > 0 ||\n\t\t\t\t(params.subagents?.length ?? 0) > 0;\n\t\t\tif (!hasExecutable) {\n\t\t\t\treturn rejectExec(\"Nothing to author. Provide hooks, mcpServers, or subagents.\", params.id);\n\t\t\t}\n\n\t\t\tif (pluginExists(ctx.cwd, params.id)) {\n\t\t\t\treturn rejectExec(\n\t\t\t\t\t`A plugin named \"${params.id}\" already exists. Uninstall it first or pick another id.`,\n\t\t\t\t\tparams.id,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Draft → display → confirm → activate. Fail closed without a UI to confirm on.\n\t\t\tconst review = buildReview(params);\n\t\t\tctx.ui.notify(review, \"warning\");\n\t\t\tif (!ctx.hasUI) {\n\t\t\t\treturn rejectExec(\n\t\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t\t\tparams.id,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t`Author executable plugin \"${params.id}\"?`,\n\t\t\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t\t\t);\n\t\t\tif (!confirmed) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${params.id}\" was not authored.` }],\n\t\t\t\t\tdetails: { id: params.id, authored: false, confirmed: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst draft: PluginDraft = {\n\t\t\t\tid: params.id,\n\t\t\t\tversion: params.version,\n\t\t\t\tdescription: params.description,\n\t\t\t\tsupportPlatform: platforms,\n\t\t\t\thooks: params.hooks,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\tagents: params.subagents,\n\t\t\t};\n\t\t\tconst result = writePluginDraft(ctx.cwd, draft, platforms);\n\t\t\tconst text = summarizeWrite(params.id, platforms, result.files, result.dest);\n\t\t\tctx.ui.notify(`Authored executable 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: true },\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction reject(\n\tmessage: string,\n\tid: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: ProposePluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\nfunction rejectExec(\n\tmessage: string,\n\tid: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: ProposeExecutablePluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false, confirmed: false } };\n}\n\n/** Both authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [createProposePluginToolDefinition(), createProposeExecutablePluginToolDefinition()];\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warm-subagent-pool.d.ts","sourceRoot":"","sources":["../../src/core/warm-subagent-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Warm subagent worker pool (experimental, opt-in via `--warm-subagents` /\n * settings.warmSubagents; default off).\n *\n * The cold path (SubagentPool) re-execs the whole CLI for every dispatch, paying\n * a full module-load + resource-graph boot each time. A warm worker instead keeps\n * a long-lived child running in RPC mode and hands it one task at a time over the\n * existing JSON-line protocol: `new_session` resets its conversation between\n * tasks, `prompt` runs the task, and `agent_end` on the event stream signals\n * completion, after which the answer + usage are pulled inline (no result.json\n * disk round-trip, no OutputVerifier). The first task per worker still pays the\n * boot; every reuse after that skips it.\n *\n * Scope/limits (deliberately conservative — see the dispatch integration):\n * - Workers are pinned per agent type: RPC has no per-prompt system-prompt/tools\n * swap, so each (agentType, model, provider) is its own worker config.\n * - Only non-resume, non-fork dispatches are eligible; resume/fork need a\n * persisted/forked session the warm path does not own.\n * - Any worker/infra failure falls back to the cold pool, so enabling this can\n * only change latency, never whether a task can run.\n */\n\nimport type { AgentEvent } from \"@kolisachint/hoocode-agent-core\";\nimport { getSubagentSpawnCommand } from \"../config.js\";\nimport { RpcClient } from \"../modes/rpc/rpc-client.js\";\nimport { MODEL_INHERIT } from \"./agent-frontmatter.js\";\nimport { type AgentRegistry, loadAgentRegistry } from \"./agent-registry.js\";\nimport { resolveModelReference } from \"./model-categories.js\";\nimport type { Settings } from \"./settings-manager.js\";\nimport {\n\tcurrentSubagentDepth,\n\tresolveMaxSubagentDepth,\n\tSUBAGENT_DEPTH_ENV,\n\tSUBAGENT_SKIP_MCP_ENV,\n\ttoolAllowlistNeedsMcp,\n} from \"./subagent-depth.js\";\nimport { DEFAULT_SUBAGENT_MAX_TURNS } from \"./subagent-pool.js\";\n\n/** Usage totals pulled from a worker after a task, shaped like SubagentResultFile.usage. */\nexport interface WarmUsage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n}\n\n/** Outcome of running one task on a warm worker. */\nexport interface WarmRunResult {\n\tok: boolean;\n\tstatus: \"complete\" | \"failed\";\n\t/** The subagent's final assistant text (its answer to the caller). */\n\tsummary: string;\n\tusage?: WarmUsage;\n\terror?: string;\n}\n\n/** Per-dispatch inputs that select and configure a worker. */\nexport interface WarmDispatchOptions {\n\tagentType: string;\n\tcwd: string;\n\t/** Model id or category (fast/standard/capable); resolved to a concrete id. */\n\tmodel?: string;\n\tprovider?: string;\n}\n\n/** Reports the tool a warm worker is currently running (\"\" = idle between tools). */\nexport type WarmProgressCallback = (activity: string) => void;\n\n/** A run failed at the infrastructure level (worker crash, timeout, protocol error). */\nexport class WarmWorkerError extends Error {}\n\n/** Default per-task wait before declaring a warm run stalled and failing over to cold. */\nconst WARM_RUN_TIMEOUT_MS = 180_000;\n\n/**\n * One long-lived RPC child pinned to a single agent-type configuration. Reused\n * across tasks via reset(); a crash makes it not-alive so the pool discards it.\n */\nexport class WarmSubagentWorker {\n\tprivate readonly client: RpcClient;\n\tprivate alive = true;\n\n\tconstructor(\n\t\treadonly key: string,\n\t\toptions: WarmDispatchOptions,\n\t\tprivate readonly env: NodeJS.ProcessEnv,\n\t\tregistry: AgentRegistry,\n\t\tskillPaths: readonly string[],\n\t\tsettings: Settings | undefined,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tspawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {\n\t\tconst { executable, prefixArgs } = spawnCommand ?? getSubagentSpawnCommand();\n\t\tthis.client = new RpcClient({\n\t\t\texecutable,\n\t\t\tprefixArgs,\n\t\t\tcwd: options.cwd,\n\t\t\tenv: this.env as Record<string, string>,\n\t\t\targs: buildWorkerArgs(options, registry, skillPaths, settings),\n\t\t});\n\t}\n\n\t/** Boot the child. Throws (as WarmWorkerError) if it fails to come up. */\n\tasync start(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.start();\n\t\t} catch (error) {\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tisAlive(): boolean {\n\t\treturn this.alive;\n\t}\n\n\t/**\n\t * Run one task to completion and return its answer + usage. Throws\n\t * WarmWorkerError on an infra failure (crash/timeout/protocol) so the caller\n\t * can fall back to the cold pool; a task that ran but reported failure returns\n\t * `{ ok: false }` instead (no fall back — the work was actually done).\n\t */\n\tasync run(\n\t\tprompt: string,\n\t\tonActivity?: WarmProgressCallback,\n\t\ttimeoutMs = WARM_RUN_TIMEOUT_MS,\n\t): Promise<WarmRunResult> {\n\t\tif (!this.alive) throw new WarmWorkerError(\"worker is not alive\");\n\t\t// Mirror the cold pool's coarse progress: report the tool the child is\n\t\t// currently running so a warm dispatch's task row reads \"⋯ grep\" rather than a\n\t\t// static \"running…\". Cleared between tools and at turn end.\n\t\tconst detachProgress = onActivity ? this.tapProgress(onActivity) : undefined;\n\t\ttry {\n\t\t\tconst events = await this.client.promptAndWait(prompt, undefined, timeoutMs);\n\t\t\tconst failure = firstTurnError(events);\n\t\t\tconst summary = (await this.client.getLastAssistantText()) ?? \"\";\n\t\t\tconst usage = await this.readUsage();\n\t\t\tif (failure) {\n\t\t\t\treturn { ok: false, status: \"failed\", summary, usage, error: failure };\n\t\t\t}\n\t\t\treturn { ok: true, status: \"complete\", summary, usage };\n\t\t} catch (error) {\n\t\t\t// A prompt/await failure means the child is no longer trustworthy: mark it\n\t\t\t// dead so the pool discards it, and signal infra failure for fallback.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t} finally {\n\t\t\tdetachProgress?.();\n\t\t\tonActivity?.(\"\");\n\t\t}\n\t}\n\n\t/** Forward the child's coarse tool-lifecycle events to an activity callback. */\n\tprivate tapProgress(onActivity: WarmProgressCallback): () => void {\n\t\treturn this.client.onEvent((event) => {\n\t\t\tconst e = event as { type?: string; toolName?: string };\n\t\t\tif (e.type === \"tool_execution_start\") onActivity(typeof e.toolName === \"string\" ? e.toolName : \"\");\n\t\t\telse if (e.type === \"tool_execution_end\" || e.type === \"turn_end\") onActivity(\"\");\n\t\t});\n\t}\n\n\t/** Reset the worker's conversation so it can take the next task cleanly. */\n\tasync reset(): Promise<void> {\n\t\tif (!this.alive) return;\n\t\ttry {\n\t\t\tawait this.client.newSession();\n\t\t} catch (error) {\n\t\t\t// A reset failure leaves the worker in an unknown state — retire it.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tthis.alive = false;\n\t\tawait this.client.stop().catch(() => {});\n\t}\n\n\tprivate async readUsage(): Promise<WarmUsage | undefined> {\n\t\ttry {\n\t\t\tconst stats = await this.client.getSessionStats();\n\t\t\treturn {\n\t\t\t\tinput: stats.tokens.input,\n\t\t\t\toutput: stats.tokens.output,\n\t\t\t\tcacheRead: stats.tokens.cacheRead,\n\t\t\t\tcacheWrite: stats.tokens.cacheWrite,\n\t\t\t\tcost: stats.cost,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\n/**\n * Pool of warm workers keyed by agent-type configuration. Hands out an idle\n * worker (or boots a new one up to a per-key cap), and on release resets the\n * worker and returns it to the idle set with an idle-TTL reclaim timer.\n */\nexport class WarmSubagentPool {\n\tprivate idle = new Map<string, WarmSubagentWorker[]>();\n\tprivate reclaimTimers = new Map<WarmSubagentWorker, ReturnType<typeof setTimeout>>();\n\tprivate liveCount = new Map<string, number>();\n\tprivate disposed = false;\n\tprivate registry?: AgentRegistry;\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly settings: Settings | undefined,\n\t\tprivate skillPaths: string[] = [],\n\t\tprivate readonly maxPerKey = 2,\n\t\tprivate readonly idleTtlMs = 30_000,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tprivate readonly spawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {}\n\n\tupdateSkillPaths(paths: string[]): void {\n\t\tthis.skillPaths = [...paths];\n\t}\n\n\tprivate getRegistry(): AgentRegistry {\n\t\tif (!this.registry) this.registry = loadAgentRegistry({ cwd: this.cwd });\n\t\treturn this.registry;\n\t}\n\n\t/** A registry agent is poolable when it is not a fork agent (fork needs a forked session). */\n\tisPoolable(agentType: string): boolean {\n\t\tconst def = this.getRegistry().get(agentType);\n\t\treturn def !== undefined && def.fork !== true;\n\t}\n\n\t/** Stable key for one worker configuration. */\n\tprivate keyFor(options: WarmDispatchOptions): string {\n\t\tconst resolved = options.model ? resolveModelReference(options.model, this.settings) : undefined;\n\t\treturn `${options.agentType}::${resolved ?? \"default\"}::${options.provider ?? \"default\"}`;\n\t}\n\n\t/** Environment for a warm child: depth stamp + MCP skip, mirroring SubagentPool.childSpawnEnv. */\n\tprivate childEnv(agentType: string): NodeJS.ProcessEnv {\n\t\tconst env: NodeJS.ProcessEnv = {\n\t\t\t...process.env,\n\t\t\t[SUBAGENT_DEPTH_ENV]: String(currentSubagentDepth(process.env) + 1),\n\t\t};\n\t\tconst def = this.getRegistry().get(agentType);\n\t\tif (!toolAllowlistNeedsMcp(def?.tools)) env[SUBAGENT_SKIP_MCP_ENV] = \"1\";\n\t\treturn env;\n\t}\n\n\t/**\n\t * Run a task on a warm worker end to end: acquire (reuse or boot), run, then\n\t * release back to the pool. Throws WarmWorkerError on infra failure so the\n\t * caller can fall back to the cold pool.\n\t */\n\tasync dispatch(\n\t\tprompt: string,\n\t\toptions: WarmDispatchOptions,\n\t\tonActivity?: WarmProgressCallback,\n\t): Promise<WarmRunResult> {\n\t\tif (this.disposed) throw new WarmWorkerError(\"warm pool disposed\");\n\t\tconst worker = await this.acquire(options);\n\t\ttry {\n\t\t\tconst result = await worker.run(prompt, onActivity);\n\t\t\tawait this.release(worker);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Infra failure: the worker already marked itself dead; drop it entirely.\n\t\t\tawait this.discard(worker);\n\t\t\tthrow error instanceof WarmWorkerError ? error : new WarmWorkerError(String(error));\n\t\t}\n\t}\n\n\tprivate async acquire(options: WarmDispatchOptions): Promise<WarmSubagentWorker> {\n\t\tconst key = this.keyFor(options);\n\t\tconst pool = this.idle.get(key);\n\t\twhile (pool && pool.length > 0) {\n\t\t\tconst worker = pool.pop()!;\n\t\t\tthis.clearReclaim(worker);\n\t\t\tif (worker.isAlive()) return worker;\n\t\t\t// Dead idle worker (child exited while parked): drop and try the next.\n\t\t\tthis.decLive(key);\n\t\t\tawait worker.dispose();\n\t\t}\n\n\t\tconst worker = new WarmSubagentWorker(\n\t\t\tkey,\n\t\t\toptions,\n\t\t\tthis.childEnv(options.agentType),\n\t\t\tthis.getRegistry(),\n\t\t\tthis.skillPaths,\n\t\t\tthis.settings,\n\t\t\tthis.spawnCommand,\n\t\t);\n\t\tthis.incLive(key);\n\t\ttry {\n\t\t\tawait worker.start();\n\t\t} catch (error) {\n\t\t\tthis.decLive(key);\n\t\t\tthrow error;\n\t\t}\n\t\treturn worker;\n\t}\n\n\tprivate async release(worker: WarmSubagentWorker): Promise<void> {\n\t\tif (this.disposed || !worker.isAlive()) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait worker.reset();\n\t\t} catch {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tconst key = worker.key;\n\t\tconst pool = this.idle.get(key) ?? [];\n\t\tif (pool.length >= this.maxPerKey) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tpool.push(worker);\n\t\tthis.idle.set(key, pool);\n\t\tthis.armReclaim(worker);\n\t}\n\n\tprivate async discard(worker: WarmSubagentWorker): Promise<void> {\n\t\tthis.clearReclaim(worker);\n\t\tthis.decLive(worker.key);\n\t\tawait worker.dispose();\n\t}\n\n\tprivate armReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = setTimeout(() => {\n\t\t\tconst pool = this.idle.get(worker.key);\n\t\t\tif (pool) {\n\t\t\t\tconst idx = pool.indexOf(worker);\n\t\t\t\tif (idx !== -1) pool.splice(idx, 1);\n\t\t\t}\n\t\t\tvoid this.discard(worker);\n\t\t}, this.idleTtlMs);\n\t\ttimer.unref?.();\n\t\tthis.reclaimTimers.set(worker, timer);\n\t}\n\n\tprivate clearReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = this.reclaimTimers.get(worker);\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\tthis.reclaimTimers.delete(worker);\n\t\t}\n\t}\n\n\tprivate incLive(key: string): void {\n\t\tthis.liveCount.set(key, (this.liveCount.get(key) ?? 0) + 1);\n\t}\n\n\tprivate decLive(key: string): void {\n\t\tconst n = (this.liveCount.get(key) ?? 1) - 1;\n\t\tif (n <= 0) this.liveCount.delete(key);\n\t\telse this.liveCount.set(key, n);\n\t}\n\n\t/** Number of currently idle (parked) workers — exposed for tests/diagnostics. */\n\tidleCount(): number {\n\t\tlet total = 0;\n\t\tfor (const pool of this.idle.values()) total += pool.length;\n\t\treturn total;\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tconst all: WarmSubagentWorker[] = [];\n\t\tfor (const pool of this.idle.values()) all.push(...pool);\n\t\tthis.idle.clear();\n\t\tfor (const timer of this.reclaimTimers.values()) clearTimeout(timer);\n\t\tthis.reclaimTimers.clear();\n\t\tthis.liveCount.clear();\n\t\tawait Promise.all(all.map((w) => w.dispose()));\n\t}\n}\n\n/**\n * Build the RPC child's CLI args for a warm worker. Mirrors the agent-config\n * portion of SubagentPool.buildArgs (system prompt, tools, model/provider, turn\n * cap, skills) but omits the one-shot `--mode json` / `--session` / `--task-id`\n * bits, since the worker runs persistently in RPC mode and resets via new_session.\n */\nfunction buildWorkerArgs(\n\toptions: WarmDispatchOptions,\n\tregistry: AgentRegistry,\n\tskillPaths: readonly string[],\n\tsettings: Settings | undefined,\n): string[] {\n\tconst args: string[] = [];\n\tconst def = registry.get(options.agentType);\n\n\tif (def?.prompt) args.push(\"--system-prompt\", def.prompt);\n\n\tconst childDepth = currentSubagentDepth(process.env) + 1;\n\tconst canChildDelegate = def?.delegate === true && childDepth < resolveMaxSubagentDepth(undefined, process.env);\n\n\tconst tools = def?.tools ? [...def.tools] : undefined;\n\tif (canChildDelegate && tools) {\n\t\tfor (const t of [\"Task\", \"TaskOutput\"]) if (!tools.includes(t)) tools.push(t);\n\t}\n\tif (tools && tools.length > 0) args.push(\"--tools\", tools.join(\",\"));\n\tif (def?.disallowedTools && def.disallowedTools.length > 0) {\n\t\targs.push(\"--disallowed-tools\", def.disallowedTools.join(\",\"));\n\t}\n\tif (canChildDelegate) {\n\t\targs.push(\"--enable-subagents\");\n\t\tif (def?.delegateTo && def.delegateTo.length > 0) args.push(\"--delegate-allow\", def.delegateTo.join(\",\"));\n\t}\n\n\t// Model precedence matches the cold path: a pinned (non-inherit) agent model\n\t// wins, else the requested model/category, resolved to a concrete id.\n\tconst explicitModel = def?.model && def.model !== MODEL_INHERIT ? def.model : undefined;\n\tconst rawModel = explicitModel ?? options.model;\n\tconst modelToUse = rawModel ? resolveModelReference(rawModel, settings) : undefined;\n\tif (modelToUse) args.push(\"--model\", modelToUse);\n\tif (options.provider) args.push(\"--provider\", options.provider);\n\n\tconst maxTurns = def?.maxTurns && def.maxTurns > 0 ? def.maxTurns : DEFAULT_SUBAGENT_MAX_TURNS;\n\targs.push(\"--max-turns\", String(maxTurns));\n\n\tfor (const skillPath of skillPaths) args.push(\"--skill\", skillPath);\n\n\treturn args;\n}\n\n/**\n * Scan a completed run's events for a turn that ended in error/abort, returning\n * its message (or a generic marker) so the caller can report a task failure\n * without re-reading the transcript. Returns undefined for a clean run.\n */\nfunction firstTurnError(events: readonly AgentEvent[]): string | undefined {\n\tfor (const event of events) {\n\t\tconst e = event as { type?: string; message?: { stopReason?: string; errorMessage?: string } };\n\t\tif (e.type === \"turn_end\" && (e.message?.stopReason === \"error\" || e.message?.stopReason === \"aborted\")) {\n\t\t\treturn e.message.errorMessage || `turn ${e.message.stopReason}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"warm-subagent-pool.d.ts","sourceRoot":"","sources":["../../src/core/warm-subagent-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Warm subagent worker pool (experimental, opt-in via `--warm-subagents` /\n * settings.warmSubagents; default off).\n *\n * The cold path (SubagentPool) re-execs the whole CLI for every dispatch, paying\n * a full module-load + resource-graph boot each time. A warm worker instead keeps\n * a long-lived child running in RPC mode and hands it one task at a time over the\n * existing JSON-line protocol: `new_session` resets its conversation between\n * tasks, `prompt` runs the task, and `agent_end` on the event stream signals\n * completion, after which the answer + usage are pulled inline (no result.json\n * disk round-trip, no OutputVerifier). The first task per worker still pays the\n * boot; every reuse after that skips it.\n *\n * Scope/limits (deliberately conservative — see the dispatch integration):\n * - Workers are pinned per agent type: RPC has no per-prompt system-prompt/tools\n * swap, so each (agentType, model, provider) is its own worker config.\n * - Only non-resume, non-fork dispatches are eligible; resume/fork need a\n * persisted/forked session the warm path does not own.\n * - Any worker/infra failure falls back to the cold pool, so enabling this can\n * only change latency, never whether a task can run.\n */\n\nimport type { AgentEvent } from \"@kolisachint/hoocode-agent-core\";\nimport { getSubagentSpawnCommand } from \"../config.js\";\nimport { RpcClient } from \"../modes/rpc/rpc-client.js\";\nimport { MODEL_INHERIT } from \"./agent-frontmatter.js\";\nimport { type AgentRegistry, loadAgentRegistry } from \"./agent-registry.js\";\nimport { resolveModelReference } from \"./model-categories.js\";\nimport type { Settings } from \"./settings-manager.js\";\nimport {\n\tcurrentSubagentDepth,\n\tDEFER_MCP_SCHEMAS_ENV,\n\tresolveMaxSubagentDepth,\n\tSUBAGENT_DEPTH_ENV,\n\tSUBAGENT_SKIP_MCP_ENV,\n\ttoolAllowlistNeedsMcp,\n} from \"./subagent-depth.js\";\nimport { DEFAULT_SUBAGENT_MAX_TURNS } from \"./subagent-pool.js\";\n\n/** Usage totals pulled from a worker after a task, shaped like SubagentResultFile.usage. */\nexport interface WarmUsage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n}\n\n/** Outcome of running one task on a warm worker. */\nexport interface WarmRunResult {\n\tok: boolean;\n\tstatus: \"complete\" | \"failed\";\n\t/** The subagent's final assistant text (its answer to the caller). */\n\tsummary: string;\n\tusage?: WarmUsage;\n\terror?: string;\n}\n\n/** Per-dispatch inputs that select and configure a worker. */\nexport interface WarmDispatchOptions {\n\tagentType: string;\n\tcwd: string;\n\t/** Model id or category (fast/standard/capable); resolved to a concrete id. */\n\tmodel?: string;\n\tprovider?: string;\n}\n\n/** Reports the tool a warm worker is currently running (\"\" = idle between tools). */\nexport type WarmProgressCallback = (activity: string) => void;\n\n/** A run failed at the infrastructure level (worker crash, timeout, protocol error). */\nexport class WarmWorkerError extends Error {}\n\n/** Default per-task wait before declaring a warm run stalled and failing over to cold. */\nconst WARM_RUN_TIMEOUT_MS = 180_000;\n\n/**\n * One long-lived RPC child pinned to a single agent-type configuration. Reused\n * across tasks via reset(); a crash makes it not-alive so the pool discards it.\n */\nexport class WarmSubagentWorker {\n\tprivate readonly client: RpcClient;\n\tprivate alive = true;\n\n\tconstructor(\n\t\treadonly key: string,\n\t\toptions: WarmDispatchOptions,\n\t\tprivate readonly env: NodeJS.ProcessEnv,\n\t\tregistry: AgentRegistry,\n\t\tskillPaths: readonly string[],\n\t\tsettings: Settings | undefined,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tspawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {\n\t\tconst { executable, prefixArgs } = spawnCommand ?? getSubagentSpawnCommand();\n\t\tthis.client = new RpcClient({\n\t\t\texecutable,\n\t\t\tprefixArgs,\n\t\t\tcwd: options.cwd,\n\t\t\tenv: this.env as Record<string, string>,\n\t\t\targs: buildWorkerArgs(options, registry, skillPaths, settings),\n\t\t});\n\t}\n\n\t/** Boot the child. Throws (as WarmWorkerError) if it fails to come up. */\n\tasync start(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.start();\n\t\t} catch (error) {\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tisAlive(): boolean {\n\t\treturn this.alive;\n\t}\n\n\t/**\n\t * Run one task to completion and return its answer + usage. Throws\n\t * WarmWorkerError on an infra failure (crash/timeout/protocol) so the caller\n\t * can fall back to the cold pool; a task that ran but reported failure returns\n\t * `{ ok: false }` instead (no fall back — the work was actually done).\n\t */\n\tasync run(\n\t\tprompt: string,\n\t\tonActivity?: WarmProgressCallback,\n\t\ttimeoutMs = WARM_RUN_TIMEOUT_MS,\n\t): Promise<WarmRunResult> {\n\t\tif (!this.alive) throw new WarmWorkerError(\"worker is not alive\");\n\t\t// Mirror the cold pool's coarse progress: report the tool the child is\n\t\t// currently running so a warm dispatch's task row reads \"⋯ grep\" rather than a\n\t\t// static \"running…\". Cleared between tools and at turn end.\n\t\tconst detachProgress = onActivity ? this.tapProgress(onActivity) : undefined;\n\t\ttry {\n\t\t\tconst events = await this.client.promptAndWait(prompt, undefined, timeoutMs);\n\t\t\tconst failure = firstTurnError(events);\n\t\t\tconst summary = (await this.client.getLastAssistantText()) ?? \"\";\n\t\t\tconst usage = await this.readUsage();\n\t\t\tif (failure) {\n\t\t\t\treturn { ok: false, status: \"failed\", summary, usage, error: failure };\n\t\t\t}\n\t\t\treturn { ok: true, status: \"complete\", summary, usage };\n\t\t} catch (error) {\n\t\t\t// A prompt/await failure means the child is no longer trustworthy: mark it\n\t\t\t// dead so the pool discards it, and signal infra failure for fallback.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t} finally {\n\t\t\tdetachProgress?.();\n\t\t\tonActivity?.(\"\");\n\t\t}\n\t}\n\n\t/** Forward the child's coarse tool-lifecycle events to an activity callback. */\n\tprivate tapProgress(onActivity: WarmProgressCallback): () => void {\n\t\treturn this.client.onEvent((event) => {\n\t\t\tconst e = event as { type?: string; toolName?: string };\n\t\t\tif (e.type === \"tool_execution_start\") onActivity(typeof e.toolName === \"string\" ? e.toolName : \"\");\n\t\t\telse if (e.type === \"tool_execution_end\" || e.type === \"turn_end\") onActivity(\"\");\n\t\t});\n\t}\n\n\t/** Reset the worker's conversation so it can take the next task cleanly. */\n\tasync reset(): Promise<void> {\n\t\tif (!this.alive) return;\n\t\ttry {\n\t\t\tawait this.client.newSession();\n\t\t} catch (error) {\n\t\t\t// A reset failure leaves the worker in an unknown state — retire it.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tthis.alive = false;\n\t\tawait this.client.stop().catch(() => {});\n\t}\n\n\tprivate async readUsage(): Promise<WarmUsage | undefined> {\n\t\ttry {\n\t\t\tconst stats = await this.client.getSessionStats();\n\t\t\treturn {\n\t\t\t\tinput: stats.tokens.input,\n\t\t\t\toutput: stats.tokens.output,\n\t\t\t\tcacheRead: stats.tokens.cacheRead,\n\t\t\t\tcacheWrite: stats.tokens.cacheWrite,\n\t\t\t\tcost: stats.cost,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\n/**\n * Pool of warm workers keyed by agent-type configuration. Hands out an idle\n * worker (or boots a new one up to a per-key cap), and on release resets the\n * worker and returns it to the idle set with an idle-TTL reclaim timer.\n */\nexport class WarmSubagentPool {\n\tprivate idle = new Map<string, WarmSubagentWorker[]>();\n\tprivate reclaimTimers = new Map<WarmSubagentWorker, ReturnType<typeof setTimeout>>();\n\tprivate liveCount = new Map<string, number>();\n\tprivate disposed = false;\n\tprivate registry?: AgentRegistry;\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly settings: Settings | undefined,\n\t\tprivate skillPaths: string[] = [],\n\t\tprivate readonly maxPerKey = 2,\n\t\tprivate readonly idleTtlMs = 30_000,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tprivate readonly spawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {}\n\n\tupdateSkillPaths(paths: string[]): void {\n\t\tthis.skillPaths = [...paths];\n\t}\n\n\tprivate getRegistry(): AgentRegistry {\n\t\tif (!this.registry) this.registry = loadAgentRegistry({ cwd: this.cwd });\n\t\treturn this.registry;\n\t}\n\n\t/** A registry agent is poolable when it is not a fork agent (fork needs a forked session). */\n\tisPoolable(agentType: string): boolean {\n\t\tconst def = this.getRegistry().get(agentType);\n\t\treturn def !== undefined && def.fork !== true;\n\t}\n\n\t/** Stable key for one worker configuration. */\n\tprivate keyFor(options: WarmDispatchOptions): string {\n\t\tconst resolved = options.model ? resolveModelReference(options.model, this.settings) : undefined;\n\t\treturn `${options.agentType}::${resolved ?? \"default\"}::${options.provider ?? \"default\"}`;\n\t}\n\n\t/** Environment for a warm child: depth stamp + MCP skip, mirroring SubagentPool.childSpawnEnv. */\n\tprivate childEnv(agentType: string): NodeJS.ProcessEnv {\n\t\tconst env: NodeJS.ProcessEnv = {\n\t\t\t...process.env,\n\t\t\t[SUBAGENT_DEPTH_ENV]: String(currentSubagentDepth(process.env) + 1),\n\t\t};\n\t\tconst def = this.getRegistry().get(agentType);\n\t\tif (!toolAllowlistNeedsMcp(def?.tools)) env[SUBAGENT_SKIP_MCP_ENV] = \"1\";\n\t\t// A child never defers MCP schemas: if it needs MCP it eager-registers its\n\t\t// allowlisted tools at dispatch so they are immediately callable (spec §2).\n\t\tdelete env[DEFER_MCP_SCHEMAS_ENV];\n\t\treturn env;\n\t}\n\n\t/**\n\t * Run a task on a warm worker end to end: acquire (reuse or boot), run, then\n\t * release back to the pool. Throws WarmWorkerError on infra failure so the\n\t * caller can fall back to the cold pool.\n\t */\n\tasync dispatch(\n\t\tprompt: string,\n\t\toptions: WarmDispatchOptions,\n\t\tonActivity?: WarmProgressCallback,\n\t): Promise<WarmRunResult> {\n\t\tif (this.disposed) throw new WarmWorkerError(\"warm pool disposed\");\n\t\tconst worker = await this.acquire(options);\n\t\ttry {\n\t\t\tconst result = await worker.run(prompt, onActivity);\n\t\t\tawait this.release(worker);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Infra failure: the worker already marked itself dead; drop it entirely.\n\t\t\tawait this.discard(worker);\n\t\t\tthrow error instanceof WarmWorkerError ? error : new WarmWorkerError(String(error));\n\t\t}\n\t}\n\n\tprivate async acquire(options: WarmDispatchOptions): Promise<WarmSubagentWorker> {\n\t\tconst key = this.keyFor(options);\n\t\tconst pool = this.idle.get(key);\n\t\twhile (pool && pool.length > 0) {\n\t\t\tconst worker = pool.pop()!;\n\t\t\tthis.clearReclaim(worker);\n\t\t\tif (worker.isAlive()) return worker;\n\t\t\t// Dead idle worker (child exited while parked): drop and try the next.\n\t\t\tthis.decLive(key);\n\t\t\tawait worker.dispose();\n\t\t}\n\n\t\tconst worker = new WarmSubagentWorker(\n\t\t\tkey,\n\t\t\toptions,\n\t\t\tthis.childEnv(options.agentType),\n\t\t\tthis.getRegistry(),\n\t\t\tthis.skillPaths,\n\t\t\tthis.settings,\n\t\t\tthis.spawnCommand,\n\t\t);\n\t\tthis.incLive(key);\n\t\ttry {\n\t\t\tawait worker.start();\n\t\t} catch (error) {\n\t\t\tthis.decLive(key);\n\t\t\tthrow error;\n\t\t}\n\t\treturn worker;\n\t}\n\n\tprivate async release(worker: WarmSubagentWorker): Promise<void> {\n\t\tif (this.disposed || !worker.isAlive()) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait worker.reset();\n\t\t} catch {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tconst key = worker.key;\n\t\tconst pool = this.idle.get(key) ?? [];\n\t\tif (pool.length >= this.maxPerKey) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tpool.push(worker);\n\t\tthis.idle.set(key, pool);\n\t\tthis.armReclaim(worker);\n\t}\n\n\tprivate async discard(worker: WarmSubagentWorker): Promise<void> {\n\t\tthis.clearReclaim(worker);\n\t\tthis.decLive(worker.key);\n\t\tawait worker.dispose();\n\t}\n\n\tprivate armReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = setTimeout(() => {\n\t\t\tconst pool = this.idle.get(worker.key);\n\t\t\tif (pool) {\n\t\t\t\tconst idx = pool.indexOf(worker);\n\t\t\t\tif (idx !== -1) pool.splice(idx, 1);\n\t\t\t}\n\t\t\tvoid this.discard(worker);\n\t\t}, this.idleTtlMs);\n\t\ttimer.unref?.();\n\t\tthis.reclaimTimers.set(worker, timer);\n\t}\n\n\tprivate clearReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = this.reclaimTimers.get(worker);\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\tthis.reclaimTimers.delete(worker);\n\t\t}\n\t}\n\n\tprivate incLive(key: string): void {\n\t\tthis.liveCount.set(key, (this.liveCount.get(key) ?? 0) + 1);\n\t}\n\n\tprivate decLive(key: string): void {\n\t\tconst n = (this.liveCount.get(key) ?? 1) - 1;\n\t\tif (n <= 0) this.liveCount.delete(key);\n\t\telse this.liveCount.set(key, n);\n\t}\n\n\t/** Number of currently idle (parked) workers — exposed for tests/diagnostics. */\n\tidleCount(): number {\n\t\tlet total = 0;\n\t\tfor (const pool of this.idle.values()) total += pool.length;\n\t\treturn total;\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tconst all: WarmSubagentWorker[] = [];\n\t\tfor (const pool of this.idle.values()) all.push(...pool);\n\t\tthis.idle.clear();\n\t\tfor (const timer of this.reclaimTimers.values()) clearTimeout(timer);\n\t\tthis.reclaimTimers.clear();\n\t\tthis.liveCount.clear();\n\t\tawait Promise.all(all.map((w) => w.dispose()));\n\t}\n}\n\n/**\n * Build the RPC child's CLI args for a warm worker. Mirrors the agent-config\n * portion of SubagentPool.buildArgs (system prompt, tools, model/provider, turn\n * cap, skills) but omits the one-shot `--mode json` / `--session` / `--task-id`\n * bits, since the worker runs persistently in RPC mode and resets via new_session.\n */\nfunction buildWorkerArgs(\n\toptions: WarmDispatchOptions,\n\tregistry: AgentRegistry,\n\tskillPaths: readonly string[],\n\tsettings: Settings | undefined,\n): string[] {\n\tconst args: string[] = [];\n\tconst def = registry.get(options.agentType);\n\n\tif (def?.prompt) args.push(\"--system-prompt\", def.prompt);\n\n\tconst childDepth = currentSubagentDepth(process.env) + 1;\n\tconst canChildDelegate = def?.delegate === true && childDepth < resolveMaxSubagentDepth(undefined, process.env);\n\n\tconst tools = def?.tools ? [...def.tools] : undefined;\n\tif (canChildDelegate && tools) {\n\t\tfor (const t of [\"Task\", \"TaskOutput\"]) if (!tools.includes(t)) tools.push(t);\n\t}\n\tif (tools && tools.length > 0) args.push(\"--tools\", tools.join(\",\"));\n\tif (def?.disallowedTools && def.disallowedTools.length > 0) {\n\t\targs.push(\"--disallowed-tools\", def.disallowedTools.join(\",\"));\n\t}\n\tif (canChildDelegate) {\n\t\targs.push(\"--enable-subagents\");\n\t\tif (def?.delegateTo && def.delegateTo.length > 0) args.push(\"--delegate-allow\", def.delegateTo.join(\",\"));\n\t}\n\n\t// Model precedence matches the cold path: a pinned (non-inherit) agent model\n\t// wins, else the requested model/category, resolved to a concrete id.\n\tconst explicitModel = def?.model && def.model !== MODEL_INHERIT ? def.model : undefined;\n\tconst rawModel = explicitModel ?? options.model;\n\tconst modelToUse = rawModel ? resolveModelReference(rawModel, settings) : undefined;\n\tif (modelToUse) args.push(\"--model\", modelToUse);\n\tif (options.provider) args.push(\"--provider\", options.provider);\n\n\tconst maxTurns = def?.maxTurns && def.maxTurns > 0 ? def.maxTurns : DEFAULT_SUBAGENT_MAX_TURNS;\n\targs.push(\"--max-turns\", String(maxTurns));\n\n\tfor (const skillPath of skillPaths) args.push(\"--skill\", skillPath);\n\n\treturn args;\n}\n\n/**\n * Scan a completed run's events for a turn that ended in error/abort, returning\n * its message (or a generic marker) so the caller can report a task failure\n * without re-reading the transcript. Returns undefined for a clean run.\n */\nfunction firstTurnError(events: readonly AgentEvent[]): string | undefined {\n\tfor (const event of events) {\n\t\tconst e = event as { type?: string; message?: { stopReason?: string; errorMessage?: string } };\n\t\tif (e.type === \"turn_end\" && (e.message?.stopReason === \"error\" || e.message?.stopReason === \"aborted\")) {\n\t\t\treturn e.message.errorMessage || `turn ${e.message.stopReason}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n"]}
|
|
@@ -24,7 +24,7 @@ import { RpcClient } from "../modes/rpc/rpc-client.js";
|
|
|
24
24
|
import { MODEL_INHERIT } from "./agent-frontmatter.js";
|
|
25
25
|
import { loadAgentRegistry } from "./agent-registry.js";
|
|
26
26
|
import { resolveModelReference } from "./model-categories.js";
|
|
27
|
-
import { currentSubagentDepth, resolveMaxSubagentDepth, SUBAGENT_DEPTH_ENV, SUBAGENT_SKIP_MCP_ENV, toolAllowlistNeedsMcp, } from "./subagent-depth.js";
|
|
27
|
+
import { currentSubagentDepth, DEFER_MCP_SCHEMAS_ENV, resolveMaxSubagentDepth, SUBAGENT_DEPTH_ENV, SUBAGENT_SKIP_MCP_ENV, toolAllowlistNeedsMcp, } from "./subagent-depth.js";
|
|
28
28
|
import { DEFAULT_SUBAGENT_MAX_TURNS } from "./subagent-pool.js";
|
|
29
29
|
/** A run failed at the infrastructure level (worker crash, timeout, protocol error). */
|
|
30
30
|
export class WarmWorkerError extends Error {
|
|
@@ -198,6 +198,9 @@ export class WarmSubagentPool {
|
|
|
198
198
|
const def = this.getRegistry().get(agentType);
|
|
199
199
|
if (!toolAllowlistNeedsMcp(def?.tools))
|
|
200
200
|
env[SUBAGENT_SKIP_MCP_ENV] = "1";
|
|
201
|
+
// A child never defers MCP schemas: if it needs MCP it eager-registers its
|
|
202
|
+
// allowlisted tools at dispatch so they are immediately callable (spec §2).
|
|
203
|
+
delete env[DEFER_MCP_SCHEMAS_ENV];
|
|
201
204
|
return env;
|
|
202
205
|
}
|
|
203
206
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warm-subagent-pool.js","sourceRoot":"","sources":["../../src/core/warm-subagent-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Warm subagent worker pool (experimental, opt-in via `--warm-subagents` /\n * settings.warmSubagents; default off).\n *\n * The cold path (SubagentPool) re-execs the whole CLI for every dispatch, paying\n * a full module-load + resource-graph boot each time. A warm worker instead keeps\n * a long-lived child running in RPC mode and hands it one task at a time over the\n * existing JSON-line protocol: `new_session` resets its conversation between\n * tasks, `prompt` runs the task, and `agent_end` on the event stream signals\n * completion, after which the answer + usage are pulled inline (no result.json\n * disk round-trip, no OutputVerifier). The first task per worker still pays the\n * boot; every reuse after that skips it.\n *\n * Scope/limits (deliberately conservative — see the dispatch integration):\n * - Workers are pinned per agent type: RPC has no per-prompt system-prompt/tools\n * swap, so each (agentType, model, provider) is its own worker config.\n * - Only non-resume, non-fork dispatches are eligible; resume/fork need a\n * persisted/forked session the warm path does not own.\n * - Any worker/infra failure falls back to the cold pool, so enabling this can\n * only change latency, never whether a task can run.\n */\n\nimport type { AgentEvent } from \"@kolisachint/hoocode-agent-core\";\nimport { getSubagentSpawnCommand } from \"../config.js\";\nimport { RpcClient } from \"../modes/rpc/rpc-client.js\";\nimport { MODEL_INHERIT } from \"./agent-frontmatter.js\";\nimport { type AgentRegistry, loadAgentRegistry } from \"./agent-registry.js\";\nimport { resolveModelReference } from \"./model-categories.js\";\nimport type { Settings } from \"./settings-manager.js\";\nimport {\n\tcurrentSubagentDepth,\n\tresolveMaxSubagentDepth,\n\tSUBAGENT_DEPTH_ENV,\n\tSUBAGENT_SKIP_MCP_ENV,\n\ttoolAllowlistNeedsMcp,\n} from \"./subagent-depth.js\";\nimport { DEFAULT_SUBAGENT_MAX_TURNS } from \"./subagent-pool.js\";\n\n/** Usage totals pulled from a worker after a task, shaped like SubagentResultFile.usage. */\nexport interface WarmUsage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n}\n\n/** Outcome of running one task on a warm worker. */\nexport interface WarmRunResult {\n\tok: boolean;\n\tstatus: \"complete\" | \"failed\";\n\t/** The subagent's final assistant text (its answer to the caller). */\n\tsummary: string;\n\tusage?: WarmUsage;\n\terror?: string;\n}\n\n/** Per-dispatch inputs that select and configure a worker. */\nexport interface WarmDispatchOptions {\n\tagentType: string;\n\tcwd: string;\n\t/** Model id or category (fast/standard/capable); resolved to a concrete id. */\n\tmodel?: string;\n\tprovider?: string;\n}\n\n/** Reports the tool a warm worker is currently running (\"\" = idle between tools). */\nexport type WarmProgressCallback = (activity: string) => void;\n\n/** A run failed at the infrastructure level (worker crash, timeout, protocol error). */\nexport class WarmWorkerError extends Error {}\n\n/** Default per-task wait before declaring a warm run stalled and failing over to cold. */\nconst WARM_RUN_TIMEOUT_MS = 180_000;\n\n/**\n * One long-lived RPC child pinned to a single agent-type configuration. Reused\n * across tasks via reset(); a crash makes it not-alive so the pool discards it.\n */\nexport class WarmSubagentWorker {\n\tprivate readonly client: RpcClient;\n\tprivate alive = true;\n\n\tconstructor(\n\t\treadonly key: string,\n\t\toptions: WarmDispatchOptions,\n\t\tprivate readonly env: NodeJS.ProcessEnv,\n\t\tregistry: AgentRegistry,\n\t\tskillPaths: readonly string[],\n\t\tsettings: Settings | undefined,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tspawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {\n\t\tconst { executable, prefixArgs } = spawnCommand ?? getSubagentSpawnCommand();\n\t\tthis.client = new RpcClient({\n\t\t\texecutable,\n\t\t\tprefixArgs,\n\t\t\tcwd: options.cwd,\n\t\t\tenv: this.env as Record<string, string>,\n\t\t\targs: buildWorkerArgs(options, registry, skillPaths, settings),\n\t\t});\n\t}\n\n\t/** Boot the child. Throws (as WarmWorkerError) if it fails to come up. */\n\tasync start(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.start();\n\t\t} catch (error) {\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tisAlive(): boolean {\n\t\treturn this.alive;\n\t}\n\n\t/**\n\t * Run one task to completion and return its answer + usage. Throws\n\t * WarmWorkerError on an infra failure (crash/timeout/protocol) so the caller\n\t * can fall back to the cold pool; a task that ran but reported failure returns\n\t * `{ ok: false }` instead (no fall back — the work was actually done).\n\t */\n\tasync run(\n\t\tprompt: string,\n\t\tonActivity?: WarmProgressCallback,\n\t\ttimeoutMs = WARM_RUN_TIMEOUT_MS,\n\t): Promise<WarmRunResult> {\n\t\tif (!this.alive) throw new WarmWorkerError(\"worker is not alive\");\n\t\t// Mirror the cold pool's coarse progress: report the tool the child is\n\t\t// currently running so a warm dispatch's task row reads \"⋯ grep\" rather than a\n\t\t// static \"running…\". Cleared between tools and at turn end.\n\t\tconst detachProgress = onActivity ? this.tapProgress(onActivity) : undefined;\n\t\ttry {\n\t\t\tconst events = await this.client.promptAndWait(prompt, undefined, timeoutMs);\n\t\t\tconst failure = firstTurnError(events);\n\t\t\tconst summary = (await this.client.getLastAssistantText()) ?? \"\";\n\t\t\tconst usage = await this.readUsage();\n\t\t\tif (failure) {\n\t\t\t\treturn { ok: false, status: \"failed\", summary, usage, error: failure };\n\t\t\t}\n\t\t\treturn { ok: true, status: \"complete\", summary, usage };\n\t\t} catch (error) {\n\t\t\t// A prompt/await failure means the child is no longer trustworthy: mark it\n\t\t\t// dead so the pool discards it, and signal infra failure for fallback.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t} finally {\n\t\t\tdetachProgress?.();\n\t\t\tonActivity?.(\"\");\n\t\t}\n\t}\n\n\t/** Forward the child's coarse tool-lifecycle events to an activity callback. */\n\tprivate tapProgress(onActivity: WarmProgressCallback): () => void {\n\t\treturn this.client.onEvent((event) => {\n\t\t\tconst e = event as { type?: string; toolName?: string };\n\t\t\tif (e.type === \"tool_execution_start\") onActivity(typeof e.toolName === \"string\" ? e.toolName : \"\");\n\t\t\telse if (e.type === \"tool_execution_end\" || e.type === \"turn_end\") onActivity(\"\");\n\t\t});\n\t}\n\n\t/** Reset the worker's conversation so it can take the next task cleanly. */\n\tasync reset(): Promise<void> {\n\t\tif (!this.alive) return;\n\t\ttry {\n\t\t\tawait this.client.newSession();\n\t\t} catch (error) {\n\t\t\t// A reset failure leaves the worker in an unknown state — retire it.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tthis.alive = false;\n\t\tawait this.client.stop().catch(() => {});\n\t}\n\n\tprivate async readUsage(): Promise<WarmUsage | undefined> {\n\t\ttry {\n\t\t\tconst stats = await this.client.getSessionStats();\n\t\t\treturn {\n\t\t\t\tinput: stats.tokens.input,\n\t\t\t\toutput: stats.tokens.output,\n\t\t\t\tcacheRead: stats.tokens.cacheRead,\n\t\t\t\tcacheWrite: stats.tokens.cacheWrite,\n\t\t\t\tcost: stats.cost,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\n/**\n * Pool of warm workers keyed by agent-type configuration. Hands out an idle\n * worker (or boots a new one up to a per-key cap), and on release resets the\n * worker and returns it to the idle set with an idle-TTL reclaim timer.\n */\nexport class WarmSubagentPool {\n\tprivate idle = new Map<string, WarmSubagentWorker[]>();\n\tprivate reclaimTimers = new Map<WarmSubagentWorker, ReturnType<typeof setTimeout>>();\n\tprivate liveCount = new Map<string, number>();\n\tprivate disposed = false;\n\tprivate registry?: AgentRegistry;\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly settings: Settings | undefined,\n\t\tprivate skillPaths: string[] = [],\n\t\tprivate readonly maxPerKey = 2,\n\t\tprivate readonly idleTtlMs = 30_000,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tprivate readonly spawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {}\n\n\tupdateSkillPaths(paths: string[]): void {\n\t\tthis.skillPaths = [...paths];\n\t}\n\n\tprivate getRegistry(): AgentRegistry {\n\t\tif (!this.registry) this.registry = loadAgentRegistry({ cwd: this.cwd });\n\t\treturn this.registry;\n\t}\n\n\t/** A registry agent is poolable when it is not a fork agent (fork needs a forked session). */\n\tisPoolable(agentType: string): boolean {\n\t\tconst def = this.getRegistry().get(agentType);\n\t\treturn def !== undefined && def.fork !== true;\n\t}\n\n\t/** Stable key for one worker configuration. */\n\tprivate keyFor(options: WarmDispatchOptions): string {\n\t\tconst resolved = options.model ? resolveModelReference(options.model, this.settings) : undefined;\n\t\treturn `${options.agentType}::${resolved ?? \"default\"}::${options.provider ?? \"default\"}`;\n\t}\n\n\t/** Environment for a warm child: depth stamp + MCP skip, mirroring SubagentPool.childSpawnEnv. */\n\tprivate childEnv(agentType: string): NodeJS.ProcessEnv {\n\t\tconst env: NodeJS.ProcessEnv = {\n\t\t\t...process.env,\n\t\t\t[SUBAGENT_DEPTH_ENV]: String(currentSubagentDepth(process.env) + 1),\n\t\t};\n\t\tconst def = this.getRegistry().get(agentType);\n\t\tif (!toolAllowlistNeedsMcp(def?.tools)) env[SUBAGENT_SKIP_MCP_ENV] = \"1\";\n\t\treturn env;\n\t}\n\n\t/**\n\t * Run a task on a warm worker end to end: acquire (reuse or boot), run, then\n\t * release back to the pool. Throws WarmWorkerError on infra failure so the\n\t * caller can fall back to the cold pool.\n\t */\n\tasync dispatch(\n\t\tprompt: string,\n\t\toptions: WarmDispatchOptions,\n\t\tonActivity?: WarmProgressCallback,\n\t): Promise<WarmRunResult> {\n\t\tif (this.disposed) throw new WarmWorkerError(\"warm pool disposed\");\n\t\tconst worker = await this.acquire(options);\n\t\ttry {\n\t\t\tconst result = await worker.run(prompt, onActivity);\n\t\t\tawait this.release(worker);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Infra failure: the worker already marked itself dead; drop it entirely.\n\t\t\tawait this.discard(worker);\n\t\t\tthrow error instanceof WarmWorkerError ? error : new WarmWorkerError(String(error));\n\t\t}\n\t}\n\n\tprivate async acquire(options: WarmDispatchOptions): Promise<WarmSubagentWorker> {\n\t\tconst key = this.keyFor(options);\n\t\tconst pool = this.idle.get(key);\n\t\twhile (pool && pool.length > 0) {\n\t\t\tconst worker = pool.pop()!;\n\t\t\tthis.clearReclaim(worker);\n\t\t\tif (worker.isAlive()) return worker;\n\t\t\t// Dead idle worker (child exited while parked): drop and try the next.\n\t\t\tthis.decLive(key);\n\t\t\tawait worker.dispose();\n\t\t}\n\n\t\tconst worker = new WarmSubagentWorker(\n\t\t\tkey,\n\t\t\toptions,\n\t\t\tthis.childEnv(options.agentType),\n\t\t\tthis.getRegistry(),\n\t\t\tthis.skillPaths,\n\t\t\tthis.settings,\n\t\t\tthis.spawnCommand,\n\t\t);\n\t\tthis.incLive(key);\n\t\ttry {\n\t\t\tawait worker.start();\n\t\t} catch (error) {\n\t\t\tthis.decLive(key);\n\t\t\tthrow error;\n\t\t}\n\t\treturn worker;\n\t}\n\n\tprivate async release(worker: WarmSubagentWorker): Promise<void> {\n\t\tif (this.disposed || !worker.isAlive()) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait worker.reset();\n\t\t} catch {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tconst key = worker.key;\n\t\tconst pool = this.idle.get(key) ?? [];\n\t\tif (pool.length >= this.maxPerKey) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tpool.push(worker);\n\t\tthis.idle.set(key, pool);\n\t\tthis.armReclaim(worker);\n\t}\n\n\tprivate async discard(worker: WarmSubagentWorker): Promise<void> {\n\t\tthis.clearReclaim(worker);\n\t\tthis.decLive(worker.key);\n\t\tawait worker.dispose();\n\t}\n\n\tprivate armReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = setTimeout(() => {\n\t\t\tconst pool = this.idle.get(worker.key);\n\t\t\tif (pool) {\n\t\t\t\tconst idx = pool.indexOf(worker);\n\t\t\t\tif (idx !== -1) pool.splice(idx, 1);\n\t\t\t}\n\t\t\tvoid this.discard(worker);\n\t\t}, this.idleTtlMs);\n\t\ttimer.unref?.();\n\t\tthis.reclaimTimers.set(worker, timer);\n\t}\n\n\tprivate clearReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = this.reclaimTimers.get(worker);\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\tthis.reclaimTimers.delete(worker);\n\t\t}\n\t}\n\n\tprivate incLive(key: string): void {\n\t\tthis.liveCount.set(key, (this.liveCount.get(key) ?? 0) + 1);\n\t}\n\n\tprivate decLive(key: string): void {\n\t\tconst n = (this.liveCount.get(key) ?? 1) - 1;\n\t\tif (n <= 0) this.liveCount.delete(key);\n\t\telse this.liveCount.set(key, n);\n\t}\n\n\t/** Number of currently idle (parked) workers — exposed for tests/diagnostics. */\n\tidleCount(): number {\n\t\tlet total = 0;\n\t\tfor (const pool of this.idle.values()) total += pool.length;\n\t\treturn total;\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tconst all: WarmSubagentWorker[] = [];\n\t\tfor (const pool of this.idle.values()) all.push(...pool);\n\t\tthis.idle.clear();\n\t\tfor (const timer of this.reclaimTimers.values()) clearTimeout(timer);\n\t\tthis.reclaimTimers.clear();\n\t\tthis.liveCount.clear();\n\t\tawait Promise.all(all.map((w) => w.dispose()));\n\t}\n}\n\n/**\n * Build the RPC child's CLI args for a warm worker. Mirrors the agent-config\n * portion of SubagentPool.buildArgs (system prompt, tools, model/provider, turn\n * cap, skills) but omits the one-shot `--mode json` / `--session` / `--task-id`\n * bits, since the worker runs persistently in RPC mode and resets via new_session.\n */\nfunction buildWorkerArgs(\n\toptions: WarmDispatchOptions,\n\tregistry: AgentRegistry,\n\tskillPaths: readonly string[],\n\tsettings: Settings | undefined,\n): string[] {\n\tconst args: string[] = [];\n\tconst def = registry.get(options.agentType);\n\n\tif (def?.prompt) args.push(\"--system-prompt\", def.prompt);\n\n\tconst childDepth = currentSubagentDepth(process.env) + 1;\n\tconst canChildDelegate = def?.delegate === true && childDepth < resolveMaxSubagentDepth(undefined, process.env);\n\n\tconst tools = def?.tools ? [...def.tools] : undefined;\n\tif (canChildDelegate && tools) {\n\t\tfor (const t of [\"Task\", \"TaskOutput\"]) if (!tools.includes(t)) tools.push(t);\n\t}\n\tif (tools && tools.length > 0) args.push(\"--tools\", tools.join(\",\"));\n\tif (def?.disallowedTools && def.disallowedTools.length > 0) {\n\t\targs.push(\"--disallowed-tools\", def.disallowedTools.join(\",\"));\n\t}\n\tif (canChildDelegate) {\n\t\targs.push(\"--enable-subagents\");\n\t\tif (def?.delegateTo && def.delegateTo.length > 0) args.push(\"--delegate-allow\", def.delegateTo.join(\",\"));\n\t}\n\n\t// Model precedence matches the cold path: a pinned (non-inherit) agent model\n\t// wins, else the requested model/category, resolved to a concrete id.\n\tconst explicitModel = def?.model && def.model !== MODEL_INHERIT ? def.model : undefined;\n\tconst rawModel = explicitModel ?? options.model;\n\tconst modelToUse = rawModel ? resolveModelReference(rawModel, settings) : undefined;\n\tif (modelToUse) args.push(\"--model\", modelToUse);\n\tif (options.provider) args.push(\"--provider\", options.provider);\n\n\tconst maxTurns = def?.maxTurns && def.maxTurns > 0 ? def.maxTurns : DEFAULT_SUBAGENT_MAX_TURNS;\n\targs.push(\"--max-turns\", String(maxTurns));\n\n\tfor (const skillPath of skillPaths) args.push(\"--skill\", skillPath);\n\n\treturn args;\n}\n\n/**\n * Scan a completed run's events for a turn that ended in error/abort, returning\n * its message (or a generic marker) so the caller can report a task failure\n * without re-reading the transcript. Returns undefined for a clean run.\n */\nfunction firstTurnError(events: readonly AgentEvent[]): string | undefined {\n\tfor (const event of events) {\n\t\tconst e = event as { type?: string; message?: { stopReason?: string; errorMessage?: string } };\n\t\tif (e.type === \"turn_end\" && (e.message?.stopReason === \"error\" || e.message?.stopReason === \"aborted\")) {\n\t\t\treturn e.message.errorMessage || `turn ${e.message.stopReason}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"warm-subagent-pool.js","sourceRoot":"","sources":["../../src/core/warm-subagent-pool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Warm subagent worker pool (experimental, opt-in via `--warm-subagents` /\n * settings.warmSubagents; default off).\n *\n * The cold path (SubagentPool) re-execs the whole CLI for every dispatch, paying\n * a full module-load + resource-graph boot each time. A warm worker instead keeps\n * a long-lived child running in RPC mode and hands it one task at a time over the\n * existing JSON-line protocol: `new_session` resets its conversation between\n * tasks, `prompt` runs the task, and `agent_end` on the event stream signals\n * completion, after which the answer + usage are pulled inline (no result.json\n * disk round-trip, no OutputVerifier). The first task per worker still pays the\n * boot; every reuse after that skips it.\n *\n * Scope/limits (deliberately conservative — see the dispatch integration):\n * - Workers are pinned per agent type: RPC has no per-prompt system-prompt/tools\n * swap, so each (agentType, model, provider) is its own worker config.\n * - Only non-resume, non-fork dispatches are eligible; resume/fork need a\n * persisted/forked session the warm path does not own.\n * - Any worker/infra failure falls back to the cold pool, so enabling this can\n * only change latency, never whether a task can run.\n */\n\nimport type { AgentEvent } from \"@kolisachint/hoocode-agent-core\";\nimport { getSubagentSpawnCommand } from \"../config.js\";\nimport { RpcClient } from \"../modes/rpc/rpc-client.js\";\nimport { MODEL_INHERIT } from \"./agent-frontmatter.js\";\nimport { type AgentRegistry, loadAgentRegistry } from \"./agent-registry.js\";\nimport { resolveModelReference } from \"./model-categories.js\";\nimport type { Settings } from \"./settings-manager.js\";\nimport {\n\tcurrentSubagentDepth,\n\tDEFER_MCP_SCHEMAS_ENV,\n\tresolveMaxSubagentDepth,\n\tSUBAGENT_DEPTH_ENV,\n\tSUBAGENT_SKIP_MCP_ENV,\n\ttoolAllowlistNeedsMcp,\n} from \"./subagent-depth.js\";\nimport { DEFAULT_SUBAGENT_MAX_TURNS } from \"./subagent-pool.js\";\n\n/** Usage totals pulled from a worker after a task, shaped like SubagentResultFile.usage. */\nexport interface WarmUsage {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n}\n\n/** Outcome of running one task on a warm worker. */\nexport interface WarmRunResult {\n\tok: boolean;\n\tstatus: \"complete\" | \"failed\";\n\t/** The subagent's final assistant text (its answer to the caller). */\n\tsummary: string;\n\tusage?: WarmUsage;\n\terror?: string;\n}\n\n/** Per-dispatch inputs that select and configure a worker. */\nexport interface WarmDispatchOptions {\n\tagentType: string;\n\tcwd: string;\n\t/** Model id or category (fast/standard/capable); resolved to a concrete id. */\n\tmodel?: string;\n\tprovider?: string;\n}\n\n/** Reports the tool a warm worker is currently running (\"\" = idle between tools). */\nexport type WarmProgressCallback = (activity: string) => void;\n\n/** A run failed at the infrastructure level (worker crash, timeout, protocol error). */\nexport class WarmWorkerError extends Error {}\n\n/** Default per-task wait before declaring a warm run stalled and failing over to cold. */\nconst WARM_RUN_TIMEOUT_MS = 180_000;\n\n/**\n * One long-lived RPC child pinned to a single agent-type configuration. Reused\n * across tasks via reset(); a crash makes it not-alive so the pool discards it.\n */\nexport class WarmSubagentWorker {\n\tprivate readonly client: RpcClient;\n\tprivate alive = true;\n\n\tconstructor(\n\t\treadonly key: string,\n\t\toptions: WarmDispatchOptions,\n\t\tprivate readonly env: NodeJS.ProcessEnv,\n\t\tregistry: AgentRegistry,\n\t\tskillPaths: readonly string[],\n\t\tsettings: Settings | undefined,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tspawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {\n\t\tconst { executable, prefixArgs } = spawnCommand ?? getSubagentSpawnCommand();\n\t\tthis.client = new RpcClient({\n\t\t\texecutable,\n\t\t\tprefixArgs,\n\t\t\tcwd: options.cwd,\n\t\t\tenv: this.env as Record<string, string>,\n\t\t\targs: buildWorkerArgs(options, registry, skillPaths, settings),\n\t\t});\n\t}\n\n\t/** Boot the child. Throws (as WarmWorkerError) if it fails to come up. */\n\tasync start(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.start();\n\t\t} catch (error) {\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tisAlive(): boolean {\n\t\treturn this.alive;\n\t}\n\n\t/**\n\t * Run one task to completion and return its answer + usage. Throws\n\t * WarmWorkerError on an infra failure (crash/timeout/protocol) so the caller\n\t * can fall back to the cold pool; a task that ran but reported failure returns\n\t * `{ ok: false }` instead (no fall back — the work was actually done).\n\t */\n\tasync run(\n\t\tprompt: string,\n\t\tonActivity?: WarmProgressCallback,\n\t\ttimeoutMs = WARM_RUN_TIMEOUT_MS,\n\t): Promise<WarmRunResult> {\n\t\tif (!this.alive) throw new WarmWorkerError(\"worker is not alive\");\n\t\t// Mirror the cold pool's coarse progress: report the tool the child is\n\t\t// currently running so a warm dispatch's task row reads \"⋯ grep\" rather than a\n\t\t// static \"running…\". Cleared between tools and at turn end.\n\t\tconst detachProgress = onActivity ? this.tapProgress(onActivity) : undefined;\n\t\ttry {\n\t\t\tconst events = await this.client.promptAndWait(prompt, undefined, timeoutMs);\n\t\t\tconst failure = firstTurnError(events);\n\t\t\tconst summary = (await this.client.getLastAssistantText()) ?? \"\";\n\t\t\tconst usage = await this.readUsage();\n\t\t\tif (failure) {\n\t\t\t\treturn { ok: false, status: \"failed\", summary, usage, error: failure };\n\t\t\t}\n\t\t\treturn { ok: true, status: \"complete\", summary, usage };\n\t\t} catch (error) {\n\t\t\t// A prompt/await failure means the child is no longer trustworthy: mark it\n\t\t\t// dead so the pool discards it, and signal infra failure for fallback.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t} finally {\n\t\t\tdetachProgress?.();\n\t\t\tonActivity?.(\"\");\n\t\t}\n\t}\n\n\t/** Forward the child's coarse tool-lifecycle events to an activity callback. */\n\tprivate tapProgress(onActivity: WarmProgressCallback): () => void {\n\t\treturn this.client.onEvent((event) => {\n\t\t\tconst e = event as { type?: string; toolName?: string };\n\t\t\tif (e.type === \"tool_execution_start\") onActivity(typeof e.toolName === \"string\" ? e.toolName : \"\");\n\t\t\telse if (e.type === \"tool_execution_end\" || e.type === \"turn_end\") onActivity(\"\");\n\t\t});\n\t}\n\n\t/** Reset the worker's conversation so it can take the next task cleanly. */\n\tasync reset(): Promise<void> {\n\t\tif (!this.alive) return;\n\t\ttry {\n\t\t\tawait this.client.newSession();\n\t\t} catch (error) {\n\t\t\t// A reset failure leaves the worker in an unknown state — retire it.\n\t\t\tthis.alive = false;\n\t\t\tthrow new WarmWorkerError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tthis.alive = false;\n\t\tawait this.client.stop().catch(() => {});\n\t}\n\n\tprivate async readUsage(): Promise<WarmUsage | undefined> {\n\t\ttry {\n\t\t\tconst stats = await this.client.getSessionStats();\n\t\t\treturn {\n\t\t\t\tinput: stats.tokens.input,\n\t\t\t\toutput: stats.tokens.output,\n\t\t\t\tcacheRead: stats.tokens.cacheRead,\n\t\t\t\tcacheWrite: stats.tokens.cacheWrite,\n\t\t\t\tcost: stats.cost,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\n/**\n * Pool of warm workers keyed by agent-type configuration. Hands out an idle\n * worker (or boots a new one up to a per-key cap), and on release resets the\n * worker and returns it to the idle set with an idle-TTL reclaim timer.\n */\nexport class WarmSubagentPool {\n\tprivate idle = new Map<string, WarmSubagentWorker[]>();\n\tprivate reclaimTimers = new Map<WarmSubagentWorker, ReturnType<typeof setTimeout>>();\n\tprivate liveCount = new Map<string, number>();\n\tprivate disposed = false;\n\tprivate registry?: AgentRegistry;\n\n\tconstructor(\n\t\tprivate readonly cwd: string,\n\t\tprivate readonly settings: Settings | undefined,\n\t\tprivate skillPaths: string[] = [],\n\t\tprivate readonly maxPerKey = 2,\n\t\tprivate readonly idleTtlMs = 30_000,\n\t\t/** Spawn command override (tests inject a fake RPC child); defaults to the real spawn command. */\n\t\tprivate readonly spawnCommand?: { executable: string; prefixArgs: string[] },\n\t) {}\n\n\tupdateSkillPaths(paths: string[]): void {\n\t\tthis.skillPaths = [...paths];\n\t}\n\n\tprivate getRegistry(): AgentRegistry {\n\t\tif (!this.registry) this.registry = loadAgentRegistry({ cwd: this.cwd });\n\t\treturn this.registry;\n\t}\n\n\t/** A registry agent is poolable when it is not a fork agent (fork needs a forked session). */\n\tisPoolable(agentType: string): boolean {\n\t\tconst def = this.getRegistry().get(agentType);\n\t\treturn def !== undefined && def.fork !== true;\n\t}\n\n\t/** Stable key for one worker configuration. */\n\tprivate keyFor(options: WarmDispatchOptions): string {\n\t\tconst resolved = options.model ? resolveModelReference(options.model, this.settings) : undefined;\n\t\treturn `${options.agentType}::${resolved ?? \"default\"}::${options.provider ?? \"default\"}`;\n\t}\n\n\t/** Environment for a warm child: depth stamp + MCP skip, mirroring SubagentPool.childSpawnEnv. */\n\tprivate childEnv(agentType: string): NodeJS.ProcessEnv {\n\t\tconst env: NodeJS.ProcessEnv = {\n\t\t\t...process.env,\n\t\t\t[SUBAGENT_DEPTH_ENV]: String(currentSubagentDepth(process.env) + 1),\n\t\t};\n\t\tconst def = this.getRegistry().get(agentType);\n\t\tif (!toolAllowlistNeedsMcp(def?.tools)) env[SUBAGENT_SKIP_MCP_ENV] = \"1\";\n\t\t// A child never defers MCP schemas: if it needs MCP it eager-registers its\n\t\t// allowlisted tools at dispatch so they are immediately callable (spec §2).\n\t\tdelete env[DEFER_MCP_SCHEMAS_ENV];\n\t\treturn env;\n\t}\n\n\t/**\n\t * Run a task on a warm worker end to end: acquire (reuse or boot), run, then\n\t * release back to the pool. Throws WarmWorkerError on infra failure so the\n\t * caller can fall back to the cold pool.\n\t */\n\tasync dispatch(\n\t\tprompt: string,\n\t\toptions: WarmDispatchOptions,\n\t\tonActivity?: WarmProgressCallback,\n\t): Promise<WarmRunResult> {\n\t\tif (this.disposed) throw new WarmWorkerError(\"warm pool disposed\");\n\t\tconst worker = await this.acquire(options);\n\t\ttry {\n\t\t\tconst result = await worker.run(prompt, onActivity);\n\t\t\tawait this.release(worker);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Infra failure: the worker already marked itself dead; drop it entirely.\n\t\t\tawait this.discard(worker);\n\t\t\tthrow error instanceof WarmWorkerError ? error : new WarmWorkerError(String(error));\n\t\t}\n\t}\n\n\tprivate async acquire(options: WarmDispatchOptions): Promise<WarmSubagentWorker> {\n\t\tconst key = this.keyFor(options);\n\t\tconst pool = this.idle.get(key);\n\t\twhile (pool && pool.length > 0) {\n\t\t\tconst worker = pool.pop()!;\n\t\t\tthis.clearReclaim(worker);\n\t\t\tif (worker.isAlive()) return worker;\n\t\t\t// Dead idle worker (child exited while parked): drop and try the next.\n\t\t\tthis.decLive(key);\n\t\t\tawait worker.dispose();\n\t\t}\n\n\t\tconst worker = new WarmSubagentWorker(\n\t\t\tkey,\n\t\t\toptions,\n\t\t\tthis.childEnv(options.agentType),\n\t\t\tthis.getRegistry(),\n\t\t\tthis.skillPaths,\n\t\t\tthis.settings,\n\t\t\tthis.spawnCommand,\n\t\t);\n\t\tthis.incLive(key);\n\t\ttry {\n\t\t\tawait worker.start();\n\t\t} catch (error) {\n\t\t\tthis.decLive(key);\n\t\t\tthrow error;\n\t\t}\n\t\treturn worker;\n\t}\n\n\tprivate async release(worker: WarmSubagentWorker): Promise<void> {\n\t\tif (this.disposed || !worker.isAlive()) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait worker.reset();\n\t\t} catch {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tconst key = worker.key;\n\t\tconst pool = this.idle.get(key) ?? [];\n\t\tif (pool.length >= this.maxPerKey) {\n\t\t\tawait this.discard(worker);\n\t\t\treturn;\n\t\t}\n\t\tpool.push(worker);\n\t\tthis.idle.set(key, pool);\n\t\tthis.armReclaim(worker);\n\t}\n\n\tprivate async discard(worker: WarmSubagentWorker): Promise<void> {\n\t\tthis.clearReclaim(worker);\n\t\tthis.decLive(worker.key);\n\t\tawait worker.dispose();\n\t}\n\n\tprivate armReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = setTimeout(() => {\n\t\t\tconst pool = this.idle.get(worker.key);\n\t\t\tif (pool) {\n\t\t\t\tconst idx = pool.indexOf(worker);\n\t\t\t\tif (idx !== -1) pool.splice(idx, 1);\n\t\t\t}\n\t\t\tvoid this.discard(worker);\n\t\t}, this.idleTtlMs);\n\t\ttimer.unref?.();\n\t\tthis.reclaimTimers.set(worker, timer);\n\t}\n\n\tprivate clearReclaim(worker: WarmSubagentWorker): void {\n\t\tconst timer = this.reclaimTimers.get(worker);\n\t\tif (timer) {\n\t\t\tclearTimeout(timer);\n\t\t\tthis.reclaimTimers.delete(worker);\n\t\t}\n\t}\n\n\tprivate incLive(key: string): void {\n\t\tthis.liveCount.set(key, (this.liveCount.get(key) ?? 0) + 1);\n\t}\n\n\tprivate decLive(key: string): void {\n\t\tconst n = (this.liveCount.get(key) ?? 1) - 1;\n\t\tif (n <= 0) this.liveCount.delete(key);\n\t\telse this.liveCount.set(key, n);\n\t}\n\n\t/** Number of currently idle (parked) workers — exposed for tests/diagnostics. */\n\tidleCount(): number {\n\t\tlet total = 0;\n\t\tfor (const pool of this.idle.values()) total += pool.length;\n\t\treturn total;\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tconst all: WarmSubagentWorker[] = [];\n\t\tfor (const pool of this.idle.values()) all.push(...pool);\n\t\tthis.idle.clear();\n\t\tfor (const timer of this.reclaimTimers.values()) clearTimeout(timer);\n\t\tthis.reclaimTimers.clear();\n\t\tthis.liveCount.clear();\n\t\tawait Promise.all(all.map((w) => w.dispose()));\n\t}\n}\n\n/**\n * Build the RPC child's CLI args for a warm worker. Mirrors the agent-config\n * portion of SubagentPool.buildArgs (system prompt, tools, model/provider, turn\n * cap, skills) but omits the one-shot `--mode json` / `--session` / `--task-id`\n * bits, since the worker runs persistently in RPC mode and resets via new_session.\n */\nfunction buildWorkerArgs(\n\toptions: WarmDispatchOptions,\n\tregistry: AgentRegistry,\n\tskillPaths: readonly string[],\n\tsettings: Settings | undefined,\n): string[] {\n\tconst args: string[] = [];\n\tconst def = registry.get(options.agentType);\n\n\tif (def?.prompt) args.push(\"--system-prompt\", def.prompt);\n\n\tconst childDepth = currentSubagentDepth(process.env) + 1;\n\tconst canChildDelegate = def?.delegate === true && childDepth < resolveMaxSubagentDepth(undefined, process.env);\n\n\tconst tools = def?.tools ? [...def.tools] : undefined;\n\tif (canChildDelegate && tools) {\n\t\tfor (const t of [\"Task\", \"TaskOutput\"]) if (!tools.includes(t)) tools.push(t);\n\t}\n\tif (tools && tools.length > 0) args.push(\"--tools\", tools.join(\",\"));\n\tif (def?.disallowedTools && def.disallowedTools.length > 0) {\n\t\targs.push(\"--disallowed-tools\", def.disallowedTools.join(\",\"));\n\t}\n\tif (canChildDelegate) {\n\t\targs.push(\"--enable-subagents\");\n\t\tif (def?.delegateTo && def.delegateTo.length > 0) args.push(\"--delegate-allow\", def.delegateTo.join(\",\"));\n\t}\n\n\t// Model precedence matches the cold path: a pinned (non-inherit) agent model\n\t// wins, else the requested model/category, resolved to a concrete id.\n\tconst explicitModel = def?.model && def.model !== MODEL_INHERIT ? def.model : undefined;\n\tconst rawModel = explicitModel ?? options.model;\n\tconst modelToUse = rawModel ? resolveModelReference(rawModel, settings) : undefined;\n\tif (modelToUse) args.push(\"--model\", modelToUse);\n\tif (options.provider) args.push(\"--provider\", options.provider);\n\n\tconst maxTurns = def?.maxTurns && def.maxTurns > 0 ? def.maxTurns : DEFAULT_SUBAGENT_MAX_TURNS;\n\targs.push(\"--max-turns\", String(maxTurns));\n\n\tfor (const skillPath of skillPaths) args.push(\"--skill\", skillPath);\n\n\treturn args;\n}\n\n/**\n * Scan a completed run's events for a turn that ended in error/abort, returning\n * its message (or a generic marker) so the caller can report a task failure\n * without re-reading the transcript. Returns undefined for a clean run.\n */\nfunction firstTurnError(events: readonly AgentEvent[]): string | undefined {\n\tfor (const event of events) {\n\t\tconst e = event as { type?: string; message?: { stopReason?: string; errorMessage?: string } };\n\t\tif (e.type === \"turn_end\" && (e.message?.stopReason === \"error\" || e.message?.stopReason === \"aborted\")) {\n\t\t\treturn e.message.errorMessage || `turn ${e.message.stopReason}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n"]}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* /plugin — marketplace add/list + plugin install/list/remove.
|
|
2
|
+
* /plugin — marketplace add/list + plugin install/list/remove (the human path).
|
|
3
3
|
*
|
|
4
4
|
* `/plugin` installs plugins from marketplaces (a git repo or local dir with a
|
|
5
5
|
* native `.agents-plugin/marketplace.json`, Claude `.claude-plugin/marketplace.json`,
|
|
6
6
|
* or Copilot-style `.github/marketplace.json` index). Installed plugins are placed in
|
|
7
7
|
* `.agents/plugins/<name>` (the primary, cross-vendor home) and loaded by the plugin
|
|
8
|
-
* loader after a reload.
|
|
9
|
-
*
|
|
8
|
+
* loader after a reload.
|
|
9
|
+
*
|
|
10
|
+
* Adding a marketplace is the human trust boundary and stays here; the shared
|
|
11
|
+
* mechanics (discovery, install, remove, the bundled default marketplace) live in
|
|
12
|
+
* `core/extensions/plugins/install.ts` so this command and the model-facing
|
|
13
|
+
* lifecycle tools never drift.
|
|
10
14
|
*
|
|
11
15
|
* /plugin marketplace add <git-url|path>
|
|
12
16
|
* /plugin marketplace list
|