pi-ptc-subagents 0.1.1 → 0.1.2
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 +27 -0
- package/dist/index.d.ts +48 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -45
- package/dist/index.js.map +1 -1
- package/dist/{protocol-DLR50XxW.js → protocol-DUnAP1Ro.js} +12 -2
- package/dist/protocol-DUnAP1Ro.js.map +1 -0
- package/dist/worker.js +50 -6
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/protocol-DLR50XxW.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["dispatchBinding","DESCRIPTION","PARAMETERS"],"sources":["../src/runtime/dispatch.ts","../src/runtime/bindings.ts","../src/runtime/limits.ts","../src/runtime/worker-pool.ts","../src/runtime/worker-source.ts","../src/runtime/dispatcher.ts","../src/tools/text.ts","../src/tools/common.ts","../src/tools/render.ts","../src/tools/run-code.ts","../src/tools/workflow.ts","../src/runtime/turn-pools.ts","../src/mode/ptc-mode.ts","../src/mode/skills-section.ts","../src/index.ts"],"sourcesContent":["/**\n * pi.dispatch: a parallel binding that spawns a fresh pi subprocess per call.\n *\n * ADR-0016 (2026-09-23) is the contract. This file is the type surface and the spawn\n * skeleton; the actual subprocess plumbing (agent-md resolution, JSON-line parser,\n * usage accumulator, signal propagation across worker_threads / child_process) is a\n * follow-up. The stubs here are typed against the contract so call sites can land\n * before the spawn path is finalised, and so a wrong shape is a compile error rather\n * than a runtime one.\n *\n * Behaviourally compatible with pi's examples/extensions/subagent/index.ts reference\n * (--mode json, -p, --no-session, --append-system-prompt <tmpfile>), but not cooperative:\n * does not require the user to install pi's subagent extension. See ADR-0016 section 2.\n */\n\nimport { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-ai\";\n\n/**\n * The CLI command used to invoke a fresh pi subprocess for one dispatch.\n *\n * Always `\"pi\"` — pi-ptc is a library, not a CLI, and `dispatch` must spawn the\n * separate `pi` binary regardless of how the host process was launched. An\n * earlier copy of this function mirrored pi's own subagent extension and tried\n * to reuse `process.execPath + process.argv[1]`, which works only when the host\n * is `pi` itself; in any other host (test scripts, the dispatcher host process,\n * etc.) the function would spawn the host script in place of `pi`, producing\n * infinite recursion and a stdout pipe that never drains (so `proc.on('close')`\n * never fires). The caller surfaces a missing-binary condition via the child's\n * `error` event — `spawn` itself does not throw synchronously on ENOENT, the\n * async `error` listener does.\n */\nconst PI_COMMAND = \"pi\";\n\n/** Per-call input. Mirrors pi's subagent extension's parameters (single-mode only). */\nexport interface DispatchInput {\n agent: string;\n task: string;\n cwd?: string;\n agentScope?: \"user\" | \"project\" | \"both\";\n model?: string;\n thinkingLevel?: ThinkingLevel;\n}\n\n/** Token usage accumulated from the child's message_end events. Optional. */\nexport interface DispatchUsage {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n cost: number;\n turns: number;\n}\n\n/** Structured return value of pi.dispatch(...). Shape mirrors Promise.allSettled records. */\nexport interface DispatchResult {\n text: string;\n status: \"fulfilled\" | \"rejected\";\n agentName: string;\n durationMs: number;\n exitCode: number;\n usage?: DispatchUsage;\n stderr?: string;\n errorMessage?: string;\n}\n\n/** Discovery of an agent by name; mirrors pi's subagent extension AgentConfig shape. */\nexport interface AgentConfigLike {\n readonly name: string;\n readonly source: \"user\" | \"project\" | \"unknown\";\n readonly model?: string;\n readonly systemPrompt: string;\n readonly tools?: readonly string[];\n}\n\n/** Per-run context handed to a dispatch binding; same shape as BindingContext. */\nexport interface DispatchContext {\n signal?: AbortSignal;\n callId: number;\n /** Run cwd (F4); default for `DispatchInput.cwd` when not overridden. */\n cwd: string;\n /** Depth of the calling PTC run: 0 for the parent turn, 1+ for a child of `pi.dispatch`. */\n depth: number;\n /** Maximum allowed depth for any run reachable from this dispatch (ADR-0016 Recursive section). */\n maxDepth: number;\n}\n\n/**\n * Compose the system prompt handed to the child subprocess.\n *\n * The agent's own markdown body is the user-supplied content; pi-ptc appends a\n * `<pi-ptc-context depth=\"N\" max-depth=\"M\">...</pi-ptc-context>` hint so the child\n * agent can see how much recursion room it has. The shape mirrors DSH's existing\n * subagent-context injection so the field recognises it.\n */\nexport function appendDepthHint(systemPrompt: string, depth: number, maxDepth: number): string {\n const hint = [\n '<pi-ptc-context depth=\"' + depth + '\" max-depth=\"' + maxDepth + '\">',\n \"You are a PTC run at depth \" +\n depth +\n \" (root is depth 0). You may write PTC programs and you may\",\n \"call pi.dispatch(...) to spawn further children, but each level costs a fresh pi subprocess.\",\n \"The remaining depth budget is \" +\n maxDepth +\n \" - \" +\n depth +\n \". Beyond it, pi.dispatch rejects with\",\n '{ status: \"rejected\", errorMessage: \"dispatch depth limit reached\" }.',\n \"</pi-ptc-context>\",\n ].join(\"\\n\");\n return systemPrompt.length === 0 ? hint : systemPrompt + \"\\n\\n\" + hint;\n}\n\n/** Default error returned when the depth limit is exceeded. */\nexport function dispatchDepthLimitReached(currentDepth: number): DispatchResult {\n return {\n text: \"\",\n status: \"rejected\",\n agentName: \"unknown\",\n durationMs: 0,\n exitCode: -1,\n errorMessage:\n \"dispatch depth limit reached (current depth \" + currentDepth + \", max-depth exceeded)\",\n };\n}\n\n/**\n * Inputs to {@link decideCloseOutcome}: the shape the child's `close` event\n * hands the host, distilled to the fields that drive the resolve decision.\n */\nexport interface CloseOutcomeInput {\n /** `code ?? -1` from the child's close event. `-1` covers signal kill and no-code exit. */\n exitCode: number;\n /** Accumulated text from the last assistant message_end event. */\n finalText: string;\n /** `true` when `ctx.signal` aborted before this close fired. */\n aborted: boolean;\n}\n\n/** Result of {@link decideCloseOutcome} — what the dispatcher commits to. */\nexport interface CloseOutcome {\n status: \"fulfilled\" | \"rejected\";\n errorMessage?: string;\n}\n\n/**\n * Decide the outcome of one `dispatch(...)` based on the child's `close` event.\n *\n * Cancellation has priority over every other branch: when the host's signal\n * aborted the run, the child exited because we asked it to, and the contract\n * (ADR-0016 §4) is to surface \"dispatch cancelled\" rather than leave the\n * caller guessing why an aborted run came back `rejected`.\n *\n * Non-cancellation cases follow the original close-handler ladder:\n * - exit 0 with final text → fulfilled\n * - exit 0 without final text → rejected (the model never answered)\n * - any other exit code → rejected, no errorMessage (spawn-error path labels\n * its own failures with `failed to spawn pi: ...`)\n */\nexport function decideCloseOutcome(input: CloseOutcomeInput): CloseOutcome {\n if (input.aborted) {\n return { status: \"rejected\", errorMessage: \"dispatch cancelled\" };\n }\n if (input.exitCode === 0 && input.finalText.length > 0) {\n return { status: \"fulfilled\" };\n }\n if (input.exitCode === 0) {\n return { status: \"rejected\", errorMessage: \"dispatch produced no final text\" };\n }\n return { status: \"rejected\" };\n}\n\n/**\n * Minimal interface for the bits of `node:child_process`'s ChildProcess that\n * `safeKill` touches — narrow on purpose so tests can pass plain objects.\n *\n * `pid` is optional on `ChildProcess` (`pid?: number`); a fresh process whose\n * pid has not yet been assigned reports `undefined`, and the kill must be a\n * no-op rather than a TS error. `killed` is intentionally absent: callers read\n * it on the real ChildProcess directly, not through this helper's contract.\n */\nexport interface Killable {\n pid?: number | undefined;\n kill(signal: NodeJS.Signals): boolean;\n}\n\n/**\n * Send `signal` to `proc` iff the process is still attached, swallowing\n * \"process already gone\" throws. Returns whether a kill was actually issued.\n *\n * `proc.pid === undefined` happens for processes spawned without a usable\n * pid (rare, but TS-strict demands the guard); `proc.kill` throwing happens\n * for processes that exited between the guard and the syscall (the OS hands\n * back ESRCH). Both are \"no-op successfully\" for our purposes.\n */\nexport function safeKill(proc: Killable, signal: NodeJS.Signals): boolean {\n if (proc.pid === undefined) return false;\n try {\n proc.kill(signal);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Minimal YAML frontmatter scalar parser. Returns the trimmed value, with surrounding\n * double or single quotes stripped when both are present.\n */\nfunction extractYamlString(yaml: string, key: string): string | undefined {\n const m = yaml.match(new RegExp(\"^\" + key + \":\\\\s*(.+)$\", \"m\"));\n if (!m) return undefined;\n let v = (m[1] ?? \"\").trim();\n if (v.length >= 2) {\n const first = v[0];\n const last = v[v.length - 1];\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n v = v.slice(1, -1);\n }\n }\n return v.length === 0 ? undefined : v;\n}\n\n/**\n * Minimal YAML list parser: accepts inline `[a, b, c]` and bare `a, b, c`.\n */\nfunction extractYamlList(yaml: string, key: string): string[] | undefined {\n const m = yaml.match(new RegExp(\"^\" + key + \":\\\\s*(.+)$\", \"m\"));\n if (!m) return undefined;\n const raw = (m[1] ?? \"\").trim();\n let inner = raw;\n if (inner.startsWith(\"[\") && inner.endsWith(\"]\")) {\n inner = inner.slice(1, -1);\n }\n const parts = inner\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return parts.length === 0 ? undefined : parts;\n}\n\n/**\n * Parse an agent markdown file: YAML frontmatter (name / description / model / tools)\n * plus the body, returned as the system prompt. Tolerates a missing or partial\n * frontmatter; the caller falls back to defaults when this returns null.\n */\nexport function parseAgentMarkdown(content: string): {\n name: string;\n description?: string;\n model?: string;\n tools?: readonly string[];\n systemPrompt: string;\n} | null {\n const fmMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/);\n if (!fmMatch) return null;\n const frontmatter = fmMatch[1] ?? \"\";\n const body = fmMatch[2] ?? \"\";\n const name = extractYamlString(frontmatter, \"name\");\n if (!name) return null;\n const description = extractYamlString(frontmatter, \"description\") ?? \"\";\n const model = extractYamlString(frontmatter, \"model\") ?? \"\";\n const tools = extractYamlList(frontmatter, \"tools\");\n const out: {\n name: string;\n description?: string;\n model?: string;\n tools?: readonly string[];\n systemPrompt: string;\n } = {\n name,\n systemPrompt: body.trim(),\n };\n if (description !== undefined) out.description = description;\n if (model !== undefined) out.model = model;\n if (tools !== undefined) out.tools = tools;\n return out;\n}\n\n/**\n * Discover an agent by name. Mirrors pi's subagent extension agentScope behaviour:\n * user-level (`~/.pi/agent/agents/`) is loaded by default; project-level\n * (`<cwd>/.pi/agents/`) is loaded only when agentScope includes it.\n */\nexport function discoverAgent(\n name: string,\n cwd: string,\n agentScope: \"user\" | \"project\" | \"both\",\n): AgentConfigLike | null {\n const userDir = path.join(os.homedir(), \".pi\", \"agent\", \"agents\");\n const projectDir = path.join(cwd, \".pi\", \"agents\");\n type Candidate = { path: string; source: \"user\" | \"project\" };\n const candidates: Candidate[] = [];\n if (agentScope === \"user\" || agentScope === \"both\") {\n candidates.push({ path: path.join(userDir, name + \".md\"), source: \"user\" });\n }\n if (agentScope === \"project\" || agentScope === \"both\") {\n candidates.push({ path: path.join(projectDir, name + \".md\"), source: \"project\" });\n }\n for (const c of candidates) {\n let raw: string;\n try {\n raw = fs.readFileSync(c.path, \"utf-8\");\n } catch {\n continue;\n }\n const parsed = parseAgentMarkdown(raw);\n if (parsed && parsed.name === name) {\n const out: AgentConfigLike = {\n name: parsed.name,\n source: c.source,\n systemPrompt: parsed.systemPrompt,\n };\n if (parsed.model !== undefined) (out as { model?: string }).model = parsed.model;\n if (parsed.tools !== undefined) (out as { tools?: readonly string[] }).tools = parsed.tools;\n return out;\n }\n }\n return null;\n}\n\n/**\n * Build the argv handed to the child pi subprocess. Model / thinking / tools / agentScope\n * precedence: dispatch input (call site) > agent markdown frontmatter > nothing.\n * The agent's system prompt (with the depth hint appended) is materialised to a tmpfile\n * and passed via --append-system-prompt, exactly as the subagent extension does.\n */\nexport function buildArgv(\n input: DispatchInput,\n agent: AgentConfigLike,\n promptFilePath: string,\n): string[] {\n const args: string[] = [\"--mode\", \"json\", \"-p\", \"--no-session\"];\n const model = input.model ?? agent.model;\n if (model) args.push(\"--model\", model);\n /*\n * Default to --thinking off: a dispatched child is a tool call, not a long-form reasoning\n * task. Thinking blocks the model from emitting text for many seconds; the parent turn keeps\n * its own thinking settings, the child inherits only what the parent's DispatchInput.\n * thinkingLevel says (and defaults to \"off\" otherwise).\n */\n const thinking = input.thinkingLevel ?? \"off\";\n\n args.push(\"--thinking\", thinking);\n // Note: agentScope is honoured at our layer via discoverAgent(); pi itself does not\n // accept an --agent-scope flag (the subagent extension does not pass one either), and\n // in --no-session mode pi loads user-scope agents from ~/.pi/agent/agents by default.\n // Project agents would need a different mechanism (e.g. PI_CODING_AGENT_DIR override).\n if (agent.tools && agent.tools.length > 0) {\n args.push(\"--tools\", agent.tools.join(\",\"));\n }\n args.push(\"--append-system-prompt\", promptFilePath);\n args.push(\"Task: \" + input.task);\n return args;\n}\n\n/**\n * One line of the child's stdout, parsed as JSON. The child emits the events described\n * in examples/extensions/subagent/index.ts (message_end, tool_result_end, error).\n * Anything unparseable is dropped: the line is one event the host cannot interpret, and\n * the run continues.\n */\ninterface ParsedAgentEvent {\n type: string;\n message?: {\n role?: string;\n content?: ReadonlyArray<{ type?: string; text?: string }>;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n model?: string;\n stopReason?: string;\n errorMessage?: string;\n };\n message_text?: string;\n}\n\nexport function parseAgentEvent(line: string): ParsedAgentEvent | null {\n const trimmed = line.trim();\n if (trimmed.length === 0) return null;\n try {\n return JSON.parse(trimmed) as ParsedAgentEvent;\n } catch {\n return null;\n }\n}\n/**\n * Write the system prompt (with depth hint appended) to a tmpfile. The caller is\n * responsible for cleanup; we use mkdtemp + 0o600 to keep the prompt private, then\n * best-effort cleanup in the dispatch() Promise.\n */\nasync function writePromptToTempFile(\n agentName: string,\n prompt: string,\n): Promise<{ dir: string; filePath: string }> {\n const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"pi-dispatch-\"));\n const safeName = agentName.replace(/[^\\w.-]+/g, \"_\");\n const filePath = path.join(tmpDir, \"prompt-\" + safeName + \".md\");\n await fs.promises.writeFile(filePath, prompt, { encoding: \"utf-8\", mode: 0o600 });\n return { dir: tmpDir, filePath };\n}\n\n/**\n * Best-effort cleanup of the tmpfile + dir used for the system prompt. Errors are\n * deliberately swallowed: leaving a tmpfile around is preferable to crashing the run.\n */\nfunction cleanupTmp(tmp: { dir: string; filePath: string }): void {\n try {\n fs.unlinkSync(tmp.filePath);\n } catch {\n /* ignore */\n }\n try {\n fs.rmdirSync(tmp.dir);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Spawn one pi subprocess for one dispatch and resolve with a DispatchResult.\n *\n * Behavioural contract (ADR-0016 sections 1, 3, 4):\n * - Resolves with status: fulfilled when the child exits 0 and emits at least one\n * assistant message whose text is non-empty.\n * - Resolves with status: rejected on non-zero exit, signal kill, spawn failure,\n * unknown agent, or no usable final text.\n * - Never rejects. The caller can pattern-match on outcome.status the same way\n * it would pattern-match a Promise.allSettled record.\n * - Honours signal with SIGTERM, then SIGKILL after a 5s grace window.\n */\nexport async function dispatch(\n input: DispatchInput,\n ctx: DispatchContext,\n): Promise<DispatchResult> {\n // ADR-0016 Recursive dispatch: bound recursion explicitly.\n const childDepth = ctx.depth + 1;\n if (childDepth > ctx.maxDepth) {\n return dispatchDepthLimitReached(ctx.depth);\n }\n\n const cwd = input.cwd ?? ctx.cwd;\n const agentScope = input.agentScope ?? \"user\";\n const start = Date.now();\n\n const agent = discoverAgent(input.agent, cwd, agentScope);\n if (!agent) {\n return {\n text: \"\",\n status: \"rejected\",\n agentName: input.agent,\n durationMs: Date.now() - start,\n exitCode: 1,\n errorMessage:\n \"unknown agent: \" + input.agent + \" (agentScope=\" + agentScope + \", cwd=\" + cwd + \")\",\n };\n }\n\n const fullPrompt = appendDepthHint(agent.systemPrompt, childDepth, ctx.maxDepth);\n const tmp = await writePromptToTempFile(agent.name, fullPrompt);\n\n const argv = buildArgv(input, agent, tmp.filePath);\n\n return await new Promise<DispatchResult>((resolve) => {\n let stdoutBuffer = \"\";\n let stderrBuf = \"\";\n let finalText = \"\";\n let usage: DispatchUsage | undefined;\n let exitCode = -1;\n let resolved = false;\n let killTimer: NodeJS.Timeout | undefined;\n let proc: ReturnType<typeof spawn> | undefined;\n let aborted = false;\n\n const finalize = (status: \"fulfilled\" | \"rejected\", errorMessage?: string): void => {\n if (resolved) return;\n resolved = true;\n if (killTimer) clearTimeout(killTimer);\n if (ctx.signal) ctx.signal.removeEventListener(\"abort\", onAbort);\n cleanupTmp(tmp);\n const out: DispatchResult = {\n text: finalText,\n status,\n agentName: agent.name,\n durationMs: Date.now() - start,\n exitCode,\n };\n if (usage) out.usage = usage;\n if (stderrBuf.length > 0) out.stderr = stderrBuf;\n if (errorMessage) out.errorMessage = errorMessage;\n resolve(out);\n };\n\n const onAbort = (): void => {\n if (!proc || proc.killed || resolved) return;\n aborted = true;\n safeKill(proc, \"SIGTERM\");\n killTimer = setTimeout(() => {\n if (proc && !proc.killed) safeKill(proc, \"SIGKILL\");\n }, 5000);\n };\n\n try {\n proc = spawn(PI_COMMAND, argv, {\n cwd,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n finalize(\"rejected\", \"failed to spawn pi: \" + message);\n return;\n }\n\n if (ctx.signal) {\n if (ctx.signal.aborted) onAbort();\n else ctx.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n const stdout = proc.stdout;\n if (!stdout) return;\n stdout.on(\"data\", (data: Buffer) => {\n stdoutBuffer += data.toString(\"utf-8\");\n let nl = stdoutBuffer.indexOf(\"\\n\");\n while (nl >= 0) {\n const line = stdoutBuffer.slice(0, nl);\n stdoutBuffer = stdoutBuffer.slice(nl + 1);\n const ev = parseAgentEvent(line);\n if (!ev) {\n nl = stdoutBuffer.indexOf(\"\\n\");\n continue;\n }\n if (ev.type === \"message_end\" && ev.message && ev.message.role === \"assistant\") {\n const m = ev.message;\n if (m.usage) {\n const cur: DispatchUsage = usage ?? {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n cost: 0,\n turns: 0,\n };\n cur.input += m.usage.input ?? 0;\n cur.output += m.usage.output ?? 0;\n cur.cacheRead += m.usage.cacheRead ?? 0;\n cur.cacheWrite += m.usage.cacheWrite ?? 0;\n cur.cost += m.usage.cost?.total ?? 0;\n cur.turns += 1;\n usage = cur;\n }\n if (Array.isArray(m.content)) {\n for (const part of m.content) {\n if (part && part.type === \"text\" && typeof part.text === \"string\") {\n finalText = part.text;\n }\n }\n }\n }\n nl = stdoutBuffer.indexOf(\"\\n\");\n }\n });\n\n const stderrStream = proc.stderr;\n if (!stderrStream) return;\n stderrStream.on(\"data\", (data: Buffer) => {\n stderrBuf += data.toString(\"utf-8\");\n });\n\n proc.on(\"close\", (code) => {\n exitCode = code ?? -1;\n const { status, errorMessage } = decideCloseOutcome({ exitCode, finalText, aborted });\n finalize(status, errorMessage);\n });\n\n proc.on(\"error\", (err) => {\n stderrBuf += \"[spawn-error] \" + err.message + \"\\n\";\n finalize(\"rejected\", \"failed to spawn pi: \" + err.message);\n });\n });\n}\n","/**\n * Bindings: the pi tools a PTC program may call as `tools.<name>(args)`.\n *\n * R5 found that pi's extension API exposes no tool-dispatch surface — `pi.on('tool_call')`\n * only fires from the agent loop, `extensionsResult.runtime` is state plumbing, and\n * `pi.exec` has no `execArgv`/`resourceLimits`/`stdio`. So a binding builds the tool with\n * pi's own SDK factory and calls `execute()` on it directly. That keeps the tool\n * implementation identical to the native one, but it also means worker-driven calls are\n * **not** seen by pi's tool-call pipeline: `tool_call` guards, `protected-paths`-style\n * extensions, permission gates and sandbox overrides never run for them. This is the\n * inherited risk ADR-0005 accepts and records — it must not be described as resolved, and\n * nothing here may pretend to route through `pi.on('tool_call')`.\n *\n * Arguments are validated with pi's own `validateToolArguments`, the same helper the agent\n * loop uses, so a bad call fails with the same message (and the same coercion) it would\n * get from the native pipeline.\n */\nimport { validateToolArguments } from \"@earendil-works/pi-ai\";\nimport {\n createBashTool,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadTool,\n createWriteTool,\n} from \"@earendil-works/pi-coding-agent\";\nimport type { TSchema } from \"typebox\";\n\nimport { dispatch as dispatchBinding, type DispatchInput } from \"./dispatch.ts\";\n\n/** pi's built-in tools that can be exposed as bindings, in native order. */\nexport const BUILTIN_BINDING_NAMES = [\n \"read\",\n \"bash\",\n \"edit\",\n \"write\",\n \"grep\",\n \"find\",\n \"ls\",\n] as const;\nexport type BuiltinBindingName = (typeof BUILTIN_BINDING_NAMES)[number];\n\n/**\n * Bindings exposed when the caller does not pass an explicit name list.\n *\n * `bash` is included on purpose: DSH's PTC preset still mounts `tool-bash`/`tool-pwsh`\n * (R1 §2 — PTC mode hides the tools from the wire list and re-exposes the same registry\n * as bindings), so a PTC program written against DSH may run shell commands. Leaving it\n * out would silently shrink the surface relative to DSH. Callers that want a read-only\n * PTC surface pass an explicit subset.\n */\n/** Parallel binding name (ADR-0016). Always bound alongside the builtin set;\n * opt-out is the callers responsibility via an explicit subset to\n * createBuiltinBindings (today the subset is restricted to builtin names,\n * so opt-out is effectively use a future flag). */\nexport const DISPATCH_BINDING_NAME = \"pi.dispatch\" as const;\n\nexport const DEFAULT_BINDING_NAMES: readonly BuiltinBindingName[] = BUILTIN_BINDING_NAMES;\n\nexport interface BindingContext {\n /** Aborted when the run is cancelled, times out, or settles. */\n signal?: AbortSignal;\n /** Wire call id; also used to build the tool call id the tools see. */\n callId: number;\n /** Depth of the current PTC run (0 for parent turn, 1+ for a child of `pi.dispatch`). */\n depth: number;\n /** Maximum allowed depth; passed through to `pi.dispatch` for the depth check. */\n maxDispatchDepth: number;\n}\n\nexport interface Binding {\n readonly name: string;\n execute(args: unknown, context: BindingContext): Promise<unknown>;\n}\n\nexport type BindingTable = ReadonlyMap<string, Binding>;\n\n/**\n * Structural view of an SDK tool, wide enough for every `createXxxTool(cwd)` factory.\n *\n * `params: never` is what makes the seven typed factories assignable to one shape: a\n * function that takes `Static<typeof readSchema>` accepts a `never`, and `never` is what\n * the wire hands us before validation.\n */\ninterface AnyBuiltinTool {\n readonly name: string;\n readonly description: string;\n readonly parameters: TSchema;\n execute(\n toolCallId: string,\n params: never,\n signal?: AbortSignal,\n onUpdate?: unknown,\n ): Promise<{ content?: unknown; details?: unknown }>;\n}\n\nconst BUILTIN_TOOL_FACTORIES: Record<BuiltinBindingName, (cwd: string) => AnyBuiltinTool> = {\n read: createReadTool,\n bash: createBashTool,\n edit: createEditTool,\n write: createWriteTool,\n grep: createGrepTool,\n find: createFindTool,\n ls: createLsTool,\n};\n\n/** Argument type of pi's `validateToolArguments`, i.e. pi-ai's `ToolCall`. */\ntype ToolCallLike = Parameters<typeof validateToolArguments>[1];\n\nexport interface CreateBuiltinBindingsOptions {\n /** Per-run working directory: the F4 `RunConfig.cwd`, used to build every tool. */\n cwd: string;\n /** Subset of {@link BUILTIN_BINDING_NAMES}; defaults to all of them (bash included). */\n names?: readonly string[];\n}\n\n/**\n * Build the binding table for one run.\n *\n * Tools are created once per table and reused across calls, matching pi's own extension\n * examples (`createXxxTool(cwd)` caches nothing internally, so one instance per run is\n * the intended granularity).\n */\nexport function createBuiltinBindings(options: CreateBuiltinBindingsOptions): BindingTable {\n const names = options.names ?? DEFAULT_BINDING_NAMES;\n const table = new Map<string, Binding>();\n for (const name of names) {\n const factory = BUILTIN_TOOL_FACTORIES[name as BuiltinBindingName];\n if (!factory) {\n throw new TypeError(\n `unknown PTC binding \"${name}\"; known bindings: ${BUILTIN_BINDING_NAMES.join(\", \")}`,\n );\n }\n const tool = factory(options.cwd);\n table.set(name, {\n name,\n execute: async (args, context) => {\n const toolCallId = `ptc:${context.callId}`;\n const toolCall = {\n type: \"toolCall\",\n id: toolCallId,\n name: tool.name,\n arguments: args,\n } as unknown as ToolCallLike;\n const validated = validateToolArguments(tool, toolCall);\n const result = await tool.execute(\n toolCallId,\n validated as never,\n context.signal,\n undefined,\n );\n // What crosses the wire is the tool's model-facing payload: `usage` and\n // `terminate` are agent-loop plumbing with no meaning inside a PTC program.\n return {\n content: result.content,\n details: result.details === undefined ? null : result.details,\n };\n },\n });\n }\n // Register the parallel binding alongside the builtin set (ADR-0016).\n // The binding is added unconditionally when the caller accepts the default set,\n // but an explicit subset (the read-only PTC surface pattern, R3) is honoured:\n // `pi.dispatch` is not mixed into a caller-curated list, because the caller\n // has signalled they want a specific surface.\n if (names === DEFAULT_BINDING_NAMES) {\n table.set(DISPATCH_BINDING_NAME, {\n name: DISPATCH_BINDING_NAME,\n execute: async (args, context) => {\n return dispatchBinding(args as DispatchInput, {\n signal: context.signal,\n callId: context.callId,\n cwd: options.cwd,\n depth: context.depth,\n maxDepth: context.maxDispatchDepth,\n });\n },\n });\n }\n return table;\n}\n","/**\n * PTC run limits and spawn-time hardening, in one frozen `DEFAULT_CONFIG`.\n *\n * The numbers are DSH's (`dsh-v0.1.6-alpha.2`, `@deepseek-ai/dsh-ptc-runtime-node`)\n * carried over verbatim — see ADR-0003 (output budget), ADR-0004 (pending calls) and\n * ADR-0005 (execution boundary, F1–F4). Tests assert against these constants rather\n * than repeating the literals, so a future re-sync only has to change this file.\n *\n * Deliberately absent:\n * - `syncTimeoutMs` / `maxConcurrentAgents` / `maxTotalAgents` — workflow-engine caps\n * for a cooperative VM and for `agent()`. We run the program directly in the worker\n * realm (no VM) and ship no `agent()` (G1 #13 → B), so there is nothing to cap.\n * - `sandbox-unavailable` is not an error kind either: ADR-0007 ships no OS sandbox.\n */\n\n/** The two worker surfaces. A worker is spawned with exactly one of them and keeps it. */\nexport type PtcSurface = \"run_code\" | \"workflow\";\n\nexport interface PtcConfig {\n /** Default elapsed deadline for a run, including nested binding waits (R1 §1). */\n timeoutMs: number;\n /** Ceiling the requested deadline is clamped to. */\n maxTimeoutMs: number;\n /** Joint budget for serialized logs + completion value (ADR-0003). */\n maxOutputBytes: number;\n /** Cap on a single control frame, either direction (R1 §1). */\n maxMessageBytes: number;\n /** Admission control for simultaneously in-flight worker→host binding calls (ADR-0004). */\n maxPendingCalls: number;\n /** Concurrent binding dispatches; DSH's `maxParallelSubCalls` (ADR-0004 consequence).\n * Renamed in spirit by ADR-0016 section 2: the cap that really matters for\n * resource safety is the per-run `dispatchConcurrency` below. This field\n * is kept for backward compatibility (and for the in-process builtin\n * binding fan-out) but is no longer the authoritative limit on the\n * parallel binding `pi.dispatch`. */\n maxParallelSubCalls: number;\n /** Per-run hard cap on concurrently in-flight `pi.dispatch(...)` calls.\n * Default 8, matches pi's `subagent` extension `MAX_PARALLEL_TASKS`.\n * ADR-0016 section 2. */\n dispatchConcurrency: number;\n /** Maximum recursion depth for `pi.dispatch`. The child PTC run spawned by\n * the (depth+1)-th dispatch is allowed only when childDepth <= maxDispatchDepth.\n * Default 3. Aligns with dsh's `SubagentCapabilities.depthLimit` and codex's\n * `agent_max_depth`. ADR-0016 Recursive dispatch section. */\n maxDispatchDepth: number;\n /** Items accepted by a single `parallel()` / `pipeline()` call (R1 §1, workflow-side; pinned\n * here because it guards a helper call rather than an agent budget). */\n maxItemsPerCall: number;\n /** Cooperative-cancel grace before the worker is terminated outright. */\n graceMs: number;\n /** V8 old-generation cap handed to `new Worker({ resourceLimits })` (F2). */\n maxOldGenerationSizeMb: number;\n /** V8 young-generation cap handed to `new Worker({ resourceLimits })` (F2). */\n maxYoungGenerationSizeMb: number;\n /**\n * Per-turn worker pool capacity (ADR-0017 §3). Decoupled from `maxParallelSubCalls`\n * because pool capacity is \"resident workers\" while `maxParallelSubCalls` is\n * \"in-flight calls\" — two different ceilings. Default 4.\n */\n poolSize: number;\n /**\n * How long `runPtcProgram({ pool })` will wait for an available worker before\n * failing the run with `kind: workerExit`. Decoupled from `timeoutMs` because\n * acquire-wait is bounded by the pool's responsiveness, not the run's overall\n * deadline. Default 30 000 ms.\n */\n poolAcquireTimeoutMs: number;\n /**\n * Ceiling on how long a pool's `drain()` waits for in-flight workers to release\n * themselves before terminating them outright: a worker whose `release()` never\n * arrives (a stuck dispatcher promise, an unobserved crash) must bound the turn-end\n * hook rather than hang it. Not `graceMs` above, which is a *run's* cooperative-cancel\n * window; this one is the *pool's* window at retirement, and drain resolves either way.\n * Default 5 000 ms.\n */\n drainGraceMs: number;\n}\n\nexport const DEFAULT_CONFIG: Readonly<PtcConfig> = Object.freeze({\n timeoutMs: 120_000,\n maxTimeoutMs: 600_000,\n maxOutputBytes: 67_108_864,\n maxMessageBytes: 134_217_728,\n maxPendingCalls: 128,\n maxParallelSubCalls: 10,\n dispatchConcurrency: 8,\n maxDispatchDepth: 3,\n maxItemsPerCall: 4_096,\n graceMs: 3_000,\n maxOldGenerationSizeMb: 512,\n maxYoungGenerationSizeMb: 64,\n poolSize: 4,\n poolAcquireTimeoutMs: 30_000,\n drainGraceMs: 5_000,\n});\n\n/**\n * F1 — the only environment variables a PTC worker may inherit.\n *\n * These are what a shell needs to resolve binaries on each platform; everything\n * else the host has (tokens, API keys, proxies, home paths) stays out of the worker.\n */\nexport const WORKER_ENV_ALLOW_LIST: readonly string[] = Object.freeze([\n \"PATH\",\n \"PATHEXT\",\n \"SYSTEMROOT\",\n \"WINDIR\",\n \"TEMP\",\n \"TMP\",\n]);\n\n/**\n * Build the per-run environment snapshot: the allow-list entries that actually exist.\n *\n * Unset or empty entries are dropped rather than passed through as empty strings, so\n * `Object.keys(workerEnv)` is exactly what the worker sees in `process.env`.\n */\nexport function createWorkerEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> {\n const env: Record<string, string> = {};\n for (const name of WORKER_ENV_ALLOW_LIST) {\n const value = source[name];\n if (typeof value === \"string\" && value.length > 0) env[name] = value;\n }\n return env;\n}\n\n/**\n * Merge per-run overrides onto `DEFAULT_CONFIG`.\n *\n * Overrides are validated rather than coerced: a negative or non-numeric limit is a\n * caller bug, and silently substituting a default would hide it.\n */\nexport function resolveConfig(overrides: Partial<PtcConfig> = {}): PtcConfig {\n const resolved: PtcConfig = { ...DEFAULT_CONFIG };\n for (const key of Object.keys(DEFAULT_CONFIG) as Array<keyof PtcConfig>) {\n const override = overrides[key];\n if (override === undefined) continue;\n if (typeof override !== \"number\" || !Number.isFinite(override) || override <= 0) {\n throw new TypeError(`invalid PTC config override for ${key}: ${String(override)}`);\n }\n resolved[key] = override;\n }\n return resolved;\n}\n\n/**\n * Resolve the effective deadline for one run.\n *\n * DSH semantics (R1 §3): `0` does not disable the deadline, it falls back to the\n * default; anything larger is clamped to `maxTimeoutMs`.\n */\nexport function effectiveTimeoutMs(\n requested: number | undefined,\n config: PtcConfig = DEFAULT_CONFIG,\n): number {\n if (requested === undefined || !Number.isFinite(requested) || requested <= 0)\n return config.timeoutMs;\n return Math.min(requested, config.maxTimeoutMs);\n}\n","/**\n * Per-turn worker pool: keeps `worker_threads` Workers warm across PTC runs so the\n * second `ptc_run_code` of a turn does not pay the V8 spin-up + module-graph load cost\n * the first one did (ADR-0017).\n *\n * The pool is created by the pi extension hook at turn entry, passed to\n * `runPtcProgram()` via `RunPtcProgramOptions.pool`, and drained at turn exit. Two\n * surfaces (`run_code`, `workflow`) do not share a pool: their worker programs are\n * different (`installWorkflowHelpers` in `worker-main.ts`), and one pool per surface\n * is the cleanest ownership story.\n *\n * Capacity model\n * ──────────────\n * \"Resident workers\" ≤ `size`, \"in-flight calls\" ≤ unbounded but queued when the pool\n * is full. An acquire when the pool is at capacity joins a FIFO wait list; a waiter is\n * served the instant an `acquire()` resolves with the released worker — the released\n * worker is handed directly to the next waiter rather than going through `idle`. This\n * matches the host's own `acquireDispatchSlot` semantics: the queue is the throughput\n * story, not the rejection story.\n *\n * Waiters time out after `acquireTimeoutMs`; on timeout the run fails with\n * `kind: workerExit` and a message naming the pool (ADR-0017 W-2 / §4).\n *\n * `drain()` — awaited by the pi extension hook at turn end — gives in-flight runs\n * `drainGraceMs` (default 5 000 ms) to release their worker. Whatever is still in flight\n * when the grace expires is terminated together with the idle workers: a worker whose\n * `release()` never arrives (a stuck dispatcher promise, a crash no one observed) must\n * bound the turn-end hook, not hang it. Drain therefore always resolves — expiry is not\n * an error, because a turn boundary should not fail over one bad worker.\n *\n * Health\n * ──────\n * A worker that has emitted `exit` is retired — it never re-enters `idle`. The pool\n * detects exit on the next `release()`: if `worker.threadId === -1` (Node marks a\n * terminated worker with a sentinel threadId) the worker is dropped rather than kept\n * idle. This keeps a worker that crashed between two runs from being handed out again.\n */\nimport { Worker } from \"node:worker_threads\";\nimport type { WorkerOptions } from \"node:worker_threads\";\nimport diagnosticsChannel from \"node:diagnostics_channel\";\nimport type { PtcConfig, PtcSurface } from \"./limits.ts\";\n\n/** Options the dispatcher hands to `new Worker(...)`. */\nexport type WorkerPoolWorkerOptions = ConstructorParameters<typeof Worker>[1];\n\n/**\n * Build the `Worker` options for one spawn (ADR-0005 F1–F3 hardening in one place).\n *\n * `runId` is optional and deliberately absent on the pooled path: a warm worker serves many runs,\n * so its spawn-time `workerData` cannot carry any single run's identity (each run's `runId`\n * travels in its `init` frame instead).\n */\nexport function workerSpawnOptions(options: {\n surface: PtcSurface;\n env: Record<string, string>;\n limits: Pick<PtcConfig, \"maxOldGenerationSizeMb\" | \"maxYoungGenerationSizeMb\">;\n runId?: string;\n}): WorkerPoolWorkerOptions {\n return {\n name: `ptc-${options.surface}`,\n // F1: allow-list only — never the host's full environment (ADR-0005).\n env: options.env,\n // F3: the frozen snapshot the worker records. On the cold path that snapshot also names the\n // run; on the pooled path it cannot (see the doc comment above).\n workerData:\n options.runId === undefined\n ? { env: options.env }\n : { runId: options.runId, env: options.env },\n // F2: V8 caps (ADR-0005).\n resourceLimits: {\n maxOldGenerationSizeMb: options.limits.maxOldGenerationSizeMb,\n maxYoungGenerationSizeMb: options.limits.maxYoungGenerationSizeMb,\n },\n };\n}\n\nexport interface WorkerPoolOptions {\n /**\n * Build a fresh worker URL for each spawn. The pool calls this only when it actually\n * needs to grow the resident count, so a heavy `buildWorkerUrl` is amortised across\n * warm reuses.\n */\n buildWorkerUrl: () => URL;\n /** Maximum resident workers; acquire when full queues the caller. Default 4. */\n size?: number;\n /** Acquire-wait bound; default 30 000 ms. */\n acquireTimeoutMs?: number;\n /**\n * Bound on how long `drain()` waits for in-flight workers to be released before it\n * terminates them anyway. Default 5 000 ms. Drain resolves either way — expiry is not\n * an error, it just means a worker was retired without a clean release.\n */\n drainGraceMs?: number;\n /** Options passed verbatim to `new Worker(url, options)` when spawning. */\n workerOptions: WorkerPoolWorkerOptions;\n}\n\n/**\n * Internal acquisition waiter. The `resolve` hands the worker directly to the caller's\n * `release` chain (no idle round-trip) when capacity opens up.\n */\ninterface PoolWaiter {\n resolve: (worker: Worker) => void;\n reject: (error: unknown) => void;\n timer: NodeJS.Timeout;\n startedAt: number;\n}\n\n/**\n * Diagnostics channels the pool publishes to (ADR-0017 W-5, Addendum). Channels have\n * zero subscribers by default — `diagnostics_channel.channel(...)` always returns the\n * same instance, but `publish` short-circuits when no one is listening, so this is the\n * documented cheap path.\n */\nconst channels = {\n acquireLatency: diagnosticsChannel.channel(\"ptc:pool:acquire-latency\"),\n resetTime: diagnosticsChannel.channel(\"ptc:worker:reset-time\"),\n imageBytes: diagnosticsChannel.channel(\"ptc:image:hoist-bytes\"),\n} as const;\n\n/** `node:diagnostics_channel` doesn't export its Channel type; this is the public surface we use. */\ntype DiagnosticsChannel = {\n publish: (message: unknown) => void;\n};\n\nfunction publish(channel: DiagnosticsChannel, message: unknown): void {\n // Node short-circuits `publish` when the channel has no subscribers, so this is a\n // no-op in production. Subscribers are e2e tests and future instrumentation.\n channel.publish(message);\n}\n\nexport interface WorkerPoolStats {\n resident: number;\n inFlight: number;\n waiters: number;\n totalAcquires: number;\n poolExhaustions: number;\n}\n\n/**\n * A fixed-capacity FIFO-served worker pool. Not safe for concurrent `acquire()` calls\n * beyond Node's microtask interleaving guarantees — callers (`runPtcProgram`) await\n * `acquire` before scheduling anything that touches the pool, so the only concurrent\n * surface is `release()` against an in-flight worker.\n */\nexport class WorkerPool {\n readonly #buildWorkerUrl: () => URL;\n readonly #size: number;\n readonly #acquireTimeoutMs: number;\n readonly #drainGraceMs: number;\n readonly #workerOptions: WorkerPoolWorkerOptions;\n\n readonly #idle: Worker[] = [];\n readonly #inFlight: Set<Worker> = new Set();\n readonly #waiters: PoolWaiter[] = [];\n /**\n * When each worker was last released (i.e., the previous run settled). Used by the\n * dispatcher to publish `ptc:worker:reset-time` when a warm worker emits its first\n * `ready` frame — the time between settle and reset-complete is the worker's\n * per-run reset cost (ADR-0017 Addendum).\n */\n readonly #lastSettledAt = new WeakMap<Worker, number>();\n #totalAcquires = 0;\n #poolExhaustions = 0;\n /**\n * `true` once `drain()` has resolved. After this point every `acquire()` rejects\n * — the pool is single-use, the parent turn owns it for one cycle (ADR-0017 §1).\n */\n #drained = false;\n\n constructor(options: WorkerPoolOptions) {\n if (typeof options.buildWorkerUrl !== \"function\") {\n throw new TypeError(\"WorkerPool: buildWorkerUrl must be a function\");\n }\n if (\n options.size !== undefined &&\n (typeof options.size !== \"number\" || !Number.isFinite(options.size) || options.size <= 0)\n ) {\n throw new TypeError(\n `WorkerPool: size must be a positive finite number, received ${String(options.size)}`,\n );\n }\n if (\n options.acquireTimeoutMs !== undefined &&\n (typeof options.acquireTimeoutMs !== \"number\" ||\n !Number.isFinite(options.acquireTimeoutMs) ||\n options.acquireTimeoutMs <= 0)\n ) {\n throw new TypeError(\n `WorkerPool: acquireTimeoutMs must be a positive finite number, received ${String(options.acquireTimeoutMs)}`,\n );\n }\n if (\n options.drainGraceMs !== undefined &&\n (typeof options.drainGraceMs !== \"number\" ||\n !Number.isFinite(options.drainGraceMs) ||\n options.drainGraceMs <= 0)\n ) {\n throw new TypeError(\n `WorkerPool: drainGraceMs must be a positive finite number, received ${String(options.drainGraceMs)}`,\n );\n }\n this.#buildWorkerUrl = options.buildWorkerUrl;\n this.#size = options.size ?? 4;\n this.#acquireTimeoutMs = options.acquireTimeoutMs ?? 30_000;\n this.#drainGraceMs = options.drainGraceMs ?? 5_000;\n this.#workerOptions = options.workerOptions;\n }\n\n /**\n * Acquire an idle worker, spawn a new one if below capacity, or queue until capacity\n * opens up. Throws `Error(\"pool acquire timed out after ${acquireTimeoutMs} ms\")` on\n * timeout; the dispatcher wraps that message with a `pool acquire failed: ` prefix (ADR-0017 §4).\n */\n async acquire(): Promise<Worker> {\n if (this.#drained) {\n throw new Error(\"pool has been drained and can no longer serve workers\");\n }\n // One logical acquire, one increment: retiring an idle worker below must not\n // re-enter this method (that would double-count, and `stats()` is the fact source\n // for tests and diagnostics).\n this.#totalAcquires += 1;\n const startedAt = Date.now();\n\n // Fast path: idle worker ready. Move it to in-flight and return. Skip (and drop) any\n // worker that has already exited — the `'exit'` listener normally removes it, but the\n // event is async, so a worker can exit between the listener and this shift.\n let idle = this.#idle.shift();\n while (idle !== undefined && !this.#isHealthy(idle)) {\n idle = this.#idle.shift();\n }\n if (idle !== undefined) {\n this.#inFlight.add(idle);\n // An idle worker is parked by `unref()` so a warm pool never keeps the host\n // process alive; taking it back into flight means the run depends on it, so\n // re-ref it before handing it over.\n idle.ref();\n publish(channels.acquireLatency, {\n poolSize: this.#size,\n waiters: this.#waiters.length,\n durationMs: Date.now() - startedAt,\n });\n return idle;\n }\n\n // Spawn path: capacity available.\n if (this.#inFlight.size < this.#size) {\n const worker = this.#spawn();\n this.#inFlight.add(worker);\n publish(channels.acquireLatency, {\n poolSize: this.#size,\n waiters: this.#waiters.length,\n durationMs: Date.now() - startedAt,\n });\n return worker;\n }\n\n // Wait path: queue a waiter with timeout.\n this.#poolExhaustions += 1;\n return await new Promise<Worker>((resolve, reject) => {\n const timer = setTimeout(() => {\n const index = this.#waiters.findIndex((entry) => entry.timer === timer);\n if (index >= 0) this.#waiters.splice(index, 1);\n reject(new Error(`pool acquire timed out after ${this.#acquireTimeoutMs} ms`));\n }, this.#acquireTimeoutMs);\n this.#waiters.push({ resolve, reject, timer, startedAt });\n });\n }\n\n /**\n * Hand a worker back to the pool. If a waiter is queued, hand the worker directly to\n * them (no idle round-trip). Otherwise park the worker in `idle` — unless it has\n * already exited, in which case retire it.\n */\n release(worker: Worker): void {\n if (!this.#inFlight.delete(worker)) {\n // Not tracked as in-flight (release called twice, or never acquired from us).\n // Be lenient: don't throw, just ignore. The dispatcher only releases what it\n // acquired.\n return;\n }\n if (!this.#isHealthy(worker)) {\n // Crashed since acquire: drop it. The next acquire will spawn a fresh one\n // (capacity accounting already removed it from in-flight above).\n return;\n }\n this.#lastSettledAt.set(worker, Date.now());\n const waiter = this.#waiters.shift();\n if (waiter) {\n clearTimeout(waiter.timer);\n this.#inFlight.add(worker);\n publish(channels.acquireLatency, {\n poolSize: this.#size,\n waiters: this.#waiters.length,\n // The waiter's queue wait, measured from when it entered `acquire()`. This is\n // the number the channel exists to report: how long a caller waited for capacity.\n durationMs: Date.now() - waiter.startedAt,\n });\n waiter.resolve(worker);\n return;\n }\n this.#idle.push(worker);\n // Park the idle worker so the host process can exit with a warm pool still standing\n // (a ref'd Worker keeps Node's event loop alive). The next `acquire()` re-refs it.\n worker.unref();\n }\n\n /**\n * Last time `release()` was called for `worker`. The dispatcher reads this to\n * publish `ptc:worker:reset-time` on a warm `ready` frame: `now - lastSettledAt`\n * is the worker's per-run reset cost (ADR-0017 Addendum). `undefined` for a worker\n * the pool never released (cold-start).\n */\n lastSettledAt(worker: Worker): number | undefined {\n return this.#lastSettledAt.get(worker);\n }\n\n /**\n * Wait for every in-flight worker to be released — bounded by `drainGraceMs` — then\n * terminate every resident worker and reject every queued waiter. After `drain()`\n * returns the pool is unusable.\n *\n * The bound matters: `drain()` is awaited from the pi `turn_end` hook, so a worker\n * whose `release()` never arrives (a stuck dispatcher promise, an unobserved crash)\n * must not hold the turn open. Workers still in flight when the grace expires are\n * terminated with the idle ones, and `drain()` resolves normally — a single bad worker\n * is not a reason to fail the turn boundary.\n */\n async drain(): Promise<void> {\n // Reject any queued waiter up front: the pool is going away.\n while (this.#waiters.length > 0) {\n const waiter = this.#waiters.shift();\n if (!waiter) break;\n clearTimeout(waiter.timer);\n waiter.reject(new Error(\"pool drained before acquire could be satisfied\"));\n }\n // Wait for in-flight to settle — drain resolves only after every active run released.\n // Callers (the pi extension hook) hand the pool to `runPtcProgram` and await all\n // those promises before `drain`, so this is a defensive barrier for tests. The\n // deadline makes the barrier bounded; polling is deliberate (simple, and 5 ms is\n // nothing against a turn boundary).\n const deadline = Date.now() + this.#drainGraceMs;\n while (this.#inFlight.size > 0 && Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 5));\n }\n // Terminate every resident worker: idle ones always, plus any in-flight worker that\n // outlived the grace. Clearing `#inFlight` first keeps `stats()` honest — a stuck\n // worker is no longer part of what the pool will serve.\n const terminating = [...this.#idle, ...this.#inFlight];\n this.#idle.length = 0;\n this.#inFlight.clear();\n await Promise.all(terminating.map((worker) => worker.terminate().catch(() => undefined)));\n this.#drained = true;\n }\n\n /** Read-only snapshot of the pool's counters; useful for tests and diagnostics. */\n stats(): WorkerPoolStats {\n return {\n resident: this.#inFlight.size + this.#idle.length,\n inFlight: this.#inFlight.size,\n waiters: this.#waiters.length,\n totalAcquires: this.#totalAcquires,\n poolExhaustions: this.#poolExhaustions,\n };\n }\n\n /**\n * Spawn one worker and wire its retirement listener. Private: the pool owns worker\n * lifecycle, so a caller outside `acquire()` has no business growing the pool (the\n * dispatcher spawns directly itself when no pool is configured).\n */\n #spawn(): Worker {\n const worker = new Worker(this.#buildWorkerUrl(), this.#workerOptions as WorkerOptions);\n // Once the worker exits, mark it retired by removing it from whichever pool list\n // currently holds it. Listeners are cheap; one per worker is fine.\n worker.once(\"exit\", () => {\n const idleIndex = this.#idle.indexOf(worker);\n if (idleIndex >= 0) {\n this.#idle.splice(idleIndex, 1);\n return;\n }\n this.#inFlight.delete(worker);\n });\n return worker;\n }\n\n /**\n * A worker is healthy when it has not exited. Node marks a terminated worker with\n * `threadId === -1`; the `'exit'` event path also clears it from our lists, but the\n * `threadId` check is the cheap synchronous check for the common release path.\n *\n * Known window: `threadId` flips to `-1` **asynchronously**, so a worker whose\n * `terminate()` has been called but whose exit has not been processed yet still reads as\n * healthy (measured). That is unreachable in-tree today — `terminate()` is only called by\n * `drain()` (after which the pool refuses every `acquire`) and by the dispatcher's\n * unpooled path (whose worker never enters this pool) — so this is a note for whoever\n * adds a third caller, not a bug to chase.\n */\n #isHealthy(worker: Worker): boolean {\n return worker.threadId !== -1;\n }\n}\n\n/**\n * Publish a frame-transfer event when the dispatcher's callResult postMessage carries\n * image bytes. Called from the dispatcher, not the pool, but lives here so the channel\n * name and payload shape stay one-file-defined (ADR-0017 Addendum).\n */\nexport function publishImageBytes(byteLength: number): void {\n publish(channels.imageBytes, { byteLength });\n}\n\n/**\n * Publish the warm-worker's reset cost when its first `ready` frame arrives at the\n * host. `previousSettledAt` is the wall-clock time the pool last released this worker;\n * the gap is what the worker spent clearing per-run state and re-installing the surface.\n */\nexport function publishResetTime(durationMs: number): void {\n publish(channels.resetTime, { durationMs });\n}\n","/**\n * Worker source location.\n *\n * The previous bootstrap assembled a `data:text/javascript,…` URL from\n * `Function.prototype.toString()` of `workerMain` plus a protocol literal —\n * a single self-contained file but with no V8 code-cache reuse across spawns\n * (ADR-0017 §7). The rolldown dual-entry build now emits `dist/worker.js`\n * alongside `dist/index.js`; the host loads that file directly, which lets\n * V8's code cache and Node's module cache survive across warm-reuse spawns.\n *\n * The two candidates the resolver tries:\n *\n * 1. `dist/worker.js` — the rolldown output, sibling of `dist/index.js`. The\n * production path used by `pi install npm:pi-ptc-subagents`.\n * 2. `src/runtime/worker-entry.ts` — the source, used by `vitest` runs\n * (vitest's transformer handles `.ts` and the worker is launched in-process\n * so the relative path resolves).\n */\nimport { existsSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\n/**\n * Resolve the worker entry's `file://` URL.\n *\n * The function takes no arguments: the protocol tables now travel through the\n * worker file itself (rolldown bundles `./protocol.ts` into `dist/worker.js`),\n * so the host has nothing to compose at URL-build time.\n */\nexport function buildWorkerUrl(): URL {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n // Production: rolldown outputs `dist/worker.js` next to `dist/index.js`,\n // which is what the host's `import.meta.url` resolves to when it loaded\n // `dist/index.js`.\n resolve(here, \"worker.js\"),\n // Fallback: the source file in the same directory. Vitest loads\n // `src/runtime/dispatcher.ts` directly (no `dist/` involved), so the\n // worker entry is `src/runtime/worker-entry.ts` and lives beside the\n // host file.\n resolve(here, \"worker-entry.ts\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return pathToFileURL(candidate);\n }\n throw new Error(`worker entry not found; tried: ${candidates.join(\", \")}`);\n}\n","/**\n * Host-side dispatcher: one `runPtcProgram()` call = one program in one worker.\n *\n * The worker's lifecycle depends on the path (ADR-0017 §1):\n * - **cold** (no `pool`): the dispatcher spawns a worker for the run and terminates it when the\n * run settles — one run, one worker;\n * - **pooled** (`pool` set): the dispatcher acquires a warm worker from the turn's pool and\n * releases it back when the run settles. The worker outlives the run — the turn's later runs\n * reuse it, and the pool's `drain()` retires it at turn end, not this file.\n *\n * Responsibilities:\n * - spawn a hardened worker (F1 env scrub, F2 V8 caps, F3 frozen per-run env in\n * `workerData`, F4 `cwd` carried in the run config and handed to the bindings) — the\n * hardening itself is defined once, in `workerSpawnOptions` (`worker-pool.ts`),\n * - complete the `MessageChannel` handshake and send the `init` frame,\n * - route `tools.*` calls to the binding table — concurrently, with DSH's\n * `maxParallelSubCalls` forwarding cap and `maxPendingCalls` admission control,\n * - collect logs / narration / phases and enforce the joint output budget,\n * - enforce the deadline, honour caller cancellation, and settle the run: hand the worker\n * back (pooled) or tear it down (cold).\n *\n * Everything the worker can influence is treated as untrusted input: every frame goes\n * through the protocol guards, is size-checked, and can only ever end the run.\n */\nimport { randomUUID } from \"node:crypto\";\nimport { MessageChannel, Worker } from \"node:worker_threads\";\nimport type { MessagePort } from \"node:worker_threads\";\nimport { BUILTIN_BINDING_NAMES, type BindingTable } from \"./bindings.ts\";\nimport { createWorkerEnv, effectiveTimeoutMs, resolveConfig } from \"./limits.ts\";\nimport type { PtcConfig, PtcSurface } from \"./limits.ts\";\nimport {\n HOST_FRAME_KIND,\n isPtcHostFrame,\n isPtcWorkerFrame,\n PTC_ERROR_KIND,\n WORKER_FRAME_KIND,\n} from \"./protocol.ts\";\nimport type {\n PtcCallFrame,\n PtcCallResultErrorFrame,\n PtcCallResultFrame,\n PtcCancelReason,\n PtcErrorKind,\n PtcErrorShape,\n PtcHostFrame,\n PtcJsonValue,\n} from \"./protocol.ts\";\nimport {\n WorkerPool,\n publishImageBytes,\n publishResetTime,\n workerSpawnOptions,\n} from \"./worker-pool.ts\";\nimport { buildWorkerUrl } from \"./worker-source.ts\";\n\nexport interface RunPtcProgramOptions {\n /** Program body: an async function body (`return`/`await` at the top level). */\n code: string;\n surface: PtcSurface;\n /** Working directory for bindings and the run's recorded cwd (F4). */\n cwd: string;\n /** Bindings the program may call; the table keys become `tools.<name>`. */\n bindings: BindingTable;\n /** Workflow surface only: value bound to the program's `args` global. */\n args?: unknown;\n /** Requested deadline; `0`/absent fall back to `timeoutMs`, then clamped to `maxTimeoutMs`. */\n timeoutMs?: number;\n /** Per-run limit overrides on top of `DEFAULT_CONFIG`. */\n config?: Partial<PtcConfig>;\n /** Cancels the run; the worker gets a cooperative cancel window before termination. */\n signal?: AbortSignal;\n /** Identifier carried to the worker; generated when omitted. */\n runId?: string;\n /**\n * Optional worker pool (ADR-0017). Absent = spawn a fresh worker and terminate it\n * at run end (the original cold-start path). Present = the dispatcher acquires a\n * worker from the pool at run start and releases it back at run end. Acquire waits\n * are bounded by `config.poolAcquireTimeoutMs`; on timeout the run fails with\n * `kind: workerExit`.\n */\n pool?: WorkerPool;\n}\n\n/**\n * One image hoisted out of a successful binding result (DSH parity — see ADR-0014,\n * ADR-0017 §8).\n *\n * `data` is base64 exactly as pi's own `read` tool returns it, so the tool layer can\n * forward it as an `ImageContent` block without re-encoding. The host↔worker wire carries\n * base64 too: the worker's channel is lossless JSON (a program may return part of a\n * binding result), and a `MessagePort` transferList of raw bytes could not survive that\n * contract — a transferred `ArrayBuffer` would be unusable as a program's return value.\n * Keeping one representation end to end means zero conversions between the binding and\n * pi's image adapter.\n */\nexport interface PtcImage {\n data: string;\n mimeType: string;\n}\n\nexport interface PtcRunOutcome {\n /** `console.*` output in arrival order. */\n logs: string[];\n /** Workflow `log(message)` narration (observers only, never console output). */\n narrations: string[];\n /** Workflow `phase(title)` titles in arrival order. */\n phases: string[];\n /** Completion value; absent when the program returned nothing or the run failed. */\n value?: PtcJsonValue;\n /**\n * Images hoisted out of successful binding results, in call order.\n *\n * Every image a program's tool calls produced, with no cap and no dedupe: how many images a run\n * attaches is the program's business, exactly as it is in DSH. Each entry carries base64 `data`,\n * the representation pi's image adapter consumes, so the tool layer forwards it untouched.\n * Present only when at least one was hoisted — a failed or cancelled run attaches nothing\n * (ADR-0014 §2), because the tool layer throws for it (`codeRunFailedError`) and its image would\n * never reach the model.\n */\n images?: PtcImage[];\n /** Failure details; absent on success. */\n error?: PtcErrorShape;\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** A leaf whose bytes JSON cannot express: it is billed by `byteLength`, not by its JSON text. */\nfunction isBinaryLeaf(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\n/**\n * Walk a value tree, calling `onBinary` on every binary leaf (`ArrayBuffer` and any\n * `ArrayBufferView`). Cycles and shared sub-objects are visited once, so a pathological frame\n * cannot wedge or inflate the walk.\n *\n * Binary leaves are billed here rather than by their JSON text for two reasons: `JSON.stringify`\n * serialises an `ArrayBuffer` as `{}` (the size vanishes) and explodes a typed-array view into\n * one key per element (the size is overstated by ~6 bytes per element). The walk therefore stops\n * at the leaf and never recurses into a view's `.buffer`, which would count the same bytes twice.\n * ADR-0017 W-6 spells out the refactor; this is its binary half.\n */\nfunction walkBinaryLeaves(\n value: unknown,\n onBinary: (binaryLeaf: ArrayBuffer | ArrayBufferView) => void,\n): void {\n const seen = new WeakSet<object>();\n const visit = (candidate: unknown): void => {\n if (candidate === null || candidate === undefined) return;\n if (candidate instanceof ArrayBuffer) {\n onBinary(candidate);\n return;\n }\n if (ArrayBuffer.isView(candidate)) {\n onBinary(candidate);\n return;\n }\n if (typeof candidate !== \"object\") return;\n if (seen.has(candidate)) return;\n seen.add(candidate);\n if (Array.isArray(candidate)) {\n for (const item of candidate) visit(item);\n return;\n }\n for (const key of Object.keys(candidate as Record<string, unknown>)) {\n visit((candidate as Record<string, unknown>)[key]);\n }\n };\n visit(value);\n}\n\n/**\n * Serialized byte size of a frame, the unit both the output and message budgets use.\n *\n * The count is the JSON text the frame becomes — every brace, bracket, separator, key name and\n * scalar — plus the bytes of any binary leaf, which that text cannot express. Two passes:\n *\n * 1. the whole value through `JSON.stringify` with binary leaves substituted by `null`: every\n * structural character, key name and scalar is billed the way the old helper billed it, and a\n * cyclic value throws here (as before);\n * 2. the binary leaves by `byteLength`, added on top.\n *\n * A frame with binary leaves therefore counts 4 bytes (its substituted `null`) more than the JSON\n * text it would have if the bytes vanished. That residue is deliberate and the safe direction:\n * under-counting the payload is the hole ADR-0017 W-6 closes, and the worker's channel clones\n * those bytes for real.\n *\n * A frame with no binary leaf produces exactly the previous helper's number — the substituter is\n * a no-op when there is nothing to substitute — and a cyclic or otherwise unaccountable payload\n * still reports `Number.POSITIVE_INFINITY` so the caller fails the run.\n */\nfunction serializedBytes(value: unknown): number {\n try {\n const text = Buffer.byteLength(\n JSON.stringify(value, (_key, item) => (isBinaryLeaf(item) ? null : item)) ?? \"\",\n \"utf8\",\n );\n let binary = 0;\n walkBinaryLeaves(value, (leaf) => {\n binary += leaf.byteLength;\n });\n return text + binary;\n } catch {\n // Cyclic or otherwise unaccountable payload: report as unbounded so the caller fails the run.\n return Number.POSITIVE_INFINITY;\n }\n}\n\n/**\n * Run one PTC program in a fresh worker and resolve with its outcome.\n *\n * Never rejects for program/ limit / cancellation failures — those are reported as\n * `outcome.error` so callers have one place to render from. It only throws on caller bugs\n * (invalid config overrides).\n */\nexport async function runPtcProgram(options: RunPtcProgramOptions): Promise<PtcRunOutcome> {\n const config = resolveConfig(options.config);\n const env = createWorkerEnv();\n const runId = options.runId ?? randomUUID();\n const timeoutMs = effectiveTimeoutMs(options.timeoutMs, config);\n\n const logs: string[] = [];\n const narrations: string[] = [];\n const phases: string[] = [];\n\n if (options.signal?.aborted) {\n return {\n logs,\n narrations,\n phases,\n error: { kind: PTC_ERROR_KIND.abort, message: \"run cancelled before start\" },\n };\n }\n\n // Acquire the worker before entering the Promise constructor: pool acquires are\n // async (they queue when the pool is full and time out per `poolAcquireTimeoutMs`),\n // and the constructor callback cannot `await`. Cold-start falls back to `new Worker`\n // when no pool is configured. ADR-0017 §1.\n let worker: Worker;\n try {\n if (options.pool) {\n worker = await options.pool.acquire();\n } else {\n // Cold path: one run owns this worker outright, so it carries the run's own `runId`.\n // The hardening (F1–F3) has one definition for both paths — `workerSpawnOptions`.\n worker = new Worker(\n buildWorkerUrl(),\n workerSpawnOptions({ surface: options.surface, env, limits: config, runId }),\n );\n }\n } catch (error) {\n // Pool acquire timed out (or another acquire-side failure): surface as workerExit\n // so the tool layer renders a coherent \"the harness could not get me a worker\"\n // message rather than a stack trace.\n return {\n logs,\n narrations,\n phases,\n error: {\n kind: PTC_ERROR_KIND.workerExit,\n message: `pool acquire failed: ${messageOf(error)}`,\n },\n };\n }\n\n return await new Promise<PtcRunOutcome>((resolve) => {\n const channel = new MessageChannel();\n const control: MessagePort = channel.port1;\n const workerPort: MessagePort = channel.port2;\n\n const bindingAbort = new AbortController();\n let settled = false;\n /** Set once a stop is under way; owns the terminal state from then on (`settleTerminal`). */\n let cancelling: PtcCancelReason | undefined;\n /**\n * Whether the worker has emitted its first `ready` frame. A cancel that lands before it does\n * still arms the grace window (the settle time must be bounded even for a worker that never\n * becomes reachable); the `ready` handler restarts that window, so a merely slow spawn gets\n * its full `graceMs` from the moment it can actually react.\n */\n let workerReady = false;\n let pendingCalls = 0;\n let outputBytes = 0;\n let activeDispatches = 0;\n const dispatchWaiters: Array<() => void> = [];\n // Per-run depth for `pi.dispatch` (ADR-0016 Recursive section).\n // The parent turn's PTC run is depth 0; a child spawned by `pi.dispatch`\n // is depth 1, and so on. The depth is read by the binding's `execute`;\n // the dispatcher itself does not change it within a single run.\n const runDepth = 0;\n /** Deadline timer: fires `timeoutMs` after the run started. A cancel does not disarm it. */\n let runTimer: NodeJS.Timeout | undefined;\n /** The cancel's cooperative window: armed and re-armed by `armGraceTimer` only. */\n let graceTimer: NodeJS.Timeout | undefined;\n\n /* --------------------------- hoisted images (ADR-0014 / ADR-0017 §8) --------------------------- */\n\n const images: PtcImage[] = [];\n\n /**\n * Capture the image blocks from one binding result.\n *\n * `PtcImage` has exactly one representation — base64 `data`, the same shape pi's own `read`\n * returns and the same shape the JSON-only worker channel carries. A binding that emits\n * `data: <base64>` passes through untouched (no decode/encode round trip); a binding that\n * produces raw bytes is normalised here, once, host-side.\n *\n * The caller (`dispatchCall`) only keeps the captured list when `postCallResult` returned\n * true: a frame the port rejected was not a successful subtool result (ADR-0014 §2).\n */\n const captureImages = (value: unknown): PtcImage[] => {\n const captured: PtcImage[] = [];\n const content = (value as { content?: unknown } | null)?.content;\n if (!Array.isArray(content)) return captured;\n for (const block of content) {\n if (block === null || typeof block !== \"object\") continue;\n const candidate = block as {\n type?: unknown;\n data?: unknown;\n bytes?: unknown;\n mimeType?: unknown;\n };\n if (candidate.type !== \"image\") continue;\n let data: string | undefined;\n if (typeof candidate.data === \"string\" && candidate.data.length > 0) {\n // The shape pi's own tools emit, and the shape the wire carries: pass through.\n data = candidate.data;\n } else if (candidate.bytes instanceof ArrayBuffer && candidate.bytes.byteLength > 0) {\n // A binding that produces raw bytes (no pi tool does today) is normalised here, so\n // `PtcImage` has exactly one representation downstream.\n data = Buffer.from(candidate.bytes).toString(\"base64\");\n }\n if (data === undefined) continue;\n captured.push({\n data,\n mimeType:\n typeof candidate.mimeType === \"string\"\n ? candidate.mimeType\n : \"application/octet-stream\",\n });\n }\n return captured;\n };\n\n const cancelMessage = (reason: PtcCancelReason): string =>\n reason === \"timeout\" ? `run timed out after ${timeoutMs} ms` : \"run cancelled\";\n\n /** The error kind a stop reports: whichever reason asked for it. */\n const cancelErrorKind = (reason: PtcCancelReason): PtcErrorKind =>\n reason === \"timeout\" ? PTC_ERROR_KIND.timeout : PTC_ERROR_KIND.abort;\n\n const finish = (outcome: PtcRunOutcome): void => {\n if (settled) return;\n settled = true;\n if (runTimer) clearTimeout(runTimer);\n if (graceTimer) clearTimeout(graceTimer);\n options.signal?.removeEventListener(\"abort\", onAbortSignal);\n bindingAbort.abort();\n control.close();\n if (options.pool) {\n // Warm-reuse path: hand the worker back to the pool. The pool retires it\n // if it has already exited (Node marks a terminated worker with\n // `threadId === -1`); otherwise it parks the worker in `idle` or hands it\n // to the next waiter. ADR-0017 §1.\n options.pool.release(worker);\n } else {\n // Cold-start path: terminate the worker. The runtime owns the lifecycle.\n void worker.terminate();\n }\n resolve(outcome);\n };\n\n const fail = (kind: PtcErrorKind, message: string, stack?: string): void => {\n finish({\n logs,\n narrations,\n phases,\n error: stack === undefined ? { kind, message } : { kind, message, stack },\n });\n };\n\n /* -------------------------------- sending -------------------------------- */\n\n const sendControl = (frame: PtcHostFrame): void => {\n if (settled) return;\n // Every outbound frame is validated by the same guard the worker's inbound frames\n // go through: a construction bug fails the run here rather than inside the worker.\n if (!isPtcHostFrame(frame)) {\n fail(PTC_ERROR_KIND.protocol, \"internal error: malformed host frame\");\n return;\n }\n control.postMessage(frame);\n };\n\n const sendInit = (): void => {\n const frame: PtcHostFrame = {\n kind: HOST_FRAME_KIND.init,\n runId,\n surface: options.surface,\n code: options.code,\n bindings: [...options.bindings.keys()],\n bindingCandidates: [...BUILTIN_BINDING_NAMES],\n maxItemsPerCall: config.maxItemsPerCall,\n maxPendingCalls: config.maxPendingCalls,\n ...(options.args === undefined ? {} : { args: options.args }),\n };\n if (!isPtcHostFrame(frame)) {\n fail(PTC_ERROR_KIND.protocol, \"internal error: malformed init frame\");\n return;\n }\n const bytes = serializedBytes(frame);\n if (bytes > config.maxMessageBytes) {\n fail(\n PTC_ERROR_KIND.protocol,\n `init frame of ${bytes} bytes exceeds maxMessageBytes (${config.maxMessageBytes})`,\n );\n return;\n }\n sendControl(frame);\n };\n\n /* ------------------------------- receiving ------------------------------- */\n\n const accountOutput = (bytes: number, source: string): boolean => {\n outputBytes += bytes;\n if (outputBytes <= config.maxOutputBytes) return true;\n // R1 §3: an oversize payload keeps the log prefix that did fit.\n fail(\n PTC_ERROR_KIND.outputLimit,\n `${source} exceeded the output budget: logs + result reached ${outputBytes} bytes (maxOutputBytes=${config.maxOutputBytes}); ${logs.length} log line(s) retained`,\n );\n return false;\n };\n\n const acquireDispatchSlot = async (): Promise<void> => {\n if (activeDispatches < config.maxParallelSubCalls) {\n activeDispatches += 1;\n return;\n }\n await new Promise<void>((slot) => {\n dispatchWaiters.push(slot);\n });\n };\n\n const releaseDispatchSlot = (): void => {\n const next = dispatchWaiters.shift();\n if (next) {\n // Hand the slot over directly; `activeDispatches` is unchanged.\n next();\n return;\n }\n activeDispatches -= 1;\n };\n\n const handleCall = (frame: PtcCallFrame): void => {\n pendingCalls += 1;\n if (pendingCalls > config.maxPendingCalls) {\n // Defense in depth only. The shipped worker admits calls before posting them\n // (`maxPendingCalls` travels in the init frame), so a normal program — including a\n // wide `Promise.all` or `parallel()` fan-out — never reaches this branch: the burst\n // queues in the worker, and arrivals here stay at or below the ceiling. This fires\n // only for a worker that ignores its admission budget, which is why it fails the\n // run instead of throttling.\n fail(\n PTC_ERROR_KIND.protocol,\n `more than maxPendingCalls (${config.maxPendingCalls}) binding calls in flight`,\n );\n return;\n }\n void dispatchCall(frame).finally(() => {\n pendingCalls -= 1;\n });\n };\n\n const dispatchCall = async (frame: PtcCallFrame): Promise<void> => {\n const binding = options.bindings.get(frame.tool);\n if (!binding) {\n postCallResult({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: false,\n message:\n `no binding named \"${frame.tool}\" in this run; available bindings: ` +\n ([...options.bindings.keys()].join(\", \") || \"(none)\"),\n });\n return;\n }\n await acquireDispatchSlot();\n if (settled) {\n // Late arrival: the run settled (deadline/abort) while this call was waiting for a\n // dispatch slot, so the worker that asked is gone and `bindingAbort` has already fired.\n // Do NOT execute the binding: it would start host-side work whose only possible outcome\n // is the abort it cannot deliver anywhere — the port is closed. The in-flight case is the\n // one that needs the signal, and it is covered: a call that was already executing when\n // the cancel fired sees `bindingAbort` through the context it was handed.\n releaseDispatchSlot();\n return;\n }\n try {\n const value = await binding.execute(frame.args, {\n signal: bindingAbort.signal,\n callId: frame.callId,\n depth: runDepth,\n maxDispatchDepth: config.maxDispatchDepth,\n });\n // Capture image blocks before the callResult post so the hoist can never race the\n // worker's view of the result; the captures are committed only once the post\n // returned true.\n const captured = captureImages(value);\n // Per-frame byte accounting before the post, as for every other frame: a `callResult` is\n // the other host→worker control frame, so it answers to the same `maxMessageBytes` cap as\n // the `init` frame and every inbound frame (\"cap on a single control frame, either\n // direction\", limits.ts / R1 §1). Over the cap is excessive control traffic — DSH's own\n // wording for `kind: protocol`, R1 §error kinds — so it ends the run here instead of\n // shipping a 128 MiB structured clone. Compare `postCallResult`: a value the *port*\n // rejects is a per-call failure (the program can catch a `ToolCallError`); a frame over\n // the documented cap is not, it is the same run-level failure the other three frame\n // checks produce.\n const callFrame: PtcHostFrame = {\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: true,\n value,\n };\n const callFrameBytes = serializedBytes(callFrame);\n if (callFrameBytes > config.maxMessageBytes) {\n fail(\n PTC_ERROR_KIND.protocol,\n `callResult frame of ${callFrameBytes} bytes exceeds maxMessageBytes (${config.maxMessageBytes})`,\n );\n return;\n }\n const posted = postCallResult(callFrame);\n // Hoist only a result the worker actually received: DSH's condition is a *successful*\n // subtool result, and a payload the port rejected was not one (ADR-0014 §2).\n if (posted) {\n // Images do not pass ADR-0003's output budget (ADR-0014 Consequences: \"Images are not\n // output ... That is deliberate\"). The per-frame `maxMessageBytes` guard above still\n // bounds what a single callResult may carry, so a pathological binding cannot smuggle\n // unbounded payload in one frame. Volume stays observable: `ptc:image:hoist-bytes`\n // publishes every hoisted block and the PTC row's meta carries the count.\n for (const image of captured) {\n images.push(image);\n publishImageBytes(Buffer.byteLength(image.data, \"base64\"));\n }\n }\n } catch (error) {\n postCallResult({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: false,\n message: messageOf(error),\n });\n } finally {\n releaseDispatchSlot();\n }\n };\n\n /** `true` when the worker actually received the result (see the hoist at the call site). */\n const postCallResult = (frame: PtcCallResultFrame): boolean => {\n if (settled) return false;\n try {\n control.postMessage(frame);\n return true;\n } catch (error) {\n // The binding's value could not be structured-cloned; report the call as failed\n // instead of leaving the worker waiting on a response that will never arrive.\n // The failing payload is dropped rather than resent — it is exactly what the\n // clone rejected.\n try {\n control.postMessage({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: false,\n message: `binding result could not be transferred: ${messageOf(error)}`,\n } satisfies PtcCallResultErrorFrame);\n } catch {\n fail(PTC_ERROR_KIND.protocol, \"binding result is not transferable\");\n }\n return false;\n }\n };\n\n const settleTerminal = (outcome: PtcRunOutcome): PtcRunOutcome => {\n if (!cancelling) return outcome;\n // Once cancelled, the caller asked for a stop: the run reports the stop, not the\n // value the program managed to produce inside the grace window. A failed run\n // attaches nothing (ADR-0014 §2): the tool layer throws for it, so images could\n // never reach the model anyway.\n return {\n logs,\n narrations,\n phases,\n error: {\n kind: cancelErrorKind(cancelling),\n message: cancelMessage(cancelling),\n },\n };\n };\n\n const handleFrame = (frame: unknown): void => {\n if (settled) return;\n if (!isPtcWorkerFrame(frame)) {\n fail(PTC_ERROR_KIND.protocol, \"malformed worker frame\");\n return;\n }\n const bytes = frame.kind === WORKER_FRAME_KIND.ready ? 0 : serializedBytes(frame);\n if (!Number.isFinite(bytes)) {\n fail(PTC_ERROR_KIND.protocol, \"worker frame could not be serialized for accounting\");\n return;\n }\n if (bytes > config.maxMessageBytes) {\n fail(\n PTC_ERROR_KIND.protocol,\n `control frame of ${bytes} bytes exceeds maxMessageBytes (${config.maxMessageBytes})`,\n );\n return;\n }\n\n switch (frame.kind) {\n case WORKER_FRAME_KIND.ready: {\n // Warm-reuse signal: a worker that has been through at least one run reports\n // \"ready\" again after clearing per-run state. The gap since the previous\n // release is the per-run reset cost, which is what ADR-0017 measures to\n // verify the warm path actually saves time.\n if (options.pool) {\n const previousSettled = options.pool.lastSettledAt(worker);\n if (previousSettled !== undefined) {\n publishResetTime(Date.now() - previousSettled);\n }\n }\n if (!workerReady) {\n workerReady = true;\n if (cancelling) {\n // The cancel arrived while this worker was still booting, so it armed the fallback\n // window (see `beginCancel`). The worker can react now: restart the window so it\n // gets its full `graceMs` to observe the cancel. Still bounded — the run's deadline\n // timer stays armed through a cancel, and the fallback never waited for `ready`.\n armGraceTimer(cancelling);\n // Do **not** hand a cancelled run a program: `init` would start work whose result\n // nobody is waiting for, and the worker would spend the grace window running it.\n // A worker that was cancelled while booting reports the cancel instead of a\n // `ready` (see `reportIdleCancel`); this branch is the belt-and-braces path for a\n // `ready` and a `cancel` that crossed on the wire.\n return;\n }\n }\n sendInit();\n return;\n }\n case WORKER_FRAME_KIND.call:\n handleCall(frame);\n return;\n case WORKER_FRAME_KIND.log:\n if (accountOutput(bytes, \"console output\")) logs.push(frame.text);\n return;\n case WORKER_FRAME_KIND.narration:\n if (accountOutput(bytes, \"workflow narration\")) narrations.push(frame.message);\n return;\n case WORKER_FRAME_KIND.phase:\n if (accountOutput(bytes, \"workflow phase\")) phases.push(frame.title);\n return;\n case WORKER_FRAME_KIND.result: {\n if (!accountOutput(bytes, \"completion value\")) return;\n // A cancel owns the terminal state and takes the images with it: `settleTerminal`\n // rebuilds the outcome without them, which is ADR-0014 §2's \"a failed run attaches\n // nothing\" rule.\n const base = {\n logs,\n narrations,\n phases,\n ...(images.length > 0 ? { images } : {}),\n };\n finish(\n settleTerminal(frame.value === undefined ? base : { ...base, value: frame.value }),\n );\n return;\n }\n case WORKER_FRAME_KIND.error:\n // A failed run attaches nothing (ADR-0014 §2): the tool layer throws for it, so an\n // image hoisted earlier in the run could never reach the model anyway.\n finish(settleTerminal({ logs, narrations, phases, error: frame.error }));\n return;\n default:\n fail(PTC_ERROR_KIND.protocol, \"unknown worker frame\");\n }\n };\n\n /* ------------------------------- lifecycle ------------------------------- */\n\n function onAbortSignal(): void {\n beginCancel(\"abort\");\n }\n\n /**\n * Arm the cooperative-cancel window: `graceMs` from now the run settles with `reason`, unless\n * the worker settles it first. Re-arming replaces the pending window.\n *\n * The invariant this guards: **from the moment a run starts, it settles in bounded time.**\n * The window is therefore armed by `beginCancel` itself, not by the worker's `ready` frame —\n * a worker that never becomes reachable (spawn failure, wedged module load, a thread the OS\n * stopped scheduling) can no longer leave the run pending forever.\n */\n const armGraceTimer = (reason: PtcCancelReason): void => {\n if (graceTimer) clearTimeout(graceTimer);\n graceTimer = setTimeout(() => {\n fail(cancelErrorKind(reason), cancelMessage(reason));\n }, config.graceMs);\n };\n\n function beginCancel(reason: PtcCancelReason): void {\n if (settled) return;\n if (cancelling) {\n // A stop arrived while one was already pending — either the deadline landing on an\n // in-flight cancel, or the caller's abort landing after the deadline already fired (the\n // abort listener is registered `once`, so it can only ever be the first abort). This\n // branch is reached whether or not the worker became reachable: a worker that is mid-run\n // when the second stop arrives takes it too.\n //\n // Settle now rather than waiting out another `graceMs`. The run is already stopping and a\n // second stop must not extend that wait: in the cancel-first ordering the deadline has\n // just arrived, so the run must not stay unsettled past it, and in the deadline-first\n // ordering that deadline has already fired and is winding down through its own grace\n // window. The reason reported is the stop that arrived first — it is why the run is\n // ending; the second one only confirmed there was nothing left to wait for.\n fail(cancelErrorKind(cancelling), cancelMessage(cancelling));\n return;\n }\n cancelling = reason;\n // The deadline timer is deliberately left armed: while it is still pending it bounds a\n // cancelled run — if it fires with a cancel in flight, the branch above settles the run\n // right there, with the reason the caller asked for — so a caller's abort cannot outlive\n // the run's own deadline. The converse ordering has no deadline bound, and is not meant to:\n // see `runTimer` below for why a timed-out run settles at `timeoutMs + graceMs`.\n bindingAbort.abort();\n sendControl({ kind: HOST_FRAME_KIND.cancel, reason });\n // Arm the window immediately rather than waiting for the worker's first `ready` frame. The\n // window is about \"time the worker has had to react\"; the `ready` handler restarts it for a\n // worker that turns out to be merely slow. What it must never do is refuse to arm — that\n // left a worker stuck in BOOTING with no timer at all.\n armGraceTimer(reason);\n }\n\n worker.on(\"error\", (error: Error) => {\n fail(PTC_ERROR_KIND.workerExit, `worker failed: ${messageOf(error)}`);\n });\n worker.on(\"exit\", (code: number) => {\n fail(PTC_ERROR_KIND.workerExit, `worker exited with code ${code} before the run completed`);\n });\n control.on(\"message\", handleFrame);\n control.on(\"messageerror\", () => {\n fail(PTC_ERROR_KIND.protocol, \"worker sent a frame that could not be deserialized\");\n });\n\n options.signal?.addEventListener(\"abort\", onAbortSignal, { once: true });\n // The run's deadline. It is one-shot: when it fires by itself the timer is spent, so what ends\n // a timed-out run is the cancel it starts — `beginCancel(\"timeout\")` arms a `graceMs` window of\n // its own, and that window's expiry is the settle. A run that times out therefore settles by\n // `timeoutMs + graceMs` (\"the program gets `timeoutMs`, then `graceMs` to wind down\"), which is\n // the intended semantics, not a leak. While this timer is still pending it also bounds a run\n // the caller has already cancelled: a cancel never disarms it (see `beginCancel`), so that\n // ordering settles at the deadline.\n runTimer = setTimeout(() => {\n beginCancel(\"timeout\");\n }, timeoutMs);\n\n // The connect frame carries the control port *in the payload*; a transfer-list-only\n // entry is silently dropped by Node and the worker would wait forever.\n const connectFrame: PtcHostFrame = { kind: HOST_FRAME_KIND.connect, port: workerPort };\n if (!isPtcHostFrame(connectFrame)) {\n fail(PTC_ERROR_KIND.protocol, \"internal error: malformed connect frame\");\n } else {\n worker.postMessage(connectFrame, [workerPort]);\n }\n });\n}\n","/**\n * Model-facing text hygiene: the step pi's built-in tools take and PTC's own text assembly missed.\n *\n * Every built-in tool result is cleaned before the model reads it —\n * `stripAnsi` → `sanitizeBinaryOutput` → drop `\\r` (`pi-coding-agent`:\n * `dist/core/tools/render-utils.js`) — because a tool's output is arbitrary bytes: ANSI colour\n * codes from `pnpm`/`vitest`, carriage-return progress bars, NUL bytes from a binary file.\n *\n * The PTC tools assemble their own text block (`common.ts: renderToolResult`) and did none of\n * that, so a program returning captured `bash` output shipped `\\u001b[38;2;212;212;212m` to the\n * model as literal escapes, and any string with a real newline arrived JSON-escaped as `\\n`.\n * Both are pure noise the model has to read — and ANSI noise is charged per character, in tokens.\n *\n * **Scope: the text block only.** `details` stays raw so the TUI keeps rendering real colours\n * (`@earendil-works/pi-tui`'s `Text` understands ANSI), and a binding's result inside a program\n * is untouched — a program that wants coloured bytes for a file still has them. See ADR-0012.\n *\n * pi exports neither helper (its `exports` map has only `.`, `./rpc-entry`, `./client`,\n * `./experimental/plugin`), so both are mirrored here rather than imported. The ANSI pattern\n * follows ansi-regex / strip-ansi (MIT, Sindre Sorhus) via pi's own implementation.\n */\nimport type { PtcJsonValue } from \"../runtime/protocol.ts\";\n\n/** Hard cap for one rendered line; longer lines are truncated with `…`. */\nexport const MAX_LINE_CHARS = 200;\n\n/** A value stays on one line while its inline form fits this; otherwise it renders as a block. */\nconst INLINE_MAX_CHARS = 100;\n\n/** Indent applied per nesting level in block form. */\nconst INDENT = \" \";\n\n/**\n * ANSI escapes, in the two shapes that matter: OSC (\\`ESC ] …\\` to BEL, \\`ESC \\\\\\\\` or C1 ST) and\n * CSI plus friends (introducer, optional intermediates, optional params, final byte).\n *\n * Built through the \\`RegExp\\` constructor from an escaped source so no control character appears\n * literally — the repo lints via oxlint, whose \\`no-control-regex\\` rule rejects that.\n */\nconst ANSI_PATTERN = new RegExp(\n [\n \"(?:\\\\u001B\\\\][\\\\s\\\\S]*?(?:\\\\u0007|\\\\u001B\\\\u005C|\\\\u009C))\",\n \"[\\\\u001B\\\\u009B][[\\\\]()#;?]*(?:\\\\d{1,4}(?:[;:]\\\\d{0,4})*)?[\\\\dA-PR-TZcf-nq-uy=><~]\",\n ].join(\"|\"),\n \"g\",\n);\n\n/** Drop ANSI escape sequences (colours, cursor moves, OSC hyperlinks). */\nexport function stripAnsi(value: string): string {\n // Fast path: without an ESC or C1 introducer there is nothing to strip.\n if (!value.includes(String.fromCharCode(27)) && !value.includes(String.fromCharCode(155))) {\n return value;\n }\n return value.replace(ANSI_PATTERN, \"\");\n}\n\n/**\n * Make arbitrary program output safe to hand the model.\n *\n * Same three steps as pi's built-ins: strip ANSI, remove `\\r` (a progress bar's carriage\n * return is not a line break), then drop control characters other than `\\n`/`\\t` and the\n * Unicode format characters that break width math. Lone surrogates fall out of code-point\n * iteration for free.\n */\nexport function sanitizeText(value: string): string {\n const stripped = stripAnsi(value).replace(/\\r/g, \"\");\n if (stripped.length === 0) return stripped;\n let out = \"\";\n for (const char of stripped) {\n const code = char.codePointAt(0);\n if (code === undefined) continue;\n if (code === 0x0a || code === 0x09) {\n out += char;\n continue;\n }\n if (code <= 0x1f) continue;\n if (code >= 0xfff9 && code <= 0xfffb) continue;\n out += char;\n }\n return out;\n}\n\n/** Object keys that read as identifiers in the summary style are left unquoted. */\nconst BARE_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\nfunction keyText(key: string): string {\n return BARE_KEY.test(key) ? key : JSON.stringify(key);\n}\n\n/**\n * The inline form of a value, or `undefined` when it cannot be inlined.\n *\n * A string with a real newline is never inlined: keeping its line structure is the whole point\n * (see ADR-0012), and quoting it would turn one readable block back into `\\n` escapes.\n */\nfunction inlineForm(value: PtcJsonValue): string | undefined {\n if (value === null) return \"null\";\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (typeof value === \"string\") {\n const sanitized = sanitizeText(value);\n return sanitized.includes(\"\\n\") ? undefined : JSON.stringify(sanitized);\n }\n if (Array.isArray(value)) {\n const items: string[] = [];\n for (const item of value) {\n const rendered = inlineForm(item);\n if (rendered === undefined) return undefined;\n items.push(rendered);\n }\n return `[${items.join(\", \")}]`;\n }\n const keys = Object.keys(value);\n if (keys.length === 0) return \"{}\";\n const parts: string[] = [];\n for (const key of keys) {\n const rendered = inlineForm(value[key] ?? null);\n if (rendered === undefined) return undefined;\n parts.push(`${keyText(key)}: ${rendered}`);\n }\n return `{${parts.join(\", \")}}`;\n}\n\n/** Inline rendering, but only while it stays short enough to read on one line. */\nfunction tryInline(value: PtcJsonValue): string | undefined {\n const rendered = inlineForm(value);\n if (rendered === undefined) return undefined;\n return rendered.length <= INLINE_MAX_CHARS ? rendered : undefined;\n}\n\n/** A non-container value on its own line (block form only meets these defensively). */\nfunction scalarText(value: string | number | boolean | null): string {\n if (value === null) return \"null\";\n return typeof value === \"string\" ? sanitizeText(value) : String(value);\n}\n\n/** One key per line, braces kept, string values raw (real newlines preserved, indented). */\nfunction blockLines(value: PtcJsonValue, indent: string): string[] {\n if (value === null || typeof value !== \"object\") return [`${indent}${scalarText(value)}`];\n if (Array.isArray(value)) {\n if (value.length === 0) return [`${indent}[]`];\n const lines = [`${indent}[`];\n for (const item of value) {\n const inline = tryInline(item);\n if (inline !== undefined) {\n lines.push(`${indent}${INDENT}${inline}`);\n continue;\n }\n lines.push(...valueLines(item, indent + INDENT));\n }\n lines.push(`${indent}]`);\n return lines;\n }\n\n const keys = Object.keys(value);\n if (keys.length === 0) return [`${indent}{}`];\n const lines = [`${indent}{`];\n const inner = indent + INDENT;\n for (const key of keys) {\n const child = value[key] ?? null;\n const inline = tryInline(child);\n if (inline !== undefined) {\n lines.push(`${inner}${keyText(key)}: ${inline}`);\n continue;\n }\n if (typeof child === \"string\") {\n // A multi-line string keeps its own lines; the key line above delimits it.\n lines.push(`${inner}${keyText(key)}:`);\n for (const line of sanitizeText(child).split(\"\\n\")) {\n // A blank line inside a multi-line value stays blank — indenting it would leave trailing\n // whitespace in every transcript that shows a block ending in a newline.\n lines.push(line.length === 0 ? \"\" : `${inner}${INDENT}${line}`);\n }\n continue;\n }\n // A container opens on its key's line (`outer: {`) and closes back at the key's indent, so a\n // nested value does not push its braces onto lines of their own.\n const nested = blockLines(child, inner);\n lines.push(\n `${inner}${keyText(key)}: ${(nested[0] ?? \"\").trimStart()}`.trimEnd(),\n ...nested.slice(1),\n );\n }\n lines.push(`${indent}}`);\n return lines;\n}\n\nfunction valueLines(value: PtcJsonValue, indent: string): string[] {\n if (typeof value === \"string\") return sanitizeText(value).split(\"\\n\");\n if (value === null || typeof value !== \"object\") return [String(value)];\n return blockLines(value, indent);\n}\n\n/** Truncate every line that exceeds {@link MAX_LINE_CHARS}. */\nfunction capLines(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => (line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS - 1)}…` : line))\n .join(\"\\n\");\n}\n\n/**\n * Render a completion value for the model: strings verbatim, everything else as a compact\n * `{key: value}` summary or an indented block — never `JSON.stringify` on the whole value, whose\n * escaping destroys exactly the content a reader needs (newlines, quotes, non-ASCII spacing).\n */\nexport function renderModelValue(value: PtcJsonValue): string {\n // A program that returns text reads as text — quoting a top-level string would turn a report\n // into one escaped line, which is the failure mode this module exists to remove.\n if (typeof value === \"string\") return capLines(sanitizeText(value));\n if (value === null || typeof value !== \"object\") return String(value);\n const inline = tryInline(value);\n const body = inline !== undefined ? [inline] : valueLines(value, \"\");\n return capLines(body.join(\"\\n\"));\n}\n","/**\n * Shared plumbing for the two model-facing PTC tools (`ptc_run_code`, `ptc_workflow`).\n *\n * Both tools are thin. They resolve the run's working directory, hand the program to\n * `runPtcProgram()` (T3), and turn the dispatcher's outcome into one of pi's two tool-result\n * shapes:\n *\n * - **success** — `content` carries one text block (logs, workflow narration, phases, then the\n * completion value) and `details` carries the same facts structurally for rendering and logs;\n * - **failure** — the tool *throws* (pi's convention: `AgentTool.execute` — \"Throw on failure\n * instead of encoding errors in `content`\"), with R1 §3's message shape\n * `code run failed (<kind>): <message>` plus a `Captured output:` block.\n *\n * The dispatcher never rejects for program failures — it reports them as `outcome.error` — so\n * \"did the run fail\" is one check in one place, shared by both tools.\n *\n * The text block is the model's copy, so it is cleaned and laid out for reading (`./text.ts`:\n * ANSI stripped, control characters dropped, strings with real newlines kept multi-line) while\n * `details` stays raw for the TUI. See ADR-0012.\n */\nimport { randomUUID } from \"node:crypto\";\nimport { writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { BUILTIN_BINDING_NAMES } from \"../runtime/bindings.ts\";\nimport {\n DEFAULT_MAX_BYTES,\n DEFAULT_MAX_LINES,\n formatSize,\n truncateTail,\n} from \"@earendil-works/pi-coding-agent\";\nimport type { AgentToolResult, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport type { PtcRunOutcome } from \"../runtime/dispatcher.ts\";\nimport type { PtcConfig, PtcSurface } from \"../runtime/limits.ts\";\nimport type { PtcJsonValue } from \"../runtime/protocol.ts\";\nimport { renderModelValue, sanitizeText } from \"./text.ts\";\n\n/** Options every PTC tool factory accepts. */\nexport interface PtcToolOptions {\n /**\n * Per-run limit overrides, merged over `DEFAULT_CONFIG` by the dispatcher (`resolveConfig`).\n *\n * The shipped extension registers the tools without overrides; the seam exists so tests can\n * exercise budget/grace behaviour without materializing 64 MiB of logs, and so a future\n * settings surface has somewhere to plug in.\n */\n config?: Partial<PtcConfig>;\n /**\n * The tool names the binding table is built from, read once per execute.\n *\n * The shipped extension returns the session's active tools — except while PTC mode is on, when\n * it returns the mode's base snapshot instead (the built-ins the mode hid must stay callable\n * from inside a program). See `src/mode/ptc-mode.ts`.\n *\n * Omitting it means \"no session context\" (direct library use, or a hand-built test stub) and\n * asserts the full built-in surface.\n */\n getBindingSourceNames?: () => readonly string[];\n}\n\n/**\n * Binding names for one run: the built-ins this session actually has enabled (T7, #21).\n *\n * A PTC program must never reach further than the session it runs in — a session started\n * with `--tools read` or `--no-builtin-tools` must not be escapable through `tools.<name>`\n * calls. Names outside the built-in factory set are ignored: only tools this package can\n * build adapters for can be bound.\n */\nexport function resolveBindingNames(\n activeToolNames: readonly string[] | undefined,\n): readonly string[] {\n if (activeToolNames === undefined) return BUILTIN_BINDING_NAMES;\n const active = new Set(activeToolNames);\n return BUILTIN_BINDING_NAMES.filter((name) => active.has(name));\n}\n\n/**\n * One-line entry for the system prompt's \"Available tools\" section.\n *\n * Custom tools are omitted from that section unless they set this, which matters most in PTC mode:\n * there the two PTC surfaces are the *only* callable tools, so a prompt that lists no usable tool\n * would contradict the tool declarations. `promptGuidelines` cannot substitute for it — those\n * bullets land in the flat `Guidelines` section, which does not tell the model what it can call.\n */\nexport const PTC_RUN_CODE_SNIPPET =\n \"Run a TypeScript program that composes pi's tools in one shot (tools.<name>(args)); only its return value and console.log come back\";\n\nexport const PTC_WORKFLOW_SNIPPET =\n \"Run a structured TypeScript workflow with named phases, narration (log/phase) and structured concurrency (parallel/pipeline)\";\n\n/**\n * Guidelines are appended flat to the `Guidelines` section with no tool-name prefix, so each bullet\n * must name its tool explicitly (pi's docs call this out; \"Use this tool when…\" is ambiguous).\n * These are the normal-mode hints — in PTC mode the injected briefing supersedes them.\n */\nexport const PTC_TOOL_GUIDELINES: readonly string[] = [\n // DSH states this contract in `run_code`'s own description (\"Image-bearing subtool results are\n // attached after the run.\"); the model has to know because the images never appear in the value it\n // returns (ADR-0014).\n \"Image-bearing tool results inside a program (a `tools.read` on a PNG, say) are attached to you after the run — never return image data as the completion value.\",\n \"Use ptc_run_code when a task needs several tool calls whose intermediate output you do not need to see — gather it inside the program and return only the final value.\",\n \"Use ptc_workflow instead of ptc_run_code when the work has named phases and the user benefits from seeing progress narration.\",\n];\n\n/** Tool name per worker surface — the names the model calls and reads in failure messages. */\nexport const SURFACE_TOOL_NAME: Record<PtcSurface, string> = {\n run_code: \"ptc_run_code\",\n workflow: \"ptc_workflow\",\n};\n\n/**\n * Resolve the run's working directory from pi's tool execution context.\n *\n * Investigated against `@earendil-works/pi-coding-agent@0.86.1`: `execute`'s fifth parameter is\n * `ExtensionContext`, and the type *does* expose `cwd: string` — documented there as \"Current\n * working directory\" (`dist/core/extensions/types.d.ts`), the same value pi's own built-in tools\n * resolve relative paths against (`ToolRenderContext.cwd`, and the extension guide's\n * `resolve(ctx.cwd, params.path)` pattern). So the session cwd comes from pi, and this package\n * never has to guess from `process.cwd()`.\n *\n * The `process.cwd()` fallback stays for hand-built contexts (tests, or a future caller passing a\n * partial object); pi always supplies the field.\n */\nexport function resolveToolCwd(ctx: ExtensionContext): string {\n const cwd: unknown = (ctx as { cwd?: unknown } | undefined)?.cwd;\n return typeof cwd === \"string\" && cwd.length > 0 ? cwd : process.cwd();\n}\n\n/** Structured, model-independent facts about a run: pi's `details` payload for both tools. */\nexport interface PtcToolDetails {\n /** Which worker surface produced this result. */\n surface: PtcSurface;\n /** `console.*` output in arrival order. */\n logs: string[];\n /** Workflow `log(message)` narration (always empty on `ptc_run_code`). */\n narrations: string[];\n /** Workflow `phase(title)` titles in arrival order (always empty on `ptc_run_code`). */\n phases: string[];\n /** Non-fatal diagnostics the run itself could not report (currently: unlisted phase titles). */\n warnings: string[];\n /** Completion value; absent when the program returned nothing. */\n result?: PtcJsonValue;\n /** Wall-clock duration of the whole run, including worker spawn and teardown. */\n durationMs: number;\n /**\n * How many images the run hoisted out of tool results (`outcome.images`).\n *\n * The images themselves live in the tool result's `content` (pi forwards them to the model from\n * there), so `details` needs only the count — a second copy here would pin the same base64 twice.\n * See ADR-0014.\n */\n imageCount: number;\n /**\n * Where the untruncated text block was written when pi's truncation contract cut it.\n *\n * Present only for a truncated run. The full text has no other home — a program's logs exist only\n * for the length of the run — so the file is the model's way back to the parts the text block had\n * to drop (it can `tools.read` it from inside the next program).\n */\n fullOutputPath?: string;\n}\n\n/**\n * The captured-output lines R1 §3 appends when a run fails.\n *\n * Phases and narrations are prefixed because they are workflow bookkeeping rather than console\n * output; `ptc_run_code` has neither, so its block is exactly the `logs` join R1 describes.\n */\nfunction capturedOutputLines(outcome: PtcRunOutcome): string[] {\n return [\n ...outcome.phases.map((title) => `[phase] ${sanitizeText(title)}`),\n ...outcome.narrations.map((message) => `[log] ${sanitizeText(message)}`),\n ...outcome.logs.map((line) => sanitizeText(line)),\n ];\n}\n\n/**\n * Build the error a failed run is surfaced with (R1 §3's `CodeRunFailedError` message).\n *\n * The model must be able to fix its program from this message alone: the error kind and message\n * explain what broke, and the captured output shows how far the program got before it did. There\n * is no `sandbox` text and no escalation guidance — ADR-0007 ships no OS sandbox, so the only\n * failure classes are the protocol ones.\n */\nexport function codeRunFailedError(outcome: PtcRunOutcome): Error {\n const error = outcome.error;\n if (error === undefined) {\n // Callers check `outcome.error` first; reaching here means the tool layer asked to render a\n // success as a failure, which is a bug in this package rather than in the model's program.\n return new Error(\"internal error: a successful PTC run was rendered as a failure\");\n }\n const captured = capturedOutputLines(outcome);\n const block = captured.length === 0 ? \"\" : `\\nCaptured output:\\n${captured.join(\"\\n\")}`;\n // A program can throw a message containing the raw bytes it just read (a coloured shell\n // error, a log line with escapes); the failure text is model-facing too.\n const failure = new Error(\n sanitizeText(`code run failed (${error.kind}): ${error.message}${block}`),\n );\n failure.name = \"CodeRunFailedError\";\n return failure;\n}\n\n/** Structural view of pi's `truncateTail` outcome, so the footer does not re-derive the numbers. */\ntype TailTruncation = ReturnType<typeof truncateTail>;\n\n/**\n * Write the untruncated text block to a temp file and return its path.\n *\n * Mirrors what pi's own `bash` does with a long output (its `fullOutputPath`): the run is the only\n * place the text ever existed, so a file under the OS temp dir is the difference between \"the rest is\n * gone\" and \"the rest is one `tools.read` away\".\n */\nfunction writeFullOutput(text: string): string {\n const filePath = join(tmpdir(), `pi-ptc-output-${randomUUID()}.txt`);\n writeFileSync(filePath, text, \"utf8\");\n return filePath;\n}\n\n/**\n * The `[Showing … Full output: path]` footer pi's built-ins use, worded for the same numbers.\n *\n * `truncatedBy` decides the wording: a line cut reports the line range, a byte cut also names the\n * ceiling, and a single oversized last line reports its own size — exactly what `bash` prints.\n */\nfunction truncationFooter(truncation: TailTruncation, fullOutputPath: string): string {\n const startLine = truncation.totalLines - truncation.outputLines + 1;\n const endLine = truncation.totalLines;\n if (truncation.lastLinePartial) {\n return `[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine}. Full output: ${fullOutputPath}]`;\n }\n if (truncation.truncatedBy === \"lines\") {\n return `[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${fullOutputPath}]`;\n }\n return `[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${fullOutputPath}]`;\n}\n\n/**\n * Render one successful outcome as pi's tool result.\n *\n * `content` is ordered the way a reader reconstructs the run: the phase roll-up, then workflow\n * narration, then console output, then the completion value, then warnings. Empty runs get R1's\n * placeholder (`(run_code completed with no output)`) so the model always sees a non-empty text\n * block — a tool result with nothing in it looks like a harness failure.\n *\n * `details` is deliberately the *uncleaned* copy: logs keep their ANSI so the TUI can still\n * colour them, and `result` stays structured for `render.ts`.\n */\nexport function renderToolResult(input: {\n outcome: PtcRunOutcome;\n surface: PtcSurface;\n durationMs: number;\n warnings?: readonly string[];\n}): AgentToolResult<PtcToolDetails> {\n const { outcome, surface } = input;\n const warnings = [...(input.warnings ?? [])];\n const images = outcome.images ?? [];\n const parts: string[] = [];\n if (outcome.phases.length > 0) {\n parts.push(`Phases: ${outcome.phases.map((title) => sanitizeText(title)).join(\" → \")}`);\n }\n if (outcome.narrations.length > 0) {\n parts.push(outcome.narrations.map((message) => sanitizeText(message)).join(\"\\n\"));\n }\n if (outcome.logs.length > 0) {\n parts.push(outcome.logs.map((line) => sanitizeText(line)).join(\"\\n\"));\n }\n if (outcome.value !== undefined) parts.push(renderModelValue(outcome.value));\n for (const warning of warnings) parts.push(`Warning: ${sanitizeText(warning)}`);\n const built =\n parts.length > 0\n ? parts.join(\"\\n\")\n : `(${SURFACE_TOOL_NAME[surface]} completed with no output)`;\n // pi's rule for a tool's model-facing text is \"Tools MUST truncate their output\" — 50 KB / 2000\n // lines, keeping the tail, with the remainder pointed at — and this package's own 64 MiB run\n // budget (ADR-0003) is what makes it bite. `fullOutputPath` is written before the tail is cut, so\n // the pointer never names a partial file.\n const truncation = truncateTail(built, {\n maxLines: DEFAULT_MAX_LINES,\n maxBytes: DEFAULT_MAX_BYTES,\n });\n let fullOutputPath: string | undefined;\n let text = truncation.content;\n if (truncation.truncated) {\n fullOutputPath = writeFullOutput(built);\n text += `\\n\\n${truncationFooter(truncation, fullOutputPath)}`;\n }\n // The text block is the model's copy; hoisted images ride the same result as image blocks, which\n // is how pi's own `read` hands a picture to the model (ADR-0014). `PtcImage.data` is already\n // base64 — the shape the binding emitted, the shape the worker's JSON channel carried, and the\n // shape pi's image adapter (`getImageDimensions(img.data, img.mimeType)`) requires — so the\n // block is forwarded verbatim, with no re-encoding anywhere on the path.\n const content: (TextContent | ImageContent)[] = [\n { type: \"text\", text },\n ...images.map((image): ImageContent => ({\n type: \"image\",\n data: image.data,\n mimeType: image.mimeType,\n })),\n ];\n return {\n content,\n details: {\n surface,\n logs: [...outcome.logs],\n narrations: [...outcome.narrations],\n phases: [...outcome.phases],\n warnings,\n ...(outcome.value === undefined ? {} : { result: outcome.value }),\n durationMs: input.durationMs,\n imageCount: images.length,\n ...(fullOutputPath === undefined ? {} : { fullOutputPath }),\n },\n };\n}\n","/**\n * Shared TUI renderer for `ptc_run_code` and `ptc_workflow`.\n *\n * Both tools produce the same `PtcToolDetails` shape (see `common.ts`), so one pair of helpers\n * drives both `renderCall` and `renderResult`.\n *\n * ## Shape: a tree, not a payload dump\n *\n * The row reads as a node in the transcript tree:\n *\n * `└─ PTC <description>` (call row)\n * ` → {file, clipNow} • 1 image · 536ms` (result collapsed — summary + right meta)\n * ` ├─ code <line 1>` (result expanded — children with tree connectors)\n * ` │ <line 2>`\n * ` ├─ out [bash stdout] …`\n * ` ├─ value { file: … }`\n * ` └─ image 1 image attached`\n *\n * The summary line and the children share the call's column, so the connectors only need to\n * distinguish nesting (`├─/└─`) from continuation (`│/space`). Right-aligned meta lives on the\n * result row, not the call row, because `renderCall` does not see the run outcome — pi's TUI\n * calls renderCall and renderResult separately, and threading the result details into renderCall\n * would break that seam.\n *\n * ## What a PTC row must never do\n *\n * Print the completion value via `JSON.stringify`. Escaping turns a returned report into `\\n` noise\n * wrapped over five rows — that is the ugly report this renderer exists to fix (ADR-0013). Values\n * render through `renderModelValue`, which keeps newlines real and indents containers.\n */\nimport type { Theme } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { PtcToolDetails } from \"./common.ts\";\nimport type { PtcJsonObject, PtcJsonValue } from \"../runtime/protocol.ts\";\nimport { renderModelValue, sanitizeText } from \"./text.ts\";\n\n/** Call-row label per surface; `ptc_workflow` gets its own so the two rows stay tellable apart. */\nconst TOOL_TITLE: Record<PtcToolDetails[\"surface\"], string> = {\n run_code: \"PTC\",\n workflow: \"PTC workflow\",\n};\n\n/**\n * Tree-connector layout. The call row owns column 0–2 (`└─ `); everything below it sits at column 3.\n * Children are siblings of the call's content (the summary line `→ {hint}`), so a child row needs a\n * connector to distinguish itself from the summary — and a continuation prefix to distinguish its\n * own wrapped lines from the child's first line.\n *\n * - Root call row: `└─ PTC …`\n * - Root continuation: ` → {hint}` or ` failed: …` (summary; same column as children)\n * - First / middle child: ` ├─ code …`\n * - First / middle cont.: ` │ …`\n * - Last child: ` └─ image …`\n * - Last cont.: ` …` (no connector — same shape as a wrapped value line)\n *\n * `TREE_INDENT` (3 spaces) is the column children and root continuations share.\n */\nconst TREE_ROOT = \"└─ \"; // call-row prefix\nconst TREE_ROOT_CONT = \" \"; // continuation under the root (summary line + error text)\nconst TREE_INDENT = \" \"; // column children start at\nconst TREE_FIRST = \"├─ \"; // connector of first / middle child\nconst TREE_LAST = \"└─ \"; // connector of last child\nconst TREE_CONT_FIRST = \"│ \"; // continuation of first / middle child\nconst TREE_CONT_LAST = \" \"; // continuation of last child (just spaces — no connector)\n\n/**\n * Gutter width for child labels. `code` / `out` / `value` / `warn` / `image` are 3–5 chars; `phases`\n * is 6 — pad to 8 so the longest label gets two trailing spaces before its content.\n */\nconst LABEL_WIDTH = 8;\n\nconst MAX_CODE_LINES_EXPANDED = 3;\nconst MAX_CODE_LINE_CHARS = 120;\nconst MAX_LOG_LINES_EXPANDED = 12;\nconst MAX_PHASES_EXPANDED = 8;\nconst MAX_WARNINGS_EXPANDED = 4;\nconst MAX_RESULT_HINT_CHARS = 60;\nconst MAX_ERROR_CHARS = 120;\n\n/** One rendered row: left content, plus an optional segment pinned to the right edge. */\ninterface RowLine {\n readonly left: string;\n readonly right?: string;\n}\n\n/**\n * A `Component` that right-aligns one meta segment per line.\n *\n * Stateless: it holds already-styled strings and pads them at render time, so pi can re-render at a\n * new width (terminal resize) without any invalidation bookkeeping.\n */\nclass PtcRow {\n private readonly lines: readonly RowLine[];\n\n constructor(lines: readonly RowLine[]) {\n this.lines = lines;\n }\n\n /** Part of the `Component` contract; nothing is cached, so nothing has to be dropped. */\n invalidate(): void {}\n\n render(width: number): string[] {\n const safeWidth = Math.max(1, Math.floor(width));\n return this.lines.map((line) => alignRow(line, safeWidth));\n }\n}\n\n/**\n * Lay out one row inside `width` columns.\n *\n * With no meta segment the line is simply truncated. With one, the meta is what must survive — a\n * count that has been cut in half (`• 19 outpu…`) tells the reader nothing — so the left side gives\n * up room first, and only a meta wider than the whole viewport is truncated itself.\n */\nfunction alignRow(line: RowLine, width: number): string {\n const { left, right } = line;\n if (right === undefined) return truncateToWidth(left, width, \"…\");\n const rightWidth = visibleWidth(right);\n if (rightWidth >= width) return truncateToWidth(right, width, \"…\");\n const leftText = truncateToWidth(left, Math.max(0, width - rightWidth - 1), \"…\");\n const pad = Math.max(1, width - rightWidth - visibleWidth(leftText));\n return leftText + \" \".repeat(pad) + right;\n}\n\n/** Truncate `s` to `n` chars, appending `…` when the input was longer. */\nfunction truncateChars(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n - 1)}…` : s;\n}\n\n/** First non-blank line of a block of text, sanitized and capped. */\nfunction firstLine(value: string): string {\n const line = sanitizeText(value)\n .split(\"\\n\")\n .find((candidate) => candidate.trim().length > 0);\n return truncateChars((line ?? \"\").trim(), MAX_ERROR_CHARS);\n}\n\n/** `n` rendered as `1.2s` / `850ms` (so users can tell short from long at a glance). */\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(2).replace(/\\.?0+$/, \"\")}s`;\n}\n\n/** `3 phases` / `1 phase` — singular and plural are both reachable from a real run. */\nfunction plural(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? \"\" : \"s\"}`;\n}\n\n/**\n * Pull the first line of code that is actually doing something — not a leading comment, blank\n * line, or import. Used as the call row's fallback when the model sent no description.\n */\nexport function firstMeaningfulCodeLine(code: string): string | undefined {\n for (const raw of code.split(\"\\n\")) {\n const line = raw.trim();\n if (line.length === 0) continue;\n if (line.startsWith(\"//\")) continue;\n if (line.startsWith(\"/*\")) continue;\n if (line.startsWith(\"*\")) continue;\n // Strip trailing inline comment so the preview doesn't double as a docstring\n const withoutTrailingComment = line.replace(/\\s*\\/\\/.*$/, \"\");\n return truncateChars(withoutTrailingComment, MAX_CODE_LINE_CHARS);\n }\n return undefined;\n}\n\n/**\n * The completion value as a single line a human can read: the value itself while it stays short\n * and single-line, otherwise its shape (`{version, node}`, `Array(3)`).\n *\n * A multi-line string contributes its first non-blank line plus a `+N lines` tail, which is the\n * whole difference between one readable row and the escaped wall this replaces.\n */\nfunction resultHint(value: PtcJsonValue | undefined): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value === \"string\") {\n const lines = sanitizeText(value).split(\"\\n\");\n const first = lines.find((line) => line.trim().length > 0)?.trim() ?? \"\";\n const extra = lines.length > 1 ? ` (+${lines.length - 1} lines)` : \"\";\n if (first.length === 0 && extra.length === 0) return `\"\"`;\n return truncateChars(first, MAX_RESULT_HINT_CHARS) + extra;\n }\n if (value === null || typeof value !== \"object\") return String(value);\n const inline = renderModelValue(value);\n if (!inline.includes(\"\\n\") && inline.length <= MAX_RESULT_HINT_CHARS) return inline;\n if (Array.isArray(value)) return `Array(${value.length})`;\n const keys = Object.keys(value);\n if (keys.length === 0) return \"{}\";\n const head = keys.slice(0, 4).join(\", \");\n return `{${head}${keys.length > 4 ? `, +${keys.length - 4}` : \"\"}}`;\n}\n\n/**\n * Right-aligned meta: the run's countable facts, in the order a reader asks for them — how much\n * output, how long it took, how many phases (workflow), and anything that went wrong.\n */\nfunction metaText(details: PtcToolDetails | undefined, theme: Theme): string | undefined {\n if (details === undefined) return undefined;\n const segments: string[] = [];\n const outputLines = details.logs.length + details.narrations.length;\n if (outputLines > 0) segments.push(theme.fg(\"dim\", plural(outputLines, \"output line\")));\n // Images rode the tool result (ADR-0014): the count says so even though pi renders them itself.\n if (details.imageCount > 0) {\n segments.push(theme.fg(\"toolOutput\", plural(details.imageCount, \"image\")));\n }\n if (details.surface === \"workflow\" && details.phases.length > 0) {\n segments.push(theme.fg(\"dim\", plural(details.phases.length, \"phase\")));\n }\n if (details.warnings.length > 0) {\n segments.push(theme.fg(\"warning\", plural(details.warnings.length, \"warning\")));\n }\n // pi's truncation contract cut the text block: the tail is what the model read, the file is the rest.\n if (details.fullOutputPath !== undefined) segments.push(theme.fg(\"warning\", \"truncated\"));\n segments.push(theme.fg(\"dim\", formatDuration(details.durationMs)));\n return `${theme.fg(\"dim\", \"•\")} ${segments.join(theme.fg(\"dim\", \" · \"))}`;\n}\n\n/**\n * One child block in the expanded view: a `label` (right-padded into the gutter), N content lines,\n * and an optional `more` count for the `…+N more lines` tail.\n *\n * Block ordering is decided by the caller (see `renderPtcToolResultExpanded`); this module only\n * owns how a block lays itself out.\n */\ninterface ChildBlock {\n readonly label: string;\n readonly lines: readonly string[];\n readonly more?: number;\n}\n\n/** Render one child block as `RowLine[]`, with `├─` / `│` / `└─` connectors decided by position. */\nfunction renderChildBlock(child: ChildBlock, isLast: boolean, theme: Theme): RowLine[] {\n const firstPrefix = TREE_INDENT + (isLast ? TREE_LAST : TREE_FIRST);\n const contPrefix = TREE_INDENT + (isLast ? TREE_CONT_LAST : TREE_CONT_FIRST);\n const gutter = \" \".repeat(LABEL_WIDTH);\n const rows: RowLine[] = [];\n const first = child.lines[0] ?? \"\";\n rows.push({\n left: `${firstPrefix}${child.label.padEnd(LABEL_WIDTH)}${theme.fg(\"dim\", first)}`,\n });\n for (let i = 1; i < child.lines.length; i++) {\n rows.push({\n left: `${contPrefix}${gutter}${theme.fg(\"dim\", child.lines[i] ?? \"\")}`,\n });\n }\n if (child.more !== undefined && child.more > 0) {\n rows.push({\n left: `${contPrefix}${gutter}…+${child.more} more lines`,\n });\n }\n return rows;\n}\n\n// ---------------------------------------------------------------------------\n// Value-as-tree renderer (ADR-0013 §5)\n//\n// The value a program returns is a tree when it is a JSON object or array:\n// each property (or index) gets its own row with a ├─ / └─ connector, recursive\n// children inherit the parent's continuation bar, and a tail row collapses\n// what was withheld. This module only knows PtcJsonValue; consumers wrap each\n// emitted row with TREE_ROOT_CONT (so a row that starts with ├─ lands at the\n// call row's column-3 indent).\n// ---------------------------------------------------------------------------\n\n/** Defaults: collapse at 4 levels, 6 children per container, 120 chars per row. */\nexport const TREE_VALUE_MAX_DEPTH = 4;\nexport const TREE_VALUE_MAX_CHILDREN = 6;\nexport const TREE_VALUE_MAX_LINE_CHARS = 120;\n\n/** A JSON value is \"expandable\" when it is a non-empty object or array. */\nexport function isExpandableContainer(\n value: PtcJsonValue,\n): value is PtcJsonObject | PtcJsonValue[] {\n if (value === null || typeof value !== \"object\") return false;\n if (Array.isArray(value)) return value.length > 0;\n return Object.keys(value).length > 0;\n}\n\n/** Object keys that read as identifiers are left unquoted; the rest get JSON quoting. */\nconst TREE_BARE_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nfunction treeFormatKey(key: string): string {\n return TREE_BARE_KEY.test(key) ? key : JSON.stringify(key);\n}\n\n/** A scalar JSON value as a single readable token; strings are JSON-quoted, newlines folded. */\nfunction treeScalarPreview(value: PtcJsonValue, maxChars: number): string {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\" || typeof value === \"number\") return String(value);\n if (typeof value === \"string\") {\n const sanitized = sanitizeText(value);\n if (sanitized.length === 0) return '\"\"';\n if (sanitized.includes(\"\\n\")) {\n const flat = sanitized.replace(/\\n/g, \" \");\n const head = truncateChars(flat, Math.max(2, maxChars - 5));\n return JSON.stringify(head) + \"…\";\n }\n if (sanitized.length + 2 <= maxChars) return JSON.stringify(sanitized);\n return JSON.stringify(truncateChars(sanitized, Math.max(2, maxChars - 5))) + \"…\";\n }\n return \"?\";\n}\n\nfunction treeIsScalarJson(value: PtcJsonValue): boolean {\n return value === null || typeof value !== \"object\";\n}\n\n/** Try to render a container as a one-liner bracket form ({k: v} / [v, v]); undefined when it does not fit or children are deep. */\nfunction treeInlinePreview(value: PtcJsonValue, maxChars: number): string | undefined {\n if (maxChars < 8) return undefined;\n if (Array.isArray(value)) {\n if (value.length === 0) return \"[]\";\n if (value.length > 5 || !value.every(treeIsScalarJson)) return undefined;\n const items = value.map((item) =>\n treeScalarPreview(item, Math.max(2, Math.floor(maxChars / 4))),\n );\n const inline = \"[\" + items.join(\", \") + \"]\";\n return inline.length <= maxChars ? inline : undefined;\n }\n if (value === null || typeof value !== \"object\") return undefined;\n const keys = Object.keys(value);\n if (keys.length === 0) return \"{}\";\n if (keys.length > 5 || !Object.values(value).every(treeIsScalarJson)) return undefined;\n const entries = keys.map((key) => {\n const child = (value as Record<string, PtcJsonValue>)[key] ?? null;\n return (\n treeFormatKey(key) + \": \" + treeScalarPreview(child, Math.max(2, Math.floor(maxChars / 4)))\n );\n });\n const inline = \"{\" + entries.join(\", \") + \"}\";\n return inline.length <= maxChars ? inline : undefined;\n}\n\n/**\n * Append one pre-prefixed tree row to out, recursing for containers. Each level passes the\n * continuation indent its children should use; the connector itself encodes \"more siblings\".\n */\nfunction treeAppendNode(\n value: PtcJsonValue,\n label: string, // \"key: \", \"[0] \", or \"\" — the row's own text after the connector\n parentPrefix: string, // page above this row's connector (root's children pass \"\")\n isLast: boolean,\n maxDepth: number,\n maxChildren: number,\n maxLineChars: number,\n depth: number,\n out: string[],\n): void {\n const connector = isLast ? \"└─ \" : \"├─ \";\n const ownPrefix = parentPrefix + connector;\n const continuation = parentPrefix + (isLast ? \" \" : \"│ \");\n\n // Depth guard: collapse to ellipsis and stop recursing.\n if (depth >= maxDepth) {\n out.push(treeTruncateIndent(ownPrefix + label + \"…\", maxLineChars));\n return;\n }\n\n // Empty containers are leaves.\n if (Array.isArray(value) && value.length === 0) {\n out.push(treeTruncateIndent(ownPrefix + label + \"[]\", maxLineChars));\n return;\n }\n if (\n !Array.isArray(value) &&\n value !== null &&\n typeof value === \"object\" &&\n Object.keys(value).length === 0\n ) {\n out.push(treeTruncateIndent(ownPrefix + label + \"{}\", maxLineChars));\n return;\n }\n\n // Scalars.\n if (value === null || typeof value !== \"object\") {\n const budget = Math.max(0, maxLineChars - visibleWidth(ownPrefix) - label.length);\n out.push(\n treeTruncateIndent(ownPrefix + label + treeScalarPreview(value, budget), maxLineChars),\n );\n return;\n }\n\n // Small all-scalar containers collapse to one line.\n const inlineBudget = Math.max(0, maxLineChars - visibleWidth(ownPrefix) - label.length);\n const inline = treeInlinePreview(value, inlineBudget);\n if (inline !== undefined) {\n out.push(treeTruncateIndent(ownPrefix + label + inline, maxLineChars));\n return;\n }\n\n // Larger containers: header row, then recurse.\n const listLike = Array.isArray(value);\n const childCount = listLike ? value.length : Object.keys(value).length;\n let header: string;\n if (listLike) {\n header = \"Array(\" + childCount + \")\";\n } else {\n header =\n childCount > maxChildren\n ? \"{\" + maxChildren + \" of \" + childCount + \"}\"\n : \"{\" + childCount + \" keys}\";\n }\n out.push(treeTruncateIndent(ownPrefix + label + header, maxLineChars));\n\n const limit = Math.min(maxChildren, childCount);\n if (listLike) {\n for (let index = 0; index < limit; index += 1) {\n const isChildLast = index === limit - 1 && childCount <= limit;\n treeAppendNode(\n (value as PtcJsonValue[])[index] ?? null,\n \"[\" + index + \"] \",\n continuation,\n isChildLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n depth + 1,\n out,\n );\n }\n } else {\n const keys = Object.keys(value);\n for (let index = 0; index < limit; index += 1) {\n const isChildLast = index === limit - 1 && childCount <= limit;\n const key = keys[index] as string;\n treeAppendNode(\n (value as Record<string, PtcJsonValue>)[key] ?? null,\n treeFormatKey(key) + \": \",\n continuation,\n isChildLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n depth + 1,\n out,\n );\n }\n }\n if (childCount > limit) {\n const tail = listLike ? \"items\" : \"keys\";\n out.push(\n treeTruncateIndent(\n continuation + \"└─ \" + \"…+\" + (childCount - limit) + \" more \" + tail,\n maxLineChars,\n ),\n );\n }\n}\n\n/** Truncate a rendered row, preserving the leading tree-prefix indent (│/├/└ etc.). */\nfunction treeTruncateIndent(line: string, maxLineChars: number): string {\n if (visibleWidth(line) <= maxLineChars) return line;\n const match = line.match(/^([\\s│├└─]*)/);\n const indent = match && match[1] ? match[1] : \"\";\n const indentWidth = visibleWidth(indent);\n const budget = Math.max(0, maxLineChars - indentWidth - 1);\n let body = \"\";\n let used = 0;\n for (const ch of line.slice(indent.length)) {\n const w = visibleWidth(ch);\n if (used + w > budget) break;\n body += ch;\n used += w;\n }\n return indent + body + \"…\";\n}\n\n/**\n * Render a JSON value as an array of indented tree rows.\n *\n * Each row is a complete prefixed line ready to live under the call row's \" \" indent\n * (TREE_ROOT_CONT): root children start at \"├─ \" / \"└─ \", deeper levels add their own\n * connectors on top of the continuation bar. Caller supplies the row's TREE_ROOT_CONT — a\n * row that starts with \"├─ \" lands at column 3, exactly where other call-row children sit.\n *\n * Empty / scalar values return a one-row preview; non-empty containers recurse.\n */\nexport function renderValueTree(\n value: PtcJsonValue,\n options: {\n maxDepth?: number;\n maxChildren?: number;\n maxLineChars?: number;\n moreAfter?: boolean;\n } = {},\n): string[] {\n const maxDepth = options.maxDepth ?? TREE_VALUE_MAX_DEPTH;\n const maxChildren = options.maxChildren ?? TREE_VALUE_MAX_CHILDREN;\n const maxLineChars = options.maxLineChars ?? TREE_VALUE_MAX_LINE_CHARS;\n // A tree followed by sibling blocks must keep the chain open: its last row stays ├─.\n const moreAfter = options.moreAfter === true;\n if (!isExpandableContainer(value)) {\n // An empty container is not \"expandable\" but still must show its own shape, not the\n // scalar fallback (\"?\").\n if (value !== null && typeof value === \"object\") {\n return [Array.isArray(value) ? \"[]\" : \"{}\"];\n }\n return [treeScalarPreview(value, maxLineChars)];\n }\n const out: string[] = [];\n if (Array.isArray(value)) {\n const total = value.length;\n const limit = Math.min(maxChildren, total);\n for (let index = 0; index < limit; index += 1) {\n const isLast = !moreAfter && index === limit - 1 && total <= limit;\n treeAppendNode(\n value[index] ?? null,\n \"[\" + index + \"] \",\n \"\",\n isLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n 0,\n out,\n );\n }\n if (total > limit) {\n out.push((moreAfter ? \"├─ \" : \"└─ \") + \"…+\" + (total - limit) + \" more items\");\n }\n return out;\n }\n const keys = Object.keys(value);\n const total = keys.length;\n const limit = Math.min(maxChildren, total);\n for (let index = 0; index < limit; index += 1) {\n const isLast = !moreAfter && index === limit - 1 && total <= limit;\n const key = keys[index] as string;\n const childValue = (value as Record<string, PtcJsonValue>)[key] ?? null;\n treeAppendNode(\n childValue,\n treeFormatKey(key) + \": \",\n \"\",\n isLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n 0,\n out,\n );\n }\n if (total > limit) {\n out.push((moreAfter ? \"├─ \" : \"└─ \") + \"…+\" + (total - limit) + \" more keys\");\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Public API: renderCall / renderResult\n// ---------------------------------------------------------------------------\n\n/** The tool-call arguments the renderer reads (a subset of both tools' parameter objects). */\nexport interface PtcRenderArgs {\n description?: string;\n code?: string;\n script?: string;\n meta?: { name?: string };\n}\n\n/**\n * One-line tool-call header: the tree root, then the tool label, then the model's own description.\n *\n * The description is the only detail that gets to sit next to the label — the previous renderer\n * appended the first code line as well, which is what made the call row read as a paragraph rather\n * than a heading. The code is one `ctrl+e` away in the expanded view.\n */\nexport function renderPtcToolCall(\n args: PtcRenderArgs,\n theme: Theme,\n surface: PtcToolDetails[\"surface\"] = \"run_code\",\n): PtcRow {\n const description =\n (typeof args.description === \"string\" && args.description.trim().length > 0\n ? args.description.trim()\n : args.meta?.name) ?? \"\";\n const fallback = firstMeaningfulCodeLine(args.code ?? args.script ?? \"\");\n const detail = description.length > 0 ? description : (fallback ?? \"(no description)\");\n\n const left = `${theme.fg(\"toolTitle\", theme.bold(TOOL_TITLE[surface]))} ${theme.fg(\"accent\", detail)}`;\n return new PtcRow([{ left: `${TREE_ROOT}${left}` }]);\n}\n\n/** The result shape both tools hand to `renderResult` (a structural subset of `AgentToolResult`). */\nexport interface PtcRenderResult {\n readonly content?: ReadonlyArray<{ type: string; text?: string }>;\n readonly details?: PtcToolDetails;\n}\n\n/** Text body of a failed run — pi surfaces the thrown error in the first text block. */\nfunction errorText(result: PtcRenderResult): string {\n const text = result.content?.find(\n (block) => block.type === \"text\" && (block.text ?? \"\").length > 0,\n );\n return firstLine(text?.text ?? \"\") || \"failed\";\n}\n\n/**\n * The result area under the call row.\n *\n * One of four shapes, all hanging at column 3 so a column of PTC rows stays scannable:\n * - error: one line with the failure reason in the error colour.\n * - no completion value: one line, \"done\".\n * - scalar / empty container: one line, `→ <hint>` (or \"done\" for null).\n * - non-empty container: a tree of rows (recursive `├─/└─/│` connectors from\n * `renderValueTree`), with the right-aligned meta pinned to\n * the tree's first row. ADR-0013 §5.\n *\n * Each row returns its left-side text WITHOUT the `TREE_ROOT_CONT` prefix being in scope;\n * we prepend it here so the same output works for both surfaces.\n */\nfunction resultArea(\n result: PtcRenderResult,\n isError: boolean,\n theme: Theme,\n moreAfter = false,\n): RowLine[] {\n const right = metaText(result.details, theme);\n const attachRight = (row: RowLine): RowLine => (right === undefined ? row : { ...row, right });\n\n if (isError) {\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"error\", `failed: ${errorText(result)}`)}`,\n }),\n ];\n }\n\n const value = result.details?.result;\n if (value === undefined) {\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"muted\", \"done\")}`,\n }),\n ];\n }\n\n if (isExpandableContainer(value)) {\n const tree = renderValueTree(value, {\n maxDepth: TREE_VALUE_MAX_DEPTH,\n maxChildren: TREE_VALUE_MAX_CHILDREN,\n maxLineChars: TREE_VALUE_MAX_LINE_CHARS,\n moreAfter,\n });\n return tree.map((line, index) => {\n const row: RowLine = { left: `${TREE_ROOT_CONT}${line}` };\n return index === 0 && right !== undefined ? { ...row, right } : row;\n });\n }\n\n // Scalar / empty / null — one-line `→ <hint>` (or \"done\" when no hint is producible).\n const hint = resultHint(value);\n if (hint === undefined) {\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"muted\", \"done\")}`,\n }),\n ];\n }\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"dim\", `→ ${hint}`)}`,\n }),\n ];\n}\n\n/**\n * Collapsed result: the result area only.\n *\n * A scalar row fits on one line; a container value expands into a tree (1+ lines). Right-aligned\n * meta (output lines / phases / warnings / duration) is pinned to the first row of the area.\n */\nexport function renderPtcToolResultCollapsed(\n result: PtcRenderResult,\n isError: boolean,\n theme: Theme,\n): PtcRow {\n return new PtcRow(resultArea(result, isError, theme));\n}\n\n/**\n * Expanded view: the result area, then the run's children as labelled blocks.\n *\n * Each child block opens with a fixed-width gutter (`code`, `phases`, `log`, `out`, `warn`,\n * `image`); the `value` block is gone — for a scalar value the result area already shows the\n * hint, and for a container value the area shows the full tree (ADR-0013 §5). Children that\n * overflow cap themselves and report `+N more lines`; the view stays bounded.\n */\nexport function renderPtcToolResultExpanded(\n result: PtcRenderResult,\n args: PtcRenderArgs,\n isError: boolean,\n theme: Theme,\n): PtcRow {\n if (isError) {\n const lines: RowLine[] = resultArea(result, true, theme);\n // The failure text is the reason the reader expanded the row at all: give it the full width\n // under the summary, no tree connectors (it is the error message, not a structured block).\n const text = sanitizeText(\n result.content?.find((block) => block.type === \"text\")?.text ?? errorText(result),\n );\n for (const line of text.split(\"\\n\").slice(0, MAX_LOG_LINES_EXPANDED)) {\n lines.push({ left: `${TREE_ROOT_CONT}${theme.fg(\"error\", line)}` });\n }\n return new PtcRow(lines);\n }\n\n const details = result.details;\n const children: ChildBlock[] = [];\n\n const code = args.code ?? args.script ?? \"\";\n if (code.length > 0) {\n const codeLines = code.split(\"\\n\").map((line) => truncateChars(line, MAX_CODE_LINE_CHARS));\n const shown = codeLines.slice(0, MAX_CODE_LINES_EXPANDED);\n children.push({\n label: \"code\",\n lines: shown,\n ...(codeLines.length > shown.length ? { more: codeLines.length - shown.length } : {}),\n });\n }\n\n if (details !== undefined && details.surface === \"workflow\" && details.phases.length > 0) {\n const shown = details.phases.slice(0, MAX_PHASES_EXPANDED);\n const tail = details.phases.length > shown.length ? \" …\" : \"\";\n children.push({ label: \"phases\", lines: [`${shown.join(\" → \")}${tail}`] });\n }\n\n if (details !== undefined && details.narrations.length > 0) {\n const shown = details.narrations.slice(0, MAX_LOG_LINES_EXPANDED);\n children.push({\n label: \"log\",\n lines: shown.map((message) => sanitizeText(message)),\n ...(details.narrations.length > shown.length\n ? { more: details.narrations.length - shown.length }\n : {}),\n });\n }\n\n if (details !== undefined && details.logs.length > 0) {\n const shown = details.logs.slice(0, MAX_LOG_LINES_EXPANDED);\n children.push({\n label: \"out\",\n lines: shown.map((line) => sanitizeText(line)),\n ...(details.logs.length > shown.length ? { more: details.logs.length - shown.length } : {}),\n });\n }\n\n if (details !== undefined && details.warnings.length > 0) {\n const shown = details.warnings.slice(0, MAX_WARNINGS_EXPANDED);\n children.push({\n label: \"warn\",\n lines: shown.map((warning) => sanitizeText(warning)),\n ...(details.warnings.length > shown.length\n ? { more: details.warnings.length - shown.length }\n : {}),\n });\n }\n\n if (details !== undefined && details.imageCount > 0) {\n const desc =\n details.imageCount === 1 ? \"1 image attached\" : `${details.imageCount} images attached`;\n children.push({ label: \"image\", lines: [desc] });\n }\n\n // Result rows first, then the labelled blocks. `moreAfter` keeps the connector chain open\n // while blocks follow, so the value tree and the blocks read as one list of siblings.\n const lines: RowLine[] = resultArea(result, false, theme, children.length > 0);\n\n for (let index = 0; index < children.length; index += 1) {\n const isLast = index === children.length - 1;\n lines.push(...renderChildBlock(children[index] ?? { label: \"\", lines: [] }, isLast, theme));\n }\n\n return new PtcRow(lines);\n}\n","/**\n * `ptc_run_code` — the unstructured PTC surface.\n *\n * The model writes the body of an async function; inside it, pi's built-in tools are reachable as\n * `tools.<name>(args)` (all seven: read, bash, edit, write, grep, find, ls — ADR-0005's set), and\n * the program's return value plus `console.log` output are what come back. There are no helpers\n * on this surface: `log` / `phase` / `parallel` / `pipeline` belong to `ptc_workflow` (G1 #13 →\n * decision B; a call to a helper that does not exist is a plain `ReferenceError` through the\n * normal code-run error path).\n *\n * Scope notes, so this stays a faithful subset of DSH's `run_code`:\n * - DSH's `sandbox_permissions` / `justification` are deliberately absent — they exist to request\n * a wider sandbox from an approval pipeline this package does not have (ADR-0007 ships no OS\n * sandbox, and the map decision on #8 dropped the approval path).\n * - `timeoutMs: 0` does not disable the deadline; it falls back to `DEFAULT_CONFIG.timeoutMs`,\n * exactly as DSH's resolver does (R1 §3). The description names the default and the ceiling.\n */\nimport { defineTool } from \"@earendil-works/pi-coding-agent\";\nimport type { ToolDefinition } from \"@earendil-works/pi-coding-agent\";\nimport { Type } from \"typebox\";\nimport type { TNumber, TOptional, TObject, TString } from \"typebox\";\nimport { createBuiltinBindings } from \"../runtime/bindings.ts\";\nimport { runPtcProgram } from \"../runtime/dispatcher.ts\";\nimport type { WorkerPool } from \"../runtime/worker-pool.ts\";\nimport { DEFAULT_CONFIG } from \"../runtime/limits.ts\";\nimport {\n codeRunFailedError,\n PTC_RUN_CODE_SNIPPET,\n PTC_TOOL_GUIDELINES,\n renderToolResult,\n resolveBindingNames,\n resolveToolCwd,\n} from \"./common.ts\";\nimport type { PtcToolDetails, PtcToolOptions } from \"./common.ts\";\nimport {\n renderPtcToolCall,\n renderPtcToolResultCollapsed,\n renderPtcToolResultExpanded,\n} from \"./render.ts\";\n\nconst DESCRIPTION = [\n \"Run a TypeScript program that composes pi's tools in one shot. Required arguments: `code` —\",\n \"the body of an async function (top-level `return` and `await` work; type annotations are\",\n \"advisory, the code runs type-stripped) — and `description`, a 5-10 word summary of what the\",\n \"program does.\",\n \"\",\n \"Inside the program, call this session's enabled built-in tools as `tools.<name>(args)` — e.g.\",\n '`await tools.read({ path: \"src/index.ts\" })` or `await tools.bash({ command: \"npm test\" })`.',\n \"The bound names mirror the session's active tools (a default session has `read`, `bash`, `edit`,\",\n \"`write`); calling a name that is not bound rejects with an error the program can catch, and\",\n \"independent calls may overlap under `Promise.all`.\",\n \"\",\n \"Only the program's return value and its `console.log` output come back. This surface has no\",\n \"helpers: `log` / `phase` / `parallel` / `pipeline` exist only in `ptc_workflow`.\",\n \"\",\n \"Image-bearing tool results inside the program (a `tools.read` on a PNG, say) are attached to you\",\n \"after the run, so never return image data as the completion value — that only spends your context\",\n \"on base64.\",\n].join(\"\\n\");\n\n/** Schema type written out explicitly for `isolatedDeclarations` (emit must not infer it). */\ntype RunCodeParameters = TObject<{\n code: TString;\n description: TString;\n timeoutMs: TOptional<TNumber>;\n}>;\n\nconst PARAMETERS: RunCodeParameters = Type.Object({\n code: Type.String({\n description: \"The program: the body of an async TypeScript function.\",\n }),\n description: Type.String({\n description:\n \"Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI).\",\n }),\n timeoutMs: Type.Optional(\n Type.Number({\n description:\n `Elapsed deadline in milliseconds for the whole run, including tool calls. ` +\n `Omit it (or pass 0) for the default of ${DEFAULT_CONFIG.timeoutMs / 1000} s; ` +\n `anything above ${DEFAULT_CONFIG.maxTimeoutMs / 1000} s is capped.`,\n }),\n ),\n});\n\n/**\n * `PtcToolOptions` plus this tool's per-turn pool seam (ADR-0017 §1).\n *\n * The extension owns one `TurnPools` per agent turn and passes a getter for the\n * `run_code` surface's pool. The getter is read **per execute**, not at registration,\n * because the pool is replaced at every turn boundary. Omitting it means one fresh\n * worker per run — the pre-pool behaviour, and what direct library use gets.\n *\n * With a pool, consecutive runs of the same turn **share one warm worker**: that is\n * what pooling buys, and it is why \"every run gets a fresh worker\" is no longer true\n * on this path. What that sharing does and does not guarantee is worth being exact\n * about. `acquire()` guarantees *ownership*: a worker is in flight to exactly one run\n * until that run releases it, and a second acquire at capacity queues behind the wait\n * list rather than reusing an occupied worker (ADR-0017 §4). It does **not** guarantee\n * that only one program is physically executing in the isolate — the host abandons a\n * run by closing its control port and releasing the worker without waiting for the\n * program to unwind, so a **superseded** run's program can still be finishing inside\n * the worker while the next run is already in flight (ADR-0017 §10(a) / §10(h)). What\n * the pool promises is \"one run at a time owns a worker's control port and result\n * channel\", not \"one program at a time\".\n *\n * Isolation of the shared realm therefore does not come from the ending run's reset\n * handshake — a superseded run's `reset()` is a generation-guarded no-op (ADR-0017 §5 /\n * §10(a)). It comes from the next run's init-side clearing, which unconditionally\n * reinstalls the frozen env and console, restores `globalThis` to the boot-time warm\n * baseline and resets the run bookkeeping before the program starts (ADR-0017 §10(d)).\n */\nexport interface PtcRunCodeToolOptions extends PtcToolOptions {\n getPool?: () => WorkerPool | undefined;\n}\n\n/**\n * Build the `ptc_run_code` tool definition.\n *\n * Called once by the extension factory. Each execute builds its own binding table against the\n * run's own cwd and reads `getPool()` for the surface's (possibly absent) pool — see\n * `PtcRunCodeToolOptions` for what pooling does and does not change about isolation.\n */\nexport function createPtcRunCodeTool(\n options: PtcRunCodeToolOptions = {},\n): ToolDefinition<RunCodeParameters, PtcToolDetails> {\n return defineTool({\n name: \"ptc_run_code\",\n label: \"PTC Run Code\",\n description: DESCRIPTION,\n promptSnippet: PTC_RUN_CODE_SNIPPET,\n promptGuidelines: [...PTC_TOOL_GUIDELINES],\n parameters: PARAMETERS,\n async execute(_toolCallId, params, signal, _onUpdate, ctx) {\n const cwd = resolveToolCwd(ctx);\n const names = resolveBindingNames(options.getBindingSourceNames?.());\n const startedAt = Date.now();\n const pool = options.getPool?.();\n const outcome = await runPtcProgram({\n code: params.code,\n surface: \"run_code\",\n cwd,\n bindings: createBuiltinBindings({ cwd, names }),\n ...(params.timeoutMs === undefined ? {} : { timeoutMs: params.timeoutMs }),\n ...(signal === undefined ? {} : { signal }),\n ...(options.config === undefined ? {} : { config: options.config }),\n ...(pool === undefined ? {} : { pool }),\n });\n if (outcome.error !== undefined) throw codeRunFailedError(outcome);\n return renderToolResult({ outcome, surface: \"run_code\", durationMs: Date.now() - startedAt });\n },\n\n // Compact TUI rendering — see `render.ts` and ADR-0013. `renderShell` stays at the default\n // `ToolExecutionComponent` shell so PTC rows match the visual rhythm of `read`/`bash`.\n renderCall(args, theme) {\n return renderPtcToolCall(args, theme, \"run_code\");\n },\n renderResult(result, options, theme, context) {\n if (options.expanded) {\n return renderPtcToolResultExpanded(result, context.args, context.isError, theme);\n }\n return renderPtcToolResultCollapsed(result, context.isError, theme);\n },\n });\n}\n","/**\n * `ptc_workflow` — the structured PTC surface.\n *\n * Same worker program, same bindings, same Node surface as `ptc_run_code`; what it adds is a plan\n * (`meta` with an ordered phase list), plain-JSON input (`args`, bound as the program's `args`\n * global) and the four workflow helpers: `log` / `phase` / `parallel` / `pipeline` (G1 #13 →\n * decision B — there is no `agent()`, not even a stub).\n *\n * Three decisions recorded here rather than re-litigated per call:\n * - **Language**: the same type-stripping pipeline as `ptc_run_code`. The worker compiles both\n * surfaces through one `compileProgram`, and the pipeline is type-*strip* only — it adds no\n * runtime semantics that a workflow could not tolerate. There is no JS-only mode because there\n * is no concrete reason for one (the old pi-ptc \"JS-only\" idea is explicitly not inherited).\n * - **Phase strictness**: warning, not error. A `phase(title)` outside `meta.phases` is a plan\n * drift — usually a typo or a forgotten declaration — but the run has already happened by the\n * time the titles are known, and failing then would discard a completed result over a cosmetic\n * mismatch. Each distinct unlisted title is reported once, as a `Warning:` line in `content`\n * and in `details.warnings`. When `meta.phases` is absent there is no declared plan to drift\n * from, so nothing is reported.\n * - **`args`**: validated as plain JSON before dispatch (see `validateWorkflowArgs`), because a\n * function/symbol/`undefined`/cycle would otherwise be silently dropped or mangled by the\n * structured clone on its way to the worker.\n */\nimport { defineTool } from \"@earendil-works/pi-coding-agent\";\nimport type { ToolDefinition } from \"@earendil-works/pi-coding-agent\";\nimport { Type } from \"typebox\";\nimport type { Static, TArray, TObject, TOptional, TRecord, TString, TUnknown } from \"typebox\";\nimport { createBuiltinBindings } from \"../runtime/bindings.ts\";\nimport { runPtcProgram } from \"../runtime/dispatcher.ts\";\nimport type { PtcRunOutcome } from \"../runtime/dispatcher.ts\";\nimport type { WorkerPool } from \"../runtime/worker-pool.ts\";\nimport {\n codeRunFailedError,\n PTC_TOOL_GUIDELINES,\n PTC_WORKFLOW_SNIPPET,\n renderToolResult,\n resolveBindingNames,\n resolveToolCwd,\n} from \"./common.ts\";\nimport type { PtcToolDetails, PtcToolOptions } from \"./common.ts\";\nimport {\n renderPtcToolCall,\n renderPtcToolResultCollapsed,\n renderPtcToolResultExpanded,\n} from \"./render.ts\";\n\nconst DESCRIPTION = [\n \"Run a structured TypeScript workflow: a named plan that reports phases and narration as it\",\n \"goes. `script` is the body of an async function, `meta` names it (`{ name, description, phases? }`,\",\n \"where `phases` is the ordered plan), and optional `args` is plain-JSON input bound to the\",\n \"program's `args` global.\",\n \"\",\n \"Helpers inside the script: `log(message)` narrates progress; `phase(title)` frames the workflow\",\n \"(titles outside `meta.phases` are reported as warnings); `parallel(thunks)` runs thunks\",\n \"concurrently, a failed item becoming `null`; `pipeline(items, ...stages)` chains stages per item\",\n \"with the same per-item `null` on failure. There is no `agent()` helper.\",\n \"\",\n \"Tools are reachable as `tools.<name>(args)` exactly as in `ptc_run_code`, and the bound names\",\n \"mirror the session's enabled tools. What comes back is the script's return value, its\",\n \"`log`/`phase` narration and its `console.log` output.\",\n \"\",\n \"Image-bearing tool results inside the script are attached to you after the run — never return\",\n \"image data as the script's value.\",\n].join(\"\\n\");\n\n/** Schema types written out explicitly for `isolatedDeclarations` (emit must not infer them). */\ntype WorkflowPhaseSchema = TObject<{ name: TString }>;\n\ntype WorkflowParameters = TObject<{\n meta: TObject<{\n name: TString;\n description: TString;\n phases: TOptional<TArray<WorkflowPhaseSchema>>;\n }>;\n script: TString;\n args: TOptional<TRecord<string, TUnknown>>;\n}>;\n\nconst PARAMETERS: WorkflowParameters = Type.Object({\n meta: Type.Object(\n {\n name: Type.String({ description: \"Short workflow name, shown in the UI.\" }),\n description: Type.String({ description: \"What this workflow does, in active voice.\" }),\n phases: Type.Optional(\n Type.Array(\n Type.Object({\n name: Type.String({ description: \"Phase title, matched against phase(title) calls.\" }),\n }),\n {\n description:\n \"Ordered plan of the workflow's phases. phase() titles outside this list are reported as warnings.\",\n },\n ),\n ),\n },\n { description: \"Workflow metadata: what this run is and which phases it declares.\" },\n ),\n script: Type.String({ description: \"The program: the body of an async TypeScript function.\" }),\n args: Type.Optional(\n Type.Record(Type.String(), Type.Unknown(), {\n description:\n \"Plain-JSON input bound to the program's `args` global. No functions, symbols, undefined values or cycles.\",\n }),\n ),\n});\n\n/** Parameters after pi's schema validation; `args` is still unvalidated plain JSON to be checked. */\nexport type PtcWorkflowParams = Static<typeof PARAMETERS>;\n\nfunction describeValue(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n return `a ${typeof value}`;\n}\n\n/**\n * Reject anything that is not plain JSON, with a path-qualified reason.\n *\n * The rule is the worker's own result rule (`toJsonValue`) applied one direction earlier, so both\n * ends agree on what \"plain JSON\" means: objects with `Object.prototype` (or null), arrays,\n * strings, finite numbers, booleans and null. Class instances, functions, symbols, bigints,\n * non-finite numbers, circular references and `undefined` values are all rejected — `undefined`\n * because the ticket calls it out and because a structured clone would drop it silently, leaving\n * the script reading a key that is not there.\n */\nexport function validateWorkflowArgs(args: unknown): void {\n if (args === undefined) return;\n if (typeof args !== \"object\" || args === null || Array.isArray(args)) {\n throw new TypeError(\n `ptc_workflow args must be a plain JSON object, received ${describeValue(args)}`,\n );\n }\n for (const key of Object.keys(args)) {\n const problem = findJsonProblem(\n (args as Record<string, unknown>)[key],\n `args.${key}`,\n new Set(),\n );\n if (problem !== undefined)\n throw new TypeError(`ptc_workflow args must be plain JSON: ${problem}`);\n }\n}\n\n/** Path-qualified reason a value cannot cross the wire as plain JSON, or `undefined` when it can. */\nfunction findJsonProblem(value: unknown, path: string, seen: Set<object>): string | undefined {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return undefined;\n if (typeof value === \"undefined\") return `${path} is undefined`;\n if (typeof value === \"number\") {\n return Number.isFinite(value)\n ? undefined\n : `${path} is ${String(value)}, which is not representable as JSON`;\n }\n if (typeof value !== \"object\") return `${path} is ${describeValue(value)}`;\n\n const container = value as object;\n if (seen.has(container)) return `${path} is a circular reference`;\n seen.add(container);\n try {\n if (Array.isArray(container)) {\n for (let index = 0; index < container.length; index += 1) {\n const problem = findJsonProblem(container[index], `${path}[${index}]`, seen);\n if (problem !== undefined) return problem;\n }\n return undefined;\n }\n const prototype = Object.getPrototypeOf(container);\n if (prototype !== Object.prototype && prototype !== null) {\n const name = (container as { constructor?: { name?: string } }).constructor?.name;\n return `${path} is a ${typeof name === \"string\" && name.length > 0 ? name : \"non-plain object\"}, not plain JSON`;\n }\n for (const key of Object.keys(container)) {\n const problem = findJsonProblem(\n (container as Record<string, unknown>)[key],\n `${path}.${key}`,\n seen,\n );\n if (problem !== undefined) return problem;\n }\n return undefined;\n } finally {\n seen.delete(container);\n }\n}\n\n/**\n * Collect the plan-drift warnings for one workflow run: distinct `phase()` titles the declared\n * `meta.phases` list does not mention, in first-seen order.\n */\nexport function unlistedPhaseWarnings(\n outcome: PtcRunOutcome,\n declared: readonly string[] | undefined,\n): string[] {\n if (declared === undefined || declared.length === 0) return [];\n const known = new Set(declared);\n const reported = new Set<string>();\n const warnings: string[] = [];\n for (const title of outcome.phases) {\n if (known.has(title) || reported.has(title)) continue;\n reported.add(title);\n warnings.push(\n `phase \"${title}\" is not listed in meta.phases (declared: ${[...known].join(\", \")})`,\n );\n }\n return warnings;\n}\n\n/**\n * `PtcToolOptions` plus this tool's per-turn pool seam (ADR-0017 §1). See\n * `createPtcRunCodeTool`'s options for why the getter is read per execute and what\n * omitting it means.\n *\n * The pooling story is the same as `ptc_run_code`'s, on this surface's own pool (ADR-0017\n * §2): consecutive runs of one turn share a warm worker, and isolation between them comes\n * from the **next** run's init-side cleanup (ADR-0017 §10(d)) — not from the previous run's\n * reset handshake, which is a no-op for a superseded run (ADR-0017 §10(a)). `acquire()`\n * guarantees one run holds the worker at a time (ADR-0017 §4), though a superseded run's\n * program may still be unwinding inside it. The worker program differs — it has the\n * workflow helpers installed — which is why the two surfaces do not share a pool.\n */\nexport interface PtcWorkflowToolOptions extends PtcToolOptions {\n getPool?: () => WorkerPool | undefined;\n}\n\n/**\n * Build the `ptc_workflow` tool definition.\n *\n * `args` is validated before `runPtcProgram` is called, so a malformed payload never acquires or\n * spawns a worker. Everything after dispatch is identical to `ptc_run_code`, plus the phase\n * roll-up.\n */\nexport function createPtcWorkflowTool(\n options: PtcWorkflowToolOptions = {},\n): ToolDefinition<WorkflowParameters, PtcToolDetails> {\n return defineTool({\n name: \"ptc_workflow\",\n label: \"PTC Workflow\",\n description: DESCRIPTION,\n promptSnippet: PTC_WORKFLOW_SNIPPET,\n promptGuidelines: [...PTC_TOOL_GUIDELINES],\n parameters: PARAMETERS,\n async execute(_toolCallId, params, signal, _onUpdate, ctx) {\n validateWorkflowArgs(params.args);\n const cwd = resolveToolCwd(ctx);\n const names = resolveBindingNames(options.getBindingSourceNames?.());\n const startedAt = Date.now();\n const pool = options.getPool?.();\n const outcome = await runPtcProgram({\n code: params.script,\n surface: \"workflow\",\n cwd,\n bindings: createBuiltinBindings({ cwd, names }),\n ...(params.args === undefined ? {} : { args: params.args }),\n ...(signal === undefined ? {} : { signal }),\n ...(options.config === undefined ? {} : { config: options.config }),\n ...(pool === undefined ? {} : { pool }),\n });\n if (outcome.error !== undefined) throw codeRunFailedError(outcome);\n const declared = params.meta.phases?.map((phase) => phase.name);\n return renderToolResult({\n outcome,\n surface: \"workflow\",\n warnings: unlistedPhaseWarnings(outcome, declared),\n durationMs: Date.now() - startedAt,\n });\n },\n\n // Same renderer as `ptc_run_code`; the `workflow` label tells the two rows apart, the collapsed\n // meta adds a phase count, and the expanded view puts the phases roll-up first so a reader sees\n // the workflow plan up top.\n renderCall(args, theme) {\n return renderPtcToolCall(args, theme, \"workflow\");\n },\n renderResult(result, options, theme, context) {\n if (options.expanded) {\n return renderPtcToolResultExpanded(result, context.args, context.isError, theme);\n }\n return renderPtcToolResultCollapsed(result, context.isError, theme);\n },\n });\n}\n","/**\n * Per-turn pool holder: one `WorkerPool` per PTC surface, created lazily on first use\n * and retired together at turn end (ADR-0017 §1–§2).\n *\n * The pi extension owns one `TurnPools` per agent turn and hands each tool a getter for\n * its own surface's pool. Lazy creation is what makes the cold turn cheap: a turn that\n * never runs a PTC program never spawns a worker, and a turn that runs one `ptc_run_code`\n * pays exactly one cold spawn (which the pool then keeps warm for the rest of the turn).\n *\n * `run_code` and `workflow` do not share a pool (ADR-0017 §2): their worker surfaces differ\n * (`installWorkflowHelpers` is conditional in `worker-main.ts`), and one pool per surface\n * keeps that story simple. The workers themselves carry no surface state — each run's\n * `init` frame names its surface — so the split is about ownership, not correctness.\n */\nimport { createWorkerEnv, resolveConfig } from \"./limits.ts\";\nimport type { PtcConfig, PtcSurface } from \"./limits.ts\";\nimport { WorkerPool, workerSpawnOptions } from \"./worker-pool.ts\";\nimport { buildWorkerUrl } from \"./worker-source.ts\";\n\nexport interface TurnPoolsOptions {\n /** Per-turn limit overrides on top of `DEFAULT_CONFIG` (pool size, acquire bound, V8 caps). */\n config?: Partial<PtcConfig>;\n}\n\nexport class TurnPools {\n readonly #config: PtcConfig;\n readonly #pools = new Map<PtcSurface, WorkerPool>();\n\n constructor(options: TurnPoolsOptions = {}) {\n this.#config = resolveConfig(options.config);\n }\n\n /**\n * The pool for one surface, created on first request. Warm workers from an earlier\n * run in this turn are served by the same pool (that is the point); a fresh\n * `TurnPools` is how a turn gets a fresh set.\n */\n get(surface: PtcSurface): WorkerPool {\n const existing = this.#pools.get(surface);\n if (existing !== undefined) return existing;\n\n const env = createWorkerEnv();\n const pool = new WorkerPool({\n buildWorkerUrl,\n size: this.#config.poolSize,\n acquireTimeoutMs: this.#config.poolAcquireTimeoutMs,\n // Pool retirement is part of the turn contract too: the `turn_end` drain waits for\n // in-flight runs only as long as this holder was configured to (ADR-0017 §1 retires the\n // holder's pools; `drainGraceMs` is the bound it drains within).\n drainGraceMs: this.#config.drainGraceMs,\n // Spawn-time options — `env`, `workerData` and the V8 caps — are built here, once, by the\n // shared factory (`workerSpawnOptions`), and a warm worker keeps them for the rest of the\n // turn: a live worker's heap ceiling is not a runtime setting.\n //\n // So a per-run `PtcConfig` override is **inert for these fields on the pooled\n // path**: `runPtcProgram({ config: { maxOldGenerationSizeMb: X } })` cannot reseat\n // the caps of a worker that already exists, and the caps in force are this holder's,\n // resolved from the `TurnPools` constructor. The cold path (no `pool`) still honours\n // a per-run config, because `dispatcher.ts` spawns that worker per run. A caller\n // wanting different caps, env or `workerData` needs a differently-constructed\n // `TurnPools` — or no pool — not a different per-run config. `workerData` carries no\n // `runId` for the same reason: a warm worker outlives any one run.\n workerOptions: workerSpawnOptions({\n surface,\n env,\n limits: this.#config,\n }),\n });\n this.#pools.set(surface, pool);\n return pool;\n }\n\n /**\n * Terminate every pool this holder created. Idempotent, and safe to call from a turn\n * boundary hook: a second call finds nothing to drain.\n */\n async drain(): Promise<void> {\n const pools = [...this.#pools.values()];\n this.#pools.clear();\n await Promise.all(pools.map((pool) => pool.drain()));\n }\n}\n","/**\n * PTC default mode — the session-level tool-restriction state machine.\n *\n * ## What \"mode\" means here\n *\n * While enabled, the session's visible tool loadout is narrowed so the model reaches\n * `read` / `bash` / `edit` / `write` / `grep` / `find` / `ls` only from *inside* a program\n * (`tools.<name>(args)`). That is DSH's PTC-preset shape, and it is what makes the mode visible\n * in the TUI: the transcript stops showing file-operation rows and shows PTC rows instead.\n *\n * The mode is decided per session (`session_start`), announced once, and always reversible.\n *\n * ## Why bindings need a base snapshot (the trap this module exists to avoid)\n *\n * `resolveBindingNames()` computes bindings as `BUILTIN_BINDING_NAMES ∩ pi.getActiveTools()`\n * (T7, ADR-0005 addendum). But `pi.getActiveTools()` reads the *live* loadout — the very set this\n * mode narrows. Hiding the built-ins therefore empties the binding table, and every\n * `tools.read(...)` fails with \"not bound\": the extension would neuter itself.\n *\n * The fix is a **base snapshot**. `base` is captured *before* the mode hides anything, and\n * bindings derive from `base` rather than the live loadout while the mode is on. T7's invariant\n * survives, because `base` is itself read from `pi.getActiveTools()`: a session launched with\n * `--tools read` has `base = [\"read\"]`, so bindings stay `[\"read\"]`. The mode never widens access\n * beyond what the session was launched with — it only makes its **own** narrowing transparent to\n * the binding table.\n *\n * ## Fail-safe on external loadout changes\n *\n * Another extension can call `setActiveTools()` (pi's own `preset.ts` and `tools.ts` examples do).\n * `ourLoadout` records the exact array this mode wrote, so a mismatch means someone else owns the\n * loadout now. The mode then exits and restores `base` instead of fighting over it.\n *\n * ## Where the mode never runs\n *\n * Print / JSON / RPC sessions (`ctx.mode !== \"tui\"`) are left alone: forcing the shape on CI\n * scripts buys nothing and breaks them. A session that was *launched* with an explicit tool\n * restriction is left alone too — see `decideModeEntry`.\n */\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { BUILTIN_BINDING_NAMES } from \"../runtime/bindings.ts\";\n\n/** The two surfaces this package exposes. `/ptc on` needs at least one of them active. */\nexport const PTC_MODE_TOOL_NAMES: readonly string[] = [\"ptc_run_code\", \"ptc_workflow\"];\n\n/**\n * Tools that must all be present for the **automatic** entry path to proceed.\n *\n * These four are pi's default session surface (`agent-session.js`: \"[\"read\", \"bash\", \"edit\",\n * \"write\"]\"), so \"all four present\" means \"this session was not narrowed\". Comparing against\n * `BUILTIN_BINDING_NAMES` instead would be wrong: that list is the set of names that *can* be\n * bound, not what a default session has (`grep` / `find` / `ls` are bindable but off by default),\n * so requiring all seven would misread every ordinary session as restricted and never turn on.\n */\nexport const MODE_REQUIRED_TOOL_NAMES: readonly string[] = [\"read\", \"bash\", \"edit\", \"write\"];\n\n/** `customType` for the persisted mode entry (`pi.appendEntry`). */\nexport const PTC_MODE_ENTRY_TYPE = \"ptc-mode\";\n\n/** Footer status key (`ctx.ui.setStatus`). */\nexport const PTC_MODE_STATUS_KEY = \"ptc-mode\";\n\n/** Config file name, resolved against pi's agent dir (`~/.pi/agent/`). */\nexport const PTC_MODE_CONFIG_FILE = \"ptc.json\";\n\n/** How much of the loadout the mode hides. */\nexport type ModeHideStrategy = \"builtins-only\" | \"all-but-ptc\";\n\n/**\n * Which tools the mode hides while it is on.\n *\n * - `\"builtins-only\"` (default) — hide the seven built-in tools; tools contributed by *other*\n * extensions (`web_search`, `todo`, `subagent`, …) stay visible and directly callable. The\n * model is forced to program for file and shell work, which is the bulk of a coding session,\n * without losing capabilities this package cannot re-expose: extension tools have no bindings\n * (`pi.getAllTools()` returns metadata only — no `execute`), so hiding one makes it\n * unreachable for the whole session.\n * - `\"all-but-ptc\"` — hide everything except the two PTC surfaces. Closest to a strict reading of\n * DSH's preset, at the cost of making other extensions' tools unreachable until `/ptc off`.\n */\nexport const DEFAULT_HIDE_STRATEGY: ModeHideStrategy = \"builtins-only\";\n\n/** Live mode state. `base` / `ourLoadout` are `undefined` while the mode is off. */\nexport interface PtcModeState {\n enabled: boolean;\n /** Loadout captured before the mode narrowed anything; bindings derive from this. */\n base: readonly string[] | undefined;\n /** Exactly the array handed to `setActiveTools`; a mismatch means someone else changed it. */\n ourLoadout: readonly string[] | undefined;\n}\n\n/** Fresh, disabled state. */\nexport function initialModeState(): PtcModeState {\n return { enabled: false, base: undefined, ourLoadout: undefined };\n}\n\n/**\n * A persisted mode record lives in the session (`pi.appendEntry`), so `/ptc off` survives a\n * resume and a `/reload` can put the loadout back the way it found it.\n */\nexport interface PersistedModeState {\n enabled: boolean;\n base?: readonly string[];\n}\n\n// --------------------------------------------------------------------------------------\n// Config: ~/.pi/agent/ptc.json → { \"defaultMode\": false }\n// --------------------------------------------------------------------------------------\n\n/** Result of reading the opt-out config, with enough detail to warn about a broken file. */\nexport interface DefaultModeConfig {\n defaultMode: boolean;\n source: \"file\" | \"default\" | \"invalid\";\n error?: string;\n}\n\n/**\n * Read `defaultMode` from the agent-dir config file.\n *\n * Absent file → on (this package is default-on by design; see CONTEXT.md). A present file with a\n * non-boolean `defaultMode`, or unparseable JSON, is reported as `invalid` **and still defaults to\n * on** — the caller should surface the problem rather than silently changing behavior.\n */\nexport function readDefaultModeConfig(agentDir: string): DefaultModeConfig {\n const path = join(agentDir, PTC_MODE_CONFIG_FILE);\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return { defaultMode: true, source: \"default\" };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return {\n defaultMode: true,\n source: \"invalid\",\n error: `${path} is not valid JSON (${error instanceof Error ? error.message : String(error)})`,\n };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { defaultMode: true, source: \"invalid\", error: `${path} must contain a JSON object` };\n }\n const value = (parsed as { defaultMode?: unknown }).defaultMode;\n if (value === undefined) {\n return { defaultMode: true, source: \"default\" };\n }\n if (typeof value !== \"boolean\") {\n return {\n defaultMode: true,\n source: \"invalid\",\n error: `${path}: \"defaultMode\" must be a boolean, received ${typeof value}`,\n };\n }\n return { defaultMode: value, source: \"file\" };\n}\n\n// --------------------------------------------------------------------------------------\n// Entry decision\n// --------------------------------------------------------------------------------------\n\n/** Why the mode declined to turn on. Surfaced in the entry notification / debug logs. */\nexport type ModeBlockReason = \"not-tui\" | \"config-off\" | \"tools-unavailable\" | \"restricted-session\";\n\n/** Either the loadout to apply, or the reason the mode stayed off. */\nexport type ModeEntryDecision =\n | { enter: true; base: readonly string[]; loadout: readonly string[] }\n | { enter: false; reason: ModeBlockReason };\n\n/** Inputs to the entry decision. `active` is `pi.getActiveTools()` at decision time. */\nexport interface ModeEntryInput {\n /** `ctx.mode` — only `\"tui\"` participates. */\n mode: string;\n /** Resolved `defaultMode` from the config file. */\n defaultMode: boolean;\n /** The session's current tool loadout. */\n active: readonly string[];\n /** True for `/ptc on`, which overrides the config and the restricted-session policy. */\n manual: boolean;\n /** Overrides `DEFAULT_HIDE_STRATEGY` (tests, or a future settings surface). */\n hide?: ModeHideStrategy | undefined;\n}\n\n/** Normalize to a de-duplicated name list, preserving order. */\nfunction uniqueNames(names: readonly string[]): string[] {\n return [...new Set(names)];\n}\n\n/** Order- and duplicate-insensitive set equality, for loadout comparison. */\nexport function sameToolSet(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false;\n const left = [...a].sort();\n const right = [...b].sort();\n return left.every((name, index) => name === right[index]);\n}\n\n/**\n * The loadout applied while the mode is on, given the session's current one.\n *\n * Always keeps the PTC surfaces; what else survives depends on the hide strategy (see\n * `DEFAULT_HIDE_STRATEGY`). Order follows `active`, so the visible list reads the way the session\n * was configured.\n */\nexport function modeLoadout(active: readonly string[], hide: ModeHideStrategy): string[] {\n const isPtc = (name: string): boolean => PTC_MODE_TOOL_NAMES.includes(name);\n if (hide === \"all-but-ptc\") return uniqueNames(active.filter(isPtc));\n const builtins: ReadonlySet<string> = new Set(BUILTIN_BINDING_NAMES);\n return uniqueNames(active.filter((name) => isPtc(name) || !builtins.has(name)));\n}\n\n/**\n * Decide whether the mode turns on for this session.\n *\n * The policy, in order:\n * 1. **TUI only** — print / JSON / RPC sessions are never touched.\n * 2. **Config** — `{\"defaultMode\": false}` opts out; `/ptc on` overrides it.\n * 3. **PTC tools must exist** — if this session has them disabled, there is nothing to run.\n * 4. **Explicit restriction wins** — a session launched with `--tools` / `--exclude-tools` /\n * `--no-builtin-tools` that is missing any of `MODE_REQUIRED_TOOL_NAMES` (pi's default four)\n * stays as launched. Narrowing *further* would be the extension overriding a deliberate user\n * instruction, and the PTC surface would be degraded anyway (bindings = `BUILTIN ∩ base`).\n * `/ptc on` overrides this: the manual path enters with the restricted loadout as `base`, so\n * bindings stay inside the user's allowlist.\n */\nexport function decideModeEntry(input: ModeEntryInput): ModeEntryDecision {\n const { mode, defaultMode, active, manual } = input;\n if (mode !== \"tui\") return { enter: false, reason: \"not-tui\" };\n if (!defaultMode && !manual) return { enter: false, reason: \"config-off\" };\n if (!PTC_MODE_TOOL_NAMES.some((name) => active.includes(name))) {\n return { enter: false, reason: \"tools-unavailable\" };\n }\n if (!manual) {\n const missing = MODE_REQUIRED_TOOL_NAMES.filter((name) => !active.includes(name));\n if (missing.length > 0) return { enter: false, reason: \"restricted-session\" };\n }\n return {\n enter: true,\n base: uniqueNames(active),\n loadout: modeLoadout(active, input.hide ?? DEFAULT_HIDE_STRATEGY),\n };\n}\n\n// --------------------------------------------------------------------------------------\n// Runtime helpers\n// --------------------------------------------------------------------------------------\n\n/**\n * Tool names the binding table should be built from.\n *\n * While the mode is on this is the base snapshot (so the built-ins the mode just hid remain\n * callable from inside a program); otherwise it is simply the live loadout, i.e. T7's rule.\n */\nexport function bindingSource(state: PtcModeState, active: readonly string[]): readonly string[] {\n return state.enabled && state.base !== undefined ? state.base : active;\n}\n\n/**\n * Whether the live loadout is no longer the one this mode wrote — i.e. another extension (or the\n * user) took ownership of the tool set. Always false while the mode is off.\n */\nexport function detectExternalLoadoutChange(\n state: PtcModeState,\n active: readonly string[],\n): boolean {\n if (!state.enabled || state.ourLoadout === undefined) return false;\n return !sameToolSet(state.ourLoadout, active);\n}\n\n/**\n * Reconstruct the base loadout on `session_start`.\n *\n * Two shapes are possible:\n * - The session is still narrowed from a previous run of this mode (a `/reload`, or a resumed\n * session): the live loadout is a subset of the PTC tools and the persisted record says the mode\n * was on. The persisted `base` is then authoritative — but only to *undo our own hiding*.\n * - Otherwise the live loadout is whatever this pi process was launched with, and it wins. That is\n * what keeps a new `--tools …` choice from being overridden by an older session's snapshot.\n */\nexport function resolveBaseOnStart(\n persisted: PersistedModeState | undefined,\n active: readonly string[],\n): { base: readonly string[]; restoreFirst: boolean } {\n const looksNarrowedByUs =\n active.length > 0 && active.every((name) => PTC_MODE_TOOL_NAMES.includes(name));\n if (persisted?.enabled === true && looksNarrowedByUs && persisted.base !== undefined) {\n return { base: persisted.base, restoreFirst: true };\n }\n return { base: active, restoreFirst: false };\n}\n\n// --------------------------------------------------------------------------------------\n// Instruction injected into the conversation once per mode entry\n// --------------------------------------------------------------------------------------\n\n/**\n * Build the system-visible briefing for the mode.\n *\n * Generated from the *actual* binding list rather than hardcoded, because bindings vary by session\n * (`base` is whatever the session was launched with) and a stale list would have the model calling\n * names that are not bound.\n */\nexport function buildModeInstruction(bindings: readonly string[], hide: ModeHideStrategy): string {\n const builtinNote =\n hide === \"all-but-ptc\"\n ? \"Every built-in tool is hidden from you, and so are other extensions' tools.\"\n : \"The built-in tools (read, bash, edit, write, grep, find, ls) are hidden from you.\";\n const callable = bindings.map((name) => `tools.${name}(args)`);\n const examples = bindings\n .slice(0, 2)\n .map((name) =>\n name === \"read\"\n ? 'await tools.read({ path: \"src/index.ts\" })'\n : name === \"bash\"\n ? 'await tools.bash({ command: \"pnpm test\" })'\n : `await tools.${name}({ /* … */ })`,\n )\n .join(\"\\n\");\n\n return [\n \"[PTC MODE ACTIVE]\",\n \"This session runs in PTC mode. \" + builtinNote,\n \"\",\n \"Work by writing one TypeScript program per task and running it:\",\n \" - ptc_run_code for a plain program (its return value and console.log come back to you)\",\n \" - ptc_workflow when the task has named phases and you want narration (log / phase /\",\n \" parallel / pipeline are available there)\",\n \"\",\n `The program body is an async function. Available inside it: ${callable.join(\", \")}.`,\n examples.length > 0 ? `Examples:\\n${examples}` : \"\",\n \"\",\n \"Batch a task's steps into ONE program instead of issuing many tool calls — gathering\",\n \"intermediate output yourself and returning only what matters is the point of this mode.\",\n \"Image-bearing tool results inside a program are attached to you after the run — never return\",\n \"base64 image data as your program's value.\",\n \"If the PTC surfaces cannot express what you need, say so and the user can run /ptc off.\",\n ]\n .filter((line) => line.length > 0)\n .join(\"\\n\");\n}\n","/**\n * The system-prompt skills section, restored for PTC mode.\n *\n * pi advertises skills only when the session can actually open one:\n *\n * const skillFileReadTool = [\"read\", \"bash\"].find((tool) => selectedTools.includes(tool));\n * if (skillFileReadTool && skills.length > 0) promptSections.skills = ...\n * (pi-coding-agent: dist/core/system-prompt.js)\n *\n * PTC mode hides exactly those two tools, so that gate drops the section — and with it the model's\n * only way to learn that skills exist. Measured on real session files: sessions before\n * PTC-mode-by-default carried 43 skills in `SystemMessage.sections.skills`; PTC sessions carried\n * no `skills` section at all. See ADR-0011.\n *\n * The list body stays pi's own — `formatSkillsForPrompt` is a public export and its XML shape is\n * the agentskills.io spec, so re-deriving it here would only create drift. Only the loading\n * sentence changes: \"Use the read tool\" is false in a session where `read` is hidden, and the way\n * in is `tools.read` from inside a program.\n */\nimport { formatSkillsForPrompt } from \"@earendil-works/pi-coding-agent\";\nimport type { Skill } from \"@earendil-works/pi-coding-agent\";\n\n/** The two tools pi accepts as \"this session can read a skill file\". */\nexport const SKILL_READING_TOOL_NAMES: readonly string[] = [\"read\", \"bash\"];\n\n/** pi's sentence, which names a tool PTC mode hides. */\nconst PI_LOAD_INSTRUCTION =\n \"Use the read tool to load a skill's file when the task matches its description.\";\n\n/** What replaces it: the same instruction, in the only call form this session has. */\nexport const PTC_SKILL_LOAD_INSTRUCTION =\n \"Use tools.read({ path }) inside a ptc_run_code program to load a skill's file when the task matches its description.\";\n\n/**\n * Would pi withhold the skills section for this loadout?\n *\n * Mirrors the gate rather than guessing: it is true exactly when neither `read` nor `bash` is\n * directly callable, which is the state the mode deliberately puts an ordinary session in.\n */\nexport function skillsSectionDropped(visibleTools: readonly string[]): boolean {\n return !SKILL_READING_TOOL_NAMES.some((name) => visibleTools.includes(name));\n}\n\n/**\n * Build the `skills` section body (pi wraps it in `<skills>…</skills>` itself).\n *\n * Returns `\"\"` when nothing is advertisable — pi's formatter filters out skills marked\n * `disable-model-invocation`, which is the correct behaviour to inherit: those are\n * `/skill:name`-only by design.\n *\n * `format` is injectable so a test can drive the reworded-header branch without patching pi.\n */\nexport function buildPtcSkillsSection(\n skills: readonly Skill[],\n format: (skills: Skill[], fileReadTool?: \"read\" | \"bash\") => string = formatSkillsForPrompt,\n): string {\n const section = format([...skills], \"read\").trim();\n if (section.length === 0) return \"\";\n if (section.includes(PI_LOAD_INSTRUCTION)) {\n return section.replace(PI_LOAD_INSTRUCTION, PTC_SKILL_LOAD_INSTRUCTION);\n }\n // pi reworded its header. Keep the list, but never inherit a sentence that names a tool this\n // session cannot call — a wrong instruction is worse than a redundant one.\n return `${PTC_SKILL_LOAD_INSTRUCTION}\\n\\n${section}`;\n}\n","/**\n * pi-ptc-subagents — DSH PTC mode (Programmable Tool Calling) for pi.\n *\n * This module is the extension entrypoint pi loads (`package.json` → `pi.extensions`).\n * The PTC machinery it exposes is complete and tested (T3): wire protocol, dispatcher,\n * bindings and the two worker surfaces. On top of it sit the two model-facing tools:\n *\n * - `ptc_run_code` — bindings + Node + `console.log`, program's return value is the result\n * - `ptc_workflow` — the same plus `log` / `phase` / `parallel` / `pipeline` and `args`\n *\n * There is no `agent()` helper on either surface (G1 #13 → decision B): calling it produces\n * a plain `ReferenceError` through the normal code-run error path.\n */\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport type {\n ExtensionAPI,\n ExtensionContext,\n NormalizedBuildSystemPromptOptions,\n} from \"@earendil-works/pi-coding-agent\";\nimport { createPtcRunCodeTool } from \"./tools/run-code.ts\";\nimport { createPtcWorkflowTool } from \"./tools/workflow.ts\";\nimport { resolveBindingNames } from \"./tools/common.ts\";\nimport { TurnPools } from \"./runtime/turn-pools.ts\";\nimport {\n bindingSource,\n buildModeInstruction,\n decideModeEntry,\n DEFAULT_HIDE_STRATEGY,\n detectExternalLoadoutChange,\n initialModeState,\n PTC_MODE_ENTRY_TYPE,\n PTC_MODE_STATUS_KEY,\n readDefaultModeConfig,\n resolveBaseOnStart,\n} from \"./mode/ptc-mode.ts\";\nimport type { ModeHideStrategy, PersistedModeState } from \"./mode/ptc-mode.ts\";\nimport { buildPtcSkillsSection, skillsSectionDropped } from \"./mode/skills-section.ts\";\n\nexport {\n bindingSource,\n buildModeInstruction,\n decideModeEntry,\n DEFAULT_HIDE_STRATEGY,\n detectExternalLoadoutChange,\n initialModeState,\n modeLoadout,\n MODE_REQUIRED_TOOL_NAMES,\n PTC_MODE_CONFIG_FILE,\n PTC_MODE_ENTRY_TYPE,\n PTC_MODE_STATUS_KEY,\n PTC_MODE_TOOL_NAMES,\n readDefaultModeConfig,\n resolveBaseOnStart,\n sameToolSet,\n} from \"./mode/ptc-mode.ts\";\nexport type {\n DefaultModeConfig,\n ModeBlockReason,\n ModeEntryDecision,\n ModeEntryInput,\n ModeHideStrategy,\n PersistedModeState,\n PtcModeState,\n} from \"./mode/ptc-mode.ts\";\nexport {\n buildPtcSkillsSection,\n PTC_SKILL_LOAD_INSTRUCTION,\n SKILL_READING_TOOL_NAMES,\n skillsSectionDropped,\n} from \"./mode/skills-section.ts\";\nexport { MAX_LINE_CHARS, renderModelValue, sanitizeText, stripAnsi } from \"./tools/text.ts\";\n/** Footer status label shown while the mode is on. */\nconst MODE_STATUS_TEXT = \"PTC\";\n\n/**\n * Which tools the mode hides. The shipped extension always uses the default; it is a named\n * constant so a future settings surface (or a test) has one place to change it.\n */\nconst MODE_HIDE_STRATEGY: ModeHideStrategy = DEFAULT_HIDE_STRATEGY;\n\n/**\n * What to tell the user when the mode turns on. Names what became unreachable, because that is the\n * part that surprises people — and the binding list, because that is the part they need in order\n * to know what a program can still do.\n */\nfunction describeEntry(base: readonly string[], hide: ModeHideStrategy): string {\n const hidden =\n hide === \"all-but-ptc\"\n ? \"all other tools\"\n : \"built-in tools (now reachable only as tools.<name> inside a program)\";\n return [\n `PTC mode on — ${hidden} hidden.`,\n `Bindings available inside a program: ${resolveBindingNames(base).join(\", \") || \"(none)\"}.`,\n \"Run /ptc off to restore the normal tool set.\",\n ].join(\"\\n\");\n}\n\nexport {\n BUILTIN_BINDING_NAMES,\n createBuiltinBindings,\n DEFAULT_BINDING_NAMES,\n} from \"./runtime/bindings.ts\";\nexport type {\n Binding,\n BindingContext,\n BindingTable,\n CreateBuiltinBindingsOptions,\n} from \"./runtime/bindings.ts\";\nexport { runPtcProgram } from \"./runtime/dispatcher.ts\";\nexport type { PtcRunOutcome, PtcImage, RunPtcProgramOptions } from \"./runtime/dispatcher.ts\";\nexport {\n WorkerPool,\n type WorkerPoolOptions,\n type WorkerPoolStats,\n type WorkerPoolWorkerOptions,\n} from \"./runtime/worker-pool.ts\";\nexport { TurnPools, type TurnPoolsOptions } from \"./runtime/turn-pools.ts\";\nexport {\n createWorkerEnv,\n DEFAULT_CONFIG,\n effectiveTimeoutMs,\n resolveConfig,\n WORKER_ENV_ALLOW_LIST,\n} from \"./runtime/limits.ts\";\nexport type { PtcConfig, PtcSurface } from \"./runtime/limits.ts\";\nexport {\n PTC_ERROR_KIND,\n PTC_LOG_LEVEL,\n HOST_FRAME_KIND,\n WORKER_FRAME_KIND,\n} from \"./runtime/protocol.ts\";\nexport type { PtcErrorKind, PtcErrorShape, PtcJsonValue } from \"./runtime/protocol.ts\";\n\nexport default function ptcSubagents(pi: ExtensionAPI): void {\n const mode = initialModeState();\n // Set on entry, cleared after the briefing has been injected, so the instruction lands once\n // per mode entry instead of on every turn.\n let briefingPending = false;\n\n /**\n * Per-turn worker pools (ADR-0017 §1–§2): one warm set per surface, created lazily by the\n * first PTC run of the turn and retired at the turn's end. The holder is swapped rather\n * than mutated so a `turn_end` drain can never race a run that is still holding a worker —\n * the outgoing holder owns everything the ending turn created.\n */\n let turnPools = new TurnPools();\n\n /**\n * Bindings come from the mode's base snapshot while PTC mode is on, and from the live loadout\n * otherwise (T7, #21). See `src/mode/ptc-mode.ts` for why the snapshot is required: the mode\n * hides the built-ins, which would otherwise empty the binding table and make every\n * `tools.<name>(...)` call fail.\n */\n const getBindingSourceNames = (): readonly string[] => bindingSource(mode, pi.getActiveTools());\n\n pi.registerTool(\n createPtcRunCodeTool({\n getBindingSourceNames,\n getPool: () => turnPools.get(\"run_code\"),\n }),\n );\n pi.registerTool(\n createPtcWorkflowTool({\n getBindingSourceNames,\n getPool: () => turnPools.get(\"workflow\"),\n }),\n );\n\n /**\n * Retire the turn's pools. `drain()` terminates the warm workers, so nothing outlives the\n * turn that spawned it; the next PTC run starts a fresh, lazily-created set. Idle workers are\n * already `unref()`-ed by the pool, so a turn that ends without this hook firing can never\n * keep the process alive either.\n */\n pi.on(\"turn_end\", async () => {\n const draining = turnPools;\n turnPools = new TurnPools();\n await draining.drain();\n });\n\n const paintStatus = (ctx: ExtensionContext): void => {\n ctx.ui.setStatus(\n PTC_MODE_STATUS_KEY,\n mode.enabled ? ctx.ui.theme.fg(\"accent\", MODE_STATUS_TEXT) : undefined,\n );\n };\n\n const enterMode = (\n base: readonly string[],\n loadout: readonly string[],\n ctx: ExtensionContext,\n announcement: string,\n ): void => {\n mode.enabled = true;\n mode.base = base;\n mode.ourLoadout = loadout;\n pi.setActiveTools([...loadout]);\n briefingPending = true;\n pi.appendEntry(PTC_MODE_ENTRY_TYPE, { enabled: true, base: [...base] });\n paintStatus(ctx);\n ctx.ui.notify(announcement, \"info\");\n };\n\n const exitMode = (\n ctx: ExtensionContext,\n opts: { announce: boolean; customType: string },\n ): void => {\n const base = mode.base;\n mode.enabled = false;\n mode.base = undefined;\n mode.ourLoadout = undefined;\n briefingPending = false;\n if (base !== undefined) pi.setActiveTools([...base]);\n pi.appendEntry(opts.customType, { enabled: false });\n paintStatus(ctx);\n if (opts.announce) {\n ctx.ui.notify(\"PTC mode off — built-in tools are directly callable again.\", \"info\");\n }\n };\n\n /** Restore the persisted mode record, if this session has one. */\n const readPersistedMode = (ctx: ExtensionContext): PersistedModeState | undefined => {\n const entries = ctx.sessionManager.getEntries();\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.type !== \"custom\" || entry.customType !== PTC_MODE_ENTRY_TYPE) continue;\n const data = entry.data as { enabled?: unknown; base?: unknown } | undefined;\n const base = Array.isArray(data?.base)\n ? data.base.filter((name): name is string => typeof name === \"string\")\n : undefined;\n return {\n enabled: data?.enabled === true,\n ...(base === undefined ? {} : { base }),\n };\n }\n return undefined;\n };\n\n pi.registerCommand(\"ptc\", {\n description:\n \"Show or toggle PTC mode (built-in tools reachable only from inside a PTC program)\",\n handler: async (args, ctx) => {\n const action = args.trim().toLowerCase();\n if (action === \"off\") {\n if (!mode.enabled) {\n ctx.ui.notify(\"PTC mode is already off.\", \"info\");\n return;\n }\n exitMode(ctx, { announce: true, customType: PTC_MODE_ENTRY_TYPE });\n return;\n }\n if (action !== \"on\") {\n ctx.ui.notify(\n mode.enabled\n ? `PTC mode is ON — run /ptc off to restore built-in tools.\\nBindings: ${resolveBindingNames(mode.base).join(\", \") || \"(none)\"}`\n : \"PTC mode is OFF — run /ptc on to enter it.\",\n \"info\",\n );\n return;\n }\n if (mode.enabled) {\n ctx.ui.notify(\"PTC mode is already on.\", \"info\");\n return;\n }\n // Manual entry bypasses the config and the restricted-session policy; a restricted session\n // simply enters with its own (smaller) loadout as `base`, so bindings stay inside it.\n const decision = decideModeEntry({\n mode: ctx.mode,\n defaultMode: true,\n active: pi.getActiveTools(),\n manual: true,\n hide: MODE_HIDE_STRATEGY,\n });\n if (!decision.enter) {\n ctx.ui.notify(\n decision.reason === \"tools-unavailable\"\n ? \"Cannot enter PTC mode: ptc_run_code / ptc_workflow are not active in this session.\"\n : \"Cannot enter PTC mode in this run mode.\",\n \"warning\",\n );\n return;\n }\n enterMode(\n decision.base,\n decision.loadout,\n ctx,\n describeEntry(decision.base, MODE_HIDE_STRATEGY),\n );\n },\n });\n\n pi.on(\"session_start\", async (_event, ctx) => {\n const config = readDefaultModeConfig(getAgentDir());\n if (config.error !== undefined) {\n ctx.ui.notify(`pi-ptc-subagents: ${config.error}`, \"warning\");\n }\n\n const persisted = readPersistedMode(ctx);\n const active = pi.getActiveTools();\n const { base, restoreFirst } = resolveBaseOnStart(persisted, active);\n // Undo our own narrowing before deciding, so the decision sees the loadout this pi process\n // was actually launched with rather than the one a previous mode invocation left behind.\n if (restoreFirst) pi.setActiveTools([...base]);\n\n const decision = decideModeEntry({\n mode: ctx.mode,\n defaultMode: config.defaultMode,\n active: base,\n manual: false,\n hide: MODE_HIDE_STRATEGY,\n });\n // A persisted record is the user's most recent explicit intent: `/ptc off` survives a resume\n // even when the config still says default-on.\n const wantMode = persisted?.enabled ?? decision.enter;\n\n if (decision.enter && wantMode) {\n enterMode(base, decision.loadout, ctx, describeEntry(base, MODE_HIDE_STRATEGY));\n return;\n }\n paintStatus(ctx);\n if (!decision.enter && decision.reason !== \"not-tui\" && persisted === undefined) {\n if (decision.reason === \"restricted-session\") {\n ctx.ui.notify(\n \"PTC mode skipped: this session was launched with an explicit tool restriction.\",\n \"info\",\n );\n }\n }\n });\n\n /**\n * Put back the skills section pi withholds while the mode hides `read`/`bash` (ADR-0011).\n *\n * pi advertises skills only when the session can open a skill file at all (`read` or `bash`\n * among the selected tools), so a PTC session silently loses the whole `<available_skills>`\n * list: the model stops knowing skills exist. Writing `sections.skills` here is the seam pi\n * leaves open — custom sections are merged after its own, so this one wins.\n *\n * The same options object is reused across turns and sessions of a resume, so the injection is\n * undone the moment the mode stops hiding those tools: a `/ptc off` must not leave PTC-flavoured\n * loading instructions behind while `read` is directly callable again.\n */\n let skillsSectionInjected = false;\n const syncSkillsSection = (\n options: NormalizedBuildSystemPromptOptions | undefined,\n hidden: boolean,\n ): void => {\n if (options === undefined) return;\n if (!mode.enabled || !hidden) {\n if (skillsSectionInjected) {\n delete options.sections.skills;\n skillsSectionInjected = false;\n }\n return;\n }\n const section = buildPtcSkillsSection(options.skills);\n if (section.length === 0) return;\n options.sections.skills = section;\n skillsSectionInjected = true;\n };\n\n pi.on(\"before_agent_start\", async (event, ctx) => {\n // A live pi always supplies these; the test stub emits a bare event, hence the undefined case.\n const options = event.systemPromptOptions;\n syncSkillsSection(options, skillsSectionDropped(pi.getActiveTools()));\n if (!mode.enabled) return;\n if (detectExternalLoadoutChange(mode, pi.getActiveTools())) {\n // Another extension owns the loadout now; hand it back rather than fight over it.\n exitMode(ctx, { announce: false, customType: PTC_MODE_ENTRY_TYPE });\n syncSkillsSection(options, skillsSectionDropped(pi.getActiveTools()));\n ctx.ui.notify(\"PTC mode off — another extension changed the active tool set.\", \"warning\");\n return;\n }\n if (!briefingPending) return;\n briefingPending = false;\n return {\n message: {\n customType: \"ptc-mode-briefing\",\n content: buildModeInstruction(resolveBindingNames(mode.base), MODE_HIDE_STRATEGY),\n display: false,\n },\n };\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["dispatchBinding","DESCRIPTION","PARAMETERS"],"sources":["../src/runtime/dispatch.ts","../src/runtime/bindings.ts","../src/runtime/limits.ts","../src/runtime/worker-pool.ts","../src/runtime/worker-source.ts","../src/runtime/dispatcher.ts","../src/tools/text.ts","../src/tools/common.ts","../src/tools/render.ts","../src/tools/run-code.ts","../src/tools/workflow.ts","../src/runtime/turn-pools.ts","../src/mode/ptc-mode.ts","../src/mode/skills-section.ts","../src/index.ts"],"sourcesContent":["/**\n * pi.dispatch: the parallel binding that spawns a fresh pi subprocess per call.\n *\n * ADR-0016 (2026-09-23) is the contract, and this file is the whole implementation:\n * agent-markdown discovery (`discoverAgent`, with the agentScope user/project split),\n * the recursion-depth hint appended to the child's system prompt (`appendDepthHint`),\n * the argv the child pi is launched with (`buildArgv`), and `dispatch()` itself —\n * spawn, JSON-line event parsing, usage accumulation, the close-outcome decision\n * (`decideCloseOutcome`), and SIGTERM → SIGKILL signal propagation when the run that\n * issued the dispatch is cancelled.\n *\n * Behaviourally compatible with pi's examples/extensions/subagent/index.ts reference\n * (--mode json, -p, --no-session, --append-system-prompt <tmpfile>), but not cooperative:\n * does not require the user to install pi's subagent extension. See ADR-0016 section 2.\n */\n\nimport { spawn } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-ai\";\n\n/**\n * The CLI command used to invoke a fresh pi subprocess for one dispatch.\n *\n * Always `\"pi\"` — pi-ptc is a library, not a CLI, and `dispatch` must spawn the\n * separate `pi` binary regardless of how the host process was launched. An\n * earlier copy of this function mirrored pi's own subagent extension and tried\n * to reuse `process.execPath + process.argv[1]`, which works only when the host\n * is `pi` itself; in any other host (test scripts, the dispatcher host process,\n * etc.) the function would spawn the host script in place of `pi`, producing\n * infinite recursion and a stdout pipe that never drains (so `proc.on('close')`\n * never fires). The caller surfaces a missing-binary condition via the child's\n * `error` event — `spawn` itself does not throw synchronously on ENOENT, the\n * async `error` listener does.\n */\nconst PI_COMMAND = \"pi\";\n\n/** Per-call input. Mirrors pi's subagent extension's parameters (single-mode only). */\nexport interface DispatchInput {\n agent: string;\n task: string;\n cwd?: string;\n agentScope?: \"user\" | \"project\" | \"both\";\n model?: string;\n thinkingLevel?: ThinkingLevel;\n}\n\n/** Token usage accumulated from the child's message_end events. Optional. */\nexport interface DispatchUsage {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n cost: number;\n turns: number;\n}\n\n/** Structured return value of pi.dispatch(...). Shape mirrors Promise.allSettled records. */\nexport interface DispatchResult {\n text: string;\n status: \"fulfilled\" | \"rejected\";\n agentName: string;\n durationMs: number;\n exitCode: number;\n usage?: DispatchUsage;\n stderr?: string;\n errorMessage?: string;\n}\n\n/** Discovery of an agent by name; mirrors pi's subagent extension AgentConfig shape. */\nexport interface AgentConfigLike {\n readonly name: string;\n readonly source: \"user\" | \"project\" | \"unknown\";\n readonly model?: string;\n readonly systemPrompt: string;\n readonly tools?: readonly string[];\n}\n\n/** Per-run context handed to a dispatch binding; same shape as BindingContext. */\nexport interface DispatchContext {\n signal?: AbortSignal;\n callId: number;\n /** Run cwd (F4); default for `DispatchInput.cwd` when not overridden. */\n cwd: string;\n /** Depth of the calling PTC run: 0 for the parent turn, 1+ for a child of `pi.dispatch`. */\n depth: number;\n /** Maximum allowed depth for any run reachable from this dispatch (ADR-0016 Recursive section). */\n maxDispatchDepth: number;\n}\n\n/**\n * Compose the system prompt handed to the child subprocess.\n *\n * The agent's own markdown body is the user-supplied content; pi-ptc appends a\n * `<pi-ptc-context depth=\"N\" max-depth=\"M\">...</pi-ptc-context>` hint so the child\n * agent can see how much recursion room it has. The shape mirrors DSH's existing\n * subagent-context injection so the field recognises it.\n */\nexport function appendDepthHint(systemPrompt: string, depth: number, maxDepth: number): string {\n const hint = [\n '<pi-ptc-context depth=\"' + depth + '\" max-depth=\"' + maxDepth + '\">',\n \"You are a PTC run at depth \" +\n depth +\n \" (root is depth 0). You may write PTC programs and you may\",\n \"call pi.dispatch(...) to spawn further children, but each level costs a fresh pi subprocess.\",\n \"The remaining depth budget is \" +\n maxDepth +\n \" - \" +\n depth +\n \". Beyond it, pi.dispatch rejects with\",\n '{ status: \"rejected\", errorMessage: \"dispatch depth limit reached\" }.',\n \"</pi-ptc-context>\",\n ].join(\"\\n\");\n return systemPrompt.length === 0 ? hint : systemPrompt + \"\\n\\n\" + hint;\n}\n\n/** Result returned when the depth limit is exceeded. */\nexport function dispatchDepthLimitReached(): DispatchResult {\n return {\n text: \"\",\n status: \"rejected\",\n agentName: \"unknown\",\n durationMs: 0,\n exitCode: -1,\n // Verbatim: the hint block appendDepthHint appends to the child's system\n // prompt promises the program exactly this errorMessage.\n errorMessage: \"dispatch depth limit reached\",\n };\n}\n\n/**\n * Verbatim ADR-0016 §2 message for the per-run dispatch concurrency cap. Exported as a\n * named constant so the contract string has one definition and cannot drift (tests pin\n * it character for character).\n */\nexport const DISPATCH_CONCURRENCY_LIMIT_MESSAGE = \"dispatch concurrency limit reached\";\n\n/** Result returned when the per-run dispatch concurrency cap is exceeded (ADR-0016 §2). */\nexport function dispatchConcurrencyLimitReached(): DispatchResult {\n return {\n text: \"\",\n status: \"rejected\",\n agentName: \"unknown\",\n durationMs: 0,\n exitCode: -1,\n errorMessage: DISPATCH_CONCURRENCY_LIMIT_MESSAGE,\n };\n}\n\n/**\n * Inputs to {@link decideCloseOutcome}: the shape the child's `close` event\n * hands the host, distilled to the fields that drive the resolve decision.\n */\nexport interface CloseOutcomeInput {\n /** `code ?? -1` from the child's close event. `-1` covers signal kill and no-code exit. */\n exitCode: number;\n /** Accumulated text from the last assistant message_end event. */\n finalText: string;\n /** `true` when `ctx.signal` aborted before this close fired. */\n aborted: boolean;\n}\n\n/** Result of {@link decideCloseOutcome} — what the dispatcher commits to. */\nexport interface CloseOutcome {\n status: \"fulfilled\" | \"rejected\";\n errorMessage?: string;\n}\n\n/**\n * Decide the outcome of one `dispatch(...)` based on the child's `close` event.\n *\n * Cancellation has priority over every other branch: when the host's signal\n * aborted the run, the child exited because we asked it to, and the contract\n * (ADR-0016 §4) is to surface \"dispatch cancelled\" rather than leave the\n * caller guessing why an aborted run came back `rejected`.\n *\n * Non-cancellation cases follow the original close-handler ladder:\n * - exit 0 with final text → fulfilled\n * - exit 0 without final text → rejected (the model never answered)\n * - any other exit code → rejected, no errorMessage (spawn-error path labels\n * its own failures with `failed to spawn pi: ...`)\n */\nexport function decideCloseOutcome(input: CloseOutcomeInput): CloseOutcome {\n if (input.aborted) {\n return { status: \"rejected\", errorMessage: \"dispatch cancelled\" };\n }\n if (input.exitCode === 0 && input.finalText.length > 0) {\n return { status: \"fulfilled\" };\n }\n if (input.exitCode === 0) {\n return { status: \"rejected\", errorMessage: \"dispatch produced no final text\" };\n }\n return { status: \"rejected\" };\n}\n\n/**\n * Minimal interface for the bits of `node:child_process`'s ChildProcess that\n * `safeKill` touches — narrow on purpose so tests can pass plain objects.\n *\n * `pid` is optional on `ChildProcess` (`pid?: number`); a fresh process whose\n * pid has not yet been assigned reports `undefined`, and the kill must be a\n * no-op rather than a TS error. `killed` is intentionally absent: callers read\n * it on the real ChildProcess directly, not through this helper's contract.\n */\nexport interface Killable {\n pid?: number | undefined;\n kill(signal: NodeJS.Signals): boolean;\n}\n\n/**\n * Send `signal` to `proc` iff the process is still attached, swallowing\n * \"process already gone\" throws. Returns whether a kill was actually issued.\n *\n * `proc.pid === undefined` happens for processes spawned without a usable\n * pid (rare, but TS-strict demands the guard); `proc.kill` throwing happens\n * for processes that exited between the guard and the syscall (the OS hands\n * back ESRCH). Both are \"no-op successfully\" for our purposes.\n */\nexport function safeKill(proc: Killable, signal: NodeJS.Signals): boolean {\n if (proc.pid === undefined) return false;\n try {\n proc.kill(signal);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Minimal YAML frontmatter scalar parser. Returns the trimmed value, with surrounding\n * double or single quotes stripped when both are present.\n */\nfunction extractYamlString(yaml: string, key: string): string | undefined {\n const m = yaml.match(new RegExp(\"^\" + key + \":\\\\s*(.+)$\", \"m\"));\n if (!m) return undefined;\n let v = (m[1] ?? \"\").trim();\n if (v.length >= 2) {\n const first = v[0];\n const last = v[v.length - 1];\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n v = v.slice(1, -1);\n }\n }\n return v.length === 0 ? undefined : v;\n}\n\n/**\n * Minimal YAML list parser: accepts inline `[a, b, c]` and bare `a, b, c`.\n */\nfunction extractYamlList(yaml: string, key: string): string[] | undefined {\n const m = yaml.match(new RegExp(\"^\" + key + \":\\\\s*(.+)$\", \"m\"));\n if (!m) return undefined;\n const raw = (m[1] ?? \"\").trim();\n let inner = raw;\n if (inner.startsWith(\"[\") && inner.endsWith(\"]\")) {\n inner = inner.slice(1, -1);\n }\n const parts = inner\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return parts.length === 0 ? undefined : parts;\n}\n\n/**\n * Parse an agent markdown file: YAML frontmatter (name / description / model / tools)\n * plus the body, returned as the system prompt. Tolerates a missing or partial\n * frontmatter; the caller falls back to defaults when this returns null.\n */\nexport function parseAgentMarkdown(content: string): {\n name: string;\n description?: string;\n model?: string;\n tools?: readonly string[];\n systemPrompt: string;\n} | null {\n const fmMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/);\n if (!fmMatch) return null;\n const frontmatter = fmMatch[1] ?? \"\";\n const body = fmMatch[2] ?? \"\";\n const name = extractYamlString(frontmatter, \"name\");\n if (!name) return null;\n const description = extractYamlString(frontmatter, \"description\") ?? \"\";\n const model = extractYamlString(frontmatter, \"model\") ?? \"\";\n const tools = extractYamlList(frontmatter, \"tools\");\n const out: {\n name: string;\n description?: string;\n model?: string;\n tools?: readonly string[];\n systemPrompt: string;\n } = {\n name,\n systemPrompt: body.trim(),\n };\n if (description !== undefined) out.description = description;\n if (model !== undefined) out.model = model;\n if (tools !== undefined) out.tools = tools;\n return out;\n}\n\n/**\n * Discover an agent by name. Mirrors pi's subagent extension agentScope behaviour:\n * user-level (`~/.pi/agent/agents/`) is loaded by default; project-level\n * (`<cwd>/.pi/agents/`) is loaded only when agentScope includes it.\n */\nexport function discoverAgent(\n name: string,\n cwd: string,\n agentScope: \"user\" | \"project\" | \"both\",\n): AgentConfigLike | null {\n const userDir = path.join(os.homedir(), \".pi\", \"agent\", \"agents\");\n const projectDir = path.join(cwd, \".pi\", \"agents\");\n type Candidate = { path: string; source: \"user\" | \"project\" };\n const candidates: Candidate[] = [];\n if (agentScope === \"user\" || agentScope === \"both\") {\n candidates.push({ path: path.join(userDir, name + \".md\"), source: \"user\" });\n }\n if (agentScope === \"project\" || agentScope === \"both\") {\n candidates.push({ path: path.join(projectDir, name + \".md\"), source: \"project\" });\n }\n for (const c of candidates) {\n let raw: string;\n try {\n raw = fs.readFileSync(c.path, \"utf-8\");\n } catch {\n continue;\n }\n const parsed = parseAgentMarkdown(raw);\n if (parsed && parsed.name === name) {\n const out: AgentConfigLike = {\n name: parsed.name,\n source: c.source,\n systemPrompt: parsed.systemPrompt,\n };\n if (parsed.model !== undefined) (out as { model?: string }).model = parsed.model;\n if (parsed.tools !== undefined) (out as { tools?: readonly string[] }).tools = parsed.tools;\n return out;\n }\n }\n return null;\n}\n\n/**\n * Build the argv handed to the child pi subprocess. Model / thinking / tools / agentScope\n * precedence: dispatch input (call site) > agent markdown frontmatter > nothing.\n * The agent's system prompt (with the depth hint appended) is materialised to a tmpfile\n * and passed via --append-system-prompt, exactly as the subagent extension does.\n */\nexport function buildArgv(\n input: DispatchInput,\n agent: AgentConfigLike,\n promptFilePath: string,\n): string[] {\n const args: string[] = [\"--mode\", \"json\", \"-p\", \"--no-session\"];\n const model = input.model ?? agent.model;\n if (model) args.push(\"--model\", model);\n /*\n * Default to --thinking off: a dispatched child is a tool call, not a long-form reasoning\n * task. Thinking blocks the model from emitting text for many seconds; the parent turn keeps\n * its own thinking settings, the child inherits only what the parent's DispatchInput.\n * thinkingLevel says (and defaults to \"off\" otherwise).\n */\n const thinking = input.thinkingLevel ?? \"off\";\n\n args.push(\"--thinking\", thinking);\n // Note: agentScope is honoured at our layer via discoverAgent(); pi itself does not\n // accept an --agent-scope flag (the subagent extension does not pass one either), and\n // in --no-session mode pi loads user-scope agents from ~/.pi/agent/agents by default.\n // Project agents would need a different mechanism (e.g. PI_CODING_AGENT_DIR override).\n if (agent.tools && agent.tools.length > 0) {\n args.push(\"--tools\", agent.tools.join(\",\"));\n }\n args.push(\"--append-system-prompt\", promptFilePath);\n args.push(\"Task: \" + input.task);\n return args;\n}\n\n/**\n * One line of the child's stdout, parsed as JSON. The child emits the events described\n * in examples/extensions/subagent/index.ts (message_end, tool_result_end, error).\n * Anything unparseable is dropped: the line is one event the host cannot interpret, and\n * the run continues.\n */\ninterface ParsedAgentEvent {\n type: string;\n message?: {\n role?: string;\n content?: ReadonlyArray<{ type?: string; text?: string }>;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n model?: string;\n stopReason?: string;\n errorMessage?: string;\n };\n message_text?: string;\n}\n\nexport function parseAgentEvent(line: string): ParsedAgentEvent | null {\n const trimmed = line.trim();\n if (trimmed.length === 0) return null;\n try {\n return JSON.parse(trimmed) as ParsedAgentEvent;\n } catch {\n return null;\n }\n}\n/**\n * Write the system prompt (with depth hint appended) to a tmpfile. The caller is\n * responsible for cleanup; we use mkdtemp + 0o600 to keep the prompt private, then\n * best-effort cleanup in the dispatch() Promise.\n */\nasync function writePromptToTempFile(\n agentName: string,\n prompt: string,\n): Promise<{ dir: string; filePath: string }> {\n const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"pi-dispatch-\"));\n const safeName = agentName.replace(/[^\\w.-]+/g, \"_\");\n const filePath = path.join(tmpDir, \"prompt-\" + safeName + \".md\");\n await fs.promises.writeFile(filePath, prompt, { encoding: \"utf-8\", mode: 0o600 });\n return { dir: tmpDir, filePath };\n}\n\n/**\n * Best-effort cleanup of the tmpfile + dir used for the system prompt. Errors are\n * deliberately swallowed: leaving a tmpfile around is preferable to crashing the run.\n */\nfunction cleanupTmp(tmp: { dir: string; filePath: string }): void {\n try {\n fs.unlinkSync(tmp.filePath);\n } catch {\n /* ignore */\n }\n try {\n fs.rmdirSync(tmp.dir);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Spawn one pi subprocess for one dispatch and resolve with a DispatchResult.\n *\n * Behavioural contract (ADR-0016 sections 1, 3, 4):\n * - Resolves with status: fulfilled when the child exits 0 and emits at least one\n * assistant message whose text is non-empty.\n * - Resolves with status: rejected on non-zero exit, signal kill, spawn failure,\n * unknown agent, or no usable final text.\n * - Never rejects. The caller can pattern-match on outcome.status the same way\n * it would pattern-match a Promise.allSettled record.\n * - Honours signal with SIGTERM, then SIGKILL after a 5s grace window.\n */\nexport async function dispatch(\n input: DispatchInput,\n ctx: DispatchContext,\n): Promise<DispatchResult> {\n // ADR-0016 Recursive dispatch: bound recursion explicitly.\n const childDepth = ctx.depth + 1;\n if (childDepth > ctx.maxDispatchDepth) {\n return dispatchDepthLimitReached();\n }\n\n const cwd = input.cwd ?? ctx.cwd;\n const agentScope = input.agentScope ?? \"user\";\n const start = Date.now();\n\n const agent = discoverAgent(input.agent, cwd, agentScope);\n if (!agent) {\n return {\n text: \"\",\n status: \"rejected\",\n agentName: input.agent,\n durationMs: Date.now() - start,\n exitCode: 1,\n errorMessage:\n \"unknown agent: \" + input.agent + \" (agentScope=\" + agentScope + \", cwd=\" + cwd + \")\",\n };\n }\n\n const fullPrompt = appendDepthHint(agent.systemPrompt, childDepth, ctx.maxDispatchDepth);\n const tmp = await writePromptToTempFile(agent.name, fullPrompt);\n\n const argv = buildArgv(input, agent, tmp.filePath);\n\n return await new Promise<DispatchResult>((resolve) => {\n let stdoutBuffer = \"\";\n let stderrBuf = \"\";\n let finalText = \"\";\n let usage: DispatchUsage | undefined;\n let exitCode = -1;\n let resolved = false;\n let killTimer: NodeJS.Timeout | undefined;\n let proc: ReturnType<typeof spawn> | undefined;\n let aborted = false;\n\n const finalize = (status: \"fulfilled\" | \"rejected\", errorMessage?: string): void => {\n if (resolved) return;\n resolved = true;\n if (killTimer) clearTimeout(killTimer);\n if (ctx.signal) ctx.signal.removeEventListener(\"abort\", onAbort);\n cleanupTmp(tmp);\n const out: DispatchResult = {\n text: finalText,\n status,\n agentName: agent.name,\n durationMs: Date.now() - start,\n exitCode,\n };\n if (usage) out.usage = usage;\n if (stderrBuf.length > 0) out.stderr = stderrBuf;\n if (errorMessage) out.errorMessage = errorMessage;\n resolve(out);\n };\n\n const onAbort = (): void => {\n if (!proc || proc.killed || resolved) return;\n aborted = true;\n safeKill(proc, \"SIGTERM\");\n killTimer = setTimeout(() => {\n if (proc && !proc.killed) safeKill(proc, \"SIGKILL\");\n }, 5000);\n };\n\n try {\n proc = spawn(PI_COMMAND, argv, {\n cwd,\n shell: false,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // ADR-0016 Recursive section: the child run's depth baseline travels in the\n // environment so the pi-ptc extension loaded inside the child starts its PTC\n // runs at childDepth instead of at 0; the rest of the environment is inherited\n // from the host (the child needs the same PATH and provider config as pi itself).\n env: { ...process.env, PI_PTC_DEPTH: String(childDepth) },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n finalize(\"rejected\", \"failed to spawn pi: \" + message);\n return;\n }\n\n if (ctx.signal) {\n if (ctx.signal.aborted) onAbort();\n else ctx.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n const stdout = proc.stdout;\n if (!stdout) return;\n stdout.on(\"data\", (data: Buffer) => {\n stdoutBuffer += data.toString(\"utf-8\");\n let nl = stdoutBuffer.indexOf(\"\\n\");\n while (nl >= 0) {\n const line = stdoutBuffer.slice(0, nl);\n stdoutBuffer = stdoutBuffer.slice(nl + 1);\n const ev = parseAgentEvent(line);\n if (!ev) {\n nl = stdoutBuffer.indexOf(\"\\n\");\n continue;\n }\n if (ev.type === \"message_end\" && ev.message && ev.message.role === \"assistant\") {\n const m = ev.message;\n if (m.usage) {\n const cur: DispatchUsage = usage ?? {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n cost: 0,\n turns: 0,\n };\n cur.input += m.usage.input ?? 0;\n cur.output += m.usage.output ?? 0;\n cur.cacheRead += m.usage.cacheRead ?? 0;\n cur.cacheWrite += m.usage.cacheWrite ?? 0;\n cur.cost += m.usage.cost?.total ?? 0;\n cur.turns += 1;\n usage = cur;\n }\n if (Array.isArray(m.content)) {\n for (const part of m.content) {\n if (part && part.type === \"text\" && typeof part.text === \"string\") {\n finalText = part.text;\n }\n }\n }\n }\n nl = stdoutBuffer.indexOf(\"\\n\");\n }\n });\n\n const stderrStream = proc.stderr;\n if (!stderrStream) return;\n stderrStream.on(\"data\", (data: Buffer) => {\n stderrBuf += data.toString(\"utf-8\");\n });\n\n proc.on(\"close\", (code) => {\n exitCode = code ?? -1;\n const { status, errorMessage } = decideCloseOutcome({ exitCode, finalText, aborted });\n finalize(status, errorMessage);\n });\n\n proc.on(\"error\", (err) => {\n stderrBuf += \"[spawn-error] \" + err.message + \"\\n\";\n finalize(\"rejected\", \"failed to spawn pi: \" + err.message);\n });\n });\n}\n","/**\n * Bindings: the pi tools a PTC program may call as `tools.<name>(args)`.\n *\n * R5 found that pi's extension API exposes no tool-dispatch surface — `pi.on('tool_call')`\n * only fires from the agent loop, `extensionsResult.runtime` is state plumbing, and\n * `pi.exec` has no `execArgv`/`resourceLimits`/`stdio`. So a binding builds the tool with\n * pi's own SDK factory and calls `execute()` on it directly. That keeps the tool\n * implementation identical to the native one, but it also means worker-driven calls are\n * **not** seen by pi's tool-call pipeline: `tool_call` guards, `protected-paths`-style\n * extensions, permission gates and sandbox overrides never run for them. This is the\n * inherited risk ADR-0005 accepts and records — it must not be described as resolved, and\n * nothing here may pretend to route through `pi.on('tool_call')`.\n *\n * Arguments are validated with pi's own `validateToolArguments`, the same helper the agent\n * loop uses, so a bad call fails with the same message (and the same coercion) it would\n * get from the native pipeline.\n */\nimport { validateToolArguments } from \"@earendil-works/pi-ai\";\nimport {\n createBashTool,\n createEditTool,\n createFindTool,\n createGrepTool,\n createLsTool,\n createReadTool,\n createWriteTool,\n} from \"@earendil-works/pi-coding-agent\";\nimport type { TSchema } from \"typebox\";\n\nimport { dispatch as dispatchBinding, type DispatchInput } from \"./dispatch.ts\";\n\n/** pi's built-in tools that can be exposed as bindings, in native order. */\nexport const BUILTIN_BINDING_NAMES = [\n \"read\",\n \"bash\",\n \"edit\",\n \"write\",\n \"grep\",\n \"find\",\n \"ls\",\n] as const;\nexport type BuiltinBindingName = (typeof BUILTIN_BINDING_NAMES)[number];\n\n/** Parallel binding name (ADR-0016). */\nexport const DISPATCH_BINDING_NAME = \"pi.dispatch\" as const;\n\n/**\n * Bindings exposed when the caller does not pass an explicit name list.\n *\n * `bash` is included on purpose: DSH's PTC preset still mounts `tool-bash`/`tool-pwsh`\n * (R1 §2 — PTC mode hides the tools from the wire list and re-exposes the same registry\n * as bindings), so a PTC program written against DSH may run shell commands. Leaving it\n * out would silently shrink the surface relative to DSH. Callers that want a read-only\n * PTC surface pass an explicit subset.\n */\nexport const DEFAULT_BINDING_NAMES: readonly BuiltinBindingName[] = BUILTIN_BINDING_NAMES;\n\nexport interface BindingContext {\n /** Aborted when the run is cancelled, times out, or settles. */\n signal?: AbortSignal;\n /** Wire call id; also used to build the tool call id the tools see. */\n callId: number;\n /** Depth of the current PTC run (0 for parent turn, 1+ for a child of `pi.dispatch`). */\n depth: number;\n /** Maximum allowed depth; passed through to `pi.dispatch` for the depth check. */\n maxDispatchDepth: number;\n}\n\nexport interface Binding {\n readonly name: string;\n execute(args: unknown, context: BindingContext): Promise<unknown>;\n}\n\nexport type BindingTable = ReadonlyMap<string, Binding>;\n\n/**\n * Structural view of an SDK tool, wide enough for every `createXxxTool(cwd)` factory.\n *\n * `params: never` is what makes the seven typed factories assignable to one shape: a\n * function that takes `Static<typeof readSchema>` accepts a `never`, and `never` is what\n * the wire hands us before validation.\n */\ninterface AnyBuiltinTool {\n readonly name: string;\n readonly description: string;\n readonly parameters: TSchema;\n execute(\n toolCallId: string,\n params: never,\n signal?: AbortSignal,\n onUpdate?: unknown,\n ): Promise<{ content?: unknown; details?: unknown }>;\n}\n\nconst BUILTIN_TOOL_FACTORIES: Record<BuiltinBindingName, (cwd: string) => AnyBuiltinTool> = {\n read: createReadTool,\n bash: createBashTool,\n edit: createEditTool,\n write: createWriteTool,\n grep: createGrepTool,\n find: createFindTool,\n ls: createLsTool,\n};\n\n/** Argument type of pi's `validateToolArguments`, i.e. pi-ai's `ToolCall`. */\ntype ToolCallLike = Parameters<typeof validateToolArguments>[1];\n\nexport interface CreateBuiltinBindingsOptions {\n /** Per-run working directory: the F4 `RunConfig.cwd`, used to build every tool. */\n cwd: string;\n /** Subset of {@link BUILTIN_BINDING_NAMES}; defaults to all of them (bash included). */\n names?: readonly string[];\n /**\n * Whether the parallel binding `pi.dispatch` (ADR-0016) joins the table. Defaults to\n * \"did the caller curate the surface\": `true` when `names` is omitted, `false` when an\n * explicit list is passed (R3's read-only PTC surface pattern). Deciding on whether\n * `names` was provided — not on array identity with {@link DEFAULT_BINDING_NAMES} —\n * matters because production callers resolve names through a `.filter()` that always\n * returns a fresh array. The two shipped tools pass `true` explicitly: the dispatch\n * binding is part of every production surface.\n */\n includeDispatch?: boolean;\n}\n\n/**\n * Build the binding table for one run.\n *\n * Tools are created once per table and reused across calls, matching pi's own extension\n * examples (`createXxxTool(cwd)` caches nothing internally, so one instance per run is\n * the intended granularity).\n */\nexport function createBuiltinBindings(options: CreateBuiltinBindingsOptions): BindingTable {\n const names = options.names ?? DEFAULT_BINDING_NAMES;\n // ADR-0016: `pi.dispatch` joins the table unless the caller curated an explicit name list.\n const includeDispatch = options.includeDispatch ?? options.names === undefined;\n const table = new Map<string, Binding>();\n for (const name of names) {\n const factory = BUILTIN_TOOL_FACTORIES[name as BuiltinBindingName];\n if (!factory) {\n throw new TypeError(\n `unknown PTC binding \"${name}\"; known bindings: ${BUILTIN_BINDING_NAMES.join(\", \")}`,\n );\n }\n const tool = factory(options.cwd);\n table.set(name, {\n name,\n execute: async (args, context) => {\n const toolCallId = `ptc:${context.callId}`;\n const toolCall = {\n type: \"toolCall\",\n id: toolCallId,\n name: tool.name,\n arguments: args,\n } as unknown as ToolCallLike;\n const validated = validateToolArguments(tool, toolCall);\n const result = await tool.execute(\n toolCallId,\n validated as never,\n context.signal,\n undefined,\n );\n // What crosses the wire is the tool's model-facing payload: `usage` and\n // `terminate` are agent-loop plumbing with no meaning inside a PTC program.\n return {\n content: result.content,\n details: result.details === undefined ? null : result.details,\n };\n },\n });\n }\n // Register the parallel binding alongside the builtin set (ADR-0016). The default\n // surface (no explicit `names`) and any caller passing `includeDispatch: true` get it;\n // an explicit caller-curated list does not, because the caller has signalled they want\n // a specific surface.\n if (includeDispatch) {\n table.set(DISPATCH_BINDING_NAME, {\n name: DISPATCH_BINDING_NAME,\n execute: async (args, context) => {\n return dispatchBinding(args as DispatchInput, {\n signal: context.signal,\n callId: context.callId,\n cwd: options.cwd,\n depth: context.depth,\n maxDispatchDepth: context.maxDispatchDepth,\n });\n },\n });\n }\n return table;\n}\n","/**\n * PTC run limits and spawn-time hardening, in one frozen `DEFAULT_CONFIG`.\n *\n * The numbers are DSH's (`dsh-v0.1.6-alpha.2`, `@deepseek-ai/dsh-ptc-runtime-node`)\n * carried over verbatim — see ADR-0003 (output budget), ADR-0004 (pending calls) and\n * ADR-0005 (execution boundary, F1–F4). Tests assert against these constants rather\n * than repeating the literals, so a future re-sync only has to change this file.\n *\n * Deliberately absent:\n * - `syncTimeoutMs` / `maxConcurrentAgents` / `maxTotalAgents` — workflow-engine caps\n * for a cooperative VM and for `agent()`. We run the program directly in the worker\n * realm (no VM) and ship no `agent()` (G1 #13 → B), so there is nothing to cap.\n * - `sandbox-unavailable` is not an error kind either: ADR-0007 ships no OS sandbox.\n */\n\n/** The two worker surfaces. A worker is spawned with exactly one of them and keeps it. */\nexport type PtcSurface = \"run_code\" | \"workflow\";\n\nexport interface PtcConfig {\n /** Default elapsed deadline for a run, including nested binding waits (R1 §1). */\n timeoutMs: number;\n /** Ceiling the requested deadline is clamped to. */\n maxTimeoutMs: number;\n /** Joint budget for serialized logs + completion value (ADR-0003). */\n maxOutputBytes: number;\n /** Cap on a single control frame, either direction (R1 §1). */\n maxMessageBytes: number;\n /** Admission control for simultaneously in-flight worker→host binding calls (ADR-0004). */\n maxPendingCalls: number;\n /**\n * Concurrent builtin binding dispatches; DSH's `maxParallelSubCalls`\n * (ADR-0004 consequence), mirrored verbatim (10). The overflow\n * FIFO-queues for a slot instead of failing. This is the authoritative\n * cap for the builtin fan-out path — independent of\n * `dispatchConcurrency`, with its own counter: neither cap throttles\n * the other.\n */\n maxParallelSubCalls: number;\n /**\n * Per-run hard cap on concurrently in-flight `pi.dispatch(...)` calls,\n * enforced by the dispatcher: the next concurrent call resolves\n * immediately with `{ status: \"rejected\", errorMessage: \"dispatch\n * concurrency limit reached\" }` — never queued, never spawned.\n * Default 8, matches pi's `subagent` extension `MAX_PARALLEL_TASKS`.\n * ADR-0016 section 2. Independent of `maxParallelSubCalls`: builtin\n * calls never consume a dispatch slot and vice versa.\n */\n dispatchConcurrency: number;\n /** Maximum recursion depth for `pi.dispatch`. The child PTC run spawned by\n * the (depth+1)-th dispatch is allowed only when childDepth <= maxDispatchDepth.\n * Default 3. Aligns with dsh's `SubagentCapabilities.depthLimit` and codex's\n * `agent_max_depth`. ADR-0016 Recursive dispatch section. */\n maxDispatchDepth: number;\n /** Items accepted by a single `parallel()` / `pipeline()` call (R1 §1, workflow-side; pinned\n * here because it guards a helper call rather than an agent budget). */\n maxItemsPerCall: number;\n /** Cooperative-cancel grace before the worker is terminated outright. */\n graceMs: number;\n /** V8 old-generation cap handed to `new Worker({ resourceLimits })` (F2). */\n maxOldGenerationSizeMb: number;\n /** V8 young-generation cap handed to `new Worker({ resourceLimits })` (F2). */\n maxYoungGenerationSizeMb: number;\n /**\n * Per-turn worker pool capacity (ADR-0017 §3). Decoupled from\n * `dispatchConcurrency` because pool capacity is \"resident workers\" while\n * `dispatchConcurrency` is \"in-flight calls\" — two different ceilings.\n * Default 4.\n */\n poolSize: number;\n /**\n * How long `runPtcProgram({ pool })` will wait for an available worker before\n * failing the run with `kind: workerExit`. Decoupled from `timeoutMs` because\n * acquire-wait is bounded by the pool's responsiveness, not the run's overall\n * deadline. Default 30 000 ms.\n */\n poolAcquireTimeoutMs: number;\n /**\n * Ceiling on how long a pool's `drain()` waits for in-flight workers to release\n * themselves before terminating them outright: a worker whose `release()` never\n * arrives (a stuck dispatcher promise, an unobserved crash) must bound the turn-end\n * hook rather than hang it. Not `graceMs` above, which is a *run's* cooperative-cancel\n * window; this one is the *pool's* window at retirement, and drain resolves either way.\n * Default 5 000 ms.\n */\n drainGraceMs: number;\n}\n\nexport const DEFAULT_CONFIG: Readonly<PtcConfig> = Object.freeze({\n timeoutMs: 120_000,\n maxTimeoutMs: 600_000,\n maxOutputBytes: 67_108_864,\n maxMessageBytes: 134_217_728,\n maxPendingCalls: 128,\n maxParallelSubCalls: 10,\n dispatchConcurrency: 8,\n maxDispatchDepth: 3,\n maxItemsPerCall: 4_096,\n graceMs: 3_000,\n maxOldGenerationSizeMb: 512,\n maxYoungGenerationSizeMb: 64,\n poolSize: 4,\n poolAcquireTimeoutMs: 30_000,\n drainGraceMs: 5_000,\n});\n\n/**\n * F1 — the only environment variables a PTC worker may inherit.\n *\n * These are what a shell needs to resolve binaries on each platform; everything\n * else the host has (tokens, API keys, proxies, home paths) stays out of the worker.\n */\nexport const WORKER_ENV_ALLOW_LIST: readonly string[] = Object.freeze([\n \"PATH\",\n \"PATHEXT\",\n \"SYSTEMROOT\",\n \"WINDIR\",\n \"TEMP\",\n \"TMP\",\n]);\n\n/**\n * Build the per-run environment snapshot: the allow-list entries that actually exist.\n *\n * Unset or empty entries are dropped rather than passed through as empty strings, so\n * `Object.keys(workerEnv)` is exactly what the worker sees in `process.env`.\n */\nexport function createWorkerEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> {\n const env: Record<string, string> = {};\n for (const name of WORKER_ENV_ALLOW_LIST) {\n const value = source[name];\n if (typeof value === \"string\" && value.length > 0) env[name] = value;\n }\n return env;\n}\n\n/**\n * Merge per-run overrides onto `DEFAULT_CONFIG`.\n *\n * Overrides are validated rather than coerced: a negative or non-numeric limit is a\n * caller bug, and silently substituting a default would hide it.\n */\nexport function resolveConfig(overrides: Partial<PtcConfig> = {}): PtcConfig {\n const resolved: PtcConfig = { ...DEFAULT_CONFIG };\n for (const key of Object.keys(DEFAULT_CONFIG) as Array<keyof PtcConfig>) {\n const override = overrides[key];\n if (override === undefined) continue;\n if (typeof override !== \"number\" || !Number.isFinite(override) || override <= 0) {\n throw new TypeError(`invalid PTC config override for ${key}: ${String(override)}`);\n }\n resolved[key] = override;\n }\n return resolved;\n}\n\n/**\n * Resolve the effective deadline for one run.\n *\n * DSH semantics (R1 §3): `0` does not disable the deadline, it falls back to the\n * default; anything larger is clamped to `maxTimeoutMs`.\n */\nexport function effectiveTimeoutMs(\n requested: number | undefined,\n config: PtcConfig = DEFAULT_CONFIG,\n): number {\n if (requested === undefined || !Number.isFinite(requested) || requested <= 0)\n return config.timeoutMs;\n return Math.min(requested, config.maxTimeoutMs);\n}\n","/**\n * Per-turn worker pool: keeps `worker_threads` Workers warm across PTC runs so the\n * second `ptc_run_code` of a turn does not pay the V8 spin-up + module-graph load cost\n * the first one did (ADR-0017).\n *\n * The pool is created by the pi extension hook at turn entry, passed to\n * `runPtcProgram()` via `RunPtcProgramOptions.pool`, and drained at turn exit. Two\n * surfaces (`run_code`, `workflow`) do not share a pool: their worker programs are\n * different (`installWorkflowHelpers` in `worker-main.ts`), and one pool per surface\n * is the cleanest ownership story.\n *\n * Capacity model\n * ──────────────\n * \"Resident workers\" ≤ `size`, \"in-flight calls\" ≤ unbounded but queued when the pool\n * is full. An acquire when the pool is at capacity joins a FIFO wait list; a waiter is\n * served the instant an `acquire()` resolves with the released worker — the released\n * worker is handed directly to the next waiter rather than going through `idle`. This\n * matches the host's own `acquireDispatchSlot` semantics: the queue is the throughput\n * story, not the rejection story.\n *\n * Waiters time out after `acquireTimeoutMs`; on timeout the run fails with\n * `kind: workerExit` and a message naming the pool (ADR-0017 W-2 / §4).\n *\n * `drain()` — awaited by the pi extension hook at turn end — gives in-flight runs\n * `drainGraceMs` (default 5 000 ms) to release their worker. Whatever is still in flight\n * when the grace expires is terminated together with the idle workers: a worker whose\n * `release()` never arrives (a stuck dispatcher promise, a crash no one observed) must\n * bound the turn-end hook, not hang it. Drain therefore always resolves — expiry is not\n * an error, because a turn boundary should not fail over one bad worker.\n *\n * Health\n * ──────\n * A worker that has emitted `exit` is retired — it never re-enters `idle`. The pool\n * detects exit on the next `release()`: if `worker.threadId === -1` (Node marks a\n * terminated worker with a sentinel threadId) the worker is dropped rather than kept\n * idle. This keeps a worker that crashed between two runs from being handed out again.\n */\nimport { Worker } from \"node:worker_threads\";\nimport type { WorkerOptions } from \"node:worker_threads\";\nimport diagnosticsChannel from \"node:diagnostics_channel\";\nimport type { PtcConfig, PtcSurface } from \"./limits.ts\";\n\n/** Options the dispatcher hands to `new Worker(...)`. */\nexport type WorkerPoolWorkerOptions = ConstructorParameters<typeof Worker>[1];\n\n/**\n * Build the `Worker` options for one spawn (ADR-0005 F1–F3 hardening in one place).\n *\n * `runId` is optional and deliberately absent on the pooled path: a warm worker serves many runs,\n * so its spawn-time `workerData` cannot carry any single run's identity (each run's `runId`\n * travels in its `init` frame instead).\n */\nexport function workerSpawnOptions(options: {\n surface: PtcSurface;\n env: Record<string, string>;\n limits: Pick<PtcConfig, \"maxOldGenerationSizeMb\" | \"maxYoungGenerationSizeMb\">;\n runId?: string;\n}): WorkerPoolWorkerOptions {\n return {\n name: `ptc-${options.surface}`,\n // F1: allow-list only — never the host's full environment (ADR-0005).\n env: options.env,\n // F3: the frozen snapshot the worker records. On the cold path that snapshot also names the\n // run; on the pooled path it cannot (see the doc comment above).\n workerData:\n options.runId === undefined\n ? { env: options.env }\n : { runId: options.runId, env: options.env },\n // F2: V8 caps (ADR-0005).\n resourceLimits: {\n maxOldGenerationSizeMb: options.limits.maxOldGenerationSizeMb,\n maxYoungGenerationSizeMb: options.limits.maxYoungGenerationSizeMb,\n },\n };\n}\n\nexport interface WorkerPoolOptions {\n /**\n * Build a fresh worker URL for each spawn. The pool calls this only when it actually\n * needs to grow the resident count, so a heavy `buildWorkerUrl` is amortised across\n * warm reuses.\n */\n buildWorkerUrl: () => URL;\n /** Maximum resident workers; acquire when full queues the caller. Default 4. */\n size?: number;\n /** Acquire-wait bound; default 30 000 ms. */\n acquireTimeoutMs?: number;\n /**\n * Bound on how long `drain()` waits for in-flight workers to be released before it\n * terminates them anyway. Default 5 000 ms. Drain resolves either way — expiry is not\n * an error, it just means a worker was retired without a clean release.\n */\n drainGraceMs?: number;\n /** Options passed verbatim to `new Worker(url, options)` when spawning. */\n workerOptions: WorkerPoolWorkerOptions;\n}\n\n/**\n * Internal acquisition waiter. The `resolve` hands the worker directly to the caller's\n * `release` chain (no idle round-trip) when capacity opens up.\n */\ninterface PoolWaiter {\n resolve: (worker: Worker) => void;\n reject: (error: unknown) => void;\n timer: NodeJS.Timeout;\n startedAt: number;\n}\n\n/**\n * Diagnostics channels the pool publishes to (ADR-0017 W-5, Addendum). Channels have\n * zero subscribers by default — `diagnostics_channel.channel(...)` always returns the\n * same instance, but `publish` short-circuits when no one is listening, so this is the\n * documented cheap path.\n */\nconst channels = {\n acquireLatency: diagnosticsChannel.channel(\"ptc:pool:acquire-latency\"),\n resetTime: diagnosticsChannel.channel(\"ptc:worker:reset-time\"),\n imageBytes: diagnosticsChannel.channel(\"ptc:image:hoist-bytes\"),\n} as const;\n\n/** `node:diagnostics_channel` doesn't export its Channel type; this is the public surface we use. */\ntype DiagnosticsChannel = {\n publish: (message: unknown) => void;\n};\n\nfunction publish(channel: DiagnosticsChannel, message: unknown): void {\n // Node short-circuits `publish` when the channel has no subscribers, so this is a\n // no-op in production. Subscribers are e2e tests and future instrumentation.\n channel.publish(message);\n}\n\nexport interface WorkerPoolStats {\n resident: number;\n inFlight: number;\n waiters: number;\n totalAcquires: number;\n poolExhaustions: number;\n}\n\n/**\n * A fixed-capacity FIFO-served worker pool. Not safe for concurrent `acquire()` calls\n * beyond Node's microtask interleaving guarantees — callers (`runPtcProgram`) await\n * `acquire` before scheduling anything that touches the pool, so the only concurrent\n * surface is `release()` against an in-flight worker.\n */\nexport class WorkerPool {\n readonly #buildWorkerUrl: () => URL;\n readonly #size: number;\n readonly #acquireTimeoutMs: number;\n readonly #drainGraceMs: number;\n readonly #workerOptions: WorkerPoolWorkerOptions;\n\n readonly #idle: Worker[] = [];\n readonly #inFlight: Set<Worker> = new Set();\n readonly #waiters: PoolWaiter[] = [];\n /**\n * When each worker was last released (i.e., the previous run settled). Used by the\n * dispatcher to publish `ptc:worker:reset-time` when a warm worker emits its first\n * `ready` frame — the time between settle and reset-complete is the worker's\n * per-run reset cost (ADR-0017 Addendum).\n */\n readonly #lastSettledAt = new WeakMap<Worker, number>();\n #totalAcquires = 0;\n #poolExhaustions = 0;\n /**\n * `true` once `drain()` has resolved. After this point every `acquire()` rejects\n * — the pool is single-use, the parent turn owns it for one cycle (ADR-0017 §1).\n */\n #drained = false;\n\n constructor(options: WorkerPoolOptions) {\n if (typeof options.buildWorkerUrl !== \"function\") {\n throw new TypeError(\"WorkerPool: buildWorkerUrl must be a function\");\n }\n if (\n options.size !== undefined &&\n (typeof options.size !== \"number\" || !Number.isFinite(options.size) || options.size <= 0)\n ) {\n throw new TypeError(\n `WorkerPool: size must be a positive finite number, received ${String(options.size)}`,\n );\n }\n if (\n options.acquireTimeoutMs !== undefined &&\n (typeof options.acquireTimeoutMs !== \"number\" ||\n !Number.isFinite(options.acquireTimeoutMs) ||\n options.acquireTimeoutMs <= 0)\n ) {\n throw new TypeError(\n `WorkerPool: acquireTimeoutMs must be a positive finite number, received ${String(options.acquireTimeoutMs)}`,\n );\n }\n if (\n options.drainGraceMs !== undefined &&\n (typeof options.drainGraceMs !== \"number\" ||\n !Number.isFinite(options.drainGraceMs) ||\n options.drainGraceMs <= 0)\n ) {\n throw new TypeError(\n `WorkerPool: drainGraceMs must be a positive finite number, received ${String(options.drainGraceMs)}`,\n );\n }\n this.#buildWorkerUrl = options.buildWorkerUrl;\n this.#size = options.size ?? 4;\n this.#acquireTimeoutMs = options.acquireTimeoutMs ?? 30_000;\n this.#drainGraceMs = options.drainGraceMs ?? 5_000;\n this.#workerOptions = options.workerOptions;\n }\n\n /**\n * Acquire an idle worker, spawn a new one if below capacity, or queue until capacity\n * opens up. Throws `Error(\"pool acquire timed out after ${acquireTimeoutMs} ms\")` on\n * timeout; the dispatcher wraps that message with a `pool acquire failed: ` prefix (ADR-0017 §4).\n */\n async acquire(): Promise<Worker> {\n if (this.#drained) {\n throw new Error(\"pool has been drained and can no longer serve workers\");\n }\n // One logical acquire, one increment: retiring an idle worker below must not\n // re-enter this method (that would double-count, and `stats()` is the fact source\n // for tests and diagnostics).\n this.#totalAcquires += 1;\n const startedAt = Date.now();\n\n // Fast path: idle worker ready. Move it to in-flight and return. Skip (and drop) any\n // worker that has already exited — the `'exit'` listener normally removes it, but the\n // event is async, so a worker can exit between the listener and this shift.\n let idle = this.#idle.shift();\n while (idle !== undefined && !this.#isHealthy(idle)) {\n idle = this.#idle.shift();\n }\n if (idle !== undefined) {\n this.#inFlight.add(idle);\n // An idle worker is parked by `unref()` so a warm pool never keeps the host\n // process alive; taking it back into flight means the run depends on it, so\n // re-ref it before handing it over.\n idle.ref();\n publish(channels.acquireLatency, {\n poolSize: this.#size,\n waiters: this.#waiters.length,\n durationMs: Date.now() - startedAt,\n });\n return idle;\n }\n\n // Spawn path: capacity available.\n if (this.#inFlight.size < this.#size) {\n const worker = this.#spawn();\n this.#inFlight.add(worker);\n publish(channels.acquireLatency, {\n poolSize: this.#size,\n waiters: this.#waiters.length,\n durationMs: Date.now() - startedAt,\n });\n return worker;\n }\n\n // Wait path: queue a waiter with timeout.\n this.#poolExhaustions += 1;\n return await new Promise<Worker>((resolve, reject) => {\n const timer = setTimeout(() => {\n const index = this.#waiters.findIndex((entry) => entry.timer === timer);\n if (index >= 0) this.#waiters.splice(index, 1);\n reject(new Error(`pool acquire timed out after ${this.#acquireTimeoutMs} ms`));\n }, this.#acquireTimeoutMs);\n this.#waiters.push({ resolve, reject, timer, startedAt });\n });\n }\n\n /**\n * Hand a worker back to the pool. If a waiter is queued, hand the worker directly to\n * them (no idle round-trip). Otherwise park the worker in `idle` — unless it has\n * already exited, in which case retire it.\n */\n release(worker: Worker): void {\n if (!this.#inFlight.delete(worker)) {\n // Not tracked as in-flight (release called twice, or never acquired from us).\n // Be lenient: don't throw, just ignore. The dispatcher only releases what it\n // acquired.\n return;\n }\n if (!this.#isHealthy(worker)) {\n // Crashed since acquire: drop it. The next acquire will spawn a fresh one\n // (capacity accounting already removed it from in-flight above).\n return;\n }\n this.#lastSettledAt.set(worker, Date.now());\n const waiter = this.#waiters.shift();\n if (waiter) {\n clearTimeout(waiter.timer);\n this.#inFlight.add(worker);\n publish(channels.acquireLatency, {\n poolSize: this.#size,\n waiters: this.#waiters.length,\n // The waiter's queue wait, measured from when it entered `acquire()`. This is\n // the number the channel exists to report: how long a caller waited for capacity.\n durationMs: Date.now() - waiter.startedAt,\n });\n waiter.resolve(worker);\n return;\n }\n this.#idle.push(worker);\n // Park the idle worker so the host process can exit with a warm pool still standing\n // (a ref'd Worker keeps Node's event loop alive). The next `acquire()` re-refs it.\n worker.unref();\n }\n\n /**\n * Last time `release()` was called for `worker`. The dispatcher reads this to\n * publish `ptc:worker:reset-time` on a warm `ready` frame: `now - lastSettledAt`\n * is the worker's per-run reset cost (ADR-0017 Addendum). `undefined` for a worker\n * the pool never released (cold-start).\n */\n lastSettledAt(worker: Worker): number | undefined {\n return this.#lastSettledAt.get(worker);\n }\n\n /**\n * Wait for every in-flight worker to be released — bounded by `drainGraceMs` — then\n * terminate every resident worker and reject every queued waiter. After `drain()`\n * returns the pool is unusable.\n *\n * The bound matters: `drain()` is awaited from the pi `turn_end` hook, so a worker\n * whose `release()` never arrives (a stuck dispatcher promise, an unobserved crash)\n * must not hold the turn open. Workers still in flight when the grace expires are\n * terminated with the idle ones, and `drain()` resolves normally — a single bad worker\n * is not a reason to fail the turn boundary.\n */\n async drain(): Promise<void> {\n // Reject any queued waiter up front: the pool is going away.\n while (this.#waiters.length > 0) {\n const waiter = this.#waiters.shift();\n if (!waiter) break;\n clearTimeout(waiter.timer);\n waiter.reject(new Error(\"pool drained before acquire could be satisfied\"));\n }\n // Wait for in-flight to settle — drain resolves only after every active run released.\n // Callers (the pi extension hook) hand the pool to `runPtcProgram` and await all\n // those promises before `drain`, so this is a defensive barrier for tests. The\n // deadline makes the barrier bounded; polling is deliberate (simple, and 5 ms is\n // nothing against a turn boundary).\n const deadline = Date.now() + this.#drainGraceMs;\n while (this.#inFlight.size > 0 && Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 5));\n }\n // Terminate every resident worker: idle ones always, plus any in-flight worker that\n // outlived the grace. Clearing `#inFlight` first keeps `stats()` honest — a stuck\n // worker is no longer part of what the pool will serve.\n const terminating = [...this.#idle, ...this.#inFlight];\n this.#idle.length = 0;\n this.#inFlight.clear();\n await Promise.all(terminating.map((worker) => worker.terminate().catch(() => undefined)));\n this.#drained = true;\n }\n\n /** Read-only snapshot of the pool's counters; useful for tests and diagnostics. */\n stats(): WorkerPoolStats {\n return {\n resident: this.#inFlight.size + this.#idle.length,\n inFlight: this.#inFlight.size,\n waiters: this.#waiters.length,\n totalAcquires: this.#totalAcquires,\n poolExhaustions: this.#poolExhaustions,\n };\n }\n\n /**\n * Spawn one worker and wire its retirement listener. Private: the pool owns worker\n * lifecycle, so a caller outside `acquire()` has no business growing the pool (the\n * dispatcher spawns directly itself when no pool is configured).\n */\n #spawn(): Worker {\n const worker = new Worker(this.#buildWorkerUrl(), this.#workerOptions as WorkerOptions);\n // Once the worker exits, mark it retired by removing it from whichever pool list\n // currently holds it. Listeners are cheap; one per worker is fine.\n worker.once(\"exit\", () => {\n const idleIndex = this.#idle.indexOf(worker);\n if (idleIndex >= 0) {\n this.#idle.splice(idleIndex, 1);\n return;\n }\n this.#inFlight.delete(worker);\n });\n return worker;\n }\n\n /**\n * A worker is healthy when it has not exited. Node marks a terminated worker with\n * `threadId === -1`; the `'exit'` event path also clears it from our lists, but the\n * `threadId` check is the cheap synchronous check for the common release path.\n *\n * Known window: `threadId` flips to `-1` **asynchronously**, so a worker whose\n * `terminate()` has been called but whose exit has not been processed yet still reads as\n * healthy (measured). That is unreachable in-tree today — `terminate()` is only called by\n * `drain()` (after which the pool refuses every `acquire`) and by the dispatcher's\n * unpooled path (whose worker never enters this pool) — so this is a note for whoever\n * adds a third caller, not a bug to chase.\n */\n #isHealthy(worker: Worker): boolean {\n return worker.threadId !== -1;\n }\n}\n\n/**\n * Publish a frame-transfer event when the dispatcher's callResult postMessage carries\n * image bytes. Called from the dispatcher, not the pool, but lives here so the channel\n * name and payload shape stay one-file-defined (ADR-0017 Addendum).\n */\nexport function publishImageBytes(byteLength: number): void {\n publish(channels.imageBytes, { byteLength });\n}\n\n/**\n * Publish the warm-worker's reset cost when its first `ready` frame arrives at the\n * host. `previousSettledAt` is the wall-clock time the pool last released this worker;\n * the gap is what the worker spent clearing per-run state and re-installing the surface.\n */\nexport function publishResetTime(durationMs: number): void {\n publish(channels.resetTime, { durationMs });\n}\n","/**\n * Worker source location.\n *\n * The previous bootstrap assembled a `data:text/javascript,…` URL from\n * `Function.prototype.toString()` of `workerMain` plus a protocol literal —\n * a single self-contained file but with no V8 code-cache reuse across spawns\n * (ADR-0017 §7). The rolldown dual-entry build now emits `dist/worker.js`\n * alongside `dist/index.js`; the host loads that file directly, which lets\n * V8's code cache and Node's module cache survive across warm-reuse spawns.\n *\n * The two candidates the resolver tries:\n *\n * 1. `dist/worker.js` — the rolldown output, sibling of `dist/index.js`. The\n * production path used by `pi install npm:pi-ptc-subagents`.\n * 2. `src/runtime/worker-entry.ts` — the source, used by `vitest` runs\n * (vitest's transformer handles `.ts` and the worker is launched in-process\n * so the relative path resolves).\n */\nimport { existsSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\n/**\n * Resolve the worker entry's `file://` URL.\n *\n * The function takes no arguments: the protocol tables now travel through the\n * worker file itself (rolldown bundles `./protocol.ts` into `dist/worker.js`),\n * so the host has nothing to compose at URL-build time.\n */\nexport function buildWorkerUrl(): URL {\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n // Production: rolldown outputs `dist/worker.js` next to `dist/index.js`,\n // which is what the host's `import.meta.url` resolves to when it loaded\n // `dist/index.js`.\n resolve(here, \"worker.js\"),\n // Fallback: the source file in the same directory. Vitest loads\n // `src/runtime/dispatcher.ts` directly (no `dist/` involved), so the\n // worker entry is `src/runtime/worker-entry.ts` and lives beside the\n // host file.\n resolve(here, \"worker-entry.ts\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) return pathToFileURL(candidate);\n }\n throw new Error(`worker entry not found; tried: ${candidates.join(\", \")}`);\n}\n","/**\n * Host-side dispatcher: one `runPtcProgram()` call = one program in one worker.\n *\n * The worker's lifecycle depends on the path (ADR-0017 §1):\n * - **cold** (no `pool`): the dispatcher spawns a worker for the run and terminates it when the\n * run settles — one run, one worker;\n * - **pooled** (`pool` set): the dispatcher acquires a warm worker from the turn's pool and\n * releases it back when the run settles. The worker outlives the run — the turn's later runs\n * reuse it, and the pool's `drain()` retires it at turn end, not this file.\n *\n * Responsibilities:\n * - spawn a hardened worker (F1 env scrub, F2 V8 caps, F3 frozen per-run env in\n * `workerData`, F4 `cwd` carried in the run config and handed to the bindings) — the\n * hardening itself is defined once, in `workerSpawnOptions` (`worker-pool.ts`),\n * - complete the `MessageChannel` handshake and send the `init` frame,\n * - route `tools.*` calls to the binding table — concurrently, with DSH's\n * `maxPendingCalls` admission control, the `maxParallelSubCalls` builtin\n * forwarding cap (ADR-0004 consequence: the overflow FIFO-queues for a\n * slot), and the per-run `dispatchConcurrency` hard cap on in-flight\n * `pi.dispatch` calls (ADR-0016 §2: the overflow resolves immediately as\n * rejected, it is never queued). The two caps have independent counters:\n * neither throttles the other.\n * - collect logs / narration / phases and enforce the joint output budget,\n * - enforce the deadline, honour caller cancellation, and settle the run: hand the worker\n * back (pooled) or tear it down (cold).\n *\n * Everything the worker can influence is treated as untrusted input: every frame goes\n * through the protocol guards, is size-checked, and can only ever end the run.\n */\nimport { randomUUID } from \"node:crypto\";\nimport { MessageChannel, Worker } from \"node:worker_threads\";\nimport type { MessagePort } from \"node:worker_threads\";\nimport { BUILTIN_BINDING_NAMES, DISPATCH_BINDING_NAME, type BindingTable } from \"./bindings.ts\";\nimport { dispatchConcurrencyLimitReached } from \"./dispatch.ts\";\nimport { createWorkerEnv, effectiveTimeoutMs, resolveConfig } from \"./limits.ts\";\nimport type { PtcConfig, PtcSurface } from \"./limits.ts\";\nimport {\n HOST_FRAME_KIND,\n isPtcHostFrame,\n isPtcWorkerFrame,\n PTC_ERROR_KIND,\n WORKER_FRAME_KIND,\n} from \"./protocol.ts\";\nimport type {\n PtcCallFrame,\n PtcCallResultErrorFrame,\n PtcCallResultFrame,\n PtcCancelReason,\n PtcErrorKind,\n PtcErrorShape,\n PtcHostFrame,\n PtcJsonValue,\n} from \"./protocol.ts\";\nimport {\n WorkerPool,\n publishImageBytes,\n publishResetTime,\n workerSpawnOptions,\n} from \"./worker-pool.ts\";\nimport { buildWorkerUrl } from \"./worker-source.ts\";\n\nexport interface RunPtcProgramOptions {\n /** Program body: an async function body (`return`/`await` at the top level). */\n code: string;\n surface: PtcSurface;\n /** Working directory for bindings and the run's recorded cwd (F4). */\n cwd: string;\n /** Bindings the program may call; the table keys become `tools.<name>`. */\n bindings: BindingTable;\n /** Workflow surface only: value bound to the program's `args` global. */\n args?: unknown;\n /** Requested deadline; `0`/absent fall back to `timeoutMs`, then clamped to `maxTimeoutMs`. */\n timeoutMs?: number;\n /** Per-run limit overrides on top of `DEFAULT_CONFIG`. */\n config?: Partial<PtcConfig>;\n /** Cancels the run; the worker gets a cooperative cancel window before termination. */\n signal?: AbortSignal;\n /**\n * Depth of this run in the `pi.dispatch` recursion chain (ADR-0016 Recursive section):\n * 0 for the parent turn's run, 1+ for a run inside a child spawned by `pi.dispatch`.\n * Handed to the binding context so the dispatch binding can bound recursion. The\n * extension entrypoint derives it from `PI_PTC_DEPTH`; direct library use defaults\n * to 0.\n */\n depth?: number;\n /** Identifier carried to the worker; generated when omitted. */\n runId?: string;\n /**\n * Optional worker pool (ADR-0017). Absent = spawn a fresh worker and terminate it\n * at run end (the original cold-start path). Present = the dispatcher acquires a\n * worker from the pool at run start and releases it back at run end. Acquire waits\n * are bounded by `config.poolAcquireTimeoutMs`; on timeout the run fails with\n * `kind: workerExit`.\n */\n pool?: WorkerPool;\n}\n\n/**\n * One image hoisted out of a successful binding result (DSH parity — see ADR-0014,\n * ADR-0017 §8).\n *\n * `data` is base64 exactly as pi's own `read` tool returns it, so the tool layer can\n * forward it as an `ImageContent` block without re-encoding. The host↔worker wire carries\n * base64 too: the worker's channel is lossless JSON (a program may return part of a\n * binding result), and a `MessagePort` transferList of raw bytes could not survive that\n * contract — a transferred `ArrayBuffer` would be unusable as a program's return value.\n * Keeping one representation end to end means zero conversions between the binding and\n * pi's image adapter.\n */\nexport interface PtcImage {\n data: string;\n mimeType: string;\n}\n\nexport interface PtcRunOutcome {\n /** `console.*` output in arrival order. */\n logs: string[];\n /** Workflow `log(message)` narration (observers only, never console output). */\n narrations: string[];\n /** Workflow `phase(title)` titles in arrival order. */\n phases: string[];\n /** Completion value; absent when the program returned nothing or the run failed. */\n value?: PtcJsonValue;\n /**\n * Images hoisted out of successful binding results, in call order.\n *\n * Every image a program's tool calls produced, with no cap and no dedupe: how many images a run\n * attaches is the program's business, exactly as it is in DSH. Each entry carries base64 `data`,\n * the representation pi's image adapter consumes, so the tool layer forwards it untouched.\n * Present only when at least one was hoisted — a failed or cancelled run attaches nothing\n * (ADR-0014 §2), because the tool layer throws for it (`codeRunFailedError`) and its image would\n * never reach the model.\n */\n images?: PtcImage[];\n /** Failure details; absent on success. */\n error?: PtcErrorShape;\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** A leaf whose bytes JSON cannot express: it is billed by `byteLength`, not by its JSON text. */\nfunction isBinaryLeaf(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\n/**\n * Walk a value tree, calling `onBinary` on every binary leaf (`ArrayBuffer` and any\n * `ArrayBufferView`). Cycles and shared sub-objects are visited once, so a pathological frame\n * cannot wedge or inflate the walk.\n *\n * Binary leaves are billed here rather than by their JSON text for two reasons: `JSON.stringify`\n * serialises an `ArrayBuffer` as `{}` (the size vanishes) and explodes a typed-array view into\n * one key per element (the size is overstated by ~6 bytes per element). The walk therefore stops\n * at the leaf and never recurses into a view's `.buffer`, which would count the same bytes twice.\n * ADR-0017 W-6 spells out the refactor; this is its binary half.\n */\nfunction walkBinaryLeaves(\n value: unknown,\n onBinary: (binaryLeaf: ArrayBuffer | ArrayBufferView) => void,\n): void {\n const seen = new WeakSet<object>();\n const visit = (candidate: unknown): void => {\n if (candidate === null || candidate === undefined) return;\n if (candidate instanceof ArrayBuffer) {\n onBinary(candidate);\n return;\n }\n if (ArrayBuffer.isView(candidate)) {\n onBinary(candidate);\n return;\n }\n if (typeof candidate !== \"object\") return;\n if (seen.has(candidate)) return;\n seen.add(candidate);\n if (Array.isArray(candidate)) {\n for (const item of candidate) visit(item);\n return;\n }\n for (const key of Object.keys(candidate as Record<string, unknown>)) {\n visit((candidate as Record<string, unknown>)[key]);\n }\n };\n visit(value);\n}\n\n/**\n * Serialized byte size of a frame, the unit both the output and message budgets use.\n *\n * The count is the JSON text the frame becomes — every brace, bracket, separator, key name and\n * scalar — plus the bytes of any binary leaf, which that text cannot express. Two passes:\n *\n * 1. the whole value through `JSON.stringify` with binary leaves substituted by `null`: every\n * structural character, key name and scalar is billed the way the old helper billed it, and a\n * cyclic value throws here (as before);\n * 2. the binary leaves by `byteLength`, added on top.\n *\n * A frame with binary leaves therefore counts 4 bytes (its substituted `null`) more than the JSON\n * text it would have if the bytes vanished. That residue is deliberate and the safe direction:\n * under-counting the payload is the hole ADR-0017 W-6 closes, and the worker's channel clones\n * those bytes for real.\n *\n * A frame with no binary leaf produces exactly the previous helper's number — the substituter is\n * a no-op when there is nothing to substitute — and a cyclic or otherwise unaccountable payload\n * still reports `Number.POSITIVE_INFINITY` so the caller fails the run.\n */\nfunction serializedBytes(value: unknown): number {\n try {\n const text = Buffer.byteLength(\n JSON.stringify(value, (_key, item) => (isBinaryLeaf(item) ? null : item)) ?? \"\",\n \"utf8\",\n );\n let binary = 0;\n walkBinaryLeaves(value, (leaf) => {\n binary += leaf.byteLength;\n });\n return text + binary;\n } catch {\n // Cyclic or otherwise unaccountable payload: report as unbounded so the caller fails the run.\n return Number.POSITIVE_INFINITY;\n }\n}\n\n/**\n * Run one PTC program in a fresh worker and resolve with its outcome.\n *\n * Never rejects for program/ limit / cancellation failures — those are reported as\n * `outcome.error` so callers have one place to render from. It only throws on caller bugs\n * (invalid config overrides).\n */\nexport async function runPtcProgram(options: RunPtcProgramOptions): Promise<PtcRunOutcome> {\n const config = resolveConfig(options.config);\n const env = createWorkerEnv();\n const runId = options.runId ?? randomUUID();\n const timeoutMs = effectiveTimeoutMs(options.timeoutMs, config);\n\n const logs: string[] = [];\n const narrations: string[] = [];\n const phases: string[] = [];\n\n if (options.signal?.aborted) {\n return {\n logs,\n narrations,\n phases,\n error: { kind: PTC_ERROR_KIND.abort, message: \"run cancelled before start\" },\n };\n }\n\n // Acquire the worker before entering the Promise constructor: pool acquires are\n // async (they queue when the pool is full and time out per `poolAcquireTimeoutMs`),\n // and the constructor callback cannot `await`. Cold-start falls back to `new Worker`\n // when no pool is configured. ADR-0017 §1.\n let worker: Worker;\n try {\n if (options.pool) {\n worker = await options.pool.acquire();\n } else {\n // Cold path: one run owns this worker outright, so it carries the run's own `runId`.\n // The hardening (F1–F3) has one definition for both paths — `workerSpawnOptions`.\n worker = new Worker(\n buildWorkerUrl(),\n workerSpawnOptions({ surface: options.surface, env, limits: config, runId }),\n );\n }\n } catch (error) {\n // Pool acquire timed out (or another acquire-side failure): surface as workerExit\n // so the tool layer renders a coherent \"the harness could not get me a worker\"\n // message rather than a stack trace.\n return {\n logs,\n narrations,\n phases,\n error: {\n kind: PTC_ERROR_KIND.workerExit,\n message: `pool acquire failed: ${messageOf(error)}`,\n },\n };\n }\n\n return await new Promise<PtcRunOutcome>((resolve) => {\n const channel = new MessageChannel();\n const control: MessagePort = channel.port1;\n const workerPort: MessagePort = channel.port2;\n\n const bindingAbort = new AbortController();\n let settled = false;\n /** Set once a stop is under way; owns the terminal state from then on (`settleTerminal`). */\n let cancelling: PtcCancelReason | undefined;\n /**\n * Whether the worker has emitted its first `ready` frame. A cancel that lands before it does\n * still arms the grace window (the settle time must be bounded even for a worker that never\n * becomes reachable); the `ready` handler restarts that window, so a merely slow spawn gets\n * its full `graceMs` from the moment it can actually react.\n */\n let workerReady = false;\n let pendingCalls = 0;\n let outputBytes = 0;\n /**\n * Concurrently in-flight `pi.dispatch` calls, counted so the per-run hard cap\n * (ADR-0016 §2) can reject the overflow immediately. There is deliberately no\n * waiter queue behind this counter: the N+1th concurrent call resolves as\n * rejected instead of waiting for a slot. Independent of `activeBuiltinCalls`.\n */\n let activeDispatches = 0;\n /**\n * Concurrently in-flight builtin binding calls, counted against\n * `maxParallelSubCalls` (ADR-0004 consequence). Unlike the dispatch cap,\n * the overflow FIFO-queues for a slot — DSH's semantics for builtin fan-out.\n * Independent of `activeDispatches`.\n */\n let activeBuiltinCalls = 0;\n const builtinWaiters: Array<() => void> = [];\n // Per-run depth for `pi.dispatch` (ADR-0016 Recursive section).\n // The parent turn's PTC run is depth 0; a run inside a child spawned by `pi.dispatch`\n // starts at the depth the child was stamped with (`PI_PTC_DEPTH` → the `depth`\n // option). The dispatcher itself does not change it within a single run.\n const runDepth = options.depth ?? 0;\n /** Deadline timer: fires `timeoutMs` after the run started. A cancel does not disarm it. */\n let runTimer: NodeJS.Timeout | undefined;\n /** The cancel's cooperative window: armed and re-armed by `armGraceTimer` only. */\n let graceTimer: NodeJS.Timeout | undefined;\n\n /* --------------------------- hoisted images (ADR-0014 / ADR-0017 §8) --------------------------- */\n\n const images: PtcImage[] = [];\n\n /**\n * Capture the image blocks from one binding result.\n *\n * `PtcImage` has exactly one representation — base64 `data`, the same shape pi's own `read`\n * returns and the same shape the JSON-only worker channel carries. A binding that emits\n * `data: <base64>` passes through untouched (no decode/encode round trip); a binding that\n * produces raw bytes is normalised here, once, host-side.\n *\n * The caller (`dispatchCall`) only keeps the captured list when `postCallResult` returned\n * true: a frame the port rejected was not a successful subtool result (ADR-0014 §2).\n */\n const captureImages = (value: unknown): PtcImage[] => {\n const captured: PtcImage[] = [];\n const content = (value as { content?: unknown } | null)?.content;\n if (!Array.isArray(content)) return captured;\n for (const block of content) {\n if (block === null || typeof block !== \"object\") continue;\n const candidate = block as {\n type?: unknown;\n data?: unknown;\n bytes?: unknown;\n mimeType?: unknown;\n };\n if (candidate.type !== \"image\") continue;\n let data: string | undefined;\n if (typeof candidate.data === \"string\" && candidate.data.length > 0) {\n // The shape pi's own tools emit, and the shape the wire carries: pass through.\n data = candidate.data;\n } else if (candidate.bytes instanceof ArrayBuffer && candidate.bytes.byteLength > 0) {\n // A binding that produces raw bytes (no pi tool does today) is normalised here, so\n // `PtcImage` has exactly one representation downstream.\n data = Buffer.from(candidate.bytes).toString(\"base64\");\n }\n if (data === undefined) continue;\n captured.push({\n data,\n mimeType:\n typeof candidate.mimeType === \"string\"\n ? candidate.mimeType\n : \"application/octet-stream\",\n });\n }\n return captured;\n };\n\n const cancelMessage = (reason: PtcCancelReason): string =>\n reason === \"timeout\" ? `run timed out after ${timeoutMs} ms` : \"run cancelled\";\n\n /** The error kind a stop reports: whichever reason asked for it. */\n const cancelErrorKind = (reason: PtcCancelReason): PtcErrorKind =>\n reason === \"timeout\" ? PTC_ERROR_KIND.timeout : PTC_ERROR_KIND.abort;\n\n const finish = (outcome: PtcRunOutcome): void => {\n if (settled) return;\n settled = true;\n if (runTimer) clearTimeout(runTimer);\n if (graceTimer) clearTimeout(graceTimer);\n options.signal?.removeEventListener(\"abort\", onAbortSignal);\n bindingAbort.abort();\n control.close();\n if (options.pool) {\n // Warm-reuse path: hand the worker back to the pool. The pool retires it\n // if it has already exited (Node marks a terminated worker with\n // `threadId === -1`); otherwise it parks the worker in `idle` or hands it\n // to the next waiter. ADR-0017 §1.\n options.pool.release(worker);\n } else {\n // Cold-start path: terminate the worker. The runtime owns the lifecycle.\n void worker.terminate();\n }\n resolve(outcome);\n };\n\n const fail = (kind: PtcErrorKind, message: string, stack?: string): void => {\n finish({\n logs,\n narrations,\n phases,\n error: stack === undefined ? { kind, message } : { kind, message, stack },\n });\n };\n\n /* -------------------------------- sending -------------------------------- */\n\n const sendControl = (frame: PtcHostFrame): void => {\n if (settled) return;\n // Every outbound frame is validated by the same guard the worker's inbound frames\n // go through: a construction bug fails the run here rather than inside the worker.\n if (!isPtcHostFrame(frame)) {\n fail(PTC_ERROR_KIND.protocol, \"internal error: malformed host frame\");\n return;\n }\n control.postMessage(frame);\n };\n\n const sendInit = (): void => {\n const frame: PtcHostFrame = {\n kind: HOST_FRAME_KIND.init,\n runId,\n surface: options.surface,\n code: options.code,\n bindings: [...options.bindings.keys()],\n bindingCandidates: [...BUILTIN_BINDING_NAMES],\n maxItemsPerCall: config.maxItemsPerCall,\n maxPendingCalls: config.maxPendingCalls,\n ...(options.args === undefined ? {} : { args: options.args }),\n };\n if (!isPtcHostFrame(frame)) {\n fail(PTC_ERROR_KIND.protocol, \"internal error: malformed init frame\");\n return;\n }\n const bytes = serializedBytes(frame);\n if (bytes > config.maxMessageBytes) {\n fail(\n PTC_ERROR_KIND.protocol,\n `init frame of ${bytes} bytes exceeds maxMessageBytes (${config.maxMessageBytes})`,\n );\n return;\n }\n sendControl(frame);\n };\n\n /* ------------------------------- receiving ------------------------------- */\n\n const accountOutput = (bytes: number, source: string): boolean => {\n outputBytes += bytes;\n if (outputBytes <= config.maxOutputBytes) return true;\n // R1 §3: an oversize payload keeps the log prefix that did fit.\n fail(\n PTC_ERROR_KIND.outputLimit,\n `${source} exceeded the output budget: logs + result reached ${outputBytes} bytes (maxOutputBytes=${config.maxOutputBytes}); ${logs.length} log line(s) retained`,\n );\n return false;\n };\n\n /* ---------------------- builtin fan-out cap (ADR-0004) ---------------------- */\n\n const acquireBuiltinSlot = async (): Promise<void> => {\n if (activeBuiltinCalls < config.maxParallelSubCalls) {\n activeBuiltinCalls += 1;\n return;\n }\n await new Promise<void>((slot) => {\n builtinWaiters.push(slot);\n });\n };\n\n const releaseBuiltinSlot = (): void => {\n const next = builtinWaiters.shift();\n if (next) {\n // Hand the slot over directly; `activeBuiltinCalls` is unchanged.\n next();\n return;\n }\n activeBuiltinCalls -= 1;\n };\n\n const handleCall = (frame: PtcCallFrame): void => {\n pendingCalls += 1;\n if (pendingCalls > config.maxPendingCalls) {\n // Defense in depth only. The shipped worker admits calls before posting them\n // (`maxPendingCalls` travels in the init frame), so a normal program — including a\n // wide `Promise.all` or `parallel()` fan-out — never reaches this branch: the burst\n // queues in the worker, and arrivals here stay at or below the ceiling. This fires\n // only for a worker that ignores its admission budget, which is why it fails the\n // run instead of throttling.\n fail(\n PTC_ERROR_KIND.protocol,\n `more than maxPendingCalls (${config.maxPendingCalls}) binding calls in flight`,\n );\n return;\n }\n void dispatchCall(frame).finally(() => {\n pendingCalls -= 1;\n });\n };\n\n const dispatchCall = async (frame: PtcCallFrame): Promise<void> => {\n const binding = options.bindings.get(frame.tool);\n if (!binding) {\n postCallResult({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: false,\n message:\n `no binding named \"${frame.tool}\" in this run; available bindings: ` +\n ([...options.bindings.keys()].join(\", \") || \"(none)\"),\n });\n return;\n }\n const isDispatch = frame.tool === DISPATCH_BINDING_NAME;\n if (isDispatch) {\n /* dispatch cap (ADR-0016 §2) */\n // Hard cap: at `dispatchConcurrency` in-flight calls the next one is rejected\n // immediately — never queued, never executed. The rejection is a settled\n // DispatchResult (the binding never throws, §3), so a Promise.all /\n // Promise.allSettled over pi.dispatch calls sees a settled record, not a throw.\n if (activeDispatches >= config.dispatchConcurrency) {\n postCallResult({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: true,\n value: dispatchConcurrencyLimitReached(),\n });\n return;\n }\n activeDispatches += 1;\n } else {\n // Builtin fan-out cap (ADR-0004 consequence): the overflow waits for a slot\n // instead of failing.\n await acquireBuiltinSlot();\n if (settled) {\n // Late arrival: the run settled (deadline/abort) while this call was waiting\n // for a builtin slot, so the worker that asked is gone and `bindingAbort` has\n // already fired. Do NOT execute the binding: it would start host-side work\n // whose only possible outcome is the abort it cannot deliver anywhere — the\n // port is closed. The in-flight case is covered: a call that was already\n // executing when the cancel fired sees `bindingAbort` through its context.\n releaseBuiltinSlot();\n return;\n }\n }\n try {\n const value = await binding.execute(frame.args, {\n signal: bindingAbort.signal,\n callId: frame.callId,\n depth: runDepth,\n maxDispatchDepth: config.maxDispatchDepth,\n });\n // Capture image blocks before the callResult post so the hoist can never race the\n // worker's view of the result; the captures are committed only once the post\n // returned true.\n const captured = captureImages(value);\n // Per-frame byte accounting before the post, as for every other frame: a `callResult` is\n // the other host→worker control frame, so it answers to the same `maxMessageBytes` cap as\n // the `init` frame and every inbound frame (\"cap on a single control frame, either\n // direction\", limits.ts / R1 §1). Over the cap is excessive control traffic — DSH's own\n // wording for `kind: protocol`, R1 §error kinds — so it ends the run here instead of\n // shipping a 128 MiB structured clone. Compare `postCallResult`: a value the *port*\n // rejects is a per-call failure (the program can catch a `ToolCallError`); a frame over\n // the documented cap is not, it is the same run-level failure the other three frame\n // checks produce.\n const callFrame: PtcHostFrame = {\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: true,\n value,\n };\n const callFrameBytes = serializedBytes(callFrame);\n if (callFrameBytes > config.maxMessageBytes) {\n fail(\n PTC_ERROR_KIND.protocol,\n `callResult frame of ${callFrameBytes} bytes exceeds maxMessageBytes (${config.maxMessageBytes})`,\n );\n return;\n }\n const posted = postCallResult(callFrame);\n // Hoist only a result the worker actually received: DSH's condition is a *successful*\n // subtool result, and a payload the port rejected was not one (ADR-0014 §2).\n if (posted) {\n // Images do not pass ADR-0003's output budget (ADR-0014 Consequences: \"Images are not\n // output ... That is deliberate\"). The per-frame `maxMessageBytes` guard above still\n // bounds what a single callResult may carry, so a pathological binding cannot smuggle\n // unbounded payload in one frame. Volume stays observable: `ptc:image:hoist-bytes`\n // publishes every hoisted block and the PTC row's meta carries the count.\n for (const image of captured) {\n images.push(image);\n publishImageBytes(Buffer.byteLength(image.data, \"base64\"));\n }\n }\n } catch (error) {\n postCallResult({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: false,\n message: messageOf(error),\n });\n } finally {\n if (isDispatch) {\n activeDispatches -= 1;\n } else {\n releaseBuiltinSlot();\n }\n }\n };\n\n /** `true` when the worker actually received the result (see the hoist at the call site). */\n const postCallResult = (frame: PtcCallResultFrame): boolean => {\n if (settled) return false;\n try {\n control.postMessage(frame);\n return true;\n } catch (error) {\n // The binding's value could not be structured-cloned; report the call as failed\n // instead of leaving the worker waiting on a response that will never arrive.\n // The failing payload is dropped rather than resent — it is exactly what the\n // clone rejected.\n try {\n control.postMessage({\n kind: HOST_FRAME_KIND.callResult,\n callId: frame.callId,\n tool: frame.tool,\n ok: false,\n message: `binding result could not be transferred: ${messageOf(error)}`,\n } satisfies PtcCallResultErrorFrame);\n } catch {\n fail(PTC_ERROR_KIND.protocol, \"binding result is not transferable\");\n }\n return false;\n }\n };\n\n const settleTerminal = (outcome: PtcRunOutcome): PtcRunOutcome => {\n if (!cancelling) return outcome;\n // Once cancelled, the caller asked for a stop: the run reports the stop, not the\n // value the program managed to produce inside the grace window. A failed run\n // attaches nothing (ADR-0014 §2): the tool layer throws for it, so images could\n // never reach the model anyway.\n return {\n logs,\n narrations,\n phases,\n error: {\n kind: cancelErrorKind(cancelling),\n message: cancelMessage(cancelling),\n },\n };\n };\n\n const handleFrame = (frame: unknown): void => {\n if (settled) return;\n if (!isPtcWorkerFrame(frame)) {\n fail(PTC_ERROR_KIND.protocol, \"malformed worker frame\");\n return;\n }\n const bytes = frame.kind === WORKER_FRAME_KIND.ready ? 0 : serializedBytes(frame);\n if (!Number.isFinite(bytes)) {\n fail(PTC_ERROR_KIND.protocol, \"worker frame could not be serialized for accounting\");\n return;\n }\n if (bytes > config.maxMessageBytes) {\n fail(\n PTC_ERROR_KIND.protocol,\n `control frame of ${bytes} bytes exceeds maxMessageBytes (${config.maxMessageBytes})`,\n );\n return;\n }\n\n switch (frame.kind) {\n case WORKER_FRAME_KIND.ready: {\n // Warm-reuse signal: a worker that has been through at least one run reports\n // \"ready\" again after clearing per-run state. The gap since the previous\n // release is the per-run reset cost, which is what ADR-0017 measures to\n // verify the warm path actually saves time.\n if (options.pool) {\n const previousSettled = options.pool.lastSettledAt(worker);\n if (previousSettled !== undefined) {\n publishResetTime(Date.now() - previousSettled);\n }\n }\n if (!workerReady) {\n workerReady = true;\n if (cancelling) {\n // The cancel arrived while this worker was still booting, so it armed the fallback\n // window (see `beginCancel`). The worker can react now: restart the window so it\n // gets its full `graceMs` to observe the cancel. Still bounded — the run's deadline\n // timer stays armed through a cancel, and the fallback never waited for `ready`.\n armGraceTimer(cancelling);\n // Do **not** hand a cancelled run a program: `init` would start work whose result\n // nobody is waiting for, and the worker would spend the grace window running it.\n // A worker that was cancelled while booting reports the cancel instead of a\n // `ready` (see `reportIdleCancel`); this branch is the belt-and-braces path for a\n // `ready` and a `cancel` that crossed on the wire.\n return;\n }\n }\n sendInit();\n return;\n }\n case WORKER_FRAME_KIND.call:\n handleCall(frame);\n return;\n case WORKER_FRAME_KIND.log:\n if (accountOutput(bytes, \"console output\")) logs.push(frame.text);\n return;\n case WORKER_FRAME_KIND.narration:\n if (accountOutput(bytes, \"workflow narration\")) narrations.push(frame.message);\n return;\n case WORKER_FRAME_KIND.phase:\n if (accountOutput(bytes, \"workflow phase\")) phases.push(frame.title);\n return;\n case WORKER_FRAME_KIND.result: {\n if (!accountOutput(bytes, \"completion value\")) return;\n // A cancel owns the terminal state and takes the images with it: `settleTerminal`\n // rebuilds the outcome without them, which is ADR-0014 §2's \"a failed run attaches\n // nothing\" rule.\n const base = {\n logs,\n narrations,\n phases,\n ...(images.length > 0 ? { images } : {}),\n };\n finish(\n settleTerminal(frame.value === undefined ? base : { ...base, value: frame.value }),\n );\n return;\n }\n case WORKER_FRAME_KIND.error:\n // A failed run attaches nothing (ADR-0014 §2): the tool layer throws for it, so an\n // image hoisted earlier in the run could never reach the model anyway.\n finish(settleTerminal({ logs, narrations, phases, error: frame.error }));\n return;\n default:\n fail(PTC_ERROR_KIND.protocol, \"unknown worker frame\");\n }\n };\n\n /* ------------------------------- lifecycle ------------------------------- */\n\n function onAbortSignal(): void {\n beginCancel(\"abort\");\n }\n\n /**\n * Arm the cooperative-cancel window: `graceMs` from now the run settles with `reason`, unless\n * the worker settles it first. Re-arming replaces the pending window.\n *\n * The invariant this guards: **from the moment a run starts, it settles in bounded time.**\n * The window is therefore armed by `beginCancel` itself, not by the worker's `ready` frame —\n * a worker that never becomes reachable (spawn failure, wedged module load, a thread the OS\n * stopped scheduling) can no longer leave the run pending forever.\n */\n const armGraceTimer = (reason: PtcCancelReason): void => {\n if (graceTimer) clearTimeout(graceTimer);\n graceTimer = setTimeout(() => {\n fail(cancelErrorKind(reason), cancelMessage(reason));\n }, config.graceMs);\n };\n\n function beginCancel(reason: PtcCancelReason): void {\n if (settled) return;\n if (cancelling) {\n // A stop arrived while one was already pending — either the deadline landing on an\n // in-flight cancel, or the caller's abort landing after the deadline already fired (the\n // abort listener is registered `once`, so it can only ever be the first abort). This\n // branch is reached whether or not the worker became reachable: a worker that is mid-run\n // when the second stop arrives takes it too.\n //\n // Settle now rather than waiting out another `graceMs`. The run is already stopping and a\n // second stop must not extend that wait: in the cancel-first ordering the deadline has\n // just arrived, so the run must not stay unsettled past it, and in the deadline-first\n // ordering that deadline has already fired and is winding down through its own grace\n // window. The reason reported is the stop that arrived first — it is why the run is\n // ending; the second one only confirmed there was nothing left to wait for.\n fail(cancelErrorKind(cancelling), cancelMessage(cancelling));\n return;\n }\n cancelling = reason;\n // The deadline timer is deliberately left armed: while it is still pending it bounds a\n // cancelled run — if it fires with a cancel in flight, the branch above settles the run\n // right there, with the reason the caller asked for — so a caller's abort cannot outlive\n // the run's own deadline. The converse ordering has no deadline bound, and is not meant to:\n // see `runTimer` below for why a timed-out run settles at `timeoutMs + graceMs`.\n bindingAbort.abort();\n sendControl({ kind: HOST_FRAME_KIND.cancel, reason });\n // Arm the window immediately rather than waiting for the worker's first `ready` frame. The\n // window is about \"time the worker has had to react\"; the `ready` handler restarts it for a\n // worker that turns out to be merely slow. What it must never do is refuse to arm — that\n // left a worker stuck in BOOTING with no timer at all.\n armGraceTimer(reason);\n }\n\n worker.on(\"error\", (error: Error) => {\n fail(PTC_ERROR_KIND.workerExit, `worker failed: ${messageOf(error)}`);\n });\n worker.on(\"exit\", (code: number) => {\n fail(PTC_ERROR_KIND.workerExit, `worker exited with code ${code} before the run completed`);\n });\n control.on(\"message\", handleFrame);\n control.on(\"messageerror\", () => {\n fail(PTC_ERROR_KIND.protocol, \"worker sent a frame that could not be deserialized\");\n });\n\n options.signal?.addEventListener(\"abort\", onAbortSignal, { once: true });\n // The run's deadline. It is one-shot: when it fires by itself the timer is spent, so what ends\n // a timed-out run is the cancel it starts — `beginCancel(\"timeout\")` arms a `graceMs` window of\n // its own, and that window's expiry is the settle. A run that times out therefore settles by\n // `timeoutMs + graceMs` (\"the program gets `timeoutMs`, then `graceMs` to wind down\"), which is\n // the intended semantics, not a leak. While this timer is still pending it also bounds a run\n // the caller has already cancelled: a cancel never disarms it (see `beginCancel`), so that\n // ordering settles at the deadline.\n runTimer = setTimeout(() => {\n beginCancel(\"timeout\");\n }, timeoutMs);\n\n // The connect frame carries the control port *in the payload*; a transfer-list-only\n // entry is silently dropped by Node and the worker would wait forever.\n const connectFrame: PtcHostFrame = { kind: HOST_FRAME_KIND.connect, port: workerPort };\n if (!isPtcHostFrame(connectFrame)) {\n fail(PTC_ERROR_KIND.protocol, \"internal error: malformed connect frame\");\n } else {\n worker.postMessage(connectFrame, [workerPort]);\n }\n });\n}\n","/**\n * Model-facing text hygiene: the step pi's built-in tools take and PTC's own text assembly missed.\n *\n * Every built-in tool result is cleaned before the model reads it —\n * `stripAnsi` → `sanitizeBinaryOutput` → drop `\\r` (`pi-coding-agent`:\n * `dist/core/tools/render-utils.js`) — because a tool's output is arbitrary bytes: ANSI colour\n * codes from `pnpm`/`vitest`, carriage-return progress bars, NUL bytes from a binary file.\n *\n * The PTC tools assemble their own text block (`common.ts: renderToolResult`) and did none of\n * that, so a program returning captured `bash` output shipped `\\u001b[38;2;212;212;212m` to the\n * model as literal escapes, and any string with a real newline arrived JSON-escaped as `\\n`.\n * Both are pure noise the model has to read — and ANSI noise is charged per character, in tokens.\n *\n * **Scope: the text block only.** `details` stays raw so the TUI keeps rendering real colours\n * (`@earendil-works/pi-tui`'s `Text` understands ANSI), and a binding's result inside a program\n * is untouched — a program that wants coloured bytes for a file still has them. See ADR-0012.\n *\n * pi exports neither helper (its `exports` map has only `.`, `./rpc-entry`, `./client`,\n * `./experimental/plugin`), so both are mirrored here rather than imported. The ANSI pattern\n * follows ansi-regex / strip-ansi (MIT, Sindre Sorhus) via pi's own implementation.\n */\nimport type { PtcJsonValue } from \"../runtime/protocol.ts\";\n\n/** Hard cap for one rendered line; longer lines are truncated with `…`. */\nexport const MAX_LINE_CHARS = 200;\n\n/** A value stays on one line while its inline form fits this; otherwise it renders as a block. */\nconst INLINE_MAX_CHARS = 100;\n\n/** Indent applied per nesting level in block form. */\nconst INDENT = \" \";\n\n/**\n * ANSI escapes, in the two shapes that matter: OSC (\\`ESC ] …\\` to BEL, \\`ESC \\\\\\\\` or C1 ST) and\n * CSI plus friends (introducer, optional intermediates, optional params, final byte).\n *\n * Built through the \\`RegExp\\` constructor from an escaped source so no control character appears\n * literally — the repo lints via oxlint, whose \\`no-control-regex\\` rule rejects that.\n */\nconst ANSI_PATTERN = new RegExp(\n [\n \"(?:\\\\u001B\\\\][\\\\s\\\\S]*?(?:\\\\u0007|\\\\u001B\\\\u005C|\\\\u009C))\",\n \"[\\\\u001B\\\\u009B][[\\\\]()#;?]*(?:\\\\d{1,4}(?:[;:]\\\\d{0,4})*)?[\\\\dA-PR-TZcf-nq-uy=><~]\",\n ].join(\"|\"),\n \"g\",\n);\n\n/** Drop ANSI escape sequences (colours, cursor moves, OSC hyperlinks). */\nexport function stripAnsi(value: string): string {\n // Fast path: without an ESC or C1 introducer there is nothing to strip.\n if (!value.includes(String.fromCharCode(27)) && !value.includes(String.fromCharCode(155))) {\n return value;\n }\n return value.replace(ANSI_PATTERN, \"\");\n}\n\n/**\n * Make arbitrary program output safe to hand the model.\n *\n * Same three steps as pi's built-ins: strip ANSI, remove `\\r` (a progress bar's carriage\n * return is not a line break), then drop control characters other than `\\n`/`\\t` and the\n * Unicode format characters that break width math. Lone surrogates fall out of code-point\n * iteration for free.\n */\nexport function sanitizeText(value: string): string {\n const stripped = stripAnsi(value).replace(/\\r/g, \"\");\n if (stripped.length === 0) return stripped;\n let out = \"\";\n for (const char of stripped) {\n const code = char.codePointAt(0);\n if (code === undefined) continue;\n if (code === 0x0a || code === 0x09) {\n out += char;\n continue;\n }\n if (code <= 0x1f) continue;\n if (code >= 0xfff9 && code <= 0xfffb) continue;\n out += char;\n }\n return out;\n}\n\n/** Object keys that read as identifiers in the summary style are left unquoted. */\nconst BARE_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\nfunction keyText(key: string): string {\n return BARE_KEY.test(key) ? key : JSON.stringify(key);\n}\n\n/**\n * The inline form of a value, or `undefined` when it cannot be inlined.\n *\n * A string with a real newline is never inlined: keeping its line structure is the whole point\n * (see ADR-0012), and quoting it would turn one readable block back into `\\n` escapes.\n */\nfunction inlineForm(value: PtcJsonValue): string | undefined {\n if (value === null) return \"null\";\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (typeof value === \"string\") {\n const sanitized = sanitizeText(value);\n return sanitized.includes(\"\\n\") ? undefined : JSON.stringify(sanitized);\n }\n if (Array.isArray(value)) {\n const items: string[] = [];\n for (const item of value) {\n const rendered = inlineForm(item);\n if (rendered === undefined) return undefined;\n items.push(rendered);\n }\n return `[${items.join(\", \")}]`;\n }\n const keys = Object.keys(value);\n if (keys.length === 0) return \"{}\";\n const parts: string[] = [];\n for (const key of keys) {\n const rendered = inlineForm(value[key] ?? null);\n if (rendered === undefined) return undefined;\n parts.push(`${keyText(key)}: ${rendered}`);\n }\n return `{${parts.join(\", \")}}`;\n}\n\n/** Inline rendering, but only while it stays short enough to read on one line. */\nfunction tryInline(value: PtcJsonValue): string | undefined {\n const rendered = inlineForm(value);\n if (rendered === undefined) return undefined;\n return rendered.length <= INLINE_MAX_CHARS ? rendered : undefined;\n}\n\n/** A non-container value on its own line (block form only meets these defensively). */\nfunction scalarText(value: string | number | boolean | null): string {\n if (value === null) return \"null\";\n return typeof value === \"string\" ? sanitizeText(value) : String(value);\n}\n\n/** One key per line, braces kept, string values raw (real newlines preserved, indented). */\nfunction blockLines(value: PtcJsonValue, indent: string): string[] {\n if (value === null || typeof value !== \"object\") return [`${indent}${scalarText(value)}`];\n if (Array.isArray(value)) {\n if (value.length === 0) return [`${indent}[]`];\n const lines = [`${indent}[`];\n for (const item of value) {\n const inline = tryInline(item);\n if (inline !== undefined) {\n lines.push(`${indent}${INDENT}${inline}`);\n continue;\n }\n lines.push(...valueLines(item, indent + INDENT));\n }\n lines.push(`${indent}]`);\n return lines;\n }\n\n const keys = Object.keys(value);\n if (keys.length === 0) return [`${indent}{}`];\n const lines = [`${indent}{`];\n const inner = indent + INDENT;\n for (const key of keys) {\n const child = value[key] ?? null;\n const inline = tryInline(child);\n if (inline !== undefined) {\n lines.push(`${inner}${keyText(key)}: ${inline}`);\n continue;\n }\n if (typeof child === \"string\") {\n // A multi-line string keeps its own lines; the key line above delimits it.\n lines.push(`${inner}${keyText(key)}:`);\n for (const line of sanitizeText(child).split(\"\\n\")) {\n // A blank line inside a multi-line value stays blank — indenting it would leave trailing\n // whitespace in every transcript that shows a block ending in a newline.\n lines.push(line.length === 0 ? \"\" : `${inner}${INDENT}${line}`);\n }\n continue;\n }\n // A container opens on its key's line (`outer: {`) and closes back at the key's indent, so a\n // nested value does not push its braces onto lines of their own.\n const nested = blockLines(child, inner);\n lines.push(\n `${inner}${keyText(key)}: ${(nested[0] ?? \"\").trimStart()}`.trimEnd(),\n ...nested.slice(1),\n );\n }\n lines.push(`${indent}}`);\n return lines;\n}\n\nfunction valueLines(value: PtcJsonValue, indent: string): string[] {\n if (typeof value === \"string\") return sanitizeText(value).split(\"\\n\");\n if (value === null || typeof value !== \"object\") return [String(value)];\n return blockLines(value, indent);\n}\n\n/** Truncate every line that exceeds {@link MAX_LINE_CHARS}. */\nfunction capLines(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => (line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS - 1)}…` : line))\n .join(\"\\n\");\n}\n\n/**\n * Render a completion value for the model: strings verbatim, everything else as a compact\n * `{key: value}` summary or an indented block — never `JSON.stringify` on the whole value, whose\n * escaping destroys exactly the content a reader needs (newlines, quotes, non-ASCII spacing).\n */\nexport function renderModelValue(value: PtcJsonValue): string {\n // A program that returns text reads as text — quoting a top-level string would turn a report\n // into one escaped line, which is the failure mode this module exists to remove.\n if (typeof value === \"string\") return capLines(sanitizeText(value));\n if (value === null || typeof value !== \"object\") return String(value);\n const inline = tryInline(value);\n const body = inline !== undefined ? [inline] : valueLines(value, \"\");\n return capLines(body.join(\"\\n\"));\n}\n","/**\n * Shared plumbing for the two model-facing PTC tools (`ptc_run_code`, `ptc_workflow`).\n *\n * Both tools are thin. They resolve the run's working directory, hand the program to\n * `runPtcProgram()` (T3), and turn the dispatcher's outcome into one of pi's two tool-result\n * shapes:\n *\n * - **success** — `content` carries one text block (logs, workflow narration, phases, then the\n * completion value) and `details` carries the same facts structurally for rendering and logs;\n * - **failure** — the tool *throws* (pi's convention: `AgentTool.execute` — \"Throw on failure\n * instead of encoding errors in `content`\"), with R1 §3's message shape\n * `code run failed (<kind>): <message>` plus a `Captured output:` block.\n *\n * The dispatcher never rejects for program failures — it reports them as `outcome.error` — so\n * \"did the run fail\" is one check in one place, shared by both tools.\n *\n * The text block is the model's copy, so it is cleaned and laid out for reading (`./text.ts`:\n * ANSI stripped, control characters dropped, strings with real newlines kept multi-line) while\n * `details` stays raw for the TUI. See ADR-0012.\n */\nimport { randomUUID } from \"node:crypto\";\nimport { writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { BUILTIN_BINDING_NAMES } from \"../runtime/bindings.ts\";\nimport {\n DEFAULT_MAX_BYTES,\n DEFAULT_MAX_LINES,\n formatSize,\n truncateTail,\n} from \"@earendil-works/pi-coding-agent\";\nimport type { AgentToolResult, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport type { PtcRunOutcome } from \"../runtime/dispatcher.ts\";\nimport type { PtcConfig, PtcSurface } from \"../runtime/limits.ts\";\nimport type { PtcJsonValue } from \"../runtime/protocol.ts\";\nimport { renderModelValue, sanitizeText } from \"./text.ts\";\n\n/** Options every PTC tool factory accepts. */\nexport interface PtcToolOptions {\n /**\n * Per-run limit overrides, merged over `DEFAULT_CONFIG` by the dispatcher (`resolveConfig`).\n *\n * The shipped extension registers the tools without overrides; the seam exists so tests can\n * exercise budget/grace behaviour without materializing 64 MiB of logs, and so a future\n * settings surface has somewhere to plug in.\n */\n config?: Partial<PtcConfig>;\n /**\n * The tool names the binding table is built from, read once per execute.\n *\n * The shipped extension returns the session's active tools — except while PTC mode is on, when\n * it returns the mode's base snapshot instead (the built-ins the mode hid must stay callable\n * from inside a program). See `src/mode/ptc-mode.ts`.\n *\n * Omitting it means \"no session context\" (direct library use, or a hand-built test stub) and\n * asserts the full built-in surface.\n */\n getBindingSourceNames?: () => readonly string[];\n /**\n * Depth baseline for PTC runs this tool starts inside a child pi process: the depth of\n * the run that dispatched them, 0 for the parent turn's run (ADR-0016 Recursive\n * section). The extension entrypoint derives it from `PI_PTC_DEPTH` (stamped on the\n * child environment by `dispatch()`); direct library users omit it.\n */\n depth?: number;\n}\n\n/**\n * Read the depth baseline pi-ptc was started with inside a child pi process.\n *\n * `dispatch()` stamps `PI_PTC_DEPTH` (the child's own depth) onto the spawned subprocess's\n * environment, and the extension entrypoint reads it back here so a child PTC run's binding\n * context starts at the dispatched depth instead of at 0 — otherwise the recursion bound in\n * `dispatch()` could never bite below the first level. Only a pure non-negative integer is\n * accepted; a missing or malformed value means \"not a dispatched child\" and yields 0.\n */\nexport function resolveDepthFromEnv(source: NodeJS.ProcessEnv = process.env): number {\n const raw = source.PI_PTC_DEPTH;\n if (raw === undefined || !/^\\d+$/.test(raw)) return 0;\n return Number.parseInt(raw, 10);\n}\n\n/**\n * Binding names for one run: the built-ins this session actually has enabled (T7, #21).\n *\n * A PTC program must never reach further than the session it runs in — a session started\n * with `--tools read` or `--no-builtin-tools` must not be escapable through `tools.<name>`\n * calls. Names outside the built-in factory set are ignored: only tools this package can\n * build adapters for can be bound.\n */\nexport function resolveBindingNames(\n activeToolNames: readonly string[] | undefined,\n): readonly string[] {\n if (activeToolNames === undefined) return BUILTIN_BINDING_NAMES;\n const active = new Set(activeToolNames);\n return BUILTIN_BINDING_NAMES.filter((name) => active.has(name));\n}\n\n/**\n * One-line entry for the system prompt's \"Available tools\" section.\n *\n * Custom tools are omitted from that section unless they set this, which matters most in PTC mode:\n * there the two PTC surfaces are the *only* callable tools, so a prompt that lists no usable tool\n * would contradict the tool declarations. `promptGuidelines` cannot substitute for it — those\n * bullets land in the flat `Guidelines` section, which does not tell the model what it can call.\n */\nexport const PTC_RUN_CODE_SNIPPET =\n \"Run a TypeScript program that composes pi's tools in one shot (tools.<name>(args)); only its return value and console.log come back\";\n\nexport const PTC_WORKFLOW_SNIPPET =\n \"Run a structured TypeScript workflow with named phases, narration (log/phase) and structured concurrency (parallel/pipeline)\";\n\n/**\n * Guidelines are appended flat to the `Guidelines` section with no tool-name prefix, so each bullet\n * must name its tool explicitly (pi's docs call this out; \"Use this tool when…\" is ambiguous).\n * These are the normal-mode hints — in PTC mode the injected briefing supersedes them.\n */\nexport const PTC_TOOL_GUIDELINES: readonly string[] = [\n // DSH states this contract in `run_code`'s own description (\"Image-bearing subtool results are\n // attached after the run.\"); the model has to know because the images never appear in the value it\n // returns (ADR-0014).\n \"Image-bearing tool results inside a program (a `tools.read` on a PNG, say) are attached to you after the run — never return image data as the completion value.\",\n \"Use ptc_run_code when a task needs several tool calls whose intermediate output you do not need to see — gather it inside the program and return only the final value.\",\n \"Use ptc_workflow instead of ptc_run_code when the work has named phases and the user benefits from seeing progress narration.\",\n];\n\n/** Tool name per worker surface — the names the model calls and reads in failure messages. */\nexport const SURFACE_TOOL_NAME: Record<PtcSurface, string> = {\n run_code: \"ptc_run_code\",\n workflow: \"ptc_workflow\",\n};\n\n/**\n * Resolve the run's working directory from pi's tool execution context.\n *\n * Investigated against `@earendil-works/pi-coding-agent@0.86.1`: `execute`'s fifth parameter is\n * `ExtensionContext`, and the type *does* expose `cwd: string` — documented there as \"Current\n * working directory\" (`dist/core/extensions/types.d.ts`), the same value pi's own built-in tools\n * resolve relative paths against (`ToolRenderContext.cwd`, and the extension guide's\n * `resolve(ctx.cwd, params.path)` pattern). So the session cwd comes from pi, and this package\n * never has to guess from `process.cwd()`.\n *\n * The `process.cwd()` fallback stays for hand-built contexts (tests, or a future caller passing a\n * partial object); pi always supplies the field.\n */\nexport function resolveToolCwd(ctx: ExtensionContext): string {\n const cwd: unknown = (ctx as { cwd?: unknown } | undefined)?.cwd;\n return typeof cwd === \"string\" && cwd.length > 0 ? cwd : process.cwd();\n}\n\n/** Structured, model-independent facts about a run: pi's `details` payload for both tools. */\nexport interface PtcToolDetails {\n /** Which worker surface produced this result. */\n surface: PtcSurface;\n /** `console.*` output in arrival order. */\n logs: string[];\n /** Workflow `log(message)` narration (always empty on `ptc_run_code`). */\n narrations: string[];\n /** Workflow `phase(title)` titles in arrival order (always empty on `ptc_run_code`). */\n phases: string[];\n /** Non-fatal diagnostics the run itself could not report (currently: unlisted phase titles). */\n warnings: string[];\n /** Completion value; absent when the program returned nothing. */\n result?: PtcJsonValue;\n /** Wall-clock duration of the whole run, including worker spawn and teardown. */\n durationMs: number;\n /**\n * How many images the run hoisted out of tool results (`outcome.images`).\n *\n * The images themselves live in the tool result's `content` (pi forwards them to the model from\n * there), so `details` needs only the count — a second copy here would pin the same base64 twice.\n * See ADR-0014.\n */\n imageCount: number;\n /**\n * Where the untruncated text block was written when pi's truncation contract cut it.\n *\n * Present only for a truncated run. The full text has no other home — a program's logs exist only\n * for the length of the run — so the file is the model's way back to the parts the text block had\n * to drop (it can `tools.read` it from inside the next program).\n */\n fullOutputPath?: string;\n}\n\n/**\n * The captured-output lines R1 §3 appends when a run fails.\n *\n * Phases and narrations are prefixed because they are workflow bookkeeping rather than console\n * output; `ptc_run_code` has neither, so its block is exactly the `logs` join R1 describes.\n */\nfunction capturedOutputLines(outcome: PtcRunOutcome): string[] {\n return [\n ...outcome.phases.map((title) => `[phase] ${sanitizeText(title)}`),\n ...outcome.narrations.map((message) => `[log] ${sanitizeText(message)}`),\n ...outcome.logs.map((line) => sanitizeText(line)),\n ];\n}\n\n/**\n * Build the error a failed run is surfaced with (R1 §3's `CodeRunFailedError` message).\n *\n * The model must be able to fix its program from this message alone: the error kind and message\n * explain what broke, and the captured output shows how far the program got before it did. There\n * is no `sandbox` text and no escalation guidance — ADR-0007 ships no OS sandbox, so the only\n * failure classes are the protocol ones.\n */\nexport function codeRunFailedError(outcome: PtcRunOutcome): Error {\n const error = outcome.error;\n if (error === undefined) {\n // Callers check `outcome.error` first; reaching here means the tool layer asked to render a\n // success as a failure, which is a bug in this package rather than in the model's program.\n return new Error(\"internal error: a successful PTC run was rendered as a failure\");\n }\n const captured = capturedOutputLines(outcome);\n const block = captured.length === 0 ? \"\" : `\\nCaptured output:\\n${captured.join(\"\\n\")}`;\n // A program can throw a message containing the raw bytes it just read (a coloured shell\n // error, a log line with escapes); the failure text is model-facing too.\n const failure = new Error(\n sanitizeText(`code run failed (${error.kind}): ${error.message}${block}`),\n );\n failure.name = \"CodeRunFailedError\";\n return failure;\n}\n\n/** Structural view of pi's `truncateTail` outcome, so the footer does not re-derive the numbers. */\ntype TailTruncation = ReturnType<typeof truncateTail>;\n\n/**\n * Write the untruncated text block to a temp file and return its path.\n *\n * Mirrors what pi's own `bash` does with a long output (its `fullOutputPath`): the run is the only\n * place the text ever existed, so a file under the OS temp dir is the difference between \"the rest is\n * gone\" and \"the rest is one `tools.read` away\".\n */\nfunction writeFullOutput(text: string): string {\n const filePath = join(tmpdir(), `pi-ptc-output-${randomUUID()}.txt`);\n writeFileSync(filePath, text, \"utf8\");\n return filePath;\n}\n\n/**\n * The `[Showing … Full output: path]` footer pi's built-ins use, worded for the same numbers.\n *\n * `truncatedBy` decides the wording: a line cut reports the line range, a byte cut also names the\n * ceiling, and a single oversized last line reports its own size — exactly what `bash` prints.\n */\nfunction truncationFooter(truncation: TailTruncation, fullOutputPath: string): string {\n const startLine = truncation.totalLines - truncation.outputLines + 1;\n const endLine = truncation.totalLines;\n if (truncation.lastLinePartial) {\n return `[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine}. Full output: ${fullOutputPath}]`;\n }\n if (truncation.truncatedBy === \"lines\") {\n return `[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${fullOutputPath}]`;\n }\n return `[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${fullOutputPath}]`;\n}\n\n/**\n * Render one successful outcome as pi's tool result.\n *\n * `content` is ordered the way a reader reconstructs the run: the phase roll-up, then workflow\n * narration, then console output, then the completion value, then warnings. Empty runs get R1's\n * placeholder (`(run_code completed with no output)`) so the model always sees a non-empty text\n * block — a tool result with nothing in it looks like a harness failure.\n *\n * `details` is deliberately the *uncleaned* copy: logs keep their ANSI so the TUI can still\n * colour them, and `result` stays structured for `render.ts`.\n */\nexport function renderToolResult(input: {\n outcome: PtcRunOutcome;\n surface: PtcSurface;\n durationMs: number;\n warnings?: readonly string[];\n}): AgentToolResult<PtcToolDetails> {\n const { outcome, surface } = input;\n const warnings = [...(input.warnings ?? [])];\n const images = outcome.images ?? [];\n const parts: string[] = [];\n if (outcome.phases.length > 0) {\n parts.push(`Phases: ${outcome.phases.map((title) => sanitizeText(title)).join(\" → \")}`);\n }\n if (outcome.narrations.length > 0) {\n parts.push(outcome.narrations.map((message) => sanitizeText(message)).join(\"\\n\"));\n }\n if (outcome.logs.length > 0) {\n parts.push(outcome.logs.map((line) => sanitizeText(line)).join(\"\\n\"));\n }\n if (outcome.value !== undefined) parts.push(renderModelValue(outcome.value));\n for (const warning of warnings) parts.push(`Warning: ${sanitizeText(warning)}`);\n const built =\n parts.length > 0\n ? parts.join(\"\\n\")\n : `(${SURFACE_TOOL_NAME[surface]} completed with no output)`;\n // pi's rule for a tool's model-facing text is \"Tools MUST truncate their output\" — 50 KB / 2000\n // lines, keeping the tail, with the remainder pointed at — and this package's own 64 MiB run\n // budget (ADR-0003) is what makes it bite. `fullOutputPath` is written before the tail is cut, so\n // the pointer never names a partial file.\n const truncation = truncateTail(built, {\n maxLines: DEFAULT_MAX_LINES,\n maxBytes: DEFAULT_MAX_BYTES,\n });\n let fullOutputPath: string | undefined;\n let text = truncation.content;\n if (truncation.truncated) {\n fullOutputPath = writeFullOutput(built);\n text += `\\n\\n${truncationFooter(truncation, fullOutputPath)}`;\n }\n // The text block is the model's copy; hoisted images ride the same result as image blocks, which\n // is how pi's own `read` hands a picture to the model (ADR-0014). `PtcImage.data` is already\n // base64 — the shape the binding emitted, the shape the worker's JSON channel carried, and the\n // shape pi's image adapter (`getImageDimensions(img.data, img.mimeType)`) requires — so the\n // block is forwarded verbatim, with no re-encoding anywhere on the path.\n const content: (TextContent | ImageContent)[] = [\n { type: \"text\", text },\n ...images.map((image): ImageContent => ({\n type: \"image\",\n data: image.data,\n mimeType: image.mimeType,\n })),\n ];\n return {\n content,\n details: {\n surface,\n logs: [...outcome.logs],\n narrations: [...outcome.narrations],\n phases: [...outcome.phases],\n warnings,\n ...(outcome.value === undefined ? {} : { result: outcome.value }),\n durationMs: input.durationMs,\n imageCount: images.length,\n ...(fullOutputPath === undefined ? {} : { fullOutputPath }),\n },\n };\n}\n","/**\n * Shared TUI renderer for `ptc_run_code` and `ptc_workflow`.\n *\n * Both tools produce the same `PtcToolDetails` shape (see `common.ts`), so one pair of helpers\n * drives both `renderCall` and `renderResult`.\n *\n * ## Shape: a tree, not a payload dump\n *\n * The row reads as a node in the transcript tree:\n *\n * `└─ PTC <description>` (call row)\n * ` → {file, clipNow} • 1 image · 536ms` (result collapsed — summary + right meta)\n * ` ├─ code <line 1>` (result expanded — children with tree connectors)\n * ` │ <line 2>`\n * ` ├─ out [bash stdout] …`\n * ` ├─ value { file: … }`\n * ` └─ image 1 image attached`\n *\n * The summary line and the children share the call's column, so the connectors only need to\n * distinguish nesting (`├─/└─`) from continuation (`│/space`). Right-aligned meta lives on the\n * result row, not the call row, because `renderCall` does not see the run outcome — pi's TUI\n * calls renderCall and renderResult separately, and threading the result details into renderCall\n * would break that seam.\n *\n * ## What a PTC row must never do\n *\n * Print the completion value via `JSON.stringify`. Escaping turns a returned report into `\\n` noise\n * wrapped over five rows — that is the ugly report this renderer exists to fix (ADR-0013). Values\n * render through `renderModelValue`, which keeps newlines real and indents containers.\n */\nimport type { Theme } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { PtcToolDetails } from \"./common.ts\";\nimport type { PtcJsonObject, PtcJsonValue } from \"../runtime/protocol.ts\";\nimport { renderModelValue, sanitizeText } from \"./text.ts\";\n\n/** Call-row label per surface; `ptc_workflow` gets its own so the two rows stay tellable apart. */\nconst TOOL_TITLE: Record<PtcToolDetails[\"surface\"], string> = {\n run_code: \"PTC\",\n workflow: \"PTC workflow\",\n};\n\n/**\n * Tree-connector layout. The call row owns column 0–2 (`└─ `); everything below it sits at column 3.\n * Children are siblings of the call's content (the summary line `→ {hint}`), so a child row needs a\n * connector to distinguish itself from the summary — and a continuation prefix to distinguish its\n * own wrapped lines from the child's first line.\n *\n * - Root call row: `└─ PTC …`\n * - Root continuation: ` → {hint}` or ` failed: …` (summary; same column as children)\n * - First / middle child: ` ├─ code …`\n * - First / middle cont.: ` │ …`\n * - Last child: ` └─ image …`\n * - Last cont.: ` …` (no connector — same shape as a wrapped value line)\n *\n * `TREE_INDENT` (3 spaces) is the column children and root continuations share.\n */\nconst TREE_ROOT = \"└─ \"; // call-row prefix\nconst TREE_ROOT_CONT = \" \"; // continuation under the root (summary line + error text)\nconst TREE_INDENT = \" \"; // column children start at\nconst TREE_FIRST = \"├─ \"; // connector of first / middle child\nconst TREE_LAST = \"└─ \"; // connector of last child\nconst TREE_CONT_FIRST = \"│ \"; // continuation of first / middle child\nconst TREE_CONT_LAST = \" \"; // continuation of last child (just spaces — no connector)\n\n/**\n * Gutter width for child labels. `code` / `out` / `value` / `warn` / `image` are 3–5 chars; `phases`\n * is 6 — pad to 8 so the longest label gets two trailing spaces before its content.\n */\nconst LABEL_WIDTH = 8;\n\nconst MAX_CODE_LINES_EXPANDED = 3;\nconst MAX_CODE_LINE_CHARS = 120;\nconst MAX_LOG_LINES_EXPANDED = 12;\nconst MAX_PHASES_EXPANDED = 8;\nconst MAX_WARNINGS_EXPANDED = 4;\nconst MAX_RESULT_HINT_CHARS = 60;\nconst MAX_ERROR_CHARS = 120;\n\n/** One rendered row: left content, plus an optional segment pinned to the right edge. */\ninterface RowLine {\n readonly left: string;\n readonly right?: string;\n}\n\n/**\n * A `Component` that right-aligns one meta segment per line.\n *\n * Stateless: it holds already-styled strings and pads them at render time, so pi can re-render at a\n * new width (terminal resize) without any invalidation bookkeeping.\n */\nclass PtcRow {\n private readonly lines: readonly RowLine[];\n\n constructor(lines: readonly RowLine[]) {\n this.lines = lines;\n }\n\n /** Part of the `Component` contract; nothing is cached, so nothing has to be dropped. */\n invalidate(): void {}\n\n render(width: number): string[] {\n const safeWidth = Math.max(1, Math.floor(width));\n return this.lines.map((line) => alignRow(line, safeWidth));\n }\n}\n\n/**\n * Lay out one row inside `width` columns.\n *\n * With no meta segment the line is simply truncated. With one, the meta is what must survive — a\n * count that has been cut in half (`• 19 outpu…`) tells the reader nothing — so the left side gives\n * up room first, and only a meta wider than the whole viewport is truncated itself.\n */\nfunction alignRow(line: RowLine, width: number): string {\n const { left, right } = line;\n if (right === undefined) return truncateToWidth(left, width, \"…\");\n const rightWidth = visibleWidth(right);\n if (rightWidth >= width) return truncateToWidth(right, width, \"…\");\n const leftText = truncateToWidth(left, Math.max(0, width - rightWidth - 1), \"…\");\n const pad = Math.max(1, width - rightWidth - visibleWidth(leftText));\n return leftText + \" \".repeat(pad) + right;\n}\n\n/** Truncate `s` to `n` chars, appending `…` when the input was longer. */\nfunction truncateChars(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n - 1)}…` : s;\n}\n\n/** First non-blank line of a block of text, sanitized and capped. */\nfunction firstLine(value: string): string {\n const line = sanitizeText(value)\n .split(\"\\n\")\n .find((candidate) => candidate.trim().length > 0);\n return truncateChars((line ?? \"\").trim(), MAX_ERROR_CHARS);\n}\n\n/** `n` rendered as `1.2s` / `850ms` (so users can tell short from long at a glance). */\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(2).replace(/\\.?0+$/, \"\")}s`;\n}\n\n/** `3 phases` / `1 phase` — singular and plural are both reachable from a real run. */\nfunction plural(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? \"\" : \"s\"}`;\n}\n\n/**\n * Pull the first line of code that is actually doing something — not a leading comment, blank\n * line, or import. Used as the call row's fallback when the model sent no description.\n */\nexport function firstMeaningfulCodeLine(code: string): string | undefined {\n for (const raw of code.split(\"\\n\")) {\n const line = raw.trim();\n if (line.length === 0) continue;\n if (line.startsWith(\"//\")) continue;\n if (line.startsWith(\"/*\")) continue;\n if (line.startsWith(\"*\")) continue;\n // Strip trailing inline comment so the preview doesn't double as a docstring\n const withoutTrailingComment = line.replace(/\\s*\\/\\/.*$/, \"\");\n return truncateChars(withoutTrailingComment, MAX_CODE_LINE_CHARS);\n }\n return undefined;\n}\n\n/**\n * The completion value as a single line a human can read: the value itself while it stays short\n * and single-line, otherwise its shape (`{version, node}`, `Array(3)`).\n *\n * A multi-line string contributes its first non-blank line plus a `+N lines` tail, which is the\n * whole difference between one readable row and the escaped wall this replaces.\n */\nfunction resultHint(value: PtcJsonValue | undefined): string | undefined {\n if (value === undefined) return undefined;\n if (typeof value === \"string\") {\n const lines = sanitizeText(value).split(\"\\n\");\n const first = lines.find((line) => line.trim().length > 0)?.trim() ?? \"\";\n const extra = lines.length > 1 ? ` (+${lines.length - 1} lines)` : \"\";\n if (first.length === 0 && extra.length === 0) return `\"\"`;\n return truncateChars(first, MAX_RESULT_HINT_CHARS) + extra;\n }\n if (value === null || typeof value !== \"object\") return String(value);\n const inline = renderModelValue(value);\n if (!inline.includes(\"\\n\") && inline.length <= MAX_RESULT_HINT_CHARS) return inline;\n if (Array.isArray(value)) return `Array(${value.length})`;\n const keys = Object.keys(value);\n if (keys.length === 0) return \"{}\";\n const head = keys.slice(0, 4).join(\", \");\n return `{${head}${keys.length > 4 ? `, +${keys.length - 4}` : \"\"}}`;\n}\n\n/**\n * Right-aligned meta: the run's countable facts, in the order a reader asks for them — how much\n * output, how long it took, how many phases (workflow), and anything that went wrong.\n */\nfunction metaText(details: PtcToolDetails | undefined, theme: Theme): string | undefined {\n if (details === undefined) return undefined;\n const segments: string[] = [];\n const outputLines = details.logs.length + details.narrations.length;\n if (outputLines > 0) segments.push(theme.fg(\"dim\", plural(outputLines, \"output line\")));\n // Images rode the tool result (ADR-0014): the count says so even though pi renders them itself.\n if (details.imageCount > 0) {\n segments.push(theme.fg(\"toolOutput\", plural(details.imageCount, \"image\")));\n }\n if (details.surface === \"workflow\" && details.phases.length > 0) {\n segments.push(theme.fg(\"dim\", plural(details.phases.length, \"phase\")));\n }\n if (details.warnings.length > 0) {\n segments.push(theme.fg(\"warning\", plural(details.warnings.length, \"warning\")));\n }\n // pi's truncation contract cut the text block: the tail is what the model read, the file is the rest.\n if (details.fullOutputPath !== undefined) segments.push(theme.fg(\"warning\", \"truncated\"));\n segments.push(theme.fg(\"dim\", formatDuration(details.durationMs)));\n return `${theme.fg(\"dim\", \"•\")} ${segments.join(theme.fg(\"dim\", \" · \"))}`;\n}\n\n/**\n * One child block in the expanded view: a `label` (right-padded into the gutter), N content lines,\n * and an optional `more` count for the `…+N more lines` tail.\n *\n * Block ordering is decided by the caller (see `renderPtcToolResultExpanded`); this module only\n * owns how a block lays itself out.\n */\ninterface ChildBlock {\n readonly label: string;\n readonly lines: readonly string[];\n readonly more?: number;\n}\n\n/** Render one child block as `RowLine[]`, with `├─` / `│` / `└─` connectors decided by position. */\nfunction renderChildBlock(child: ChildBlock, isLast: boolean, theme: Theme): RowLine[] {\n const firstPrefix = TREE_INDENT + (isLast ? TREE_LAST : TREE_FIRST);\n const contPrefix = TREE_INDENT + (isLast ? TREE_CONT_LAST : TREE_CONT_FIRST);\n const gutter = \" \".repeat(LABEL_WIDTH);\n const rows: RowLine[] = [];\n const first = child.lines[0] ?? \"\";\n rows.push({\n left: `${firstPrefix}${child.label.padEnd(LABEL_WIDTH)}${theme.fg(\"dim\", first)}`,\n });\n for (let i = 1; i < child.lines.length; i++) {\n rows.push({\n left: `${contPrefix}${gutter}${theme.fg(\"dim\", child.lines[i] ?? \"\")}`,\n });\n }\n if (child.more !== undefined && child.more > 0) {\n rows.push({\n left: `${contPrefix}${gutter}…+${child.more} more lines`,\n });\n }\n return rows;\n}\n\n// ---------------------------------------------------------------------------\n// Value-as-tree renderer (ADR-0013 §5)\n//\n// The value a program returns is a tree when it is a JSON object or array:\n// each property (or index) gets its own row with a ├─ / └─ connector, recursive\n// children inherit the parent's continuation bar, and a tail row collapses\n// what was withheld. This module only knows PtcJsonValue; consumers wrap each\n// emitted row with TREE_ROOT_CONT (so a row that starts with ├─ lands at the\n// call row's column-3 indent).\n// ---------------------------------------------------------------------------\n\n/** Defaults: collapse at 4 levels, 6 children per container, 120 chars per row. */\nexport const TREE_VALUE_MAX_DEPTH = 4;\nexport const TREE_VALUE_MAX_CHILDREN = 6;\nexport const TREE_VALUE_MAX_LINE_CHARS = 120;\n\n/** A JSON value is \"expandable\" when it is a non-empty object or array. */\nexport function isExpandableContainer(\n value: PtcJsonValue,\n): value is PtcJsonObject | PtcJsonValue[] {\n if (value === null || typeof value !== \"object\") return false;\n if (Array.isArray(value)) return value.length > 0;\n return Object.keys(value).length > 0;\n}\n\n/** Object keys that read as identifiers are left unquoted; the rest get JSON quoting. */\nconst TREE_BARE_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nfunction treeFormatKey(key: string): string {\n return TREE_BARE_KEY.test(key) ? key : JSON.stringify(key);\n}\n\n/** A scalar JSON value as a single readable token; strings are JSON-quoted, newlines folded. */\nfunction treeScalarPreview(value: PtcJsonValue, maxChars: number): string {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\" || typeof value === \"number\") return String(value);\n if (typeof value === \"string\") {\n const sanitized = sanitizeText(value);\n if (sanitized.length === 0) return '\"\"';\n if (sanitized.includes(\"\\n\")) {\n const flat = sanitized.replace(/\\n/g, \" \");\n const head = truncateChars(flat, Math.max(2, maxChars - 5));\n return JSON.stringify(head) + \"…\";\n }\n if (sanitized.length + 2 <= maxChars) return JSON.stringify(sanitized);\n return JSON.stringify(truncateChars(sanitized, Math.max(2, maxChars - 5))) + \"…\";\n }\n return \"?\";\n}\n\nfunction treeIsScalarJson(value: PtcJsonValue): boolean {\n return value === null || typeof value !== \"object\";\n}\n\n/** Try to render a container as a one-liner bracket form ({k: v} / [v, v]); undefined when it does not fit or children are deep. */\nfunction treeInlinePreview(value: PtcJsonValue, maxChars: number): string | undefined {\n if (maxChars < 8) return undefined;\n if (Array.isArray(value)) {\n if (value.length === 0) return \"[]\";\n if (value.length > 5 || !value.every(treeIsScalarJson)) return undefined;\n const items = value.map((item) =>\n treeScalarPreview(item, Math.max(2, Math.floor(maxChars / 4))),\n );\n const inline = \"[\" + items.join(\", \") + \"]\";\n return inline.length <= maxChars ? inline : undefined;\n }\n if (value === null || typeof value !== \"object\") return undefined;\n const keys = Object.keys(value);\n if (keys.length === 0) return \"{}\";\n if (keys.length > 5 || !Object.values(value).every(treeIsScalarJson)) return undefined;\n const entries = keys.map((key) => {\n const child = (value as Record<string, PtcJsonValue>)[key] ?? null;\n return (\n treeFormatKey(key) + \": \" + treeScalarPreview(child, Math.max(2, Math.floor(maxChars / 4)))\n );\n });\n const inline = \"{\" + entries.join(\", \") + \"}\";\n return inline.length <= maxChars ? inline : undefined;\n}\n\n/**\n * Append one pre-prefixed tree row to out, recursing for containers. Each level passes the\n * continuation indent its children should use; the connector itself encodes \"more siblings\".\n */\nfunction treeAppendNode(\n value: PtcJsonValue,\n label: string, // \"key: \", \"[0] \", or \"\" — the row's own text after the connector\n parentPrefix: string, // page above this row's connector (root's children pass \"\")\n isLast: boolean,\n maxDepth: number,\n maxChildren: number,\n maxLineChars: number,\n depth: number,\n out: string[],\n): void {\n const connector = isLast ? \"└─ \" : \"├─ \";\n const ownPrefix = parentPrefix + connector;\n const continuation = parentPrefix + (isLast ? \" \" : \"│ \");\n\n // Depth guard: collapse to ellipsis and stop recursing.\n if (depth >= maxDepth) {\n out.push(treeTruncateIndent(ownPrefix + label + \"…\", maxLineChars));\n return;\n }\n\n // Empty containers are leaves.\n if (Array.isArray(value) && value.length === 0) {\n out.push(treeTruncateIndent(ownPrefix + label + \"[]\", maxLineChars));\n return;\n }\n if (\n !Array.isArray(value) &&\n value !== null &&\n typeof value === \"object\" &&\n Object.keys(value).length === 0\n ) {\n out.push(treeTruncateIndent(ownPrefix + label + \"{}\", maxLineChars));\n return;\n }\n\n // Scalars.\n if (value === null || typeof value !== \"object\") {\n const budget = Math.max(0, maxLineChars - visibleWidth(ownPrefix) - label.length);\n out.push(\n treeTruncateIndent(ownPrefix + label + treeScalarPreview(value, budget), maxLineChars),\n );\n return;\n }\n\n // Small all-scalar containers collapse to one line.\n const inlineBudget = Math.max(0, maxLineChars - visibleWidth(ownPrefix) - label.length);\n const inline = treeInlinePreview(value, inlineBudget);\n if (inline !== undefined) {\n out.push(treeTruncateIndent(ownPrefix + label + inline, maxLineChars));\n return;\n }\n\n // Larger containers: header row, then recurse.\n const listLike = Array.isArray(value);\n const childCount = listLike ? value.length : Object.keys(value).length;\n let header: string;\n if (listLike) {\n header = \"Array(\" + childCount + \")\";\n } else {\n header =\n childCount > maxChildren\n ? \"{\" + maxChildren + \" of \" + childCount + \"}\"\n : \"{\" + childCount + \" keys}\";\n }\n out.push(treeTruncateIndent(ownPrefix + label + header, maxLineChars));\n\n const limit = Math.min(maxChildren, childCount);\n if (listLike) {\n for (let index = 0; index < limit; index += 1) {\n const isChildLast = index === limit - 1 && childCount <= limit;\n treeAppendNode(\n (value as PtcJsonValue[])[index] ?? null,\n \"[\" + index + \"] \",\n continuation,\n isChildLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n depth + 1,\n out,\n );\n }\n } else {\n const keys = Object.keys(value);\n for (let index = 0; index < limit; index += 1) {\n const isChildLast = index === limit - 1 && childCount <= limit;\n const key = keys[index] as string;\n treeAppendNode(\n (value as Record<string, PtcJsonValue>)[key] ?? null,\n treeFormatKey(key) + \": \",\n continuation,\n isChildLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n depth + 1,\n out,\n );\n }\n }\n if (childCount > limit) {\n const tail = listLike ? \"items\" : \"keys\";\n out.push(\n treeTruncateIndent(\n continuation + \"└─ \" + \"…+\" + (childCount - limit) + \" more \" + tail,\n maxLineChars,\n ),\n );\n }\n}\n\n/** Truncate a rendered row, preserving the leading tree-prefix indent (│/├/└ etc.). */\nfunction treeTruncateIndent(line: string, maxLineChars: number): string {\n if (visibleWidth(line) <= maxLineChars) return line;\n const match = line.match(/^([\\s│├└─]*)/);\n const indent = match && match[1] ? match[1] : \"\";\n const indentWidth = visibleWidth(indent);\n const budget = Math.max(0, maxLineChars - indentWidth - 1);\n let body = \"\";\n let used = 0;\n for (const ch of line.slice(indent.length)) {\n const w = visibleWidth(ch);\n if (used + w > budget) break;\n body += ch;\n used += w;\n }\n return indent + body + \"…\";\n}\n\n/**\n * Render a JSON value as an array of indented tree rows.\n *\n * Each row is a complete prefixed line ready to live under the call row's \" \" indent\n * (TREE_ROOT_CONT): root children start at \"├─ \" / \"└─ \", deeper levels add their own\n * connectors on top of the continuation bar. Caller supplies the row's TREE_ROOT_CONT — a\n * row that starts with \"├─ \" lands at column 3, exactly where other call-row children sit.\n *\n * Empty / scalar values return a one-row preview; non-empty containers recurse.\n */\nexport function renderValueTree(\n value: PtcJsonValue,\n options: {\n maxDepth?: number;\n maxChildren?: number;\n maxLineChars?: number;\n moreAfter?: boolean;\n } = {},\n): string[] {\n const maxDepth = options.maxDepth ?? TREE_VALUE_MAX_DEPTH;\n const maxChildren = options.maxChildren ?? TREE_VALUE_MAX_CHILDREN;\n const maxLineChars = options.maxLineChars ?? TREE_VALUE_MAX_LINE_CHARS;\n // A tree followed by sibling blocks must keep the chain open: its last row stays ├─.\n const moreAfter = options.moreAfter === true;\n if (!isExpandableContainer(value)) {\n // An empty container is not \"expandable\" but still must show its own shape, not the\n // scalar fallback (\"?\").\n if (value !== null && typeof value === \"object\") {\n return [Array.isArray(value) ? \"[]\" : \"{}\"];\n }\n return [treeScalarPreview(value, maxLineChars)];\n }\n const out: string[] = [];\n if (Array.isArray(value)) {\n const total = value.length;\n const limit = Math.min(maxChildren, total);\n for (let index = 0; index < limit; index += 1) {\n const isLast = !moreAfter && index === limit - 1 && total <= limit;\n treeAppendNode(\n value[index] ?? null,\n \"[\" + index + \"] \",\n \"\",\n isLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n 0,\n out,\n );\n }\n if (total > limit) {\n out.push((moreAfter ? \"├─ \" : \"└─ \") + \"…+\" + (total - limit) + \" more items\");\n }\n return out;\n }\n const keys = Object.keys(value);\n const total = keys.length;\n const limit = Math.min(maxChildren, total);\n for (let index = 0; index < limit; index += 1) {\n const isLast = !moreAfter && index === limit - 1 && total <= limit;\n const key = keys[index] as string;\n const childValue = (value as Record<string, PtcJsonValue>)[key] ?? null;\n treeAppendNode(\n childValue,\n treeFormatKey(key) + \": \",\n \"\",\n isLast,\n maxDepth,\n maxChildren,\n maxLineChars,\n 0,\n out,\n );\n }\n if (total > limit) {\n out.push((moreAfter ? \"├─ \" : \"└─ \") + \"…+\" + (total - limit) + \" more keys\");\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Public API: renderCall / renderResult\n// ---------------------------------------------------------------------------\n\n/** The tool-call arguments the renderer reads (a subset of both tools' parameter objects). */\nexport interface PtcRenderArgs {\n description?: string;\n code?: string;\n script?: string;\n meta?: { name?: string };\n}\n\n/**\n * One-line tool-call header: the tree root, then the tool label, then the model's own description.\n *\n * The description is the only detail that gets to sit next to the label — the previous renderer\n * appended the first code line as well, which is what made the call row read as a paragraph rather\n * than a heading. The code is one `ctrl+e` away in the expanded view.\n */\nexport function renderPtcToolCall(\n args: PtcRenderArgs,\n theme: Theme,\n surface: PtcToolDetails[\"surface\"] = \"run_code\",\n): PtcRow {\n const description =\n (typeof args.description === \"string\" && args.description.trim().length > 0\n ? args.description.trim()\n : args.meta?.name) ?? \"\";\n const fallback = firstMeaningfulCodeLine(args.code ?? args.script ?? \"\");\n const detail = description.length > 0 ? description : (fallback ?? \"(no description)\");\n\n const left = `${theme.fg(\"toolTitle\", theme.bold(TOOL_TITLE[surface]))} ${theme.fg(\"accent\", detail)}`;\n return new PtcRow([{ left: `${TREE_ROOT}${left}` }]);\n}\n\n/** The result shape both tools hand to `renderResult` (a structural subset of `AgentToolResult`). */\nexport interface PtcRenderResult {\n readonly content?: ReadonlyArray<{ type: string; text?: string }>;\n readonly details?: PtcToolDetails;\n}\n\n/** Text body of a failed run — pi surfaces the thrown error in the first text block. */\nfunction errorText(result: PtcRenderResult): string {\n const text = result.content?.find(\n (block) => block.type === \"text\" && (block.text ?? \"\").length > 0,\n );\n return firstLine(text?.text ?? \"\") || \"failed\";\n}\n\n/**\n * The result area under the call row.\n *\n * One of four shapes, all hanging at column 3 so a column of PTC rows stays scannable:\n * - error: one line with the failure reason in the error colour.\n * - no completion value: one line, \"done\".\n * - scalar / empty container: one line, `→ <hint>` (or \"done\" for null).\n * - non-empty container: a tree of rows (recursive `├─/└─/│` connectors from\n * `renderValueTree`), with the right-aligned meta pinned to\n * the tree's first row. ADR-0013 §5.\n *\n * Each row returns its left-side text WITHOUT the `TREE_ROOT_CONT` prefix being in scope;\n * we prepend it here so the same output works for both surfaces.\n */\nfunction resultArea(\n result: PtcRenderResult,\n isError: boolean,\n theme: Theme,\n moreAfter = false,\n): RowLine[] {\n const right = metaText(result.details, theme);\n const attachRight = (row: RowLine): RowLine => (right === undefined ? row : { ...row, right });\n\n if (isError) {\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"error\", `failed: ${errorText(result)}`)}`,\n }),\n ];\n }\n\n const value = result.details?.result;\n if (value === undefined) {\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"muted\", \"done\")}`,\n }),\n ];\n }\n\n if (isExpandableContainer(value)) {\n const tree = renderValueTree(value, {\n maxDepth: TREE_VALUE_MAX_DEPTH,\n maxChildren: TREE_VALUE_MAX_CHILDREN,\n maxLineChars: TREE_VALUE_MAX_LINE_CHARS,\n moreAfter,\n });\n return tree.map((line, index) => {\n const row: RowLine = { left: `${TREE_ROOT_CONT}${line}` };\n return index === 0 && right !== undefined ? { ...row, right } : row;\n });\n }\n\n // Scalar / empty / null — one-line `→ <hint>` (or \"done\" when no hint is producible).\n const hint = resultHint(value);\n if (hint === undefined) {\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"muted\", \"done\")}`,\n }),\n ];\n }\n return [\n attachRight({\n left: `${TREE_ROOT_CONT}${theme.fg(\"dim\", `→ ${hint}`)}`,\n }),\n ];\n}\n\n/**\n * Collapsed result: the result area only.\n *\n * A scalar row fits on one line; a container value expands into a tree (1+ lines). Right-aligned\n * meta (output lines / phases / warnings / duration) is pinned to the first row of the area.\n */\nexport function renderPtcToolResultCollapsed(\n result: PtcRenderResult,\n isError: boolean,\n theme: Theme,\n): PtcRow {\n return new PtcRow(resultArea(result, isError, theme));\n}\n\n/**\n * Expanded view: the result area, then the run's children as labelled blocks.\n *\n * Each child block opens with a fixed-width gutter (`code`, `phases`, `log`, `out`, `warn`,\n * `image`); the `value` block is gone — for a scalar value the result area already shows the\n * hint, and for a container value the area shows the full tree (ADR-0013 §5). Children that\n * overflow cap themselves and report `+N more lines`; the view stays bounded.\n */\nexport function renderPtcToolResultExpanded(\n result: PtcRenderResult,\n args: PtcRenderArgs,\n isError: boolean,\n theme: Theme,\n): PtcRow {\n if (isError) {\n const lines: RowLine[] = resultArea(result, true, theme);\n // The failure text is the reason the reader expanded the row at all: give it the full width\n // under the summary, no tree connectors (it is the error message, not a structured block).\n const text = sanitizeText(\n result.content?.find((block) => block.type === \"text\")?.text ?? errorText(result),\n );\n for (const line of text.split(\"\\n\").slice(0, MAX_LOG_LINES_EXPANDED)) {\n lines.push({ left: `${TREE_ROOT_CONT}${theme.fg(\"error\", line)}` });\n }\n return new PtcRow(lines);\n }\n\n const details = result.details;\n const children: ChildBlock[] = [];\n\n const code = args.code ?? args.script ?? \"\";\n if (code.length > 0) {\n const codeLines = code.split(\"\\n\").map((line) => truncateChars(line, MAX_CODE_LINE_CHARS));\n const shown = codeLines.slice(0, MAX_CODE_LINES_EXPANDED);\n children.push({\n label: \"code\",\n lines: shown,\n ...(codeLines.length > shown.length ? { more: codeLines.length - shown.length } : {}),\n });\n }\n\n if (details !== undefined && details.surface === \"workflow\" && details.phases.length > 0) {\n const shown = details.phases.slice(0, MAX_PHASES_EXPANDED);\n const tail = details.phases.length > shown.length ? \" …\" : \"\";\n children.push({ label: \"phases\", lines: [`${shown.join(\" → \")}${tail}`] });\n }\n\n if (details !== undefined && details.narrations.length > 0) {\n const shown = details.narrations.slice(0, MAX_LOG_LINES_EXPANDED);\n children.push({\n label: \"log\",\n lines: shown.map((message) => sanitizeText(message)),\n ...(details.narrations.length > shown.length\n ? { more: details.narrations.length - shown.length }\n : {}),\n });\n }\n\n if (details !== undefined && details.logs.length > 0) {\n const shown = details.logs.slice(0, MAX_LOG_LINES_EXPANDED);\n children.push({\n label: \"out\",\n lines: shown.map((line) => sanitizeText(line)),\n ...(details.logs.length > shown.length ? { more: details.logs.length - shown.length } : {}),\n });\n }\n\n if (details !== undefined && details.warnings.length > 0) {\n const shown = details.warnings.slice(0, MAX_WARNINGS_EXPANDED);\n children.push({\n label: \"warn\",\n lines: shown.map((warning) => sanitizeText(warning)),\n ...(details.warnings.length > shown.length\n ? { more: details.warnings.length - shown.length }\n : {}),\n });\n }\n\n if (details !== undefined && details.imageCount > 0) {\n const desc =\n details.imageCount === 1 ? \"1 image attached\" : `${details.imageCount} images attached`;\n children.push({ label: \"image\", lines: [desc] });\n }\n\n // Result rows first, then the labelled blocks. `moreAfter` keeps the connector chain open\n // while blocks follow, so the value tree and the blocks read as one list of siblings.\n const lines: RowLine[] = resultArea(result, false, theme, children.length > 0);\n\n for (let index = 0; index < children.length; index += 1) {\n const isLast = index === children.length - 1;\n lines.push(...renderChildBlock(children[index] ?? { label: \"\", lines: [] }, isLast, theme));\n }\n\n return new PtcRow(lines);\n}\n","/**\n * `ptc_run_code` — the unstructured PTC surface.\n *\n * The model writes the body of an async function; inside it, pi's built-in tools are reachable as\n * `tools.<name>(args)` (all seven: read, bash, edit, write, grep, find, ls — ADR-0005's set), and\n * the program's return value plus `console.log` output are what come back. There are no helpers\n * on this surface: `log` / `phase` / `parallel` / `pipeline` belong to `ptc_workflow` (G1 #13 →\n * decision B; a call to a helper that does not exist is a plain `ReferenceError` through the\n * normal code-run error path).\n *\n * Scope notes, so this stays a faithful subset of DSH's `run_code`:\n * - DSH's `sandbox_permissions` / `justification` are deliberately absent — they exist to request\n * a wider sandbox from an approval pipeline this package does not have (ADR-0007 ships no OS\n * sandbox, and the map decision on #8 dropped the approval path).\n * - `timeoutMs: 0` does not disable the deadline; it falls back to `DEFAULT_CONFIG.timeoutMs`,\n * exactly as DSH's resolver does (R1 §3). The description names the default and the ceiling.\n */\nimport { defineTool } from \"@earendil-works/pi-coding-agent\";\nimport type { ToolDefinition } from \"@earendil-works/pi-coding-agent\";\nimport { Type } from \"typebox\";\nimport type { TNumber, TOptional, TObject, TString } from \"typebox\";\nimport { createBuiltinBindings } from \"../runtime/bindings.ts\";\nimport { runPtcProgram } from \"../runtime/dispatcher.ts\";\nimport type { WorkerPool } from \"../runtime/worker-pool.ts\";\nimport { DEFAULT_CONFIG } from \"../runtime/limits.ts\";\nimport {\n codeRunFailedError,\n PTC_RUN_CODE_SNIPPET,\n PTC_TOOL_GUIDELINES,\n renderToolResult,\n resolveBindingNames,\n resolveToolCwd,\n} from \"./common.ts\";\nimport type { PtcToolDetails, PtcToolOptions } from \"./common.ts\";\nimport {\n renderPtcToolCall,\n renderPtcToolResultCollapsed,\n renderPtcToolResultExpanded,\n} from \"./render.ts\";\n\nconst DESCRIPTION = [\n \"Run a TypeScript program that composes pi's tools in one shot. Required arguments: `code` —\",\n \"the body of an async function (top-level `return` and `await` work; type annotations are\",\n \"advisory, the code runs type-stripped) — and `description`, a 5-10 word summary of what the\",\n \"program does.\",\n \"\",\n \"Inside the program, call this session's enabled built-in tools as `tools.<name>(args)` — e.g.\",\n '`await tools.read({ path: \"src/index.ts\" })` or `await tools.bash({ command: \"npm test\" })`.',\n \"The bound names mirror the session's active tools (a default session has `read`, `bash`, `edit`,\",\n \"`write`); calling a name that is not bound rejects with an error the program can catch, and\",\n \"independent calls may overlap under `Promise.all`.\",\n \"\",\n \"Only the program's return value and its `console.log` output come back. This surface has no\",\n \"helpers: `log` / `phase` / `parallel` / `pipeline` exist only in `ptc_workflow`.\",\n \"\",\n \"Image-bearing tool results inside the program (a `tools.read` on a PNG, say) are attached to you\",\n \"after the run, so never return image data as the completion value — that only spends your context\",\n \"on base64.\",\n].join(\"\\n\");\n\n/** Schema type written out explicitly for `isolatedDeclarations` (emit must not infer it). */\ntype RunCodeParameters = TObject<{\n code: TString;\n description: TString;\n timeoutMs: TOptional<TNumber>;\n}>;\n\nconst PARAMETERS: RunCodeParameters = Type.Object({\n code: Type.String({\n description: \"The program: the body of an async TypeScript function.\",\n }),\n description: Type.String({\n description:\n \"Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI).\",\n }),\n timeoutMs: Type.Optional(\n Type.Number({\n description:\n `Elapsed deadline in milliseconds for the whole run, including tool calls. ` +\n `Omit it (or pass 0) for the default of ${DEFAULT_CONFIG.timeoutMs / 1000} s; ` +\n `anything above ${DEFAULT_CONFIG.maxTimeoutMs / 1000} s is capped.`,\n }),\n ),\n});\n\n/**\n * `PtcToolOptions` plus this tool's per-turn pool seam (ADR-0017 §1).\n *\n * The extension owns one `TurnPools` per agent turn and passes a getter for the\n * `run_code` surface's pool. The getter is read **per execute**, not at registration,\n * because the pool is replaced at every turn boundary. Omitting it means one fresh\n * worker per run — the pre-pool behaviour, and what direct library use gets.\n *\n * With a pool, consecutive runs of the same turn **share one warm worker**: that is\n * what pooling buys, and it is why \"every run gets a fresh worker\" is no longer true\n * on this path. What that sharing does and does not guarantee is worth being exact\n * about. `acquire()` guarantees *ownership*: a worker is in flight to exactly one run\n * until that run releases it, and a second acquire at capacity queues behind the wait\n * list rather than reusing an occupied worker (ADR-0017 §4). It does **not** guarantee\n * that only one program is physically executing in the isolate — the host abandons a\n * run by closing its control port and releasing the worker without waiting for the\n * program to unwind, so a **superseded** run's program can still be finishing inside\n * the worker while the next run is already in flight (ADR-0017 §10(a) / §10(h)). What\n * the pool promises is \"one run at a time owns a worker's control port and result\n * channel\", not \"one program at a time\".\n *\n * Isolation of the shared realm therefore does not come from the ending run's reset\n * handshake — a superseded run's `reset()` is a generation-guarded no-op (ADR-0017 §5 /\n * §10(a)). It comes from the next run's init-side clearing, which unconditionally\n * reinstalls the frozen env and console, restores `globalThis` to the boot-time warm\n * baseline and resets the run bookkeeping before the program starts (ADR-0017 §10(d)).\n */\nexport interface PtcRunCodeToolOptions extends PtcToolOptions {\n getPool?: () => WorkerPool | undefined;\n}\n\n/**\n * Build the `ptc_run_code` tool definition.\n *\n * Called once by the extension factory. Each execute builds its own binding table against the\n * run's own cwd and reads `getPool()` for the surface's (possibly absent) pool — see\n * `PtcRunCodeToolOptions` for what pooling does and does not change about isolation.\n */\nexport function createPtcRunCodeTool(\n options: PtcRunCodeToolOptions = {},\n): ToolDefinition<RunCodeParameters, PtcToolDetails> {\n return defineTool({\n name: \"ptc_run_code\",\n label: \"PTC Run Code\",\n description: DESCRIPTION,\n promptSnippet: PTC_RUN_CODE_SNIPPET,\n promptGuidelines: [...PTC_TOOL_GUIDELINES],\n parameters: PARAMETERS,\n async execute(_toolCallId, params, signal, _onUpdate, ctx) {\n const cwd = resolveToolCwd(ctx);\n const names = resolveBindingNames(options.getBindingSourceNames?.());\n const startedAt = Date.now();\n const pool = options.getPool?.();\n const outcome = await runPtcProgram({\n code: params.code,\n surface: \"run_code\",\n cwd,\n // The shipped surface always exposes the parallel binding (ADR-0016); the\n // binding-source names only curate the built-in subset.\n bindings: createBuiltinBindings({ cwd, names, includeDispatch: true }),\n ...(options.depth === undefined ? {} : { depth: options.depth }),\n ...(params.timeoutMs === undefined ? {} : { timeoutMs: params.timeoutMs }),\n ...(signal === undefined ? {} : { signal }),\n ...(options.config === undefined ? {} : { config: options.config }),\n ...(pool === undefined ? {} : { pool }),\n });\n if (outcome.error !== undefined) throw codeRunFailedError(outcome);\n return renderToolResult({ outcome, surface: \"run_code\", durationMs: Date.now() - startedAt });\n },\n\n // Compact TUI rendering — see `render.ts` and ADR-0013. `renderShell` stays at the default\n // `ToolExecutionComponent` shell so PTC rows match the visual rhythm of `read`/`bash`.\n renderCall(args, theme) {\n return renderPtcToolCall(args, theme, \"run_code\");\n },\n renderResult(result, options, theme, context) {\n if (options.expanded) {\n return renderPtcToolResultExpanded(result, context.args, context.isError, theme);\n }\n return renderPtcToolResultCollapsed(result, context.isError, theme);\n },\n });\n}\n","/**\n * `ptc_workflow` — the structured PTC surface.\n *\n * Same worker program, same bindings, same Node surface as `ptc_run_code`; what it adds is a plan\n * (`meta` with an ordered phase list), plain-JSON input (`args`, bound as the program's `args`\n * global) and the four workflow helpers: `log` / `phase` / `parallel` / `pipeline` (G1 #13 →\n * decision B — there is no `agent()`, not even a stub).\n *\n * Three decisions recorded here rather than re-litigated per call:\n * - **Language**: the same type-stripping pipeline as `ptc_run_code`. The worker compiles both\n * surfaces through one `compileProgram`, and the pipeline is type-*strip* only — it adds no\n * runtime semantics that a workflow could not tolerate. There is no JS-only mode because there\n * is no concrete reason for one (the old pi-ptc \"JS-only\" idea is explicitly not inherited).\n * - **Phase strictness**: warning, not error. A `phase(title)` outside `meta.phases` is a plan\n * drift — usually a typo or a forgotten declaration — but the run has already happened by the\n * time the titles are known, and failing then would discard a completed result over a cosmetic\n * mismatch. Each distinct unlisted title is reported once, as a `Warning:` line in `content`\n * and in `details.warnings`. When `meta.phases` is absent there is no declared plan to drift\n * from, so nothing is reported.\n * - **`args`**: validated as plain JSON before dispatch (see `validateWorkflowArgs`), because a\n * function/symbol/`undefined`/cycle would otherwise be silently dropped or mangled by the\n * structured clone on its way to the worker.\n */\nimport { defineTool } from \"@earendil-works/pi-coding-agent\";\nimport type { ToolDefinition } from \"@earendil-works/pi-coding-agent\";\nimport { Type } from \"typebox\";\nimport type { Static, TArray, TObject, TOptional, TRecord, TString, TUnknown } from \"typebox\";\nimport { createBuiltinBindings } from \"../runtime/bindings.ts\";\nimport { runPtcProgram } from \"../runtime/dispatcher.ts\";\nimport type { PtcRunOutcome } from \"../runtime/dispatcher.ts\";\nimport { describeValue } from \"../runtime/protocol.ts\";\nimport type { WorkerPool } from \"../runtime/worker-pool.ts\";\nimport {\n codeRunFailedError,\n PTC_TOOL_GUIDELINES,\n PTC_WORKFLOW_SNIPPET,\n renderToolResult,\n resolveBindingNames,\n resolveToolCwd,\n} from \"./common.ts\";\nimport type { PtcToolDetails, PtcToolOptions } from \"./common.ts\";\nimport {\n renderPtcToolCall,\n renderPtcToolResultCollapsed,\n renderPtcToolResultExpanded,\n} from \"./render.ts\";\n\nconst DESCRIPTION = [\n \"Run a structured TypeScript workflow: a named plan that reports phases and narration as it\",\n \"goes. `script` is the body of an async function, `meta` names it (`{ name, description, phases? }`,\",\n \"where `phases` is the ordered plan), and optional `args` is plain-JSON input bound to the\",\n \"program's `args` global.\",\n \"\",\n \"Helpers inside the script: `log(message)` narrates progress; `phase(title)` frames the workflow\",\n \"(titles outside `meta.phases` are reported as warnings); `parallel(thunks)` runs thunks\",\n \"concurrently, a failed item becoming `null`; `pipeline(items, ...stages)` chains stages per item\",\n \"with the same per-item `null` on failure. There is no `agent()` helper.\",\n \"\",\n \"Tools are reachable as `tools.<name>(args)` exactly as in `ptc_run_code`, and the bound names\",\n \"mirror the session's enabled tools. What comes back is the script's return value, its\",\n \"`log`/`phase` narration and its `console.log` output.\",\n \"\",\n \"Image-bearing tool results inside the script are attached to you after the run — never return\",\n \"image data as the script's value.\",\n].join(\"\\n\");\n\n/** Schema types written out explicitly for `isolatedDeclarations` (emit must not infer them). */\ntype WorkflowPhaseSchema = TObject<{ name: TString }>;\n\ntype WorkflowParameters = TObject<{\n meta: TObject<{\n name: TString;\n description: TString;\n phases: TOptional<TArray<WorkflowPhaseSchema>>;\n }>;\n script: TString;\n args: TOptional<TRecord<string, TUnknown>>;\n}>;\n\nconst PARAMETERS: WorkflowParameters = Type.Object({\n meta: Type.Object(\n {\n name: Type.String({ description: \"Short workflow name, shown in the UI.\" }),\n description: Type.String({ description: \"What this workflow does, in active voice.\" }),\n phases: Type.Optional(\n Type.Array(\n Type.Object({\n name: Type.String({ description: \"Phase title, matched against phase(title) calls.\" }),\n }),\n {\n description:\n \"Ordered plan of the workflow's phases. phase() titles outside this list are reported as warnings.\",\n },\n ),\n ),\n },\n { description: \"Workflow metadata: what this run is and which phases it declares.\" },\n ),\n script: Type.String({ description: \"The program: the body of an async TypeScript function.\" }),\n args: Type.Optional(\n Type.Record(Type.String(), Type.Unknown(), {\n description:\n \"Plain-JSON input bound to the program's `args` global. No functions, symbols, undefined values or cycles.\",\n }),\n ),\n});\n\n/** Parameters after pi's schema validation; `args` is still unvalidated plain JSON to be checked. */\nexport type PtcWorkflowParams = Static<typeof PARAMETERS>;\n\n/**\n * Reject anything that is not plain JSON, with a path-qualified reason.\n *\n * The rule is the worker's own result rule (`toJsonValue`) applied one direction earlier, so both\n * ends agree on what \"plain JSON\" means: objects with `Object.prototype` (or null), arrays,\n * strings, finite numbers, booleans and null. Class instances, functions, symbols, bigints,\n * non-finite numbers, circular references and `undefined` values are all rejected — `undefined`\n * because the ticket calls it out and because a structured clone would drop it silently, leaving\n * the script reading a key that is not there.\n */\nexport function validateWorkflowArgs(args: unknown): void {\n if (args === undefined) return;\n if (typeof args !== \"object\" || args === null || Array.isArray(args)) {\n throw new TypeError(\n `ptc_workflow args must be a plain JSON object, received ${describeValue(args)}`,\n );\n }\n for (const key of Object.keys(args)) {\n const problem = findJsonProblem(\n (args as Record<string, unknown>)[key],\n `args.${key}`,\n new Set(),\n );\n if (problem !== undefined)\n throw new TypeError(`ptc_workflow args must be plain JSON: ${problem}`);\n }\n}\n\n/** Path-qualified reason a value cannot cross the wire as plain JSON, or `undefined` when it can. */\nfunction findJsonProblem(value: unknown, path: string, seen: Set<object>): string | undefined {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return undefined;\n if (typeof value === \"undefined\") return `${path} is undefined`;\n if (typeof value === \"number\") {\n return Number.isFinite(value)\n ? undefined\n : `${path} is ${String(value)}, which is not representable as JSON`;\n }\n if (typeof value !== \"object\") return `${path} is ${describeValue(value)}`;\n\n const container = value as object;\n if (seen.has(container)) return `${path} is a circular reference`;\n seen.add(container);\n try {\n if (Array.isArray(container)) {\n for (let index = 0; index < container.length; index += 1) {\n const problem = findJsonProblem(container[index], `${path}[${index}]`, seen);\n if (problem !== undefined) return problem;\n }\n return undefined;\n }\n const prototype = Object.getPrototypeOf(container);\n if (prototype !== Object.prototype && prototype !== null) {\n const name = (container as { constructor?: { name?: string } }).constructor?.name;\n return `${path} is a ${typeof name === \"string\" && name.length > 0 ? name : \"non-plain object\"}, not plain JSON`;\n }\n for (const key of Object.keys(container)) {\n const problem = findJsonProblem(\n (container as Record<string, unknown>)[key],\n `${path}.${key}`,\n seen,\n );\n if (problem !== undefined) return problem;\n }\n return undefined;\n } finally {\n seen.delete(container);\n }\n}\n\n/**\n * Collect the plan-drift warnings for one workflow run: distinct `phase()` titles the declared\n * `meta.phases` list does not mention, in first-seen order.\n */\nexport function unlistedPhaseWarnings(\n outcome: PtcRunOutcome,\n declared: readonly string[] | undefined,\n): string[] {\n if (declared === undefined || declared.length === 0) return [];\n const known = new Set(declared);\n const reported = new Set<string>();\n const warnings: string[] = [];\n for (const title of outcome.phases) {\n if (known.has(title) || reported.has(title)) continue;\n reported.add(title);\n warnings.push(\n `phase \"${title}\" is not listed in meta.phases (declared: ${[...known].join(\", \")})`,\n );\n }\n return warnings;\n}\n\n/**\n * `PtcToolOptions` plus this tool's per-turn pool seam (ADR-0017 §1). See\n * `createPtcRunCodeTool`'s options for why the getter is read per execute and what\n * omitting it means.\n *\n * The pooling story is the same as `ptc_run_code`'s, on this surface's own pool (ADR-0017\n * §2): consecutive runs of one turn share a warm worker, and isolation between them comes\n * from the **next** run's init-side cleanup (ADR-0017 §10(d)) — not from the previous run's\n * reset handshake, which is a no-op for a superseded run (ADR-0017 §10(a)). `acquire()`\n * guarantees one run holds the worker at a time (ADR-0017 §4), though a superseded run's\n * program may still be unwinding inside it. The worker program differs — it has the\n * workflow helpers installed — which is why the two surfaces do not share a pool.\n */\nexport interface PtcWorkflowToolOptions extends PtcToolOptions {\n getPool?: () => WorkerPool | undefined;\n}\n\n/**\n * Build the `ptc_workflow` tool definition.\n *\n * `args` is validated before `runPtcProgram` is called, so a malformed payload never acquires or\n * spawns a worker. Everything after dispatch is identical to `ptc_run_code`, plus the phase\n * roll-up.\n */\nexport function createPtcWorkflowTool(\n options: PtcWorkflowToolOptions = {},\n): ToolDefinition<WorkflowParameters, PtcToolDetails> {\n return defineTool({\n name: \"ptc_workflow\",\n label: \"PTC Workflow\",\n description: DESCRIPTION,\n promptSnippet: PTC_WORKFLOW_SNIPPET,\n promptGuidelines: [...PTC_TOOL_GUIDELINES],\n parameters: PARAMETERS,\n async execute(_toolCallId, params, signal, _onUpdate, ctx) {\n validateWorkflowArgs(params.args);\n const cwd = resolveToolCwd(ctx);\n const names = resolveBindingNames(options.getBindingSourceNames?.());\n const startedAt = Date.now();\n const pool = options.getPool?.();\n const outcome = await runPtcProgram({\n code: params.script,\n surface: \"workflow\",\n cwd,\n // Same surface contract as ptc_run_code: pi.dispatch is always bound (ADR-0016).\n bindings: createBuiltinBindings({ cwd, names, includeDispatch: true }),\n ...(params.args === undefined ? {} : { args: params.args }),\n ...(options.depth === undefined ? {} : { depth: options.depth }),\n ...(signal === undefined ? {} : { signal }),\n ...(options.config === undefined ? {} : { config: options.config }),\n ...(pool === undefined ? {} : { pool }),\n });\n if (outcome.error !== undefined) throw codeRunFailedError(outcome);\n const declared = params.meta.phases?.map((phase) => phase.name);\n return renderToolResult({\n outcome,\n surface: \"workflow\",\n warnings: unlistedPhaseWarnings(outcome, declared),\n durationMs: Date.now() - startedAt,\n });\n },\n\n // Same renderer as `ptc_run_code`; the `workflow` label tells the two rows apart, the collapsed\n // meta adds a phase count, and the expanded view puts the phases roll-up first so a reader sees\n // the workflow plan up top.\n renderCall(args, theme) {\n return renderPtcToolCall(args, theme, \"workflow\");\n },\n renderResult(result, options, theme, context) {\n if (options.expanded) {\n return renderPtcToolResultExpanded(result, context.args, context.isError, theme);\n }\n return renderPtcToolResultCollapsed(result, context.isError, theme);\n },\n });\n}\n","/**\n * Per-turn pool holder: one `WorkerPool` per PTC surface, created lazily on first use\n * and retired together at turn end (ADR-0017 §1–§2).\n *\n * The pi extension owns one `TurnPools` per agent turn and hands each tool a getter for\n * its own surface's pool. Lazy creation is what makes the cold turn cheap: a turn that\n * never runs a PTC program never spawns a worker, and a turn that runs one `ptc_run_code`\n * pays exactly one cold spawn (which the pool then keeps warm for the rest of the turn).\n *\n * `run_code` and `workflow` do not share a pool (ADR-0017 §2): their worker surfaces differ\n * (`installWorkflowHelpers` is conditional in `worker-main.ts`), and one pool per surface\n * keeps that story simple. The workers themselves carry no surface state — each run's\n * `init` frame names its surface — so the split is about ownership, not correctness.\n */\nimport { createWorkerEnv, resolveConfig } from \"./limits.ts\";\nimport type { PtcConfig, PtcSurface } from \"./limits.ts\";\nimport { WorkerPool, workerSpawnOptions } from \"./worker-pool.ts\";\nimport { buildWorkerUrl } from \"./worker-source.ts\";\n\nexport interface TurnPoolsOptions {\n /** Per-turn limit overrides on top of `DEFAULT_CONFIG` (pool size, acquire bound, V8 caps). */\n config?: Partial<PtcConfig>;\n}\n\nexport class TurnPools {\n readonly #config: PtcConfig;\n readonly #pools = new Map<PtcSurface, WorkerPool>();\n\n constructor(options: TurnPoolsOptions = {}) {\n this.#config = resolveConfig(options.config);\n }\n\n /**\n * The pool for one surface, created on first request. Warm workers from an earlier\n * run in this turn are served by the same pool (that is the point); a fresh\n * `TurnPools` is how a turn gets a fresh set.\n */\n get(surface: PtcSurface): WorkerPool {\n const existing = this.#pools.get(surface);\n if (existing !== undefined) return existing;\n\n const env = createWorkerEnv();\n const pool = new WorkerPool({\n buildWorkerUrl,\n size: this.#config.poolSize,\n acquireTimeoutMs: this.#config.poolAcquireTimeoutMs,\n // Pool retirement is part of the turn contract too: the `turn_end` drain waits for\n // in-flight runs only as long as this holder was configured to (ADR-0017 §1 retires the\n // holder's pools; `drainGraceMs` is the bound it drains within).\n drainGraceMs: this.#config.drainGraceMs,\n // Spawn-time options — `env`, `workerData` and the V8 caps — are built here, once, by the\n // shared factory (`workerSpawnOptions`), and a warm worker keeps them for the rest of the\n // turn: a live worker's heap ceiling is not a runtime setting.\n //\n // So a per-run `PtcConfig` override is **inert for these fields on the pooled\n // path**: `runPtcProgram({ config: { maxOldGenerationSizeMb: X } })` cannot reseat\n // the caps of a worker that already exists, and the caps in force are this holder's,\n // resolved from the `TurnPools` constructor. The cold path (no `pool`) still honours\n // a per-run config, because `dispatcher.ts` spawns that worker per run. A caller\n // wanting different caps, env or `workerData` needs a differently-constructed\n // `TurnPools` — or no pool — not a different per-run config. `workerData` carries no\n // `runId` for the same reason: a warm worker outlives any one run.\n workerOptions: workerSpawnOptions({\n surface,\n env,\n limits: this.#config,\n }),\n });\n this.#pools.set(surface, pool);\n return pool;\n }\n\n /**\n * Terminate every pool this holder created. Idempotent, and safe to call from a turn\n * boundary hook: a second call finds nothing to drain.\n */\n async drain(): Promise<void> {\n const pools = [...this.#pools.values()];\n this.#pools.clear();\n await Promise.all(pools.map((pool) => pool.drain()));\n }\n}\n","/**\n * PTC default mode — the session-level tool-restriction state machine.\n *\n * ## What \"mode\" means here\n *\n * While enabled, the session's visible tool loadout is narrowed so the model reaches\n * `read` / `bash` / `edit` / `write` / `grep` / `find` / `ls` only from *inside* a program\n * (`tools.<name>(args)`). That is DSH's PTC-preset shape, and it is what makes the mode visible\n * in the TUI: the transcript stops showing file-operation rows and shows PTC rows instead.\n *\n * The mode is decided per session (`session_start`), announced once, and always reversible.\n *\n * ## Why bindings need a base snapshot (the trap this module exists to avoid)\n *\n * `resolveBindingNames()` computes bindings as `BUILTIN_BINDING_NAMES ∩ pi.getActiveTools()`\n * (T7, ADR-0005 addendum). But `pi.getActiveTools()` reads the *live* loadout — the very set this\n * mode narrows. Hiding the built-ins therefore empties the binding table, and every\n * `tools.read(...)` fails with \"not bound\": the extension would neuter itself.\n *\n * The fix is a **base snapshot**. `base` is captured *before* the mode hides anything, and\n * bindings derive from `base` rather than the live loadout while the mode is on. T7's invariant\n * survives, because `base` is itself read from `pi.getActiveTools()`: a session launched with\n * `--tools read` has `base = [\"read\"]`, so bindings stay `[\"read\"]`. The mode never widens access\n * beyond what the session was launched with — it only makes its **own** narrowing transparent to\n * the binding table.\n *\n * ## Fail-safe on external loadout changes\n *\n * Another extension can call `setActiveTools()` (pi's own `preset.ts` and `tools.ts` examples do).\n * `ourLoadout` records the exact array this mode wrote, so a mismatch means someone else owns the\n * loadout now. The mode then exits and restores `base` instead of fighting over it.\n *\n * ## Where the mode never runs\n *\n * Print / JSON / RPC sessions (`ctx.mode !== \"tui\"`) are left alone: forcing the shape on CI\n * scripts buys nothing and breaks them. A session that was *launched* with an explicit tool\n * restriction is left alone too — see `decideModeEntry`.\n */\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { BUILTIN_BINDING_NAMES } from \"../runtime/bindings.ts\";\n\n/** The two surfaces this package exposes. `/ptc on` needs at least one of them active. */\nexport const PTC_MODE_TOOL_NAMES: readonly string[] = [\"ptc_run_code\", \"ptc_workflow\"];\n\n/**\n * Tools that must all be present for the **automatic** entry path to proceed.\n *\n * These four are pi's default session surface (`agent-session.js`: \"[\"read\", \"bash\", \"edit\",\n * \"write\"]\"), so \"all four present\" means \"this session was not narrowed\". Comparing against\n * `BUILTIN_BINDING_NAMES` instead would be wrong: that list is the set of names that *can* be\n * bound, not what a default session has (`grep` / `find` / `ls` are bindable but off by default),\n * so requiring all seven would misread every ordinary session as restricted and never turn on.\n */\nexport const MODE_REQUIRED_TOOL_NAMES: readonly string[] = [\"read\", \"bash\", \"edit\", \"write\"];\n\n/** `customType` for the persisted mode entry (`pi.appendEntry`). */\nexport const PTC_MODE_ENTRY_TYPE = \"ptc-mode\";\n\n/** Footer status key (`ctx.ui.setStatus`). */\nexport const PTC_MODE_STATUS_KEY = \"ptc-mode\";\n\n/** Config file name, resolved against pi's agent dir (`~/.pi/agent/`). */\nexport const PTC_MODE_CONFIG_FILE = \"ptc.json\";\n\n/** How much of the loadout the mode hides. */\nexport type ModeHideStrategy = \"builtins-only\" | \"all-but-ptc\";\n\n/**\n * Which tools the mode hides while it is on.\n *\n * - `\"builtins-only\"` (default) — hide the seven built-in tools; tools contributed by *other*\n * extensions (`web_search`, `todo`, `subagent`, …) stay visible and directly callable. The\n * model is forced to program for file and shell work, which is the bulk of a coding session,\n * without losing capabilities this package cannot re-expose: extension tools have no bindings\n * (`pi.getAllTools()` returns metadata only — no `execute`), so hiding one makes it\n * unreachable for the whole session.\n * - `\"all-but-ptc\"` — hide everything except the two PTC surfaces. Closest to a strict reading of\n * DSH's preset, at the cost of making other extensions' tools unreachable until `/ptc off`.\n */\nexport const DEFAULT_HIDE_STRATEGY: ModeHideStrategy = \"builtins-only\";\n\n/** Live mode state. `base` / `ourLoadout` are `undefined` while the mode is off. */\nexport interface PtcModeState {\n enabled: boolean;\n /** Loadout captured before the mode narrowed anything; bindings derive from this. */\n base: readonly string[] | undefined;\n /** Exactly the array handed to `setActiveTools`; a mismatch means someone else changed it. */\n ourLoadout: readonly string[] | undefined;\n}\n\n/** Fresh, disabled state. */\nexport function initialModeState(): PtcModeState {\n return { enabled: false, base: undefined, ourLoadout: undefined };\n}\n\n/**\n * A persisted mode record lives in the session (`pi.appendEntry`), so `/ptc off` survives a\n * resume and a `/reload` can put the loadout back the way it found it.\n */\nexport interface PersistedModeState {\n enabled: boolean;\n base?: readonly string[];\n}\n\n// --------------------------------------------------------------------------------------\n// Config: ~/.pi/agent/ptc.json → { \"defaultMode\": false }\n// --------------------------------------------------------------------------------------\n\n/** Result of reading the opt-out config, with enough detail to warn about a broken file. */\nexport interface DefaultModeConfig {\n defaultMode: boolean;\n source: \"file\" | \"default\" | \"invalid\";\n error?: string;\n}\n\n/**\n * Read `defaultMode` from the agent-dir config file.\n *\n * Absent file → on (this package is default-on by design; see CONTEXT.md). A present file with a\n * non-boolean `defaultMode`, or unparseable JSON, is reported as `invalid` **and still defaults to\n * on** — the caller should surface the problem rather than silently changing behavior.\n */\nexport function readDefaultModeConfig(agentDir: string): DefaultModeConfig {\n const path = join(agentDir, PTC_MODE_CONFIG_FILE);\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return { defaultMode: true, source: \"default\" };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n return {\n defaultMode: true,\n source: \"invalid\",\n error: `${path} is not valid JSON (${error instanceof Error ? error.message : String(error)})`,\n };\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return { defaultMode: true, source: \"invalid\", error: `${path} must contain a JSON object` };\n }\n const value = (parsed as { defaultMode?: unknown }).defaultMode;\n if (value === undefined) {\n return { defaultMode: true, source: \"default\" };\n }\n if (typeof value !== \"boolean\") {\n return {\n defaultMode: true,\n source: \"invalid\",\n error: `${path}: \"defaultMode\" must be a boolean, received ${typeof value}`,\n };\n }\n return { defaultMode: value, source: \"file\" };\n}\n\n// --------------------------------------------------------------------------------------\n// Entry decision\n// --------------------------------------------------------------------------------------\n\n/** Why the mode declined to turn on. Surfaced in the entry notification / debug logs. */\nexport type ModeBlockReason = \"not-tui\" | \"config-off\" | \"tools-unavailable\" | \"restricted-session\";\n\n/** Either the loadout to apply, or the reason the mode stayed off. */\nexport type ModeEntryDecision =\n | { enter: true; base: readonly string[]; loadout: readonly string[] }\n | { enter: false; reason: ModeBlockReason };\n\n/** Inputs to the entry decision. `active` is `pi.getActiveTools()` at decision time. */\nexport interface ModeEntryInput {\n /** `ctx.mode` — only `\"tui\"` participates. */\n mode: string;\n /** Resolved `defaultMode` from the config file. */\n defaultMode: boolean;\n /** The session's current tool loadout. */\n active: readonly string[];\n /** True for `/ptc on`, which overrides the config and the restricted-session policy. */\n manual: boolean;\n /** Overrides `DEFAULT_HIDE_STRATEGY` (tests, or a future settings surface). */\n hide?: ModeHideStrategy | undefined;\n}\n\n/** Normalize to a de-duplicated name list, preserving order. */\nfunction uniqueNames(names: readonly string[]): string[] {\n return [...new Set(names)];\n}\n\n/** Order- and duplicate-insensitive set equality, for loadout comparison. */\nexport function sameToolSet(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false;\n const left = [...a].sort();\n const right = [...b].sort();\n return left.every((name, index) => name === right[index]);\n}\n\n/**\n * The loadout applied while the mode is on, given the session's current one.\n *\n * Always keeps the PTC surfaces; what else survives depends on the hide strategy (see\n * `DEFAULT_HIDE_STRATEGY`). Order follows `active`, so the visible list reads the way the session\n * was configured.\n */\nexport function modeLoadout(active: readonly string[], hide: ModeHideStrategy): string[] {\n const isPtc = (name: string): boolean => PTC_MODE_TOOL_NAMES.includes(name);\n if (hide === \"all-but-ptc\") return uniqueNames(active.filter(isPtc));\n const builtins: ReadonlySet<string> = new Set(BUILTIN_BINDING_NAMES);\n return uniqueNames(active.filter((name) => isPtc(name) || !builtins.has(name)));\n}\n\n/**\n * Decide whether the mode turns on for this session.\n *\n * The policy, in order:\n * 1. **TUI only** — print / JSON / RPC sessions are never touched.\n * 2. **Config** — `{\"defaultMode\": false}` opts out; `/ptc on` overrides it.\n * 3. **PTC tools must exist** — if this session has them disabled, there is nothing to run.\n * 4. **Explicit restriction wins** — a session launched with `--tools` / `--exclude-tools` /\n * `--no-builtin-tools` that is missing any of `MODE_REQUIRED_TOOL_NAMES` (pi's default four)\n * stays as launched. Narrowing *further* would be the extension overriding a deliberate user\n * instruction, and the PTC surface would be degraded anyway (bindings = `BUILTIN ∩ base`).\n * `/ptc on` overrides this: the manual path enters with the restricted loadout as `base`, so\n * bindings stay inside the user's allowlist.\n */\nexport function decideModeEntry(input: ModeEntryInput): ModeEntryDecision {\n const { mode, defaultMode, active, manual } = input;\n if (mode !== \"tui\") return { enter: false, reason: \"not-tui\" };\n if (!defaultMode && !manual) return { enter: false, reason: \"config-off\" };\n if (!PTC_MODE_TOOL_NAMES.some((name) => active.includes(name))) {\n return { enter: false, reason: \"tools-unavailable\" };\n }\n if (!manual) {\n const missing = MODE_REQUIRED_TOOL_NAMES.filter((name) => !active.includes(name));\n if (missing.length > 0) return { enter: false, reason: \"restricted-session\" };\n }\n return {\n enter: true,\n base: uniqueNames(active),\n loadout: modeLoadout(active, input.hide ?? DEFAULT_HIDE_STRATEGY),\n };\n}\n\n// --------------------------------------------------------------------------------------\n// Runtime helpers\n// --------------------------------------------------------------------------------------\n\n/**\n * Tool names the binding table should be built from.\n *\n * While the mode is on this is the base snapshot (so the built-ins the mode just hid remain\n * callable from inside a program); otherwise it is simply the live loadout, i.e. T7's rule.\n */\nexport function bindingSource(state: PtcModeState, active: readonly string[]): readonly string[] {\n return state.enabled && state.base !== undefined ? state.base : active;\n}\n\n/**\n * Whether the live loadout is no longer the one this mode wrote — i.e. another extension (or the\n * user) took ownership of the tool set. Always false while the mode is off.\n */\nexport function detectExternalLoadoutChange(\n state: PtcModeState,\n active: readonly string[],\n): boolean {\n if (!state.enabled || state.ourLoadout === undefined) return false;\n return !sameToolSet(state.ourLoadout, active);\n}\n\n/**\n * Reconstruct the base loadout on `session_start`.\n *\n * Two shapes are possible:\n * - The session is still narrowed from a previous run of this mode (a `/reload`, or a resumed\n * session): the live loadout is a subset of the PTC tools and the persisted record says the mode\n * was on. The persisted `base` is then authoritative — but only to *undo our own hiding*.\n * - Otherwise the live loadout is whatever this pi process was launched with, and it wins. That is\n * what keeps a new `--tools …` choice from being overridden by an older session's snapshot.\n */\nexport function resolveBaseOnStart(\n persisted: PersistedModeState | undefined,\n active: readonly string[],\n): { base: readonly string[]; restoreFirst: boolean } {\n const looksNarrowedByUs =\n active.length > 0 && active.every((name) => PTC_MODE_TOOL_NAMES.includes(name));\n if (persisted?.enabled === true && looksNarrowedByUs && persisted.base !== undefined) {\n return { base: persisted.base, restoreFirst: true };\n }\n return { base: active, restoreFirst: false };\n}\n\n// --------------------------------------------------------------------------------------\n// Instruction injected into the conversation once per mode entry\n// --------------------------------------------------------------------------------------\n\n/**\n * Build the system-visible briefing for the mode.\n *\n * Generated from the *actual* binding list rather than hardcoded, because bindings vary by session\n * (`base` is whatever the session was launched with) and a stale list would have the model calling\n * names that are not bound.\n */\nexport function buildModeInstruction(bindings: readonly string[], hide: ModeHideStrategy): string {\n const builtinNote =\n hide === \"all-but-ptc\"\n ? \"Every built-in tool is hidden from you, and so are other extensions' tools.\"\n : \"The built-in tools (read, bash, edit, write, grep, find, ls) are hidden from you.\";\n const callable = bindings.map((name) => `tools.${name}(args)`);\n const examples = bindings\n .slice(0, 2)\n .map((name) =>\n name === \"read\"\n ? 'await tools.read({ path: \"src/index.ts\" })'\n : name === \"bash\"\n ? 'await tools.bash({ command: \"pnpm test\" })'\n : `await tools.${name}({ /* … */ })`,\n )\n .join(\"\\n\");\n\n return [\n \"[PTC MODE ACTIVE]\",\n \"This session runs in PTC mode. \" + builtinNote,\n \"\",\n \"Work by writing one TypeScript program per task and running it:\",\n \" - ptc_run_code for a plain program (its return value and console.log come back to you)\",\n \" - ptc_workflow when the task has named phases and you want narration (log / phase /\",\n \" parallel / pipeline are available there)\",\n \"\",\n `The program body is an async function. Available inside it: ${callable.join(\", \")}.`,\n examples.length > 0 ? `Examples:\\n${examples}` : \"\",\n \"\",\n \"Batch a task's steps into ONE program instead of issuing many tool calls — gathering\",\n \"intermediate output yourself and returning only what matters is the point of this mode.\",\n \"Image-bearing tool results inside a program are attached to you after the run — never return\",\n \"base64 image data as your program's value.\",\n \"If the PTC surfaces cannot express what you need, say so and the user can run /ptc off.\",\n ]\n .filter((line) => line.length > 0)\n .join(\"\\n\");\n}\n","/**\n * The system-prompt skills section, restored for PTC mode.\n *\n * pi advertises skills only when the session can actually open one:\n *\n * const skillFileReadTool = [\"read\", \"bash\"].find((tool) => selectedTools.includes(tool));\n * if (skillFileReadTool && skills.length > 0) promptSections.skills = ...\n * (pi-coding-agent: dist/core/system-prompt.js)\n *\n * PTC mode hides exactly those two tools, so that gate drops the section — and with it the model's\n * only way to learn that skills exist. Measured on real session files: sessions before\n * PTC-mode-by-default carried 43 skills in `SystemMessage.sections.skills`; PTC sessions carried\n * no `skills` section at all. See ADR-0011.\n *\n * The list body stays pi's own — `formatSkillsForPrompt` is a public export and its XML shape is\n * the agentskills.io spec, so re-deriving it here would only create drift. Only the loading\n * sentence changes: \"Use the read tool\" is false in a session where `read` is hidden, and the way\n * in is `tools.read` from inside a program.\n */\nimport { formatSkillsForPrompt } from \"@earendil-works/pi-coding-agent\";\nimport type { Skill } from \"@earendil-works/pi-coding-agent\";\n\n/** The two tools pi accepts as \"this session can read a skill file\". */\nexport const SKILL_READING_TOOL_NAMES: readonly string[] = [\"read\", \"bash\"];\n\n/** pi's sentence, which names a tool PTC mode hides. */\nconst PI_LOAD_INSTRUCTION =\n \"Use the read tool to load a skill's file when the task matches its description.\";\n\n/** What replaces it: the same instruction, in the only call form this session has. */\nexport const PTC_SKILL_LOAD_INSTRUCTION =\n \"Use tools.read({ path }) inside a ptc_run_code program to load a skill's file when the task matches its description.\";\n\n/**\n * Would pi withhold the skills section for this loadout?\n *\n * Mirrors the gate rather than guessing: it is true exactly when neither `read` nor `bash` is\n * directly callable, which is the state the mode deliberately puts an ordinary session in.\n */\nexport function skillsSectionDropped(visibleTools: readonly string[]): boolean {\n return !SKILL_READING_TOOL_NAMES.some((name) => visibleTools.includes(name));\n}\n\n/**\n * Build the `skills` section body (pi wraps it in `<skills>…</skills>` itself).\n *\n * Returns `\"\"` when nothing is advertisable — pi's formatter filters out skills marked\n * `disable-model-invocation`, which is the correct behaviour to inherit: those are\n * `/skill:name`-only by design.\n *\n * `format` is injectable so a test can drive the reworded-header branch without patching pi.\n */\nexport function buildPtcSkillsSection(\n skills: readonly Skill[],\n format: (skills: Skill[], fileReadTool?: \"read\" | \"bash\") => string = formatSkillsForPrompt,\n): string {\n const section = format([...skills], \"read\").trim();\n if (section.length === 0) return \"\";\n if (section.includes(PI_LOAD_INSTRUCTION)) {\n return section.replace(PI_LOAD_INSTRUCTION, PTC_SKILL_LOAD_INSTRUCTION);\n }\n // pi reworded its header. Keep the list, but never inherit a sentence that names a tool this\n // session cannot call — a wrong instruction is worse than a redundant one.\n return `${PTC_SKILL_LOAD_INSTRUCTION}\\n\\n${section}`;\n}\n","/**\n * pi-ptc-subagents — DSH PTC mode (Programmable Tool Calling) for pi.\n *\n * This module is the extension entrypoint pi loads (`package.json` → `pi.extensions`).\n * The PTC machinery it exposes is complete and tested (T3): wire protocol, dispatcher,\n * bindings and the two worker surfaces. On top of it sit the two model-facing tools:\n *\n * - `ptc_run_code` — bindings + Node + `console.log`, program's return value is the result\n * - `ptc_workflow` — the same plus `log` / `phase` / `parallel` / `pipeline` and `args`\n *\n * There is no `agent()` helper on either surface (G1 #13 → decision B): calling it produces\n * a plain `ReferenceError` through the normal code-run error path.\n */\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport type {\n ExtensionAPI,\n ExtensionContext,\n NormalizedBuildSystemPromptOptions,\n} from \"@earendil-works/pi-coding-agent\";\nimport { createPtcRunCodeTool } from \"./tools/run-code.ts\";\nimport { createPtcWorkflowTool } from \"./tools/workflow.ts\";\nimport { resolveBindingNames, resolveDepthFromEnv } from \"./tools/common.ts\";\nimport { TurnPools } from \"./runtime/turn-pools.ts\";\nimport {\n bindingSource,\n buildModeInstruction,\n decideModeEntry,\n DEFAULT_HIDE_STRATEGY,\n detectExternalLoadoutChange,\n initialModeState,\n PTC_MODE_ENTRY_TYPE,\n PTC_MODE_STATUS_KEY,\n readDefaultModeConfig,\n resolveBaseOnStart,\n} from \"./mode/ptc-mode.ts\";\nimport type { ModeHideStrategy, PersistedModeState } from \"./mode/ptc-mode.ts\";\nimport { buildPtcSkillsSection, skillsSectionDropped } from \"./mode/skills-section.ts\";\n\nexport {\n bindingSource,\n buildModeInstruction,\n decideModeEntry,\n DEFAULT_HIDE_STRATEGY,\n detectExternalLoadoutChange,\n initialModeState,\n modeLoadout,\n MODE_REQUIRED_TOOL_NAMES,\n PTC_MODE_CONFIG_FILE,\n PTC_MODE_ENTRY_TYPE,\n PTC_MODE_STATUS_KEY,\n PTC_MODE_TOOL_NAMES,\n readDefaultModeConfig,\n resolveBaseOnStart,\n sameToolSet,\n} from \"./mode/ptc-mode.ts\";\nexport type {\n DefaultModeConfig,\n ModeBlockReason,\n ModeEntryDecision,\n ModeEntryInput,\n ModeHideStrategy,\n PersistedModeState,\n PtcModeState,\n} from \"./mode/ptc-mode.ts\";\nexport {\n buildPtcSkillsSection,\n PTC_SKILL_LOAD_INSTRUCTION,\n SKILL_READING_TOOL_NAMES,\n skillsSectionDropped,\n} from \"./mode/skills-section.ts\";\nexport { MAX_LINE_CHARS, renderModelValue, sanitizeText, stripAnsi } from \"./tools/text.ts\";\n/** Footer status label shown while the mode is on. */\nconst MODE_STATUS_TEXT = \"PTC\";\n\n/**\n * Which tools the mode hides. The shipped extension always uses the default; it is a named\n * constant so a future settings surface (or a test) has one place to change it.\n */\nconst MODE_HIDE_STRATEGY: ModeHideStrategy = DEFAULT_HIDE_STRATEGY;\n\n/**\n * What to tell the user when the mode turns on. Names what became unreachable, because that is the\n * part that surprises people — and the binding list, because that is the part they need in order\n * to know what a program can still do.\n */\nfunction describeEntry(base: readonly string[], hide: ModeHideStrategy): string {\n const hidden =\n hide === \"all-but-ptc\"\n ? \"all other tools\"\n : \"built-in tools (now reachable only as tools.<name> inside a program)\";\n return [\n `PTC mode on — ${hidden} hidden.`,\n `Bindings available inside a program: ${resolveBindingNames(base).join(\", \") || \"(none)\"}.`,\n \"Run /ptc off to restore the normal tool set.\",\n ].join(\"\\n\");\n}\n\nexport {\n BUILTIN_BINDING_NAMES,\n createBuiltinBindings,\n DEFAULT_BINDING_NAMES,\n} from \"./runtime/bindings.ts\";\nexport type {\n Binding,\n BindingContext,\n BindingTable,\n CreateBuiltinBindingsOptions,\n} from \"./runtime/bindings.ts\";\nexport { runPtcProgram } from \"./runtime/dispatcher.ts\";\nexport type { PtcRunOutcome, PtcImage, RunPtcProgramOptions } from \"./runtime/dispatcher.ts\";\nexport {\n WorkerPool,\n type WorkerPoolOptions,\n type WorkerPoolStats,\n type WorkerPoolWorkerOptions,\n} from \"./runtime/worker-pool.ts\";\nexport { TurnPools, type TurnPoolsOptions } from \"./runtime/turn-pools.ts\";\nexport {\n createWorkerEnv,\n DEFAULT_CONFIG,\n effectiveTimeoutMs,\n resolveConfig,\n WORKER_ENV_ALLOW_LIST,\n} from \"./runtime/limits.ts\";\nexport type { PtcConfig, PtcSurface } from \"./runtime/limits.ts\";\nexport {\n PTC_ERROR_KIND,\n PTC_LOG_LEVEL,\n HOST_FRAME_KIND,\n WORKER_FRAME_KIND,\n} from \"./runtime/protocol.ts\";\nexport type { PtcErrorKind, PtcErrorShape, PtcJsonValue } from \"./runtime/protocol.ts\";\n\nexport default function ptcSubagents(pi: ExtensionAPI): void {\n const mode = initialModeState();\n // Set on entry, cleared after the briefing has been injected, so the instruction lands once\n // per mode entry instead of on every turn.\n let briefingPending = false;\n\n /**\n * Per-turn worker pools (ADR-0017 §1–§2): one warm set per surface, created lazily by the\n * first PTC run of the turn and retired at the turn's end. The holder is swapped rather\n * than mutated so a `turn_end` drain can never race a run that is still holding a worker —\n * the outgoing holder owns everything the ending turn created.\n */\n let turnPools = new TurnPools();\n\n /**\n * Bindings come from the mode's base snapshot while PTC mode is on, and from the live loadout\n * otherwise (T7, #21). See `src/mode/ptc-mode.ts` for why the snapshot is required: the mode\n * hides the built-ins, which would otherwise empty the binding table and make every\n * `tools.<name>(...)` call fail.\n */\n const getBindingSourceNames = (): readonly string[] => bindingSource(mode, pi.getActiveTools());\n\n /**\n * Depth baseline for PTC runs this pi process starts (ADR-0016 Recursive section).\n * Inside a pi process spawned by `pi.dispatch`, the env carries `PI_PTC_DEPTH`; a\n * normal pi session has none, and the parent turn's runs stay at depth 0.\n */\n const ptcDepth = resolveDepthFromEnv();\n\n pi.registerTool(\n createPtcRunCodeTool({\n getBindingSourceNames,\n getPool: () => turnPools.get(\"run_code\"),\n depth: ptcDepth,\n }),\n );\n pi.registerTool(\n createPtcWorkflowTool({\n getBindingSourceNames,\n getPool: () => turnPools.get(\"workflow\"),\n depth: ptcDepth,\n }),\n );\n\n /**\n * Retire the turn's pools. `drain()` terminates the warm workers, so nothing outlives the\n * turn that spawned it; the next PTC run starts a fresh, lazily-created set. Idle workers are\n * already `unref()`-ed by the pool, so a turn that ends without this hook firing can never\n * keep the process alive either.\n */\n pi.on(\"turn_end\", async () => {\n const draining = turnPools;\n turnPools = new TurnPools();\n await draining.drain();\n });\n\n const paintStatus = (ctx: ExtensionContext): void => {\n ctx.ui.setStatus(\n PTC_MODE_STATUS_KEY,\n mode.enabled ? ctx.ui.theme.fg(\"accent\", MODE_STATUS_TEXT) : undefined,\n );\n };\n\n const enterMode = (\n base: readonly string[],\n loadout: readonly string[],\n ctx: ExtensionContext,\n announcement: string,\n ): void => {\n mode.enabled = true;\n mode.base = base;\n mode.ourLoadout = loadout;\n pi.setActiveTools([...loadout]);\n briefingPending = true;\n pi.appendEntry(PTC_MODE_ENTRY_TYPE, { enabled: true, base: [...base] });\n paintStatus(ctx);\n ctx.ui.notify(announcement, \"info\");\n };\n\n const exitMode = (\n ctx: ExtensionContext,\n opts: { announce: boolean; customType: string },\n ): void => {\n const base = mode.base;\n mode.enabled = false;\n mode.base = undefined;\n mode.ourLoadout = undefined;\n briefingPending = false;\n if (base !== undefined) pi.setActiveTools([...base]);\n pi.appendEntry(opts.customType, { enabled: false });\n paintStatus(ctx);\n if (opts.announce) {\n ctx.ui.notify(\"PTC mode off — built-in tools are directly callable again.\", \"info\");\n }\n };\n\n /** Restore the persisted mode record, if this session has one. */\n const readPersistedMode = (ctx: ExtensionContext): PersistedModeState | undefined => {\n const entries = ctx.sessionManager.getEntries();\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.type !== \"custom\" || entry.customType !== PTC_MODE_ENTRY_TYPE) continue;\n const data = entry.data as { enabled?: unknown; base?: unknown } | undefined;\n const base = Array.isArray(data?.base)\n ? data.base.filter((name): name is string => typeof name === \"string\")\n : undefined;\n return {\n enabled: data?.enabled === true,\n ...(base === undefined ? {} : { base }),\n };\n }\n return undefined;\n };\n\n pi.registerCommand(\"ptc\", {\n description:\n \"Show or toggle PTC mode (built-in tools reachable only from inside a PTC program)\",\n handler: async (args, ctx) => {\n const action = args.trim().toLowerCase();\n if (action === \"off\") {\n if (!mode.enabled) {\n ctx.ui.notify(\"PTC mode is already off.\", \"info\");\n return;\n }\n exitMode(ctx, { announce: true, customType: PTC_MODE_ENTRY_TYPE });\n return;\n }\n if (action !== \"on\") {\n ctx.ui.notify(\n mode.enabled\n ? `PTC mode is ON — run /ptc off to restore built-in tools.\\nBindings: ${resolveBindingNames(mode.base).join(\", \") || \"(none)\"}`\n : \"PTC mode is OFF — run /ptc on to enter it.\",\n \"info\",\n );\n return;\n }\n if (mode.enabled) {\n ctx.ui.notify(\"PTC mode is already on.\", \"info\");\n return;\n }\n // Manual entry bypasses the config and the restricted-session policy; a restricted session\n // simply enters with its own (smaller) loadout as `base`, so bindings stay inside it.\n const decision = decideModeEntry({\n mode: ctx.mode,\n defaultMode: true,\n active: pi.getActiveTools(),\n manual: true,\n hide: MODE_HIDE_STRATEGY,\n });\n if (!decision.enter) {\n ctx.ui.notify(\n decision.reason === \"tools-unavailable\"\n ? \"Cannot enter PTC mode: ptc_run_code / ptc_workflow are not active in this session.\"\n : \"Cannot enter PTC mode in this run mode.\",\n \"warning\",\n );\n return;\n }\n enterMode(\n decision.base,\n decision.loadout,\n ctx,\n describeEntry(decision.base, MODE_HIDE_STRATEGY),\n );\n },\n });\n\n pi.on(\"session_start\", async (_event, ctx) => {\n const config = readDefaultModeConfig(getAgentDir());\n if (config.error !== undefined) {\n ctx.ui.notify(`pi-ptc-subagents: ${config.error}`, \"warning\");\n }\n\n const persisted = readPersistedMode(ctx);\n const active = pi.getActiveTools();\n const { base, restoreFirst } = resolveBaseOnStart(persisted, active);\n // Undo our own narrowing before deciding, so the decision sees the loadout this pi process\n // was actually launched with rather than the one a previous mode invocation left behind.\n if (restoreFirst) pi.setActiveTools([...base]);\n\n const decision = decideModeEntry({\n mode: ctx.mode,\n defaultMode: config.defaultMode,\n active: base,\n manual: false,\n hide: MODE_HIDE_STRATEGY,\n });\n // A persisted record is the user's most recent explicit intent: `/ptc off` survives a resume\n // even when the config still says default-on.\n const wantMode = persisted?.enabled ?? decision.enter;\n\n if (decision.enter && wantMode) {\n enterMode(base, decision.loadout, ctx, describeEntry(base, MODE_HIDE_STRATEGY));\n return;\n }\n paintStatus(ctx);\n if (!decision.enter && decision.reason !== \"not-tui\" && persisted === undefined) {\n if (decision.reason === \"restricted-session\") {\n ctx.ui.notify(\n \"PTC mode skipped: this session was launched with an explicit tool restriction.\",\n \"info\",\n );\n }\n }\n });\n\n /**\n * Put back the skills section pi withholds while the mode hides `read`/`bash` (ADR-0011).\n *\n * pi advertises skills only when the session can open a skill file at all (`read` or `bash`\n * among the selected tools), so a PTC session silently loses the whole `<available_skills>`\n * list: the model stops knowing skills exist. Writing `sections.skills` here is the seam pi\n * leaves open — custom sections are merged after its own, so this one wins.\n *\n * The same options object is reused across turns and sessions of a resume, so the injection is\n * undone the moment the mode stops hiding those tools: a `/ptc off` must not leave PTC-flavoured\n * loading instructions behind while `read` is directly callable again.\n */\n let skillsSectionInjected = false;\n const syncSkillsSection = (\n options: NormalizedBuildSystemPromptOptions | undefined,\n hidden: boolean,\n ): void => {\n if (options === undefined) return;\n if (!mode.enabled || !hidden) {\n if (skillsSectionInjected) {\n delete options.sections.skills;\n skillsSectionInjected = false;\n }\n return;\n }\n const section = buildPtcSkillsSection(options.skills);\n if (section.length === 0) return;\n options.sections.skills = section;\n skillsSectionInjected = true;\n };\n\n pi.on(\"before_agent_start\", async (event, ctx) => {\n // A live pi always supplies these; the test stub emits a bare event, hence the undefined case.\n const options = event.systemPromptOptions;\n syncSkillsSection(options, skillsSectionDropped(pi.getActiveTools()));\n if (!mode.enabled) return;\n if (detectExternalLoadoutChange(mode, pi.getActiveTools())) {\n // Another extension owns the loadout now; hand it back rather than fight over it.\n exitMode(ctx, { announce: false, customType: PTC_MODE_ENTRY_TYPE });\n syncSkillsSection(options, skillsSectionDropped(pi.getActiveTools()));\n ctx.ui.notify(\"PTC mode off — another extension changed the active tool set.\", \"warning\");\n return;\n }\n if (!briefingPending) return;\n briefingPending = false;\n return {\n message: {\n customType: \"ptc-mode-briefing\",\n content: buildModeInstruction(resolveBindingNames(mode.base), MODE_HIDE_STRATEGY),\n display: false,\n },\n };\n });\n}\n"],"mappings":"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"}
|