@kolisachint/hoocode-agent 0.4.131 → 0.4.133
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 +34 -0
- package/dist/core/extensions/plugins/authoring.d.ts +67 -0
- package/dist/core/extensions/plugins/authoring.d.ts.map +1 -1
- package/dist/core/extensions/plugins/authoring.js +0 -0
- package/dist/core/extensions/plugins/authoring.js.map +1 -1
- package/dist/core/tools/plugin-tool-names.d.ts +2 -1
- package/dist/core/tools/plugin-tool-names.d.ts.map +1 -1
- package/dist/core/tools/plugin-tool-names.js +11 -3
- package/dist/core/tools/plugin-tool-names.js.map +1 -1
- package/dist/core/tools/plugins.d.ts.map +1 -1
- package/dist/core/tools/plugins.js +10 -6
- package/dist/core/tools/plugins.js.map +1 -1
- package/dist/core/tools/propose-plugin.d.ts +26 -16
- package/dist/core/tools/propose-plugin.d.ts.map +1 -1
- package/dist/core/tools/propose-plugin.js +276 -143
- package/dist/core/tools/propose-plugin.js.map +1 -1
- package/dist/extensions/core/ask-options.d.ts.map +1 -1
- package/dist/extensions/core/ask-options.js +42 -0
- package/dist/extensions/core/ask-options.js.map +1 -1
- package/dist/extensions/core/loop.d.ts +12 -0
- package/dist/extensions/core/loop.d.ts.map +1 -1
- package/dist/extensions/core/loop.js +35 -5
- package/dist/extensions/core/loop.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propose-plugin.js","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability authoring tools (spec §3). 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\tpluginExists,\n\tresolveAuthoringPlatforms,\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\t// Explicit tool param → session --support-platform targets → claude + github.\n\treturn resolveAuthoringPlatforms(input as MarketplacePlatform[] | undefined);\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`\\nRemove 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, {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Formats to scaffold into. Default: the session's --support-platform targets, else claude + github.\",\n\t\t\t}),\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\"Sense reusability proactively: when you complete a multi-step recipe you would plausibly repeat (or repeat the same pattern twice in one session) and SearchPlugins finds nothing that covers it, author it as a skill/command with ProposePlugin autonomously — announce what you created and why. It activates immediately and is reversible with UninstallPlugin.\",\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\t// Passive capabilities activate live — the authored skill/command/subagent\n\t\t\t// is usable on the very next model request, in this same turn.\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest)}\\n${activation.message}`;\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, {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Formats to scaffold into. Default: the session's --support-platform targets, else claude + github.\",\n\t\t\t}),\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\t// Human confirmed above: activate now. Subagents go live immediately;\n\t\t\t// hooks/MCP servers activate via the automatic reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest)}\\n${activation.message}`;\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
|
+
{"version":3,"file":"propose-plugin.js","sourceRoot":"","sources":["../../../src/core/tools/propose-plugin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability authoring tools (spec §3), refactored to a single risk-gated path.\n *\n * ProposePlugin author a NEW plugin from any capability mix — skills,\n * commands, subagents, hooks, MCP servers. The risk gate is\n * *computed from content*, not pre-declared by tool choice:\n * passive content (skills, commands, read-only subagents) is\n * authored autonomously; executable content (hooks, MCP servers,\n * mutating/high-privilege subagents) auto-triggers a \"show the\n * code + tool grant → human confirms → activate\" gate in the\n * same call. A mixed plugin (skill + hook) is authored in one\n * call, and a hook can never be mis-routed through a \"passive\"\n * tool because the gate keys off what the draft contains.\n * UpdatePlugin merge inline-authored capabilities into an EXISTING local\n * plugin. Nothing is fetched from a remote, so the supply-chain\n * \"benign v1 → hostile v2\" risk that keeps a marketplace\n * UpdatePlugin out of the model's hands does not apply here;\n * executable additions still pass through the same confirm gate.\n *\n * Both author into `.agents/plugins/<id>/` in the requested vendor layouts\n * (Claude Code + GitHub Copilot by default) via the format registry, so results\n * are proper, publishable plugins that round-trip through parsePluginDir.\n *\n * Privilege-amplification guardrail: an authored subagent may never carry a\n * plugin-system (capability-acquisition) tool in its allowlist — enforced in\n * both tools — so a low-trust authored agent cannot bootstrap privilege.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport {\n\tclassifyAllowlist,\n\tgetPlugin,\n\tisAuthoredPlugin,\n\tmergePluginDraft,\n\tpluginExists,\n\tremoveFromPlugin,\n\tresolveAuthoringPlatforms,\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 {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} from \"./plugin-tool-names.js\";\n\nexport {\n\tPROPOSE_PLUGIN_TOOL_NAME,\n\tREMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\tUPDATE_PLUGIN_TOOL_NAME,\n} 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({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Comma-separated allowed-tools, e.g. 'read, grep, glob'. Read-only grants are autonomous; \" +\n\t\t\t\t\t\"mutating/exec/network grants (Bash, Write, Edit, MCP) or '*' require human confirmation. Omit for none.\",\n\t\t\t}),\n\t\t),\n\t\tmodel: Type.Optional(Type.String({ description: \"Model override, or 'inherit'.\" })),\n\t\tbody: Type.String({ description: \"System-prompt / instruction body (markdown).\" }),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst hookSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ...\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Regex matched against the tool name. Empty/'*' = all.\" })),\n\t\tcommand: Type.String({ description: \"Shell command to run on the event.\" }),\n\t\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst mcpServerSchema = Type.Object(\n\t{\n\t\tname: Type.String({ description: \"MCP server name.\" }),\n\t\tcommand: Type.String({ description: \"Executable to launch the server.\" }),\n\t\targs: Type.Optional(Type.Array(Type.String(), { description: \"Command arguments.\" })),\n\t\tenv: Type.Optional(Type.Record(Type.String(), Type.String(), { description: \"Environment variables.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\n/** The capability params shared by ProposePlugin (create) and UpdatePlugin (merge). */\nconst capabilityProps = {\n\tdescription: Type.Optional(Type.String({ description: \"Plugin description.\" })),\n\tversion: Type.Optional(Type.String({ description: \"Plugin version, e.g. '0.1.0'.\" })),\n\tplatforms: Type.Optional(\n\t\tType.Array(platformSchema, {\n\t\t\tdescription:\n\t\t\t\t\"Formats to scaffold into. Default: the session's --support-platform targets, else claude + github \" +\n\t\t\t\t\"(UpdatePlugin defaults to the plugin's existing platforms).\",\n\t\t}),\n\t),\n\tskills: Type.Optional(Type.Array(skillSchema)),\n\tcommands: Type.Optional(Type.Array(commandSchema)),\n\tsubagents: Type.Optional(\n\t\tType.Array(subagentSchema, {\n\t\t\tdescription: \"Subagents. Read-only allowlists are autonomous; mutating ones trigger human confirmation.\",\n\t\t}),\n\t),\n\thooks: Type.Optional(Type.Array(hookSchema, { description: \"Shell hooks (executable — trigger confirmation).\" })),\n\tmcpServers: Type.Optional(\n\t\tType.Array(mcpServerSchema, { description: \"MCP servers (executable — trigger confirmation).\" }),\n\t),\n} as const;\n\n/** Union of every capability a draft can carry (used to build a draft and to classify risk). */\ninterface CapabilityInput {\n\tdescription?: string;\n\tversion?: string;\n\tplatforms?: MarketplacePlatform[];\n\tskills?: Static<typeof skillSchema>[];\n\tcommands?: Static<typeof commandSchema>[];\n\tsubagents?: Static<typeof subagentSchema>[];\n\thooks?: Static<typeof hookSchema>[];\n\tmcpServers?: Static<typeof mcpServerSchema>[];\n}\n\nfunction resolvePlatforms(input: MarketplacePlatform[] | undefined): MarketplacePlatform[] {\n\t// Explicit tool param → session --support-platform targets → claude + github.\n\treturn resolveAuthoringPlatforms(input);\n}\n\nfunction draftFrom(id: string, params: CapabilityInput, platforms: MarketplacePlatform[]): PluginDraft {\n\treturn {\n\t\tid,\n\t\tversion: params.version,\n\t\tdescription: params.description,\n\t\tsupportPlatform: platforms,\n\t\tskills: params.skills,\n\t\tcommands: params.commands,\n\t\tagents: params.subagents,\n\t\thooks: params.hooks,\n\t\tmcpServers: params.mcpServers,\n\t};\n}\n\n/** Total capabilities carried by the draft (empty arrays count as nothing). */\nfunction capabilityCount(params: CapabilityInput): number {\n\treturn (\n\t\t(params.skills?.length ?? 0) +\n\t\t(params.commands?.length ?? 0) +\n\t\t(params.subagents?.length ?? 0) +\n\t\t(params.hooks?.length ?? 0) +\n\t\t(params.mcpServers?.length ?? 0)\n\t);\n}\n\n/** The subagents whose allowlist makes them mutating/high-privilege (need the confirm gate). */\nfunction mutatingSubagents(params: CapabilityInput): Static<typeof subagentSchema>[] {\n\treturn (params.subagents ?? []).filter((sa) => classifyAllowlist(sa.tools).risk === \"mutating\");\n}\n\n/** True when the draft carries anything executable — hooks, MCP servers, or a mutating subagent. */\nfunction hasExecutable(params: CapabilityInput): boolean {\n\treturn (\n\t\t(params.hooks?.length ?? 0) > 0 || (params.mcpServers?.length ?? 0) > 0 || mutatingSubagents(params).length > 0\n\t);\n}\n\n/** Reject if any subagent carries a plugin-system tool (privilege-amplification guardrail). Returns the message, or null. */\nfunction guardrailViolation(params: CapabilityInput): string | null {\n\tfor (const sa of params.subagents ?? []) {\n\t\tconst cls = classifyAllowlist(sa.tools);\n\t\tif (cls.pluginTools.length > 0) {\n\t\t\treturn (\n\t\t\t\t`Subagent \"${sa.name}\" requests plugin-system tools (${cls.pluginTools.join(\", \")}). ` +\n\t\t\t\t\"Authored subagents may never carry capability-acquisition tools.\"\n\t\t\t);\n\t\t}\n\t}\n\treturn null;\n}\n\n/** Build the human-facing review text: the executable code and every mutating tool grant. */\nfunction buildReview(id: string, params: CapabilityInput): string {\n\tconst lines: string[] = [`Plugin \"${id}\" wants to install executable capabilities:`];\n\tfor (const h of params.hooks ?? []) {\n\t\tlines.push(` hook [${h.event}${h.matcher ? ` matcher=${h.matcher}` : \"\"}]: ${h.command}`);\n\t}\n\tfor (const s of params.mcpServers ?? []) {\n\t\tlines.push(` mcp server \"${s.name}\": ${s.command}${s.args?.length ? ` ${s.args.join(\" \")}` : \"\"}`);\n\t}\n\tfor (const sa of mutatingSubagents(params)) {\n\t\tlines.push(` subagent \"${sa.name}\" tools: ${sa.tools ?? \"(none)\"} (${classifyAllowlist(sa.tools).reason})`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nfunction summarizeWrite(\n\tid: string,\n\tplatforms: MarketplacePlatform[],\n\tfiles: string[],\n\tdest: string,\n\tverb: string,\n): string {\n\treturn (\n\t\t`${verb} plugin \"${id}\" (${platforms.join(\", \")}) with ${files.length} file(s) at ${dest}:\\n` +\n\t\tfiles.map((f) => ` ${f}`).join(\"\\n\") +\n\t\t`\\nRemove it with UninstallPlugin.`\n\t);\n}\n\nexport interface AuthorPluginDetails {\n\tid: string;\n\tauthored: boolean;\n\t/** Whether an executable-capability confirmation gate ran (and was accepted). */\n\tconfirmed?: boolean;\n}\n\nfunction reject(\n\tid: string,\n\tmessage: string,\n): {\n\tcontent: { type: \"text\"; text: string }[];\n\tdetails: AuthorPluginDetails;\n} {\n\treturn { content: [{ type: \"text\" as const, text: message }], details: { id, authored: false } };\n}\n\n/**\n * Run the shared \"executable capabilities → show → confirm\" gate. Returns:\n * - `{ ok: true }` when there is nothing executable, or the human confirmed;\n * - a tool result (authored:false) when there is no UI to confirm on, or the\n * human declined.\n */\nasync function passExecutableGate(\n\tid: string,\n\tparams: CapabilityInput,\n\tctx: ExtensionContext,\n): Promise<{ ok: true; gated: boolean } | { ok: false; result: ReturnType<typeof reject> }> {\n\tif (!hasExecutable(params)) return { ok: true, gated: false };\n\n\tconst review = buildReview(id, params);\n\tctx.ui.notify(review, \"warning\");\n\tif (!ctx.hasUI) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: reject(\n\t\t\t\tid,\n\t\t\t\t\"Authoring executable capabilities requires human confirmation, which is unavailable in this mode. \" +\n\t\t\t\t\t`Not activated.\\n${review}`,\n\t\t\t),\n\t\t};\n\t}\n\tconst confirmed = await ctx.ui.confirm(\n\t\t`Author executable plugin \"${id}\"?`,\n\t\t`${review}\\n\\nThis installs and can run the code above. Activate it?`,\n\t);\n\tif (!confirmed) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tresult: {\n\t\t\t\tcontent: [{ type: \"text\" as const, text: `Declined — plugin \"${id}\" was not authored.` }],\n\t\t\t\tdetails: { id, authored: false, confirmed: false },\n\t\t\t},\n\t\t};\n\t}\n\treturn { ok: true, gated: true };\n}\n\n// ── ProposePlugin (create) ────────────────────────────────────────────────────\n\nconst proposeParams = Type.Object(\n\t{ id: Type.String({ description: \"Plugin id (directory + manifest name).\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createProposePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof proposeParams, AuthorPluginDetails>({\n\t\tname: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tlabel: PROPOSE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Author a NEW plugin to fill a capability gap when no marketplace plugin fits. Accepts any capability mix — \" +\n\t\t\t\"skills, slash commands, subagents, hooks, MCP servers. Passive content (skills, commands, read-only \" +\n\t\t\t\"subagents) is authored autonomously; executable content (hooks, MCP servers, mutating subagents) is shown \" +\n\t\t\t\"and requires human confirmation before it activates. To change an existing plugin, use UpdatePlugin.\",\n\t\tpromptSnippet:\n\t\t\t\"Author a new plugin to fill a capability gap (passive is autonomous; executable asks to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Sense reusability proactively: when you complete a multi-step recipe you'd plausibly repeat (or repeat the same pattern twice in one session) and SearchPlugins finds nothing that covers it, author it with ProposePlugin. Passive skills/commands activate immediately and are reversible with UninstallPlugin — announce what you created and why.\",\n\t\t\t\"One tool for the whole plugin: put skills + a hook in a single call. The risk gate is computed from content — you don't pre-classify. Read-only subagents and skills/commands go straight through; hooks, MCP servers, or a subagent needing Bash/Write/Edit/MCP or tools:* pause for human confirmation.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t\t\"Publishing a proven-useful plugin to a marketplace stays a human action — do not do it autonomously.\",\n\t\t],\n\t\tparameters: proposeParams,\n\t\tasync execute(_id, params: Static<typeof proposeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tif (capabilityCount(params) === 0) {\n\t\t\t\treturn reject(params.id, \"Nothing to author. Provide skills, commands, subagents, hooks, or mcpServers.\");\n\t\t\t}\n\n\t\t\tif (pluginExists(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`A plugin named \"${params.id}\" already exists. Use UpdatePlugin to change it, or pick another id.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\tconst platforms = resolvePlatforms(params.platforms);\n\t\t\tconst result = writePluginDraft(ctx.cwd, draftFrom(params.id, params, platforms), platforms);\n\t\t\t// Passive capabilities activate live — usable on the very next model request,\n\t\t\t// this same turn; hooks/MCP servers activate via the reload once the turn ends.\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Authored\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Authored plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── UpdatePlugin (merge into an existing local plugin) ─────────────────────────\n\nconst updateParams = Type.Object(\n\t{ id: Type.String({ description: \"Id of the existing local plugin to update.\" }), ...capabilityProps },\n\t{ additionalProperties: false },\n);\n\nexport function createUpdatePluginToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof updateParams, AuthorPluginDetails>({\n\t\tname: UPDATE_PLUGIN_TOOL_NAME,\n\t\tlabel: UPDATE_PLUGIN_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Merge inline-authored capabilities into an EXISTING locally AUTHORED plugin (marketplace-installed plugins \" +\n\t\t\t\"are refused). Skills/commands/subagents are added or replaced by name; hooks and MCP servers are unioned \" +\n\t\t\t\"with what's already there; metadata is overwritten only where you supply it. Additive only — remove a \" +\n\t\t\t\"capability with RemovePluginCapability. Nothing is fetched from a remote. Passive \" +\n\t\t\t\"additions apply autonomously; executable additions (hooks, MCP servers, mutating subagents) require human \" +\n\t\t\t\"confirmation. Use ProposePlugin to create.\",\n\t\tpromptSnippet:\n\t\t\t\"Add/replace capabilities in a plugin you authored (additive; executable additions ask to confirm).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use UpdatePlugin to grow a plugin you already authored — e.g. add a skill to it, or attach a hook. Supply only the delta; existing capabilities are preserved (a matching name replaces just that one). It cannot remove a capability — use RemovePluginCapability for that.\",\n\t\t\t\"Hooks cannot be modified in place: they have no name, so supplying a changed command ADDS a second hook alongside the old one (both fire). To change a hook, RemovePluginCapability the old one first, then add the new one here.\",\n\t\t\t\"Only executable *additions* trigger confirmation — adding a passive skill to an already-executable plugin does not re-prompt.\",\n\t\t\t\"Never grant a subagent any plugin-system tool (InstallPlugin, ProposePlugin, ...); that is always rejected.\",\n\t\t],\n\t\tparameters: updateParams,\n\t\tasync execute(_id, params: Static<typeof updateParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst violation = guardrailViolation(params);\n\t\t\tif (violation) return reject(params.id, violation);\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`No plugin named \"${params.id}\" is installed. Use ProposePlugin to create it first.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Authored-only: marketplace installs land in the same directory but don't\n\t\t\t// round-trip losslessly through our emitters (see mergePluginDraft).\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"UpdatePlugin only modifies locally authored plugins — updating a marketplace plugin is a human \" +\n\t\t\t\t\t\t\"action (uninstall it and install a newer version instead).\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (capabilityCount(params) === 0 && !params.version && !params.description && !params.platforms) {\n\t\t\t\treturn reject(\n\t\t\t\t\tparams.id,\n\t\t\t\t\t\"Nothing to update. Provide skills, commands, subagents, hooks, mcpServers, platforms, or metadata.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Gate on the DELTA only — existing executables aren't re-confirmed.\n\t\t\tconst gate = await passExecutableGate(params.id, params, ctx);\n\t\t\tif (!gate.ok) return gate.result;\n\n\t\t\tconst result = mergePluginDraft(\n\t\t\t\tctx.cwd,\n\t\t\t\tparams.id,\n\t\t\t\tdraftFrom(params.id, params, existing.supportPlatform),\n\t\t\t\tparams.platforms,\n\t\t\t);\n\t\t\tconst platforms = result.plugin?.supportPlatform ?? existing.supportPlatform;\n\t\t\tconst activation = ctx.activatePlugin(result.dest);\n\t\t\tconst text = `${summarizeWrite(params.id, platforms, result.files, result.dest, \"Updated\")}\\n${activation.message}`;\n\t\t\tctx.ui.notify(`Updated plugin \"${params.id}\" (${platforms.join(\", \")}).`, \"info\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, authored: true, confirmed: gate.gated },\n\t\t\t};\n\t\t},\n\t});\n}\n\n// ── RemovePluginCapability (subtract from an authored plugin) ─────────────────\n\nconst hookRemovalSchema = Type.Object(\n\t{\n\t\tevent: Type.String({ description: \"Event of the hook(s) to remove, e.g. PreToolUse.\" }),\n\t\tmatcher: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this matcher.\" })),\n\t\tcommand: Type.Optional(Type.String({ description: \"Narrow to hooks with exactly this command.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\nconst removeParams = Type.Object(\n\t{\n\t\tid: Type.String({ description: \"Id of the authored plugin to remove capabilities from.\" }),\n\t\tskills: Type.Optional(Type.Array(Type.String(), { description: \"Skill names to remove.\" })),\n\t\tcommands: Type.Optional(Type.Array(Type.String(), { description: \"Command names to remove.\" })),\n\t\tsubagents: Type.Optional(Type.Array(Type.String(), { description: \"Subagent names to remove.\" })),\n\t\tmcpServers: Type.Optional(Type.Array(Type.String(), { description: \"MCP server names to remove.\" })),\n\t\thooks: Type.Optional(\n\t\t\tType.Array(hookRemovalSchema, {\n\t\t\t\tdescription: \"Hooks to remove, matched by event and narrowed by matcher/command when provided.\",\n\t\t\t}),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport interface RemovePluginCapabilityDetails {\n\tid: string;\n\tremoved: string[];\n\tmissing: string[];\n}\n\nexport function createRemovePluginCapabilityToolDefinition(): ToolDefinition {\n\treturn defineTool<typeof removeParams, RemovePluginCapabilityDetails>({\n\t\tname: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tlabel: REMOVE_PLUGIN_CAPABILITY_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Remove named capabilities from a locally AUTHORED plugin — skills, commands, subagents, and MCP servers by \" +\n\t\t\t\"name; hooks by event (narrowed by matcher/command). The subtractive half of UpdatePlugin. Removal is \" +\n\t\t\t\"low-risk and autonomous (deleting capabilities cannot execute code). To remove the whole plugin, use \" +\n\t\t\t\"UninstallPlugin; marketplace-installed plugins are refused here.\",\n\t\tpromptSnippet: \"Remove capabilities from a plugin you authored (low risk; autonomous).\",\n\t\tpromptGuidelines: [\n\t\t\t\"Removal runs autonomously (the low-risk direction) — announce what you removed and why.\",\n\t\t\t\"To CHANGE a hook (hooks have no name to replace by): RemovePluginCapability the old hook, then UpdatePlugin the new one (which asks for confirmation).\",\n\t\t],\n\t\tparameters: removeParams,\n\t\tasync execute(_id, params: Static<typeof removeParams>, _signal, _onUpdate, ctx: ExtensionContext) {\n\t\t\tconst noDetails = (msg: string) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: msg }],\n\t\t\t\tdetails: { id: params.id, removed: [], missing: [] },\n\t\t\t});\n\n\t\t\tconst existing = getPlugin(ctx.cwd, params.id);\n\t\t\tif (!existing) {\n\t\t\t\treturn noDetails(`No plugin named \"${params.id}\" is installed.`);\n\t\t\t}\n\t\t\tif (!isAuthoredPlugin(ctx.cwd, params.id)) {\n\t\t\t\treturn noDetails(\n\t\t\t\t\t`Plugin \"${params.id}\" was not authored in this workspace (likely installed from a marketplace). ` +\n\t\t\t\t\t\t\"RemovePluginCapability only edits locally authored plugins — use UninstallPlugin to remove it entirely.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst requested =\n\t\t\t\t(params.skills?.length ?? 0) +\n\t\t\t\t(params.commands?.length ?? 0) +\n\t\t\t\t(params.subagents?.length ?? 0) +\n\t\t\t\t(params.mcpServers?.length ?? 0) +\n\t\t\t\t(params.hooks?.length ?? 0);\n\t\t\tif (requested === 0) {\n\t\t\t\treturn noDetails(\"Nothing to remove. Name skills, commands, subagents, mcpServers, or hooks.\");\n\t\t\t}\n\n\t\t\tconst result = removeFromPlugin(ctx.cwd, params.id, {\n\t\t\t\tskills: params.skills,\n\t\t\t\tcommands: params.commands,\n\t\t\t\tsubagents: params.subagents,\n\t\t\t\tmcpServers: params.mcpServers,\n\t\t\t\thooks: params.hooks,\n\t\t\t});\n\t\t\tconst lines: string[] = [];\n\t\t\tif (result.removed.length > 0) {\n\t\t\t\tlines.push(`Removed from plugin \"${params.id}\":`, ...result.removed.map((r) => ` ${r}`));\n\t\t\t}\n\t\t\tif (result.missing.length > 0) {\n\t\t\t\tlines.push(`Not found (nothing removed):`, ...result.missing.map((m) => ` ${m}`));\n\t\t\t}\n\t\t\tconst text = lines.join(\"\\n\");\n\t\t\t// Removal takes effect through the reload path, same as UninstallPlugin.\n\t\t\tif (result.removed.length > 0) ctx.requestReloadWhenIdle();\n\t\t\tctx.ui.notify(text, result.removed.length > 0 ? \"info\" : \"warning\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\tdetails: { id: params.id, removed: result.removed, missing: result.missing },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** All three authoring tool definitions, for registration on the top-level agent. */\nexport function createProposePluginToolDefinitions(): ToolDefinition[] {\n\treturn [\n\t\tcreateProposePluginToolDefinition(),\n\t\tcreateUpdatePluginToolDefinition(),\n\t\tcreateRemovePluginCapabilityToolDefinition(),\n\t];\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask-options.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/ask-options.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAe,YAAY,EAAuC,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"ask-options.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/ask-options.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAe,YAAY,EAAuC,MAAM,gCAAgC,CAAC;AAgCrH,wBAAgB,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAiHtD","sourcesContent":["/**\n * Options pane — the ask_options tool.\n *\n * The model calls this tool when it needs the user to make a decision before\n * continuing. Each question is shown in an inline options pane where the user\n * moves with up/down, advances with right, and may type a custom answer.\n */\n\nimport type { AgentToolResult, AgentToolUpdateCallback } from \"@kolisachint/hoocode-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport type { AskQuestion, ExtensionAPI, ExtensionContext, SessionStartEvent } from \"../../core/extensions/types.js\";\nimport { LOOP_AUTO_CHANGED, LOOP_HALT } from \"./loop.js\";\n\nconst askOptionsSchema = Type.Object({\n\tquestions: Type.Array(\n\t\tType.Object({\n\t\t\tquestion: Type.String({ description: \"The question to ask the user.\" }),\n\t\t\tdetail: Type.Optional(Type.String({ description: \"Optional clarifying sub-text shown under the question.\" })),\n\t\t\toptions: Type.Array(\n\t\t\t\tType.Object({\n\t\t\t\t\tlabel: Type.String({ description: \"The option text; returned verbatim when chosen.\" }),\n\t\t\t\t\tdescription: Type.Optional(\n\t\t\t\t\t\tType.String({ description: \"Optional short description shown next to the option.\" }),\n\t\t\t\t\t),\n\t\t\t\t\trecommended: Type.Optional(\n\t\t\t\t\t\tType.Boolean({\n\t\t\t\t\t\t\tdescription: \"When true, the option is marked '(recommended)' to help the user choose.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t}),\n\t\t\t\t{ description: \"The options the user can choose from.\" },\n\t\t\t),\n\t\t\tallow_custom: Type.Optional(\n\t\t\t\tType.Boolean({\n\t\t\t\t\tdescription: \"When true, the user can type a free-form answer instead of choosing an option.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\t{ description: \"One or more decisions to ask the user, in order.\" },\n\t),\n});\n\nexport function setupAskOptions(pi: ExtensionAPI): void {\n\t// Capture the latest context so the tool can reach the interactive UI.\n\tlet activeCtx: ExtensionContext | undefined;\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tactiveCtx = ctx;\n\t});\n\n\t// Track whether an autonomous /loop is running so we never block on a human\n\t// who isn't there. The loop extension broadcasts this on the shared bus.\n\tlet autoLoopActive = false;\n\tpi.events.on(LOOP_AUTO_CHANGED, (data) => {\n\t\tautoLoopActive = !!(data as { active?: boolean })?.active;\n\t});\n\n\tpi.registerTool({\n\t\tname: \"ask_options\",\n\t\tlabel: \"Ask the user\",\n\t\tdescription:\n\t\t\t\"Ask the user to make one or more decisions before continuing. Each question is presented \" +\n\t\t\t\"in an interactive options pane where the user selects an option (or types a custom answer). \" +\n\t\t\t\"Use this when you genuinely need input to proceed and cannot reasonably decide yourself. \" +\n\t\t\t\"Returns the user's answer for each question; if the user skips, no answers are returned.\",\n\t\tparameters: askOptionsSchema,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tparams: Static<typeof askOptionsSchema>,\n\t\t\tsignal: AbortSignal,\n\t\t\t_onUpdate: AgentToolUpdateCallback,\n\t\t): Promise<AgentToolResult<undefined>> {\n\t\t\tif (!activeCtx || !activeCtx.hasUI) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Cannot ask the user: no interactive UI is available in this session. Proceed using your best judgement.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!params.questions.length) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No questions were provided.\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Autonomous loop: no human is watching, so never block on the pane.\n\t\t\t// Decide any question that carries a recommended default and proceed;\n\t\t\t// if any question lacks one, there is no safe default — halt the loop\n\t\t\t// and let the model report the blocker instead of guessing.\n\t\t\tif (autoLoopActive) {\n\t\t\t\tconst blockers = params.questions.filter((q) => !q.options.some((o) => o.recommended));\n\t\t\t\tif (blockers.length) {\n\t\t\t\t\tconst list = blockers.map((q) => ` • ${q.question}`).join(\"\\n\");\n\t\t\t\t\tpi.events.emit(LOOP_HALT, {\n\t\t\t\t\t\treason: `ask_options had ${blockers.length} question(s) with no recommended default.`,\n\t\t\t\t\t});\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t\t`Autonomous loop: no user is available to answer, and ${blockers.length} question(s) have ` +\n\t\t\t\t\t\t\t\t\t`no recommended default to fall back on:\\n${list}\\n\\n` +\n\t\t\t\t\t\t\t\t\t`The loop has been stopped. Do not guess — stop and report this blocker to the user, ` +\n\t\t\t\t\t\t\t\t\t`explaining what decision is needed and the options you were weighing.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tconst text = params.questions\n\t\t\t\t\t.map((q) => {\n\t\t\t\t\t\tconst rec = q.options.find((o) => o.recommended);\n\t\t\t\t\t\treturn `${q.question}\\n → ${rec?.label} (auto-selected recommended default; autonomous loop, no user present)`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst questions: AskQuestion[] = params.questions.map((q) => ({\n\t\t\t\tquestion: q.question,\n\t\t\t\tdetail: q.detail,\n\t\t\t\toptions: q.options.map((o) => ({ label: o.label, description: o.description, recommended: o.recommended })),\n\t\t\t\tallowCustom: q.allow_custom,\n\t\t\t}));\n\n\t\t\tconst answers = await activeCtx.ui.askOptions(questions, { signal });\n\n\t\t\tif (!answers) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"The user skipped the question(s) without answering. Ask how they would like to proceed.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = questions.map((q, i) => `${q.question}\\n → ${answers[i] ?? \"(no answer)\"}`).join(\"\\n\\n\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t},\n\t});\n}\n"]}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* moves with up/down, advances with right, and may type a custom answer.
|
|
7
7
|
*/
|
|
8
8
|
import { Type } from "typebox";
|
|
9
|
+
import { LOOP_AUTO_CHANGED, LOOP_HALT } from "./loop.js";
|
|
9
10
|
const askOptionsSchema = Type.Object({
|
|
10
11
|
questions: Type.Array(Type.Object({
|
|
11
12
|
question: Type.String({ description: "The question to ask the user." }),
|
|
@@ -28,6 +29,12 @@ export function setupAskOptions(pi) {
|
|
|
28
29
|
pi.on("session_start", (_event, ctx) => {
|
|
29
30
|
activeCtx = ctx;
|
|
30
31
|
});
|
|
32
|
+
// Track whether an autonomous /loop is running so we never block on a human
|
|
33
|
+
// who isn't there. The loop extension broadcasts this on the shared bus.
|
|
34
|
+
let autoLoopActive = false;
|
|
35
|
+
pi.events.on(LOOP_AUTO_CHANGED, (data) => {
|
|
36
|
+
autoLoopActive = !!data?.active;
|
|
37
|
+
});
|
|
31
38
|
pi.registerTool({
|
|
32
39
|
name: "ask_options",
|
|
33
40
|
label: "Ask the user",
|
|
@@ -54,6 +61,41 @@ export function setupAskOptions(pi) {
|
|
|
54
61
|
details: undefined,
|
|
55
62
|
};
|
|
56
63
|
}
|
|
64
|
+
// Autonomous loop: no human is watching, so never block on the pane.
|
|
65
|
+
// Decide any question that carries a recommended default and proceed;
|
|
66
|
+
// if any question lacks one, there is no safe default — halt the loop
|
|
67
|
+
// and let the model report the blocker instead of guessing.
|
|
68
|
+
if (autoLoopActive) {
|
|
69
|
+
const blockers = params.questions.filter((q) => !q.options.some((o) => o.recommended));
|
|
70
|
+
if (blockers.length) {
|
|
71
|
+
const list = blockers.map((q) => ` • ${q.question}`).join("\n");
|
|
72
|
+
pi.events.emit(LOOP_HALT, {
|
|
73
|
+
reason: `ask_options had ${blockers.length} question(s) with no recommended default.`,
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `Autonomous loop: no user is available to answer, and ${blockers.length} question(s) have ` +
|
|
80
|
+
`no recommended default to fall back on:\n${list}\n\n` +
|
|
81
|
+
`The loop has been stopped. Do not guess — stop and report this blocker to the user, ` +
|
|
82
|
+
`explaining what decision is needed and the options you were weighing.`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
details: undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const text = params.questions
|
|
89
|
+
.map((q) => {
|
|
90
|
+
const rec = q.options.find((o) => o.recommended);
|
|
91
|
+
return `${q.question}\n → ${rec?.label} (auto-selected recommended default; autonomous loop, no user present)`;
|
|
92
|
+
})
|
|
93
|
+
.join("\n\n");
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: "text", text }],
|
|
96
|
+
details: undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
57
99
|
const questions = params.questions.map((q) => ({
|
|
58
100
|
question: q.question,
|
|
59
101
|
detail: q.detail,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask-options.js","sourceRoot":"","sources":["../../../src/extensions/core/ask-options.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Options pane — the ask_options tool.\n *\n * The model calls this tool when it needs the user to make a decision before\n * continuing. Each question is shown in an inline options pane where the user\n * moves with up/down, advances with right, and may type a custom answer.\n */\n\nimport type { AgentToolResult, AgentToolUpdateCallback } from \"@kolisachint/hoocode-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport type { AskQuestion, ExtensionAPI, ExtensionContext, SessionStartEvent } from \"../../core/extensions/types.js\";\n\nconst askOptionsSchema = Type.Object({\n\tquestions: Type.Array(\n\t\tType.Object({\n\t\t\tquestion: Type.String({ description: \"The question to ask the user.\" }),\n\t\t\tdetail: Type.Optional(Type.String({ description: \"Optional clarifying sub-text shown under the question.\" })),\n\t\t\toptions: Type.Array(\n\t\t\t\tType.Object({\n\t\t\t\t\tlabel: Type.String({ description: \"The option text; returned verbatim when chosen.\" }),\n\t\t\t\t\tdescription: Type.Optional(\n\t\t\t\t\t\tType.String({ description: \"Optional short description shown next to the option.\" }),\n\t\t\t\t\t),\n\t\t\t\t\trecommended: Type.Optional(\n\t\t\t\t\t\tType.Boolean({\n\t\t\t\t\t\t\tdescription: \"When true, the option is marked '(recommended)' to help the user choose.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t}),\n\t\t\t\t{ description: \"The options the user can choose from.\" },\n\t\t\t),\n\t\t\tallow_custom: Type.Optional(\n\t\t\t\tType.Boolean({\n\t\t\t\t\tdescription: \"When true, the user can type a free-form answer instead of choosing an option.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\t{ description: \"One or more decisions to ask the user, in order.\" },\n\t),\n});\n\nexport function setupAskOptions(pi: ExtensionAPI): void {\n\t// Capture the latest context so the tool can reach the interactive UI.\n\tlet activeCtx: ExtensionContext | undefined;\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tactiveCtx = ctx;\n\t});\n\n\tpi.registerTool({\n\t\tname: \"ask_options\",\n\t\tlabel: \"Ask the user\",\n\t\tdescription:\n\t\t\t\"Ask the user to make one or more decisions before continuing. Each question is presented \" +\n\t\t\t\"in an interactive options pane where the user selects an option (or types a custom answer). \" +\n\t\t\t\"Use this when you genuinely need input to proceed and cannot reasonably decide yourself. \" +\n\t\t\t\"Returns the user's answer for each question; if the user skips, no answers are returned.\",\n\t\tparameters: askOptionsSchema,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tparams: Static<typeof askOptionsSchema>,\n\t\t\tsignal: AbortSignal,\n\t\t\t_onUpdate: AgentToolUpdateCallback,\n\t\t): Promise<AgentToolResult<undefined>> {\n\t\t\tif (!activeCtx || !activeCtx.hasUI) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Cannot ask the user: no interactive UI is available in this session. Proceed using your best judgement.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!params.questions.length) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No questions were provided.\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst questions: AskQuestion[] = params.questions.map((q) => ({\n\t\t\t\tquestion: q.question,\n\t\t\t\tdetail: q.detail,\n\t\t\t\toptions: q.options.map((o) => ({ label: o.label, description: o.description, recommended: o.recommended })),\n\t\t\t\tallowCustom: q.allow_custom,\n\t\t\t}));\n\n\t\t\tconst answers = await activeCtx.ui.askOptions(questions, { signal });\n\n\t\t\tif (!answers) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"The user skipped the question(s) without answering. Ask how they would like to proceed.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = questions.map((q, i) => `${q.question}\\n → ${answers[i] ?? \"(no answer)\"}`).join(\"\\n\\n\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ask-options.js","sourceRoot":"","sources":["../../../src/extensions/core/ask-options.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Options pane — the ask_options tool.\n *\n * The model calls this tool when it needs the user to make a decision before\n * continuing. Each question is shown in an inline options pane where the user\n * moves with up/down, advances with right, and may type a custom answer.\n */\n\nimport type { AgentToolResult, AgentToolUpdateCallback } from \"@kolisachint/hoocode-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport type { AskQuestion, ExtensionAPI, ExtensionContext, SessionStartEvent } from \"../../core/extensions/types.js\";\nimport { LOOP_AUTO_CHANGED, LOOP_HALT } from \"./loop.js\";\n\nconst askOptionsSchema = Type.Object({\n\tquestions: Type.Array(\n\t\tType.Object({\n\t\t\tquestion: Type.String({ description: \"The question to ask the user.\" }),\n\t\t\tdetail: Type.Optional(Type.String({ description: \"Optional clarifying sub-text shown under the question.\" })),\n\t\t\toptions: Type.Array(\n\t\t\t\tType.Object({\n\t\t\t\t\tlabel: Type.String({ description: \"The option text; returned verbatim when chosen.\" }),\n\t\t\t\t\tdescription: Type.Optional(\n\t\t\t\t\t\tType.String({ description: \"Optional short description shown next to the option.\" }),\n\t\t\t\t\t),\n\t\t\t\t\trecommended: Type.Optional(\n\t\t\t\t\t\tType.Boolean({\n\t\t\t\t\t\t\tdescription: \"When true, the option is marked '(recommended)' to help the user choose.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t}),\n\t\t\t\t{ description: \"The options the user can choose from.\" },\n\t\t\t),\n\t\t\tallow_custom: Type.Optional(\n\t\t\t\tType.Boolean({\n\t\t\t\t\tdescription: \"When true, the user can type a free-form answer instead of choosing an option.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\t{ description: \"One or more decisions to ask the user, in order.\" },\n\t),\n});\n\nexport function setupAskOptions(pi: ExtensionAPI): void {\n\t// Capture the latest context so the tool can reach the interactive UI.\n\tlet activeCtx: ExtensionContext | undefined;\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tactiveCtx = ctx;\n\t});\n\n\t// Track whether an autonomous /loop is running so we never block on a human\n\t// who isn't there. The loop extension broadcasts this on the shared bus.\n\tlet autoLoopActive = false;\n\tpi.events.on(LOOP_AUTO_CHANGED, (data) => {\n\t\tautoLoopActive = !!(data as { active?: boolean })?.active;\n\t});\n\n\tpi.registerTool({\n\t\tname: \"ask_options\",\n\t\tlabel: \"Ask the user\",\n\t\tdescription:\n\t\t\t\"Ask the user to make one or more decisions before continuing. Each question is presented \" +\n\t\t\t\"in an interactive options pane where the user selects an option (or types a custom answer). \" +\n\t\t\t\"Use this when you genuinely need input to proceed and cannot reasonably decide yourself. \" +\n\t\t\t\"Returns the user's answer for each question; if the user skips, no answers are returned.\",\n\t\tparameters: askOptionsSchema,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tparams: Static<typeof askOptionsSchema>,\n\t\t\tsignal: AbortSignal,\n\t\t\t_onUpdate: AgentToolUpdateCallback,\n\t\t): Promise<AgentToolResult<undefined>> {\n\t\t\tif (!activeCtx || !activeCtx.hasUI) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Cannot ask the user: no interactive UI is available in this session. Proceed using your best judgement.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (!params.questions.length) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No questions were provided.\" }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Autonomous loop: no human is watching, so never block on the pane.\n\t\t\t// Decide any question that carries a recommended default and proceed;\n\t\t\t// if any question lacks one, there is no safe default — halt the loop\n\t\t\t// and let the model report the blocker instead of guessing.\n\t\t\tif (autoLoopActive) {\n\t\t\t\tconst blockers = params.questions.filter((q) => !q.options.some((o) => o.recommended));\n\t\t\t\tif (blockers.length) {\n\t\t\t\t\tconst list = blockers.map((q) => ` • ${q.question}`).join(\"\\n\");\n\t\t\t\t\tpi.events.emit(LOOP_HALT, {\n\t\t\t\t\t\treason: `ask_options had ${blockers.length} question(s) with no recommended default.`,\n\t\t\t\t\t});\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t\t`Autonomous loop: no user is available to answer, and ${blockers.length} question(s) have ` +\n\t\t\t\t\t\t\t\t\t`no recommended default to fall back on:\\n${list}\\n\\n` +\n\t\t\t\t\t\t\t\t\t`The loop has been stopped. Do not guess — stop and report this blocker to the user, ` +\n\t\t\t\t\t\t\t\t\t`explaining what decision is needed and the options you were weighing.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tconst text = params.questions\n\t\t\t\t\t.map((q) => {\n\t\t\t\t\t\tconst rec = q.options.find((o) => o.recommended);\n\t\t\t\t\t\treturn `${q.question}\\n → ${rec?.label} (auto-selected recommended default; autonomous loop, no user present)`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst questions: AskQuestion[] = params.questions.map((q) => ({\n\t\t\t\tquestion: q.question,\n\t\t\t\tdetail: q.detail,\n\t\t\t\toptions: q.options.map((o) => ({ label: o.label, description: o.description, recommended: o.recommended })),\n\t\t\t\tallowCustom: q.allow_custom,\n\t\t\t}));\n\n\t\t\tconst answers = await activeCtx.ui.askOptions(questions, { signal });\n\n\t\t\tif (!answers) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"The user skipped the question(s) without answering. Ask how they would like to proceed.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = questions.map((q, i) => `${q.question}\\n → ${answers[i] ?? \"(no answer)\"}`).join(\"\\n\\n\");\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t},\n\t});\n}\n"]}
|
|
@@ -11,5 +11,17 @@
|
|
|
11
11
|
* /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE
|
|
12
12
|
*/
|
|
13
13
|
import type { ExtensionAPI } from "../../core/extensions/types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Event-bus channel: the autonomous-loop active state changed.
|
|
16
|
+
* Payload: `{ active: boolean }`. Emitted whenever `/loop auto` starts or stops
|
|
17
|
+
* so other extensions (e.g. ask_options) can adapt to running unattended.
|
|
18
|
+
*/
|
|
19
|
+
export declare const LOOP_AUTO_CHANGED = "loop:auto-changed";
|
|
20
|
+
/**
|
|
21
|
+
* Event-bus channel: request to halt the autonomous loop.
|
|
22
|
+
* Payload: `{ reason: string }`. Sent by another extension when it hits a
|
|
23
|
+
* blocker that requires a human decision the loop cannot safely make on its own.
|
|
24
|
+
*/
|
|
25
|
+
export declare const LOOP_HALT = "loop:halt";
|
|
14
26
|
export declare function setupLoop(pi: ExtensionAPI): void;
|
|
15
27
|
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACX,YAAY,EAIZ,MAAM,gCAAgC,CAAC;AAuCxC,wBAAgB,SAAS,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAoNhD","sourcesContent":["/**\n * /loop — cron scheduler, Cron* tools, and autonomous continuation.\n *\n * `/loop` schedules prompts via cron and drives autonomous continuation. The same\n * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.\n *\n * /loop \"<cron>\" <prompt> schedule recurring (5-field cron, local time)\n * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval\n * /loop once \"<cron>\" <prompt> schedule a one-shot\n * /loop list | /loop delete <id> | /loop stop\n * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE\n */\n\nimport { join } from \"node:path\";\nimport { Type } from \"typebox\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tSessionStartEvent,\n} from \"../../core/extensions/types.js\";\nimport { defineTool } from \"../../core/extensions/types.js\";\nimport { TaskScheduler } from \"../../core/scheduler.js\";\n\nconst AUTO_LOOP_DONE_TOKEN = \"LOOP_DONE\";\nconst DEFAULT_AUTO_MAX_TURNS = 10;\n\n/** Convert a simple interval token (\"5m\", \"2h\", \"1d\") to a 5-field cron, or null. */\nfunction intervalToCron(token: string): string | null {\n\tconst m = /^(\\d+)(m|h|d)$/.exec(token.trim());\n\tif (!m) return null;\n\tconst n = Number(m[1]);\n\tif (n < 1) return null;\n\tif (m[2] === \"m\") return `*/${n} * * * *`;\n\tif (m[2] === \"h\") return `0 */${n} * * *`;\n\treturn `0 0 */${n} * *`; // days\n}\n\n/** Pull a quoted cron expression off the front of an argument string. */\nfunction extractQuotedCron(args: string): { cron: string; rest: string } | null {\n\tconst m = /^\"([^\"]+)\"\\s*(.*)$/.exec(args.trim());\n\treturn m ? { cron: m[1].trim(), rest: m[2].trim() } : null;\n}\n\nfunction isFiveFieldCron(expr: string): boolean {\n\treturn expr.trim().split(/\\s+/).length === 5;\n}\n\n/** Flatten an assistant message's text blocks. */\nfunction assistantText(message: { content: unknown }): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((b): b is { type: \"text\"; text: string } => !!b && (b as { type?: string }).type === \"text\")\n\t\t.map((b) => b.text)\n\t\t.join(\"\\n\");\n}\n\nexport function setupLoop(pi: ExtensionAPI): void {\n\tlet scheduler: TaskScheduler | undefined;\n\tlet auto: { remaining: number } | null = null;\n\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tif (scheduler) return;\n\t\tconst isIdle = () => {\n\t\t\ttry {\n\t\t\t\treturn ctx.isIdle();\n\t\t\t} catch {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tscheduler = new TaskScheduler({\n\t\t\t// `.agents/` is the primary, cross-vendor home; the legacy `.hoocode/`\n\t\t\t// store is read once and migrates forward on the next persist.\n\t\t\tstorePath: join(ctx.cwd, \".agents\", \"scheduled_tasks.json\"),\n\t\t\tlegacyStorePath: join(ctx.cwd, \".hoocode\", \"scheduled_tasks.json\"),\n\t\t\tfire: (prompt) => pi.sendUserMessage(prompt, { deliverAs: \"followUp\" }),\n\t\t\tisIdle,\n\t\t});\n\t\tscheduler.start();\n\t});\n\n\tpi.on(\"session_shutdown\", () => {\n\t\tscheduler?.stop();\n\t\tauto = null;\n\t});\n\n\t// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.\n\tpi.on(\"agent_end\", (event, ctx) => {\n\t\tif (!auto) return;\n\t\tif (ctx.hasPendingMessages()) return; // user is steering — yield\n\t\tconst last = [...event.messages].reverse().find((m) => m.role === \"assistant\");\n\t\tconst text = last ? assistantText(last) : \"\";\n\t\tif (text.includes(AUTO_LOOP_DONE_TOKEN)) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop complete.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tif (auto.remaining <= 0) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop stopped: max turns reached.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tauto.remaining -= 1;\n\t\tpi.sendUserMessage(`Continue working toward the goal. Reply with ${AUTO_LOOP_DONE_TOKEN} when fully complete.`, {\n\t\t\tdeliverAs: \"followUp\",\n\t\t});\n\t});\n\n\t// ── Cron* tools (agent-callable) ──────────────────────────────────────────\n\tconst toolText = (s: string) => ({ content: [{ type: \"text\" as const, text: s }], details: undefined });\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronCreate\",\n\t\t\tlabel: \"Schedule Task\",\n\t\t\tdescription:\n\t\t\t\t\"Schedule a prompt to be re-submitted on a cron schedule (5-field, local time: minute hour day-of-month month day-of-week). recurring=false fires once then deletes.\",\n\t\t\tparameters: Type.Object({\n\t\t\t\tcron: Type.String({ description: \"5-field cron expression in local time\" }),\n\t\t\t\tprompt: Type.String({ description: \"Prompt to enqueue at each fire time\" }),\n\t\t\t\trecurring: Type.Optional(Type.Boolean({ description: \"Fire repeatedly (default true) or once\" })),\n\t\t\t}),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tif (!scheduler) return toolText(\"Scheduler not ready.\");\n\t\t\t\tif (!isFiveFieldCron(params.cron)) return toolText(`Invalid cron \"${params.cron}\" (need 5 fields).`);\n\t\t\t\tconst task = scheduler.create({\n\t\t\t\t\tcron: params.cron,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\trecurring: params.recurring ?? true,\n\t\t\t\t});\n\t\t\t\treturn toolText(`Scheduled ${task.id}: \"${task.cron}\" (${task.recurring ? \"recurring\" : \"once\"})`);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronList\",\n\t\t\tlabel: \"List Scheduled Tasks\",\n\t\t\tdescription: \"List all scheduled tasks (id, cron, recurring, prompt).\",\n\t\t\tparameters: Type.Object({}),\n\t\t\tasync execute() {\n\t\t\t\tconst tasks = scheduler?.list() ?? [];\n\t\t\t\tif (tasks.length === 0) return toolText(\"No scheduled tasks.\");\n\t\t\t\treturn toolText(\n\t\t\t\t\ttasks\n\t\t\t\t\t\t.map((t) => `${t.id} ${t.cron} ${t.recurring ? \"recurring\" : \"once\"} ${JSON.stringify(t.prompt)}`)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronDelete\",\n\t\t\tlabel: \"Delete Scheduled Task\",\n\t\t\tdescription: \"Delete a scheduled task by id.\",\n\t\t\tparameters: Type.Object({ id: Type.String({ description: \"Task id from CronCreate/CronList\" }) }),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tconst removed = scheduler?.delete(params.id) ?? false;\n\t\t\t\treturn toolText(removed ? `Deleted ${params.id}.` : `No task ${params.id}.`);\n\t\t\t},\n\t\t}),\n\t);\n\n\t// ── /loop command ─────────────────────────────────────────────────────────\n\tpi.registerCommand(\"loop\", {\n\t\tdescription:\n\t\t\t'Schedule prompts via cron or run an autonomous loop. /loop \"<cron>\" <prompt> | /loop <5m|2h> <prompt> | /loop once \"<cron>\" <prompt> | /loop list | /loop delete <id> | /loop stop | /loop auto [--max-turns N] <task>',\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"list\", \"delete\", \"stop\", \"once\", \"auto\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!scheduler) {\n\t\t\t\tctx.ui.notify(\"Scheduler not ready yet.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!trimmed || trimmed === \"list\") {\n\t\t\t\tconst tasks = scheduler.list();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttasks.length === 0\n\t\t\t\t\t\t? auto\n\t\t\t\t\t\t\t? `Autonomous loop active (${auto.remaining} turns left).`\n\t\t\t\t\t\t\t: \"No scheduled tasks.\"\n\t\t\t\t\t\t: tasks.map((t) => `${t.id}: ${t.cron} ${t.recurring ? \"\" : \"(once) \"}— ${t.prompt}`).join(\"\\n\"),\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed === \"stop\") {\n\t\t\t\tconst had = scheduler.list().length > 0 || auto !== null;\n\t\t\t\tscheduler.clear();\n\t\t\t\tauto = null;\n\t\t\t\tctx.ui.notify(had ? \"Stopped all loops and scheduled tasks.\" : \"Nothing to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"delete\")) {\n\t\t\t\tconst id = trimmed.slice(\"delete\".length).trim();\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop delete <id>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(scheduler.delete(id) ? `Deleted ${id}.` : `No task ${id}.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"auto\")) {\n\t\t\t\tlet rest = trimmed.slice(\"auto\".length).trim();\n\t\t\t\tlet maxTurns = DEFAULT_AUTO_MAX_TURNS;\n\t\t\t\tconst flag = /^--max-turns\\s+(\\d+)\\s*(.*)$/.exec(rest);\n\t\t\t\tif (flag) {\n\t\t\t\t\tmaxTurns = Number(flag[1]);\n\t\t\t\t\trest = flag[2].trim();\n\t\t\t\t}\n\t\t\t\tif (!rest) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop auto [--max-turns N] <task>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tauto = { remaining: maxTurns };\n\t\t\t\tpi.sendUserMessage(\n\t\t\t\t\t`${rest}\\n\\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`,\n\t\t\t\t\t{ deliverAs: \"followUp\" },\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Scheduling: one-shot or recurring, via quoted cron or interval token.\n\t\t\tlet recurring = true;\n\t\t\tlet body = trimmed;\n\t\t\tif (body.startsWith(\"once\")) {\n\t\t\t\trecurring = false;\n\t\t\t\tbody = body.slice(\"once\".length).trim();\n\t\t\t}\n\n\t\t\tlet cron: string | null = null;\n\t\t\tlet prompt = \"\";\n\t\t\tconst quoted = extractQuotedCron(body);\n\t\t\tif (quoted) {\n\t\t\t\tcron = quoted.cron;\n\t\t\t\tprompt = quoted.rest;\n\t\t\t} else {\n\t\t\t\tconst [first, ...restWords] = body.split(/\\s+/);\n\t\t\t\tcron = intervalToCron(first);\n\t\t\t\tprompt = restWords.join(\" \").trim();\n\t\t\t}\n\n\t\t\tif (!cron || !isFiveFieldCron(cron)) {\n\t\t\t\tctx.ui.notify('Usage: /loop \"<cron>\" <prompt> or /loop <5m|2h|1d> <prompt>', \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!prompt) {\n\t\t\t\tctx.ui.notify(\"Nothing to schedule — provide a prompt after the schedule.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst task = scheduler.create({ cron, prompt, recurring });\n\t\t\tctx.ui.notify(\n\t\t\t\t`Scheduled ${task.id}: \"${cron}\" ${recurring ? \"recurring\" : \"once\"} — \"${prompt}\". Manage with /loop list • /loop delete ${task.id}.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../../src/extensions/core/loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACX,YAAY,EAIZ,MAAM,gCAAgC,CAAC;AAOxC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,SAAS,cAAc,CAAC;AAkCrC,wBAAgB,SAAS,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAsOhD","sourcesContent":["/**\n * /loop — cron scheduler, Cron* tools, and autonomous continuation.\n *\n * `/loop` schedules prompts via cron and drives autonomous continuation. The same\n * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.\n *\n * /loop \"<cron>\" <prompt> schedule recurring (5-field cron, local time)\n * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval\n * /loop once \"<cron>\" <prompt> schedule a one-shot\n * /loop list | /loop delete <id> | /loop stop\n * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE\n */\n\nimport { join } from \"node:path\";\nimport { Type } from \"typebox\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tSessionStartEvent,\n} from \"../../core/extensions/types.js\";\nimport { defineTool } from \"../../core/extensions/types.js\";\nimport { TaskScheduler } from \"../../core/scheduler.js\";\n\nconst AUTO_LOOP_DONE_TOKEN = \"LOOP_DONE\";\nconst DEFAULT_AUTO_MAX_TURNS = 10;\n\n/**\n * Event-bus channel: the autonomous-loop active state changed.\n * Payload: `{ active: boolean }`. Emitted whenever `/loop auto` starts or stops\n * so other extensions (e.g. ask_options) can adapt to running unattended.\n */\nexport const LOOP_AUTO_CHANGED = \"loop:auto-changed\";\n\n/**\n * Event-bus channel: request to halt the autonomous loop.\n * Payload: `{ reason: string }`. Sent by another extension when it hits a\n * blocker that requires a human decision the loop cannot safely make on its own.\n */\nexport const LOOP_HALT = \"loop:halt\";\n\n/** Convert a simple interval token (\"5m\", \"2h\", \"1d\") to a 5-field cron, or null. */\nfunction intervalToCron(token: string): string | null {\n\tconst m = /^(\\d+)(m|h|d)$/.exec(token.trim());\n\tif (!m) return null;\n\tconst n = Number(m[1]);\n\tif (n < 1) return null;\n\tif (m[2] === \"m\") return `*/${n} * * * *`;\n\tif (m[2] === \"h\") return `0 */${n} * * *`;\n\treturn `0 0 */${n} * *`; // days\n}\n\n/** Pull a quoted cron expression off the front of an argument string. */\nfunction extractQuotedCron(args: string): { cron: string; rest: string } | null {\n\tconst m = /^\"([^\"]+)\"\\s*(.*)$/.exec(args.trim());\n\treturn m ? { cron: m[1].trim(), rest: m[2].trim() } : null;\n}\n\nfunction isFiveFieldCron(expr: string): boolean {\n\treturn expr.trim().split(/\\s+/).length === 5;\n}\n\n/** Flatten an assistant message's text blocks. */\nfunction assistantText(message: { content: unknown }): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((b): b is { type: \"text\"; text: string } => !!b && (b as { type?: string }).type === \"text\")\n\t\t.map((b) => b.text)\n\t\t.join(\"\\n\");\n}\n\nexport function setupLoop(pi: ExtensionAPI): void {\n\tlet scheduler: TaskScheduler | undefined;\n\tlet auto: { remaining: number } | null = null;\n\tlet activeCtx: ExtensionContext | undefined;\n\n\t/** Set the autonomous-loop state and broadcast the active flag on the bus. */\n\tfunction setAuto(next: { remaining: number } | null): void {\n\t\tconst was = auto !== null;\n\t\tauto = next;\n\t\tif (was !== (next !== null)) pi.events.emit(LOOP_AUTO_CHANGED, { active: next !== null });\n\t}\n\n\t// Another extension (e.g. ask_options) hit a decision it cannot safely make\n\t// while unattended. Stop iterating and let the model report the blocker.\n\tpi.events.on(LOOP_HALT, (data) => {\n\t\tif (!auto) return;\n\t\tconst reason = (data as { reason?: string })?.reason?.trim() || \"a decision that needs the user.\";\n\t\tsetAuto(null);\n\t\tactiveCtx?.ui.notify(`Autonomous loop halted: ${reason}`, \"warning\");\n\t});\n\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tactiveCtx = ctx;\n\t\tif (scheduler) return;\n\t\tconst isIdle = () => {\n\t\t\ttry {\n\t\t\t\treturn ctx.isIdle();\n\t\t\t} catch {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tscheduler = new TaskScheduler({\n\t\t\t// `.agents/` is the primary, cross-vendor home; the legacy `.hoocode/`\n\t\t\t// store is read once and migrates forward on the next persist.\n\t\t\tstorePath: join(ctx.cwd, \".agents\", \"scheduled_tasks.json\"),\n\t\t\tlegacyStorePath: join(ctx.cwd, \".hoocode\", \"scheduled_tasks.json\"),\n\t\t\tfire: (prompt) => pi.sendUserMessage(prompt, { deliverAs: \"followUp\" }),\n\t\t\tisIdle,\n\t\t});\n\t\tscheduler.start();\n\t});\n\n\tpi.on(\"session_shutdown\", () => {\n\t\tscheduler?.stop();\n\t\tsetAuto(null);\n\t});\n\n\t// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.\n\tpi.on(\"agent_end\", (event, ctx) => {\n\t\tif (!auto) return;\n\t\tif (ctx.hasPendingMessages()) return; // user is steering — yield\n\t\tconst last = [...event.messages].reverse().find((m) => m.role === \"assistant\");\n\t\tconst text = last ? assistantText(last) : \"\";\n\t\tif (text.includes(AUTO_LOOP_DONE_TOKEN)) {\n\t\t\tsetAuto(null);\n\t\t\tctx.ui.notify(\"Autonomous loop complete.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tif (auto.remaining <= 0) {\n\t\t\tsetAuto(null);\n\t\t\tctx.ui.notify(\"Autonomous loop stopped: max turns reached.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tauto.remaining -= 1;\n\t\tpi.sendUserMessage(`Continue working toward the goal. Reply with ${AUTO_LOOP_DONE_TOKEN} when fully complete.`, {\n\t\t\tdeliverAs: \"followUp\",\n\t\t});\n\t});\n\n\t// ── Cron* tools (agent-callable) ──────────────────────────────────────────\n\tconst toolText = (s: string) => ({ content: [{ type: \"text\" as const, text: s }], details: undefined });\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronCreate\",\n\t\t\tlabel: \"Schedule Task\",\n\t\t\tdescription:\n\t\t\t\t\"Schedule a prompt to be re-submitted on a cron schedule (5-field, local time: minute hour day-of-month month day-of-week). recurring=false fires once then deletes.\",\n\t\t\tparameters: Type.Object({\n\t\t\t\tcron: Type.String({ description: \"5-field cron expression in local time\" }),\n\t\t\t\tprompt: Type.String({ description: \"Prompt to enqueue at each fire time\" }),\n\t\t\t\trecurring: Type.Optional(Type.Boolean({ description: \"Fire repeatedly (default true) or once\" })),\n\t\t\t}),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tif (!scheduler) return toolText(\"Scheduler not ready.\");\n\t\t\t\tif (!isFiveFieldCron(params.cron)) return toolText(`Invalid cron \"${params.cron}\" (need 5 fields).`);\n\t\t\t\tconst task = scheduler.create({\n\t\t\t\t\tcron: params.cron,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\trecurring: params.recurring ?? true,\n\t\t\t\t});\n\t\t\t\treturn toolText(`Scheduled ${task.id}: \"${task.cron}\" (${task.recurring ? \"recurring\" : \"once\"})`);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronList\",\n\t\t\tlabel: \"List Scheduled Tasks\",\n\t\t\tdescription: \"List all scheduled tasks (id, cron, recurring, prompt).\",\n\t\t\tparameters: Type.Object({}),\n\t\t\tasync execute() {\n\t\t\t\tconst tasks = scheduler?.list() ?? [];\n\t\t\t\tif (tasks.length === 0) return toolText(\"No scheduled tasks.\");\n\t\t\t\treturn toolText(\n\t\t\t\t\ttasks\n\t\t\t\t\t\t.map((t) => `${t.id} ${t.cron} ${t.recurring ? \"recurring\" : \"once\"} ${JSON.stringify(t.prompt)}`)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronDelete\",\n\t\t\tlabel: \"Delete Scheduled Task\",\n\t\t\tdescription: \"Delete a scheduled task by id.\",\n\t\t\tparameters: Type.Object({ id: Type.String({ description: \"Task id from CronCreate/CronList\" }) }),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tconst removed = scheduler?.delete(params.id) ?? false;\n\t\t\t\treturn toolText(removed ? `Deleted ${params.id}.` : `No task ${params.id}.`);\n\t\t\t},\n\t\t}),\n\t);\n\n\t// ── /loop command ─────────────────────────────────────────────────────────\n\tpi.registerCommand(\"loop\", {\n\t\tdescription:\n\t\t\t'Schedule prompts via cron or run an autonomous loop. /loop \"<cron>\" <prompt> | /loop <5m|2h> <prompt> | /loop once \"<cron>\" <prompt> | /loop list | /loop delete <id> | /loop stop | /loop auto [--max-turns N] <task>',\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"list\", \"delete\", \"stop\", \"once\", \"auto\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!scheduler) {\n\t\t\t\tctx.ui.notify(\"Scheduler not ready yet.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!trimmed || trimmed === \"list\") {\n\t\t\t\tconst tasks = scheduler.list();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttasks.length === 0\n\t\t\t\t\t\t? auto\n\t\t\t\t\t\t\t? `Autonomous loop active (${auto.remaining} turns left).`\n\t\t\t\t\t\t\t: \"No scheduled tasks.\"\n\t\t\t\t\t\t: tasks.map((t) => `${t.id}: ${t.cron} ${t.recurring ? \"\" : \"(once) \"}— ${t.prompt}`).join(\"\\n\"),\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed === \"stop\") {\n\t\t\t\tconst had = scheduler.list().length > 0 || auto !== null;\n\t\t\t\tscheduler.clear();\n\t\t\t\tsetAuto(null);\n\t\t\t\tctx.ui.notify(had ? \"Stopped all loops and scheduled tasks.\" : \"Nothing to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"delete\")) {\n\t\t\t\tconst id = trimmed.slice(\"delete\".length).trim();\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop delete <id>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(scheduler.delete(id) ? `Deleted ${id}.` : `No task ${id}.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"auto\")) {\n\t\t\t\tlet rest = trimmed.slice(\"auto\".length).trim();\n\t\t\t\tlet maxTurns = DEFAULT_AUTO_MAX_TURNS;\n\t\t\t\tconst flag = /^--max-turns\\s+(\\d+)\\s*(.*)$/.exec(rest);\n\t\t\t\tif (flag) {\n\t\t\t\t\tmaxTurns = Number(flag[1]);\n\t\t\t\t\trest = flag[2].trim();\n\t\t\t\t}\n\t\t\t\tif (!rest) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop auto [--max-turns N] <task>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsetAuto({ remaining: maxTurns });\n\t\t\t\tpi.sendUserMessage(\n\t\t\t\t\t`${rest}\\n\\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`,\n\t\t\t\t\t{ deliverAs: \"followUp\" },\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Scheduling: one-shot or recurring, via quoted cron or interval token.\n\t\t\tlet recurring = true;\n\t\t\tlet body = trimmed;\n\t\t\tif (body.startsWith(\"once\")) {\n\t\t\t\trecurring = false;\n\t\t\t\tbody = body.slice(\"once\".length).trim();\n\t\t\t}\n\n\t\t\tlet cron: string | null = null;\n\t\t\tlet prompt = \"\";\n\t\t\tconst quoted = extractQuotedCron(body);\n\t\t\tif (quoted) {\n\t\t\t\tcron = quoted.cron;\n\t\t\t\tprompt = quoted.rest;\n\t\t\t} else {\n\t\t\t\tconst [first, ...restWords] = body.split(/\\s+/);\n\t\t\t\tcron = intervalToCron(first);\n\t\t\t\tprompt = restWords.join(\" \").trim();\n\t\t\t}\n\n\t\t\tif (!cron || !isFiveFieldCron(cron)) {\n\t\t\t\tctx.ui.notify('Usage: /loop \"<cron>\" <prompt> or /loop <5m|2h|1d> <prompt>', \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!prompt) {\n\t\t\t\tctx.ui.notify(\"Nothing to schedule — provide a prompt after the schedule.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst task = scheduler.create({ cron, prompt, recurring });\n\t\t\tctx.ui.notify(\n\t\t\t\t`Scheduled ${task.id}: \"${cron}\" ${recurring ? \"recurring\" : \"once\"} — \"${prompt}\". Manage with /loop list • /loop delete ${task.id}.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
@@ -16,6 +16,18 @@ import { defineTool } from "../../core/extensions/types.js";
|
|
|
16
16
|
import { TaskScheduler } from "../../core/scheduler.js";
|
|
17
17
|
const AUTO_LOOP_DONE_TOKEN = "LOOP_DONE";
|
|
18
18
|
const DEFAULT_AUTO_MAX_TURNS = 10;
|
|
19
|
+
/**
|
|
20
|
+
* Event-bus channel: the autonomous-loop active state changed.
|
|
21
|
+
* Payload: `{ active: boolean }`. Emitted whenever `/loop auto` starts or stops
|
|
22
|
+
* so other extensions (e.g. ask_options) can adapt to running unattended.
|
|
23
|
+
*/
|
|
24
|
+
export const LOOP_AUTO_CHANGED = "loop:auto-changed";
|
|
25
|
+
/**
|
|
26
|
+
* Event-bus channel: request to halt the autonomous loop.
|
|
27
|
+
* Payload: `{ reason: string }`. Sent by another extension when it hits a
|
|
28
|
+
* blocker that requires a human decision the loop cannot safely make on its own.
|
|
29
|
+
*/
|
|
30
|
+
export const LOOP_HALT = "loop:halt";
|
|
19
31
|
/** Convert a simple interval token ("5m", "2h", "1d") to a 5-field cron, or null. */
|
|
20
32
|
function intervalToCron(token) {
|
|
21
33
|
const m = /^(\d+)(m|h|d)$/.exec(token.trim());
|
|
@@ -53,7 +65,25 @@ function assistantText(message) {
|
|
|
53
65
|
export function setupLoop(pi) {
|
|
54
66
|
let scheduler;
|
|
55
67
|
let auto = null;
|
|
68
|
+
let activeCtx;
|
|
69
|
+
/** Set the autonomous-loop state and broadcast the active flag on the bus. */
|
|
70
|
+
function setAuto(next) {
|
|
71
|
+
const was = auto !== null;
|
|
72
|
+
auto = next;
|
|
73
|
+
if (was !== (next !== null))
|
|
74
|
+
pi.events.emit(LOOP_AUTO_CHANGED, { active: next !== null });
|
|
75
|
+
}
|
|
76
|
+
// Another extension (e.g. ask_options) hit a decision it cannot safely make
|
|
77
|
+
// while unattended. Stop iterating and let the model report the blocker.
|
|
78
|
+
pi.events.on(LOOP_HALT, (data) => {
|
|
79
|
+
if (!auto)
|
|
80
|
+
return;
|
|
81
|
+
const reason = data?.reason?.trim() || "a decision that needs the user.";
|
|
82
|
+
setAuto(null);
|
|
83
|
+
activeCtx?.ui.notify(`Autonomous loop halted: ${reason}`, "warning");
|
|
84
|
+
});
|
|
56
85
|
pi.on("session_start", (_event, ctx) => {
|
|
86
|
+
activeCtx = ctx;
|
|
57
87
|
if (scheduler)
|
|
58
88
|
return;
|
|
59
89
|
const isIdle = () => {
|
|
@@ -76,7 +106,7 @@ export function setupLoop(pi) {
|
|
|
76
106
|
});
|
|
77
107
|
pi.on("session_shutdown", () => {
|
|
78
108
|
scheduler?.stop();
|
|
79
|
-
|
|
109
|
+
setAuto(null);
|
|
80
110
|
});
|
|
81
111
|
// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.
|
|
82
112
|
pi.on("agent_end", (event, ctx) => {
|
|
@@ -87,12 +117,12 @@ export function setupLoop(pi) {
|
|
|
87
117
|
const last = [...event.messages].reverse().find((m) => m.role === "assistant");
|
|
88
118
|
const text = last ? assistantText(last) : "";
|
|
89
119
|
if (text.includes(AUTO_LOOP_DONE_TOKEN)) {
|
|
90
|
-
|
|
120
|
+
setAuto(null);
|
|
91
121
|
ctx.ui.notify("Autonomous loop complete.", "info");
|
|
92
122
|
return;
|
|
93
123
|
}
|
|
94
124
|
if (auto.remaining <= 0) {
|
|
95
|
-
|
|
125
|
+
setAuto(null);
|
|
96
126
|
ctx.ui.notify("Autonomous loop stopped: max turns reached.", "warning");
|
|
97
127
|
return;
|
|
98
128
|
}
|
|
@@ -173,7 +203,7 @@ export function setupLoop(pi) {
|
|
|
173
203
|
if (trimmed === "stop") {
|
|
174
204
|
const had = scheduler.list().length > 0 || auto !== null;
|
|
175
205
|
scheduler.clear();
|
|
176
|
-
|
|
206
|
+
setAuto(null);
|
|
177
207
|
ctx.ui.notify(had ? "Stopped all loops and scheduled tasks." : "Nothing to stop.", "info");
|
|
178
208
|
return;
|
|
179
209
|
}
|
|
@@ -198,7 +228,7 @@ export function setupLoop(pi) {
|
|
|
198
228
|
ctx.ui.notify("Usage: /loop auto [--max-turns N] <task>", "warning");
|
|
199
229
|
return;
|
|
200
230
|
}
|
|
201
|
-
|
|
231
|
+
setAuto({ remaining: maxTurns });
|
|
202
232
|
pi.sendUserMessage(`${rest}\n\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`, { deliverAs: "followUp" });
|
|
203
233
|
ctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, "info");
|
|
204
234
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../src/extensions/core/loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /loop — cron scheduler, Cron* tools, and autonomous continuation.\n *\n * `/loop` schedules prompts via cron and drives autonomous continuation. The same\n * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.\n *\n * /loop \"<cron>\" <prompt> schedule recurring (5-field cron, local time)\n * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval\n * /loop once \"<cron>\" <prompt> schedule a one-shot\n * /loop list | /loop delete <id> | /loop stop\n * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE\n */\n\nimport { join } from \"node:path\";\nimport { Type } from \"typebox\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tSessionStartEvent,\n} from \"../../core/extensions/types.js\";\nimport { defineTool } from \"../../core/extensions/types.js\";\nimport { TaskScheduler } from \"../../core/scheduler.js\";\n\nconst AUTO_LOOP_DONE_TOKEN = \"LOOP_DONE\";\nconst DEFAULT_AUTO_MAX_TURNS = 10;\n\n/** Convert a simple interval token (\"5m\", \"2h\", \"1d\") to a 5-field cron, or null. */\nfunction intervalToCron(token: string): string | null {\n\tconst m = /^(\\d+)(m|h|d)$/.exec(token.trim());\n\tif (!m) return null;\n\tconst n = Number(m[1]);\n\tif (n < 1) return null;\n\tif (m[2] === \"m\") return `*/${n} * * * *`;\n\tif (m[2] === \"h\") return `0 */${n} * * *`;\n\treturn `0 0 */${n} * *`; // days\n}\n\n/** Pull a quoted cron expression off the front of an argument string. */\nfunction extractQuotedCron(args: string): { cron: string; rest: string } | null {\n\tconst m = /^\"([^\"]+)\"\\s*(.*)$/.exec(args.trim());\n\treturn m ? { cron: m[1].trim(), rest: m[2].trim() } : null;\n}\n\nfunction isFiveFieldCron(expr: string): boolean {\n\treturn expr.trim().split(/\\s+/).length === 5;\n}\n\n/** Flatten an assistant message's text blocks. */\nfunction assistantText(message: { content: unknown }): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((b): b is { type: \"text\"; text: string } => !!b && (b as { type?: string }).type === \"text\")\n\t\t.map((b) => b.text)\n\t\t.join(\"\\n\");\n}\n\nexport function setupLoop(pi: ExtensionAPI): void {\n\tlet scheduler: TaskScheduler | undefined;\n\tlet auto: { remaining: number } | null = null;\n\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tif (scheduler) return;\n\t\tconst isIdle = () => {\n\t\t\ttry {\n\t\t\t\treturn ctx.isIdle();\n\t\t\t} catch {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tscheduler = new TaskScheduler({\n\t\t\t// `.agents/` is the primary, cross-vendor home; the legacy `.hoocode/`\n\t\t\t// store is read once and migrates forward on the next persist.\n\t\t\tstorePath: join(ctx.cwd, \".agents\", \"scheduled_tasks.json\"),\n\t\t\tlegacyStorePath: join(ctx.cwd, \".hoocode\", \"scheduled_tasks.json\"),\n\t\t\tfire: (prompt) => pi.sendUserMessage(prompt, { deliverAs: \"followUp\" }),\n\t\t\tisIdle,\n\t\t});\n\t\tscheduler.start();\n\t});\n\n\tpi.on(\"session_shutdown\", () => {\n\t\tscheduler?.stop();\n\t\tauto = null;\n\t});\n\n\t// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.\n\tpi.on(\"agent_end\", (event, ctx) => {\n\t\tif (!auto) return;\n\t\tif (ctx.hasPendingMessages()) return; // user is steering — yield\n\t\tconst last = [...event.messages].reverse().find((m) => m.role === \"assistant\");\n\t\tconst text = last ? assistantText(last) : \"\";\n\t\tif (text.includes(AUTO_LOOP_DONE_TOKEN)) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop complete.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tif (auto.remaining <= 0) {\n\t\t\tauto = null;\n\t\t\tctx.ui.notify(\"Autonomous loop stopped: max turns reached.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tauto.remaining -= 1;\n\t\tpi.sendUserMessage(`Continue working toward the goal. Reply with ${AUTO_LOOP_DONE_TOKEN} when fully complete.`, {\n\t\t\tdeliverAs: \"followUp\",\n\t\t});\n\t});\n\n\t// ── Cron* tools (agent-callable) ──────────────────────────────────────────\n\tconst toolText = (s: string) => ({ content: [{ type: \"text\" as const, text: s }], details: undefined });\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronCreate\",\n\t\t\tlabel: \"Schedule Task\",\n\t\t\tdescription:\n\t\t\t\t\"Schedule a prompt to be re-submitted on a cron schedule (5-field, local time: minute hour day-of-month month day-of-week). recurring=false fires once then deletes.\",\n\t\t\tparameters: Type.Object({\n\t\t\t\tcron: Type.String({ description: \"5-field cron expression in local time\" }),\n\t\t\t\tprompt: Type.String({ description: \"Prompt to enqueue at each fire time\" }),\n\t\t\t\trecurring: Type.Optional(Type.Boolean({ description: \"Fire repeatedly (default true) or once\" })),\n\t\t\t}),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tif (!scheduler) return toolText(\"Scheduler not ready.\");\n\t\t\t\tif (!isFiveFieldCron(params.cron)) return toolText(`Invalid cron \"${params.cron}\" (need 5 fields).`);\n\t\t\t\tconst task = scheduler.create({\n\t\t\t\t\tcron: params.cron,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\trecurring: params.recurring ?? true,\n\t\t\t\t});\n\t\t\t\treturn toolText(`Scheduled ${task.id}: \"${task.cron}\" (${task.recurring ? \"recurring\" : \"once\"})`);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronList\",\n\t\t\tlabel: \"List Scheduled Tasks\",\n\t\t\tdescription: \"List all scheduled tasks (id, cron, recurring, prompt).\",\n\t\t\tparameters: Type.Object({}),\n\t\t\tasync execute() {\n\t\t\t\tconst tasks = scheduler?.list() ?? [];\n\t\t\t\tif (tasks.length === 0) return toolText(\"No scheduled tasks.\");\n\t\t\t\treturn toolText(\n\t\t\t\t\ttasks\n\t\t\t\t\t\t.map((t) => `${t.id} ${t.cron} ${t.recurring ? \"recurring\" : \"once\"} ${JSON.stringify(t.prompt)}`)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronDelete\",\n\t\t\tlabel: \"Delete Scheduled Task\",\n\t\t\tdescription: \"Delete a scheduled task by id.\",\n\t\t\tparameters: Type.Object({ id: Type.String({ description: \"Task id from CronCreate/CronList\" }) }),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tconst removed = scheduler?.delete(params.id) ?? false;\n\t\t\t\treturn toolText(removed ? `Deleted ${params.id}.` : `No task ${params.id}.`);\n\t\t\t},\n\t\t}),\n\t);\n\n\t// ── /loop command ─────────────────────────────────────────────────────────\n\tpi.registerCommand(\"loop\", {\n\t\tdescription:\n\t\t\t'Schedule prompts via cron or run an autonomous loop. /loop \"<cron>\" <prompt> | /loop <5m|2h> <prompt> | /loop once \"<cron>\" <prompt> | /loop list | /loop delete <id> | /loop stop | /loop auto [--max-turns N] <task>',\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"list\", \"delete\", \"stop\", \"once\", \"auto\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!scheduler) {\n\t\t\t\tctx.ui.notify(\"Scheduler not ready yet.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!trimmed || trimmed === \"list\") {\n\t\t\t\tconst tasks = scheduler.list();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttasks.length === 0\n\t\t\t\t\t\t? auto\n\t\t\t\t\t\t\t? `Autonomous loop active (${auto.remaining} turns left).`\n\t\t\t\t\t\t\t: \"No scheduled tasks.\"\n\t\t\t\t\t\t: tasks.map((t) => `${t.id}: ${t.cron} ${t.recurring ? \"\" : \"(once) \"}— ${t.prompt}`).join(\"\\n\"),\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed === \"stop\") {\n\t\t\t\tconst had = scheduler.list().length > 0 || auto !== null;\n\t\t\t\tscheduler.clear();\n\t\t\t\tauto = null;\n\t\t\t\tctx.ui.notify(had ? \"Stopped all loops and scheduled tasks.\" : \"Nothing to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"delete\")) {\n\t\t\t\tconst id = trimmed.slice(\"delete\".length).trim();\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop delete <id>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(scheduler.delete(id) ? `Deleted ${id}.` : `No task ${id}.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"auto\")) {\n\t\t\t\tlet rest = trimmed.slice(\"auto\".length).trim();\n\t\t\t\tlet maxTurns = DEFAULT_AUTO_MAX_TURNS;\n\t\t\t\tconst flag = /^--max-turns\\s+(\\d+)\\s*(.*)$/.exec(rest);\n\t\t\t\tif (flag) {\n\t\t\t\t\tmaxTurns = Number(flag[1]);\n\t\t\t\t\trest = flag[2].trim();\n\t\t\t\t}\n\t\t\t\tif (!rest) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop auto [--max-turns N] <task>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tauto = { remaining: maxTurns };\n\t\t\t\tpi.sendUserMessage(\n\t\t\t\t\t`${rest}\\n\\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`,\n\t\t\t\t\t{ deliverAs: \"followUp\" },\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Scheduling: one-shot or recurring, via quoted cron or interval token.\n\t\t\tlet recurring = true;\n\t\t\tlet body = trimmed;\n\t\t\tif (body.startsWith(\"once\")) {\n\t\t\t\trecurring = false;\n\t\t\t\tbody = body.slice(\"once\".length).trim();\n\t\t\t}\n\n\t\t\tlet cron: string | null = null;\n\t\t\tlet prompt = \"\";\n\t\t\tconst quoted = extractQuotedCron(body);\n\t\t\tif (quoted) {\n\t\t\t\tcron = quoted.cron;\n\t\t\t\tprompt = quoted.rest;\n\t\t\t} else {\n\t\t\t\tconst [first, ...restWords] = body.split(/\\s+/);\n\t\t\t\tcron = intervalToCron(first);\n\t\t\t\tprompt = restWords.join(\" \").trim();\n\t\t\t}\n\n\t\t\tif (!cron || !isFiveFieldCron(cron)) {\n\t\t\t\tctx.ui.notify('Usage: /loop \"<cron>\" <prompt> or /loop <5m|2h|1d> <prompt>', \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!prompt) {\n\t\t\t\tctx.ui.notify(\"Nothing to schedule — provide a prompt after the schedule.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst task = scheduler.create({ cron, prompt, recurring });\n\t\t\tctx.ui.notify(\n\t\t\t\t`Scheduled ${task.id}: \"${cron}\" ${recurring ? \"recurring\" : \"once\"} — \"${prompt}\". Manage with /loop list • /loop delete ${task.id}.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../../../src/extensions/core/loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * /loop — cron scheduler, Cron* tools, and autonomous continuation.\n *\n * `/loop` schedules prompts via cron and drives autonomous continuation. The same\n * scheduler backs the agent-callable CronCreate/CronList/CronDelete tools.\n *\n * /loop \"<cron>\" <prompt> schedule recurring (5-field cron, local time)\n * /loop <5m|2h|1d> <prompt> schedule recurring at a simple interval\n * /loop once \"<cron>\" <prompt> schedule a one-shot\n * /loop list | /loop delete <id> | /loop stop\n * /loop auto [--max-turns N] <task> keep iterating until the task says LOOP_DONE\n */\n\nimport { join } from \"node:path\";\nimport { Type } from \"typebox\";\nimport type {\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n\tSessionStartEvent,\n} from \"../../core/extensions/types.js\";\nimport { defineTool } from \"../../core/extensions/types.js\";\nimport { TaskScheduler } from \"../../core/scheduler.js\";\n\nconst AUTO_LOOP_DONE_TOKEN = \"LOOP_DONE\";\nconst DEFAULT_AUTO_MAX_TURNS = 10;\n\n/**\n * Event-bus channel: the autonomous-loop active state changed.\n * Payload: `{ active: boolean }`. Emitted whenever `/loop auto` starts or stops\n * so other extensions (e.g. ask_options) can adapt to running unattended.\n */\nexport const LOOP_AUTO_CHANGED = \"loop:auto-changed\";\n\n/**\n * Event-bus channel: request to halt the autonomous loop.\n * Payload: `{ reason: string }`. Sent by another extension when it hits a\n * blocker that requires a human decision the loop cannot safely make on its own.\n */\nexport const LOOP_HALT = \"loop:halt\";\n\n/** Convert a simple interval token (\"5m\", \"2h\", \"1d\") to a 5-field cron, or null. */\nfunction intervalToCron(token: string): string | null {\n\tconst m = /^(\\d+)(m|h|d)$/.exec(token.trim());\n\tif (!m) return null;\n\tconst n = Number(m[1]);\n\tif (n < 1) return null;\n\tif (m[2] === \"m\") return `*/${n} * * * *`;\n\tif (m[2] === \"h\") return `0 */${n} * * *`;\n\treturn `0 0 */${n} * *`; // days\n}\n\n/** Pull a quoted cron expression off the front of an argument string. */\nfunction extractQuotedCron(args: string): { cron: string; rest: string } | null {\n\tconst m = /^\"([^\"]+)\"\\s*(.*)$/.exec(args.trim());\n\treturn m ? { cron: m[1].trim(), rest: m[2].trim() } : null;\n}\n\nfunction isFiveFieldCron(expr: string): boolean {\n\treturn expr.trim().split(/\\s+/).length === 5;\n}\n\n/** Flatten an assistant message's text blocks. */\nfunction assistantText(message: { content: unknown }): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\tif (!Array.isArray(content)) return \"\";\n\treturn content\n\t\t.filter((b): b is { type: \"text\"; text: string } => !!b && (b as { type?: string }).type === \"text\")\n\t\t.map((b) => b.text)\n\t\t.join(\"\\n\");\n}\n\nexport function setupLoop(pi: ExtensionAPI): void {\n\tlet scheduler: TaskScheduler | undefined;\n\tlet auto: { remaining: number } | null = null;\n\tlet activeCtx: ExtensionContext | undefined;\n\n\t/** Set the autonomous-loop state and broadcast the active flag on the bus. */\n\tfunction setAuto(next: { remaining: number } | null): void {\n\t\tconst was = auto !== null;\n\t\tauto = next;\n\t\tif (was !== (next !== null)) pi.events.emit(LOOP_AUTO_CHANGED, { active: next !== null });\n\t}\n\n\t// Another extension (e.g. ask_options) hit a decision it cannot safely make\n\t// while unattended. Stop iterating and let the model report the blocker.\n\tpi.events.on(LOOP_HALT, (data) => {\n\t\tif (!auto) return;\n\t\tconst reason = (data as { reason?: string })?.reason?.trim() || \"a decision that needs the user.\";\n\t\tsetAuto(null);\n\t\tactiveCtx?.ui.notify(`Autonomous loop halted: ${reason}`, \"warning\");\n\t});\n\n\tpi.on(\"session_start\", (_event: SessionStartEvent, ctx: ExtensionContext) => {\n\t\tactiveCtx = ctx;\n\t\tif (scheduler) return;\n\t\tconst isIdle = () => {\n\t\t\ttry {\n\t\t\t\treturn ctx.isIdle();\n\t\t\t} catch {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\tscheduler = new TaskScheduler({\n\t\t\t// `.agents/` is the primary, cross-vendor home; the legacy `.hoocode/`\n\t\t\t// store is read once and migrates forward on the next persist.\n\t\t\tstorePath: join(ctx.cwd, \".agents\", \"scheduled_tasks.json\"),\n\t\t\tlegacyStorePath: join(ctx.cwd, \".hoocode\", \"scheduled_tasks.json\"),\n\t\t\tfire: (prompt) => pi.sendUserMessage(prompt, { deliverAs: \"followUp\" }),\n\t\t\tisIdle,\n\t\t});\n\t\tscheduler.start();\n\t});\n\n\tpi.on(\"session_shutdown\", () => {\n\t\tscheduler?.stop();\n\t\tsetAuto(null);\n\t});\n\n\t// Autonomous continuation: re-prompt on each agent_end until LOOP_DONE or budget.\n\tpi.on(\"agent_end\", (event, ctx) => {\n\t\tif (!auto) return;\n\t\tif (ctx.hasPendingMessages()) return; // user is steering — yield\n\t\tconst last = [...event.messages].reverse().find((m) => m.role === \"assistant\");\n\t\tconst text = last ? assistantText(last) : \"\";\n\t\tif (text.includes(AUTO_LOOP_DONE_TOKEN)) {\n\t\t\tsetAuto(null);\n\t\t\tctx.ui.notify(\"Autonomous loop complete.\", \"info\");\n\t\t\treturn;\n\t\t}\n\t\tif (auto.remaining <= 0) {\n\t\t\tsetAuto(null);\n\t\t\tctx.ui.notify(\"Autonomous loop stopped: max turns reached.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tauto.remaining -= 1;\n\t\tpi.sendUserMessage(`Continue working toward the goal. Reply with ${AUTO_LOOP_DONE_TOKEN} when fully complete.`, {\n\t\t\tdeliverAs: \"followUp\",\n\t\t});\n\t});\n\n\t// ── Cron* tools (agent-callable) ──────────────────────────────────────────\n\tconst toolText = (s: string) => ({ content: [{ type: \"text\" as const, text: s }], details: undefined });\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronCreate\",\n\t\t\tlabel: \"Schedule Task\",\n\t\t\tdescription:\n\t\t\t\t\"Schedule a prompt to be re-submitted on a cron schedule (5-field, local time: minute hour day-of-month month day-of-week). recurring=false fires once then deletes.\",\n\t\t\tparameters: Type.Object({\n\t\t\t\tcron: Type.String({ description: \"5-field cron expression in local time\" }),\n\t\t\t\tprompt: Type.String({ description: \"Prompt to enqueue at each fire time\" }),\n\t\t\t\trecurring: Type.Optional(Type.Boolean({ description: \"Fire repeatedly (default true) or once\" })),\n\t\t\t}),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tif (!scheduler) return toolText(\"Scheduler not ready.\");\n\t\t\t\tif (!isFiveFieldCron(params.cron)) return toolText(`Invalid cron \"${params.cron}\" (need 5 fields).`);\n\t\t\t\tconst task = scheduler.create({\n\t\t\t\t\tcron: params.cron,\n\t\t\t\t\tprompt: params.prompt,\n\t\t\t\t\trecurring: params.recurring ?? true,\n\t\t\t\t});\n\t\t\t\treturn toolText(`Scheduled ${task.id}: \"${task.cron}\" (${task.recurring ? \"recurring\" : \"once\"})`);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronList\",\n\t\t\tlabel: \"List Scheduled Tasks\",\n\t\t\tdescription: \"List all scheduled tasks (id, cron, recurring, prompt).\",\n\t\t\tparameters: Type.Object({}),\n\t\t\tasync execute() {\n\t\t\t\tconst tasks = scheduler?.list() ?? [];\n\t\t\t\tif (tasks.length === 0) return toolText(\"No scheduled tasks.\");\n\t\t\t\treturn toolText(\n\t\t\t\t\ttasks\n\t\t\t\t\t\t.map((t) => `${t.id} ${t.cron} ${t.recurring ? \"recurring\" : \"once\"} ${JSON.stringify(t.prompt)}`)\n\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t);\n\t\t\t},\n\t\t}),\n\t);\n\n\tpi.registerTool(\n\t\tdefineTool({\n\t\t\tname: \"CronDelete\",\n\t\t\tlabel: \"Delete Scheduled Task\",\n\t\t\tdescription: \"Delete a scheduled task by id.\",\n\t\t\tparameters: Type.Object({ id: Type.String({ description: \"Task id from CronCreate/CronList\" }) }),\n\t\t\tasync execute(_id, params) {\n\t\t\t\tconst removed = scheduler?.delete(params.id) ?? false;\n\t\t\t\treturn toolText(removed ? `Deleted ${params.id}.` : `No task ${params.id}.`);\n\t\t\t},\n\t\t}),\n\t);\n\n\t// ── /loop command ─────────────────────────────────────────────────────────\n\tpi.registerCommand(\"loop\", {\n\t\tdescription:\n\t\t\t'Schedule prompts via cron or run an autonomous loop. /loop \"<cron>\" <prompt> | /loop <5m|2h> <prompt> | /loop once \"<cron>\" <prompt> | /loop list | /loop delete <id> | /loop stop | /loop auto [--max-turns N] <task>',\n\t\tgetArgumentCompletions: (prefix: string) =>\n\t\t\t[\"list\", \"delete\", \"stop\", \"once\", \"auto\"]\n\t\t\t\t.filter((s) => s.startsWith(prefix))\n\t\t\t\t.map((s) => ({ value: s, label: s })),\n\t\thandler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {\n\t\t\tconst trimmed = args.trim();\n\t\t\tif (!scheduler) {\n\t\t\t\tctx.ui.notify(\"Scheduler not ready yet.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!trimmed || trimmed === \"list\") {\n\t\t\t\tconst tasks = scheduler.list();\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttasks.length === 0\n\t\t\t\t\t\t? auto\n\t\t\t\t\t\t\t? `Autonomous loop active (${auto.remaining} turns left).`\n\t\t\t\t\t\t\t: \"No scheduled tasks.\"\n\t\t\t\t\t\t: tasks.map((t) => `${t.id}: ${t.cron} ${t.recurring ? \"\" : \"(once) \"}— ${t.prompt}`).join(\"\\n\"),\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed === \"stop\") {\n\t\t\t\tconst had = scheduler.list().length > 0 || auto !== null;\n\t\t\t\tscheduler.clear();\n\t\t\t\tsetAuto(null);\n\t\t\t\tctx.ui.notify(had ? \"Stopped all loops and scheduled tasks.\" : \"Nothing to stop.\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"delete\")) {\n\t\t\t\tconst id = trimmed.slice(\"delete\".length).trim();\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop delete <id>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctx.ui.notify(scheduler.delete(id) ? `Deleted ${id}.` : `No task ${id}.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (trimmed.startsWith(\"auto\")) {\n\t\t\t\tlet rest = trimmed.slice(\"auto\".length).trim();\n\t\t\t\tlet maxTurns = DEFAULT_AUTO_MAX_TURNS;\n\t\t\t\tconst flag = /^--max-turns\\s+(\\d+)\\s*(.*)$/.exec(rest);\n\t\t\t\tif (flag) {\n\t\t\t\t\tmaxTurns = Number(flag[1]);\n\t\t\t\t\trest = flag[2].trim();\n\t\t\t\t}\n\t\t\t\tif (!rest) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /loop auto [--max-turns N] <task>\", \"warning\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsetAuto({ remaining: maxTurns });\n\t\t\t\tpi.sendUserMessage(\n\t\t\t\t\t`${rest}\\n\\n(Autonomous loop: keep working until the task is fully complete, then reply with ${AUTO_LOOP_DONE_TOKEN}.)`,\n\t\t\t\t\t{ deliverAs: \"followUp\" },\n\t\t\t\t);\n\t\t\t\tctx.ui.notify(`Autonomous loop started (max ${maxTurns} turns). Stop with /loop stop.`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Scheduling: one-shot or recurring, via quoted cron or interval token.\n\t\t\tlet recurring = true;\n\t\t\tlet body = trimmed;\n\t\t\tif (body.startsWith(\"once\")) {\n\t\t\t\trecurring = false;\n\t\t\t\tbody = body.slice(\"once\".length).trim();\n\t\t\t}\n\n\t\t\tlet cron: string | null = null;\n\t\t\tlet prompt = \"\";\n\t\t\tconst quoted = extractQuotedCron(body);\n\t\t\tif (quoted) {\n\t\t\t\tcron = quoted.cron;\n\t\t\t\tprompt = quoted.rest;\n\t\t\t} else {\n\t\t\t\tconst [first, ...restWords] = body.split(/\\s+/);\n\t\t\t\tcron = intervalToCron(first);\n\t\t\t\tprompt = restWords.join(\" \").trim();\n\t\t\t}\n\n\t\t\tif (!cron || !isFiveFieldCron(cron)) {\n\t\t\t\tctx.ui.notify('Usage: /loop \"<cron>\" <prompt> or /loop <5m|2h|1d> <prompt>', \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!prompt) {\n\t\t\t\tctx.ui.notify(\"Nothing to schedule — provide a prompt after the schedule.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst task = scheduler.create({ cron, prompt, recurring });\n\t\t\tctx.ui.notify(\n\t\t\t\t`Scheduled ${task.id}: \"${cron}\" ${recurring ? \"recurring\" : \"once\"} — \"${prompt}\". Manage with /loop list • /loop delete ${task.id}.`,\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
|