{"version":3,"file":"tool-description.d.ts","sourceRoot":"","sources":["../../../src/extension/tool-description.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAM/E,eAAO,MAAM,wBAAwB,49BAMsJ,CAAC;AAE5L,eAAO,MAAM,8BAA8B,01IAqBf,CAAC;AAE7B,eAAO,MAAM,iCAAiC,u2EAalB,CAAC;AAU7B,MAAM,WAAW,sBAAsB;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,qBAAqB,CAAC,EACpD,OAAO,CAAC,EAAE,sBAAsB,GAC9B,mBAAmB,CASrB;AA8FD,wBAAgB,4BAA4B,CAC3C,MAAM,GAAE,IAAI,CAAC,eAAe,EAAE,qBAAqB,CAAM,EACzD,OAAO,CAAC,EAAE,sBAAsB,GAC9B,MAAM,CAYR","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ExtensionConfig, ToolDescriptionMode } from \"../shared/types.ts\";\nimport { getAgentDir, getProjectConfigDir } from \"../shared/utils.ts\";\n\nconst CUSTOM_TOOL_DESCRIPTION_FILE = \"subagent-tool-description.md\";\nconst CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024;\n\nexport const SUBAGENT_SAFETY_GUIDANCE = `SAFETY-CRITICAL SUBAGENT GUIDANCE:\n• Use { action: \"list\" } before execution and only run executable/non-disabled agents.\n• Direct execution is one child call: omit action, pass { agent?: \"worker\", task: \"...\" }. Omitted agent means worker.\n• Direct calls wait for the child by default. Set async:true only when deliberately running background work; use subagent_wait for a result needed in this turn.\n• The parent owns sequencing, lifecycle, and any parallel fanout. Children are workers, not orchestrators; they may contact the parent with contact_supervisor when blocked or needing a decision.\n• Keep one writer for the same cwd/worktree. Use fresh-context read-only reviewers for independent review, then have the parent synthesize and apply fixes.\n• Async runs expose asyncId/asyncDir with status.json, events.jsonl, output logs, and status via { action: \"status\", id }. Include output paths and residual risks when reporting results.`;\n\nexport const FULL_SUBAGENT_TOOL_DESCRIPTION = `Run one subagent directly with { agent?, task }; omit action. Omitted agent defaults to the built-in worker. Use action only for management/control. Use workflowScript only when the parent explicitly needs one durable scripted orchestration request.\n\nDIRECT EXECUTION:\n• Before executing, use { action: \"list\" } and run only executable/non-disabled agents.\n• { agent: \"reviewer\", task: \"Review the current diff for correctness.\" } runs one child and waits for the result.\n• Omit agent to use worker. Pass model and thinking to override this child. Pass context:\"fresh\" (default for worker) or context:\"fork\" when the child must branch the parent transcript.\n• The parent, not the child, owns the overall plan. For deliberate background or parallel work, make explicit async:true calls, then manage them with { action:\"status\" }, { action:\"steer\" }, { action:\"stop\" }, or subagent_wait.\n• For repository mutation lanes, use worktree:true with runs.run/runs.all for managed isolation.\n\nWORKFLOW SCRIPT (ADVANCED):\n• { workflowScript: \"return runs.run('main', {agent:'worker', task:'...'})\" } is the explicit scripted orchestration surface. Every workflow is a workflowScript; use stable-key runs.run for one child and runs.all for parallel children. workflowScript is an ordinary JavaScript statement body, so use an explicit return for a useful result. Scripts start asynchronously by default; pass async:false only for a small foreground workflow. Same-repo foreground workflows default to a live in-chat card; set chatProgress to auto, off, or live-card to control that projection. Workflow-level child controls default onto each runs.run launch, and explicit child fields override them.\n• Sequential example: { workflowScript: \"const a = await runs.run('analyze', {agent:'researcher', task:'Analyze the request'}); return (await runs.run('plan', {agent:'worker', task:'Plan from: '+a.output})).output\" }\n• Parallel example: { workflowScript: \"const [a,b] = await runs.all([{key:'correctness',agent:'reviewer',task:'Review correctness'},{key:'tests',agent:'reviewer',task:'Review tests'}]); return {correctness:a.output,tests:b.output}\" }\n• Durable mission attachment is automatic by default. Use missionId to attach an existing mission, mission:{...} to override auto-create, or mission:false for ephemeral work.\n\nMANAGEMENT / CONTROL:\n• list, get, models, children.list, create, update, delete, eject, disable, enable, reset, doctor, grant-spawn-budget, worktree.discard, refine/refine.show/refine.rollback, mission.create/list/show/update/attach-run/close, inspector.open/status/close, project.open/status/close, and watchdog actions remain available.\n• status, interrupt, stop, resume, and steer manage live or persisted runs. Use status view:\"fleet\" for an overview or view:\"transcript\" with id and optional index to tail output.\n• { action: \"append-step\", id: \"...\", step: {agent:\"agent-c\", task:\"Use {previous}\"} } appends one step to an already-running durable legacy chain. step is control-only, not a public execution mode.\n• Create durable project schedules with { action:\"schedule.create\", id?, name?, at:\"+10m\" | ISO, workflowScript:\"return runs.run('main', {agent:'worker', task:'...'})\" } or { every:\"6h\", workflowScript:\"...\" }. Manage them with schedule.list/show/history/pause/resume/run/run-due/delete.\n\n${SUBAGENT_SAFETY_GUIDANCE}`;\n\nexport const COMPACT_SUBAGENT_TOOL_DESCRIPTION = `Run one subagent directly with { agent?, task }; omit action. Omitted agent defaults to worker. Use action only for management/control.\n\nEXECUTE:\n• Call { action:\"list\" } first and use only executable/non-disabled agents.\n• DIRECT {agent:\"reviewer\",task:\"Review the diff\"} waits synchronously by default. Use {agent:\"worker\",task:\"...\",async:true} for deliberate background work; use subagent_wait when this turn needs the result.\n• The parent owns sequencing, lifecycle, and parallel fanout. Children are workers, not orchestrators; use contact_supervisor only for decisions, structured input, or meaningful progress updates.\n• model and thinking override the direct child. context can be fresh or fork. For repository mutation lanes, use worktree:true with runs.run/runs.all for managed isolation.\n\nWORKFLOW / MANAGE:\n• Use workflowScript for explicit scripted orchestration: runs.run for one child and runs.all for parallel work, plus retries and aggregation. Scripts are ordinary JavaScript statement bodies; use explicit return.\n• Use action without execution fields for list/get/models/authoring, mission, watchdog, status, interrupt, stop, resume, steer, scheduling, diagnostics, and other management actions.\n• Async workflow and child status live under asyncId/asyncDir with status.json, events.jsonl, output logs, and {action:\"status\",id:\"...\"}.\n\n${SUBAGENT_SAFETY_GUIDANCE}`;\n\nfunction isToolDescriptionMode(value: unknown): value is ToolDescriptionMode {\n\treturn value === \"full\" || value === \"compact\" || value === \"custom\";\n}\n\nfunction warn(options: ToolDescriptionOptions | undefined, message: string): void {\n\t(options?.warn ?? console.warn)(`[pi-subagents] ${message}`);\n}\n\nexport interface ToolDescriptionOptions {\n\tcwd?: string;\n\tagentDir?: string;\n\twarn?: (message: string) => void;\n}\n\nexport function resolveToolDescriptionMode(\n\tconfig: Pick<ExtensionConfig, \"toolDescriptionMode\">,\n\toptions?: ToolDescriptionOptions,\n): ToolDescriptionMode {\n\tconst mode = config.toolDescriptionMode;\n\tif (mode === undefined) return \"full\";\n\tif (isToolDescriptionMode(mode)) return mode;\n\twarn(\n\t\toptions,\n\t\t`Ignoring invalid toolDescriptionMode ${JSON.stringify(mode)}; expected \"full\", \"compact\", or \"custom\".`,\n\t);\n\treturn \"full\";\n}\n\nfunction customDescriptionPaths(options?: ToolDescriptionOptions): string[] {\n\tconst cwd = options?.cwd ?? process.cwd();\n\tconst agentDir = options?.agentDir ?? getAgentDir();\n\treturn [\n\t\tpath.join(getProjectConfigDir(cwd), CUSTOM_TOOL_DESCRIPTION_FILE),\n\t\tpath.join(agentDir, CUSTOM_TOOL_DESCRIPTION_FILE),\n\t];\n}\n\nfunction renderCustomTemplate(template: string, options?: ToolDescriptionOptions): string {\n\tconst cwd = options?.cwd ?? process.cwd();\n\tconst agentDir = options?.agentDir ?? getAgentDir();\n\tconst projectConfigDir = getProjectConfigDir(cwd);\n\tconst variables: Record<string, () => string> = {\n\t\tfullDescription: () => FULL_SUBAGENT_TOOL_DESCRIPTION,\n\t\tfull: () => FULL_SUBAGENT_TOOL_DESCRIPTION,\n\t\tcompactDescription: () => COMPACT_SUBAGENT_TOOL_DESCRIPTION,\n\t\tcompact: () => COMPACT_SUBAGENT_TOOL_DESCRIPTION,\n\t\tsafetyGuidance: () => SUBAGENT_SAFETY_GUIDANCE,\n\t\tsafety: () => SUBAGENT_SAFETY_GUIDANCE,\n\t\tagentDir: () => agentDir,\n\t\tprojectConfigDir: () => projectConfigDir,\n\t};\n\treturn template.replace(/\\{\\{(\\w+)\\}\\}/g, (raw, name: string) => {\n\t\tconst replacement = variables[name];\n\t\tif (replacement) return replacement();\n\t\twarn(options, `${CUSTOM_TOOL_DESCRIPTION_FILE}: unknown placeholder ${raw} left unchanged.`);\n\t\treturn raw;\n\t});\n}\n\nfunction loadCustomToolDescription(options?: ToolDescriptionOptions): string | undefined {\n\tfor (const filePath of customDescriptionPaths(options)) {\n\t\tlet stat: fs.Stats;\n\t\ttry {\n\t\t\tstat = fs.statSync(filePath);\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\ttypeof error === \"object\" &&\n\t\t\t\terror !== null &&\n\t\t\t\t\"code\" in error &&\n\t\t\t\t(error as NodeJS.ErrnoException).code === \"ENOENT\"\n\t\t\t)\n\t\t\t\tcontinue;\n\t\t\twarn(\n\t\t\t\toptions,\n\t\t\t\t`Failed to inspect custom tool description '${filePath}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!stat.isFile()) {\n\t\t\twarn(options, `Ignoring custom tool description '${filePath}' because it is not a file.`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (stat.size > CUSTOM_TOOL_DESCRIPTION_MAX_BYTES) {\n\t\t\twarn(\n\t\t\t\toptions,\n\t\t\t\t`Ignoring custom tool description '${filePath}' because it is larger than ${CUSTOM_TOOL_DESCRIPTION_MAX_BYTES} bytes.`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst template = fs.readFileSync(filePath, \"utf-8\").trim();\n\t\t\tif (!template) {\n\t\t\t\twarn(options, `Ignoring empty custom tool description '${filePath}'.`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst rendered = renderCustomTemplate(template, options).trim();\n\t\t\tif (!rendered) {\n\t\t\t\twarn(options, `Ignoring custom tool description '${filePath}' because it rendered empty.`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treturn rendered;\n\t\t} catch (error) {\n\t\t\twarn(\n\t\t\t\toptions,\n\t\t\t\t`Failed to read custom tool description '${filePath}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction withMandatorySafetyGuidance(description: string): string {\n\tconst customDescription = description\n\t\t.split(SUBAGENT_SAFETY_GUIDANCE)\n\t\t.map((part) => part.trim())\n\t\t.filter(Boolean)\n\t\t.join(\"\\n\\n\");\n\treturn customDescription ? `${customDescription}\\n\\n${SUBAGENT_SAFETY_GUIDANCE}` : SUBAGENT_SAFETY_GUIDANCE;\n}\n\nexport function buildSubagentToolDescription(\n\tconfig: Pick<ExtensionConfig, \"toolDescriptionMode\"> = {},\n\toptions?: ToolDescriptionOptions,\n): string {\n\tconst mode = resolveToolDescriptionMode(config, options);\n\tif (mode === \"compact\") return COMPACT_SUBAGENT_TOOL_DESCRIPTION;\n\tif (mode === \"custom\") {\n\t\tconst custom = loadCustomToolDescription(options);\n\t\tif (custom) return withMandatorySafetyGuidance(custom);\n\t\twarn(\n\t\t\toptions,\n\t\t\t`${CUSTOM_TOOL_DESCRIPTION_FILE} was not found or valid for toolDescriptionMode \"custom\"; using full description.`,\n\t\t);\n\t}\n\treturn FULL_SUBAGENT_TOOL_DESCRIPTION;\n}\n"]}