@tekmidian/pai 0.66.9 → 0.66.10
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/dist/auto-route-C0QZLN4f.mjs +7 -0
- package/dist/{auto-route-DoFrncW7.mjs → auto-route-CAg45rck.mjs} +2 -2
- package/dist/{auto-route-DoFrncW7.mjs.map → auto-route-CAg45rck.mjs.map} +1 -1
- package/dist/{chain-BL4khGgy.mjs → chain-DyVKYcIZ.mjs} +3 -3
- package/dist/{chain-BL4khGgy.mjs.map → chain-DyVKYcIZ.mjs.map} +1 -1
- package/dist/cli/index.mjs +7 -7
- package/dist/cli/program.mjs +7 -7
- package/dist/{context-handover-cache-5qnqzWqp.mjs → context-handover-cache-fCyfgKm5.mjs} +4 -4
- package/dist/{context-handover-cache-5qnqzWqp.mjs.map → context-handover-cache-fCyfgKm5.mjs.map} +1 -1
- package/dist/daemon/index.mjs +6 -6
- package/dist/{daemon-4VZOwUXe.mjs → daemon-Blzrmw6r.mjs} +6 -6
- package/dist/{daemon-C-2mo7y_.mjs → daemon-DorRRaAl.mjs} +5 -5
- package/dist/{daemon-C-2mo7y_.mjs.map → daemon-DorRRaAl.mjs.map} +1 -1
- package/dist/daemon-mcp/index.mjs +4 -4
- package/dist/{fallback-2yWDDYiT.mjs → fallback-HinZYSNU.mjs} +3 -3
- package/dist/{fallback-2yWDDYiT.mjs.map → fallback-HinZYSNU.mjs.map} +1 -1
- package/dist/hooks/load-project-context.mjs.map +2 -2
- package/dist/hooks/security-validator.mjs.map +1 -1
- package/dist/hooks/worker-status-line.mjs.map +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{pai-marker-BFOlchq2.mjs → pai-marker-B1splddK.mjs} +2 -2
- package/dist/{pai-marker-BFOlchq2.mjs.map → pai-marker-B1splddK.mjs.map} +1 -1
- package/dist/{planner-CspP59ya.mjs → planner-4nCXw4TS.mjs} +4 -4
- package/dist/{planner-CspP59ya.mjs.map → planner-4nCXw4TS.mjs.map} +1 -1
- package/dist/{program-D_Ks5nkL.mjs → program-DnGZ_mrf.mjs} +9 -9
- package/dist/{program-D_Ks5nkL.mjs.map → program-DnGZ_mrf.mjs.map} +1 -1
- package/dist/{run-BZNdpUGQ.mjs → run-BHLXvURR.mjs} +3 -3
- package/dist/{run-BZNdpUGQ.mjs.map → run-BHLXvURR.mjs.map} +1 -1
- package/dist/{worktree-CwMRTa0H.mjs → worktree-DxsZ85ni.mjs} +20 -2
- package/dist/worktree-DxsZ85ni.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/auto-route-UD9PySwP.mjs +0 -7
- package/dist/worktree-CwMRTa0H.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-BZNdpUGQ.mjs","names":[],"sources":["../src/workers/mcp.ts","../src/workers/project-config.ts","../src/workers/routing.ts","../src/workers/proxy/server.ts","../src/workers/scope.ts","../src/workers/endreason.ts","../src/workers/controls.ts","../src/workers/engines/image.ts","../src/workers/pane.ts","../src/workers/tree.ts","../src/workers/operator.ts","../src/workers/handoff.ts","../src/workers/wait-on.ts","../src/workers/agentish.ts","../src/workers/report.ts","../src/workers/deadline.ts","../src/workers/browser-tools.ts","../src/workers/codex.ts","../src/workers/run.ts"],"sourcesContent":["/**\n * mcp.ts — the MCP allowlist for headless workers.\n *\n * Headless workers start with NO MCP servers: every server definition is a\n * prompt-time tool inventory the model pays to know, and a worker that only\n * reads and edits files needs none of it. A run may opt in with `--mcp\n * name[,name…]`, a role carrying `\"mcp\": [...]`, or implicitly by naming\n * `mcp__server__tool` in --allowedTools (a grant without its server loaded is\n * a dead letter). Names may be single servers from ~/.claude.json's\n * `mcpServers` or `workers.mcpSets` set names, which expand to their member\n * list. The filtered config lands in\n * `<logDir>/<id>.mcp.json` and is passed with `--strict-mcp-config\n * --mcp-config` so exactly those servers load. MCP servers are chosen at\n * launch only — a mid-run `say` cannot add any.\n *\n * One name in that space is not a server at all: `claude-in-chrome` is the\n * Chrome native-host bridge, which no config file can load and which the\n * `--chrome` flag switches on instead. It is recognised here so a grant\n * naming it becomes that flag rather than an \"unknown MCP server\" rejection.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { WorkersConfigError, type WorkersConfig } from \"./config.js\";\n\n/** ~/.claude.json — the user's MCP server definitions (top-level mcpServers). */\nexport const CLAUDE_JSON = join(homedir(), \".claude.json\");\n\n/**\n * The browser bridge is not an MCP server. It rides the Chrome native-host\n * channel, so it never appears in `mcpServers`; a spawned claude has it off\n * and the `--chrome` flag switches it on. Tool grants naming it must\n * therefore neither resolve to a server nor be rejected as unknown — they\n * select a flag. See `grantsChrome`.\n */\nexport const CHROME_SERVER = \"claude-in-chrome\";\n\n/**\n * True for a real server definition, false for anything else that happens to\n * sit under `mcpServers`.\n *\n * The vendor config is not ours and has been observed carrying non-server\n * entries under that key — tool-usage records keyed by tool name\n * (`Read`, `Bash`, `mcp__server__tool`, …). Those are not servers: handing\n * one to `--mcp-config` yields \"invalid MCP server config\", and listing one\n * as available invites a run that cannot work. A server has a `command` (or a\n * `url` for remote transports); an `mcp__…` key is a tool name whatever its\n * shape.\n */\nfunction isServerDefinition(name: string, value: unknown): boolean {\n if (name.startsWith(\"mcp__\")) return false;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const v = value as Record<string, unknown>;\n return (\n (typeof v.command === \"string\" && v.command.length > 0) ||\n (typeof v.url === \"string\" && v.url.length > 0)\n );\n}\n\nexport function readMcpServers(claudeJson = CLAUDE_JSON): Record<string, unknown> {\n try {\n if (!existsSync(claudeJson)) return {};\n const parsed = JSON.parse(readFileSync(claudeJson, \"utf8\")) as Record<string, unknown>;\n const servers = parsed.mcpServers;\n if (typeof servers !== \"object\" || servers === null || Array.isArray(servers)) return {};\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(servers as Record<string, unknown>)) {\n if (isServerDefinition(name, value)) out[name] = value;\n }\n return out;\n } catch {\n // a damaged ~/.claude.json must not take workers down with it\n return {};\n }\n}\n\n/**\n * True when any `--mcp` name or `--allowedTools` grant asks for the browser\n * bridge: the bare server name, `mcp__claude-in-chrome`, or any\n * `mcp__claude-in-chrome__<tool>`. The runner turns this into `--chrome` on\n * the claude argv.\n */\nexport function grantsChrome(entries: string[]): boolean {\n for (const entry of entries) {\n for (const name of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (name === CHROME_SERVER) return true;\n if (name === `mcp__${CHROME_SERVER}`) return true;\n if (name.startsWith(`mcp__${CHROME_SERVER}__`)) return true;\n }\n }\n return false;\n}\n\n/**\n * Split a `--mcp a,b,c` flag value (also accepts repeated flags already split\n * by the caller) and expand set names from workers.mcpSets.\n */\nexport function expandMcpNames(\n names: string[],\n config: Pick<WorkersConfig, \"mcpSets\">,\n claudeJson = CLAUDE_JSON\n): string[] {\n const available = readMcpServers(claudeJson);\n const out: string[] = [];\n for (const raw of names) {\n for (const name of raw.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (name === CHROME_SERVER) continue; // a flag, not a server — see grantsChrome\n if (name in config.mcpSets) {\n for (const member of config.mcpSets[name]) {\n if (!out.includes(member)) out.push(member);\n }\n continue;\n }\n if (!(name in available)) {\n const sets = Object.keys(config.mcpSets);\n throw new WorkersConfigError(\n `unknown MCP server \"${name}\". Available servers: ` +\n `${Object.keys(available).join(\", \") || \"(none in ~/.claude.json)\"}` +\n `${sets.length ? `; sets: ${sets.join(\", \")}` : \"\"}`\n );\n }\n if (!out.includes(name)) out.push(name);\n }\n }\n return out;\n}\n\n/**\n * Derive server names from tool grants: every `mcp__<server>__<tool>` (or bare\n * `mcp__<server>`, or `mcp__<server>__*`) in an --allowedTools list names a\n * server the run expects to be loaded. A grant is a dead letter unless its\n * server is in the filtered config, so the runner treats these as implicit\n * --mcp names — and only these; non-mcp grants load nothing.\n */\nexport function mcpServersFromToolGrants(tools: string[]): string[] {\n const out: string[] = [];\n for (const entry of tools) {\n for (const name of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (!name.startsWith(\"mcp__\")) continue;\n const server = name.slice(\"mcp__\".length).split(\"__\")[0];\n if (server === CHROME_SERVER) continue; // a flag, not a server — see grantsChrome\n if (server && !out.includes(server)) out.push(server);\n }\n }\n return out;\n}\n\n/** Path of a run's filtered MCP config. */\nexport function runMcpConfigPath(logDir: string, id: string): string {\n return join(logDir, `${id}.mcp.json`);\n}\n\n/**\n * Write a config containing only `names` (already expanded) and return its\n * path. Unknown names fail fast with the available list.\n */\nexport function writeMcpConfig(\n logDir: string,\n id: string,\n names: string[],\n claudeJson = CLAUDE_JSON\n): string {\n const available = readMcpServers(claudeJson);\n const unknown = names.filter((n) => !(n in available));\n if (unknown.length) {\n throw new WorkersConfigError(\n `unknown MCP server(s): ${unknown.join(\", \")}. Available: ` +\n `${Object.keys(available).join(\", \") || \"(none in ~/.claude.json)\"}`\n );\n }\n const servers: Record<string, unknown> = {};\n for (const n of names) servers[n] = available[n];\n const path = runMcpConfigPath(logDir, id);\n if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n writeFileSync(path, JSON.stringify({ mcpServers: servers }, null, 2) + \"\\n\", \"utf8\");\n return path;\n}\n\n/** `pai worker mcp list`: the servers and sets a run could name. */\nexport function describeMcp(config: Pick<WorkersConfig, \"mcpSets\">, claudeJson = CLAUDE_JSON): string[] {\n const servers = Object.keys(readMcpServers(claudeJson));\n const lines: string[] = [];\n if (servers.length) {\n lines.push(`servers (~/.claude.json):`);\n for (const s of servers) lines.push(` ${s}`);\n } else {\n lines.push(`no MCP servers defined in ~/.claude.json`);\n }\n const sets = Object.entries(config.mcpSets);\n if (sets.length) {\n lines.push(`sets (workers.mcpSets):`);\n for (const [name, members] of sets) lines.push(` ${name} = ${members.join(\", \")}`);\n }\n lines.push(`usage: pai worker run --mcp <name>[,<name>…] -p '<task>'`);\n return lines;\n}\n","/**\n * project-config.ts — the project registry's optional launch pin, read for\n * the interactive branch of run.ts only: which MCP servers and built-in\n * tools a project's supervisor session (`pai worker run`, no -p) loads. Set\n * with `pai project mcp` / `pai project tools` (src/cli/commands/project/\n * session-config.ts), stored in the same `projects.session_config` JSON\n * column as the rest of a project's launch config. Unset = today's\n * behavior: every server, every tool.\n *\n * A standalone SQL lookup rather than importing the CLI's detectProject:\n * workers/ is a dependency of cli/, not the other way around.\n */\n\nimport { resolve } from \"node:path\";\nimport { getRegistryBackend } from \"../storage/factory.js\";\nimport type { RegistryBackend } from \"../storage/registry-interface.js\";\nimport { readMcpServers } from \"./mcp.js\";\n\nexport interface ProjectLaunchConfig {\n slug: string;\n mcp?: string[];\n tools?: string[];\n}\n\nfunction asStringArray(v: unknown): string[] | undefined {\n if (!Array.isArray(v)) return undefined;\n const out = v.filter((x): x is string => typeof x === \"string\");\n return out.length ? out : undefined;\n}\n\n/**\n * The launch config of the project whose root_path is `cwd` or an ancestor\n * of it (longest match wins) — null when no registered project covers it,\n * or when the matching project has no mcp/tools pin set. `registry` is\n * injectable for tests; production callers take the process-wide backend.\n */\nexport async function projectLaunchConfig(\n cwd: string,\n registry?: RegistryBackend\n): Promise<ProjectLaunchConfig | null> {\n const target = resolve(cwd);\n const backend = registry ?? (await getRegistryBackend());\n const rows = await backend.listProjectsByPathLengthDesc({ excludeArchived: true });\n for (const row of rows) {\n const root = resolve(row.root_path);\n if (target !== root && !target.startsWith(root + \"/\")) continue;\n if (!row.session_config) return null;\n let parsed: { mcp?: unknown; tools?: unknown };\n try {\n parsed = JSON.parse(row.session_config);\n } catch {\n return null;\n }\n const mcp = asStringArray(parsed.mcp);\n const tools = asStringArray(parsed.tools);\n return mcp || tools ? { slug: row.slug, mcp, tools } : null;\n }\n return null;\n}\n\n/**\n * One line per active pin, so a pin never silently removes a server or tool:\n * shown by the SessionStart hook and by `pai worker run`'s interactive launch.\n * `claudeJson` is injectable for tests; read-only.\n */\nexport function pinNotices(launch: ProjectLaunchConfig | null, claudeJson?: string): string[] {\n if (!launch) return [];\n const lines: string[] = [];\n if (launch.mcp) {\n const all = Object.keys(readMcpServers(claudeJson));\n const loaded = all.filter((n) => launch.mcp!.includes(n));\n const missing = all.filter((n) => !launch.mcp!.includes(n));\n lines.push(\n `MCP pinned for ${launch.slug}: loading ${loaded.length} of ${all.length} servers; ` +\n `not loaded: ${missing.join(\", \") || \"(none)\"} — \\`pai project mcp --clear\\` to load all`\n );\n }\n if (launch.tools) {\n lines.push(`Tools pinned for ${launch.slug}: ${launch.tools.join(\",\")} — \\`pai project tools --clear\\``);\n }\n return lines;\n}\n","/**\n * routing.ts — provider selection: flag > class > active (possibly \"auto\").\n *\n * Auto-routing walks `workers.routing.order` — or the class's own `order` —\n * and takes the first provider that is enabled, out of cooldown, (when it\n * defines a quotaProbe) under its quotaSkipAt threshold, and — when the class\n * constrains it — within `maxCostTier` and carrying all `requireTags`. A run\n * that dies of a quota/rate error puts its provider in cooldown for\n * cooldownMinutes; when that happens before the first tool call and\n * retryOnQuota is set, the runner restarts the same task on the next provider\n * (ledger: WORKER-REROUTE).\n *\n * An explicit --provider or a class mapping that pins a provider always\n * bypasses all of this.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n type WorkerEngine,\n type WorkerProvider,\n type WorkersConfig,\n type ProviderTag,\n WORKER_CLASSES,\n ANTHROPIC_NATIVE,\n WorkersConfigError,\n classModelCapability,\n providerCostTier,\n getProviderOrNative,\n resolveCapability,\n} from \"./config.js\";\nimport { routingStatePath } from \"./paths.js\";\n\nconst QUOTA_SKIP_DEFAULT = 95;\nconst PROBE_TIMEOUT_MS = 10_000;\n\nexport interface RoutingState {\n /** provider name → ISO timestamp when its cooldown ends */\n cooldowns: Record<string, string>;\n}\n\nexport function readRoutingState(logDir: string): RoutingState {\n const path = routingStatePath(logDir);\n if (!existsSync(path)) return { cooldowns: {} };\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as RoutingState;\n return { cooldowns: parsed.cooldowns ?? {} };\n } catch {\n return { cooldowns: {} };\n }\n}\n\nexport function writeRoutingState(logDir: string, state: RoutingState): void {\n const path = routingStatePath(logDir);\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n renameSync(tmp, path);\n}\n\nexport function cooldownRemaining(\n state: RoutingState,\n provider: string,\n now: Date = new Date()\n): number {\n const end = state.cooldowns[provider];\n if (!end) return 0;\n const ms = Date.parse(end) - now.getTime();\n return ms > 0 ? ms : 0;\n}\n\nexport function setCooldown(\n logDir: string,\n provider: string,\n minutes: number,\n now: Date = new Date()\n): void {\n const state = readRoutingState(logDir);\n state.cooldowns[provider] = new Date(now.getTime() + minutes * 60_000).toISOString();\n writeRoutingState(logDir, state);\n}\n\nexport function clearCooldown(logDir: string, provider: string): void {\n const state = readRoutingState(logDir);\n if (!(provider in state.cooldowns)) return;\n delete state.cooldowns[provider];\n writeRoutingState(logDir, state);\n}\n\n/**\n * Run a provider's quotaProbe and return its percentage (0–100), or null when\n * there is no probe or it printed nothing usable. A probe must never break\n * routing: failures read as \"unknown\", not as \"full\".\n */\nexport function probeQuota(provider: WorkerProvider): number | null {\n if (!provider.quotaProbe) return null;\n try {\n const out = execFileSync(\"/bin/sh\", [\"-c\", provider.quotaProbe], {\n timeout: PROBE_TIMEOUT_MS,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n const m = out.match(/(\\d+(?:\\.\\d+)?)/);\n if (!m) return null;\n return Math.min(100, Math.max(0, Math.round(parseFloat(m[1]))));\n } catch {\n return null;\n }\n}\n\nexport function quotaSkipThreshold(provider: WorkerProvider): number {\n return provider.quotaSkipAt ?? QUOTA_SKIP_DEFAULT;\n}\n\n/** Does this provider exceed its quota threshold right now? */\nexport function quotaExceeded(provider: WorkerProvider): boolean {\n const used = probeQuota(provider);\n return used !== null && used >= quotaSkipThreshold(provider);\n}\n\nexport interface ResolvedTarget {\n providerName: string;\n provider: WorkerProvider;\n /** Model alias from the class (\"glm/fast\" → \"fast\"), null = provider default. */\n modelAlias: string | null;\n /** MCP servers (or set names) from the class target, null when it sets none. */\n classMcp: string[] | null;\n /** How the provider was chosen — names the bypass rule for rerouting. */\n via: \"flag\" | \"class\" | \"active\" | \"auto\";\n}\n\nexport class NoProviderError extends WorkersConfigError {}\n\nfunction mustExist(config: WorkersConfig, name: string): WorkerProvider {\n const p = getProviderOrNative(config, name);\n if (!p) {\n throw new NoProviderError(\n `no worker provider named \"${name}\". Configured: ` +\n `${Object.keys(config.providers).join(\", \") || \"(none)\"}.` +\n `\\nAdd one with: pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n );\n }\n return p;\n}\n\nfunction mustBeRunnable(name: string, p: WorkerProvider): WorkerProvider {\n if (!p.enabled) {\n throw new NoProviderError(\n `provider \"${name}\" is disabled. Enable it with: pai worker providers enable ${name}`\n );\n }\n return p;\n}\n\n/** Why one provider of a routing order did not qualify (message fragment). */\nfunction exclusionReason(\n config: WorkersConfig,\n state: RoutingState,\n name: string,\n cls?: { maxCostTier?: number; requireTags?: string[] }\n): string | null {\n const p = getProviderOrNative(config, name);\n if (!p) return \"not configured\";\n if (!p.enabled) return \"disabled\";\n if (cooldownRemaining(state, name) > 0) return \"cooldown\";\n if (quotaExceeded(p)) return \"quota\";\n const tier = providerCostTier(p);\n if (cls?.maxCostTier !== undefined && tier > cls.maxCostTier) {\n return `cost tier ${tier} > max ${cls.maxCostTier}`;\n }\n if (cls?.requireTags?.length) {\n const have = p.tags ?? [];\n const missing = cls.requireTags.filter((t) => !have.includes(t as ProviderTag));\n if (missing.length) return `missing tags: ${missing.join(\", \")}`;\n }\n return null;\n}\n\n/**\n * Resolve which provider (and model alias) a run uses.\n *\n * @param flagProvider --provider value, highest precedence\n * @param className --class value (the old --role), looked up in workers.classes\n */\nexport function resolveTarget(\n config: WorkersConfig,\n logDir: string,\n opts: { flagProvider?: string; className?: string } = {}\n): ResolvedTarget {\n if (opts.flagProvider) {\n return {\n providerName: opts.flagProvider,\n provider: mustBeRunnable(opts.flagProvider, mustExist(config, opts.flagProvider)),\n modelAlias: null,\n classMcp: null,\n via: \"flag\",\n };\n }\n\n // class constraints (present whether or not the target pins a provider)\n const clsTarget = opts.className ? config.classes[opts.className] : undefined;\n const cls =\n typeof clsTarget === \"object\" && clsTarget !== null ? clsTarget : undefined;\n\n if (opts.className && clsTarget !== undefined && typeof clsTarget !== \"object\") {\n const [name, alias] = clsTarget.split(\"/\");\n return {\n providerName: name,\n provider: mustBeRunnable(name, mustExist(config, name)),\n modelAlias: alias ?? null,\n classMcp: null,\n via: \"class\",\n };\n }\n if (opts.className && cls?.provider) {\n const provider = mustBeRunnable(cls.provider, mustExist(config, cls.provider));\n return {\n providerName: cls.provider,\n provider,\n modelAlias: null,\n classMcp: cls.mcp ?? null,\n via: \"class\",\n };\n }\n if (opts.className && clsTarget === undefined) {\n // a standard class may simply be unconfigured (it then routes like a run\n // without a class); anything else is a typo and must not pass silently\n if (!(WORKER_CLASSES as readonly string[]).includes(opts.className)) {\n throw new NoProviderError(\n `no class named \"${opts.className}\". Standard classes: ${WORKER_CLASSES.join(\", \")}.` +\n `Defined: ${Object.keys(config.classes).join(\", \") || \"(none)\"}.` +\n `\\nSet one with: pai worker classes set ${opts.className}=<provider[/alias]>`\n );\n }\n }\n\n if (config.active !== \"auto\") {\n // nothing active and no class entry: the built-in provider, no config written\n const name = config.active ?? ANTHROPIC_NATIVE;\n return {\n providerName: name,\n provider: mustBeRunnable(name, mustExist(config, name)),\n modelAlias: null,\n classMcp: null,\n via: \"active\",\n };\n }\n\n // auto: first provider in (class or global) order that is enabled,\n // cooled-down-free, under quota and within the class constraints\n const state = readRoutingState(logDir);\n const order = cls?.order ?? config.routing.order;\n const excluded: string[] = [];\n for (const name of order) {\n const p = getProviderOrNative(config, name);\n const why = exclusionReason(config, state, name, cls);\n if (why) {\n excluded.push(`${name}: ${why}`);\n continue;\n }\n // exclusionReason already returned null (no exclusion) above, which is\n // only possible when getProviderOrNative found a real provider\n return { providerName: name, provider: p!, modelAlias: null, classMcp: cls?.mcp ?? null, via: \"auto\" };\n }\n throw new NoProviderError(\n `auto-routing found no usable provider${opts.className ? ` for class \"${opts.className}\"` : \"\"} ` +\n `(order: [${order.join(\", \")}]).` +\n `${excluded.length ? `\\nExcluded: ${excluded.join(\"; \")}.` : \"\"}` +\n `\\nClear a cooldown with: pai worker providers enable <name>; widen the class with: pai worker classes set ${opts.className ?? \"<class>\"}=<target>`\n );\n}\n\n/**\n * Next provider after `from` in auto order, applying the same filters.\n * Used by rerouting; null when the order is exhausted.\n */\nexport function nextAutoProvider(\n config: WorkersConfig,\n logDir: string,\n from: string,\n now: Date = new Date()\n): string | null {\n const state = readRoutingState(logDir);\n const order = config.routing.order;\n const start = order.indexOf(from);\n for (let i = start + 1; i < order.length; i++) {\n const name = order[i];\n const p = getProviderOrNative(config, name);\n if (!p || !p.enabled) continue;\n const end = state.cooldowns[name];\n if (end && Date.parse(end) > now.getTime()) continue;\n if (quotaExceeded(p)) continue;\n return name;\n }\n return null;\n}\n\n/**\n * Was this failure a quota/rate failure? Detected from the result text the\n * endpoint produced (HTTP status is not visible in the stream events).\n */\nexport function isQuotaFailure(resultText: string): boolean {\n return /usage limit reached|rate limit|quota/i.test(resultText);\n}\n\nexport interface CapabilityRunResolution {\n providerName: string;\n provider: WorkerProvider;\n model: string;\n engine: WorkerEngine;\n fellBack: boolean;\n capability: string;\n}\n\n/**\n * Whether a run should resolve through `resolveCapability` instead of\n * `resolveTarget`: an explicit `--capability` always does. A `--class`\n * whose implied capability is not \"default\" (e.g. `image`) does too, but\n * only when the class itself names no provider — a class already pinned to\n * one (`image: glm/image`) always wins unchanged, same as any other class.\n */\nexport function capabilityForRun(\n config: WorkersConfig,\n opts: { capabilityFlag?: string; className?: string }\n): string | null {\n if (opts.capabilityFlag) return opts.capabilityFlag;\n if (!opts.className) return null;\n const capability = classModelCapability(opts.className);\n if (capability === \"default\") return null;\n const clsTarget = config.classes[opts.className];\n const namesProvider = typeof clsTarget === \"string\" ? true : Boolean(clsTarget?.provider);\n return namesProvider ? null : capability;\n}\n\n/**\n * resolveCapability, turned into a runnable provider (NoProviderError-shaped\n * on failure). Unlike resolveCapability itself — which falls back to the\n * active provider's default model so `describeCapabilities` has something to\n * report — an actual run refuses the fallback: silently running a normal\n * model on a prompt written for a capability it does not have (an image\n * prompt fed to a text model, say) produces a confusing non-answer instead\n * of a clear error naming the config key to set.\n */\nexport function resolveCapabilityRun(config: WorkersConfig, capability: string): CapabilityRunResolution {\n const r = resolveCapability(config, capability);\n if (r.fellBack) {\n throw new NoProviderError(\n `no provider declares the \"${capability}\" capability — configure one with: ` +\n `pai worker capability ${capability} <provider>[,<provider>…], or ` +\n `pai worker model ${capability} <model-id> --provider <name>`\n );\n }\n // \"image\" specifically promises real pixel output — a provider whose only\n // claim to it is a `models.image` entry from the classes-based idiom\n // (`image: glm/image`, a plain model choice on an unchanged claude/codex\n // engine) cannot deliver that through this cross-provider run path, so a\n // run refuses it rather than spawning claude on a prompt it cannot answer.\n if (capability === \"image\" && r.engine !== \"image\") {\n throw new NoProviderError(\n `provider \"${r.provider}\" has no image engine (it would run \"${r.model}\" on its normal ` +\n `${r.engine} engine, not generate an image) — configure an engine=image provider with: ` +\n `pai worker providers add <name> --engine image --url <url> --key <key> --model <id>, then ` +\n `pai worker capability image <name>`\n );\n }\n const provider = getProviderOrNative(config, r.provider);\n if (!provider) {\n throw new NoProviderError(`capability \"${capability}\" resolved to unknown provider \"${r.provider}\"`);\n }\n return {\n providerName: r.provider,\n provider,\n model: r.model,\n engine: r.engine,\n fellBack: r.fellBack,\n capability,\n };\n}\n","/**\n * server.ts — the PAI worker proxy: Anthropic Messages API on the front,\n * OpenAI Chat Completions on the back, loopback only.\n *\n * One proxy serves every OpenAI-protocol provider: Claude Code points\n * ANTHROPIC_BASE_URL at `http://127.0.0.1:8797/<provider>` and the provider\n * name in the path selects the upstream (`upstreamUrl` + `keyFile` from the\n * workers config, re-read per request so config edits apply without a\n * restart). `run` starts the proxy on demand (detached, pid file under the\n * logDir) and `pai worker proxy stop` stops it again.\n *\n * All translation lives in translate.ts; this file is only HTTP plumbing.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer, type Server } from \"node:http\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { readWorkersSection, resolveProviderKey, type WorkerProvider } from \"../config.js\";\nimport { workersLogDir } from \"../paths.js\";\nimport {\n anthropicError,\n anthropicToOpenAi,\n openAiToAnthropic,\n OpenAiStreamTranslator,\n type AnthropicRequest,\n} from \"./translate.js\";\n\nexport const DEFAULT_PROXY_PORT = 8797;\nconst HOST = \"127.0.0.1\";\n\n/** How providers are looked up — swapped out by the tests. */\nexport type ProviderResolver = () => Record<string, WorkerProvider>;\n\nconst configProviders: ProviderResolver = () => readWorkersSection().workers.providers;\n\nexport function proxyPidPath(logDir: string): string {\n return join(logDir, \"proxy.pid\");\n}\n\n// ---------------------------------------------------------------------------\n// HTTP server\n// ---------------------------------------------------------------------------\n\ninterface ProxyServerOptions {\n port?: number;\n resolveProvider?: ProviderResolver;\n /** Test hook: called with every (translated) upstream request body. */\n onUpstreamRequest?: (url: string, body: unknown) => void;\n}\n\n/**\n * Create (but not start) the proxy server. Routes:\n * GET /healthz → 200 ok\n * POST /<provider>/v1/messages (and /<provider>/messages) → translated call\n */\nexport function createProxyServer(opts: ProxyServerOptions = {}): Server {\n const resolveProvider = opts.resolveProvider ?? configProviders;\n return createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${HOST}`);\n if (req.method === \"GET\" && url.pathname === \"/healthz\") {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"ok\");\n return;\n }\n const m = url.pathname.match(/^\\/([a-zA-Z0-9_-]+)\\/(v1\\/)?messages$/);\n if (req.method !== \"POST\" || !m) {\n res.writeHead(404, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(404, `no such route: ${req.method} ${url.pathname}`)));\n return;\n }\n const providerName = m[1];\n const chunks: Buffer[] = [];\n req.on(\"data\", (c: Buffer) => chunks.push(c));\n req.on(\"end\", () => {\n void handleMessages(providerName, Buffer.concat(chunks).toString(\"utf8\"), resolveProvider, res, opts);\n });\n req.on(\"error\", () => {\n res.writeHead(400, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(400, \"request body read failed\")));\n });\n });\n}\n\nasync function handleMessages(\n providerName: string,\n bodyText: string,\n resolveProvider: ProviderResolver,\n res: import(\"node:http\").ServerResponse,\n opts: ProxyServerOptions\n): Promise<void> {\n const fail = (status: number, message: string) => {\n res.writeHead(status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(status, JSON.stringify({ message }))));\n };\n let reqBody: AnthropicRequest;\n try {\n reqBody = JSON.parse(bodyText) as AnthropicRequest;\n } catch {\n fail(400, \"request body is not valid JSON\");\n return;\n }\n const provider = resolveProvider()[providerName];\n if (!provider) {\n fail(404, `no worker provider named \"${providerName}\"`);\n return;\n }\n if (provider.protocol !== \"openai\" || !provider.upstreamUrl) {\n fail(400, `provider \"${providerName}\" is not an openai-protocol provider`);\n return;\n }\n\n const model = provider.models.default;\n const openaiBody = anthropicToOpenAi(reqBody, model);\n opts.onUpstreamRequest?.(`${provider.upstreamUrl}/chat/completions`, openaiBody);\n\n let upstream: Response;\n try {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n let token: string | null;\n try {\n token = resolveProviderKey(provider);\n } catch (e) {\n fail(500, e instanceof Error ? e.message : String(e));\n return;\n }\n if (token) headers.authorization = `Bearer ${token}`;\n upstream = await fetch(`${provider.upstreamUrl}/chat/completions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(openaiBody),\n });\n } catch (e) {\n fail(502, `upstream unreachable: ${e instanceof Error ? e.message : String(e)}`);\n return;\n }\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n res.writeHead(upstream.status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(upstream.status, text)));\n return;\n }\n\n if (reqBody.stream) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n const translator = new OpenAiStreamTranslator(model);\n let buf = \"\";\n if (!upstream.body) {\n res.end(translator.finish());\n return;\n }\n const reader = upstream.body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += new TextDecoder().decode(value, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line.startsWith(\"data:\")) continue;\n const payload = line.slice(5).trim();\n if (payload === \"[DONE]\") {\n res.write(translator.finish());\n } else {\n try {\n res.write(translator.feed(JSON.parse(payload) as Parameters<typeof translator.feed>[0]));\n } catch {\n // skip a malformed chunk rather than kill the stream\n }\n }\n }\n }\n } finally {\n res.write(translator.finish()); // no-op when [DONE] already finished it\n res.end();\n }\n return;\n }\n\n const json = (await upstream.json().catch(() => null)) as unknown;\n if (!json) {\n fail(502, \"upstream returned a non-JSON body\");\n return;\n }\n res.writeHead(200, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(openAiToAnthropic(json as Parameters<typeof openAiToAnthropic>[0], model)));\n}\n\n// ---------------------------------------------------------------------------\n// Start / stop\n// ---------------------------------------------------------------------------\n\n/** Listen on host:port (loopback). Resolves with the bound port. */\nexport function listenProxy(server: Server, port = DEFAULT_PROXY_PORT): Promise<number> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, HOST, () => {\n const addr = server.address();\n resolve(typeof addr === \"object\" && addr ? addr.port : port);\n });\n });\n}\n\n/** True when something already accepts connections on the port. */\nexport function proxyListening(port = DEFAULT_PROXY_PORT, timeoutMs = 400): Promise<boolean> {\n return new Promise((resolve) => {\n const sock = connect({ port, host: HOST });\n const done = (ok: boolean) => {\n sock.removeAllListeners();\n sock.destroy();\n resolve(ok);\n };\n sock.setTimeout(timeoutMs, () => done(false));\n sock.once(\"connect\", () => done(true));\n sock.once(\"error\", () => done(false));\n });\n}\n\n/**\n * Where the standalone proxy artefact lives, found from this module's own\n * location (dist/cli bundle → ../hooks/, dev checkout → <repo>/dist/hooks).\n */\nexport function standaloneProxyPath(): string | null {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 6; i++) {\n for (const cand of [\n join(dir, \"worker-proxy.mjs\"),\n join(dir, \"hooks\", \"worker-proxy.mjs\"),\n join(dir, \"dist\", \"hooks\", \"worker-proxy.mjs\"),\n ]) {\n if (existsSync(cand)) return cand;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/**\n * Ensure a proxy is listening on the port; start the detached standalone when\n * nothing answers. Returns the base URL (`http://127.0.0.1:<port>`).\n */\nexport async function ensureProxyRunning(port: number, logDir: string): Promise<string> {\n if (await proxyListening(port)) return `http://${HOST}:${port}`;\n const script = standaloneProxyPath();\n if (!script) {\n throw new Error(\n `the PAI worker proxy is not running and its build was not found — ` +\n `run \\`bun run build\\` (or start one with: pai worker proxy --port ${port})`\n );\n }\n if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n const child = spawn(process.execPath, [script, \"--port\", String(port)], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n writeFileSync(proxyPidPath(logDir), `${child.pid}\\n`, \"utf8\");\n for (let i = 0; i < 40; i++) {\n if (await proxyListening(port)) return `http://${HOST}:${port}`;\n await new Promise((r) => setTimeout(r, 250));\n }\n throw new Error(`the PAI worker proxy did not come up on port ${port} (see ${proxyPidPath(logDir)})`);\n}\n\n/** `pai worker proxy stop`: SIGTERM the pid from the pid file. */\nexport function stopProxy(logDir: string): string {\n const path = proxyPidPath(logDir);\n if (!existsSync(path)) return \"no proxy pid file — nothing to stop\";\n const pid = parseInt(readFileSync(path, \"utf8\").trim(), 10);\n unlinkSync(path);\n if (!Number.isFinite(pid)) return \"stale proxy pid file removed\";\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n return `proxy pid ${pid} was not running (pid file removed)`;\n }\n return `proxy pid ${pid} stopped`;\n}\n","/**\n * scope.ts — which workers \"belong\" to the terminal asking about them.\n *\n * Two identities, in priority order:\n *\n * 1. AIBroker's session registry (~/.aibroker/session-names.json): iTerm\n * session UUID → session name. The launching session's UUID is stored in\n * each worker's status file as `session.id`, so every pane of a named\n * session sees its workers — regardless of tab layout.\n * 2. The iTerm tab key (`w<window>t<tab>` prefix of ITERM_SESSION_ID): the\n * original heuristic, kept as the fallback when no registry entry\n * matches (AIBroker absent, unnamed session, non-iTerm terminal).\n *\n * If neither is available, viewers fall back to \"all workers\".\n *\n * A third case: workers spawned from a Claude Code Bash tool (the\n * orchestrator pattern) inherit the orchestrator's `ITERM_SESSION_ID`, which\n * identifies the orchestrator's own pane, not a pane for the sub-worker —\n * so it does not key the sub-worker's tab. Those record `spawnerSession`,\n * the orchestrator's claude session id bridged through the status line's\n * session map (see below), so their tab still claims them.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { loadStatus, type WorkerStatus } from \"./status.js\";\n\nexport const AIBROKER_REGISTRY = join(homedir(), \".aibroker\", \"session-names.json\");\n\n/**\n * `w<window>t<tab>` prefix of an iTerm session id, \"\" if empty or malformed.\n * Panes split from the same tab share this prefix and differ only in `p<n>`.\n */\nexport function tabKey(term: string): string {\n if (!term) return \"\";\n const head = term.split(\"p\", 1)[0];\n const parts = head.slice(1).split(\"t\");\n if (head.startsWith(\"w\") && parts.length === 2 && parts.every((p) => /^\\d+$/.test(p))) {\n return head;\n }\n return \"\";\n}\n\n/** Tab key of the iTerm tab this process runs in, \"\" if not inside iTerm2. */\nexport function currentTabKey(env: NodeJS.ProcessEnv = process.env): string {\n return tabKey(env.ITERM_SESSION_ID ?? \"\");\n}\n\n/** The iTerm UUID part of an ITERM_SESSION_ID (after the last colon). */\nexport function itermUuid(term: string): string {\n if (!term) return \"\";\n return term.split(\":\").pop() ?? \"\";\n}\n\nexport interface SessionIdentity {\n id: string;\n name: string;\n}\n\n/**\n * Resolve the AIBroker session for an ITERM_SESSION_ID. Reads the persistent\n * name registry (the same store `aibroker_rename` writes); returns null when\n * AIBroker is absent or the session is not in it — callers then use the tab\n * key, which is the pre-AIBroker behaviour.\n */\nexport function resolveSession(\n term: string,\n registryPath: string = AIBROKER_REGISTRY\n): SessionIdentity | null {\n const uuid = itermUuid(term);\n if (!uuid) return null;\n let names: Record<string, unknown>;\n try {\n if (!existsSync(registryPath)) return null;\n names = JSON.parse(readFileSync(registryPath, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n const name = names[uuid];\n if (typeof name !== \"string\" || !name) return null;\n return { id: uuid, name };\n}\n\n/**\n * True when `worker` was launched from `term` (the terminal asking about it).\n * Session-id match first; the tab key only when no registry entry matched —\n * a session without a name keeps the tab-scoped behaviour it always had.\n */\nexport function workerInScope(worker: WorkerStatus, term: string): boolean {\n if (!term) return false;\n const uuid = itermUuid(term);\n if (worker.session?.id && uuid) return worker.session.id === uuid;\n return tabKey(worker.term) === tabKey(term) && tabKey(term) !== \"\";\n}\n\n/**\n * Registry key for pane files: the session id when AIBroker knows this\n * terminal, else the tab key, else the raw iTerm UUID.\n */\nexport function scopeKey(term: string): string {\n const session = resolveSession(term);\n if (session) return session.id;\n return currentTabKey() || itermUuid(term);\n}\n\n/** `[Name]` when the worker has an AIBroker session name, else \"\". */\nexport function sessionTag(worker: { session?: { name?: string } | null }): string {\n return worker.session?.name ? `[${worker.session.name}]` : \"\";\n}\n\n// ---------------------------------------------------------------------------\n// spawner sessions — attribution for workers launched from a Claude Code Bash\n// ---------------------------------------------------------------------------\n\n/**\n * Claude Code exports neither ITERM_SESSION_ID nor its own session id to the\n * Bash tool, so a `pai worker run` from an orchestrator has no terminal to\n * record and its status would be unattributable. The status line is the one\n * process that sees both identities at once — the payload's session_id and\n * the tab's ITERM_SESSION_ID — so it bridges them: every refresh writes this\n * cwd-keyed map, and the runner reads it back at spawn time.\n */\nexport interface SessionMapEntry {\n session: string;\n ts: number;\n /**\n * The tab's ITERM_SESSION_ID, when the status line could see one — the\n * bridge back from a claude session id to the terminal it runs in\n * (supervision uses it to push worker events into that terminal).\n * Absent on entries written before the field existed.\n */\n term?: string;\n}\n\n/** How old a map entry may be for a spawn to adopt it (status lines refresh constantly while a session lives). */\nexport const SPAWNER_SESSION_TTL_MS = 10 * 60_000;\n\n/** Map entries not refreshed within this window are pruned on write. */\nconst SESSION_MAP_PRUNE_MS = 60 * 60_000;\n\nexport function sessionMapPath(logDir: string): string {\n return join(logDir, \"claude-session-map.json\");\n}\n\n/**\n * Record that the claude session `session` renders its status line in `cwd`\n * (and, when known, in iTerm session `term`). Never throws — a broken map\n * must not break the bar. Prunes stale entries; skips the write when the\n * entry is unchanged and fresh.\n */\nexport function recordSessionMapEntry(\n logDir: string,\n cwd: string,\n session: string,\n term?: string,\n now: number = Date.now()\n): void {\n if (!cwd || !session) return;\n const path = sessionMapPath(logDir);\n let map: Record<string, SessionMapEntry> = {};\n try {\n if (existsSync(path)) {\n map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n }\n } catch {\n map = {}; // a damaged map is rewritten, never fatal\n }\n const prev = map[cwd];\n if (\n prev &&\n prev.session === session &&\n (prev.term ?? \"\") === (term ?? \"\") &&\n now - prev.ts < 60_000\n ) {\n return;\n }\n const pruned: Record<string, SessionMapEntry> = {};\n for (const [dir, e] of Object.entries(map)) {\n if (now - e.ts < SESSION_MAP_PRUNE_MS) pruned[dir] = e;\n }\n pruned[cwd] = { session, ts: now, ...(term ? { term } : {}) };\n try {\n mkdirSync(logDir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(pruned), \"utf8\");\n renameSync(tmp, path);\n } catch {\n // unwritable log dir: no attribution this round, nothing else breaks\n }\n}\n\n/**\n * The claude session a new run was spawned by, when it can be known: a run\n * inside another worker inherits its spawner (chain stages keep the\n * orchestrator's tab that way); otherwise a fresh map entry for `cwd`.\n */\nexport function resolveSpawnerSession(\n logDir: string,\n cwd: string,\n env: NodeJS.ProcessEnv = process.env,\n now: number = Date.now()\n): string | null {\n const parentId = env.PAI_WORKER_ID;\n if (parentId) {\n const inherited = loadStatus(logDir, parentId)?.spawnerSession;\n if (inherited) return inherited;\n }\n const path = sessionMapPath(logDir);\n if (!existsSync(path) || !cwd) return null;\n try {\n const entry = (JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>)[cwd];\n if (entry && entry.session && now - entry.ts < SPAWNER_SESSION_TTL_MS) return entry.session;\n } catch {\n // a damaged map simply attributes nothing\n }\n return null;\n}\n\n/**\n * The iTerm UUID a claude session last rendered its status line in, when the\n * map knows one: the freshest entry naming that session. This is the reverse\n * of resolveSpawnerSession — worker → orchestrator there, orchestrator →\n * terminal here — and it exists so daemon-side pushes (supervision) can reach\n * a session that has no iTerm identity of its own in any worker status.\n */\nexport function itermForClaudeSession(\n logDir: string,\n claudeSession: string,\n now: number = Date.now()\n): string | null {\n const path = sessionMapPath(logDir);\n if (!existsSync(path) || !claudeSession) return null;\n let map: Record<string, SessionMapEntry>;\n try {\n map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n } catch {\n return null;\n }\n let best: SessionMapEntry | null = null;\n for (const e of Object.values(map)) {\n if (e.session !== claudeSession || !e.term) continue;\n // a stale entry names a tab the session left; freshness orders them\n if (now - e.ts >= SESSION_MAP_PRUNE_MS) continue;\n if (!best || e.ts > best.ts) best = e;\n }\n const term = best?.term;\n return term ? itermUuid(term) : null;\n}\n","/**\n * Who or what ended a worker. One place decides the reason; the runner's\n * signal handlers and the deadline record it, `pai worker kill` and the daemon\n * leave a kill-request marker (`<logDir>/<id>.kill`) before signalling so the\n * runner can tell their stops from an external one.\n */\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { appendLedger } from \"./ledger.js\";\n\nexport type EndReason = \"exit\" | \"deadline\" | \"pai-worker-kill\" | \"parent-gone\" | \"external-signal\";\n\nexport interface EndInfo {\n reason: EndReason;\n by?: string;\n note?: string;\n}\n\nexport interface KillRequest {\n ts: string;\n by: string;\n pid: number;\n ppid: number;\n cwd: string;\n}\n\nexport const killMarkerPath = (logDir: string, id: string): string => join(logDir, `${id}.kill`);\n\n/** Best description of the invoking session: PAI parent/AIBroker session env, else the Claude Code session id. */\nexport function callerDescription(env: NodeJS.ProcessEnv = process.env): string {\n const name =\n env.PAI_WORKER_PARENT ?? env.PAI_SESSION_NAME ?? env.AIBROKER_SESSION_NAME ?? env.AIBROKER_SESSION;\n if (name) return name;\n if (env.CLAUDE_CODE_SESSION_ID) return `claude-session:${env.CLAUDE_CODE_SESSION_ID}`;\n return env.CLAUDECODE ? \"claude-code\" : \"shell\";\n}\n\n/** Write the kill-request marker and the WORKER-KILL ledger line; call right before signalling. */\nexport function requestKill(logDir: string, ledger: string, id: string, by: string = callerDescription()): void {\n const req: KillRequest = {\n ts: new Date().toISOString(),\n by,\n pid: process.pid,\n ppid: process.ppid,\n cwd: process.cwd(),\n };\n writeFileSync(killMarkerPath(logDir, id), JSON.stringify(req));\n appendLedger(ledger, \"WORKER-KILL\", { id, by, pid: req.pid, ppid: req.ppid, cwd: req.cwd });\n}\n\nfunction readMarker(logDir: string, id: string): KillRequest | null {\n const p = killMarkerPath(logDir, id);\n if (!existsSync(p)) return null;\n try {\n return JSON.parse(readFileSync(p, \"utf8\")) as KillRequest;\n } catch {\n return { ts: \"\", by: \"unknown\", pid: 0, ppid: 0, cwd: \"\" };\n } finally {\n try {\n unlinkSync(p);\n } catch {\n /* already gone */\n }\n }\n}\n\nexport function pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/** Command name of a pid (`ps -o comm=`), or undefined when it is gone or ps fails. */\nexport function commOf(pid: number): string | undefined {\n try {\n return execFileSync(\"ps\", [\"-o\", \"comm=\", \"-p\", String(pid)], { encoding: \"utf8\" }).trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Parent facts recorded at start, for the end report (\"parent: claude\"). */\nexport function parentInfo(): { parentPid: number; parentComm?: string; parentBackground?: boolean } {\n const parentPid = process.ppid;\n return {\n parentPid,\n parentComm: commOf(parentPid),\n // a Claude Code Bash task carries CLAUDECODE; the runner strips it from children only\n parentBackground: process.env.CLAUDECODE ? true : undefined,\n };\n}\n\n/**\n * The single decision. `signal` = the signal the runner received (absent for a\n * deadline stop or a normal exit); `timedOut` = the deadline fired. Reads (and\n * consumes) the kill marker.\n */\nexport function decideEndReason(a: {\n logDir: string;\n id: string;\n signal?: string;\n timedOut?: boolean;\n parentPid?: number;\n parentComm?: string;\n parentBackground?: boolean;\n isAlive?: (pid: number) => boolean;\n}): EndInfo {\n if (a.timedOut) return { reason: \"deadline\" };\n if (!a.signal) return { reason: \"exit\" };\n const marker = readMarker(a.logDir, a.id);\n if (marker) return { reason: \"pai-worker-kill\", by: marker.by, note: `${a.signal} requested by pid ${marker.pid}` };\n const alive = (a.isAlive ?? pidAlive)(a.parentPid ?? process.ppid);\n const parent = `parent ${a.parentPid ?? \"?\"}${a.parentComm ? ` (${a.parentComm})` : \"\"}`;\n if (!alive) return { reason: \"parent-gone\", note: `${a.signal}; ${parent} not alive` };\n return {\n reason: \"external-signal\",\n note:\n `${a.signal}; sender unknown (not deadline, not pai worker kill); a Claude Code background task stop ` +\n `(TaskStop, Bash timeout, session end) is the usual source; ${parent} alive` +\n (a.parentBackground ? \"; started from a Claude Code session\" : \"\"),\n };\n}\n\n/** The status/ledger fields for an end. */\nexport function endFields(e: EndInfo): { reason: EndReason; by?: string } {\n return { reason: e.reason, ...(e.by ? { by: e.by } : {}) };\n}\n\n/** \"external-signal\", \"pai-worker-kill by <caller>\" for a finished worker; null when nothing was recorded. */\nexport function endLabel(s: { endReason?: string; endBy?: string }): string | null {\n return s.endReason ? `${s.endReason}${s.endBy ? ` by ${s.endBy}` : \"\"}` : null;\n}\n","/**\n * controls.ts — the clickr argv for handing desktop control to a worker or\n * taking it back, shared by `pai worker controls` and the follow pane's\n * \"your controls\" / \"my controls\" chat action, so both go through clickr the\n * same way.\n *\n * The grant is exclusive to one worker at a time: clickr's MCP server checks\n * PAI_WORKER_ID/pid against the `--agent <id> --pid <pid>` it was handed\n * control for. Crash paths are covered by clickr's own pid check; a clean\n * exit is covered by returnControlsIfHeld below, called from every exit path\n * of run.ts's runWorker.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { loadStatus, saveStatus } from \"./status.js\";\n\n/** clickr argv for `who`: `you` binds the grant to `id` (and its runner pid, when known); `me` has no target. */\nexport function clickrControlsArgv(id: string, who: \"you\" | \"me\", pid?: number): string[] {\n if (who === \"me\") return [\"controls\", \"me\"];\n const argv = [\"controls\", \"you\", \"--agent\", id];\n if (pid !== undefined) argv.push(\"--pid\", String(pid));\n return argv;\n}\n\n/** clickr argv to hand a worker's grant back on its exit. */\nexport function clickrControlsReturnArgv(id: string): string[] {\n return [\"controls\", \"return\", \"--agent\", id];\n}\n\n/** Record that `id` was granted clickr desktop controls (`who: \"you\"` succeeded). */\nexport function markControlsHeld(logDir: string, id: string): void {\n const status = loadStatus(logDir, id);\n if (!status) return;\n status.controlsHeld = true;\n saveStatus(logDir, status);\n}\n\n/**\n * Best-effort hand desktop controls back on a worker's exit, when it ever\n * held them. Never throws and never hangs past clickr's own exit or a 3s\n * cap — a missing clickr binary or a stuck process must not hold up the\n * worker's own exit.\n */\nexport async function returnControlsIfHeld(logDir: string, id: string | undefined): Promise<void> {\n if (!id) return;\n const status = loadStatus(logDir, id);\n if (!status?.controlsHeld) return;\n await new Promise<void>((resolve) => {\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n resolve();\n };\n try {\n const proc = spawn(\"clickr\", clickrControlsReturnArgv(id), { stdio: \"ignore\" });\n proc.on(\"close\", finish);\n proc.on(\"error\", finish);\n } catch {\n finish();\n }\n setTimeout(finish, 3000).unref();\n });\n}\n","/**\n * engines/image.ts — the `engine: image` worker.\n *\n * No claude-code process, no worktree, no MCP: `pai worker run --capability\n * image` POSTs one OpenAI-compatible images-generations request straight to\n * the provider's `url` and writes the PNG it gets back. One request, no\n * retries beyond the fetch's own timeout — a caller that wants another try\n * just runs again.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { resolveProviderKey, WorkersConfigError, type WorkerProvider } from \"../config.js\";\n\nexport interface ImageRunOptions {\n providerName: string;\n provider: WorkerProvider;\n model: string;\n prompt: string;\n /** Where the PNG is written; caller resolves the default path. */\n outPath: string;\n /** \"WIDTHxHEIGHT\", default 1024x1024. */\n size?: string;\n /** Default 120000 (2 minutes). */\n timeoutMs?: number;\n}\n\nexport interface ImageRunResult {\n ok: true;\n path: string;\n model: string;\n provider: string;\n durationMs: number;\n bytes: number;\n}\n\nconst DEFAULT_SIZE = \"1024x1024\";\nexport const DEFAULT_IMAGE_TIMEOUT_MS = 120_000;\n\ninterface ImagesGenerationsResponse {\n data?: { b64_json?: string }[];\n}\n\n/**\n * `POST {url}/images/generations` with `{model, prompt, size, n: 1,\n * response_format: \"b64_json\"}` and the provider's bearer token, decode the\n * base64 PNG it returns, and write it to `outPath`.\n */\nexport async function runImageCapability(opts: ImageRunOptions): Promise<ImageRunResult> {\n if (!opts.provider.baseUrl) {\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\" (engine image) has no \"url\" configured — set one with: ` +\n `pai worker providers update ${opts.providerName} --base-url <url>`\n );\n }\n const token = resolveProviderKey(opts.provider);\n const url = `${opts.provider.baseUrl.replace(/\\/+$/, \"\")}/images/generations`;\n const timeoutMs = opts.timeoutMs ?? DEFAULT_IMAGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const t0 = Date.now();\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(token ? { authorization: `Bearer ${token}` } : {}),\n },\n body: JSON.stringify({\n model: opts.model,\n prompt: opts.prompt,\n size: opts.size ?? DEFAULT_SIZE,\n n: 1,\n response_format: \"b64_json\",\n }),\n signal: controller.signal,\n });\n } catch (e) {\n if (e instanceof Error && e.name === \"AbortError\") {\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\": image request timed out after ${timeoutMs}ms`\n );\n }\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\": image request failed: ${e instanceof Error ? e.message : String(e)}`\n );\n } finally {\n clearTimeout(timer);\n }\n const durationMs = Date.now() - t0;\n const text = await res.text();\n if (!res.ok) {\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\": image request failed (${res.status}): ${text.slice(0, 500)}`\n );\n }\n let parsed: ImagesGenerationsResponse;\n try {\n parsed = JSON.parse(text) as ImagesGenerationsResponse;\n } catch {\n throw new WorkersConfigError(`provider \"${opts.providerName}\": image response was not JSON`);\n }\n const b64 = parsed.data?.[0]?.b64_json;\n if (!b64) {\n throw new WorkersConfigError(`provider \"${opts.providerName}\": image response had no data[0].b64_json`);\n }\n const bytes = Buffer.from(b64, \"base64\");\n mkdirSync(dirname(opts.outPath), { recursive: true });\n writeFileSync(opts.outPath, bytes);\n return {\n ok: true,\n path: opts.outPath,\n model: opts.model,\n provider: opts.providerName,\n durationMs,\n bytes: bytes.length,\n };\n}\n","/**\n * pane.ts — the per-worker follow pane in iTerm2.\n *\n * One small-font pane per worker, stacked in a right-hand column: the first\n * worker of a scope splits the launching session vertically, every further\n * one splits the lowest live worker pane horizontally, so panes stack top to\n * bottom. The split never sizes the new session — that would grow the whole\n * window; instead the window's bounds are read before the split and restored\n * right after, so the panes share the space the window already had. The follow\n * command is part of the split itself — iTerm creates the new session already\n * running it — so no text is ever typed into any session afterwards; a separate\n * typing step raced the operator's keystrokes (focus briefly sits on the new\n * pane after a split, and in one observed failure the command landed in the\n * operator's own shell). A split still makes the new session the tab's active\n * one — the scripts re-select the launching session right after, so a pane\n * opening never steals the keystrokes the operator is typing. Each\n * pane runs `pai worker follow <id> --auto-exit <n>` under the `pai-worker`\n * dynamic profile (Close Sessions On End), so panes disappear by themselves.\n *\n * Panes are tracked per scope (AIBroker session id, else tab key) in\n * <logDir>/panes/<key>/<worker id>.json (one file per worker), iTerm session unique id, newest first —\n * the split lands below the lowest live worker pane.\n *\n * The dynamic profile's font is the family of iTerm's DEFAULT profile (the\n * `Default Bookmark Guid` entry in New Bookmarks) at workers.pane.fontSize —\n * never the launching session's profile, which the Python version did and\n * which produced iTerm's \"unknown parent name\" dialog whenever the two\n * differed. `Dynamic Profile Parent Name` is only written when that name\n * actually exists in New Bookmarks; when iTerm's preferences cannot be read\n * the profile is still written, with font Menlo-Regular and no parent.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { execFileSync } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n mkdtempSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { WorkersConfig } from \"./config.js\";\nimport { panesDir } from \"./paths.js\";\nimport { itermUuid, scopeKey } from \"./scope.js\";\n\nexport const PROFILE_NAME = \"pai-worker\";\n\n/** Where the dynamic profile lives (env override keeps tests off the real one). */\nexport function dynamicProfilePath(): string {\n return process.env.PAI_WORKER_PROFILE ?? join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"iTerm2\",\n \"DynamicProfiles\",\n \"pai-worker.json\"\n );\n}\n\n// ---------------------------------------------------------------------------\n// JXA scripts (arguments are passed as argv items, never interpolated).\n// JXA, not AppleScript: iTerm's AppleScript window collection can get stuck\n// failing every enumeration (\"every window doesn't understand the count\n// message\", -1708) while the JXA object model keeps answering.\n// ---------------------------------------------------------------------------\n\n// Shared prelude: locate the window/tab/session with a given unique id.\nconst FIND_SESSION = `function findSession(app, id) {\n var ws = app.windows();\n for (var i = 0; i < ws.length; i++) {\n var ts = ws[i].tabs();\n for (var j = 0; j < ts.length; j++) {\n var ss = ts[j].sessions();\n for (var k = 0; k < ss.length; k++) {\n if (ss[k].id() === id) return { w: ws[i], t: ts[j], s: ss[k], all: ss };\n }\n }\n }\n return null;\n}`;\n\n// One pane per worker: split the launching session vertically, or the lowest\n// live candidate horizontally. Returns \"<live ids>,|<new session id>\".\n// Exported for the script-content tests (window size, argv-only arguments).\nexport const WORKER_SPLIT_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var targetID = argv[0], candList = argv[1], followCmd = argv[2], profileName = argv[3];\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, targetID);\n if (!f) return \"notfound\";\n // sizing the new session would grow the whole window: pin the window's\n // bounds now and restore them after the split, so the panes share the\n // existing space\n var winBounds = f.w.bounds();\n var sessIDs = f.all.map(function (o) { return o.id(); });\n var lived = [];\n var splitS = null;\n (candList === \"\" ? [] : candList.split(\",\")).forEach(function (cid) {\n var at = sessIDs.indexOf(cid);\n if (at < 0) return;\n lived.push(cid);\n if (!splitS) splitS = f.all[at];\n });\n // the follow command is part of the split itself: the pane is born already\n // running it, so no text is ever typed into any session — a typing step\n // raced the operator's keystrokes and could even land in the operator's\n // own session\n var newS;\n if (!splitS) {\n newS = profileName === \"\"\n ? f.s.splitVerticallyWithDefaultProfile({ command: followCmd })\n : f.s.splitVertically({ withProfile: profileName, command: followCmd });\n } else {\n newS = profileName === \"\"\n ? splitS.splitHorizontallyWithDefaultProfile({ command: followCmd })\n : splitS.splitHorizontally({ withProfile: profileName, command: followCmd });\n }\n // a split makes the new session the tab's active one: re-select the\n // launching session so focus returns to where the operator was typing,\n // never the new pane\n try { f.s.select(); } catch (e) {}\n try { f.w.bounds = winBounds; } catch (e) {}\n return lived.map(function (l) { return l + \",\"; }).join(\"\") + \"|\" + newS.id();\n}`;\n\n// TTys of every session in the launching session's tab (no-worker pane variant),\n// comma-joined.\nconst TAB_TTYS_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n return f.all.map(function (o) { return o.tty(); }).join(\",\");\n}`;\n\n// Bounds (x1, y1, x2, y2, comma-joined) of the window hosting one iTerm\n// session — read-only, for `pai worker pane <id> --check`. Exported for the\n// script-content tests (reads bounds, never sets them).\nexport const WINDOW_BOUNDS_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n var b = f.w.bounds();\n return [b.x, b.y, b.x + b.width, b.y + b.height].join(\", \");\n}`;\n\n// Split the launching session vertically and run followCmd in the new pane.\n// Exported for the script-content tests (focus stays on the launching session).\nexport const SPLIT_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var followCmd = argv[1];\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n // the follow command is part of the split itself (see WORKER_SPLIT_SCRIPT):\n // nothing is ever typed into a session afterwards\n f.s.splitVerticallyWithDefaultProfile({ command: followCmd });\n // keep the tab's active session where it was (see WORKER_SPLIT_SCRIPT)\n try { f.s.select(); } catch (e) {}\n return \"opened\";\n}`;\n\n// ---------------------------------------------------------------------------\n// osascript / ps / defaults helpers\n// ---------------------------------------------------------------------------\n\n/** Run a JXA script with argv. Rejects when osascript itself cannot run. */\nfunction osascript(script: string, args: string[]): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n const proc = spawn(\"osascript\", [\"-l\", \"JavaScript\", \"-\", ...args], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n let out = \"\";\n let err = \"\";\n proc.stdout.on(\"data\", (c: Buffer) => (out += c.toString(\"utf8\")));\n proc.stderr.on(\"data\", (c: Buffer) => (err += c.toString(\"utf8\")));\n proc.on(\"error\", reject);\n proc.on(\"close\", () => resolve({ stdout: out, stderr: err }));\n proc.stdin.write(script);\n proc.stdin.end();\n });\n}\n\nfunction psOutput(format: string): string {\n try {\n return execFileSync(\"ps\", [\"-axo\", format], { encoding: \"utf8\" });\n } catch {\n return \"\";\n }\n}\n\n/** TTys of running `worker follow` processes, normalised to /dev/ttysNNN. */\nfunction followTtys(): Set<string> {\n const ttys = new Set<string>();\n for (const line of psOutput(\"tty=,command=\").split(\"\\n\")) {\n const parts = line.trim().split(/\\s+/, 2);\n if (parts.length < 2 || parts[0] === \"ps\") continue;\n if (!/(^|\\/)(worker-follow|pai worker follow|glm-ps follow)\\b/.test(parts[1])) continue;\n if (parts[0].startsWith(\"ttys\")) ttys.add(`/dev/${parts[0]}`);\n }\n return ttys;\n}\n\n/** True while a `follow <wid>` process runs (its pane shows that worker). */\nexport function workerPaneOpen(wid: string): boolean {\n const pat = new RegExp(`(?:worker follow|worker-follow|glm-ps follow) ${wid.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b`);\n return psOutput(\"command=\").split(\"\\n\").some((ln) => pat.test(ln));\n}\n\n/** One entry of iTerm's New Bookmarks (the fields the pane cares about). */\nexport interface Bookmark {\n Name?: string;\n Guid?: string;\n \"Normal Font\"?: string;\n}\n\n/** What reading iTerm's preferences yielded — `error` carries the reason. */\nexport interface PrefsRead {\n bookmarks: Bookmark[];\n defaultGuid: string | null;\n error: string | null;\n}\n\n/** plutil's message for an ExecFileSync failure, else the exception text. */\nfunction toolErr(e: unknown): string {\n const err = e as { stderr?: string | Buffer; message?: string };\n const stderr = typeof err.stderr === \"string\" ? err.stderr : err.stderr?.toString(\"utf8\");\n return (stderr || err.message || String(e)).trim();\n}\n\n/**\n * Read New Bookmarks and Default Bookmark Guid from an iTerm preferences\n * plist. Key-scoped `plutil -extract`, never a whole-file JSON conversion:\n * real iTerm preferences carry `<date>` objects (SULastCheckTime & friends)\n * which `plutil -convert json` rejects outright — \"Invalid object in plist\n * for JSON format\" — so the whole-file read always failed and the dynamic\n * profile was never written.\n */\nexport function readItermPlist(plistPath: string): PrefsRead {\n let bookmarks: Bookmark[] = [];\n try {\n const out = execFileSync(\n \"plutil\",\n [\"-extract\", \"New Bookmarks\", \"json\", \"-o\", \"-\", plistPath],\n { encoding: \"utf8\", timeout: 10_000 }\n );\n const parsed = JSON.parse(out) as unknown;\n if (Array.isArray(parsed)) bookmarks = parsed as Bookmark[];\n } catch (e) {\n return { bookmarks: [], defaultGuid: null, error: `extracting New Bookmarks: ${toolErr(e)}` };\n }\n let defaultGuid: string | null = null;\n try {\n const out = execFileSync(\n \"plutil\",\n [\"-extract\", \"Default Bookmark Guid\", \"raw\", \"-o\", \"-\", plistPath],\n { encoding: \"utf8\", timeout: 10_000 }\n );\n defaultGuid = out.trim() || null;\n } catch {\n // no default guid set — defaultBookmarkFrom() yields null, fine\n }\n return { bookmarks, defaultGuid, error: null };\n}\n\n/** iTerm's exported preferences, via a temp plist (no shell pipe involved). */\nexport function itermPrefs(): PrefsRead {\n let dir: string | null = null;\n try {\n dir = mkdtempSync(join(tmpdir(), \"pai-iterm-\"));\n const plist = join(dir, \"iterm2.plist\");\n execFileSync(\"defaults\", [\"export\", \"com.googlecode.iterm2\", plist], {\n encoding: \"utf8\",\n timeout: 10_000,\n });\n return readItermPlist(plist);\n } catch (e) {\n return { bookmarks: [], defaultGuid: null, error: `defaults export: ${toolErr(e)}` };\n } finally {\n if (dir) {\n try {\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // best effort cleanup of a temp dir\n }\n }\n }\n}\n\n/** The New Bookmarks entry of that profile name, null if absent. */\nfunction bookmarkFrom(read: PrefsRead, named: string): Bookmark | null {\n return read.bookmarks.find((b) => b.Name === named) ?? null;\n}\n\n/** The bookmark iTerm marks default, null when it is missing or unreadable. */\nfunction defaultBookmarkFrom(read: PrefsRead): Bookmark | null {\n return read.defaultGuid\n ? read.bookmarks.find((b) => b.Guid === read.defaultGuid) ?? null\n : null;\n}\n\n/** The default profile, read fresh from iTerm's preferences. */\nexport function defaultBookmark(): Bookmark | null {\n return defaultBookmarkFrom(itermPrefs());\n}\n\n/**\n * The pane profile's font: the family of iTerm's default profile at\n * `fontSize` points (\"MesloLGLNFM-Regular 18\" + 13 → \"MesloLGLNFM-Regular 13\"),\n * or \"Menlo-Regular <fontSize>\" when the default font cannot be read.\n */\nexport function paneFont(font: string | undefined, fontSize: number): string {\n const idx = (font ?? \"\").lastIndexOf(\" \");\n if (idx > 0) {\n const family = font!.slice(0, idx);\n if (!Number.isNaN(parseFloat(font!.slice(idx + 1)))) return `${family} ${fontSize}`;\n }\n return `Menlo-Regular ${fontSize}`;\n}\n\n/** Write the pai-worker dynamic profile; iTerm2 loads that directory itself. */\nexport function writeDynamicProfile(\n parent: Bookmark | null,\n fontSize: number,\n read: () => PrefsRead = itermPrefs\n): void {\n const profile: Record<string, unknown> = {\n Name: PROFILE_NAME,\n Guid: \"pai-worker-dynamic-profile\",\n \"Normal Font\": paneFont(parent?.[\"Normal Font\"], fontSize),\n \"Close Sessions On End\": true,\n };\n // Parent name only when iTerm actually has that profile loaded — a name it\n // does not know makes every split open an error dialog instead of a pane.\n if (parent?.Name && bookmarkFrom(read(), parent.Name)) {\n profile[\"Dynamic Profile Parent Name\"] = parent.Name;\n }\n const path = dynamicProfilePath();\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify({ Profiles: [profile] }, null, 2) + \"\\n\", \"utf8\");\n renameSync(tmp, path);\n}\n\nlet warnedPrefs = false;\n\n/**\n * Profile for new worker panes, creating the small-font one on demand.\n * \"\" means \"split with the default profile\" (also the fallback when the\n * dynamic profile does not show up in time).\n *\n * The profile file is written whenever it is missing — even when iTerm's\n * preferences cannot be read; then it gets font \"Menlo-Regular <fontSize>\"\n * and no parent, and the reason is logged to stderr once.\n */\nexport async function followProfile(\n fontSize: number,\n read: () => PrefsRead = itermPrefs\n): Promise<string> {\n const first = read();\n const parent = defaultBookmarkFrom(first);\n const path = dynamicProfilePath();\n const existed = existsSync(path);\n if (!existed) writeDynamicProfile(parent, fontSize, () => first);\n if (!parent) {\n if (!warnedPrefs) {\n warnedPrefs = true;\n const reason = first.error ?? \"no profile is marked default\";\n const what = existed\n ? `keeping ${path} as-is`\n : `wrote ${path} with Menlo-Regular ${fontSize} and no parent`;\n process.stderr.write(\n `pai worker pane: cannot read iTerm's default profile (${reason}) — ${what}\\n`\n );\n }\n if (first.error) return \"\"; // polling cannot succeed against unreadable prefs\n }\n for (let i = 0; i < 10; i++) {\n // iTerm2 picks DynamicProfiles up quickly; allow it 2 s\n if (bookmarkFrom(read(), PROFILE_NAME)) return PROFILE_NAME;\n await new Promise((r) => setTimeout(r, 200));\n }\n process.stderr.write(`pai worker pane: profile ${PROFILE_NAME} not visible, splitting with default\\n`);\n return \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Pane registry\n// ---------------------------------------------------------------------------\n\nexport interface PaneEntry {\n session: string;\n worker: string;\n opened: string;\n /** the scope (registry key) the entry was opened under */\n scope?: string;\n}\n\n/**\n * One file per worker, `<panes>/<scope>/<worker id>.json`: concurrent opens\n * for the same scope write different files, so none can be lost (the former\n * shared `<scope>.json` list was a read-modify-write that dropped entries when\n * workers started a second apart). Legacy `<scope>.json` lists are still read.\n */\nfunction readEntry(path: string, scope: string): PaneEntry[] {\n try {\n const v = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n return (Array.isArray(v) ? v : [v])\n .filter((e): e is PaneEntry => !!e && typeof (e as PaneEntry).session === \"string\" && typeof (e as PaneEntry).worker === \"string\")\n .map((e) => ({ ...e, scope }));\n } catch {\n return [];\n }\n}\n\n/** Registry entries of one scope (`scope` set) or of all scopes, oldest first. */\nexport function loadRegistry(logDir: string, scope?: string): PaneEntry[] {\n const root = panesDir(logDir);\n const out: PaneEntry[] = [];\n let names: string[] = [];\n try {\n names = readdirSync(root);\n } catch {\n return out;\n }\n for (const name of names.sort()) {\n const full = join(root, name);\n if (name.endsWith(\".json\")) {\n const sc = name.slice(0, -5);\n if (!scope || sc === scope) out.push(...readEntry(full, sc));\n } else if (!scope || name === scope) {\n try {\n for (const f of readdirSync(full)) if (f.endsWith(\".json\")) out.push(...readEntry(join(full, f), name));\n } catch {\n /* not a directory */\n }\n }\n }\n return out.sort((x, y) => x.opened.localeCompare(y.opened));\n}\n\nfunction entryPath(logDir: string, e: PaneEntry): string {\n return join(panesDir(logDir), e.scope ?? \"\", `${e.worker}.json`);\n}\n\nfunction saveEntry(logDir: string, e: PaneEntry): void {\n const path = entryPath(logDir, e);\n mkdirSync(dirname(path), { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify({ session: e.session, worker: e.worker, opened: e.opened }, null, 1), \"utf8\");\n renameSync(tmp, path);\n}\n\n/** Forget an entry (its per-worker file; a legacy list is rewritten without it). */\nfunction dropEntry(logDir: string, e: PaneEntry): void {\n const legacy = join(panesDir(logDir), `${e.scope}.json`);\n rmSync(entryPath(logDir, e), { force: true });\n if (existsSync(legacy)) {\n const rest = readEntry(legacy, e.scope ?? \"\").filter((x) => x.session !== e.session);\n writeFileSync(legacy, JSON.stringify(rest.map(({ scope: _s, ...x }) => x), null, 1), \"utf8\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Ending panes of finished workers\n// ---------------------------------------------------------------------------\n\nconst LIST_SESSIONS_SCRIPT = `function run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"\";\n var out = \"\";\n app.windows().forEach(function (w) {\n w.tabs().forEach(function (t) {\n t.sessions().forEach(function (s) {\n out += s.id() + \"\\t\" + s.tty() + \"\\t\" + s.name() + \"\\n\";\n });\n });\n });\n return out;\n}`;\n\n// Ends one session by its unique id, never a window or the app.\nconst END_SESSION_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n f.s.close();\n return \"closed\";\n}`;\n\n/** The real iTerm / tmux calls, swappable so tests never touch either. */\nexport interface PaneOps {\n listIterm(): Promise<Array<{ id: string; name: string }>>;\n closeIterm(id: string): Promise<boolean>;\n listTmux(): Array<{ id: string; command: string }>;\n closeTmux(id: string): boolean;\n}\n\nexport const realPaneOps: PaneOps = {\n async listIterm() {\n if (process.platform !== \"darwin\") return [];\n try {\n const p = await osascript(LIST_SESSIONS_SCRIPT, []);\n // iTerm renames a pane after its running command (\"node\"), so the follow\n // command is looked up by the session's tty instead\n const cmdByTty = new Map<string, string>();\n for (const line of psOutput(\"tty=,command=\").split(\"\\n\")) {\n const [tty, ...rest] = line.trim().split(/\\s+/);\n if (tty && tty !== \"??\") cmdByTty.set(`/dev/${tty}`, rest.join(\" \"));\n }\n return p.stdout\n .split(\"\\n\")\n .filter(Boolean)\n .map((l) => {\n const [id, tty, ...name] = l.split(\"\\t\");\n return { id, name: `${name.join(\"\\t\")} ${cmdByTty.get(tty) ?? \"\"}`.trim() };\n });\n } catch {\n return [];\n }\n },\n async closeIterm(id) {\n const p = await osascript(END_SESSION_SCRIPT, [id]);\n return p.stdout.trim() === \"closed\";\n },\n listTmux() {\n try {\n const out = execFileSync(\"tmux\", [\"list-panes\", \"-a\", \"-F\", \"#{pane_id}\\t#{pane_start_command}\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n return out.split(\"\\n\").filter(Boolean).map((l) => ({ id: l.split(\"\\t\")[0], command: l.split(\"\\t\").slice(1).join(\" \") }));\n } catch {\n return [];\n }\n },\n closeTmux(id) {\n try {\n execFileSync(\"tmux\", [\"kill-pane\", \"-t\", id], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n },\n};\n\nconst FOLLOW_ID = /worker[- ]follow (\\d{8}-\\d{6}-\\d+)/;\n\n/** End one worker's registered iTerm pane, found by worker id. True if a pane was closed. */\nexport async function closePaneForWorker(logDir: string, wid: string, ops: PaneOps = realPaneOps): Promise<boolean> {\n let closed = false;\n for (const e of loadRegistry(logDir).filter((x) => x.worker === wid)) {\n try {\n if (await ops.closeIterm(e.session)) closed = true;\n } catch {\n /* iTerm gone: the entry is stale either way */\n }\n dropEntry(logDir, e);\n }\n return closed;\n}\n\nexport interface CloseEndedResult {\n closed: string[];\n kept: string[];\n}\n\n/**\n * Close every open follow pane whose worker has ended: registry entries first\n * (by worker id), then any iTerm session named after / tmux pane started with a\n * `worker follow <id>` command of an ended worker. `ended(id)` says whether\n * that worker is over; unknown ids are never closed. `dryRun` only reports.\n */\nexport async function closeEndedPanes(\n logDir: string,\n ended: (wid: string) => boolean,\n dryRun: boolean,\n ops: PaneOps = realPaneOps\n): Promise<CloseEndedResult> {\n const res: CloseEndedResult = { closed: [], kept: [] };\n const seen = new Set<string>();\n const handle = async (backend: \"iterm\" | \"tmux\", id: string, worker: string, entry?: PaneEntry): Promise<void> => {\n const key = `${backend}:${id}`;\n if (seen.has(key)) return;\n seen.add(key);\n if (!ended(worker)) {\n res.kept.push(`${worker} ${key} (running)`);\n return;\n }\n if (!dryRun) {\n const ok = backend === \"tmux\" ? ops.closeTmux(id) : await ops.closeIterm(id).catch(() => false);\n if (entry) dropEntry(logDir, entry); // stale whether or not the pane still existed\n if (!ok && !entry) return;\n }\n res.closed.push(`${worker} ${key}${dryRun ? \" (dry run)\" : \"\"}`);\n };\n const sessions = await ops.listIterm();\n const open = new Set(sessions.map((s) => s.id));\n for (const e of loadRegistry(logDir)) {\n // iTerm answered and the session is gone: a stale entry, forgotten without a close\n if (sessions.length && !open.has(e.session)) {\n if (!dryRun) dropEntry(logDir, e);\n continue;\n }\n await handle(\"iterm\", e.session, e.worker, e);\n }\n for (const s of sessions) {\n const m = FOLLOW_ID.exec(s.name);\n if (m) await handle(\"iterm\", s.id, m[1]);\n }\n for (const t of ops.listTmux()) {\n const m = FOLLOW_ID.exec(t.command);\n if (m) await handle(\"tmux\", t.id, m[1]);\n }\n return res;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry points\n// ---------------------------------------------------------------------------\n\n/**\n * The command a worker pane runs: one exec-able line with no shell syntax —\n * iTerm's split command parameter execs without a shell (proven live: panes\n * survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on\n * `export …; …` or a redirect), so the command must be one exec-able line.\n * Absolute node on the CLI entry file also sidesteps the panes' bare launchd\n * PATH (no pai, no node) and the `#!/usr/bin/env node` shebang.\n */\nexport function followCommand(wid: string, autoExitSecs: number): string {\n return `${process.execPath} ${absoluteCliPath()} worker follow ${wid} --auto-exit ${autoExitSecs}`;\n}\n\n/** Absolute path of the running pai CLI; falls back to PATH lookup at spawn. */\nfunction absoluteCliPath(): string {\n const argv1 = process.argv[1] ?? \"\";\n if (argv1.endsWith(\"pai\")) return argv1;\n return \"pai\";\n}\n\nexport type PaneBackend = \"tmux\" | \"iterm\" | \"none\";\n\n/** The one place that decides where a follow pane opens: tmux (any OS) > iTerm2 (macOS) > nowhere. */\nexport function paneBackend(env: NodeJS.ProcessEnv = process.env, platform: string = process.platform): PaneBackend {\n if (env.TMUX) return \"tmux\";\n if (platform === \"darwin\" && env.ITERM_SESSION_ID) return \"iterm\";\n return \"none\";\n}\n\nexport function noPaneMessage(wid?: string): string {\n return `no pane support here, use: pai worker follow${wid ? ` ${wid}` : \"\"}`;\n}\n\n/** Split the current tmux window horizontally (right-hand column) and run cmd in it, focus stays put. */\nfunction tmuxSplit(cmd: string): void {\n const target = process.env.TMUX_PANE ? [\"-t\", process.env.TMUX_PANE] : [];\n try {\n execFileSync(\"tmux\", [\"split-window\", \"-h\", \"-d\", \"-l\", \"40%\", ...target, cmd], { stdio: \"pipe\" });\n } catch (e) {\n const err = e as { stderr?: Buffer; message?: string };\n throw new Error(`pai worker pane: tmux split-window failed: ${(err.stderr?.toString() || err.message || \"\").trim().slice(0, 200)}`);\n }\n}\n\n/** Outcome of opening a worker's follow pane: the report line plus, on a fresh open, the new iTerm session id. */\nexport interface PaneOpenResult {\n message: string;\n session: string | null;\n}\n\n/** Open (or only report on) the stacked follow pane of one worker. */\nexport async function openPaneForWorker(\n logDir: string,\n config: WorkersConfig,\n wid: string,\n term: string\n): Promise<PaneOpenResult> {\n if (workerPaneOpen(wid)) return { message: `pane for ${wid} already open`, session: null };\n const backend = paneBackend();\n if (backend === \"none\") return { message: noPaneMessage(wid), session: null };\n if (backend === \"tmux\") {\n tmuxSplit(followCommand(wid, config.pane.autoExitSecs));\n return { message: `pane opened for ${wid}`, session: null };\n }\n const uid = itermUuid(term);\n const scope = scopeKey(term);\n const reg = loadRegistry(logDir, scope);\n const profile = await followProfile(config.pane.fontSize);\n const cmd = followCommand(wid, config.pane.autoExitSecs);\n // registry newest first: the split lands below the lowest live worker pane\n const cands = [...reg].reverse().map((e) => e.session).join(\",\");\n const p = await osascript(WORKER_SPLIT_SCRIPT, [uid, cands, cmd, profile]);\n const out = p.stdout.trim();\n if (out === \"notfound\" || out === \"notrunning\") {\n throw new Error(\n out === \"notfound\"\n ? \"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\"\n : \"pai worker pane: iTerm2 is not running\"\n );\n }\n const bar = out.indexOf(\"|\");\n if (bar < 0) {\n throw new Error(\n `pai worker pane: osascript failed: ${(p.stderr || out).slice(0, 200)}`\n );\n }\n const live = new Set(out.slice(0, bar).split(\",\").filter(Boolean));\n for (const e of reg) if (!live.has(e.session)) dropEntry(logDir, e);\n saveEntry(logDir, {\n session: out.slice(bar + 1),\n worker: wid,\n opened: new Date().toISOString().replace(\"T\", \" \").slice(0, 19),\n scope,\n });\n return { message: `pane opened for ${wid}`, session: out.slice(bar + 1) };\n}\n\n/**\n * One `--check` line with the bounds of the window hosting `term`'s iTerm\n * session — the before/after pair that shows whether a split moved it.\n * Read-only; never touches the window.\n */\nasync function windowBoundsLine(term: string): Promise<string> {\n const uid = itermUuid(term);\n if (!uid) return \"window bounds: (not in iTerm2)\";\n try {\n const p = await osascript(WINDOW_BOUNDS_SCRIPT, [uid]);\n const out = p.stdout.trim();\n if (out && out !== \"notfound\" && out !== \"notrunning\") return `window bounds: ${out}`;\n const why =\n out === \"notfound\" ? \"iTerm2 session not found\"\n : out === \"notrunning\" ? \"iTerm2 not running\"\n : (p.stderr.trim() || \"no output\").slice(0, 120);\n return `window bounds: (${why})`;\n } catch (e) {\n return `window bounds: (osascript: ${String((e as Error).message ?? e).slice(0, 120)})`;\n }\n}\n\n/**\n * Report-only variant used by `pai worker pane <id> --check`: whether a pane\n * runs for the worker, the bounds of the window hosting the asking session,\n * plus the dynamic profile's path, its existence, and the font it contains\n * (or, when missing, would write).\n */\nexport async function checkPaneForWorker(wid: string, fontSize: number, term: string): Promise<string> {\n const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\n if (paneBackend() !== \"iterm\") return lines.join(\"\\n\");\n lines.push(await windowBoundsLine(term));\n const path = dynamicProfilePath();\n if (existsSync(path)) {\n let font = \"(unreadable)\";\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as {\n Profiles?: Array<{ \"Normal Font\"?: string }>;\n };\n font = parsed.Profiles?.[0]?.[\"Normal Font\"] ?? \"(none)\";\n } catch {\n // font stays \"(unreadable)\"\n }\n lines.push(`profile file: ${path} (exists)`);\n lines.push(`profile font: ${font}`);\n } else {\n const parent = defaultBookmark();\n lines.push(`profile file: ${path} (missing)`);\n lines.push(`profile font (would write): ${paneFont(parent?.[\"Normal Font\"], fontSize)}`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Split this iTerm tab and run `pai worker follow` in the new pane, unless\n * one already runs here (detected by TTY — iTerm overwrites session names\n * with the running command, so names cannot serve as idempotence).\n */\nexport async function openFollowPane(\n logDir: string,\n _config: WorkersConfig,\n term: string,\n checkOnly: boolean\n): Promise<string> {\n void logDir;\n const backend = paneBackend();\n if (backend === \"none\") return noPaneMessage();\n if (backend === \"tmux\") {\n if (checkOnly) return \"no follow pane\";\n tmuxSplit(`${process.execPath} ${absoluteCliPath()} worker follow`);\n return \"follow pane opened\";\n }\n const uid = itermUuid(term);\n const p = await osascript(TAB_TTYS_SCRIPT, [uid]);\n const out = p.stdout.trim();\n if (out === \"notfound\") throw new Error(\"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\");\n if (out === \"notrunning\") throw new Error(\"pai worker pane: iTerm2 is not running\");\n const tabTtys = new Set(out.split(\",\").map((t) => t.trim()).filter(Boolean));\n const overlap = [...tabTtys].filter((t) => followTtys().has(t));\n if (overlap.length > 0) return \"follow pane already open\";\n if (checkOnly) return \"no follow pane\";\n // one exec-able line, no shell syntax — see followCommand()\n const cmd = `${process.execPath} ${absoluteCliPath()} worker follow`;\n const s = await osascript(SPLIT_SCRIPT, [uid, cmd]);\n const sout = s.stdout.trim();\n if (sout === \"opened\") return \"follow pane opened\";\n if (sout === \"notfound\") throw new Error(\"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\");\n throw new Error(`pai worker pane: osascript failed: ${(s.stderr || sout).slice(0, 200)}`);\n}\n","/**\n * tree.ts — sub-workers: parent detection and the tree caps.\n *\n * A worker launched by another worker (the runner exports PAI_WORKER_ID in\n * every worker's environment) carries `parent` in its status, so the worker\n * forest is visible in `ps`, the status line and the ledger. Two caps keep\n * the tree from growing without bound, both under `workers.tree`:\n *\n * - maxDepth (default 2): how deep sub-workers may nest. A top-level worker\n * sits at depth 0; its children at 1; grandchildren at 2 — one level past\n * the cap refuses with a clear message.\n * - maxChildren (default 4): how many children of one parent may run at the\n * same time. Finished children do not count; a planner works through its\n * sub-tasks in waves of this size.\n *\n * Chain stages also carry `parent` (the chain id), but chains are not workers\n * and never get a status file — a parent without a status is not capped.\n */\n\nimport type { WorkersTreeConfig } from \"./config.js\";\nimport { isLive, loadStatus, loadStatuses, saveStatus, type WorkerStatus } from \"./status.js\";\n\n/** The env var the runner sets in every worker's environment. */\nexport const WORKER_ID_ENV = \"PAI_WORKER_ID\";\n\n/** The worker this process runs inside, when it runs inside one. */\nexport function parentFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n const id = env[WORKER_ID_ENV];\n return typeof id === \"string\" && id.trim() ? id.trim() : null;\n}\n\n/**\n * Depth of `id` in the worker forest: 0 for a top-level worker, 1 + the\n * parent's depth for a sub-worker. Parents without a status file (chain ids,\n * unknown ids) count as roots; a cycle reads as its own depth and is cut off\n * after the statuses it walked. `chatIsRoot` stops one level short of the\n * chat-pane tracker (origin \"chat\"): the status line treats the pane as the\n * bar itself, so the workers it spawned are its top level.\n */\nexport function workerDepth(\n statuses: WorkerStatus[],\n id: string,\n opts?: { chatIsRoot?: boolean }\n): number {\n const byId = new Map(statuses.map((s) => [s.id, s]));\n let depth = 0;\n let cur = byId.get(id);\n const seen = new Set<string>([id]);\n while (cur?.parent && !seen.has(cur.parent)) {\n seen.add(cur.parent);\n const next = byId.get(cur.parent);\n if (!next) break; // chain id or stale parent: a root, not a level\n if (!(opts?.chatIsRoot && next.origin === \"chat\")) depth += 1;\n cur = next;\n }\n return depth;\n}\n\n/** Children of `parent` that are still running, oldest first. */\nexport function runningChildren(statuses: WorkerStatus[], parent: string): WorkerStatus[] {\n return statuses.filter((s) => s.parent === parent && isLive(s));\n}\n\n/**\n * Would starting a child of `parent` stay within the caps? Throws with a\n * clear message when it would not; returns silently when `parent` has no\n * status file (a chain id or an unknown id — not a worker, not capped).\n */\nexport function assertChildAllowed(\n logDir: string,\n parent: string,\n caps: WorkersTreeConfig,\n statuses: WorkerStatus[] = loadStatuses(logDir)\n): void {\n if (!statuses.some((s) => s.id === parent)) return;\n const depth = workerDepth(statuses, parent);\n if (depth + 1 > caps.maxDepth) {\n throw new Error(\n `worker tree: ${parent} sits at depth ${depth} and ` +\n `workers.tree.maxDepth is ${caps.maxDepth} — it cannot start another level of sub-workers. ` +\n `Hand the task up instead: pai worker handoff '{\"kind\":\"proposal\",\"text\":\"…\"}'`\n );\n }\n const kids = runningChildren(statuses, parent);\n if (kids.length >= caps.maxChildren) {\n throw new Error(\n `worker tree: ${parent} already has ${kids.length} running sub-workers ` +\n `(${kids.map((k) => k.id).join(\", \")}) and workers.tree.maxChildren is ${caps.maxChildren} — ` +\n `wait for one to finish, or raise the cap in the workers config`\n );\n }\n}\n\n/**\n * The parent a launch should record: an explicit one (chain stage) wins, else\n * the worker this process runs inside. Returns null for top-level runs.\n */\nexport function launchParent(explicit: string | undefined, env: NodeJS.ProcessEnv = process.env): string | null {\n return explicit ?? parentFromEnv(env);\n}\n\n/** Does a status file exist for `id` (i.e. is it a worker rather than a chain)? */\nexport function isWorkerId(logDir: string, id: string): boolean {\n return loadStatus(logDir, id) !== null;\n}\n\n/**\n * A worker owns its children's results (the worker contract says so): if\n * `parent` exits leaving sub-workers unaccounted for, its own run must not\n * report success. Two shapes count, both read off `runningChildren` and the\n * raw statuses, never a second live-child lookup:\n *\n * - still live: left running untouched (killing another process's work\n * here would be its own bug) but named in the reason.\n * - marked \"running\" with a dead pid (the child never got to write its own\n * terminal state before its parent vanished from under it): finalised\n * here as failed, so `ps` stops showing a corpse as live.\n *\n * Returns the reason string for the parent's own report, or null when every\n * child it started is accounted for.\n */\nexport function checkOrphanedChildren(logDir: string, parent: string): string | null {\n const statuses = loadStatuses(logDir);\n const live = runningChildren(statuses, parent);\n const abandoned = statuses.filter(\n (s) => s.parent === parent && s.state === \"running\" && !isLive(s)\n );\n for (const child of abandoned) {\n saveStatus(logDir, { ...child, state: \"failed\", last: `parent ${parent} exited` });\n }\n const ids = [...live, ...abandoned].map((s) => s.id);\n if (!ids.length) return null;\n return (\n `exited with ${ids.length} sub-worker${ids.length === 1 ? \"\" : \"s\"} still running ` +\n `(${ids.join(\", \")}); they were not waited for — run children in the foreground or wait for them before finishing`\n );\n}\n","/**\n * operator.ts — talking to a running worker.\n *\n * Headless workers run with `--input-format stream-json` and their stdin held\n * open, so a run is a conversation: every line sent to the per-worker Unix\n * socket `<logDir>/<id>.sock` is forwarded to the child as a user message and\n * mirrored into the transcript as an `operator` event (rendered with a »\n * marker). Once the worker has finished its turn, stdin closes 2 s later\n * unless a new message arrives — after that, `say` refuses and `resume`\n * continues the same Claude session with the worker's context intact.\n */\n\nimport { createServer, connect, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadStatus, isLive } from \"./status.js\";\n\nexport function operatorSocketPath(logDir: string, id: string): string {\n return join(logDir, `${id}.sock`);\n}\n\n/**\n * The runner's side: listen on the worker socket, hand every received line to\n * `onLine`. It acknowledges with \"ok\" only when `onLine` returns without a\n * string; a returned string (or a throw) is the failure reason, sent back as\n * `err <reason>` so `say` never reports ok for a message nobody received. Returns the server (close it when the run ends; the socket file is\n * unlinked on close, best effort).\n */\nexport function createOperatorServer(\n logDir: string,\n id: string,\n onLine: (text: string) => unknown\n): import(\"node:net\").Server {\n const path = operatorSocketPath(logDir, id);\n try {\n if (existsSync(path)) unlinkSync(path);\n } catch {\n // a stale socket from a crashed run must not block the new one\n }\n const server = createServer((socket: Socket) => {\n let buf = \"\";\n socket.on(\"error\", () => {});\n socket.on(\"data\", async (chunk: Buffer) => {\n buf += chunk.toString(\"utf8\");\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, nl).replace(/\\r$/, \"\");\n buf = buf.slice(nl + 1);\n let failure: unknown;\n if (line.trim()) {\n try {\n failure = await onLine(line);\n } catch (e) {\n failure = (e as Error).message;\n }\n }\n if (!socket.destroyed) socket.write(typeof failure === \"string\" && failure ? `err ${failure.replace(/\\n/g, \" \")}\\n` : \"ok\\n\");\n }\n });\n });\n server.listen(path);\n server.on(\"close\", () => {\n try {\n if (existsSync(path)) unlinkSync(path);\n } catch {\n // already gone\n }\n });\n return server;\n}\n\n/**\n * `pai worker say <id> \"<text>\"`: forward one line to a running worker.\n * Resolves \"ok\" only once the worker acknowledged delivery; rejects with the reason otherwise.\n */\nexport function sayToWorker(logDir: string, id: string, text: string, timeoutMs = 4000): Promise<string> {\n const status = loadStatus(logDir, id);\n if (!status) {\n return Promise.reject(new Error(`no worker named \"${id}\"`));\n }\n if (!isLive(status)) {\n return Promise.reject(\n new Error(\n `worker ${id} is not running (state: ${status.state}) — ` +\n `continue it instead with: pai worker resume ${id} \"<text>\"`\n )\n );\n }\n const path = operatorSocketPath(logDir, id);\n if (!existsSync(path)) {\n return Promise.reject(\n new Error(`worker ${id} has no operator socket (${path}) — it may predate this PAI version`)\n );\n }\n return new Promise((resolve, reject) => {\n const sock = connect(path);\n const fail = (e: Error) => {\n sock.destroy();\n reject(new Error(`cannot talk to worker ${id}: ${e.message}`));\n };\n sock.setTimeout(timeoutMs, () => fail(new Error(\"timeout\")));\n sock.once(\"error\", (e: Error) => fail(e));\n sock.once(\"connect\", () => {\n sock.write(text.replace(/\\n/g, \" \") + \"\\n\");\n });\n sock.once(\"data\", (d: Buffer) => {\n sock.end();\n const reply = d.toString(\"utf8\").trim();\n if (reply === \"ok\") resolve(\"ok\");\n else reject(new Error(`worker ${id} did not take the message: ${reply.replace(/^err /, \"\") || \"no acknowledgement\"}`));\n });\n sock.once(\"close\", () => fail(new Error(\"connection closed before the worker acknowledged\")));\n });\n}\n","/**\n * handoff.ts — upward messages between workers.\n *\n * Coordination in the worker tree is upward only: a child appends a handoff\n * to `<logDir>/<parent>.inbox.jsonl` and (when the parent is a running\n * worker) the same text is said to it, so it lands in the parent's\n * conversation as `[handoff from <child>] …`. The parent's pane renders the\n * inbox as `◆` lines, `ps`/`worker_status` show an inbox count, and a child\n * that finishes delivers its structured report automatically as a\n * `kind: \"result\"` handoff. There is no downward or sideways path — telling\n * a worker something is the operator's job (`pai worker say`).\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { isLive, loadStatus } from \"./status.js\";\nimport { sayToWorker } from \"./operator.js\";\n\nexport const HANDOFF_KINDS = [\"proposal\", \"result\", \"question\", \"blocker\"] as const;\n\nexport type HandoffKind = (typeof HANDOFF_KINDS)[number];\n\nexport interface Handoff {\n /** Sending worker id. */\n from: string;\n /** Receiving worker id (the parent). */\n to: string;\n kind: HandoffKind;\n text: string;\n /** Structured payload (a report, a proposal's fields, …). */\n data?: Record<string, unknown>;\n /** ISO stamp, attached on append. */\n _ts?: string;\n}\n\n/** Where a worker's handoffs land: <logDir>/<id>.inbox.jsonl. */\nexport function inboxPath(logDir: string, id: string): string {\n return join(logDir, `${id}.inbox.jsonl`);\n}\n\nexport function isHandoffKind(v: unknown): v is HandoffKind {\n return typeof v === \"string\" && (HANDOFF_KINDS as readonly string[]).includes(v);\n}\n\n/**\n * Normalize a raw parsed value into a Handoff, or explain what is missing.\n * `from`/`to` may be preset by the caller (the CLI fills them from the\n * environment); everything else must be present and well-formed.\n */\nexport function parseHandoff(\n v: unknown,\n preset: Partial<Pick<Handoff, \"from\" | \"to\">> = {}\n): Handoff {\n if (typeof v !== \"object\" || v === null || Array.isArray(v)) {\n throw new Error(\"handoff payload must be a JSON object\");\n }\n const o = v as Record<string, unknown>;\n const from = typeof o.from === \"string\" && o.from ? o.from : preset.from;\n const to = typeof o.to === \"string\" && o.to ? o.to : preset.to;\n if (!from) throw new Error(`handoff needs \"from\" (the sending worker id)`);\n if (!to) throw new Error(`handoff needs \"to\" (the parent worker id)`);\n if (!isHandoffKind(o.kind)) {\n throw new Error(`handoff \"kind\" must be one of: ${HANDOFF_KINDS.join(\", \")}`);\n }\n const text = typeof o.text === \"string\" ? o.text : \"\";\n if (!text.trim()) throw new Error(`handoff needs a non-empty \"text\"`);\n const data =\n typeof o.data === \"object\" && o.data !== null && !Array.isArray(o.data)\n ? (o.data as Record<string, unknown>)\n : undefined;\n return { from, to, kind: o.kind, text, ...(data ? { data } : {}) };\n}\n\n/** Append one handoff to its recipient's inbox, stamped now. */\nexport function appendHandoff(logDir: string, h: Handoff, now: Date = new Date()): void {\n const path = inboxPath(logDir, h.to);\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n appendFileSync(path, JSON.stringify({ ...h, _ts: now.toISOString() }) + \"\\n\", \"utf8\");\n}\n\n/** Every handoff in a worker's inbox, oldest first; damaged lines skip. */\nexport function readInbox(logDir: string, id: string): Handoff[] {\n const path = inboxPath(logDir, id);\n if (!existsSync(path)) return [];\n const out: Handoff[] = [];\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n out.push(JSON.parse(line) as Handoff);\n } catch {\n // a half-written line is not worth a crash; the rest still reads\n }\n }\n return out;\n}\n\n/** How the say path prefixes a handoff so the parent model knows its source. */\nexport function handoffMessage(h: Pick<Handoff, \"from\" | \"kind\" | \"text\">): string {\n return `[handoff from ${h.from}] (${h.kind}) ${h.text.replace(/\\s+/g, \" \").trim()}`;\n}\n\n/** True for the exact shape of handoffMessage() — the runner marks the mirror. */\nexport function isHandoffMessage(text: string): boolean {\n return new RegExp(`^\\\\[handoff from \\\\S+\\\\] \\\\((${HANDOFF_KINDS.join(\"|\")})\\\\) `).test(text);\n}\n\n/** Test seam for deliverHandoff: the say path, overridable with a mock. */\nexport interface HandoffDeps {\n say?: (id: string, text: string) => Promise<string>;\n}\n\n/**\n * Deliver one handoff: append it to the parent's inbox, then — when the\n * parent is a running worker with a live operator socket — say it so it\n * enters the parent's conversation. The say is best effort: a parent that is\n * busy, finished or gone still has the inbox line, and nothing here may make\n * a finishing child's exit path fail.\n */\nexport async function deliverHandoff(\n logDir: string,\n h: Handoff,\n deps: HandoffDeps = {},\n now: Date = new Date()\n): Promise<Handoff> {\n appendHandoff(logDir, h, now);\n const say = deps.say ?? ((id: string, text: string) => sayToWorker(logDir, id, text));\n const parent = loadStatus(logDir, h.to);\n if (parent && isLive(parent)) {\n try {\n await say(h.to, handoffMessage(h));\n } catch {\n // the inbox line is the durable record; a failed say is not an error\n }\n }\n return h;\n}\n\n/**\n * Send a handoff from inside a worker (the `pai worker handoff` CLI and the\n * MCP tool both land here): the sender comes from PAI_WORKER_ID, the\n * recipient from its status file's `parent`. Rejects with a clear message\n * outside a worker or under a parentless worker.\n */\nexport async function handoffFromInside(\n logDir: string,\n env: NodeJS.ProcessEnv,\n payload: unknown,\n deps: HandoffDeps = {}\n): Promise<Handoff> {\n const mine = env.PAI_WORKER_ID;\n if (!mine) {\n throw new Error(\n \"not inside a worker — handoffs are sent by workers (PAI_WORKER_ID unset). \" +\n \"Talk to a running worker with: pai worker say <id> \\\"<text>\\\"\"\n );\n }\n const st = loadStatus(logDir, mine);\n if (!st) throw new Error(`no status for this worker (${mine}) — cannot find its parent`);\n if (!st.parent || !loadStatus(logDir, st.parent)) {\n throw new Error(\n `worker ${mine} has no worker parent to hand off to ` +\n `(parent: ${st.parent ?? \"(none)\"}) — handoffs go up the worker tree only`\n );\n }\n const h = parseHandoff(payload, { from: mine, to: st.parent });\n return deliverHandoff(logDir, h, deps);\n}\n","/**\n * wait-on.ts — bounded wait for a detached job, the worker alternative to a\n * long blocking foreground call (which starves operator messages and\n * supervision). Recipe: `nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid`,\n * then `pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` until exit 0.\n *\n * Exit codes: 0 pid gone, 3 still running at --max, 2 pid never existed or not ours.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\n\nexport const WAIT_DEFAULT_SECS = 110;\nexport const WAIT_CAP_SECS = 115;\nconst POLL_MS = 2000;\nconst HEARTBEAT_MS = 20_000;\n/** Slack between the first observation and a process start time (ps lstart has 1 s resolution). */\nconst REUSE_SLACK_MS = 2000;\n\nexport interface WaitOnOpts {\n pid: number;\n log?: string;\n maxSecs?: number;\n tail?: number;\n}\n\nexport interface WaitOnDeps {\n /** \"alive\" | \"gone\" | \"foreign\" (exists, not ours). */\n probe: (pid: number) => \"alive\" | \"gone\" | \"foreign\";\n /** Process start time in ms epoch, null when unknown. */\n startTime: (pid: number) => number | null;\n now: () => number;\n sleep: (ms: number) => Promise<void>;\n out: (line: string) => void;\n}\n\nexport const realDeps: WaitOnDeps = {\n probe(pid) {\n try {\n process.kill(pid, 0);\n return \"alive\";\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === \"EPERM\" ? \"foreign\" : \"gone\";\n }\n },\n startTime(pid) {\n try {\n const t = Date.parse(execFileSync(\"ps\", [\"-o\", \"lstart=\", \"-p\", String(pid)], { encoding: \"utf8\" }).trim());\n return Number.isFinite(t) ? t : null;\n } catch {\n return null;\n }\n },\n now: () => Date.now(),\n sleep: (ms) => new Promise((r) => setTimeout(r, ms)),\n out: (l) => console.log(l),\n};\n\nconst readLines = (file: string | undefined): string[] => {\n if (!file || !existsSync(file)) return [];\n const t = readFileSync(file, \"utf8\");\n return t === \"\" ? [] : t.replace(/\\n$/, \"\").split(\"\\n\");\n};\n\nfunction rcOf(pid: number, log?: string): string | null {\n for (const f of [log && `${log}.rc`, log && log.replace(/\\.[^./]+$/, \".rc\"), `/tmp/${pid}.rc`]) {\n if (f && existsSync(f)) return readFileSync(f, \"utf8\").trim();\n }\n return null;\n}\n\nexport async function waitOn(o: WaitOnOpts, d: WaitOnDeps = realDeps): Promise<number> {\n const maxMs = Math.min(o.maxSecs ?? WAIT_DEFAULT_SECS, WAIT_CAP_SECS) * 1000;\n const tail = o.tail ?? 20;\n const t0 = d.now();\n const baseLines = readLines(o.log).length;\n\n const finish = (): number => {\n const rc = rcOf(o.pid, o.log);\n d.out(`pid ${o.pid} gone${rc !== null ? `, exit code ${rc}` : \"\"}`);\n if (tail > 0) for (const l of readLines(o.log).slice(-tail)) d.out(l);\n return 0;\n };\n\n if (!Number.isInteger(o.pid) || o.pid <= 0) {\n d.out(`wait-on: invalid pid ${o.pid}`);\n return 2;\n }\n const first = d.probe(o.pid);\n if (first === \"foreign\") {\n d.out(`wait-on: pid ${o.pid} is not owned by this user`);\n return 2;\n }\n if (first === \"gone\") {\n // A job that finished between two wait-on calls leaves an rc or a log; neither means the pid never existed.\n if (rcOf(o.pid, o.log) !== null || (o.log && existsSync(o.log))) return finish();\n d.out(`wait-on: pid ${o.pid} does not exist`);\n return 2;\n }\n\n let nextBeat = t0 + HEARTBEAT_MS;\n while (d.now() - t0 < maxMs) {\n await d.sleep(Math.min(POLL_MS, Math.max(0, maxMs - (d.now() - t0))));\n const st = d.startTime(o.pid);\n if (d.probe(o.pid) !== \"alive\" || (st !== null && st > t0 + REUSE_SLACK_MS)) return finish();\n if (d.now() >= nextBeat) {\n d.out(`running ${Math.round((d.now() - t0) / 1000)}s, log +${readLines(o.log).length - baseLines} lines`);\n nextBeat += HEARTBEAT_MS;\n }\n }\n d.out(`still running after ${Math.round((d.now() - t0) / 1000)}s (pid ${o.pid}); call wait-on again`);\n if (tail > 0) for (const l of readLines(o.log).slice(-tail)) d.out(l);\n return 3;\n}\n\n/** The documented long-job recipe, quoted by the worker contract and every deny message. */\nexport const WAIT_RECIPE =\n \"`nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid`, then repeat \" +\n \"`pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` (each call returns within 2 min; do other work between calls) until it exits 0\";\n","/**\n * agentish.ts — Agentish v2 (AG2), the wire format a worker's final report\n * uses by default: a kind line plus one-letter k=v lines instead of JSON\n * prose. Spec and validator live in the `aibroker` CLI\n * (see /Users/i052341/Daten/Cloud/Development/ai/AIBroker/docs/agentish.md);\n * this module shells out to it when present and falls back to a frozen copy\n * of the spec text otherwise, so a worker still gets a contract when\n * `aibroker` is not installed on its box.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport type { WorkerReport } from \"./report.js\";\n\n/** Frozen copy of `aibroker agentish spec`'s first line, for when the CLI is absent. */\nexport const AG2_SPEC_FALLBACK =\n \"AG2. msg=kind line+k=v lines. kinds T R S Q A X. keys i id g goal o own n forbid d steps p proof u out l limits r res c changes t tests G gate I inst m images # nums w worst x next z note(<200ch). sep |. outcomes + - ~ ? !. @n=path declared once then reused; @n:12=file:line. tests as Name+ Name-. no prose, no articles, never restate, unknown=?. r=+ only if all t +.\";\n\n/** Frozen copy of the `y` (why) extension line. */\nexport const AG2_EXTENSIONS_FALLBACK = \"y why(≤600ch)\";\n\ninterface Ag2SpecResult {\n spec: string;\n extensions: string;\n source: \"aibroker\" | \"builtin\";\n}\n\nlet cachedSpec: Ag2SpecResult | null = null;\n\n/**\n * The AG2 spec + extensions lines: from `aibroker agentish spec` when the\n * CLI runs and its output looks right, else the frozen fallback. Memoised —\n * every caller in one process gets the same spec without re-spawning aibroker.\n */\nexport function ag2Spec(): Ag2SpecResult {\n if (cachedSpec) return cachedSpec;\n try {\n const out = execFileSync(\"aibroker\", [\"agentish\", \"spec\"], {\n timeout: 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n encoding: \"utf8\",\n });\n const lines = out.split(\"\\n\").filter((l) => l.trim());\n if (lines[0]?.startsWith(\"AG2.\")) {\n cachedSpec = { spec: lines[0], extensions: lines[1] ?? AG2_EXTENSIONS_FALLBACK, source: \"aibroker\" };\n return cachedSpec;\n }\n } catch {\n // aibroker missing, not runnable, or timed out — fall through\n }\n cachedSpec = { spec: AG2_SPEC_FALLBACK, extensions: AG2_EXTENSIONS_FALLBACK, source: \"builtin\" };\n return cachedSpec;\n}\n\n/** Only for tests: drop the memoised spec so the next ag2Spec() re-probes. */\nexport function resetAg2SpecCache(): void {\n cachedSpec = null;\n}\n\n/**\n * Strip what workers wrap around a report: one surrounding ``` fence (opening\n * line may carry a language tag) and a leading `[YYYY-MM-DD HH:MM]` stamp line.\n */\nexport function unwrapAg2(text: string): string {\n let t = (text ?? \"\").trim();\n const fenced = t.match(/^```[^\\n]*\\n([\\s\\S]*?)\\n?```$/);\n if (fenced) t = fenced[1].trim();\n return t.replace(/^\\[\\d{4}-\\d{2}-\\d{2}[^\\]\\n]*\\][ \\t]*\\n/, \"\").trim();\n}\n\nexport interface ParsedAg2 {\n kind: string;\n fields: Record<string, string>;\n symbols: Record<string, string>;\n}\n\n/**\n * Parse one AG2 message: first non-empty line is the kind (T R S Q A X,\n * optionally followed by trailing text — only the first character is the\n * kind), later lines are `key=value` or `@n=path` symbol declarations.\n * ```-fenced lines are ignored so a message pasted inside a code fence still\n * parses. Returns null when the first non-empty line is not a known kind.\n */\nexport function parseAg2Report(text: string): ParsedAg2 | null {\n const kinds = new Set([\"T\", \"R\", \"S\", \"Q\", \"A\", \"X\"]);\n const lines = unwrapAg2(text)\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith(\"```\"));\n if (!lines.length) return null;\n const kind = lines[0][0];\n if (!kinds.has(kind)) return null;\n\n const fields: Record<string, string> = {};\n const symbols: Record<string, string> = {};\n for (const line of lines.slice(1)) {\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n const key = line.slice(0, eq).trim();\n const value = line.slice(eq + 1).trim();\n if (key.startsWith(\"@\")) {\n symbols[key.slice(1)] = value;\n } else {\n fields[key] = value;\n }\n }\n return { kind, fields, symbols };\n}\n\n/** true iff the value ends with the AG2 pass outcome character. */\nfunction passed(entry: string): boolean {\n return entry.endsWith(\"+\");\n}\n\n/**\n * Map a parsed AG2 report onto the runner's WorkerReport shape, so the\n * viewer and `printResult` can render an AG2 `R` exactly like a JSON one.\n */\nexport function ag2ToWorkerReport(parsed: ParsedAg2): WorkerReport {\n const f = parsed.fields;\n const out: WorkerReport = { format: \"ag2\" };\n\n if (f.c) {\n out.changed = f.c\n .split(\"|\")\n .map((s) => s.trim())\n .filter(Boolean)\n .map((entry) => {\n const colon = entry.indexOf(\":\");\n const space = entry.indexOf(\" \");\n const cut = colon >= 0 && (space < 0 || colon < space) ? colon : space;\n if (cut < 0) return { path: entry, summary: \"\" };\n return { path: entry.slice(0, cut).trim(), summary: entry.slice(cut + 1).trim() };\n });\n }\n\n if (f.t) {\n const tokens = f.t.split(/\\s+/).filter(Boolean);\n const isGrammar = tokens.length > 0 && tokens.every((tok) => /^\\S+[+\\-~?!]$/.test(tok));\n out.checks = isGrammar\n ? tokens.map((entry) => ({\n name: entry.slice(0, -1),\n ok: passed(entry),\n detail: entry.slice(-1),\n }))\n : [{ name: \"tests\", detail: f.t }];\n }\n\n if (f.p) out.commands = f.p.split(\"|\").map((s) => s.trim()).filter(Boolean);\n\n const open: string[] = [];\n if (f.x) open.push(f.x);\n for (const [k, v] of Object.entries(f)) {\n if ((v.endsWith(\"!\") || v.endsWith(\"?\")) && k !== \"r\" && k !== \"res\") open.push(`${k}: ${v}`);\n }\n if (open.length) out.open = open;\n\n if (f.z) out.notes = f.z;\n const res = f.r ?? f.res;\n if (res === \"+\" || res === \"-\" || res === \"~\" || res === \"?\" || res === \"!\") out.result = res;\n if (f.y) out.why = f.y;\n\n return out;\n}\n\nexport interface Ag2ValidationResult {\n ok: boolean;\n errors: string[];\n validator: \"aibroker\" | \"none\";\n}\n\ninterface AibrokerCheckJson {\n ok?: boolean;\n errors?: Array<{ code?: string; message?: string; line?: number }>;\n}\n\n/**\n * Validate an AG2 message with `aibroker agentish check - --json`. Never\n * fails the caller: a missing/broken CLI reports ok:true, validator \"none\" —\n * a worker's run is never blocked on a local tooling gap, only its\n * `reportValid` marker goes unset.\n */\nexport function validateAg2(text: string): Ag2ValidationResult {\n try {\n const out = execFileSync(\"aibroker\", [\"agentish\", \"check\", \"-\", \"--json\"], {\n input: unwrapAg2(text),\n timeout: 5000,\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n encoding: \"utf8\",\n });\n const parsed = JSON.parse(out) as AibrokerCheckJson;\n const errors = (parsed.errors ?? []).map((e) => e.message ?? e.code ?? \"unknown error\");\n return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n } catch (e) {\n // execFileSync throws on non-zero exit too — stdout still carries the JSON\n const out = (e as { stdout?: Buffer | string }).stdout;\n if (out) {\n try {\n const parsed = JSON.parse(out.toString()) as AibrokerCheckJson;\n const errors = (parsed.errors ?? []).map((er) => er.message ?? er.code ?? \"unknown error\");\n return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n } catch {\n // fall through to \"no validator\" below\n }\n }\n return { ok: true, errors: [], validator: \"none\" };\n }\n}\n","/**\n * report.ts — the worker contract: terse, structured final reports.\n *\n * Headless workers run with an appended system prompt that fixes their output\n * shape: act, verify, then finish with ONE final message — AG2 (see\n * agentish.ts) by default, JSON as the pre-AG2 fallback. The runner parses it\n * out of the result; the viewer renders it as a compact block instead of a\n * wall of text.\n */\n\nimport { statSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { WAIT_RECIPE } from \"./wait-on.js\";\nimport type { Paint } from \"./render.js\";\nimport { ag2Spec, ag2ToWorkerReport, parseAg2Report } from \"./agentish.js\";\n\n/** The marker the runner puts on operator messages the worker must answer. */\nexport const OPERATOR_MARK = \"[operator]\";\n\n/** Classes that get the short contract — cheap, bounded, single-answer tasks. */\nconst SHORT_CONTRACT_CLASSES = new Set([\"spotcheck\", \"simple\"]);\n\n/**\n * The AG2 report block appended to a contract when format is \"ag2\" — field\n * set matches `aibroker agentish check`'s real R schema (verified 2026-09-20:\n * i/r/t/c are required, not optional as an early draft of this contract had\n * it; r=+ needs G=+ and non-empty p too, not just all-t-passing; R has no\n * `u`/out field at all — u is T-only — so the answer goes in z instead).\n */\nfunction ag2ReportBlock(): string[] {\n const spec = ag2Spec();\n return [\n spec.spec,\n spec.extensions,\n \"Final message: one AG2 `R` message only — no prose, no code fence, no markdown.\",\n \"R requires i (id), r (+ - ~ ? !), t (tests, Name+ Name-), c (path summary|path summary).\",\n \"r=+ requires G=+ (gate) and p non-empty (proof command), and every t entry +. z= the answer/note,\",\n \"one line, always present. x= what is left (omit if nothing). y= one line why when r is not +.\",\n \"Example:\",\n \"R\",\n \"i=count-audit-ts\",\n \"r=+\",\n \"G=+\",\n \"c=src/audit summary of files counted\",\n \"t=Count+\",\n \"p=wc -l src/audit/*.ts\",\n \"z=19 files, 2523 lines\",\n ];\n}\n\n/** The JSON report block appended to a contract when format is \"json\" (pre-AG2 fallback). */\nfunction jsonReportBlock(lead: string): string[] {\n return [\n lead,\n '{\"changed\":[{\"path\":\"…\",\"summary\":\"…\"}],\"commands\":[\"…\"],\"checks\":[{\"name\":\"…\",\"ok\":true,\"detail\":\"…\"}],\"open\":[\"…\"],\"notes\":\"one line\"}',\n ];\n}\n\n/** The two sentences that stop a worker from breaking on shell quoting — kept in both contracts. */\nfunction fileNotInlineBlock(): string[] {\n return [\n \"NEVER put a multi-line or quoted payload inline in a shell command — no heredocs (<<EOF), no long\",\n \"-p '…' specs for child workers, no inline JSON, no inline scripts. ALWAYS write it to a file with\",\n \"the Write tool first and pass the file: -p \\\"$(cat spec.txt)\\\", --file spec.txt, or < spec.txt.\",\n \"Inline quoting fails silently or dies with a parse error and burns a worker launch; there are no\",\n \"exceptions.\",\n \"Never inline code one-liners (python3 -c, node -e, ruby -e and friends) — the permission layer\",\n \"denies them (\\\"ztk: command denied by permission rules\\\"); write the script to a file with the\",\n \"Write tool and run it (python3 script.py), or use jq for JSON inspection.\",\n ];\n}\n\n/** The file-not-inline rule, condensed for the short contract's tight length budget. */\nfunction fileNotInlineBlockShort(): string[] {\n return [\n \"NEVER put a multi-line or quoted payload inline in a shell command (no heredocs, no inline\",\n \"scripts): write it to a file with the Write tool and pass it, e.g. -p \\\"$(cat spec.txt)\\\".\",\n \"Never inline code (python3 -c and friends) — write a script file and run it, or use jq.\",\n ];\n}\n\n/** spotcheck/simple: headless, bounded, no delegation language — under 1500 chars in ag2 format. */\nfunction shortContractPrompt(format: \"ag2\" | \"json\"): string {\n const lines = [\n \"You are a headless implementation worker, run non-interactively.\",\n \"This task is yours: answer the question or do the bounded task now. Run at most one command\",\n \"to confirm a result and never re-derive a number you already have. No child workers, no handoff.\",\n ...fileNotInlineBlockShort(),\n ];\n lines.push(\n ...(format === \"ag2\"\n ? ag2ReportBlock()\n : jsonReportBlock(\n \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n ))\n );\n return lines.join(\"\\n\");\n}\n\n/** Every other class (and no class): the full worker/orchestrator contract. */\nfunction fullContractPrompt(format: \"ag2\" | \"json\"): string {\n const lines = [\n \"You are a headless implementation worker, run non-interactively by an orchestrating session.\",\n \"You are the worker: this task is yours to finish in this process. Never hand your whole task to a\",\n \"single child worker and step back — that is delegation, not work. Child workers are for two things\",\n \"only: (1) PARALLELISING independent parts of your task, each part a self-contained spec; (2) CHEAPER\",\n \"bounded sub-tasks — probes, renders, test runs, lookups — run one tier down with --class spotcheck or\",\n \"--class simple. Always pin --provider anthropic on children.\",\n \"You own your children's results: run them in the foreground or poll until each has finished, read\",\n \"their result, verify it, and fold it into your own report. When your turn ends this run ends and any\",\n \"running child is killed, so never end with a child still running.\",\n \"Ending your turn ends this run. There is no later wake-up: never call ScheduleWakeup, never say you\",\n \"will pick something up later, never leave a background command as your final action. Finish, then\",\n \"report.\",\n \"No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.\",\n \"Never sleep-poll: no `sleep N` over 60 seconds, anywhere in a command — standalone, after `&&`/`||`/\",\n \"`;`/`|`, or inside a loop or subshell — and the sleeps of one command add up (`sleep 58; sleep 58` is\",\n \"116 s). Ending your turn ends this process, so a background job gets no later wake-up here:\",\n \"run_in_background is denied. Foreground Bash calls stay under 2 min (longer ones block operator messages and supervision and are denied);\",\n `run a long job detached: ${WAIT_RECIPE}.`,\n \"Kill only processes you started, by PID (`cmd & echo $! > /tmp/x.pid`, then `kill $(cat /tmp/x.pid)`); never `pkill`/`killall`/`kill $(pgrep ...)` — other workers' command lines contain your spec text.\",\n ...fileNotInlineBlock(),\n \"\",\n `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,\n \"run (the prompt of a resumed run is the operator's too). Answer it FIRST, in one or two plain lines —\",\n \"a question gets its answer, an instruction gets one line naming what will change — then continue the\",\n \"task you were on.\",\n \"\",\n \"Sub-workers: you may start your own workers with `pai worker run --class <class> -p '<prompt>'`\",\n \"(Bash tool). Your children run on their own provider and report back to you automatically when\",\n \"they finish. Send a handoff UP instead of doing work yourself when a cheaper provider would\",\n \"suffice, the task is out of your scope, or a decision is needed:\",\n \"`pai worker handoff '{\\\"kind\\\":\\\"proposal\\\",\\\"text\\\":\\\"…\\\",\\\"data\\\":{…}}'` (kinds: proposal,\",\n \"question, blocker; results are sent for you when you finish). Sibling workers are not a\",\n \"coordination path — there is no sideways channel; everything goes up to your parent.\",\n \"\",\n ];\n lines.push(\n ...(format === \"ag2\"\n ? ag2ReportBlock()\n : jsonReportBlock(\n \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n ))\n );\n if (format === \"json\") {\n lines.push(\n \"changed: files you touched (path + one-line summary). commands: the commands that verify the work.\",\n \"checks: each with ok true/false and the evidence in detail. open: what you could not finish, if anything.\",\n \"notes: one line, the headline a reviewer reads first.\"\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * The system prompt appended to every headless run, chosen by task class and\n * report format. `spotcheck`/`simple` get a short contract (no delegation\n * language, under 1200 chars in json format / 1500 in ag2); every other class (and no class) gets the\n * full contract. Both end with the AG2 report block unless `format` is\n * \"json\", the pre-AG2 fallback.\n */\nexport function workerContractPrompt(cls: string | undefined, format: \"ag2\" | \"json\"): string {\n return SHORT_CONTRACT_CLASSES.has(cls ?? \"\") ? shortContractPrompt(format) : fullContractPrompt(format);\n}\n\n/**\n * The line appended to a headless prompt's own text (never the system\n * prompt): models weight an instruction at the end of the user turn more\n * than one buried in a long system-prompt paragraph, and this is the\n * difference between a worker that returns a valid AG2/JSON report and one\n * that answers in prose despite the contract (measured 2026-09-20 on both\n * Haiku and Sonnet with the AG2 contract alone).\n */\nexport function promptTrailer(format: \"ag2\" | \"json\"): string {\n return format === \"ag2\"\n ? \"\\n\\nFinal message: one AG2 R message only, no prose (format in system prompt).\"\n : \"\\n\\nFinal message: a JSON object only, no prose before or after, no code fence (format in system prompt).\";\n}\n\n/** The exact text sent to resend a final message that failed AG2 validation. */\nexport const AG2_REASK_TEXT =\n \"Your final message was not a valid AG2 R message. Resend it now as a single AG2 R message only, \" +\n \"no prose, no code fence. Minimal example (one key=value per line, first line R):\\n\" +\n \"R\\ni=task-id\\nr=+\\nG=+\\nc=path summary\\nt=Name+\\np=proof command\\nz=the answer, one line\\n\" +\n \"(r is + - ~ ? !; r=+ needs G=+ and p; when r is not +, add y=why.)\";\n\n/** The full-contract AG2 prompt — the keepalive path's identical-construction baseline. */\nexport const WORKER_CONTRACT_PROMPT = fullContractPrompt(\"ag2\");\n\nexport interface WorkerReport {\n changed?: Array<{ path?: string; summary?: string }>;\n commands?: string[];\n checks?: Array<{ name?: string; ok?: boolean; detail?: string }>;\n open?: string[];\n notes?: string;\n /** AG2 `r` (res): the single outcome character, when the report was AG2. */\n result?: \"+\" | \"-\" | \"~\" | \"?\" | \"!\";\n /** AG2 `y` (why): one line explaining a non-\"+\" result. */\n why?: string;\n /** Which wire format the final message was parsed as. */\n format?: \"ag2\" | \"json\";\n}\n\n/**\n * Extract the report from a worker's final message: AG2 first (an `R`\n * message, see agentish.ts), then the JSON contract as a fallback for\n * workers still on the pre-AG2 shape. Accepts a bare JSON object, a\n * ```json fenced block, or an object embedded in surrounding text — only\n * objects that look like the contract count (at least one of changed /\n * checks / notes) — any other JSON falls through to null so the raw text is\n * kept as-is.\n */\nexport function parseWorkerReport(text: string): WorkerReport | null {\n const trimmed = (text ?? \"\").trim();\n if (!trimmed) return null;\n\n const ag2 = parseAg2Report(trimmed);\n if (ag2 && ag2.kind === \"R\") return ag2ToWorkerReport(ag2);\n\n const candidates: string[] = [];\n const fence = trimmed.match(/```(?:json)?\\s*(\\{[\\s\\S]*?\\})\\s*```/);\n if (fence) candidates.push(fence[1]);\n if (trimmed.startsWith(\"{\")) {\n // a bare object may still carry trailing punctuation/newlines\n candidates.push(trimmed);\n }\n // object embedded in prose: first \"{\" to the matching last \"}\"\n const first = trimmed.indexOf(\"{\");\n const last = trimmed.lastIndexOf(\"}\");\n if (first >= 0 && last > first) candidates.push(trimmed.slice(first, last + 1));\n for (const cand of candidates) {\n try {\n const v = JSON.parse(cand) as unknown;\n if (typeof v === \"object\" && v !== null && !Array.isArray(v) && looksLikeReport(v)) {\n return { ...(v as WorkerReport), format: \"json\" };\n }\n } catch {\n // try the next candidate\n }\n }\n return null;\n}\n\n/** The contract's fingerprint — guards against unrelated JSON in a reply. */\nfunction looksLikeReport(v: object): boolean {\n const o = v as WorkerReport;\n return Array.isArray(o.changed) || Array.isArray(o.checks) || typeof o.notes === \"string\";\n}\n\n/**\n * Render a parsed report as the compact block the viewer shows: changed paths\n * with summaries, checks with ✓/✗, open items, notes. Empty report → [].\n */\nexport function renderReport(c: Paint, prefix: string, r: WorkerReport, cwd = \"\"): string[] {\n const out: string[] = [];\n const has = (xs: unknown[] | undefined) => (xs ?? []).length > 0;\n if (has(r.changed)) {\n out.push(`${prefix}${c(\"bold\", \"changed\")}`);\n for (const ch of r.changed!) {\n out.push(`${prefix} ${relShort(ch.path ?? \"?\", cwd)} — ${shortText(ch.summary ?? \"\", 90)}`);\n }\n }\n if (has(r.checks)) {\n out.push(`${prefix}${c(\"bold\", \"checks\")}`);\n for (const ck of r.checks!) {\n const mark = ck.ok === false ? c(\"red\", \"✗\") : ck.ok === true ? c(\"green\", \"✓\") : c(\"dim\", \"·\");\n const detail = ck.detail ? c(\"dim\", ` · ${shortText(ck.detail, 80)}`) : \"\";\n out.push(`${prefix} ${mark} ${ck.name ?? \"?\"}${detail}`);\n }\n }\n if (has(r.commands)) {\n out.push(`${prefix}${c(\"bold\", \"commands\")}`);\n for (const cmd of r.commands!) out.push(`${prefix} ${c(\"dim\", shortText(cmd, 100))}`);\n }\n if (has(r.open)) {\n out.push(`${prefix}${c(\"bold\", \"open\")}`);\n for (const o of r.open!) out.push(`${prefix} ${c(\"yellow\", shortText(o, 100))}`);\n }\n if (r.notes) out.push(`${prefix}${c(\"bold\", \"notes\")} ${shortText(r.notes, 120)}`);\n return out;\n}\n\nexport interface ReportVerifyFailure {\n path: string;\n reason: \"missing\" | \"not-deleted\" | \"stale\";\n}\n\nexport interface ReportVerifyResult {\n ok: boolean;\n failures: ReportVerifyFailure[];\n}\n\n/** A summary that names its own deletion — the path is expected to be gone. */\nconst DELETED_RE = /\\b(delete|deleted|deleting|remove|removed|removing)\\b/i;\n\n/**\n * Check every path a report's `changed` list claims against the filesystem:\n * it must exist (mtime at or after `startMs`, the run's start) unless its\n * summary says it was deleted, in which case it must be absent. Mechanical\n * only — counts, row totals and other free-text claims are not checked (a\n * worker's R report is trusted as written; a 2026-09-23 report claimed \"181\n * rows saved\" while it had written 31, and only its listed paths are\n * something the filesystem can confirm or refute).\n */\nexport function verifyReportChanges(\n changed: Array<{ path?: string; summary?: string }> | undefined,\n cwd: string,\n startMs: number\n): ReportVerifyResult {\n const failures: ReportVerifyFailure[] = [];\n for (const ch of changed ?? []) {\n const raw = (ch.path ?? \"\").trim();\n if (!raw) continue;\n const abs = isAbsolute(raw) ? raw : resolve(cwd, raw);\n let mtimeMs: number | null;\n try {\n mtimeMs = statSync(abs).mtimeMs;\n } catch {\n mtimeMs = null;\n }\n const deleted = DELETED_RE.test(ch.summary ?? \"\");\n if (deleted) {\n if (mtimeMs !== null) failures.push({ path: raw, reason: \"not-deleted\" });\n } else if (mtimeMs === null) {\n failures.push({ path: raw, reason: \"missing\" });\n } else if (mtimeMs < startMs) {\n failures.push({ path: raw, reason: \"stale\" });\n }\n }\n return { ok: failures.length === 0, failures };\n}\n\n/** The one-line note appended to a report/ledger when verify fails. */\nexport function verifyFailNote(v: ReportVerifyResult): string {\n const paths = v.failures.map((f) => f.path).slice(0, 5).join(\", \");\n return `verify: ${v.failures.length} claimed change${v.failures.length === 1 ? \"\" : \"s\"} not confirmed: ${paths}`;\n}\n\n/** repo-relative display of a changed path when it lies under cwd. */\nfunction relShort(p: string, cwd: string): string {\n if (!cwd) return p;\n const r = relative(cwd, p);\n return r && !r.startsWith(\"..\") ? r : p;\n}\n","/**\n * deadline.ts — the time gate of a worker run: resolving --max-minutes /\n * --deadline / workers.defaultMaxMinutes into one absolute deadline, the\n * prompt line that tells the worker, the ps label, and the timer that stops\n * the child (SIGTERM, then SIGKILL after a grace period).\n */\n\nimport type { WorkerStatus } from \"./status.js\";\n\nexport interface RunLimit {\n /** Epoch ms the run is stopped at. */\n deadlineAt: number;\n /** Minutes from launch to the deadline. */\n minutes: number;\n}\n\n/** Local HH:MM → epoch ms: today, or tomorrow when already past. */\nexport function parseDeadline(hhmm: string, now: Date = new Date()): number {\n const m = /^(\\d{1,2}):(\\d{2})$/.exec(hhmm.trim());\n if (!m || Number(m[1]) > 23 || Number(m[2]) > 59) {\n throw new Error(`--deadline: expected HH:MM (local time, 00:00-23:59), got \"${hhmm}\"`);\n }\n const at = new Date(now);\n at.setHours(Number(m[1]), Number(m[2]), 0, 0);\n if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 1);\n return at.getTime();\n}\n\n/** The run's limit, or null when disabled (--max-minutes 0 / default 0). */\nexport function resolveLimit(\n flags: { maxMinutes?: number; deadline?: string },\n defaultMinutes: number,\n now: Date = new Date()\n): RunLimit | null {\n if (flags.maxMinutes !== undefined && flags.deadline !== undefined) {\n throw new Error(\"--max-minutes and --deadline are mutually exclusive\");\n }\n if (flags.deadline !== undefined) {\n const deadlineAt = parseDeadline(flags.deadline, now);\n return { deadlineAt, minutes: Math.max(1, Math.round((deadlineAt - now.getTime()) / 60_000)) };\n }\n const minutes = flags.maxMinutes ?? defaultMinutes;\n if (!Number.isFinite(minutes) || minutes < 0) throw new Error(`--max-minutes: expected a non-negative number, got ${minutes}`);\n return minutes === 0 ? null : { deadlineAt: now.getTime() + minutes * 60_000, minutes };\n}\n\nconst pad = (n: number): string => String(n).padStart(2, \"0\");\n\n/** The one line injected into the worker's system prompt. */\nexport function deadlinePromptLine(limit: RunLimit): string {\n const d = new Date(limit.deadlineAt);\n return (\n `Hard deadline ${pad(d.getHours())}:${pad(d.getMinutes())} (${limit.minutes} min). ` +\n \"Finish and report before it; at the deadline the run is stopped and whatever you have is salvaged.\"\n );\n}\n\n/** ps column: \"36s left\", \"12m left\", \"1h05m left\" for running; \"OVERDUE ...\" for overdue; \"timed out\" for stopped; null otherwise. */\nexport function deadlineLabel(\n s: Pick<WorkerStatus, \"state\" | \"deadlineAt\" | \"timedOut\">,\n now: number = Date.now()\n): string | null {\n if (s.timedOut) return \"timed out\";\n if (s.state !== \"running\" || !s.deadlineAt) return null;\n const secs = Math.abs(s.deadlineAt - now) / 1_000;\n let label: string;\n if (secs < 60) {\n label = `${Math.floor(secs)}s`;\n } else if (secs < 3600) {\n label = `${Math.floor(secs / 60)}m`;\n } else {\n const hours = Math.floor(secs / 3600);\n const mins = Math.floor((secs % 3600) / 60);\n label = `${hours}h${String(mins).padStart(2, \"0\")}m`;\n }\n return s.deadlineAt >= now ? `${label} left` : `OVERDUE ${label}`;\n}\n\n/** Grace between SIGTERM and SIGKILL. */\nexport const DEADLINE_GRACE_MS = 10_000;\n\n/**\n * Stop `proc` at the deadline: `onFire`, SIGTERM, SIGKILL after `graceMs`\n * when it is still alive. Returns the disarm function.\n */\nexport function armDeadline(\n proc: { kill(sig?: NodeJS.Signals): boolean; exitCode: number | null; signalCode: NodeJS.Signals | null },\n limit: RunLimit,\n onFire: () => void,\n graceMs: number = DEADLINE_GRACE_MS\n): () => void {\n let kill: NodeJS.Timeout | null = null;\n const t = setTimeout(() => {\n onFire();\n try {\n proc.kill(\"SIGTERM\");\n } catch {\n /* already gone */\n }\n kill = setTimeout(() => {\n if (proc.exitCode === null && proc.signalCode === null) {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n }\n }, graceMs);\n kill.unref();\n }, Math.max(0, limit.deadlineAt - Date.now()));\n return () => {\n clearTimeout(t);\n if (kill) clearTimeout(kill);\n };\n}\n","/**\n * browser-tools.ts — what `--no-browser` strips: --chrome, every browser MCP\n * tool grant (mcp__claude-in-chrome__*, browsr, playwright, …) and browser\n * server names, from the caller's claude args. The PreToolUse worker guard\n * denies the same tools (hooks/ts/lib/worker-guard.ts) when\n * PAI_WORKER_NO_BROWSER=1.\n */\n\nconst BROWSER_SERVER = /chrome|browsr|browser|playwright|puppeteer/i;\n\n/** A browser MCP server name (bare, e.g. \"browsr\") or tool name (mcp__browsr__open). */\nexport function isBrowserTool(name: string): boolean {\n const server = /^mcp__(.+?)(__|$)/.exec(name)?.[1] ?? name;\n return BROWSER_SERVER.test(server) && (name.startsWith(\"mcp__\") || !name.includes(\"__\"));\n}\n\nconst keepNonBrowser = (list: string): string[] =>\n list.split(\",\").map((s) => s.trim()).filter((s) => s && !isBrowserTool(s));\n\n/** claude args without --chrome and browser tool grants (an emptied --allowedTools goes too). */\nexport function stripBrowserArgs(rest: string[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i];\n if (a === \"--chrome\") continue;\n if (a === \"--allowedTools\" && i + 1 < rest.length && !rest[i + 1].startsWith(\"-\")) {\n const kept = keepNonBrowser(rest[++i]);\n if (kept.length) out.push(a, kept.join(\",\"));\n } else if (a.startsWith(\"--allowedTools=\")) {\n const kept = keepNonBrowser(a.slice(\"--allowedTools=\".length));\n if (kept.length) out.push(`--allowedTools=${kept.join(\",\")}`);\n } else out.push(a);\n }\n return out;\n}\n\n/** Comma-list entries (--mcp values, class MCP) without browser servers. */\nexport function stripBrowserNames(names: string[]): string[] {\n return names.map((n) => keepNonBrowser(n).join(\",\")).filter(Boolean);\n}\n","/**\n * codex.ts — the \"codex\" runner engine.\n *\n * A ChatGPT plan gives no API key, only Codex CLI access, so a provider may\n * set `engine: \"codex\"`: `run` then shells out to `codex exec --json <prompt>`\n * (non-interactive) instead of Claude Code. The JSONL event stream is parsed\n * into the same status-file fields (turns, tools, last) and ledger lines as a\n * Claude run, and is normalised into claude-code-shaped events in the\n * worker's .jsonl so follow/replay/the pane render it unchanged.\n *\n * Implemented against the documented `codex exec --json` interface\n * (thread.started / item.completed / turn.completed / turn.failed lines);\n * verify against the installed CLI when one is present.\n * `--allowedTools` has no Codex equivalent and is dropped (ledgered).\n */\n\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { providerKeyPath, resolveProviderKey, type WorkerProvider } from \"./config.js\";\n\n/** Build the codex exec argument vector (without the binary itself). */\nexport function buildCodexArgs(prompt: string, model: string | undefined): string[] {\n return [\n \"exec\",\n \"--json\",\n \"--skip-git-repo-check\",\n ...(model ? [\"-m\", model] : []),\n \"--\",\n prompt,\n ];\n}\n\n/** Env for a codex run: caller's env, Anthropic vars stripped, key applied. */\nexport function buildCodexEnv(provider: WorkerProvider): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n delete env.ANTHROPIC_API_KEY;\n delete env.ANTHROPIC_BASE_URL;\n delete env.ANTHROPIC_AUTH_TOKEN;\n delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;\n delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;\n delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;\n // API-key providers: OpenAI env from key/keyFile/baseUrl; ChatGPT-login\n // codex (neither set) keeps its own auth from ~/.codex. A key_file that\n // does not exist is tolerated here (unlike resolveProviderKey's other\n // callers) — that is the ChatGPT-login shape, not a broken config.\n const keyPath = providerKeyPath(provider);\n if (provider.key || (keyPath && existsSync(keyPath))) {\n env.OPENAI_API_KEY = resolveProviderKey(provider) ?? \"\";\n }\n if (provider.upstreamUrl) env.OPENAI_BASE_URL = provider.upstreamUrl;\n for (const [k, v] of Object.entries(provider.env)) env[k] = v;\n env.PAI_WORKER = \"1\";\n return env;\n}\n\n/** Codex \"not applicable\" flags the runner drops with a ledger note. */\nexport function codexDroppedFlags(argv: string[]): string[] {\n const dropped: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === \"--allowedTools\" || a === \"--disallowedTools\" || a === \"--mcp-config\" || a === \"--mcp\") {\n dropped.push(a);\n if (i + 1 < argv.length && !argv[i + 1].startsWith(\"-\")) i++; // and its value\n } else if (a.startsWith(\"--allowedTools=\") || a.startsWith(\"--mcp-config=\")) {\n dropped.push(a.split(\"=\")[0]);\n }\n }\n return dropped;\n}\n\n// ---------------------------------------------------------------------------\n// Event stream parsing (pure — tested with recorded JSONL lines)\n// ---------------------------------------------------------------------------\n\n/** One normalised claude-code-shaped event + its effect on the status. */\nexport interface CodexEventResult {\n events: Array<Record<string, unknown>>;\n turns: number;\n tools: number;\n last: string | null;\n isError: boolean;\n contextTokens: number | null;\n finalText: string | null;\n threadId: string | null;\n}\n\nexport const emptyCodexResult = (): CodexEventResult => ({\n events: [],\n turns: 0,\n tools: 0,\n last: null,\n isError: false,\n contextTokens: null,\n finalText: null,\n threadId: null,\n});\n\n/**\n * Fold one parsed `codex exec --json` line into the running result: appends\n * normalised transcript events and updates the counters in place.\n */\nexport function foldCodexLine(line: unknown, r: CodexEventResult): void {\n if (typeof line !== \"object\" || line === null) return;\n const e = line as Record<string, unknown>;\n const type = typeof e.type === \"string\" ? e.type : \"\";\n\n if (type === \"thread.started\" && typeof e.thread_id === \"string\") {\n r.threadId = e.thread_id;\n return;\n }\n if (type === \"item.completed\" && typeof e.item === \"object\" && e.item !== null) {\n const item = e.item as Record<string, unknown>;\n const kind = typeof item.type === \"string\" ? item.type : \"\";\n if (kind === \"agent_message\" && typeof item.text === \"string\") {\n r.turns += 1;\n r.last = `says: ${item.text.slice(0, 70)}`;\n r.finalText = item.text;\n r.events.push({\n type: \"assistant\",\n message: { content: [{ type: \"text\", text: item.text }] },\n });\n } else if (kind === \"command_execution\") {\n r.tools += 1;\n const cmd = typeof item.command === \"string\" ? item.command : \"?\";\n const rc = typeof item.exit_code === \"number\" ? item.exit_code : 0;\n r.last = `Bash: ${cmd.slice(0, 70)}`;\n r.events.push({\n type: \"assistant\",\n message: { content: [{ type: \"tool_use\", name: \"Bash\", input: { command: cmd } }] },\n });\n r.events.push({\n type: \"user\",\n message: {\n content: [\n {\n type: \"tool_result\",\n tool_use_id: \"\",\n is_error: rc !== 0,\n content: item.aggregated_output ?? \"\",\n },\n ],\n },\n });\n } else if (kind === \"file_change\") {\n r.tools += 1;\n const changes = Array.isArray(item.changes) ? item.changes : [];\n const files = changes\n .map((c) => (typeof c === \"object\" && c !== null && typeof (c as { path?: unknown }).path === \"string\" ? (c as { path: string }).path : \"?\"))\n .join(\", \");\n r.last = `files: ${files.slice(0, 70)}`;\n r.events.push({\n type: \"assistant\",\n message: { content: [{ type: \"tool_use\", name: \"Write\", input: { file_path: files } }] },\n });\n } else if (kind === \"mcp_tool_call\") {\n r.tools += 1;\n r.last = `mcp: ${String(item.tool ?? \"?\")}`;\n }\n return;\n }\n if (type === \"turn.completed\") {\n const usage = (typeof e.usage === \"object\" && e.usage !== null ? e.usage : {}) as {\n input_tokens?: number;\n output_tokens?: number;\n cached_input_tokens?: number;\n };\n const tokens =\n (usage.input_tokens ?? 0) + (usage.cached_input_tokens ?? 0) + (usage.output_tokens ?? 0);\n if (tokens > 0) r.contextTokens = tokens;\n return;\n }\n if (type === \"turn.failed\" || type === \"error\") {\n r.isError = true;\n const err = typeof e.error === \"object\" && e.error !== null ? e.error : {};\n r.last = String((err as { message?: unknown }).message ?? e.message ?? \"codex turn failed\");\n }\n}\n\n/** Parse one JSONL line; null for blanks and non-JSON noise. */\nexport function parseCodexLine(line: string): unknown | null {\n const t = line.trim();\n if (!t.startsWith(\"{\")) return null;\n try {\n return JSON.parse(t) as unknown;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// The engine probe used by `providers test`\n// ---------------------------------------------------------------------------\n\n/** True when the Codex CLI is on PATH (test reports \"not installed\" else). */\nexport function codexInstalled(): boolean {\n try {\n execFileSync(\"codex\", [\"--version\"], { timeout: 5000, stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Spawn helper shared by run and test; caller wires stdout. */\nexport function spawnCodex(args: string[], env: NodeJS.ProcessEnv, cwd: string) {\n return spawn(\"codex\", args, { env, cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n}\n","/**\n * run.ts — the worker runner (port of the glm / glm-run pair, provider-neutral).\n *\n * One claude-code process per call, pointed at the chosen provider:\n *\n * - env: ANTHROPIC_BASE_URL/AUTH_TOKEN from the provider (token from its\n * key file, never from the environment), the three DEFAULT_*_MODEL vars,\n * the provider's extra env, nonessential traffic off, and ANTHROPIC_API_KEY\n * stripped so nothing can fall back to Anthropic billing. Headless runs\n * also drop the spawner's session identity (messaging socket, session id,\n * nesting markers) — inherited, a worktree child starts tool-blind.\n * OpenAI-protocol providers point at the PAI proxy instead (started on\n * demand, the provider name in the URL path); codex-engine providers run\n * the Codex CLI.\n * - headless (-p): strict empty MCP config unless the caller brings one or\n * names servers via --mcp / a role (then a filtered <id>.mcp.json), the\n * core tool grants the caller did not bring (a headless run cannot\n * approve a permission-gated tool mid-flight — with no grant at all,\n * claude drops the file/shell tools entirely and the worker is\n * tool-blind), PAI_WORKER=1 so PAI's per-session hooks leave it alone,\n * the worker\n * contract appended to the system prompt, `--input-format stream-json`\n * with the prompt as the first stdin user message (the operator socket\n * can add more mid-run), stream-json mirroring (every line stamped `_ts`)\n * into <logDir>/<id>.jsonl, a live <id>.status for ps/follow/status line\n * (context meter included), ledger lines, result printed in the caller's\n * --output-format (json adds the parsed `report`), and the follow pane\n * (unless --no-pane).\n * - interactive: no MCP/tool restriction by default, no pane,\n * ENABLE_TOOL_SEARCH=true. A project that pinned a list with `pai\n * project mcp` / `pai project tools` gets `--strict-mcp-config\n * --mcp-config <filtered>` / `--tools <list>` built the same way a\n * headless run's allowlist is; an explicit --mcp or --tools on this\n * command line still wins over the project's pin.\n *\n * Auto-routed runs that die of a quota error before the first tool call are\n * restarted on the next provider in routing order (WORKER-REROUTE ledger line).\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport {\n existsSync,\n mkdirSync,\n openSync,\n writeFileSync,\n closeSync,\n writeSync,\n} from \"node:fs\";\nimport { appendSystemPromptArgs, parseRunnerArgs, shortText, stripPromptValues } from \"./args.js\";\nimport {\n assertProviderRunnable,\n classModelCapability,\n isModelCapability,\n NATIVE_ANTHROPIC_MODELS,\n readWorkersSection,\n resolveModelCapability,\n type WorkerProvider,\n} from \"./config.js\";\nimport { buildRunEnv, claudeCommand } from \"./run-env.js\";\n\nexport { buildRunEnv } from \"./run-env.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { decideEndReason, endFields, parentInfo, type EndInfo } from \"./endreason.js\";\nimport {\n ensureNoMcpConfig,\n eventsPath,\n ledgerPath,\n noMcpConfigPath,\n workersLogDir,\n} from \"./paths.js\";\nimport {\n type WorkerStatus,\n newWorkerId,\n saveStatus,\n describeTool,\n nowStamp,\n parseSleepSecs,\n UNLABELED,\n} from \"./status.js\";\nimport { resolveSession, resolveSpawnerSession } from \"./scope.js\";\nimport { returnControlsIfHeld } from \"./controls.js\";\nimport {\n capabilityForRun,\n isQuotaFailure,\n nextAutoProvider,\n resolveCapabilityRun,\n resolveTarget,\n setCooldown,\n type CapabilityRunResolution,\n} from \"./routing.js\";\nimport { runImageCapability } from \"./engines/image.js\";\nimport { openPaneForWorker, paneBackend } from \"./pane.js\";\nimport { assertChildAllowed, checkOrphanedChildren, isWorkerId, launchParent } from \"./tree.js\";\nimport { deliverHandoff, isHandoffMessage } from \"./handoff.js\";\nimport {\n addWorktree,\n salvageOnExit,\n gcWorktreesThrottled,\n inPlaceSystemPrompt,\n recordWorktree,\n worktreeSystemPrompt,\n worktreeWanted,\n type WorktreeInfo,\n} from \"./worktree.js\";\nimport {\n OPERATOR_MARK,\n AG2_REASK_TEXT,\n promptTrailer,\n workerContractPrompt,\n parseWorkerReport,\n verifyReportChanges,\n verifyFailNote,\n type WorkerReport,\n} from \"./report.js\";\nimport { validateAg2 } from \"./agentish.js\";\nimport { armDeadline, deadlinePromptLine, resolveLimit, type RunLimit } from \"./deadline.js\";\nimport { stripBrowserArgs, stripBrowserNames } from \"./browser-tools.js\";\nimport { expandMcpNames, grantsChrome, mcpServersFromToolGrants, writeMcpConfig } from \"./mcp.js\";\nimport { pinNotices, projectLaunchConfig, type ProjectLaunchConfig } from \"./project-config.js\";\nimport { createOperatorServer } from \"./operator.js\";\nimport { DEFAULT_PROXY_PORT, ensureProxyRunning } from \"./proxy/server.js\";\nimport {\n buildCodexArgs,\n buildCodexEnv,\n codexDroppedFlags,\n codexInstalled,\n emptyCodexResult,\n foldCodexLine,\n parseCodexLine,\n} from \"./codex.js\";\n\nexport interface RunOptions {\n providerFlag?: string;\n /** --class value (the old --role): a task class from workers.classes. */\n className?: string;\n modelFlag?: string;\n label?: string;\n noPane?: boolean;\n /** --spec value, resolved for display/recording (\"-\" for stdin). */\n specPath?: string;\n /** --mcp value: server/set names, comma-separated. */\n mcpFlag?: string;\n /** Everything after `--` (the claude args). */\n claudeArgs: string[];\n /** Working directory for the run (default: this process's cwd). */\n cwd?: string;\n /** Chain stage bookkeeping: the chain id this stage belongs to. */\n parent?: string;\n /** Chain stage bookkeeping: the class name of this stage. */\n stage?: string;\n /** Suppress result printing (MCP worker_run: its stdout is the RPC channel). */\n quiet?: boolean;\n /** Internal: notified with the worker id once it exists (resume uses it). */\n onWorkerStart?: (wid: string) => void;\n /** Internal: preset worker id (the planner mints its id before phase 1). */\n id?: string;\n /** --worktree/--no-worktree; undefined lets the class default decide. */\n worktreeFlag?: boolean;\n /** --max-minutes: stop the run after N minutes (0 = no limit; default workers.defaultMaxMinutes). */\n maxMinutes?: number;\n /** --deadline HH:MM (local): stop the run then; exclusive with maxMinutes. */\n deadline?: string;\n /** --browser / --no-browser: undefined follows workers.noBrowserByDefault. */\n browser?: boolean;\n /** --print-cmd: print the assembled claude argv as JSON and exit, no spawn. */\n printCmd?: boolean;\n /** Internal: suppress recursion depth on reroute. */\n _reroutes?: number;\n /** Internal: this run is the planner's phase-1 worker, not a new orchestration. */\n _planner?: boolean;\n /** --report json|ag2: which contract/parser the run uses (default: ag2, or PAI_WORKER_REPORT). */\n reportFormatFlag?: \"json\" | \"ag2\";\n /** --no-report-retry: skip the one bounded re-ask on an invalid AG2 final message. */\n noReportRetry?: boolean;\n /** --capability/--for: run through resolveCapability instead of resolveTarget. */\n capabilityFlag?: string;\n /** --out: image-engine output path (default: <logDir>/<id>.png). */\n imageOut?: string;\n /** --size: image-engine \"WIDTHxHEIGHT\" (default: 1024x1024). */\n imageSize?: string;\n /** --timeout-ms: image-engine request timeout (default: 120000). */\n imageTimeoutMs?: number;\n}\n\n/**\n * ag2 unless the caller passed --report json or PAI_WORKER_REPORT=json is\n * set in the environment; an explicit flag always wins over the env default.\n */\nexport function resolveReportFormat(\n flag: \"json\" | \"ag2\" | undefined,\n env: NodeJS.ProcessEnv = process.env\n): \"json\" | \"ag2\" {\n if (flag) return flag;\n return env.PAI_WORKER_REPORT === \"json\" ? \"json\" : \"ag2\";\n}\n\n/** The strict empty MCP config for headless workers, written on demand. */\nexport { ensureNoMcpConfig } from \"./paths.js\";\n\n/** A stream-json user message for the child's stdin. */\nexport function stdinUserMessage(text: string): string {\n return JSON.stringify({ type: \"user\", message: { role: \"user\", content: text } });\n}\n\n/** An operator line as the worker sees it: carrying the contract's marker. */\nexport function operatorUserText(text: string): string {\n return `${OPERATOR_MARK} ${text}`;\n}\n\n/**\n * ISO stamp with seconds, attached to every mirrored event (2g): local time\n * with its offset (`2026-09-17T14:19:23+02:00`), so the viewer can render the\n * wall clock the operator lives in. `offMin` is east-positive minutes —\n * injectable so tests do not depend on the machine's zone.\n */\nexport function isoStamp(d = new Date(), offMin = -d.getTimezoneOffset()): string {\n const t = new Date(d.getTime() + offMin * 60_000);\n const sign = offMin < 0 ? \"-\" : \"+\";\n const abs = Math.abs(offMin);\n const hh = String(Math.floor(abs / 60)).padStart(2, \"0\");\n const mm = String(abs % 60).padStart(2, \"0\");\n return `${t.toISOString().slice(0, 19)}${sign}${hh}:${mm}`;\n}\n\nexport interface UsageBlock {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n}\n\n// a type alias (not an interface): it must stay assignable to\n// Record<string, unknown> when written into the event transcript\nexport type StreamEvent = {\n type?: string;\n subtype?: string;\n session_id?: string;\n model?: string;\n cwd?: string;\n context_window?: number;\n model_info?: { context_window?: number } | null;\n message?: {\n content?: Array<{\n type?: string;\n text?: string;\n name?: string;\n id?: string;\n input?: unknown;\n /** tool_result blocks: the result payload (string or text blocks). */\n content?: unknown;\n is_error?: boolean;\n }>;\n usage?: UsageBlock;\n };\n usage?: UsageBlock;\n result?: string;\n is_error?: boolean;\n is_compact?: boolean;\n num_turns?: number;\n duration_ms?: number;\n /** API time of the final turn — the TTFT proxy on zeroed per-turn usage. */\n duration_api_ms?: number;\n};\n\n/** Context tokens of an assistant/result usage block (input+cache+output). */\nexport function usageContextTokens(u: UsageBlock | undefined): number | null {\n if (!u) return null;\n const t =\n (u.input_tokens ?? 0) +\n (u.cache_read_input_tokens ?? 0) +\n (u.cache_creation_input_tokens ?? 0) +\n (u.output_tokens ?? 0);\n return t > 0 ? t : null;\n}\n\n/**\n * Context never shrinks mid-segment, so the status keeps the high-water\n * mark of the usage it has seen: a smaller later reading (short reply,\n * sidechain answer) must not drag the meter down. Compaction is the one\n * legitimate drop — see `resetContextTokensOnCompact`.\n */\nexport function bumpContextTokens(status: Pick<WorkerStatus, \"contextTokens\">, tokens: number | null): void {\n if (tokens === null || tokens <= 0) return;\n status.contextTokens = Math.max(status.contextTokens ?? 0, tokens);\n}\n\n/**\n * A compact boundary (`system`/`compact_boundary`, the shape Claude Code\n * writes with `compactMetadata.preTokens`; `compact` kept as the older\n * spelling) legitimately restarts the context at a lower size: the floor\n * drops to the event's own usage — usually none — so the next usage\n * reading re-seeds the meter at the fresh, smaller context.\n */\nexport function resetContextTokensOnCompact(status: Pick<WorkerStatus, \"contextTokens\">, e: StreamEvent): void {\n status.contextTokens = usageContextTokens(e.usage) ?? null;\n}\n\n/** A compact boundary in a worker's stream, in either event spelling. */\nexport function isCompactBoundary(e: StreamEvent): boolean {\n return e.type === \"system\" && (e.subtype === \"compact_boundary\" || e.subtype === \"compact\");\n}\n\n/** The command string of a Bash tool_use input, \"\" when there is none. */\nfunction bashCommand(input: unknown): string {\n if (typeof input === \"object\" && input !== null && typeof (input as { command?: unknown }).command === \"string\") {\n return (input as { command: string }).command;\n }\n return \"\";\n}\n\nfunction toolResultText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return content\n .map((c) =>\n typeof c === \"object\" && c !== null && typeof (c as { text?: unknown }).text === \"string\"\n ? (c as { text: string }).text\n : \"\"\n )\n .join(\"\\n\");\n }\n return \"\";\n}\n\n/**\n * Whether a tool_result block is a failed execution: the harness's own\n * is_error flag, or — the older Bash spelling — an \"Exit code N\" line with a\n * non-zero N in the result text. Feeds the consecFails counter supervision's\n * thrashing detector reads.\n */\nexport function toolResultFailed(block: { is_error?: boolean; content?: unknown }): boolean {\n if (block.is_error === true) return true;\n const m = toolResultText(block.content).match(/Exit code (\\d+)/);\n return m !== null && Number(m[1]) !== 0;\n}\n\n/** CLI aliases the built-in provider hands to `--model` (NATIVE_ANTHROPIC_MODELS'\n * values): a bare name like \"sonnet\", not a concrete id, so the init event's\n * resolved id is always the more useful thing to display. */\nconst NATIVE_MODEL_ALIASES = new Set(Object.values(NATIVE_ANTHROPIC_MODELS));\n\n/**\n * The model the init event announces, adopted into the status when the spawn\n * could not name one (a caller that passed `--model` itself in the claude\n * args, or a provider whose table has no entry for the capability) or named\n * only a CLI alias (the built-in provider's \"sonnet\"/\"haiku\", which tracks\n * whatever release is current — the init event says which one actually ran).\n * The run's real model is then only knowable from the first event it sends.\n * Never overwrites a concrete resolved or pinned model — that stays the\n * recorded truth.\n */\nexport function adoptInitModel(status: Pick<WorkerStatus, \"model\">, e: StreamEvent): void {\n if (status.model && !NATIVE_MODEL_ALIASES.has(status.model)) return;\n const m = (e.model ?? \"\").trim();\n if (m) status.model = m;\n}\n\n/**\n * The model a run goes out on: an explicit `--model` wins; else the class\n * target's alias (\"glm/fast\") names the capability; else the capability the\n * class implies (image → image model, spotcheck/simple → fast, everything\n * else → default), resolved against the provider's model table. The built-in\n * anthropic provider resolves through the same table as any other, so a\n * worker never inherits the orchestrator session's model.\n */\nexport function resolveRunModel(\n target: { provider: WorkerProvider; modelAlias: string | null },\n className?: string,\n modelFlag?: string\n): string {\n if (modelFlag) return modelFlag;\n const alias = target.modelAlias;\n const capability =\n alias && isModelCapability(alias) ? alias : classModelCapability(className);\n return resolveModelCapability(target.provider, capability);\n}\n\n/**\n * The `--model` part of the claude argv. Skipped when the caller already\n * pinned one in the claude args (it must not be clobbered) or when nothing\n * resolved — `--model \"\"` would break the spawn.\n */\nexport function modelArgs(model: string, callerPinned: boolean): string[] {\n return !callerPinned && model ? [\"--model\", model] : [];\n}\n\n/**\n * Whether the run's assembled cmd gets a forced --model. Headless workers\n * always take the resolved class/provider model — that table exists so\n * spawned subagents stay cheap. An interactive launch (`pai worker run` with\n * no -p) IS the chat pane, not a subagent: with no --model of the caller's\n * own it gets none here either, so Claude Code's own settings.json model\n * applies, exactly as launching `claude` by hand would (2026-09-20: an\n * interactive supervisor came up on the class model instead of the\n * settings.json [1m] variant, then needed a manual /model switch that\n * rebuilt the whole cache). A caller's own --model, headless or not, is\n * already in the passthrough args and must not be duplicated here.\n */\nexport function modelFlagArgs(headless: boolean, model: string, callerPinned: boolean): string[] {\n return headless ? modelArgs(model, callerPinned) : [];\n}\n\n/**\n * The prompt text actually sent to the worker and whether the trailer got\n * appended. An instruction at the END of the user turn outweighs one buried\n * in the system prompt (measured 2026-09-20: the AG2 contract alone was\n * ignored by both Haiku and Sonnet). Never applied to an interactive launch\n * (no -p) or when the caller brought its own --append-system-prompt — the\n * trailer's \"format in system prompt\" would then point at nothing.\n */\nexport function headlessPromptText(\n prompt: string | null,\n headless: boolean,\n callerSystemPrompt: boolean,\n format: \"ag2\" | \"json\"\n): { text: string | null; applied: boolean } {\n const applied = headless && prompt !== null && !callerSystemPrompt;\n return { text: applied ? prompt! + promptTrailer(format) : prompt, applied };\n}\n\n/**\n * Whether an invalid final report earns the one bounded re-ask: only when a\n * real validator caught the problem (a missing/broken aibroker must never\n * block the run further), the caller did not opt out, and there is a session\n * to resume into. Cap of exactly one is enforced by the caller never calling\n * this twice for the same run.\n */\nexport function shouldRetryReport(\n validation: { ok: boolean; validator: \"aibroker\" | \"none\" },\n noReportRetry: boolean,\n hasSession: boolean\n): boolean {\n return !validation.ok && validation.validator === \"aibroker\" && !noReportRetry && hasSession;\n}\n\n/** Context window announced by the init event, when the endpoint sends one. */\n/**\n * Did the run end well? A headless run must have produced a stream-json\n * result event that is not an error; an interactive launch IS the chat pane\n * and never emits one, so its clean exit (rc 0) is the whole verdict —\n * without this every interactive session that ends normally was recorded\n * as `failed rc=0` and supervision pinged the spawner about a non-failure.\n */\nexport function runSucceeded(\n headless: boolean,\n rc: number,\n resultEvent: Pick<StreamEvent, \"is_error\"> | null,\n): boolean {\n if (rc !== 0) return false;\n if (!headless) return true;\n return resultEvent !== null && !resultEvent.is_error;\n}\n\nexport function initContextWindow(e: StreamEvent): number | null {\n if (typeof e.context_window === \"number\" && e.context_window > 0) return e.context_window;\n if (e.model_info && typeof e.model_info.context_window === \"number\" && e.model_info.context_window > 0) {\n return e.model_info.context_window;\n }\n // the `[1m]` model variant announces a 1M-token window by suffix\n if (/\\[1m\\]$/.test((e.model ?? \"\").trim())) return 1_000_000;\n return null;\n}\n\n/**\n * Run one worker. Returns the process exit code to pass through.\n * Throws WorkersConfigError-shaped Errors for configuration problems.\n */\nexport async function runWorker(opts: RunOptions): Promise<number> {\n const { raw: _raw, workers: config } = readWorkersSection();\n void _raw;\n if (!config.enabled) {\n throw new Error(\n `workers are off. Turn them on with: pai worker on` +\n `\\n(then the Agent-tool hook stops denying Anthropic subagents only when you do)`\n );\n }\n const logDir = workersLogDir(config);\n mkdirSync(logDir, { recursive: true });\n try {\n const gc = gcWorktreesThrottled(logDir)?.filter((r) => r.archive);\n if (gc?.length) process.stderr.write(`pai worker: archived ${gc.length} leftover worktree(s) (pai worker gc)\\n`);\n } catch {\n // best effort\n }\n\n // class plan is not one worker but the planner orchestration (planner.ts)\n if (opts.className === \"plan\" && !opts._planner) {\n const { runPlanner } = await import(\"./planner.js\");\n return runPlanner(opts);\n }\n\n // Sub-worker bookkeeping: an explicit parent (chain stage, planner child)\n // wins, else the worker this process runs inside (PAI_WORKER_ID). Both\n // caps from workers.tree apply to parents that are workers themselves.\n const parent = launchParent(opts.parent);\n if (parent) assertChildAllowed(logDir, parent, config.tree);\n\n // An explicit --provider always bypasses capability resolution entirely;\n // otherwise --capability, or a class whose implied capability is not\n // \"default\" and names no provider of its own, resolves cross-provider.\n const capability = opts.providerFlag\n ? null\n : capabilityForRun(config, { capabilityFlag: opts.capabilityFlag, className: opts.className });\n const capabilityResolution: CapabilityRunResolution | null = capability\n ? resolveCapabilityRun(config, capability)\n : null;\n\n const target = capabilityResolution\n ? {\n providerName: capabilityResolution.providerName,\n provider: capabilityResolution.provider,\n modelAlias: null,\n classMcp: null,\n via: \"class\" as const,\n }\n : resolveTarget(config, logDir, {\n flagProvider: opts.providerFlag,\n className: opts.className,\n });\n assertProviderRunnable(target.providerName, target.provider);\n\n const noBrowser = opts.browser === undefined ? config.noBrowserByDefault : !opts.browser;\n const parsed = parseRunnerArgs(noBrowser ? stripBrowserArgs(opts.claudeArgs) : opts.claudeArgs);\n // The config default is a headless-run guard; an interactive launch is the operator's own session and is\n // limited only by an explicit --max-minutes / --deadline.\n const limit = resolveLimit(opts, parsed.headless ? config.defaultMaxMinutes : 0);\n const label =\n opts.label ??\n shortText(parsed.prompt ?? UNLABELED, 70);\n\n const model = capabilityResolution\n ? capabilityResolution.model\n : resolveRunModel(target, opts.className, opts.modelFlag);\n const reportFormat = resolveReportFormat(opts.reportFormatFlag);\n\n // tracked across every engine (including a quota reroute's recursive\n // runWorker call) so the exit hook below knows which status to check,\n // whatever opts.id/onWorkerStart the caller did or didn't pass\n let workerId: string | undefined = opts.id;\n const trackStart = (wid: string) => {\n workerId = wid;\n opts.onWorkerStart?.(wid);\n };\n\n try {\n let rc: number;\n if (target.provider.engine === \"image\") {\n rc = await executeImageRun({\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n capability: capability ?? \"image\",\n outPath: opts.imageOut,\n size: opts.imageSize,\n timeoutMs: opts.imageTimeoutMs,\n cwd: opts.cwd,\n specPath: opts.specPath,\n parent: parent ?? undefined,\n stage: opts.stage,\n quiet: opts.quiet,\n onWorkerStart: trackStart,\n id: opts.id,\n });\n } else if (target.provider.engine === \"codex\") {\n rc = await executeCodexRun({\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n claudeArgs: opts.claudeArgs,\n noPane: opts.noPane ?? false,\n cwd: opts.cwd,\n specPath: opts.specPath,\n parent: parent ?? undefined,\n stage: opts.stage,\n quiet: opts.quiet,\n onWorkerStart: trackStart,\n id: opts.id,\n worktreeFlag: opts.worktreeFlag,\n className: opts.className,\n reportFormat,\n capability: capability ?? undefined,\n limit,\n noBrowser,\n });\n } else {\n rc = await executeRun({\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n claudeArgs: opts.claudeArgs,\n noPane: opts.noPane ?? false,\n mcpFlag: opts.mcpFlag,\n cwd: opts.cwd,\n specPath: opts.specPath,\n parent: parent ?? undefined,\n stage: opts.stage,\n quiet: opts.quiet,\n onWorkerStart: trackStart,\n id: opts.id,\n capability: capability ?? undefined,\n worktreeFlag: opts.worktreeFlag,\n className: opts.className,\n reroutes: opts._reroutes ?? 0,\n printCmd: opts.printCmd,\n reportFormat,\n noReportRetry: opts.noReportRetry ?? false,\n limit,\n noBrowser,\n });\n }\n await returnControlsIfHeld(logDir, workerId);\n return rc;\n } catch (e) {\n await returnControlsIfHeld(logDir, workerId);\n if (e instanceof Error && e.message.startsWith(\"key file\")) {\n throw new Error(\n `provider \"${target.providerName}\": ${e.message}` +\n `\\nPut the token in that file (chmod 600) or point keyFile elsewhere.`\n );\n }\n throw e;\n }\n}\n\n/**\n * The core tools a headless run grants when the caller brings none of its\n * own. Permission-gated tools (Bash, Read, Write, …) are never offered to a\n * headless session that has no allow rule for them — the child starts with\n * only the tools that never ask (tool search, web, cron, tasks) and cannot\n * touch a file or shell (2026-09-18). A caller's own --allowedTools is a\n * deliberate restriction and passes through untouched.\n */\nconst DEFAULT_WORKER_TOOLS = \"Read,Edit,Write,Bash,Grep,Glob\";\n\n/** The --allowedTools args a headless run needs; [] when the caller granted. */\nexport function headlessToolGrants(allowedTools: string[]): string[] {\n return allowedTools.length ? [] : [\"--allowedTools\", DEFAULT_WORKER_TOOLS];\n}\n\n/**\n * `--tools` narrows which built-in tool *schemas* claude loads into the\n * system prompt — distinct from `--allowedTools`, which narrows which of\n * those loaded tools may run without asking. A headless worker with a small\n * grant (say, just Read) still pays for every built-in schema (Bash, Web*,\n * NotebookEdit, …) it never uses: ~9k tokens per spawn (29,270 -> 20,352,\n * measured 2026-09-20). Passing --tools built from the same grant removes\n * the unused schemas.\n *\n * MCP grants (`mcp__server__tool` / `mcp__server__*`) are left out of the\n * --tools list: they are not built-in tool names, --tools does not\n * recognize them, and the MCP server's tools load through --mcp-config\n * regardless. Tested 2026-09-20: with an --allowedTools grant of\n * `Read,mcp__pai__memory_search`, passing `--tools Read` (mcp name\n * omitted) still let the worker call memory_search successfully; the tool\n * loads from the filtered MCP config, not from --tools.\n */\nexport function headlessToolsFlag(allowedTools: string[]): string[] {\n const names: string[] = [];\n for (const entry of allowedTools) {\n for (const raw of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (raw.startsWith(\"mcp__\")) continue; // loads via --mcp-config, not --tools\n const patterned = raw.match(/^([A-Za-z]+)\\(.*\\)$/); // e.g. Bash(git *) -> Bash\n const name = patterned ? patterned[1] : raw;\n if (!names.includes(name)) names.push(name);\n }\n }\n return names.length ? [\"--tools\", names.join(\",\")] : [];\n}\n\n/**\n * A `--tools` value with no ToolSearch silently disables MCP tool deferral\n * (measured 2026-09-20: a run with `--tools \"Bash,Read,Grep,Glob,Agent\"` paid\n * 65,300 first-turn tokens with 5 MCP servers, 230,448 with 16, versus\n * 24,889 / 37,133 with ToolSearch present). Applied to the fully-assembled\n * argv so it catches a `--tools` from any source — the computed\n * `headlessToolsFlag`/project-pin flag, or one the caller put directly on\n * the command line.\n */\nexport function ensureToolSearch(cmd: string[]): string[] {\n const flagIdx = cmd.findIndex((a) => a === \"--tools\");\n if (flagIdx >= 0 && flagIdx + 1 < cmd.length) {\n const value = cmd[flagIdx + 1];\n if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n const out = [...cmd];\n out[flagIdx + 1] = `${value},ToolSearch`;\n process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n return out;\n }\n return cmd;\n }\n const eqIdx = cmd.findIndex((a) => a.startsWith(\"--tools=\"));\n if (eqIdx >= 0) {\n const value = cmd[eqIdx].slice(\"--tools=\".length);\n if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n const out = [...cmd];\n out[eqIdx] = `--tools=${value},ToolSearch`;\n process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n return out;\n }\n }\n return cmd;\n}\n\n/**\n * The claude flag that turns on the browser bridge, when the run asked for it.\n *\n * The bridge is not an MCP server — it rides the Chrome native-host channel,\n * is absent from `mcpServers`, and is off in a spawned claude until `--chrome`\n * is passed. A grant such as `mcp__claude-in-chrome__tabs_context_mcp` is\n * therefore a request for that flag, not for a server to load; without this\n * the grant names a tool that never exists. `rest` is the caller's own argv:\n * a `--chrome` they passed themselves is kept rather than duplicated.\n */\nexport function chromeGrantArgs(wanted: string[], rest: string[] = []): string[] {\n if (rest.includes(\"--chrome\")) return [];\n return grantsChrome(wanted) ? [\"--chrome\"] : [];\n}\n\n/**\n * Which MCP server names and --tools list an interactive launch (`pai\n * worker run`, no -p) uses: an explicit --mcp on this command line beats\n * the project's `pai project mcp` pin; an explicit --tools beats `pai\n * project tools`. Neither given: [] — today's \"everything loads\" default.\n */\nexport function interactiveMcpTools(\n explicitMcp: string[],\n callerTools: boolean,\n projectLaunch: Omit<ProjectLaunchConfig, \"slug\"> | null\n): { mcpNames: string[]; tools: string[] } {\n return {\n mcpNames: explicitMcp.length ? explicitMcp : (projectLaunch?.mcp ?? []),\n tools: !callerTools && projectLaunch?.tools?.length ? projectLaunch.tools : [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// image engine (2i) — no process spawn: one HTTP request, one file written.\n// ---------------------------------------------------------------------------\n\ninterface ImageExecuteArgs {\n logDir: string;\n target: ReturnType<typeof resolveTarget>;\n model: string;\n label: string;\n parsed: ReturnType<typeof parseRunnerArgs>;\n capability: string;\n outPath?: string;\n size?: string;\n timeoutMs?: number;\n cwd?: string;\n specPath?: string;\n parent?: string;\n stage?: string;\n quiet?: boolean;\n onWorkerStart?: (wid: string) => void;\n id?: string;\n}\n\ninterface FinaliseArgs {\n logDir: string;\n ledger: string;\n status: WorkerStatus;\n /** The process's real outcome; recordWorktree sees this, never the orphan verdict. */\n ok: boolean;\n rc: number;\n secs: number;\n /** Engine-specific WORKER-END fields (provider, mode, engine, model, label, ...). */\n end: Record<string, unknown>;\n report: WorkerReport | null;\n /** Handoff text when the report has no notes. */\n fallbackText?: string;\n worktree?: WorktreeInfo | null;\n /** The deadline stopped the run after this many minutes. */\n timedOut?: number;\n /**\n * Runs after the worktree outcome is recorded, before the parent handoff\n * (print the result, quota reroute). A returned number ends the run with\n * it, skipping the handoff — the reroute's own run reports instead.\n */\n beforeHandoff?: (status: WorkerStatus, report: WorkerReport | null) => Promise<number | void> | number | void;\n}\n\n/**\n * The finalisation every engine shares: orphaned-children check, state, the\n * WORKER-END ledger line, worktree outcome, parent handoff, final rc. A worker\n * owns its children's results (the worker contract says so): if it\n * backgrounded a sub-worker and exited without waiting, the run must not\n * report success, whatever the process returned.\n */\n/** Per-git-call bound for the salvage a kill signal runs before exiting. */\nconst SIGNAL_SALVAGE_MS = 5_000;\n\n/** The deadline fired: record it on the status and in the ledger (the caller then stops the child). */\nexport function fireDeadline(logDir: string, status: WorkerStatus, ledger: string, limit: RunLimit): void {\n status.timedOut = true;\n status.last = `deadline reached after ${limit.minutes} min`;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-DEADLINE\", { id: status.id, minutes: limit.minutes });\n}\n\n/** Decide why a signal ended the run and put it on the status; the caller writes WORKER-END. */\nexport function recordSignalEnd(logDir: string, status: WorkerStatus, signal: string): EndInfo {\n const end = decideEndReason({\n logDir,\n id: status.id,\n signal,\n timedOut: status.timedOut,\n parentPid: status.parentPid,\n parentComm: status.parentComm,\n parentBackground: status.parentBackground,\n });\n status.endReason = end.reason;\n status.endBy = end.by;\n status.endNote = end.note;\n return end;\n}\n\nexport async function finaliseRun(f: FinaliseArgs): Promise<number> {\n const { logDir, ledger, ok, rc } = f;\n let { status, report } = f;\n const wid = status.id;\n // first, whatever the outcome (rc 143 included): uncommitted edits in the\n // worktree must reach the branch before anything can drop the worktree\n const salvaged = f.worktree\n ? salvageOnExit(ledger, wid, status.label || UNLABELED, f.worktree.dir)\n : [];\n if (salvaged.length) {\n const msg = `WORKER-SALVAGE: committed uncommitted edits (${salvaged.join(\", \")})`;\n report = { ...(report ?? {}), notes: [report?.notes ?? f.fallbackText, msg].filter(Boolean).join(\"\\n\"), result: report?.result ?? \"-\" };\n }\n if (f.timedOut) {\n const reason = `deadline reached after ${f.timedOut} min`;\n status.timedOut = true;\n status.last = reason;\n // partial state = the salvage summary (r=~: stopped, not failed outright)\n report = { ...(report ?? {}), result: \"~\", why: reason, notes: [reason, report?.notes ?? f.fallbackText].filter(Boolean).join(\"\\n\") };\n }\n const orphanReason = checkOrphanedChildren(logDir, wid);\n const succeeded = ok && !orphanReason && !f.timedOut;\n status.state = succeeded ? \"done\" : \"failed\";\n status.rc = rc;\n status.secs = f.secs;\n if (orphanReason) {\n status.last = shortText(orphanReason, 90);\n report = { ...(report ?? {}), notes: orphanReason, result: \"-\" };\n appendLedger(ledger, \"WORKER-ORPHAN-CHILDREN\", { id: wid, reason: orphanReason });\n }\n const end = decideEndReason({ logDir, id: wid, timedOut: f.timedOut !== undefined });\n status.endReason = end.reason;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-END\", { id: wid, ...f.end, rc, secs: f.secs, ...endFields(end) });\n\n if (f.worktree) status = recordWorktree(logDir, status, f.worktree, ok);\n\n const early = await f.beforeHandoff?.(status, report);\n if (typeof early === \"number\") return early;\n\n const finalRc = rc !== 0 ? rc : succeeded ? 0 : 1;\n if (status.parent && isWorkerId(logDir, status.parent)) {\n try {\n await deliverHandoff(logDir, {\n from: wid,\n to: status.parent,\n kind: \"result\",\n text: shortText(report?.notes ?? f.fallbackText ?? (succeeded ? \"done\" : \"failed\"), 400),\n data: {\n rc: finalRc,\n ok: succeeded,\n ...(status.branch ? { branch: status.branch, commits: status.commits ?? 0 } : {}),\n ...(report ? { report } : {}),\n },\n });\n } catch {\n // the inbox line is best effort; it must never fail the exit path\n }\n }\n return finalRc;\n}\n\nasync function executeImageRun(a: ImageExecuteArgs & { config: ReturnType<typeof readWorkersSection>[\"workers\"] }): Promise<number> {\n const { logDir, target, model, label, parsed } = a;\n if (!parsed.headless || parsed.prompt === null) {\n throw new Error(\n `provider \"${target.providerName}\" (engine image) supports headless runs only: ` +\n `pass the task with -p '<prompt>'`\n );\n }\n const wid = a.id ?? newWorkerId();\n const cwd = a.cwd ?? process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const outPath = a.outPath ?? join(logDir, `${wid}.png`);\n\n const status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(),\n updated: nowStamp(),\n turns: 0,\n tools: 0,\n last: \"generating image\",\n rc: null,\n secs: null,\n origin: \"spawn\",\n outputFormat: parsed.outputFormat,\n reportFormat: \"json\",\n ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n };\n saveStatus(logDir, status);\n a.onWorkerStart?.(wid);\n const ledger = ledgerPath(logDir);\n appendLedger(ledger, \"WORKER-START\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n engine: \"image\",\n model,\n cwd,\n label,\n capability: a.capability,\n ...(a.specPath ? { spec: a.specPath } : {}),\n });\n\n const imageEnd = {\n provider: target.providerName,\n mode: \"headless\",\n engine: \"image\",\n model,\n label,\n capability: a.capability,\n };\n const t0 = Date.now();\n try {\n const result = await runImageCapability({\n providerName: target.providerName,\n provider: target.provider,\n model,\n prompt: parsed.prompt,\n outPath,\n size: a.size,\n timeoutMs: a.timeoutMs,\n });\n const secs = Math.round((Date.now() - t0) / 1000);\n status.last = `wrote ${result.path}`;\n return await finaliseRun({\n logDir,\n ledger,\n status,\n ok: true,\n rc: 0,\n secs,\n end: imageEnd,\n report: null,\n beforeHandoff: () => {\n if (!a.quiet) process.stdout.write(JSON.stringify(result) + \"\\n\");\n },\n });\n } catch (e) {\n const secs = Math.round((Date.now() - t0) / 1000);\n status.last = e instanceof Error ? e.message : String(e);\n await finaliseRun({ logDir, ledger, status, ok: false, rc: 1, secs, end: imageEnd, report: null });\n throw e;\n }\n}\n\n/**\n * Follow pane, made observable on stderr (stdout stays pure JSON for\n * `--output-format json`): one line naming the outcome — opened, skipped\n * (with reason), or failed (with reason, alongside the existing PANE-FAIL\n * ledger line). Fire-and-forget, never blocks the worker.\n */\nexport async function announcePane(\n logDir: string,\n config: ReturnType<typeof readWorkersSection>[\"workers\"],\n wid: string,\n term: string,\n noPane: boolean,\n ledger: string\n): Promise<void> {\n const skip = !noPane\n ? !config.pane.enabled\n ? \"workers.pane.enabled is false\"\n : process.env.PAI_WORKER_AUTOPANE === \"0\"\n ? \"PAI_WORKER_AUTOPANE=0\"\n : paneBackend() === \"none\"\n ? \"no tmux ($TMUX) or iTerm2 (ITERM_SESSION_ID) here\"\n : null\n : \"--no-pane\";\n if (skip) {\n process.stderr.write(`[pai worker] no pane for ${wid}: ${skip}\\n`);\n return;\n }\n try {\n const r = await openPaneForWorker(logDir, config, wid, term);\n const suffix = r.session ? ` (iTerm session ${r.session})` : \"\";\n process.stderr.write(`[pai worker] pane opened for ${wid}${suffix}\\n`);\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n process.stderr.write(`[pai worker] no pane for ${wid}: ${message.slice(0, 200)}\\n`);\n appendLedger(ledger, \"PANE-FAIL\", { id: wid, error: message.slice(0, 200) });\n }\n}\n\ninterface ExecuteArgs {\n config: ReturnType<typeof readWorkersSection>[\"workers\"];\n logDir: string;\n target: ReturnType<typeof resolveTarget>;\n model: string;\n label: string;\n parsed: ReturnType<typeof parseRunnerArgs>;\n claudeArgs: string[];\n noPane: boolean;\n mcpFlag?: string;\n cwd?: string;\n specPath?: string;\n parent?: string;\n stage?: string;\n quiet?: boolean;\n onWorkerStart?: (wid: string) => void;\n id?: string;\n worktreeFlag?: boolean;\n className?: string;\n reroutes: number;\n printCmd?: boolean;\n reportFormat: \"json\" | \"ag2\";\n noReportRetry?: boolean;\n /** Capability name this run resolved through (--capability, or an implied class capability), when it did. */\n capability?: string;\n /** Time limit of the run; null = none. */\n limit: RunLimit | null;\n /** Browser tools are stripped from this run (and denied by the worker guard). */\n noBrowser: boolean;\n}\n\nasync function executeRun(a: ExecuteArgs): Promise<number> {\n const { config, logDir, target, model, label, parsed, noPane, limit, noBrowser } = a;\n const headless = parsed.headless;\n\n // openai-protocol providers run through the local proxy (started on demand)\n let proxyUrl: string | undefined;\n if (target.provider.protocol === \"openai\") {\n const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n proxyUrl = `${base}/${target.providerName}`;\n }\n const env = buildRunEnv(target.provider, headless, proxyUrl);\n\n const wid = a.id ?? newWorkerId();\n const cwd = a.cwd ?? process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const session = resolveSession(term);\n // orchestrator Bash children have no terminal identity (see scope.ts) — the\n // session map supplies the claude session they were spawned by instead\n const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n // One worktree per run whose tools can write (Edit/Write/Bash or no\n // restriction), in a git cwd; --worktree/--no-worktree override. A git refusal\n // degrades to an in-place run — the worker itself must still run.\n // Skipped for --print-cmd: it is a dry-run and must not touch git.\n let worktree: WorktreeInfo | null = null;\n if (\n headless &&\n !a.printCmd &&\n worktreeWanted(a.worktreeFlag, { cwd, allowedTools: parsed.allowedTools })\n ) {\n try {\n worktree = addWorktree(logDir, wid, cwd);\n } catch (e) {\n const why = (e as Error).message;\n process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n }\n }\n // sub-workers detect themselves (and their parent) through this variable\n env.PAI_WORKER_ID = wid;\n if (noBrowser) env.PAI_WORKER_NO_BROWSER = \"1\";\n\n const mcpFlag = noBrowser ? stripBrowserNames(a.mcpFlag ? [a.mcpFlag] : [])[0] : a.mcpFlag;\n const classMcp = noBrowser && target.classMcp ? stripBrowserNames(target.classMcp) : target.classMcp;\n if (noBrowser) parsed.mcp = stripBrowserNames(parsed.mcp);\n const chromeArgs = noBrowser\n ? []\n : chromeGrantArgs(\n [...(mcpFlag ? [mcpFlag] : []), ...parsed.mcp, ...(classMcp ?? []), ...parsed.allowedTools],\n parsed.rest\n );\n\n // MCP / tools: caller config > allowlist (--mcp flag / --mcp args / role /\n // mcp__ grants in --allowedTools) > project pin (interactive only, `pai\n // project mcp` / `pai project tools`) > the strict empty set (headless) /\n // everything (interactive, today's default).\n let mcpArgs: string[] = [];\n let toolsFlag: string[] = headless ? headlessToolsFlag(parsed.allowedTools) : [];\n if (headless && !parsed.callerMcpConfig) {\n const wanted = [\n ...(mcpFlag ? [mcpFlag] : []),\n ...parsed.mcp,\n ...(classMcp ?? []),\n ...mcpServersFromToolGrants(parsed.allowedTools),\n ];\n if (wanted.length) {\n const names = expandMcpNames(wanted, config); // unknown names fail fast\n mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n } else {\n mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", ensureNoMcpConfig(logDir)];\n }\n } else if (!headless && !parsed.callerMcpConfig) {\n const explicitMcp = [...(mcpFlag ? [mcpFlag] : []), ...parsed.mcp];\n const projectLaunch = await projectLaunchConfig(cwd);\n const picked = interactiveMcpTools(explicitMcp, parsed.callerTools, projectLaunch);\n // applied pins only: an explicit --mcp / --tools overrides its pin\n const applied = projectLaunch && {\n ...projectLaunch,\n mcp: explicitMcp.length ? undefined : projectLaunch.mcp,\n tools: picked.tools.length ? projectLaunch.tools : undefined,\n };\n for (const line of pinNotices(applied)) console.error(line);\n if (picked.mcpNames.length) {\n const names = expandMcpNames(picked.mcpNames, config);\n mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n }\n if (picked.tools.length) toolsFlag = [\"--tools\", picked.tools.join(\",\")];\n }\n\n // In stdin mode the prompt moves to the first user message on stdin, so it\n // must come off the command line (bare -p stays: stream-json needs --print).\n const restArgs = headless ? stripPromptValues(parsed.rest) : parsed.rest;\n const toolArgs = headless ? headlessToolGrants(parsed.allowedTools) : [];\n const { text: promptText, applied: trailerApplied } = headlessPromptText(\n parsed.prompt,\n headless,\n parsed.callerSystemPrompt,\n a.reportFormat\n );\n const claudeCmd = claudeCommand(env, config.caveman);\n let cmd: string[] = [...claudeCmd];\n cmd.push(...modelFlagArgs(headless, model, Boolean(parsed.callerModel)));\n cmd.push(...chromeArgs, ...mcpArgs, ...toolArgs, ...toolsFlag, ...restArgs);\n if (headless) {\n cmd.push(\"--output-format\", \"stream-json\", \"--verbose\", \"--input-format\", \"stream-json\");\n }\n // ONE flag: contract (unless the caller brought their own), placement, caller text\n cmd.push(\n ...appendSystemPromptArgs([\n ...(headless && !parsed.callerSystemPrompt ? [workerContractPrompt(a.className, a.reportFormat)] : []),\n ...(headless\n ? [worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) : inPlaceSystemPrompt()]\n : []),\n ...(headless && limit ? [deadlinePromptLine(limit)] : []),\n ...parsed.callerSystemPrompts,\n ])\n );\n cmd = ensureToolSearch(cmd);\n\n // Dry run: the argv audit, no status/ledger/pane, no spawn.\n if (a.printCmd) {\n console.log(\n JSON.stringify({\n cmd,\n cwd: worktree?.dir ?? cwd,\n browser: noBrowser ? \"off\" : \"on\",\n ...(limit ? { deadline: new Date(limit.deadlineAt).toISOString(), maxMinutes: limit.minutes } : { limit: \"none\" }),\n })\n );\n return 0;\n }\n\n let status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n ...parentInfo(),\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(),\n updated: nowStamp(),\n turns: 0,\n tools: 0,\n last: headless ? \"starting\" : \"interactive\",\n rc: null,\n secs: null,\n // interactive runs ARE the chat pane, not a spawned subagent of it\n origin: headless ? \"spawn\" : \"chat\",\n outputFormat: parsed.outputFormat,\n ...(headless ? { reportFormat: a.reportFormat } : {}),\n ...(trailerApplied ? { promptTrailer: true } : {}),\n ...(session ? { session } : {}),\n ...(spawnerSession ? { spawnerSession } : {}),\n // no window seed: contextWindow comes from the init event only, and the\n // meter stays hidden until one is announced (never a guessed default)\n ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n ...(worktree\n ? {\n worktreeDir: worktree.dir,\n branch: worktree.branch,\n worktreeBase: worktree.base,\n worktreeSnapshot: worktree.snapshot,\n }\n : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n ...(limit ? { deadlineAt: limit.deadlineAt, maxMinutes: limit.minutes } : {}),\n };\n saveStatus(logDir, status);\n a.onWorkerStart?.(wid);\n const ledger = ledgerPath(logDir);\n appendLedger(ledger, \"WORKER-START\", {\n id: wid,\n provider: target.providerName,\n mode: headless ? \"headless\" : \"interactive\",\n model,\n cwd,\n label,\n ...(a.capability ? { capability: a.capability } : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n });\n\n // Follow pane: headless only (interactive runs ARE the chat pane), best\n // effort, never blocking the worker.\n if (headless) void announcePane(logDir, config, wid, term, noPane, ledger);\n\n const t0 = Date.now();\n const proc = spawn(cmd[0], cmd.slice(1), {\n env,\n cwd: worktree?.dir ?? cwd,\n stdio: headless ? [\"pipe\", \"pipe\", \"inherit\"] : \"inherit\",\n });\n\n // --- stdin lifecycle (2i): prompt in, socket forwards, close 2 s after result\n let operatorInFlight = 0;\n let closeTimer: NodeJS.Timeout | null = null;\n const armStdinClose = () => {\n if (closeTimer) clearTimeout(closeTimer);\n closeTimer = setTimeout(() => {\n if (operatorInFlight === 0) {\n try {\n proc.stdin?.end();\n } catch {\n /* already closed */\n }\n }\n }, 2_000);\n };\n\n let eventsFd: number | null = null;\n const writeEvent = (obj: Record<string, unknown>): void => {\n if (eventsFd === null) return;\n try {\n writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n } catch {\n // a full disk must not take the worker transcript's process down\n }\n };\n\n const operatorServer = headless\n ? createOperatorServer(logDir, wid, async (text) => {\n const stdin = proc.stdin;\n if (!stdin || !stdin.writable || stdin.writableEnded) {\n return \"the worker's stdin is already closed (its turn ended) — use: pai worker resume\";\n }\n operatorInFlight += 1;\n if (closeTimer) {\n clearTimeout(closeTimer);\n closeTimer = null;\n }\n // a handoff delivery's mirror carries a flag: the viewer shows the\n // inbox ◆ line instead, never both\n writeEvent({ type: \"operator\", text, handoff: isHandoffMessage(text) });\n return new Promise<void | string>((resolve) => {\n try {\n stdin.write(stdinUserMessage(operatorUserText(text)) + \"\\n\", (e) =>\n resolve(e ? `write to the worker failed: ${e.message}` : undefined)\n );\n } catch (e) {\n resolve(`write to the worker failed: ${(e as Error).message}`);\n }\n });\n })\n : null;\n\n let killed = false;\n const stopDeadline = limit ? armDeadline(proc, limit, () => fireDeadline(logDir, status, ledger, limit)) : null;\n const cleanup = () => {\n stopDeadline?.();\n if (closeTimer) clearTimeout(closeTimer);\n operatorServer?.close();\n };\n const onSignal = (sig: string) => {\n killed = true;\n status.state = \"killed\";\n status.rc = 143;\n status.secs = Math.floor((Date.now() - t0) / 1000);\n const end = recordSignalEnd(logDir, status, sig);\n status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : \"\"})`;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-END\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n model,\n rc: 143,\n secs: status.secs,\n killed: 1,\n ...endFields(end),\n note: end.note,\n label,\n });\n // a killed worktree run leaves nothing to merge — drop its worktree and\n // branch too, or every kill strands them for hand-pruning (2026-09-18)\n if (worktree) {\n salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);\n try {\n recordWorktree(logDir, status, worktree, false);\n } catch {\n /* best effort: the status already says killed */\n }\n }\n cleanup();\n try {\n proc.kill();\n } catch {\n /* already gone */\n }\n process.exit(143);\n };\n process.once(\"SIGTERM\", () => onSignal(\"SIGTERM\"));\n process.once(\"SIGINT\", () => onSignal(\"SIGINT\"));\n process.once(\"SIGHUP\", () => onSignal(\"SIGHUP\"));\n\n // Holder for the last result event + its parsed report: assigned inside the\n // readline callback below, read after the await.\n const ctx: { resultEvent: StreamEvent | null; resultReport: WorkerReport | null } = {\n resultEvent: null,\n resultReport: null,\n };\n\n if (headless) {\n // the first user message carries the prompt (the -p value was stripped)\n if (promptText !== null) {\n try {\n proc.stdin!.write(stdinUserMessage(promptText) + \"\\n\");\n } catch {\n /* child died instantly; the close handler reports it */\n }\n }\n eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n const rl = createInterface({ input: proc.stdout! });\n rl.on(\"line\", (line) => {\n if (parsed.outputFormat === \"stream-json\") {\n process.stdout.write(line + \"\\n\");\n }\n if (!line.startsWith(\"{\")) return;\n let e: StreamEvent;\n try {\n e = JSON.parse(line) as StreamEvent;\n } catch {\n return;\n }\n writeEvent(e as Record<string, unknown>);\n if (e.type === \"system\" && e.subtype === \"init\") {\n if (e.session_id) status.claudeSession = e.session_id;\n adoptInitModel(status, e);\n const cw = initContextWindow(e);\n if (cw) status.contextWindow = cw;\n saveStatus(logDir, status);\n } else if (isCompactBoundary(e)) {\n resetContextTokensOnCompact(status, e);\n saveStatus(logDir, status);\n } else if (e.type === \"assistant\") {\n status.turns += 1;\n bumpContextTokens(status, usageContextTokens(e.message?.usage));\n for (const block of e.message?.content ?? []) {\n if (block.type === \"tool_use\") {\n status.tools += 1;\n status.last = describeTool(block.name ?? \"?\", block.input);\n // a long single sleep is worth seeing while it runs (supervision's\n // \"sleeping\" kind) — set when issued, cleared when it completes\n if (block.name === \"Bash\") status.sleepSec = parseSleepSecs(bashCommand(block.input));\n } else if (block.type === \"text\" && (block.text ?? \"\").trim()) {\n status.last = \"says: \" + shortText(block.text, 70);\n }\n }\n saveStatus(logDir, status);\n } else if (e.type === \"user\") {\n // tool results arrive as user events: each one advances (or resets)\n // the consecutive-failure counter supervision's thrashing detector\n // reads, and retires a pending sleep observation. Plain user turns\n // (operator input) have no tool_result blocks and change nothing.\n const blocks = e.message?.content;\n if (Array.isArray(blocks)) {\n let sawResult = false;\n for (const block of blocks) {\n if (block.type !== \"tool_result\") continue;\n sawResult = true;\n status.consecFails = toolResultFailed(block) ? (status.consecFails ?? 0) + 1 : 0;\n status.sleepSec = null;\n }\n if (sawResult) saveStatus(logDir, status);\n }\n } else if (e.type === \"result\") {\n const tokens = usageContextTokens(e.usage);\n // a compact result legitimately restarts the context lower\n if (e.is_compact) status.contextTokens = tokens ?? status.contextTokens;\n else bumpContextTokens(status, tokens);\n const report = parseWorkerReport(e.result ?? \"\");\n if (report?.notes) status.last = shortText(report.notes, 90);\n else if (e.result) status.last = shortText(e.result, 90);\n saveStatus(logDir, status);\n operatorInFlight = 0;\n armStdinClose();\n ctx.resultEvent = e;\n ctx.resultReport = report;\n }\n });\n }\n\n const rc = await new Promise<number>((resolve, reject) => {\n proc.on(\"error\", reject);\n proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n });\n if (eventsFd !== null) closeSync(eventsFd);\n cleanup();\n\n const secs = Math.floor((Date.now() - t0) / 1000);\n const resultEvent = ctx.resultEvent;\n const ok = runSucceeded(headless, rc, resultEvent);\n if (resultEvent) status.last = shortText(resultEvent.result ?? \"\", 90);\n if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n if (headless && a.reportFormat === \"ag2\" && resultEvent?.result) {\n const v = validateAg2(resultEvent.result);\n status.reportValid = v.ok;\n status.reportErrors = v.errors;\n // one bounded re-ask, cap exactly one — see shouldRetryReport\n if (shouldRetryReport(v, a.noReportRetry ?? false, Boolean(status.claudeSession))) {\n status.reportRetried = true;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-REPORT-RETRY\", { id: wid, reason: v.errors.join(\"; \") || \"invalid AG2 message\" });\n try {\n const reaskText = await reaskAg2Report({\n env,\n cwd: worktree?.dir ?? cwd,\n model,\n callerPinnedModel: Boolean(parsed.callerModel),\n chromeArgs,\n mcpArgs,\n toolArgs,\n toolsFlag,\n sessionId: status.claudeSession!,\n claudeCmd,\n });\n const v2 = validateAg2(reaskText);\n status.reportValid = v2.ok;\n status.reportErrors = v2.errors;\n if (v2.ok) {\n resultEvent.result = reaskText;\n ctx.resultReport = parseWorkerReport(reaskText);\n if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n }\n } catch {\n // the re-ask is best effort; the original invalid report stands\n }\n }\n }\n if (headless && ctx.resultReport?.changed?.length) {\n const verify = verifyReportChanges(ctx.resultReport.changed, worktree?.dir ?? cwd, t0);\n if (!verify.ok) {\n status.reportValid = false;\n const note = verifyFailNote(verify);\n status.last = shortText(note, 90);\n ctx.resultReport.notes = ctx.resultReport.notes ? `${ctx.resultReport.notes} — ${note}` : note;\n appendLedger(ledger, \"REPORT-UNVERIFIED\", {\n id: wid,\n failures: verify.failures.length,\n paths: verify.failures.map((f) => f.path).slice(0, 5).join(\",\"),\n });\n }\n }\n return finaliseRun({\n logDir,\n ledger,\n status,\n ok,\n rc,\n secs,\n end: {\n provider: target.providerName,\n mode: headless ? \"headless\" : \"interactive\",\n model,\n turns: status.turns,\n tools: status.tools,\n label,\n },\n report: ctx.resultReport,\n fallbackText: resultEvent?.result,\n worktree,\n timedOut: status.timedOut ? limit?.minutes : undefined,\n beforeHandoff: async (st, rep) => {\n if (headless && !a.quiet) {\n printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n }\n\n // Quota reroute: only auto-routed runs, dead before the first tool call.\n const resultText = resultEvent?.result ?? \"\";\n if (\n !ok &&\n headless &&\n a.target.via === \"auto\" &&\n config.routing.retryOnQuota &&\n status.turns <= 1 &&\n status.tools === 0 &&\n isQuotaFailure(resultText)\n ) {\n setCooldown(logDir, target.providerName, config.routing.cooldownMinutes);\n const next = nextAutoProvider(config, logDir, target.providerName);\n if (next && a.reroutes < config.routing.order.length) {\n appendLedger(ledger, \"WORKER-REROUTE\", {\n from: target.providerName,\n to: next,\n reason: \"quota\",\n });\n return runWorker({\n providerFlag: next,\n label,\n noPane: a.noPane,\n mcpFlag: a.mcpFlag,\n cwd: a.cwd,\n specPath: a.specPath,\n parent: a.parent,\n stage: a.stage,\n claudeArgs: a.claudeArgs,\n onWorkerStart: a.onWorkerStart,\n worktreeFlag: a.worktreeFlag,\n className: a.className,\n _reroutes: a.reroutes + 1,\n });\n }\n }\n },\n });\n}\n\n/** The worktree fields printResult adds to a json payload, when there is one. */\nexport function worktreeExtras(s: WorkerStatus): Record<string, unknown> | undefined {\n return s.branch ? { branch: s.branch, commits: s.commits ?? 0 } : undefined;\n}\n\nexport interface Ag2ReaskArgs {\n env: NodeJS.ProcessEnv;\n cwd: string;\n model: string;\n callerPinnedModel: boolean;\n chromeArgs: string[];\n mcpArgs: string[];\n toolArgs: string[];\n toolsFlag: string[];\n sessionId: string;\n timeoutMs?: number;\n /** Argv head of the original launch (claudeCommand); defaults to plain claude. */\n claudeCmd?: string[];\n}\n\n/**\n * The one bounded re-ask, cap exactly one: `claude --resume <session>` with\n * AG2_REASK_TEXT, single-shot `--output-format json` (one turn, no streaming\n * plumbing needed). Same env/model/mcp/tool flags as the original launch —\n * only --resume and the fixed re-ask prompt are new — so the resumed turn\n * sees the same grants the worker ran with.\n */\nexport async function reaskAg2Report(a: Ag2ReaskArgs): Promise<string> {\n let cmd: string[] = [...(a.claudeCmd ?? [\"claude\"])];\n cmd.push(...modelFlagArgs(true, a.model, a.callerPinnedModel));\n cmd.push(...a.chromeArgs, ...a.mcpArgs, ...a.toolArgs, ...a.toolsFlag);\n cmd.push(\"--resume\", a.sessionId, \"-p\", AG2_REASK_TEXT, \"--output-format\", \"json\");\n cmd = ensureToolSearch(cmd);\n const proc = spawn(cmd[0], cmd.slice(1), { env: a.env, cwd: a.cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n const timer = setTimeout(() => {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n }, a.timeoutMs ?? 60_000);\n let out = \"\";\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n out += chunk.toString(\"utf8\");\n });\n await new Promise<void>((resolve) => {\n proc.on(\"error\", () => resolve());\n proc.on(\"close\", () => resolve());\n });\n clearTimeout(timer);\n return resultFromOutput(out);\n}\n\n// ---------------------------------------------------------------------------\n// codex engine (2d)\n// ---------------------------------------------------------------------------\n\ninterface CodexArgs extends Omit<ExecuteArgs, \"reroutes\" | \"mcpFlag\"> {}\n\nasync function executeCodexRun(a: CodexArgs): Promise<number> {\n const { config, logDir, target, model, label, parsed, noPane } = a;\n if (!parsed.headless || parsed.prompt === null) {\n throw new Error(\n `provider \"${target.providerName}\" (engine codex) supports headless runs only: ` +\n `pass the task with -p '<prompt>'`\n );\n }\n const env = buildCodexEnv(target.provider);\n const wid = a.id ?? newWorkerId();\n const cwd = a.cwd ?? process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const session = resolveSession(term);\n const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n let worktree: WorktreeInfo | null = null;\n if (worktreeWanted(a.worktreeFlag, { cwd, allowedTools: parsed.allowedTools })) {\n try {\n worktree = addWorktree(logDir, wid, cwd);\n } catch (e) {\n const why = (e as Error).message;\n process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n }\n }\n env.PAI_WORKER_ID = wid;\n // codex takes instructions through the prompt, not a system prompt flag\n const prompt =\n (a.limit ? deadlinePromptLine(a.limit) + \"\\n\\n\" : \"\") +\n (worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) + \"\\n\\n\" : \"\") +\n parsed.prompt;\n\n let status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n ...parentInfo(),\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(),\n updated: nowStamp(),\n turns: 0,\n tools: 0,\n last: \"starting\",\n rc: null,\n secs: null,\n origin: \"spawn\",\n outputFormat: parsed.outputFormat,\n // codex takes instructions through the prompt, not the AG2 contract flag\n reportFormat: \"json\",\n ...(session ? { session } : {}),\n ...(spawnerSession ? { spawnerSession } : {}),\n // codex has no init event of its own: the synthetic one below announces\n // an explicitly configured window (never a guessed default)\n ...(target.provider.contextWindow ? { contextWindow: target.provider.contextWindow } : {}),\n ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n ...(worktree\n ? {\n worktreeDir: worktree.dir,\n branch: worktree.branch,\n worktreeBase: worktree.base,\n worktreeSnapshot: worktree.snapshot,\n }\n : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n ...(a.limit ? { deadlineAt: a.limit.deadlineAt, maxMinutes: a.limit.minutes } : {}),\n };\n saveStatus(logDir, status);\n a.onWorkerStart?.(wid);\n const ledger = ledgerPath(logDir);\n appendLedger(ledger, \"WORKER-START\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n engine: \"codex\",\n model,\n cwd,\n label,\n ...(a.capability ? { capability: a.capability } : {}),\n });\n const dropped = codexDroppedFlags(a.claudeArgs);\n if (dropped.length) {\n appendLedger(ledger, \"WORKER-NOTE\", {\n id: wid,\n note: `dropped for codex: ${dropped.join(\", \")}`,\n });\n }\n\n void announcePane(logDir, config, wid, term, noPane, ledger);\n\n const t0 = Date.now();\n const proc = spawn(\"codex\", buildCodexArgs(prompt, parsed.callerModel ? undefined : model), {\n env,\n cwd: worktree?.dir ?? cwd,\n stdio: [\"ignore\", \"pipe\", \"inherit\"],\n });\n\n const fold = emptyCodexResult();\n const eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n const writeEvent = (obj: Record<string, unknown>) => {\n try {\n writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n } catch {\n // best effort transcript\n }\n };\n writeEvent({\n type: \"system\",\n subtype: \"init\",\n model,\n cwd,\n ...(target.provider.contextWindow ? { context_window: target.provider.contextWindow } : {}),\n });\n\n let killed = false;\n const stopDeadline = a.limit ? armDeadline(proc, a.limit, () => fireDeadline(logDir, status, ledger, a.limit!)) : null;\n process.once(\"SIGTERM\", onCodexSignal(\"SIGTERM\"));\n process.once(\"SIGINT\", onCodexSignal(\"SIGINT\"));\n process.once(\"SIGHUP\", onCodexSignal(\"SIGHUP\"));\n function onCodexSignal(sig: string) {\n return () => {\n killed = true;\n status.state = \"killed\";\n status.rc = 143;\n status.secs = Math.floor((Date.now() - t0) / 1000);\n const end = recordSignalEnd(logDir, status, sig);\n status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : \"\"})`;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-END\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n model,\n rc: 143,\n secs: status.secs,\n killed: 1,\n ...endFields(end),\n note: end.note,\n label,\n });\n // same as the claude path: a killed run's worktree and branch go now\n if (worktree) {\n salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);\n try {\n recordWorktree(logDir, status, worktree, false);\n } catch {\n /* best effort: the status already says killed */\n }\n }\n try {\n proc.kill();\n } catch {\n /* already gone */\n }\n process.exit(143);\n };\n }\n\n const rl = createInterface({ input: proc.stdout! });\n rl.on(\"line\", (line) => {\n if (parsed.outputFormat === \"stream-json\") process.stdout.write(line + \"\\n\");\n const parsedLine = parseCodexLine(line);\n if (parsedLine === null) return;\n foldCodexLine(parsedLine, fold);\n if (fold.threadId && !status.claudeSession) status.claudeSession = fold.threadId;\n status.turns = fold.turns;\n status.tools = fold.tools;\n if (fold.last) status.last = shortText(fold.last, 90);\n bumpContextTokens(status, fold.contextTokens);\n saveStatus(logDir, status);\n for (const ev of fold.events.splice(0)) writeEvent(ev);\n });\n\n const rc = await new Promise<number>((resolve, reject) => {\n proc.on(\"error\", reject);\n proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n });\n stopDeadline?.();\n closeSync(eventsFd);\n\n const secs = Math.floor((Date.now() - t0) / 1000);\n const finalText = fold.finalText ?? \"\";\n const report = parseWorkerReport(finalText);\n const resultEvent: StreamEvent = {\n type: \"result\",\n result: finalText,\n is_error: fold.isError || rc !== 0,\n num_turns: fold.turns,\n duration_ms: secs * 1000,\n };\n writeEvent(resultEvent);\n\n const ok = rc === 0 && !fold.isError;\n status.last = shortText(report?.notes ?? finalText, 90) || (ok ? \"done\" : \"failed\");\n if (report?.changed?.length) {\n const verify = verifyReportChanges(report.changed, worktree?.dir ?? cwd, t0);\n if (!verify.ok) {\n status.reportValid = false;\n const note = verifyFailNote(verify);\n status.last = shortText(note, 90);\n report.notes = report.notes ? `${report.notes} — ${note}` : note;\n appendLedger(ledger, \"REPORT-UNVERIFIED\", {\n id: wid,\n failures: verify.failures.length,\n paths: verify.failures.map((f) => f.path).slice(0, 5).join(\",\"),\n });\n }\n }\n return finaliseRun({\n logDir,\n ledger,\n status,\n ok,\n rc,\n secs,\n end: {\n provider: target.providerName,\n mode: \"headless\",\n engine: \"codex\",\n model,\n turns: status.turns,\n tools: status.tools,\n label,\n },\n report,\n fallbackText: finalText,\n worktree,\n timedOut: status.timedOut ? a.limit?.minutes : undefined,\n beforeHandoff: (st, rep) => {\n if (!a.quiet) printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// result printing\n// ---------------------------------------------------------------------------\n\nexport function printResult(\n fmt: \"text\" | \"json\" | \"stream-json\",\n resultEvent: StreamEvent | null,\n rc: number,\n logDir: string,\n wid: string,\n report?: WorkerReport | null,\n extras?: Record<string, unknown>\n): void {\n if (fmt === \"stream-json\") return; // already mirrored live\n if (fmt === \"json\") {\n const payload = report\n ? { ...(resultEvent ?? { is_error: true, result: \"no result event\", rc }), report, ...extras }\n : { ...(resultEvent ?? { is_error: true, result: \"no result event\", rc }), ...extras };\n console.log(JSON.stringify(payload));\n return;\n }\n if (resultEvent) {\n console.log(resultEvent.result ?? \"\");\n } else {\n process.stderr.write(\n `pai worker: run produced no result (rc=${rc}); see ${eventsPath(logDir, wid)}\\n`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// providers test: the 90-second pong probe\n// ---------------------------------------------------------------------------\n\nexport interface ProviderTestResult {\n provider: string;\n model: string;\n latencyMs: number;\n result: string;\n ok: boolean;\n /** Set when the probe could not run (e.g. \"codex not installed\"). */\n skipped?: string;\n}\n\n/**\n * Run a one-word pong probe through the provider (headless, no pane) and\n * report provider, model, latency and the reply. ok is false unless the reply\n * was exactly \"pong\" (case-insensitive, whitespace-trimmed).\n */\nexport async function testProvider(\n providerName: string,\n provider: WorkerProvider,\n logDir: string,\n timeoutMs = 90_000,\n caveman = false\n): Promise<ProviderTestResult> {\n assertProviderRunnable(providerName, provider);\n const model = provider.models.default;\n\n if (provider.engine === \"codex\") {\n const skipped = codexInstalled() ? undefined : \"codex not installed\";\n return {\n provider: providerName,\n model,\n latencyMs: 0,\n result: skipped ?? \"codex engine: pong probe through codex exec not implemented\",\n ok: false,\n ...(skipped ? { skipped } : {}),\n };\n }\n\n let proxyUrl: string | undefined;\n if (provider.protocol === \"openai\") {\n const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n proxyUrl = `${base}/${providerName}`;\n }\n const env = buildRunEnv(provider, true, proxyUrl);\n const [bin, ...head] = claudeCommand(env, caveman);\n const t0 = Date.now();\n const proc = spawn(\n bin,\n [\n ...head,\n \"--model\", model,\n \"--strict-mcp-config\", \"--mcp-config\", ensureNoMcpConfig(logDir),\n \"-p\", \"Reply with exactly one word: pong\",\n \"--output-format\", \"json\",\n ],\n { env, stdio: [\"ignore\", \"pipe\", \"inherit\"] }\n );\n const timer = setTimeout(() => {\n try { proc.kill(\"SIGKILL\"); } catch { /* already gone */ }\n }, timeoutMs);\n\n let out = \"\";\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n out += chunk.toString(\"utf8\");\n });\n const rc = await new Promise<number>((resolve) => {\n proc.on(\"error\", () => resolve(1));\n proc.on(\"close\", (code) => resolve(code ?? 1));\n });\n clearTimeout(timer);\n\n return {\n provider: providerName,\n model,\n latencyMs: Date.now() - t0,\n result: resultFromOutput(out),\n ok: rc === 0 && resultFromOutput(out).trim().toLowerCase() === \"pong\",\n };\n}\n\n/**\n * Pull the reply out of claude's stdout. With --verbose, `--output-format\n * json` dumps a JSON array of stream events and the reply sits in the last\n * \"result\" event; without it, stdout is the single result object. Accept\n * either shape, plus a bare-text fallback for error output.\n */\nexport function resultFromOutput(out: string): string {\n const trimmed = out.trim();\n if (!trimmed) return \"\";\n const parse = (s: string): unknown => {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n };\n const fromValue = (v: unknown): string | null => {\n if (Array.isArray(v)) {\n for (let i = v.length - 1; i >= 0; i--) {\n const r = fromValue(v[i]);\n if (r !== null) return r;\n }\n return null;\n }\n if (typeof v === \"object\" && v !== null) {\n const o = v as StreamEvent;\n if (o.type === \"result\" && typeof o.result === \"string\") return o.result;\n }\n return null;\n };\n const direct = fromValue(parse(trimmed));\n if (direct !== null) return direct;\n const lines = trimmed.split(\"\\n\");\n for (let i = lines.length - 1; i >= 0; i--) {\n const r = fromValue(parse(lines[i]));\n if (r !== null) return r;\n }\n return trimmed.slice(0, 200);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"run-BHLXvURR.mjs","names":[],"sources":["../src/workers/mcp.ts","../src/workers/project-config.ts","../src/workers/routing.ts","../src/workers/proxy/server.ts","../src/workers/scope.ts","../src/workers/endreason.ts","../src/workers/controls.ts","../src/workers/engines/image.ts","../src/workers/pane.ts","../src/workers/tree.ts","../src/workers/operator.ts","../src/workers/handoff.ts","../src/workers/wait-on.ts","../src/workers/agentish.ts","../src/workers/report.ts","../src/workers/deadline.ts","../src/workers/browser-tools.ts","../src/workers/codex.ts","../src/workers/run.ts"],"sourcesContent":["/**\n * mcp.ts — the MCP allowlist for headless workers.\n *\n * Headless workers start with NO MCP servers: every server definition is a\n * prompt-time tool inventory the model pays to know, and a worker that only\n * reads and edits files needs none of it. A run may opt in with `--mcp\n * name[,name…]`, a role carrying `\"mcp\": [...]`, or implicitly by naming\n * `mcp__server__tool` in --allowedTools (a grant without its server loaded is\n * a dead letter). Names may be single servers from ~/.claude.json's\n * `mcpServers` or `workers.mcpSets` set names, which expand to their member\n * list. The filtered config lands in\n * `<logDir>/<id>.mcp.json` and is passed with `--strict-mcp-config\n * --mcp-config` so exactly those servers load. MCP servers are chosen at\n * launch only — a mid-run `say` cannot add any.\n *\n * One name in that space is not a server at all: `claude-in-chrome` is the\n * Chrome native-host bridge, which no config file can load and which the\n * `--chrome` flag switches on instead. It is recognised here so a grant\n * naming it becomes that flag rather than an \"unknown MCP server\" rejection.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { WorkersConfigError, type WorkersConfig } from \"./config.js\";\n\n/** ~/.claude.json — the user's MCP server definitions (top-level mcpServers). */\nexport const CLAUDE_JSON = join(homedir(), \".claude.json\");\n\n/**\n * The browser bridge is not an MCP server. It rides the Chrome native-host\n * channel, so it never appears in `mcpServers`; a spawned claude has it off\n * and the `--chrome` flag switches it on. Tool grants naming it must\n * therefore neither resolve to a server nor be rejected as unknown — they\n * select a flag. See `grantsChrome`.\n */\nexport const CHROME_SERVER = \"claude-in-chrome\";\n\n/**\n * True for a real server definition, false for anything else that happens to\n * sit under `mcpServers`.\n *\n * The vendor config is not ours and has been observed carrying non-server\n * entries under that key — tool-usage records keyed by tool name\n * (`Read`, `Bash`, `mcp__server__tool`, …). Those are not servers: handing\n * one to `--mcp-config` yields \"invalid MCP server config\", and listing one\n * as available invites a run that cannot work. A server has a `command` (or a\n * `url` for remote transports); an `mcp__…` key is a tool name whatever its\n * shape.\n */\nfunction isServerDefinition(name: string, value: unknown): boolean {\n if (name.startsWith(\"mcp__\")) return false;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const v = value as Record<string, unknown>;\n return (\n (typeof v.command === \"string\" && v.command.length > 0) ||\n (typeof v.url === \"string\" && v.url.length > 0)\n );\n}\n\nexport function readMcpServers(claudeJson = CLAUDE_JSON): Record<string, unknown> {\n try {\n if (!existsSync(claudeJson)) return {};\n const parsed = JSON.parse(readFileSync(claudeJson, \"utf8\")) as Record<string, unknown>;\n const servers = parsed.mcpServers;\n if (typeof servers !== \"object\" || servers === null || Array.isArray(servers)) return {};\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(servers as Record<string, unknown>)) {\n if (isServerDefinition(name, value)) out[name] = value;\n }\n return out;\n } catch {\n // a damaged ~/.claude.json must not take workers down with it\n return {};\n }\n}\n\n/**\n * True when any `--mcp` name or `--allowedTools` grant asks for the browser\n * bridge: the bare server name, `mcp__claude-in-chrome`, or any\n * `mcp__claude-in-chrome__<tool>`. The runner turns this into `--chrome` on\n * the claude argv.\n */\nexport function grantsChrome(entries: string[]): boolean {\n for (const entry of entries) {\n for (const name of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (name === CHROME_SERVER) return true;\n if (name === `mcp__${CHROME_SERVER}`) return true;\n if (name.startsWith(`mcp__${CHROME_SERVER}__`)) return true;\n }\n }\n return false;\n}\n\n/**\n * Split a `--mcp a,b,c` flag value (also accepts repeated flags already split\n * by the caller) and expand set names from workers.mcpSets.\n */\nexport function expandMcpNames(\n names: string[],\n config: Pick<WorkersConfig, \"mcpSets\">,\n claudeJson = CLAUDE_JSON\n): string[] {\n const available = readMcpServers(claudeJson);\n const out: string[] = [];\n for (const raw of names) {\n for (const name of raw.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (name === CHROME_SERVER) continue; // a flag, not a server — see grantsChrome\n if (name in config.mcpSets) {\n for (const member of config.mcpSets[name]) {\n if (!out.includes(member)) out.push(member);\n }\n continue;\n }\n if (!(name in available)) {\n const sets = Object.keys(config.mcpSets);\n throw new WorkersConfigError(\n `unknown MCP server \"${name}\". Available servers: ` +\n `${Object.keys(available).join(\", \") || \"(none in ~/.claude.json)\"}` +\n `${sets.length ? `; sets: ${sets.join(\", \")}` : \"\"}`\n );\n }\n if (!out.includes(name)) out.push(name);\n }\n }\n return out;\n}\n\n/**\n * Derive server names from tool grants: every `mcp__<server>__<tool>` (or bare\n * `mcp__<server>`, or `mcp__<server>__*`) in an --allowedTools list names a\n * server the run expects to be loaded. A grant is a dead letter unless its\n * server is in the filtered config, so the runner treats these as implicit\n * --mcp names — and only these; non-mcp grants load nothing.\n */\nexport function mcpServersFromToolGrants(tools: string[]): string[] {\n const out: string[] = [];\n for (const entry of tools) {\n for (const name of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (!name.startsWith(\"mcp__\")) continue;\n const server = name.slice(\"mcp__\".length).split(\"__\")[0];\n if (server === CHROME_SERVER) continue; // a flag, not a server — see grantsChrome\n if (server && !out.includes(server)) out.push(server);\n }\n }\n return out;\n}\n\n/** Path of a run's filtered MCP config. */\nexport function runMcpConfigPath(logDir: string, id: string): string {\n return join(logDir, `${id}.mcp.json`);\n}\n\n/**\n * Write a config containing only `names` (already expanded) and return its\n * path. Unknown names fail fast with the available list.\n */\nexport function writeMcpConfig(\n logDir: string,\n id: string,\n names: string[],\n claudeJson = CLAUDE_JSON\n): string {\n const available = readMcpServers(claudeJson);\n const unknown = names.filter((n) => !(n in available));\n if (unknown.length) {\n throw new WorkersConfigError(\n `unknown MCP server(s): ${unknown.join(\", \")}. Available: ` +\n `${Object.keys(available).join(\", \") || \"(none in ~/.claude.json)\"}`\n );\n }\n const servers: Record<string, unknown> = {};\n for (const n of names) servers[n] = available[n];\n const path = runMcpConfigPath(logDir, id);\n if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n writeFileSync(path, JSON.stringify({ mcpServers: servers }, null, 2) + \"\\n\", \"utf8\");\n return path;\n}\n\n/** `pai worker mcp list`: the servers and sets a run could name. */\nexport function describeMcp(config: Pick<WorkersConfig, \"mcpSets\">, claudeJson = CLAUDE_JSON): string[] {\n const servers = Object.keys(readMcpServers(claudeJson));\n const lines: string[] = [];\n if (servers.length) {\n lines.push(`servers (~/.claude.json):`);\n for (const s of servers) lines.push(` ${s}`);\n } else {\n lines.push(`no MCP servers defined in ~/.claude.json`);\n }\n const sets = Object.entries(config.mcpSets);\n if (sets.length) {\n lines.push(`sets (workers.mcpSets):`);\n for (const [name, members] of sets) lines.push(` ${name} = ${members.join(\", \")}`);\n }\n lines.push(`usage: pai worker run --mcp <name>[,<name>…] -p '<task>'`);\n return lines;\n}\n","/**\n * project-config.ts — the project registry's optional launch pin, read for\n * the interactive branch of run.ts only: which MCP servers and built-in\n * tools a project's supervisor session (`pai worker run`, no -p) loads. Set\n * with `pai project mcp` / `pai project tools` (src/cli/commands/project/\n * session-config.ts), stored in the same `projects.session_config` JSON\n * column as the rest of a project's launch config. Unset = today's\n * behavior: every server, every tool.\n *\n * A standalone SQL lookup rather than importing the CLI's detectProject:\n * workers/ is a dependency of cli/, not the other way around.\n */\n\nimport { resolve } from \"node:path\";\nimport { getRegistryBackend } from \"../storage/factory.js\";\nimport type { RegistryBackend } from \"../storage/registry-interface.js\";\nimport { readMcpServers } from \"./mcp.js\";\n\nexport interface ProjectLaunchConfig {\n slug: string;\n mcp?: string[];\n tools?: string[];\n}\n\nfunction asStringArray(v: unknown): string[] | undefined {\n if (!Array.isArray(v)) return undefined;\n const out = v.filter((x): x is string => typeof x === \"string\");\n return out.length ? out : undefined;\n}\n\n/**\n * The launch config of the project whose root_path is `cwd` or an ancestor\n * of it (longest match wins) — null when no registered project covers it,\n * or when the matching project has no mcp/tools pin set. `registry` is\n * injectable for tests; production callers take the process-wide backend.\n */\nexport async function projectLaunchConfig(\n cwd: string,\n registry?: RegistryBackend\n): Promise<ProjectLaunchConfig | null> {\n const target = resolve(cwd);\n const backend = registry ?? (await getRegistryBackend());\n const rows = await backend.listProjectsByPathLengthDesc({ excludeArchived: true });\n for (const row of rows) {\n const root = resolve(row.root_path);\n if (target !== root && !target.startsWith(root + \"/\")) continue;\n if (!row.session_config) return null;\n let parsed: { mcp?: unknown; tools?: unknown };\n try {\n parsed = JSON.parse(row.session_config);\n } catch {\n return null;\n }\n const mcp = asStringArray(parsed.mcp);\n const tools = asStringArray(parsed.tools);\n return mcp || tools ? { slug: row.slug, mcp, tools } : null;\n }\n return null;\n}\n\n/**\n * One line per active pin, so a pin never silently removes a server or tool:\n * shown by the SessionStart hook and by `pai worker run`'s interactive launch.\n * `claudeJson` is injectable for tests; read-only.\n */\nexport function pinNotices(launch: ProjectLaunchConfig | null, claudeJson?: string): string[] {\n if (!launch) return [];\n const lines: string[] = [];\n if (launch.mcp) {\n const all = Object.keys(readMcpServers(claudeJson));\n const loaded = all.filter((n) => launch.mcp!.includes(n));\n const missing = all.filter((n) => !launch.mcp!.includes(n));\n lines.push(\n `MCP pinned for ${launch.slug}: loading ${loaded.length} of ${all.length} servers; ` +\n `not loaded: ${missing.join(\", \") || \"(none)\"} — \\`pai project mcp --clear\\` to load all`\n );\n }\n if (launch.tools) {\n lines.push(`Tools pinned for ${launch.slug}: ${launch.tools.join(\",\")} — \\`pai project tools --clear\\``);\n }\n return lines;\n}\n","/**\n * routing.ts — provider selection: flag > class > active (possibly \"auto\").\n *\n * Auto-routing walks `workers.routing.order` — or the class's own `order` —\n * and takes the first provider that is enabled, out of cooldown, (when it\n * defines a quotaProbe) under its quotaSkipAt threshold, and — when the class\n * constrains it — within `maxCostTier` and carrying all `requireTags`. A run\n * that dies of a quota/rate error puts its provider in cooldown for\n * cooldownMinutes; when that happens before the first tool call and\n * retryOnQuota is set, the runner restarts the same task on the next provider\n * (ledger: WORKER-REROUTE).\n *\n * An explicit --provider or a class mapping that pins a provider always\n * bypasses all of this.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n type WorkerEngine,\n type WorkerProvider,\n type WorkersConfig,\n type ProviderTag,\n WORKER_CLASSES,\n ANTHROPIC_NATIVE,\n WorkersConfigError,\n classModelCapability,\n providerCostTier,\n getProviderOrNative,\n resolveCapability,\n} from \"./config.js\";\nimport { routingStatePath } from \"./paths.js\";\n\nconst QUOTA_SKIP_DEFAULT = 95;\nconst PROBE_TIMEOUT_MS = 10_000;\n\nexport interface RoutingState {\n /** provider name → ISO timestamp when its cooldown ends */\n cooldowns: Record<string, string>;\n}\n\nexport function readRoutingState(logDir: string): RoutingState {\n const path = routingStatePath(logDir);\n if (!existsSync(path)) return { cooldowns: {} };\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as RoutingState;\n return { cooldowns: parsed.cooldowns ?? {} };\n } catch {\n return { cooldowns: {} };\n }\n}\n\nexport function writeRoutingState(logDir: string, state: RoutingState): void {\n const path = routingStatePath(logDir);\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n renameSync(tmp, path);\n}\n\nexport function cooldownRemaining(\n state: RoutingState,\n provider: string,\n now: Date = new Date()\n): number {\n const end = state.cooldowns[provider];\n if (!end) return 0;\n const ms = Date.parse(end) - now.getTime();\n return ms > 0 ? ms : 0;\n}\n\nexport function setCooldown(\n logDir: string,\n provider: string,\n minutes: number,\n now: Date = new Date()\n): void {\n const state = readRoutingState(logDir);\n state.cooldowns[provider] = new Date(now.getTime() + minutes * 60_000).toISOString();\n writeRoutingState(logDir, state);\n}\n\nexport function clearCooldown(logDir: string, provider: string): void {\n const state = readRoutingState(logDir);\n if (!(provider in state.cooldowns)) return;\n delete state.cooldowns[provider];\n writeRoutingState(logDir, state);\n}\n\n/**\n * Run a provider's quotaProbe and return its percentage (0–100), or null when\n * there is no probe or it printed nothing usable. A probe must never break\n * routing: failures read as \"unknown\", not as \"full\".\n */\nexport function probeQuota(provider: WorkerProvider): number | null {\n if (!provider.quotaProbe) return null;\n try {\n const out = execFileSync(\"/bin/sh\", [\"-c\", provider.quotaProbe], {\n timeout: PROBE_TIMEOUT_MS,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n const m = out.match(/(\\d+(?:\\.\\d+)?)/);\n if (!m) return null;\n return Math.min(100, Math.max(0, Math.round(parseFloat(m[1]))));\n } catch {\n return null;\n }\n}\n\nexport function quotaSkipThreshold(provider: WorkerProvider): number {\n return provider.quotaSkipAt ?? QUOTA_SKIP_DEFAULT;\n}\n\n/** Does this provider exceed its quota threshold right now? */\nexport function quotaExceeded(provider: WorkerProvider): boolean {\n const used = probeQuota(provider);\n return used !== null && used >= quotaSkipThreshold(provider);\n}\n\nexport interface ResolvedTarget {\n providerName: string;\n provider: WorkerProvider;\n /** Model alias from the class (\"glm/fast\" → \"fast\"), null = provider default. */\n modelAlias: string | null;\n /** MCP servers (or set names) from the class target, null when it sets none. */\n classMcp: string[] | null;\n /** How the provider was chosen — names the bypass rule for rerouting. */\n via: \"flag\" | \"class\" | \"active\" | \"auto\";\n}\n\nexport class NoProviderError extends WorkersConfigError {}\n\nfunction mustExist(config: WorkersConfig, name: string): WorkerProvider {\n const p = getProviderOrNative(config, name);\n if (!p) {\n throw new NoProviderError(\n `no worker provider named \"${name}\". Configured: ` +\n `${Object.keys(config.providers).join(\", \") || \"(none)\"}.` +\n `\\nAdd one with: pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n );\n }\n return p;\n}\n\nfunction mustBeRunnable(name: string, p: WorkerProvider): WorkerProvider {\n if (!p.enabled) {\n throw new NoProviderError(\n `provider \"${name}\" is disabled. Enable it with: pai worker providers enable ${name}`\n );\n }\n return p;\n}\n\n/** Why one provider of a routing order did not qualify (message fragment). */\nfunction exclusionReason(\n config: WorkersConfig,\n state: RoutingState,\n name: string,\n cls?: { maxCostTier?: number; requireTags?: string[] }\n): string | null {\n const p = getProviderOrNative(config, name);\n if (!p) return \"not configured\";\n if (!p.enabled) return \"disabled\";\n if (cooldownRemaining(state, name) > 0) return \"cooldown\";\n if (quotaExceeded(p)) return \"quota\";\n const tier = providerCostTier(p);\n if (cls?.maxCostTier !== undefined && tier > cls.maxCostTier) {\n return `cost tier ${tier} > max ${cls.maxCostTier}`;\n }\n if (cls?.requireTags?.length) {\n const have = p.tags ?? [];\n const missing = cls.requireTags.filter((t) => !have.includes(t as ProviderTag));\n if (missing.length) return `missing tags: ${missing.join(\", \")}`;\n }\n return null;\n}\n\n/**\n * Resolve which provider (and model alias) a run uses.\n *\n * @param flagProvider --provider value, highest precedence\n * @param className --class value (the old --role), looked up in workers.classes\n */\nexport function resolveTarget(\n config: WorkersConfig,\n logDir: string,\n opts: { flagProvider?: string; className?: string } = {}\n): ResolvedTarget {\n if (opts.flagProvider) {\n return {\n providerName: opts.flagProvider,\n provider: mustBeRunnable(opts.flagProvider, mustExist(config, opts.flagProvider)),\n modelAlias: null,\n classMcp: null,\n via: \"flag\",\n };\n }\n\n // class constraints (present whether or not the target pins a provider)\n const clsTarget = opts.className ? config.classes[opts.className] : undefined;\n const cls =\n typeof clsTarget === \"object\" && clsTarget !== null ? clsTarget : undefined;\n\n if (opts.className && clsTarget !== undefined && typeof clsTarget !== \"object\") {\n const [name, alias] = clsTarget.split(\"/\");\n return {\n providerName: name,\n provider: mustBeRunnable(name, mustExist(config, name)),\n modelAlias: alias ?? null,\n classMcp: null,\n via: \"class\",\n };\n }\n if (opts.className && cls?.provider) {\n const provider = mustBeRunnable(cls.provider, mustExist(config, cls.provider));\n return {\n providerName: cls.provider,\n provider,\n modelAlias: null,\n classMcp: cls.mcp ?? null,\n via: \"class\",\n };\n }\n if (opts.className && clsTarget === undefined) {\n // a standard class may simply be unconfigured (it then routes like a run\n // without a class); anything else is a typo and must not pass silently\n if (!(WORKER_CLASSES as readonly string[]).includes(opts.className)) {\n throw new NoProviderError(\n `no class named \"${opts.className}\". Standard classes: ${WORKER_CLASSES.join(\", \")}.` +\n `Defined: ${Object.keys(config.classes).join(\", \") || \"(none)\"}.` +\n `\\nSet one with: pai worker classes set ${opts.className}=<provider[/alias]>`\n );\n }\n }\n\n if (config.active !== \"auto\") {\n // nothing active and no class entry: the built-in provider, no config written\n const name = config.active ?? ANTHROPIC_NATIVE;\n return {\n providerName: name,\n provider: mustBeRunnable(name, mustExist(config, name)),\n modelAlias: null,\n classMcp: null,\n via: \"active\",\n };\n }\n\n // auto: first provider in (class or global) order that is enabled,\n // cooled-down-free, under quota and within the class constraints\n const state = readRoutingState(logDir);\n const order = cls?.order ?? config.routing.order;\n const excluded: string[] = [];\n for (const name of order) {\n const p = getProviderOrNative(config, name);\n const why = exclusionReason(config, state, name, cls);\n if (why) {\n excluded.push(`${name}: ${why}`);\n continue;\n }\n // exclusionReason already returned null (no exclusion) above, which is\n // only possible when getProviderOrNative found a real provider\n return { providerName: name, provider: p!, modelAlias: null, classMcp: cls?.mcp ?? null, via: \"auto\" };\n }\n throw new NoProviderError(\n `auto-routing found no usable provider${opts.className ? ` for class \"${opts.className}\"` : \"\"} ` +\n `(order: [${order.join(\", \")}]).` +\n `${excluded.length ? `\\nExcluded: ${excluded.join(\"; \")}.` : \"\"}` +\n `\\nClear a cooldown with: pai worker providers enable <name>; widen the class with: pai worker classes set ${opts.className ?? \"<class>\"}=<target>`\n );\n}\n\n/**\n * Next provider after `from` in auto order, applying the same filters.\n * Used by rerouting; null when the order is exhausted.\n */\nexport function nextAutoProvider(\n config: WorkersConfig,\n logDir: string,\n from: string,\n now: Date = new Date()\n): string | null {\n const state = readRoutingState(logDir);\n const order = config.routing.order;\n const start = order.indexOf(from);\n for (let i = start + 1; i < order.length; i++) {\n const name = order[i];\n const p = getProviderOrNative(config, name);\n if (!p || !p.enabled) continue;\n const end = state.cooldowns[name];\n if (end && Date.parse(end) > now.getTime()) continue;\n if (quotaExceeded(p)) continue;\n return name;\n }\n return null;\n}\n\n/**\n * Was this failure a quota/rate failure? Detected from the result text the\n * endpoint produced (HTTP status is not visible in the stream events).\n */\nexport function isQuotaFailure(resultText: string): boolean {\n return /usage limit reached|rate limit|quota/i.test(resultText);\n}\n\nexport interface CapabilityRunResolution {\n providerName: string;\n provider: WorkerProvider;\n model: string;\n engine: WorkerEngine;\n fellBack: boolean;\n capability: string;\n}\n\n/**\n * Whether a run should resolve through `resolveCapability` instead of\n * `resolveTarget`: an explicit `--capability` always does. A `--class`\n * whose implied capability is not \"default\" (e.g. `image`) does too, but\n * only when the class itself names no provider — a class already pinned to\n * one (`image: glm/image`) always wins unchanged, same as any other class.\n */\nexport function capabilityForRun(\n config: WorkersConfig,\n opts: { capabilityFlag?: string; className?: string }\n): string | null {\n if (opts.capabilityFlag) return opts.capabilityFlag;\n if (!opts.className) return null;\n const capability = classModelCapability(opts.className);\n if (capability === \"default\") return null;\n const clsTarget = config.classes[opts.className];\n const namesProvider = typeof clsTarget === \"string\" ? true : Boolean(clsTarget?.provider);\n return namesProvider ? null : capability;\n}\n\n/**\n * resolveCapability, turned into a runnable provider (NoProviderError-shaped\n * on failure). Unlike resolveCapability itself — which falls back to the\n * active provider's default model so `describeCapabilities` has something to\n * report — an actual run refuses the fallback: silently running a normal\n * model on a prompt written for a capability it does not have (an image\n * prompt fed to a text model, say) produces a confusing non-answer instead\n * of a clear error naming the config key to set.\n */\nexport function resolveCapabilityRun(config: WorkersConfig, capability: string): CapabilityRunResolution {\n const r = resolveCapability(config, capability);\n if (r.fellBack) {\n throw new NoProviderError(\n `no provider declares the \"${capability}\" capability — configure one with: ` +\n `pai worker capability ${capability} <provider>[,<provider>…], or ` +\n `pai worker model ${capability} <model-id> --provider <name>`\n );\n }\n // \"image\" specifically promises real pixel output — a provider whose only\n // claim to it is a `models.image` entry from the classes-based idiom\n // (`image: glm/image`, a plain model choice on an unchanged claude/codex\n // engine) cannot deliver that through this cross-provider run path, so a\n // run refuses it rather than spawning claude on a prompt it cannot answer.\n if (capability === \"image\" && r.engine !== \"image\") {\n throw new NoProviderError(\n `provider \"${r.provider}\" has no image engine (it would run \"${r.model}\" on its normal ` +\n `${r.engine} engine, not generate an image) — configure an engine=image provider with: ` +\n `pai worker providers add <name> --engine image --url <url> --key <key> --model <id>, then ` +\n `pai worker capability image <name>`\n );\n }\n const provider = getProviderOrNative(config, r.provider);\n if (!provider) {\n throw new NoProviderError(`capability \"${capability}\" resolved to unknown provider \"${r.provider}\"`);\n }\n return {\n providerName: r.provider,\n provider,\n model: r.model,\n engine: r.engine,\n fellBack: r.fellBack,\n capability,\n };\n}\n","/**\n * server.ts — the PAI worker proxy: Anthropic Messages API on the front,\n * OpenAI Chat Completions on the back, loopback only.\n *\n * One proxy serves every OpenAI-protocol provider: Claude Code points\n * ANTHROPIC_BASE_URL at `http://127.0.0.1:8797/<provider>` and the provider\n * name in the path selects the upstream (`upstreamUrl` + `keyFile` from the\n * workers config, re-read per request so config edits apply without a\n * restart). `run` starts the proxy on demand (detached, pid file under the\n * logDir) and `pai worker proxy stop` stops it again.\n *\n * All translation lives in translate.ts; this file is only HTTP plumbing.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer, type Server } from \"node:http\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { readWorkersSection, resolveProviderKey, type WorkerProvider } from \"../config.js\";\nimport { workersLogDir } from \"../paths.js\";\nimport {\n anthropicError,\n anthropicToOpenAi,\n openAiToAnthropic,\n OpenAiStreamTranslator,\n type AnthropicRequest,\n} from \"./translate.js\";\n\nexport const DEFAULT_PROXY_PORT = 8797;\nconst HOST = \"127.0.0.1\";\n\n/** How providers are looked up — swapped out by the tests. */\nexport type ProviderResolver = () => Record<string, WorkerProvider>;\n\nconst configProviders: ProviderResolver = () => readWorkersSection().workers.providers;\n\nexport function proxyPidPath(logDir: string): string {\n return join(logDir, \"proxy.pid\");\n}\n\n// ---------------------------------------------------------------------------\n// HTTP server\n// ---------------------------------------------------------------------------\n\ninterface ProxyServerOptions {\n port?: number;\n resolveProvider?: ProviderResolver;\n /** Test hook: called with every (translated) upstream request body. */\n onUpstreamRequest?: (url: string, body: unknown) => void;\n}\n\n/**\n * Create (but not start) the proxy server. Routes:\n * GET /healthz → 200 ok\n * POST /<provider>/v1/messages (and /<provider>/messages) → translated call\n */\nexport function createProxyServer(opts: ProxyServerOptions = {}): Server {\n const resolveProvider = opts.resolveProvider ?? configProviders;\n return createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${HOST}`);\n if (req.method === \"GET\" && url.pathname === \"/healthz\") {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"ok\");\n return;\n }\n const m = url.pathname.match(/^\\/([a-zA-Z0-9_-]+)\\/(v1\\/)?messages$/);\n if (req.method !== \"POST\" || !m) {\n res.writeHead(404, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(404, `no such route: ${req.method} ${url.pathname}`)));\n return;\n }\n const providerName = m[1];\n const chunks: Buffer[] = [];\n req.on(\"data\", (c: Buffer) => chunks.push(c));\n req.on(\"end\", () => {\n void handleMessages(providerName, Buffer.concat(chunks).toString(\"utf8\"), resolveProvider, res, opts);\n });\n req.on(\"error\", () => {\n res.writeHead(400, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(400, \"request body read failed\")));\n });\n });\n}\n\nasync function handleMessages(\n providerName: string,\n bodyText: string,\n resolveProvider: ProviderResolver,\n res: import(\"node:http\").ServerResponse,\n opts: ProxyServerOptions\n): Promise<void> {\n const fail = (status: number, message: string) => {\n res.writeHead(status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(status, JSON.stringify({ message }))));\n };\n let reqBody: AnthropicRequest;\n try {\n reqBody = JSON.parse(bodyText) as AnthropicRequest;\n } catch {\n fail(400, \"request body is not valid JSON\");\n return;\n }\n const provider = resolveProvider()[providerName];\n if (!provider) {\n fail(404, `no worker provider named \"${providerName}\"`);\n return;\n }\n if (provider.protocol !== \"openai\" || !provider.upstreamUrl) {\n fail(400, `provider \"${providerName}\" is not an openai-protocol provider`);\n return;\n }\n\n const model = provider.models.default;\n const openaiBody = anthropicToOpenAi(reqBody, model);\n opts.onUpstreamRequest?.(`${provider.upstreamUrl}/chat/completions`, openaiBody);\n\n let upstream: Response;\n try {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n let token: string | null;\n try {\n token = resolveProviderKey(provider);\n } catch (e) {\n fail(500, e instanceof Error ? e.message : String(e));\n return;\n }\n if (token) headers.authorization = `Bearer ${token}`;\n upstream = await fetch(`${provider.upstreamUrl}/chat/completions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(openaiBody),\n });\n } catch (e) {\n fail(502, `upstream unreachable: ${e instanceof Error ? e.message : String(e)}`);\n return;\n }\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n res.writeHead(upstream.status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(upstream.status, text)));\n return;\n }\n\n if (reqBody.stream) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n const translator = new OpenAiStreamTranslator(model);\n let buf = \"\";\n if (!upstream.body) {\n res.end(translator.finish());\n return;\n }\n const reader = upstream.body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += new TextDecoder().decode(value, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line.startsWith(\"data:\")) continue;\n const payload = line.slice(5).trim();\n if (payload === \"[DONE]\") {\n res.write(translator.finish());\n } else {\n try {\n res.write(translator.feed(JSON.parse(payload) as Parameters<typeof translator.feed>[0]));\n } catch {\n // skip a malformed chunk rather than kill the stream\n }\n }\n }\n }\n } finally {\n res.write(translator.finish()); // no-op when [DONE] already finished it\n res.end();\n }\n return;\n }\n\n const json = (await upstream.json().catch(() => null)) as unknown;\n if (!json) {\n fail(502, \"upstream returned a non-JSON body\");\n return;\n }\n res.writeHead(200, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(openAiToAnthropic(json as Parameters<typeof openAiToAnthropic>[0], model)));\n}\n\n// ---------------------------------------------------------------------------\n// Start / stop\n// ---------------------------------------------------------------------------\n\n/** Listen on host:port (loopback). Resolves with the bound port. */\nexport function listenProxy(server: Server, port = DEFAULT_PROXY_PORT): Promise<number> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, HOST, () => {\n const addr = server.address();\n resolve(typeof addr === \"object\" && addr ? addr.port : port);\n });\n });\n}\n\n/** True when something already accepts connections on the port. */\nexport function proxyListening(port = DEFAULT_PROXY_PORT, timeoutMs = 400): Promise<boolean> {\n return new Promise((resolve) => {\n const sock = connect({ port, host: HOST });\n const done = (ok: boolean) => {\n sock.removeAllListeners();\n sock.destroy();\n resolve(ok);\n };\n sock.setTimeout(timeoutMs, () => done(false));\n sock.once(\"connect\", () => done(true));\n sock.once(\"error\", () => done(false));\n });\n}\n\n/**\n * Where the standalone proxy artefact lives, found from this module's own\n * location (dist/cli bundle → ../hooks/, dev checkout → <repo>/dist/hooks).\n */\nexport function standaloneProxyPath(): string | null {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 6; i++) {\n for (const cand of [\n join(dir, \"worker-proxy.mjs\"),\n join(dir, \"hooks\", \"worker-proxy.mjs\"),\n join(dir, \"dist\", \"hooks\", \"worker-proxy.mjs\"),\n ]) {\n if (existsSync(cand)) return cand;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/**\n * Ensure a proxy is listening on the port; start the detached standalone when\n * nothing answers. Returns the base URL (`http://127.0.0.1:<port>`).\n */\nexport async function ensureProxyRunning(port: number, logDir: string): Promise<string> {\n if (await proxyListening(port)) return `http://${HOST}:${port}`;\n const script = standaloneProxyPath();\n if (!script) {\n throw new Error(\n `the PAI worker proxy is not running and its build was not found — ` +\n `run \\`bun run build\\` (or start one with: pai worker proxy --port ${port})`\n );\n }\n if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n const child = spawn(process.execPath, [script, \"--port\", String(port)], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n writeFileSync(proxyPidPath(logDir), `${child.pid}\\n`, \"utf8\");\n for (let i = 0; i < 40; i++) {\n if (await proxyListening(port)) return `http://${HOST}:${port}`;\n await new Promise((r) => setTimeout(r, 250));\n }\n throw new Error(`the PAI worker proxy did not come up on port ${port} (see ${proxyPidPath(logDir)})`);\n}\n\n/** `pai worker proxy stop`: SIGTERM the pid from the pid file. */\nexport function stopProxy(logDir: string): string {\n const path = proxyPidPath(logDir);\n if (!existsSync(path)) return \"no proxy pid file — nothing to stop\";\n const pid = parseInt(readFileSync(path, \"utf8\").trim(), 10);\n unlinkSync(path);\n if (!Number.isFinite(pid)) return \"stale proxy pid file removed\";\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n return `proxy pid ${pid} was not running (pid file removed)`;\n }\n return `proxy pid ${pid} stopped`;\n}\n","/**\n * scope.ts — which workers \"belong\" to the terminal asking about them.\n *\n * Two identities, in priority order:\n *\n * 1. AIBroker's session registry (~/.aibroker/session-names.json): iTerm\n * session UUID → session name. The launching session's UUID is stored in\n * each worker's status file as `session.id`, so every pane of a named\n * session sees its workers — regardless of tab layout.\n * 2. The iTerm tab key (`w<window>t<tab>` prefix of ITERM_SESSION_ID): the\n * original heuristic, kept as the fallback when no registry entry\n * matches (AIBroker absent, unnamed session, non-iTerm terminal).\n *\n * If neither is available, viewers fall back to \"all workers\".\n *\n * A third case: workers spawned from a Claude Code Bash tool (the\n * orchestrator pattern) inherit the orchestrator's `ITERM_SESSION_ID`, which\n * identifies the orchestrator's own pane, not a pane for the sub-worker —\n * so it does not key the sub-worker's tab. Those record `spawnerSession`,\n * the orchestrator's claude session id bridged through the status line's\n * session map (see below), so their tab still claims them.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { loadStatus, type WorkerStatus } from \"./status.js\";\n\nexport const AIBROKER_REGISTRY = join(homedir(), \".aibroker\", \"session-names.json\");\n\n/**\n * `w<window>t<tab>` prefix of an iTerm session id, \"\" if empty or malformed.\n * Panes split from the same tab share this prefix and differ only in `p<n>`.\n */\nexport function tabKey(term: string): string {\n if (!term) return \"\";\n const head = term.split(\"p\", 1)[0];\n const parts = head.slice(1).split(\"t\");\n if (head.startsWith(\"w\") && parts.length === 2 && parts.every((p) => /^\\d+$/.test(p))) {\n return head;\n }\n return \"\";\n}\n\n/** Tab key of the iTerm tab this process runs in, \"\" if not inside iTerm2. */\nexport function currentTabKey(env: NodeJS.ProcessEnv = process.env): string {\n return tabKey(env.ITERM_SESSION_ID ?? \"\");\n}\n\n/** The iTerm UUID part of an ITERM_SESSION_ID (after the last colon). */\nexport function itermUuid(term: string): string {\n if (!term) return \"\";\n return term.split(\":\").pop() ?? \"\";\n}\n\nexport interface SessionIdentity {\n id: string;\n name: string;\n}\n\n/**\n * Resolve the AIBroker session for an ITERM_SESSION_ID. Reads the persistent\n * name registry (the same store `aibroker_rename` writes); returns null when\n * AIBroker is absent or the session is not in it — callers then use the tab\n * key, which is the pre-AIBroker behaviour.\n */\nexport function resolveSession(\n term: string,\n registryPath: string = AIBROKER_REGISTRY\n): SessionIdentity | null {\n const uuid = itermUuid(term);\n if (!uuid) return null;\n let names: Record<string, unknown>;\n try {\n if (!existsSync(registryPath)) return null;\n names = JSON.parse(readFileSync(registryPath, \"utf8\")) as Record<string, unknown>;\n } catch {\n return null;\n }\n const name = names[uuid];\n if (typeof name !== \"string\" || !name) return null;\n return { id: uuid, name };\n}\n\n/**\n * True when `worker` was launched from `term` (the terminal asking about it).\n * Session-id match first; the tab key only when no registry entry matched —\n * a session without a name keeps the tab-scoped behaviour it always had.\n */\nexport function workerInScope(worker: WorkerStatus, term: string): boolean {\n if (!term) return false;\n const uuid = itermUuid(term);\n if (worker.session?.id && uuid) return worker.session.id === uuid;\n return tabKey(worker.term) === tabKey(term) && tabKey(term) !== \"\";\n}\n\n/**\n * Registry key for pane files: the session id when AIBroker knows this\n * terminal, else the tab key, else the raw iTerm UUID.\n */\nexport function scopeKey(term: string): string {\n const session = resolveSession(term);\n if (session) return session.id;\n return currentTabKey() || itermUuid(term);\n}\n\n/** `[Name]` when the worker has an AIBroker session name, else \"\". */\nexport function sessionTag(worker: { session?: { name?: string } | null }): string {\n return worker.session?.name ? `[${worker.session.name}]` : \"\";\n}\n\n// ---------------------------------------------------------------------------\n// spawner sessions — attribution for workers launched from a Claude Code Bash\n// ---------------------------------------------------------------------------\n\n/**\n * Claude Code exports neither ITERM_SESSION_ID nor its own session id to the\n * Bash tool, so a `pai worker run` from an orchestrator has no terminal to\n * record and its status would be unattributable. The status line is the one\n * process that sees both identities at once — the payload's session_id and\n * the tab's ITERM_SESSION_ID — so it bridges them: every refresh writes this\n * cwd-keyed map, and the runner reads it back at spawn time.\n */\nexport interface SessionMapEntry {\n session: string;\n ts: number;\n /**\n * The tab's ITERM_SESSION_ID, when the status line could see one — the\n * bridge back from a claude session id to the terminal it runs in\n * (supervision uses it to push worker events into that terminal).\n * Absent on entries written before the field existed.\n */\n term?: string;\n}\n\n/** How old a map entry may be for a spawn to adopt it (status lines refresh constantly while a session lives). */\nexport const SPAWNER_SESSION_TTL_MS = 10 * 60_000;\n\n/** Map entries not refreshed within this window are pruned on write. */\nconst SESSION_MAP_PRUNE_MS = 60 * 60_000;\n\nexport function sessionMapPath(logDir: string): string {\n return join(logDir, \"claude-session-map.json\");\n}\n\n/**\n * Record that the claude session `session` renders its status line in `cwd`\n * (and, when known, in iTerm session `term`). Never throws — a broken map\n * must not break the bar. Prunes stale entries; skips the write when the\n * entry is unchanged and fresh.\n */\nexport function recordSessionMapEntry(\n logDir: string,\n cwd: string,\n session: string,\n term?: string,\n now: number = Date.now()\n): void {\n if (!cwd || !session) return;\n const path = sessionMapPath(logDir);\n let map: Record<string, SessionMapEntry> = {};\n try {\n if (existsSync(path)) {\n map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n }\n } catch {\n map = {}; // a damaged map is rewritten, never fatal\n }\n const prev = map[cwd];\n if (\n prev &&\n prev.session === session &&\n (prev.term ?? \"\") === (term ?? \"\") &&\n now - prev.ts < 60_000\n ) {\n return;\n }\n const pruned: Record<string, SessionMapEntry> = {};\n for (const [dir, e] of Object.entries(map)) {\n if (now - e.ts < SESSION_MAP_PRUNE_MS) pruned[dir] = e;\n }\n pruned[cwd] = { session, ts: now, ...(term ? { term } : {}) };\n try {\n mkdirSync(logDir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(pruned), \"utf8\");\n renameSync(tmp, path);\n } catch {\n // unwritable log dir: no attribution this round, nothing else breaks\n }\n}\n\n/**\n * The claude session a new run was spawned by, when it can be known: a run\n * inside another worker inherits its spawner (chain stages keep the\n * orchestrator's tab that way); otherwise a fresh map entry for `cwd`.\n */\nexport function resolveSpawnerSession(\n logDir: string,\n cwd: string,\n env: NodeJS.ProcessEnv = process.env,\n now: number = Date.now()\n): string | null {\n const parentId = env.PAI_WORKER_ID;\n if (parentId) {\n const inherited = loadStatus(logDir, parentId)?.spawnerSession;\n if (inherited) return inherited;\n }\n const path = sessionMapPath(logDir);\n if (!existsSync(path) || !cwd) return null;\n try {\n const entry = (JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>)[cwd];\n if (entry && entry.session && now - entry.ts < SPAWNER_SESSION_TTL_MS) return entry.session;\n } catch {\n // a damaged map simply attributes nothing\n }\n return null;\n}\n\n/**\n * The iTerm UUID a claude session last rendered its status line in, when the\n * map knows one: the freshest entry naming that session. This is the reverse\n * of resolveSpawnerSession — worker → orchestrator there, orchestrator →\n * terminal here — and it exists so daemon-side pushes (supervision) can reach\n * a session that has no iTerm identity of its own in any worker status.\n */\nexport function itermForClaudeSession(\n logDir: string,\n claudeSession: string,\n now: number = Date.now()\n): string | null {\n const path = sessionMapPath(logDir);\n if (!existsSync(path) || !claudeSession) return null;\n let map: Record<string, SessionMapEntry>;\n try {\n map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n } catch {\n return null;\n }\n let best: SessionMapEntry | null = null;\n for (const e of Object.values(map)) {\n if (e.session !== claudeSession || !e.term) continue;\n // a stale entry names a tab the session left; freshness orders them\n if (now - e.ts >= SESSION_MAP_PRUNE_MS) continue;\n if (!best || e.ts > best.ts) best = e;\n }\n const term = best?.term;\n return term ? itermUuid(term) : null;\n}\n","/**\n * Who or what ended a worker. One place decides the reason; the runner's\n * signal handlers and the deadline record it, `pai worker kill` and the daemon\n * leave a kill-request marker (`<logDir>/<id>.kill`) before signalling so the\n * runner can tell their stops from an external one.\n */\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { appendLedger } from \"./ledger.js\";\n\nexport type EndReason = \"exit\" | \"deadline\" | \"pai-worker-kill\" | \"parent-gone\" | \"external-signal\";\n\nexport interface EndInfo {\n reason: EndReason;\n by?: string;\n note?: string;\n}\n\nexport interface KillRequest {\n ts: string;\n by: string;\n pid: number;\n ppid: number;\n cwd: string;\n}\n\nexport const killMarkerPath = (logDir: string, id: string): string => join(logDir, `${id}.kill`);\n\n/** Best description of the invoking session: PAI parent/AIBroker session env, else the Claude Code session id. */\nexport function callerDescription(env: NodeJS.ProcessEnv = process.env): string {\n const name =\n env.PAI_WORKER_PARENT ?? env.PAI_SESSION_NAME ?? env.AIBROKER_SESSION_NAME ?? env.AIBROKER_SESSION;\n if (name) return name;\n if (env.CLAUDE_CODE_SESSION_ID) return `claude-session:${env.CLAUDE_CODE_SESSION_ID}`;\n return env.CLAUDECODE ? \"claude-code\" : \"shell\";\n}\n\n/** Write the kill-request marker and the WORKER-KILL ledger line; call right before signalling. */\nexport function requestKill(logDir: string, ledger: string, id: string, by: string = callerDescription()): void {\n const req: KillRequest = {\n ts: new Date().toISOString(),\n by,\n pid: process.pid,\n ppid: process.ppid,\n cwd: process.cwd(),\n };\n writeFileSync(killMarkerPath(logDir, id), JSON.stringify(req));\n appendLedger(ledger, \"WORKER-KILL\", { id, by, pid: req.pid, ppid: req.ppid, cwd: req.cwd });\n}\n\nfunction readMarker(logDir: string, id: string): KillRequest | null {\n const p = killMarkerPath(logDir, id);\n if (!existsSync(p)) return null;\n try {\n return JSON.parse(readFileSync(p, \"utf8\")) as KillRequest;\n } catch {\n return { ts: \"\", by: \"unknown\", pid: 0, ppid: 0, cwd: \"\" };\n } finally {\n try {\n unlinkSync(p);\n } catch {\n /* already gone */\n }\n }\n}\n\nexport function pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/** Command name of a pid (`ps -o comm=`), or undefined when it is gone or ps fails. */\nexport function commOf(pid: number): string | undefined {\n try {\n return execFileSync(\"ps\", [\"-o\", \"comm=\", \"-p\", String(pid)], { encoding: \"utf8\" }).trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Parent facts recorded at start, for the end report (\"parent: claude\"). */\nexport function parentInfo(): { parentPid: number; parentComm?: string; parentBackground?: boolean } {\n const parentPid = process.ppid;\n return {\n parentPid,\n parentComm: commOf(parentPid),\n // a Claude Code Bash task carries CLAUDECODE; the runner strips it from children only\n parentBackground: process.env.CLAUDECODE ? true : undefined,\n };\n}\n\n/**\n * The single decision. `signal` = the signal the runner received (absent for a\n * deadline stop or a normal exit); `timedOut` = the deadline fired. Reads (and\n * consumes) the kill marker.\n */\nexport function decideEndReason(a: {\n logDir: string;\n id: string;\n signal?: string;\n timedOut?: boolean;\n parentPid?: number;\n parentComm?: string;\n parentBackground?: boolean;\n isAlive?: (pid: number) => boolean;\n}): EndInfo {\n if (a.timedOut) return { reason: \"deadline\" };\n if (!a.signal) return { reason: \"exit\" };\n const marker = readMarker(a.logDir, a.id);\n if (marker) return { reason: \"pai-worker-kill\", by: marker.by, note: `${a.signal} requested by pid ${marker.pid}` };\n const alive = (a.isAlive ?? pidAlive)(a.parentPid ?? process.ppid);\n const parent = `parent ${a.parentPid ?? \"?\"}${a.parentComm ? ` (${a.parentComm})` : \"\"}`;\n if (!alive) return { reason: \"parent-gone\", note: `${a.signal}; ${parent} not alive` };\n return {\n reason: \"external-signal\",\n note:\n `${a.signal}; sender unknown (not deadline, not pai worker kill); a Claude Code background task stop ` +\n `(TaskStop, Bash timeout, session end) is the usual source; ${parent} alive` +\n (a.parentBackground ? \"; started from a Claude Code session\" : \"\"),\n };\n}\n\n/** The status/ledger fields for an end. */\nexport function endFields(e: EndInfo): { reason: EndReason; by?: string } {\n return { reason: e.reason, ...(e.by ? { by: e.by } : {}) };\n}\n\n/** \"external-signal\", \"pai-worker-kill by <caller>\" for a finished worker; null when nothing was recorded. */\nexport function endLabel(s: { endReason?: string; endBy?: string }): string | null {\n return s.endReason ? `${s.endReason}${s.endBy ? ` by ${s.endBy}` : \"\"}` : null;\n}\n","/**\n * controls.ts — the clickr argv for handing desktop control to a worker or\n * taking it back, shared by `pai worker controls` and the follow pane's\n * \"your controls\" / \"my controls\" chat action, so both go through clickr the\n * same way.\n *\n * The grant is exclusive to one worker at a time: clickr's MCP server checks\n * PAI_WORKER_ID/pid against the `--agent <id> --pid <pid>` it was handed\n * control for. Crash paths are covered by clickr's own pid check; a clean\n * exit is covered by returnControlsIfHeld below, called from every exit path\n * of run.ts's runWorker.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { loadStatus, saveStatus } from \"./status.js\";\n\n/** clickr argv for `who`: `you` binds the grant to `id` (and its runner pid, when known); `me` has no target. */\nexport function clickrControlsArgv(id: string, who: \"you\" | \"me\", pid?: number): string[] {\n if (who === \"me\") return [\"controls\", \"me\"];\n const argv = [\"controls\", \"you\", \"--agent\", id];\n if (pid !== undefined) argv.push(\"--pid\", String(pid));\n return argv;\n}\n\n/** clickr argv to hand a worker's grant back on its exit. */\nexport function clickrControlsReturnArgv(id: string): string[] {\n return [\"controls\", \"return\", \"--agent\", id];\n}\n\n/** Record that `id` was granted clickr desktop controls (`who: \"you\"` succeeded). */\nexport function markControlsHeld(logDir: string, id: string): void {\n const status = loadStatus(logDir, id);\n if (!status) return;\n status.controlsHeld = true;\n saveStatus(logDir, status);\n}\n\n/**\n * Best-effort hand desktop controls back on a worker's exit, when it ever\n * held them. Never throws and never hangs past clickr's own exit or a 3s\n * cap — a missing clickr binary or a stuck process must not hold up the\n * worker's own exit.\n */\nexport async function returnControlsIfHeld(logDir: string, id: string | undefined): Promise<void> {\n if (!id) return;\n const status = loadStatus(logDir, id);\n if (!status?.controlsHeld) return;\n await new Promise<void>((resolve) => {\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n resolve();\n };\n try {\n const proc = spawn(\"clickr\", clickrControlsReturnArgv(id), { stdio: \"ignore\" });\n proc.on(\"close\", finish);\n proc.on(\"error\", finish);\n } catch {\n finish();\n }\n setTimeout(finish, 3000).unref();\n });\n}\n","/**\n * engines/image.ts — the `engine: image` worker.\n *\n * No claude-code process, no worktree, no MCP: `pai worker run --capability\n * image` POSTs one OpenAI-compatible images-generations request straight to\n * the provider's `url` and writes the PNG it gets back. One request, no\n * retries beyond the fetch's own timeout — a caller that wants another try\n * just runs again.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { resolveProviderKey, WorkersConfigError, type WorkerProvider } from \"../config.js\";\n\nexport interface ImageRunOptions {\n providerName: string;\n provider: WorkerProvider;\n model: string;\n prompt: string;\n /** Where the PNG is written; caller resolves the default path. */\n outPath: string;\n /** \"WIDTHxHEIGHT\", default 1024x1024. */\n size?: string;\n /** Default 120000 (2 minutes). */\n timeoutMs?: number;\n}\n\nexport interface ImageRunResult {\n ok: true;\n path: string;\n model: string;\n provider: string;\n durationMs: number;\n bytes: number;\n}\n\nconst DEFAULT_SIZE = \"1024x1024\";\nexport const DEFAULT_IMAGE_TIMEOUT_MS = 120_000;\n\ninterface ImagesGenerationsResponse {\n data?: { b64_json?: string }[];\n}\n\n/**\n * `POST {url}/images/generations` with `{model, prompt, size, n: 1,\n * response_format: \"b64_json\"}` and the provider's bearer token, decode the\n * base64 PNG it returns, and write it to `outPath`.\n */\nexport async function runImageCapability(opts: ImageRunOptions): Promise<ImageRunResult> {\n if (!opts.provider.baseUrl) {\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\" (engine image) has no \"url\" configured — set one with: ` +\n `pai worker providers update ${opts.providerName} --base-url <url>`\n );\n }\n const token = resolveProviderKey(opts.provider);\n const url = `${opts.provider.baseUrl.replace(/\\/+$/, \"\")}/images/generations`;\n const timeoutMs = opts.timeoutMs ?? DEFAULT_IMAGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const t0 = Date.now();\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(token ? { authorization: `Bearer ${token}` } : {}),\n },\n body: JSON.stringify({\n model: opts.model,\n prompt: opts.prompt,\n size: opts.size ?? DEFAULT_SIZE,\n n: 1,\n response_format: \"b64_json\",\n }),\n signal: controller.signal,\n });\n } catch (e) {\n if (e instanceof Error && e.name === \"AbortError\") {\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\": image request timed out after ${timeoutMs}ms`\n );\n }\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\": image request failed: ${e instanceof Error ? e.message : String(e)}`\n );\n } finally {\n clearTimeout(timer);\n }\n const durationMs = Date.now() - t0;\n const text = await res.text();\n if (!res.ok) {\n throw new WorkersConfigError(\n `provider \"${opts.providerName}\": image request failed (${res.status}): ${text.slice(0, 500)}`\n );\n }\n let parsed: ImagesGenerationsResponse;\n try {\n parsed = JSON.parse(text) as ImagesGenerationsResponse;\n } catch {\n throw new WorkersConfigError(`provider \"${opts.providerName}\": image response was not JSON`);\n }\n const b64 = parsed.data?.[0]?.b64_json;\n if (!b64) {\n throw new WorkersConfigError(`provider \"${opts.providerName}\": image response had no data[0].b64_json`);\n }\n const bytes = Buffer.from(b64, \"base64\");\n mkdirSync(dirname(opts.outPath), { recursive: true });\n writeFileSync(opts.outPath, bytes);\n return {\n ok: true,\n path: opts.outPath,\n model: opts.model,\n provider: opts.providerName,\n durationMs,\n bytes: bytes.length,\n };\n}\n","/**\n * pane.ts — the per-worker follow pane in iTerm2.\n *\n * One small-font pane per worker, stacked in a right-hand column: the first\n * worker of a scope splits the launching session vertically, every further\n * one splits the lowest live worker pane horizontally, so panes stack top to\n * bottom. The split never sizes the new session — that would grow the whole\n * window; instead the window's bounds are read before the split and restored\n * right after, so the panes share the space the window already had. The follow\n * command is part of the split itself — iTerm creates the new session already\n * running it — so no text is ever typed into any session afterwards; a separate\n * typing step raced the operator's keystrokes (focus briefly sits on the new\n * pane after a split, and in one observed failure the command landed in the\n * operator's own shell). A split still makes the new session the tab's active\n * one — the scripts re-select the launching session right after, so a pane\n * opening never steals the keystrokes the operator is typing. Each\n * pane runs `pai worker follow <id> --auto-exit <n>` under the `pai-worker`\n * dynamic profile (Close Sessions On End), so panes disappear by themselves.\n *\n * Panes are tracked per scope (AIBroker session id, else tab key) in\n * <logDir>/panes/<key>/<worker id>.json (one file per worker), iTerm session unique id, newest first —\n * the split lands below the lowest live worker pane.\n *\n * The dynamic profile's font is the family of iTerm's DEFAULT profile (the\n * `Default Bookmark Guid` entry in New Bookmarks) at workers.pane.fontSize —\n * never the launching session's profile, which the Python version did and\n * which produced iTerm's \"unknown parent name\" dialog whenever the two\n * differed. `Dynamic Profile Parent Name` is only written when that name\n * actually exists in New Bookmarks; when iTerm's preferences cannot be read\n * the profile is still written, with font Menlo-Regular and no parent.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { execFileSync } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n mkdtempSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { WorkersConfig } from \"./config.js\";\nimport { panesDir } from \"./paths.js\";\nimport { itermUuid, scopeKey } from \"./scope.js\";\n\nexport const PROFILE_NAME = \"pai-worker\";\n\n/** Where the dynamic profile lives (env override keeps tests off the real one). */\nexport function dynamicProfilePath(): string {\n return process.env.PAI_WORKER_PROFILE ?? join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"iTerm2\",\n \"DynamicProfiles\",\n \"pai-worker.json\"\n );\n}\n\n// ---------------------------------------------------------------------------\n// JXA scripts (arguments are passed as argv items, never interpolated).\n// JXA, not AppleScript: iTerm's AppleScript window collection can get stuck\n// failing every enumeration (\"every window doesn't understand the count\n// message\", -1708) while the JXA object model keeps answering.\n// ---------------------------------------------------------------------------\n\n// Shared prelude: locate the window/tab/session with a given unique id.\nconst FIND_SESSION = `function findSession(app, id) {\n var ws = app.windows();\n for (var i = 0; i < ws.length; i++) {\n var ts = ws[i].tabs();\n for (var j = 0; j < ts.length; j++) {\n var ss = ts[j].sessions();\n for (var k = 0; k < ss.length; k++) {\n if (ss[k].id() === id) return { w: ws[i], t: ts[j], s: ss[k], all: ss };\n }\n }\n }\n return null;\n}`;\n\n// One pane per worker: split the launching session vertically, or the lowest\n// live candidate horizontally. Returns \"<live ids>,|<new session id>\".\n// Exported for the script-content tests (window size, argv-only arguments).\nexport const WORKER_SPLIT_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var targetID = argv[0], candList = argv[1], followCmd = argv[2], profileName = argv[3];\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, targetID);\n if (!f) return \"notfound\";\n // sizing the new session would grow the whole window: pin the window's\n // bounds now and restore them after the split, so the panes share the\n // existing space\n var winBounds = f.w.bounds();\n var sessIDs = f.all.map(function (o) { return o.id(); });\n var lived = [];\n var splitS = null;\n (candList === \"\" ? [] : candList.split(\",\")).forEach(function (cid) {\n var at = sessIDs.indexOf(cid);\n if (at < 0) return;\n lived.push(cid);\n if (!splitS) splitS = f.all[at];\n });\n // the follow command is part of the split itself: the pane is born already\n // running it, so no text is ever typed into any session — a typing step\n // raced the operator's keystrokes and could even land in the operator's\n // own session\n var newS;\n if (!splitS) {\n newS = profileName === \"\"\n ? f.s.splitVerticallyWithDefaultProfile({ command: followCmd })\n : f.s.splitVertically({ withProfile: profileName, command: followCmd });\n } else {\n newS = profileName === \"\"\n ? splitS.splitHorizontallyWithDefaultProfile({ command: followCmd })\n : splitS.splitHorizontally({ withProfile: profileName, command: followCmd });\n }\n // a split makes the new session the tab's active one: re-select the\n // launching session so focus returns to where the operator was typing,\n // never the new pane\n try { f.s.select(); } catch (e) {}\n try { f.w.bounds = winBounds; } catch (e) {}\n return lived.map(function (l) { return l + \",\"; }).join(\"\") + \"|\" + newS.id();\n}`;\n\n// TTys of every session in the launching session's tab (no-worker pane variant),\n// comma-joined.\nconst TAB_TTYS_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n return f.all.map(function (o) { return o.tty(); }).join(\",\");\n}`;\n\n// Bounds (x1, y1, x2, y2, comma-joined) of the window hosting one iTerm\n// session — read-only, for `pai worker pane <id> --check`. Exported for the\n// script-content tests (reads bounds, never sets them).\nexport const WINDOW_BOUNDS_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n var b = f.w.bounds();\n return [b.x, b.y, b.x + b.width, b.y + b.height].join(\", \");\n}`;\n\n// Split the launching session vertically and run followCmd in the new pane.\n// Exported for the script-content tests (focus stays on the launching session).\nexport const SPLIT_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var followCmd = argv[1];\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n // the follow command is part of the split itself (see WORKER_SPLIT_SCRIPT):\n // nothing is ever typed into a session afterwards\n f.s.splitVerticallyWithDefaultProfile({ command: followCmd });\n // keep the tab's active session where it was (see WORKER_SPLIT_SCRIPT)\n try { f.s.select(); } catch (e) {}\n return \"opened\";\n}`;\n\n// ---------------------------------------------------------------------------\n// osascript / ps / defaults helpers\n// ---------------------------------------------------------------------------\n\n/** Run a JXA script with argv. Rejects when osascript itself cannot run. */\nfunction osascript(script: string, args: string[]): Promise<{ stdout: string; stderr: string }> {\n return new Promise((resolve, reject) => {\n const proc = spawn(\"osascript\", [\"-l\", \"JavaScript\", \"-\", ...args], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n let out = \"\";\n let err = \"\";\n proc.stdout.on(\"data\", (c: Buffer) => (out += c.toString(\"utf8\")));\n proc.stderr.on(\"data\", (c: Buffer) => (err += c.toString(\"utf8\")));\n proc.on(\"error\", reject);\n proc.on(\"close\", () => resolve({ stdout: out, stderr: err }));\n proc.stdin.write(script);\n proc.stdin.end();\n });\n}\n\nfunction psOutput(format: string): string {\n try {\n return execFileSync(\"ps\", [\"-axo\", format], { encoding: \"utf8\" });\n } catch {\n return \"\";\n }\n}\n\n/** TTys of running `worker follow` processes, normalised to /dev/ttysNNN. */\nfunction followTtys(): Set<string> {\n const ttys = new Set<string>();\n for (const line of psOutput(\"tty=,command=\").split(\"\\n\")) {\n const parts = line.trim().split(/\\s+/, 2);\n if (parts.length < 2 || parts[0] === \"ps\") continue;\n if (!/(^|\\/)(worker-follow|pai worker follow|glm-ps follow)\\b/.test(parts[1])) continue;\n if (parts[0].startsWith(\"ttys\")) ttys.add(`/dev/${parts[0]}`);\n }\n return ttys;\n}\n\n/** True while a `follow <wid>` process runs (its pane shows that worker). */\nexport function workerPaneOpen(wid: string): boolean {\n const pat = new RegExp(`(?:worker follow|worker-follow|glm-ps follow) ${wid.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b`);\n return psOutput(\"command=\").split(\"\\n\").some((ln) => pat.test(ln));\n}\n\n/** One entry of iTerm's New Bookmarks (the fields the pane cares about). */\nexport interface Bookmark {\n Name?: string;\n Guid?: string;\n \"Normal Font\"?: string;\n}\n\n/** What reading iTerm's preferences yielded — `error` carries the reason. */\nexport interface PrefsRead {\n bookmarks: Bookmark[];\n defaultGuid: string | null;\n error: string | null;\n}\n\n/** plutil's message for an ExecFileSync failure, else the exception text. */\nfunction toolErr(e: unknown): string {\n const err = e as { stderr?: string | Buffer; message?: string };\n const stderr = typeof err.stderr === \"string\" ? err.stderr : err.stderr?.toString(\"utf8\");\n return (stderr || err.message || String(e)).trim();\n}\n\n/**\n * Read New Bookmarks and Default Bookmark Guid from an iTerm preferences\n * plist. Key-scoped `plutil -extract`, never a whole-file JSON conversion:\n * real iTerm preferences carry `<date>` objects (SULastCheckTime & friends)\n * which `plutil -convert json` rejects outright — \"Invalid object in plist\n * for JSON format\" — so the whole-file read always failed and the dynamic\n * profile was never written.\n */\nexport function readItermPlist(plistPath: string): PrefsRead {\n let bookmarks: Bookmark[] = [];\n try {\n const out = execFileSync(\n \"plutil\",\n [\"-extract\", \"New Bookmarks\", \"json\", \"-o\", \"-\", plistPath],\n { encoding: \"utf8\", timeout: 10_000 }\n );\n const parsed = JSON.parse(out) as unknown;\n if (Array.isArray(parsed)) bookmarks = parsed as Bookmark[];\n } catch (e) {\n return { bookmarks: [], defaultGuid: null, error: `extracting New Bookmarks: ${toolErr(e)}` };\n }\n let defaultGuid: string | null = null;\n try {\n const out = execFileSync(\n \"plutil\",\n [\"-extract\", \"Default Bookmark Guid\", \"raw\", \"-o\", \"-\", plistPath],\n { encoding: \"utf8\", timeout: 10_000 }\n );\n defaultGuid = out.trim() || null;\n } catch {\n // no default guid set — defaultBookmarkFrom() yields null, fine\n }\n return { bookmarks, defaultGuid, error: null };\n}\n\n/** iTerm's exported preferences, via a temp plist (no shell pipe involved). */\nexport function itermPrefs(): PrefsRead {\n let dir: string | null = null;\n try {\n dir = mkdtempSync(join(tmpdir(), \"pai-iterm-\"));\n const plist = join(dir, \"iterm2.plist\");\n execFileSync(\"defaults\", [\"export\", \"com.googlecode.iterm2\", plist], {\n encoding: \"utf8\",\n timeout: 10_000,\n });\n return readItermPlist(plist);\n } catch (e) {\n return { bookmarks: [], defaultGuid: null, error: `defaults export: ${toolErr(e)}` };\n } finally {\n if (dir) {\n try {\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // best effort cleanup of a temp dir\n }\n }\n }\n}\n\n/** The New Bookmarks entry of that profile name, null if absent. */\nfunction bookmarkFrom(read: PrefsRead, named: string): Bookmark | null {\n return read.bookmarks.find((b) => b.Name === named) ?? null;\n}\n\n/** The bookmark iTerm marks default, null when it is missing or unreadable. */\nfunction defaultBookmarkFrom(read: PrefsRead): Bookmark | null {\n return read.defaultGuid\n ? read.bookmarks.find((b) => b.Guid === read.defaultGuid) ?? null\n : null;\n}\n\n/** The default profile, read fresh from iTerm's preferences. */\nexport function defaultBookmark(): Bookmark | null {\n return defaultBookmarkFrom(itermPrefs());\n}\n\n/**\n * The pane profile's font: the family of iTerm's default profile at\n * `fontSize` points (\"MesloLGLNFM-Regular 18\" + 13 → \"MesloLGLNFM-Regular 13\"),\n * or \"Menlo-Regular <fontSize>\" when the default font cannot be read.\n */\nexport function paneFont(font: string | undefined, fontSize: number): string {\n const idx = (font ?? \"\").lastIndexOf(\" \");\n if (idx > 0) {\n const family = font!.slice(0, idx);\n if (!Number.isNaN(parseFloat(font!.slice(idx + 1)))) return `${family} ${fontSize}`;\n }\n return `Menlo-Regular ${fontSize}`;\n}\n\n/** Write the pai-worker dynamic profile; iTerm2 loads that directory itself. */\nexport function writeDynamicProfile(\n parent: Bookmark | null,\n fontSize: number,\n read: () => PrefsRead = itermPrefs\n): void {\n const profile: Record<string, unknown> = {\n Name: PROFILE_NAME,\n Guid: \"pai-worker-dynamic-profile\",\n \"Normal Font\": paneFont(parent?.[\"Normal Font\"], fontSize),\n \"Close Sessions On End\": true,\n };\n // Parent name only when iTerm actually has that profile loaded — a name it\n // does not know makes every split open an error dialog instead of a pane.\n if (parent?.Name && bookmarkFrom(read(), parent.Name)) {\n profile[\"Dynamic Profile Parent Name\"] = parent.Name;\n }\n const path = dynamicProfilePath();\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify({ Profiles: [profile] }, null, 2) + \"\\n\", \"utf8\");\n renameSync(tmp, path);\n}\n\nlet warnedPrefs = false;\n\n/**\n * Profile for new worker panes, creating the small-font one on demand.\n * \"\" means \"split with the default profile\" (also the fallback when the\n * dynamic profile does not show up in time).\n *\n * The profile file is written whenever it is missing — even when iTerm's\n * preferences cannot be read; then it gets font \"Menlo-Regular <fontSize>\"\n * and no parent, and the reason is logged to stderr once.\n */\nexport async function followProfile(\n fontSize: number,\n read: () => PrefsRead = itermPrefs\n): Promise<string> {\n const first = read();\n const parent = defaultBookmarkFrom(first);\n const path = dynamicProfilePath();\n const existed = existsSync(path);\n if (!existed) writeDynamicProfile(parent, fontSize, () => first);\n if (!parent) {\n if (!warnedPrefs) {\n warnedPrefs = true;\n const reason = first.error ?? \"no profile is marked default\";\n const what = existed\n ? `keeping ${path} as-is`\n : `wrote ${path} with Menlo-Regular ${fontSize} and no parent`;\n process.stderr.write(\n `pai worker pane: cannot read iTerm's default profile (${reason}) — ${what}\\n`\n );\n }\n if (first.error) return \"\"; // polling cannot succeed against unreadable prefs\n }\n for (let i = 0; i < 10; i++) {\n // iTerm2 picks DynamicProfiles up quickly; allow it 2 s\n if (bookmarkFrom(read(), PROFILE_NAME)) return PROFILE_NAME;\n await new Promise((r) => setTimeout(r, 200));\n }\n process.stderr.write(`pai worker pane: profile ${PROFILE_NAME} not visible, splitting with default\\n`);\n return \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Pane registry\n// ---------------------------------------------------------------------------\n\nexport interface PaneEntry {\n session: string;\n worker: string;\n opened: string;\n /** the scope (registry key) the entry was opened under */\n scope?: string;\n}\n\n/**\n * One file per worker, `<panes>/<scope>/<worker id>.json`: concurrent opens\n * for the same scope write different files, so none can be lost (the former\n * shared `<scope>.json` list was a read-modify-write that dropped entries when\n * workers started a second apart). Legacy `<scope>.json` lists are still read.\n */\nfunction readEntry(path: string, scope: string): PaneEntry[] {\n try {\n const v = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n return (Array.isArray(v) ? v : [v])\n .filter((e): e is PaneEntry => !!e && typeof (e as PaneEntry).session === \"string\" && typeof (e as PaneEntry).worker === \"string\")\n .map((e) => ({ ...e, scope }));\n } catch {\n return [];\n }\n}\n\n/** Registry entries of one scope (`scope` set) or of all scopes, oldest first. */\nexport function loadRegistry(logDir: string, scope?: string): PaneEntry[] {\n const root = panesDir(logDir);\n const out: PaneEntry[] = [];\n let names: string[] = [];\n try {\n names = readdirSync(root);\n } catch {\n return out;\n }\n for (const name of names.sort()) {\n const full = join(root, name);\n if (name.endsWith(\".json\")) {\n const sc = name.slice(0, -5);\n if (!scope || sc === scope) out.push(...readEntry(full, sc));\n } else if (!scope || name === scope) {\n try {\n for (const f of readdirSync(full)) if (f.endsWith(\".json\")) out.push(...readEntry(join(full, f), name));\n } catch {\n /* not a directory */\n }\n }\n }\n return out.sort((x, y) => x.opened.localeCompare(y.opened));\n}\n\nfunction entryPath(logDir: string, e: PaneEntry): string {\n return join(panesDir(logDir), e.scope ?? \"\", `${e.worker}.json`);\n}\n\nfunction saveEntry(logDir: string, e: PaneEntry): void {\n const path = entryPath(logDir, e);\n mkdirSync(dirname(path), { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify({ session: e.session, worker: e.worker, opened: e.opened }, null, 1), \"utf8\");\n renameSync(tmp, path);\n}\n\n/** Forget an entry (its per-worker file; a legacy list is rewritten without it). */\nfunction dropEntry(logDir: string, e: PaneEntry): void {\n const legacy = join(panesDir(logDir), `${e.scope}.json`);\n rmSync(entryPath(logDir, e), { force: true });\n if (existsSync(legacy)) {\n const rest = readEntry(legacy, e.scope ?? \"\").filter((x) => x.session !== e.session);\n writeFileSync(legacy, JSON.stringify(rest.map(({ scope: _s, ...x }) => x), null, 1), \"utf8\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Ending panes of finished workers\n// ---------------------------------------------------------------------------\n\nconst LIST_SESSIONS_SCRIPT = `function run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"\";\n var out = \"\";\n app.windows().forEach(function (w) {\n w.tabs().forEach(function (t) {\n t.sessions().forEach(function (s) {\n out += s.id() + \"\\t\" + s.tty() + \"\\t\" + s.name() + \"\\n\";\n });\n });\n });\n return out;\n}`;\n\n// Ends one session by its unique id, never a window or the app.\nconst END_SESSION_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = findSession(app, argv[0]);\n if (!f) return \"notfound\";\n f.s.close();\n return \"closed\";\n}`;\n\n/** The real iTerm / tmux calls, swappable so tests never touch either. */\nexport interface PaneOps {\n listIterm(): Promise<Array<{ id: string; name: string }>>;\n closeIterm(id: string): Promise<boolean>;\n listTmux(): Array<{ id: string; command: string }>;\n closeTmux(id: string): boolean;\n}\n\nexport const realPaneOps: PaneOps = {\n async listIterm() {\n if (process.platform !== \"darwin\") return [];\n try {\n const p = await osascript(LIST_SESSIONS_SCRIPT, []);\n // iTerm renames a pane after its running command (\"node\"), so the follow\n // command is looked up by the session's tty instead\n const cmdByTty = new Map<string, string>();\n for (const line of psOutput(\"tty=,command=\").split(\"\\n\")) {\n const [tty, ...rest] = line.trim().split(/\\s+/);\n if (tty && tty !== \"??\") cmdByTty.set(`/dev/${tty}`, rest.join(\" \"));\n }\n return p.stdout\n .split(\"\\n\")\n .filter(Boolean)\n .map((l) => {\n const [id, tty, ...name] = l.split(\"\\t\");\n return { id, name: `${name.join(\"\\t\")} ${cmdByTty.get(tty) ?? \"\"}`.trim() };\n });\n } catch {\n return [];\n }\n },\n async closeIterm(id) {\n const p = await osascript(END_SESSION_SCRIPT, [id]);\n return p.stdout.trim() === \"closed\";\n },\n listTmux() {\n try {\n const out = execFileSync(\"tmux\", [\"list-panes\", \"-a\", \"-F\", \"#{pane_id}\\t#{pane_start_command}\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n return out.split(\"\\n\").filter(Boolean).map((l) => ({ id: l.split(\"\\t\")[0], command: l.split(\"\\t\").slice(1).join(\" \") }));\n } catch {\n return [];\n }\n },\n closeTmux(id) {\n try {\n execFileSync(\"tmux\", [\"kill-pane\", \"-t\", id], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n },\n};\n\nconst FOLLOW_ID = /worker[- ]follow (\\d{8}-\\d{6}-\\d+)/;\n\n/** End one worker's registered iTerm pane, found by worker id. True if a pane was closed. */\nexport async function closePaneForWorker(logDir: string, wid: string, ops: PaneOps = realPaneOps): Promise<boolean> {\n let closed = false;\n for (const e of loadRegistry(logDir).filter((x) => x.worker === wid)) {\n try {\n if (await ops.closeIterm(e.session)) closed = true;\n } catch {\n /* iTerm gone: the entry is stale either way */\n }\n dropEntry(logDir, e);\n }\n return closed;\n}\n\nexport interface CloseEndedResult {\n closed: string[];\n kept: string[];\n}\n\n/**\n * Close every open follow pane whose worker has ended: registry entries first\n * (by worker id), then any iTerm session named after / tmux pane started with a\n * `worker follow <id>` command of an ended worker. `ended(id)` says whether\n * that worker is over; unknown ids are never closed. `dryRun` only reports.\n */\nexport async function closeEndedPanes(\n logDir: string,\n ended: (wid: string) => boolean,\n dryRun: boolean,\n ops: PaneOps = realPaneOps\n): Promise<CloseEndedResult> {\n const res: CloseEndedResult = { closed: [], kept: [] };\n const seen = new Set<string>();\n const handle = async (backend: \"iterm\" | \"tmux\", id: string, worker: string, entry?: PaneEntry): Promise<void> => {\n const key = `${backend}:${id}`;\n if (seen.has(key)) return;\n seen.add(key);\n if (!ended(worker)) {\n res.kept.push(`${worker} ${key} (running)`);\n return;\n }\n if (!dryRun) {\n const ok = backend === \"tmux\" ? ops.closeTmux(id) : await ops.closeIterm(id).catch(() => false);\n if (entry) dropEntry(logDir, entry); // stale whether or not the pane still existed\n if (!ok && !entry) return;\n }\n res.closed.push(`${worker} ${key}${dryRun ? \" (dry run)\" : \"\"}`);\n };\n const sessions = await ops.listIterm();\n const open = new Set(sessions.map((s) => s.id));\n for (const e of loadRegistry(logDir)) {\n // iTerm answered and the session is gone: a stale entry, forgotten without a close\n if (sessions.length && !open.has(e.session)) {\n if (!dryRun) dropEntry(logDir, e);\n continue;\n }\n await handle(\"iterm\", e.session, e.worker, e);\n }\n for (const s of sessions) {\n const m = FOLLOW_ID.exec(s.name);\n if (m) await handle(\"iterm\", s.id, m[1]);\n }\n for (const t of ops.listTmux()) {\n const m = FOLLOW_ID.exec(t.command);\n if (m) await handle(\"tmux\", t.id, m[1]);\n }\n return res;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry points\n// ---------------------------------------------------------------------------\n\n/**\n * The command a worker pane runs: one exec-able line with no shell syntax —\n * iTerm's split command parameter execs without a shell (proven live: panes\n * survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on\n * `export …; …` or a redirect), so the command must be one exec-able line.\n * Absolute node on the CLI entry file also sidesteps the panes' bare launchd\n * PATH (no pai, no node) and the `#!/usr/bin/env node` shebang.\n */\nexport function followCommand(wid: string, autoExitSecs: number): string {\n return `${process.execPath} ${absoluteCliPath()} worker follow ${wid} --auto-exit ${autoExitSecs}`;\n}\n\n/** Absolute path of the running pai CLI; falls back to PATH lookup at spawn. */\nfunction absoluteCliPath(): string {\n const argv1 = process.argv[1] ?? \"\";\n if (argv1.endsWith(\"pai\")) return argv1;\n return \"pai\";\n}\n\nexport type PaneBackend = \"tmux\" | \"iterm\" | \"none\";\n\n/** The one place that decides where a follow pane opens: tmux (any OS) > iTerm2 (macOS) > nowhere. */\nexport function paneBackend(env: NodeJS.ProcessEnv = process.env, platform: string = process.platform): PaneBackend {\n if (env.TMUX) return \"tmux\";\n if (platform === \"darwin\" && env.ITERM_SESSION_ID) return \"iterm\";\n return \"none\";\n}\n\nexport function noPaneMessage(wid?: string): string {\n return `no pane support here, use: pai worker follow${wid ? ` ${wid}` : \"\"}`;\n}\n\n/** Split the current tmux window horizontally (right-hand column) and run cmd in it, focus stays put. */\nfunction tmuxSplit(cmd: string): void {\n const target = process.env.TMUX_PANE ? [\"-t\", process.env.TMUX_PANE] : [];\n try {\n execFileSync(\"tmux\", [\"split-window\", \"-h\", \"-d\", \"-l\", \"40%\", ...target, cmd], { stdio: \"pipe\" });\n } catch (e) {\n const err = e as { stderr?: Buffer; message?: string };\n throw new Error(`pai worker pane: tmux split-window failed: ${(err.stderr?.toString() || err.message || \"\").trim().slice(0, 200)}`);\n }\n}\n\n/** Outcome of opening a worker's follow pane: the report line plus, on a fresh open, the new iTerm session id. */\nexport interface PaneOpenResult {\n message: string;\n session: string | null;\n}\n\n/** Open (or only report on) the stacked follow pane of one worker. */\nexport async function openPaneForWorker(\n logDir: string,\n config: WorkersConfig,\n wid: string,\n term: string\n): Promise<PaneOpenResult> {\n if (workerPaneOpen(wid)) return { message: `pane for ${wid} already open`, session: null };\n const backend = paneBackend();\n if (backend === \"none\") return { message: noPaneMessage(wid), session: null };\n if (backend === \"tmux\") {\n tmuxSplit(followCommand(wid, config.pane.autoExitSecs));\n return { message: `pane opened for ${wid}`, session: null };\n }\n const uid = itermUuid(term);\n const scope = scopeKey(term);\n const reg = loadRegistry(logDir, scope);\n const profile = await followProfile(config.pane.fontSize);\n const cmd = followCommand(wid, config.pane.autoExitSecs);\n // registry newest first: the split lands below the lowest live worker pane\n const cands = [...reg].reverse().map((e) => e.session).join(\",\");\n const p = await osascript(WORKER_SPLIT_SCRIPT, [uid, cands, cmd, profile]);\n const out = p.stdout.trim();\n if (out === \"notfound\" || out === \"notrunning\") {\n throw new Error(\n out === \"notfound\"\n ? \"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\"\n : \"pai worker pane: iTerm2 is not running\"\n );\n }\n const bar = out.indexOf(\"|\");\n if (bar < 0) {\n throw new Error(\n `pai worker pane: osascript failed: ${(p.stderr || out).slice(0, 200)}`\n );\n }\n const live = new Set(out.slice(0, bar).split(\",\").filter(Boolean));\n for (const e of reg) if (!live.has(e.session)) dropEntry(logDir, e);\n saveEntry(logDir, {\n session: out.slice(bar + 1),\n worker: wid,\n opened: new Date().toISOString().replace(\"T\", \" \").slice(0, 19),\n scope,\n });\n return { message: `pane opened for ${wid}`, session: out.slice(bar + 1) };\n}\n\n/**\n * One `--check` line with the bounds of the window hosting `term`'s iTerm\n * session — the before/after pair that shows whether a split moved it.\n * Read-only; never touches the window.\n */\nasync function windowBoundsLine(term: string): Promise<string> {\n const uid = itermUuid(term);\n if (!uid) return \"window bounds: (not in iTerm2)\";\n try {\n const p = await osascript(WINDOW_BOUNDS_SCRIPT, [uid]);\n const out = p.stdout.trim();\n if (out && out !== \"notfound\" && out !== \"notrunning\") return `window bounds: ${out}`;\n const why =\n out === \"notfound\" ? \"iTerm2 session not found\"\n : out === \"notrunning\" ? \"iTerm2 not running\"\n : (p.stderr.trim() || \"no output\").slice(0, 120);\n return `window bounds: (${why})`;\n } catch (e) {\n return `window bounds: (osascript: ${String((e as Error).message ?? e).slice(0, 120)})`;\n }\n}\n\n/**\n * Report-only variant used by `pai worker pane <id> --check`: whether a pane\n * runs for the worker, the bounds of the window hosting the asking session,\n * plus the dynamic profile's path, its existence, and the font it contains\n * (or, when missing, would write).\n */\nexport async function checkPaneForWorker(wid: string, fontSize: number, term: string): Promise<string> {\n const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\n if (paneBackend() !== \"iterm\") return lines.join(\"\\n\");\n lines.push(await windowBoundsLine(term));\n const path = dynamicProfilePath();\n if (existsSync(path)) {\n let font = \"(unreadable)\";\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as {\n Profiles?: Array<{ \"Normal Font\"?: string }>;\n };\n font = parsed.Profiles?.[0]?.[\"Normal Font\"] ?? \"(none)\";\n } catch {\n // font stays \"(unreadable)\"\n }\n lines.push(`profile file: ${path} (exists)`);\n lines.push(`profile font: ${font}`);\n } else {\n const parent = defaultBookmark();\n lines.push(`profile file: ${path} (missing)`);\n lines.push(`profile font (would write): ${paneFont(parent?.[\"Normal Font\"], fontSize)}`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Split this iTerm tab and run `pai worker follow` in the new pane, unless\n * one already runs here (detected by TTY — iTerm overwrites session names\n * with the running command, so names cannot serve as idempotence).\n */\nexport async function openFollowPane(\n logDir: string,\n _config: WorkersConfig,\n term: string,\n checkOnly: boolean\n): Promise<string> {\n void logDir;\n const backend = paneBackend();\n if (backend === \"none\") return noPaneMessage();\n if (backend === \"tmux\") {\n if (checkOnly) return \"no follow pane\";\n tmuxSplit(`${process.execPath} ${absoluteCliPath()} worker follow`);\n return \"follow pane opened\";\n }\n const uid = itermUuid(term);\n const p = await osascript(TAB_TTYS_SCRIPT, [uid]);\n const out = p.stdout.trim();\n if (out === \"notfound\") throw new Error(\"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\");\n if (out === \"notrunning\") throw new Error(\"pai worker pane: iTerm2 is not running\");\n const tabTtys = new Set(out.split(\",\").map((t) => t.trim()).filter(Boolean));\n const overlap = [...tabTtys].filter((t) => followTtys().has(t));\n if (overlap.length > 0) return \"follow pane already open\";\n if (checkOnly) return \"no follow pane\";\n // one exec-able line, no shell syntax — see followCommand()\n const cmd = `${process.execPath} ${absoluteCliPath()} worker follow`;\n const s = await osascript(SPLIT_SCRIPT, [uid, cmd]);\n const sout = s.stdout.trim();\n if (sout === \"opened\") return \"follow pane opened\";\n if (sout === \"notfound\") throw new Error(\"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\");\n throw new Error(`pai worker pane: osascript failed: ${(s.stderr || sout).slice(0, 200)}`);\n}\n","/**\n * tree.ts — sub-workers: parent detection and the tree caps.\n *\n * A worker launched by another worker (the runner exports PAI_WORKER_ID in\n * every worker's environment) carries `parent` in its status, so the worker\n * forest is visible in `ps`, the status line and the ledger. Two caps keep\n * the tree from growing without bound, both under `workers.tree`:\n *\n * - maxDepth (default 2): how deep sub-workers may nest. A top-level worker\n * sits at depth 0; its children at 1; grandchildren at 2 — one level past\n * the cap refuses with a clear message.\n * - maxChildren (default 4): how many children of one parent may run at the\n * same time. Finished children do not count; a planner works through its\n * sub-tasks in waves of this size.\n *\n * Chain stages also carry `parent` (the chain id), but chains are not workers\n * and never get a status file — a parent without a status is not capped.\n */\n\nimport type { WorkersTreeConfig } from \"./config.js\";\nimport { isLive, loadStatus, loadStatuses, saveStatus, type WorkerStatus } from \"./status.js\";\n\n/** The env var the runner sets in every worker's environment. */\nexport const WORKER_ID_ENV = \"PAI_WORKER_ID\";\n\n/** The worker this process runs inside, when it runs inside one. */\nexport function parentFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n const id = env[WORKER_ID_ENV];\n return typeof id === \"string\" && id.trim() ? id.trim() : null;\n}\n\n/**\n * Depth of `id` in the worker forest: 0 for a top-level worker, 1 + the\n * parent's depth for a sub-worker. Parents without a status file (chain ids,\n * unknown ids) count as roots; a cycle reads as its own depth and is cut off\n * after the statuses it walked. `chatIsRoot` stops one level short of the\n * chat-pane tracker (origin \"chat\"): the status line treats the pane as the\n * bar itself, so the workers it spawned are its top level.\n */\nexport function workerDepth(\n statuses: WorkerStatus[],\n id: string,\n opts?: { chatIsRoot?: boolean }\n): number {\n const byId = new Map(statuses.map((s) => [s.id, s]));\n let depth = 0;\n let cur = byId.get(id);\n const seen = new Set<string>([id]);\n while (cur?.parent && !seen.has(cur.parent)) {\n seen.add(cur.parent);\n const next = byId.get(cur.parent);\n if (!next) break; // chain id or stale parent: a root, not a level\n if (!(opts?.chatIsRoot && next.origin === \"chat\")) depth += 1;\n cur = next;\n }\n return depth;\n}\n\n/** Children of `parent` that are still running, oldest first. */\nexport function runningChildren(statuses: WorkerStatus[], parent: string): WorkerStatus[] {\n return statuses.filter((s) => s.parent === parent && isLive(s));\n}\n\n/**\n * Would starting a child of `parent` stay within the caps? Throws with a\n * clear message when it would not; returns silently when `parent` has no\n * status file (a chain id or an unknown id — not a worker, not capped).\n */\nexport function assertChildAllowed(\n logDir: string,\n parent: string,\n caps: WorkersTreeConfig,\n statuses: WorkerStatus[] = loadStatuses(logDir)\n): void {\n if (!statuses.some((s) => s.id === parent)) return;\n const depth = workerDepth(statuses, parent);\n if (depth + 1 > caps.maxDepth) {\n throw new Error(\n `worker tree: ${parent} sits at depth ${depth} and ` +\n `workers.tree.maxDepth is ${caps.maxDepth} — it cannot start another level of sub-workers. ` +\n `Hand the task up instead: pai worker handoff '{\"kind\":\"proposal\",\"text\":\"…\"}'`\n );\n }\n const kids = runningChildren(statuses, parent);\n if (kids.length >= caps.maxChildren) {\n throw new Error(\n `worker tree: ${parent} already has ${kids.length} running sub-workers ` +\n `(${kids.map((k) => k.id).join(\", \")}) and workers.tree.maxChildren is ${caps.maxChildren} — ` +\n `wait for one to finish, or raise the cap in the workers config`\n );\n }\n}\n\n/**\n * The parent a launch should record: an explicit one (chain stage) wins, else\n * the worker this process runs inside. Returns null for top-level runs.\n */\nexport function launchParent(explicit: string | undefined, env: NodeJS.ProcessEnv = process.env): string | null {\n return explicit ?? parentFromEnv(env);\n}\n\n/** Does a status file exist for `id` (i.e. is it a worker rather than a chain)? */\nexport function isWorkerId(logDir: string, id: string): boolean {\n return loadStatus(logDir, id) !== null;\n}\n\n/**\n * A worker owns its children's results (the worker contract says so): if\n * `parent` exits leaving sub-workers unaccounted for, its own run must not\n * report success. Two shapes count, both read off `runningChildren` and the\n * raw statuses, never a second live-child lookup:\n *\n * - still live: left running untouched (killing another process's work\n * here would be its own bug) but named in the reason.\n * - marked \"running\" with a dead pid (the child never got to write its own\n * terminal state before its parent vanished from under it): finalised\n * here as failed, so `ps` stops showing a corpse as live.\n *\n * Returns the reason string for the parent's own report, or null when every\n * child it started is accounted for.\n */\nexport function checkOrphanedChildren(logDir: string, parent: string): string | null {\n const statuses = loadStatuses(logDir);\n const live = runningChildren(statuses, parent);\n const abandoned = statuses.filter(\n (s) => s.parent === parent && s.state === \"running\" && !isLive(s)\n );\n for (const child of abandoned) {\n saveStatus(logDir, { ...child, state: \"failed\", last: `parent ${parent} exited` });\n }\n const ids = [...live, ...abandoned].map((s) => s.id);\n if (!ids.length) return null;\n return (\n `exited with ${ids.length} sub-worker${ids.length === 1 ? \"\" : \"s\"} still running ` +\n `(${ids.join(\", \")}); they were not waited for — run children in the foreground or wait for them before finishing`\n );\n}\n","/**\n * operator.ts — talking to a running worker.\n *\n * Headless workers run with `--input-format stream-json` and their stdin held\n * open, so a run is a conversation: every line sent to the per-worker Unix\n * socket `<logDir>/<id>.sock` is forwarded to the child as a user message and\n * mirrored into the transcript as an `operator` event (rendered with a »\n * marker). Once the worker has finished its turn, stdin closes 2 s later\n * unless a new message arrives — after that, `say` refuses and `resume`\n * continues the same Claude session with the worker's context intact.\n */\n\nimport { createServer, connect, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadStatus, isLive } from \"./status.js\";\n\nexport function operatorSocketPath(logDir: string, id: string): string {\n return join(logDir, `${id}.sock`);\n}\n\n/**\n * The runner's side: listen on the worker socket, hand every received line to\n * `onLine`. It acknowledges with \"ok\" only when `onLine` returns without a\n * string; a returned string (or a throw) is the failure reason, sent back as\n * `err <reason>` so `say` never reports ok for a message nobody received. Returns the server (close it when the run ends; the socket file is\n * unlinked on close, best effort).\n */\nexport function createOperatorServer(\n logDir: string,\n id: string,\n onLine: (text: string) => unknown\n): import(\"node:net\").Server {\n const path = operatorSocketPath(logDir, id);\n try {\n if (existsSync(path)) unlinkSync(path);\n } catch {\n // a stale socket from a crashed run must not block the new one\n }\n const server = createServer((socket: Socket) => {\n let buf = \"\";\n socket.on(\"error\", () => {});\n socket.on(\"data\", async (chunk: Buffer) => {\n buf += chunk.toString(\"utf8\");\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, nl).replace(/\\r$/, \"\");\n buf = buf.slice(nl + 1);\n let failure: unknown;\n if (line.trim()) {\n try {\n failure = await onLine(line);\n } catch (e) {\n failure = (e as Error).message;\n }\n }\n if (!socket.destroyed) socket.write(typeof failure === \"string\" && failure ? `err ${failure.replace(/\\n/g, \" \")}\\n` : \"ok\\n\");\n }\n });\n });\n server.listen(path);\n server.on(\"close\", () => {\n try {\n if (existsSync(path)) unlinkSync(path);\n } catch {\n // already gone\n }\n });\n return server;\n}\n\n/**\n * `pai worker say <id> \"<text>\"`: forward one line to a running worker.\n * Resolves \"ok\" only once the worker acknowledged delivery; rejects with the reason otherwise.\n */\nexport function sayToWorker(logDir: string, id: string, text: string, timeoutMs = 4000): Promise<string> {\n const status = loadStatus(logDir, id);\n if (!status) {\n return Promise.reject(new Error(`no worker named \"${id}\"`));\n }\n if (!isLive(status)) {\n return Promise.reject(\n new Error(\n `worker ${id} is not running (state: ${status.state}) — ` +\n `continue it instead with: pai worker resume ${id} \"<text>\"`\n )\n );\n }\n const path = operatorSocketPath(logDir, id);\n if (!existsSync(path)) {\n return Promise.reject(\n new Error(`worker ${id} has no operator socket (${path}) — it may predate this PAI version`)\n );\n }\n return new Promise((resolve, reject) => {\n const sock = connect(path);\n const fail = (e: Error) => {\n sock.destroy();\n reject(new Error(`cannot talk to worker ${id}: ${e.message}`));\n };\n sock.setTimeout(timeoutMs, () => fail(new Error(\"timeout\")));\n sock.once(\"error\", (e: Error) => fail(e));\n sock.once(\"connect\", () => {\n sock.write(text.replace(/\\n/g, \" \") + \"\\n\");\n });\n sock.once(\"data\", (d: Buffer) => {\n sock.end();\n const reply = d.toString(\"utf8\").trim();\n if (reply === \"ok\") resolve(\"ok\");\n else reject(new Error(`worker ${id} did not take the message: ${reply.replace(/^err /, \"\") || \"no acknowledgement\"}`));\n });\n sock.once(\"close\", () => fail(new Error(\"connection closed before the worker acknowledged\")));\n });\n}\n","/**\n * handoff.ts — upward messages between workers.\n *\n * Coordination in the worker tree is upward only: a child appends a handoff\n * to `<logDir>/<parent>.inbox.jsonl` and (when the parent is a running\n * worker) the same text is said to it, so it lands in the parent's\n * conversation as `[handoff from <child>] …`. The parent's pane renders the\n * inbox as `◆` lines, `ps`/`worker_status` show an inbox count, and a child\n * that finishes delivers its structured report automatically as a\n * `kind: \"result\"` handoff. There is no downward or sideways path — telling\n * a worker something is the operator's job (`pai worker say`).\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { isLive, loadStatus } from \"./status.js\";\nimport { sayToWorker } from \"./operator.js\";\n\nexport const HANDOFF_KINDS = [\"proposal\", \"result\", \"question\", \"blocker\"] as const;\n\nexport type HandoffKind = (typeof HANDOFF_KINDS)[number];\n\nexport interface Handoff {\n /** Sending worker id. */\n from: string;\n /** Receiving worker id (the parent). */\n to: string;\n kind: HandoffKind;\n text: string;\n /** Structured payload (a report, a proposal's fields, …). */\n data?: Record<string, unknown>;\n /** ISO stamp, attached on append. */\n _ts?: string;\n}\n\n/** Where a worker's handoffs land: <logDir>/<id>.inbox.jsonl. */\nexport function inboxPath(logDir: string, id: string): string {\n return join(logDir, `${id}.inbox.jsonl`);\n}\n\nexport function isHandoffKind(v: unknown): v is HandoffKind {\n return typeof v === \"string\" && (HANDOFF_KINDS as readonly string[]).includes(v);\n}\n\n/**\n * Normalize a raw parsed value into a Handoff, or explain what is missing.\n * `from`/`to` may be preset by the caller (the CLI fills them from the\n * environment); everything else must be present and well-formed.\n */\nexport function parseHandoff(\n v: unknown,\n preset: Partial<Pick<Handoff, \"from\" | \"to\">> = {}\n): Handoff {\n if (typeof v !== \"object\" || v === null || Array.isArray(v)) {\n throw new Error(\"handoff payload must be a JSON object\");\n }\n const o = v as Record<string, unknown>;\n const from = typeof o.from === \"string\" && o.from ? o.from : preset.from;\n const to = typeof o.to === \"string\" && o.to ? o.to : preset.to;\n if (!from) throw new Error(`handoff needs \"from\" (the sending worker id)`);\n if (!to) throw new Error(`handoff needs \"to\" (the parent worker id)`);\n if (!isHandoffKind(o.kind)) {\n throw new Error(`handoff \"kind\" must be one of: ${HANDOFF_KINDS.join(\", \")}`);\n }\n const text = typeof o.text === \"string\" ? o.text : \"\";\n if (!text.trim()) throw new Error(`handoff needs a non-empty \"text\"`);\n const data =\n typeof o.data === \"object\" && o.data !== null && !Array.isArray(o.data)\n ? (o.data as Record<string, unknown>)\n : undefined;\n return { from, to, kind: o.kind, text, ...(data ? { data } : {}) };\n}\n\n/** Append one handoff to its recipient's inbox, stamped now. */\nexport function appendHandoff(logDir: string, h: Handoff, now: Date = new Date()): void {\n const path = inboxPath(logDir, h.to);\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n appendFileSync(path, JSON.stringify({ ...h, _ts: now.toISOString() }) + \"\\n\", \"utf8\");\n}\n\n/** Every handoff in a worker's inbox, oldest first; damaged lines skip. */\nexport function readInbox(logDir: string, id: string): Handoff[] {\n const path = inboxPath(logDir, id);\n if (!existsSync(path)) return [];\n const out: Handoff[] = [];\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n out.push(JSON.parse(line) as Handoff);\n } catch {\n // a half-written line is not worth a crash; the rest still reads\n }\n }\n return out;\n}\n\n/** How the say path prefixes a handoff so the parent model knows its source. */\nexport function handoffMessage(h: Pick<Handoff, \"from\" | \"kind\" | \"text\">): string {\n return `[handoff from ${h.from}] (${h.kind}) ${h.text.replace(/\\s+/g, \" \").trim()}`;\n}\n\n/** True for the exact shape of handoffMessage() — the runner marks the mirror. */\nexport function isHandoffMessage(text: string): boolean {\n return new RegExp(`^\\\\[handoff from \\\\S+\\\\] \\\\((${HANDOFF_KINDS.join(\"|\")})\\\\) `).test(text);\n}\n\n/** Test seam for deliverHandoff: the say path, overridable with a mock. */\nexport interface HandoffDeps {\n say?: (id: string, text: string) => Promise<string>;\n}\n\n/**\n * Deliver one handoff: append it to the parent's inbox, then — when the\n * parent is a running worker with a live operator socket — say it so it\n * enters the parent's conversation. The say is best effort: a parent that is\n * busy, finished or gone still has the inbox line, and nothing here may make\n * a finishing child's exit path fail.\n */\nexport async function deliverHandoff(\n logDir: string,\n h: Handoff,\n deps: HandoffDeps = {},\n now: Date = new Date()\n): Promise<Handoff> {\n appendHandoff(logDir, h, now);\n const say = deps.say ?? ((id: string, text: string) => sayToWorker(logDir, id, text));\n const parent = loadStatus(logDir, h.to);\n if (parent && isLive(parent)) {\n try {\n await say(h.to, handoffMessage(h));\n } catch {\n // the inbox line is the durable record; a failed say is not an error\n }\n }\n return h;\n}\n\n/**\n * Send a handoff from inside a worker (the `pai worker handoff` CLI and the\n * MCP tool both land here): the sender comes from PAI_WORKER_ID, the\n * recipient from its status file's `parent`. Rejects with a clear message\n * outside a worker or under a parentless worker.\n */\nexport async function handoffFromInside(\n logDir: string,\n env: NodeJS.ProcessEnv,\n payload: unknown,\n deps: HandoffDeps = {}\n): Promise<Handoff> {\n const mine = env.PAI_WORKER_ID;\n if (!mine) {\n throw new Error(\n \"not inside a worker — handoffs are sent by workers (PAI_WORKER_ID unset). \" +\n \"Talk to a running worker with: pai worker say <id> \\\"<text>\\\"\"\n );\n }\n const st = loadStatus(logDir, mine);\n if (!st) throw new Error(`no status for this worker (${mine}) — cannot find its parent`);\n if (!st.parent || !loadStatus(logDir, st.parent)) {\n throw new Error(\n `worker ${mine} has no worker parent to hand off to ` +\n `(parent: ${st.parent ?? \"(none)\"}) — handoffs go up the worker tree only`\n );\n }\n const h = parseHandoff(payload, { from: mine, to: st.parent });\n return deliverHandoff(logDir, h, deps);\n}\n","/**\n * wait-on.ts — bounded wait for a detached job, the worker alternative to a\n * long blocking foreground call (which starves operator messages and\n * supervision). Recipe: `nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid`,\n * then `pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` until exit 0.\n *\n * Exit codes: 0 pid gone, 3 still running at --max, 2 pid never existed or not ours.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\n\nexport const WAIT_DEFAULT_SECS = 110;\nexport const WAIT_CAP_SECS = 115;\nconst POLL_MS = 2000;\nconst HEARTBEAT_MS = 20_000;\n/** Slack between the first observation and a process start time (ps lstart has 1 s resolution). */\nconst REUSE_SLACK_MS = 2000;\n\nexport interface WaitOnOpts {\n pid: number;\n log?: string;\n maxSecs?: number;\n tail?: number;\n}\n\nexport interface WaitOnDeps {\n /** \"alive\" | \"gone\" | \"foreign\" (exists, not ours). */\n probe: (pid: number) => \"alive\" | \"gone\" | \"foreign\";\n /** Process start time in ms epoch, null when unknown. */\n startTime: (pid: number) => number | null;\n now: () => number;\n sleep: (ms: number) => Promise<void>;\n out: (line: string) => void;\n}\n\nexport const realDeps: WaitOnDeps = {\n probe(pid) {\n try {\n process.kill(pid, 0);\n return \"alive\";\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === \"EPERM\" ? \"foreign\" : \"gone\";\n }\n },\n startTime(pid) {\n try {\n const t = Date.parse(execFileSync(\"ps\", [\"-o\", \"lstart=\", \"-p\", String(pid)], { encoding: \"utf8\" }).trim());\n return Number.isFinite(t) ? t : null;\n } catch {\n return null;\n }\n },\n now: () => Date.now(),\n sleep: (ms) => new Promise((r) => setTimeout(r, ms)),\n out: (l) => console.log(l),\n};\n\nconst readLines = (file: string | undefined): string[] => {\n if (!file || !existsSync(file)) return [];\n const t = readFileSync(file, \"utf8\");\n return t === \"\" ? [] : t.replace(/\\n$/, \"\").split(\"\\n\");\n};\n\nfunction rcOf(pid: number, log?: string): string | null {\n for (const f of [log && `${log}.rc`, log && log.replace(/\\.[^./]+$/, \".rc\"), `/tmp/${pid}.rc`]) {\n if (f && existsSync(f)) return readFileSync(f, \"utf8\").trim();\n }\n return null;\n}\n\nexport async function waitOn(o: WaitOnOpts, d: WaitOnDeps = realDeps): Promise<number> {\n const maxMs = Math.min(o.maxSecs ?? WAIT_DEFAULT_SECS, WAIT_CAP_SECS) * 1000;\n const tail = o.tail ?? 20;\n const t0 = d.now();\n const baseLines = readLines(o.log).length;\n\n const finish = (): number => {\n const rc = rcOf(o.pid, o.log);\n d.out(`pid ${o.pid} gone${rc !== null ? `, exit code ${rc}` : \"\"}`);\n if (tail > 0) for (const l of readLines(o.log).slice(-tail)) d.out(l);\n return 0;\n };\n\n if (!Number.isInteger(o.pid) || o.pid <= 0) {\n d.out(`wait-on: invalid pid ${o.pid}`);\n return 2;\n }\n const first = d.probe(o.pid);\n if (first === \"foreign\") {\n d.out(`wait-on: pid ${o.pid} is not owned by this user`);\n return 2;\n }\n if (first === \"gone\") {\n // A job that finished between two wait-on calls leaves an rc or a log; neither means the pid never existed.\n if (rcOf(o.pid, o.log) !== null || (o.log && existsSync(o.log))) return finish();\n d.out(`wait-on: pid ${o.pid} does not exist`);\n return 2;\n }\n\n let nextBeat = t0 + HEARTBEAT_MS;\n while (d.now() - t0 < maxMs) {\n await d.sleep(Math.min(POLL_MS, Math.max(0, maxMs - (d.now() - t0))));\n const st = d.startTime(o.pid);\n if (d.probe(o.pid) !== \"alive\" || (st !== null && st > t0 + REUSE_SLACK_MS)) return finish();\n if (d.now() >= nextBeat) {\n d.out(`running ${Math.round((d.now() - t0) / 1000)}s, log +${readLines(o.log).length - baseLines} lines`);\n nextBeat += HEARTBEAT_MS;\n }\n }\n d.out(`still running after ${Math.round((d.now() - t0) / 1000)}s (pid ${o.pid}); call wait-on again`);\n if (tail > 0) for (const l of readLines(o.log).slice(-tail)) d.out(l);\n return 3;\n}\n\n/** The documented long-job recipe, quoted by the worker contract and every deny message. */\nexport const WAIT_RECIPE =\n \"`nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid`, then repeat \" +\n \"`pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` (each call returns within 2 min; do other work between calls) until it exits 0\";\n","/**\n * agentish.ts — Agentish v2 (AG2), the wire format a worker's final report\n * uses by default: a kind line plus one-letter k=v lines instead of JSON\n * prose. Spec and validator live in the `aibroker` CLI\n * (see /Users/i052341/Daten/Cloud/Development/ai/AIBroker/docs/agentish.md);\n * this module shells out to it when present and falls back to a frozen copy\n * of the spec text otherwise, so a worker still gets a contract when\n * `aibroker` is not installed on its box.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport type { WorkerReport } from \"./report.js\";\n\n/** Frozen copy of `aibroker agentish spec`'s first line, for when the CLI is absent. */\nexport const AG2_SPEC_FALLBACK =\n \"AG2. msg=kind line+k=v lines. kinds T R S Q A X. keys i id g goal o own n forbid d steps p proof u out l limits r res c changes t tests G gate I inst m images # nums w worst x next z note(<200ch). sep |. outcomes + - ~ ? !. @n=path declared once then reused; @n:12=file:line. tests as Name+ Name-. no prose, no articles, never restate, unknown=?. r=+ only if all t +.\";\n\n/** Frozen copy of the `y` (why) extension line. */\nexport const AG2_EXTENSIONS_FALLBACK = \"y why(≤600ch)\";\n\ninterface Ag2SpecResult {\n spec: string;\n extensions: string;\n source: \"aibroker\" | \"builtin\";\n}\n\nlet cachedSpec: Ag2SpecResult | null = null;\n\n/**\n * The AG2 spec + extensions lines: from `aibroker agentish spec` when the\n * CLI runs and its output looks right, else the frozen fallback. Memoised —\n * every caller in one process gets the same spec without re-spawning aibroker.\n */\nexport function ag2Spec(): Ag2SpecResult {\n if (cachedSpec) return cachedSpec;\n try {\n const out = execFileSync(\"aibroker\", [\"agentish\", \"spec\"], {\n timeout: 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n encoding: \"utf8\",\n });\n const lines = out.split(\"\\n\").filter((l) => l.trim());\n if (lines[0]?.startsWith(\"AG2.\")) {\n cachedSpec = { spec: lines[0], extensions: lines[1] ?? AG2_EXTENSIONS_FALLBACK, source: \"aibroker\" };\n return cachedSpec;\n }\n } catch {\n // aibroker missing, not runnable, or timed out — fall through\n }\n cachedSpec = { spec: AG2_SPEC_FALLBACK, extensions: AG2_EXTENSIONS_FALLBACK, source: \"builtin\" };\n return cachedSpec;\n}\n\n/** Only for tests: drop the memoised spec so the next ag2Spec() re-probes. */\nexport function resetAg2SpecCache(): void {\n cachedSpec = null;\n}\n\n/**\n * Strip what workers wrap around a report: one surrounding ``` fence (opening\n * line may carry a language tag) and a leading `[YYYY-MM-DD HH:MM]` stamp line.\n */\nexport function unwrapAg2(text: string): string {\n let t = (text ?? \"\").trim();\n const fenced = t.match(/^```[^\\n]*\\n([\\s\\S]*?)\\n?```$/);\n if (fenced) t = fenced[1].trim();\n return t.replace(/^\\[\\d{4}-\\d{2}-\\d{2}[^\\]\\n]*\\][ \\t]*\\n/, \"\").trim();\n}\n\nexport interface ParsedAg2 {\n kind: string;\n fields: Record<string, string>;\n symbols: Record<string, string>;\n}\n\n/**\n * Parse one AG2 message: first non-empty line is the kind (T R S Q A X,\n * optionally followed by trailing text — only the first character is the\n * kind), later lines are `key=value` or `@n=path` symbol declarations.\n * ```-fenced lines are ignored so a message pasted inside a code fence still\n * parses. Returns null when the first non-empty line is not a known kind.\n */\nexport function parseAg2Report(text: string): ParsedAg2 | null {\n const kinds = new Set([\"T\", \"R\", \"S\", \"Q\", \"A\", \"X\"]);\n const lines = unwrapAg2(text)\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith(\"```\"));\n if (!lines.length) return null;\n const kind = lines[0][0];\n if (!kinds.has(kind)) return null;\n\n const fields: Record<string, string> = {};\n const symbols: Record<string, string> = {};\n for (const line of lines.slice(1)) {\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n const key = line.slice(0, eq).trim();\n const value = line.slice(eq + 1).trim();\n if (key.startsWith(\"@\")) {\n symbols[key.slice(1)] = value;\n } else {\n fields[key] = value;\n }\n }\n return { kind, fields, symbols };\n}\n\n/** true iff the value ends with the AG2 pass outcome character. */\nfunction passed(entry: string): boolean {\n return entry.endsWith(\"+\");\n}\n\n/**\n * Map a parsed AG2 report onto the runner's WorkerReport shape, so the\n * viewer and `printResult` can render an AG2 `R` exactly like a JSON one.\n */\nexport function ag2ToWorkerReport(parsed: ParsedAg2): WorkerReport {\n const f = parsed.fields;\n const out: WorkerReport = { format: \"ag2\" };\n\n if (f.c) {\n out.changed = f.c\n .split(\"|\")\n .map((s) => s.trim())\n .filter(Boolean)\n .map((entry) => {\n const colon = entry.indexOf(\":\");\n const space = entry.indexOf(\" \");\n const cut = colon >= 0 && (space < 0 || colon < space) ? colon : space;\n if (cut < 0) return { path: entry, summary: \"\" };\n return { path: entry.slice(0, cut).trim(), summary: entry.slice(cut + 1).trim() };\n });\n }\n\n if (f.t) {\n const tokens = f.t.split(/\\s+/).filter(Boolean);\n const isGrammar = tokens.length > 0 && tokens.every((tok) => /^\\S+[+\\-~?!]$/.test(tok));\n out.checks = isGrammar\n ? tokens.map((entry) => ({\n name: entry.slice(0, -1),\n ok: passed(entry),\n detail: entry.slice(-1),\n }))\n : [{ name: \"tests\", detail: f.t }];\n }\n\n if (f.p) out.commands = f.p.split(\"|\").map((s) => s.trim()).filter(Boolean);\n\n const open: string[] = [];\n if (f.x) open.push(f.x);\n for (const [k, v] of Object.entries(f)) {\n if ((v.endsWith(\"!\") || v.endsWith(\"?\")) && k !== \"r\" && k !== \"res\") open.push(`${k}: ${v}`);\n }\n if (open.length) out.open = open;\n\n if (f.z) out.notes = f.z;\n const res = f.r ?? f.res;\n if (res === \"+\" || res === \"-\" || res === \"~\" || res === \"?\" || res === \"!\") out.result = res;\n if (f.y) out.why = f.y;\n\n return out;\n}\n\nexport interface Ag2ValidationResult {\n ok: boolean;\n errors: string[];\n validator: \"aibroker\" | \"none\";\n}\n\ninterface AibrokerCheckJson {\n ok?: boolean;\n errors?: Array<{ code?: string; message?: string; line?: number }>;\n}\n\n/**\n * Validate an AG2 message with `aibroker agentish check - --json`. Never\n * fails the caller: a missing/broken CLI reports ok:true, validator \"none\" —\n * a worker's run is never blocked on a local tooling gap, only its\n * `reportValid` marker goes unset.\n */\nexport function validateAg2(text: string): Ag2ValidationResult {\n try {\n const out = execFileSync(\"aibroker\", [\"agentish\", \"check\", \"-\", \"--json\"], {\n input: unwrapAg2(text),\n timeout: 5000,\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n encoding: \"utf8\",\n });\n const parsed = JSON.parse(out) as AibrokerCheckJson;\n const errors = (parsed.errors ?? []).map((e) => e.message ?? e.code ?? \"unknown error\");\n return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n } catch (e) {\n // execFileSync throws on non-zero exit too — stdout still carries the JSON\n const out = (e as { stdout?: Buffer | string }).stdout;\n if (out) {\n try {\n const parsed = JSON.parse(out.toString()) as AibrokerCheckJson;\n const errors = (parsed.errors ?? []).map((er) => er.message ?? er.code ?? \"unknown error\");\n return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n } catch {\n // fall through to \"no validator\" below\n }\n }\n return { ok: true, errors: [], validator: \"none\" };\n }\n}\n","/**\n * report.ts — the worker contract: terse, structured final reports.\n *\n * Headless workers run with an appended system prompt that fixes their output\n * shape: act, verify, then finish with ONE final message — AG2 (see\n * agentish.ts) by default, JSON as the pre-AG2 fallback. The runner parses it\n * out of the result; the viewer renders it as a compact block instead of a\n * wall of text.\n */\n\nimport { statSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { WAIT_RECIPE } from \"./wait-on.js\";\nimport type { Paint } from \"./render.js\";\nimport { ag2Spec, ag2ToWorkerReport, parseAg2Report } from \"./agentish.js\";\n\n/** The marker the runner puts on operator messages the worker must answer. */\nexport const OPERATOR_MARK = \"[operator]\";\n\n/** Classes that get the short contract — cheap, bounded, single-answer tasks. */\nconst SHORT_CONTRACT_CLASSES = new Set([\"spotcheck\", \"simple\"]);\n\n/**\n * The AG2 report block appended to a contract when format is \"ag2\" — field\n * set matches `aibroker agentish check`'s real R schema (verified 2026-09-20:\n * i/r/t/c are required, not optional as an early draft of this contract had\n * it; r=+ needs G=+ and non-empty p too, not just all-t-passing; R has no\n * `u`/out field at all — u is T-only — so the answer goes in z instead).\n */\nfunction ag2ReportBlock(): string[] {\n const spec = ag2Spec();\n return [\n spec.spec,\n spec.extensions,\n \"Final message: one AG2 `R` message only — no prose, no code fence, no markdown.\",\n \"R requires i (id), r (+ - ~ ? !), t (tests, Name+ Name-), c (path summary|path summary).\",\n \"r=+ requires G=+ (gate) and p non-empty (proof command), and every t entry +. z= the answer/note,\",\n \"one line, always present. x= what is left (omit if nothing). y= one line why when r is not +.\",\n \"Example:\",\n \"R\",\n \"i=count-audit-ts\",\n \"r=+\",\n \"G=+\",\n \"c=src/audit summary of files counted\",\n \"t=Count+\",\n \"p=wc -l src/audit/*.ts\",\n \"z=19 files, 2523 lines\",\n ];\n}\n\n/** The JSON report block appended to a contract when format is \"json\" (pre-AG2 fallback). */\nfunction jsonReportBlock(lead: string): string[] {\n return [\n lead,\n '{\"changed\":[{\"path\":\"…\",\"summary\":\"…\"}],\"commands\":[\"…\"],\"checks\":[{\"name\":\"…\",\"ok\":true,\"detail\":\"…\"}],\"open\":[\"…\"],\"notes\":\"one line\"}',\n ];\n}\n\n/** The two sentences that stop a worker from breaking on shell quoting — kept in both contracts. */\nfunction fileNotInlineBlock(): string[] {\n return [\n \"NEVER put a multi-line or quoted payload inline in a shell command — no heredocs (<<EOF), no long\",\n \"-p '…' specs for child workers, no inline JSON, no inline scripts. ALWAYS write it to a file with\",\n \"the Write tool first and pass the file: -p \\\"$(cat spec.txt)\\\", --file spec.txt, or < spec.txt.\",\n \"Inline quoting fails silently or dies with a parse error and burns a worker launch; there are no\",\n \"exceptions.\",\n \"Never inline code one-liners (python3 -c, node -e, ruby -e and friends) — the permission layer\",\n \"denies them (\\\"ztk: command denied by permission rules\\\"); write the script to a file with the\",\n \"Write tool and run it (python3 script.py), or use jq for JSON inspection.\",\n ];\n}\n\n/** The file-not-inline rule, condensed for the short contract's tight length budget. */\nfunction fileNotInlineBlockShort(): string[] {\n return [\n \"NEVER put a multi-line or quoted payload inline in a shell command (no heredocs, no inline\",\n \"scripts): write it to a file with the Write tool and pass it, e.g. -p \\\"$(cat spec.txt)\\\".\",\n \"Never inline code (python3 -c and friends) — write a script file and run it, or use jq.\",\n ];\n}\n\n/** spotcheck/simple: headless, bounded, no delegation language — under 1500 chars in ag2 format. */\nfunction shortContractPrompt(format: \"ag2\" | \"json\"): string {\n const lines = [\n \"You are a headless implementation worker, run non-interactively.\",\n \"This task is yours: answer the question or do the bounded task now. Run at most one command\",\n \"to confirm a result and never re-derive a number you already have. No child workers, no handoff.\",\n ...fileNotInlineBlockShort(),\n ];\n lines.push(\n ...(format === \"ag2\"\n ? ag2ReportBlock()\n : jsonReportBlock(\n \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n ))\n );\n return lines.join(\"\\n\");\n}\n\n/** Every other class (and no class): the full worker/orchestrator contract. */\nfunction fullContractPrompt(format: \"ag2\" | \"json\"): string {\n const lines = [\n \"You are a headless implementation worker, run non-interactively by an orchestrating session.\",\n \"You are the worker: this task is yours to finish in this process. Never hand your whole task to a\",\n \"single child worker and step back — that is delegation, not work. Child workers are for two things\",\n \"only: (1) PARALLELISING independent parts of your task, each part a self-contained spec; (2) CHEAPER\",\n \"bounded sub-tasks — probes, renders, test runs, lookups — run one tier down with --class spotcheck or\",\n \"--class simple. Always pin --provider anthropic on children.\",\n \"You own your children's results: run them in the foreground or poll until each has finished, read\",\n \"their result, verify it, and fold it into your own report. When your turn ends this run ends and any\",\n \"running child is killed, so never end with a child still running.\",\n \"Ending your turn ends this run. There is no later wake-up: never call ScheduleWakeup, never say you\",\n \"will pick something up later, never leave a background command as your final action. Finish, then\",\n \"report.\",\n \"No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.\",\n \"Never sleep-poll: no `sleep N` over 60 seconds, anywhere in a command — standalone, after `&&`/`||`/\",\n \"`;`/`|`, or inside a loop or subshell — and the sleeps of one command add up (`sleep 58; sleep 58` is\",\n \"116 s). Ending your turn ends this process, so a background job gets no later wake-up here:\",\n \"run_in_background is denied. Foreground Bash calls stay under 2 min (longer ones block operator messages and supervision and are denied);\",\n `run a long job detached: ${WAIT_RECIPE}.`,\n \"Kill only processes you started, by PID (`cmd & echo $! > /tmp/x.pid`, then `kill $(cat /tmp/x.pid)`); never `pkill`/`killall`/`kill $(pgrep ...)` — other workers' command lines contain your spec text.\",\n ...fileNotInlineBlock(),\n \"\",\n `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,\n \"run (the prompt of a resumed run is the operator's too). Answer it FIRST, in one or two plain lines —\",\n \"a question gets its answer, an instruction gets one line naming what will change — then continue the\",\n \"task you were on.\",\n \"\",\n \"Sub-workers: you may start your own workers with `pai worker run --class <class> -p '<prompt>'`\",\n \"(Bash tool). Your children run on their own provider and report back to you automatically when\",\n \"they finish. Send a handoff UP instead of doing work yourself when a cheaper provider would\",\n \"suffice, the task is out of your scope, or a decision is needed:\",\n \"`pai worker handoff '{\\\"kind\\\":\\\"proposal\\\",\\\"text\\\":\\\"…\\\",\\\"data\\\":{…}}'` (kinds: proposal,\",\n \"question, blocker; results are sent for you when you finish). Sibling workers are not a\",\n \"coordination path — there is no sideways channel; everything goes up to your parent.\",\n \"\",\n ];\n lines.push(\n ...(format === \"ag2\"\n ? ag2ReportBlock()\n : jsonReportBlock(\n \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n ))\n );\n if (format === \"json\") {\n lines.push(\n \"changed: files you touched (path + one-line summary). commands: the commands that verify the work.\",\n \"checks: each with ok true/false and the evidence in detail. open: what you could not finish, if anything.\",\n \"notes: one line, the headline a reviewer reads first.\"\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * The system prompt appended to every headless run, chosen by task class and\n * report format. `spotcheck`/`simple` get a short contract (no delegation\n * language, under 1200 chars in json format / 1500 in ag2); every other class (and no class) gets the\n * full contract. Both end with the AG2 report block unless `format` is\n * \"json\", the pre-AG2 fallback.\n */\nexport function workerContractPrompt(cls: string | undefined, format: \"ag2\" | \"json\"): string {\n return SHORT_CONTRACT_CLASSES.has(cls ?? \"\") ? shortContractPrompt(format) : fullContractPrompt(format);\n}\n\n/**\n * The line appended to a headless prompt's own text (never the system\n * prompt): models weight an instruction at the end of the user turn more\n * than one buried in a long system-prompt paragraph, and this is the\n * difference between a worker that returns a valid AG2/JSON report and one\n * that answers in prose despite the contract (measured 2026-09-20 on both\n * Haiku and Sonnet with the AG2 contract alone).\n */\nexport function promptTrailer(format: \"ag2\" | \"json\"): string {\n return format === \"ag2\"\n ? \"\\n\\nFinal message: one AG2 R message only, no prose (format in system prompt).\"\n : \"\\n\\nFinal message: a JSON object only, no prose before or after, no code fence (format in system prompt).\";\n}\n\n/** The exact text sent to resend a final message that failed AG2 validation. */\nexport const AG2_REASK_TEXT =\n \"Your final message was not a valid AG2 R message. Resend it now as a single AG2 R message only, \" +\n \"no prose, no code fence. Minimal example (one key=value per line, first line R):\\n\" +\n \"R\\ni=task-id\\nr=+\\nG=+\\nc=path summary\\nt=Name+\\np=proof command\\nz=the answer, one line\\n\" +\n \"(r is + - ~ ? !; r=+ needs G=+ and p; when r is not +, add y=why.)\";\n\n/** The full-contract AG2 prompt — the keepalive path's identical-construction baseline. */\nexport const WORKER_CONTRACT_PROMPT = fullContractPrompt(\"ag2\");\n\nexport interface WorkerReport {\n changed?: Array<{ path?: string; summary?: string }>;\n commands?: string[];\n checks?: Array<{ name?: string; ok?: boolean; detail?: string }>;\n open?: string[];\n notes?: string;\n /** AG2 `r` (res): the single outcome character, when the report was AG2. */\n result?: \"+\" | \"-\" | \"~\" | \"?\" | \"!\";\n /** AG2 `y` (why): one line explaining a non-\"+\" result. */\n why?: string;\n /** Which wire format the final message was parsed as. */\n format?: \"ag2\" | \"json\";\n}\n\n/**\n * Extract the report from a worker's final message: AG2 first (an `R`\n * message, see agentish.ts), then the JSON contract as a fallback for\n * workers still on the pre-AG2 shape. Accepts a bare JSON object, a\n * ```json fenced block, or an object embedded in surrounding text — only\n * objects that look like the contract count (at least one of changed /\n * checks / notes) — any other JSON falls through to null so the raw text is\n * kept as-is.\n */\nexport function parseWorkerReport(text: string): WorkerReport | null {\n const trimmed = (text ?? \"\").trim();\n if (!trimmed) return null;\n\n const ag2 = parseAg2Report(trimmed);\n if (ag2 && ag2.kind === \"R\") return ag2ToWorkerReport(ag2);\n\n const candidates: string[] = [];\n const fence = trimmed.match(/```(?:json)?\\s*(\\{[\\s\\S]*?\\})\\s*```/);\n if (fence) candidates.push(fence[1]);\n if (trimmed.startsWith(\"{\")) {\n // a bare object may still carry trailing punctuation/newlines\n candidates.push(trimmed);\n }\n // object embedded in prose: first \"{\" to the matching last \"}\"\n const first = trimmed.indexOf(\"{\");\n const last = trimmed.lastIndexOf(\"}\");\n if (first >= 0 && last > first) candidates.push(trimmed.slice(first, last + 1));\n for (const cand of candidates) {\n try {\n const v = JSON.parse(cand) as unknown;\n if (typeof v === \"object\" && v !== null && !Array.isArray(v) && looksLikeReport(v)) {\n return { ...(v as WorkerReport), format: \"json\" };\n }\n } catch {\n // try the next candidate\n }\n }\n return null;\n}\n\n/** The contract's fingerprint — guards against unrelated JSON in a reply. */\nfunction looksLikeReport(v: object): boolean {\n const o = v as WorkerReport;\n return Array.isArray(o.changed) || Array.isArray(o.checks) || typeof o.notes === \"string\";\n}\n\n/**\n * Render a parsed report as the compact block the viewer shows: changed paths\n * with summaries, checks with ✓/✗, open items, notes. Empty report → [].\n */\nexport function renderReport(c: Paint, prefix: string, r: WorkerReport, cwd = \"\"): string[] {\n const out: string[] = [];\n const has = (xs: unknown[] | undefined) => (xs ?? []).length > 0;\n if (has(r.changed)) {\n out.push(`${prefix}${c(\"bold\", \"changed\")}`);\n for (const ch of r.changed!) {\n out.push(`${prefix} ${relShort(ch.path ?? \"?\", cwd)} — ${shortText(ch.summary ?? \"\", 90)}`);\n }\n }\n if (has(r.checks)) {\n out.push(`${prefix}${c(\"bold\", \"checks\")}`);\n for (const ck of r.checks!) {\n const mark = ck.ok === false ? c(\"red\", \"✗\") : ck.ok === true ? c(\"green\", \"✓\") : c(\"dim\", \"·\");\n const detail = ck.detail ? c(\"dim\", ` · ${shortText(ck.detail, 80)}`) : \"\";\n out.push(`${prefix} ${mark} ${ck.name ?? \"?\"}${detail}`);\n }\n }\n if (has(r.commands)) {\n out.push(`${prefix}${c(\"bold\", \"commands\")}`);\n for (const cmd of r.commands!) out.push(`${prefix} ${c(\"dim\", shortText(cmd, 100))}`);\n }\n if (has(r.open)) {\n out.push(`${prefix}${c(\"bold\", \"open\")}`);\n for (const o of r.open!) out.push(`${prefix} ${c(\"yellow\", shortText(o, 100))}`);\n }\n if (r.notes) out.push(`${prefix}${c(\"bold\", \"notes\")} ${shortText(r.notes, 120)}`);\n return out;\n}\n\nexport interface ReportVerifyFailure {\n path: string;\n reason: \"missing\" | \"not-deleted\" | \"stale\";\n}\n\nexport interface ReportVerifyResult {\n ok: boolean;\n failures: ReportVerifyFailure[];\n}\n\n/** A summary that names its own deletion — the path is expected to be gone. */\nconst DELETED_RE = /\\b(delete|deleted|deleting|remove|removed|removing)\\b/i;\n\n/**\n * Check every path a report's `changed` list claims against the filesystem:\n * it must exist (mtime at or after `startMs`, the run's start) unless its\n * summary says it was deleted, in which case it must be absent. Mechanical\n * only — counts, row totals and other free-text claims are not checked (a\n * worker's R report is trusted as written; a 2026-09-23 report claimed \"181\n * rows saved\" while it had written 31, and only its listed paths are\n * something the filesystem can confirm or refute).\n */\nexport function verifyReportChanges(\n changed: Array<{ path?: string; summary?: string }> | undefined,\n cwd: string,\n startMs: number\n): ReportVerifyResult {\n const failures: ReportVerifyFailure[] = [];\n for (const ch of changed ?? []) {\n const raw = (ch.path ?? \"\").trim();\n if (!raw) continue;\n const abs = isAbsolute(raw) ? raw : resolve(cwd, raw);\n let mtimeMs: number | null;\n try {\n mtimeMs = statSync(abs).mtimeMs;\n } catch {\n mtimeMs = null;\n }\n const deleted = DELETED_RE.test(ch.summary ?? \"\");\n if (deleted) {\n if (mtimeMs !== null) failures.push({ path: raw, reason: \"not-deleted\" });\n } else if (mtimeMs === null) {\n failures.push({ path: raw, reason: \"missing\" });\n } else if (mtimeMs < startMs) {\n failures.push({ path: raw, reason: \"stale\" });\n }\n }\n return { ok: failures.length === 0, failures };\n}\n\n/** The one-line note appended to a report/ledger when verify fails. */\nexport function verifyFailNote(v: ReportVerifyResult): string {\n const paths = v.failures.map((f) => f.path).slice(0, 5).join(\", \");\n return `verify: ${v.failures.length} claimed change${v.failures.length === 1 ? \"\" : \"s\"} not confirmed: ${paths}`;\n}\n\n/** repo-relative display of a changed path when it lies under cwd. */\nfunction relShort(p: string, cwd: string): string {\n if (!cwd) return p;\n const r = relative(cwd, p);\n return r && !r.startsWith(\"..\") ? r : p;\n}\n","/**\n * deadline.ts — the time gate of a worker run: resolving --max-minutes /\n * --deadline / workers.defaultMaxMinutes into one absolute deadline, the\n * prompt line that tells the worker, the ps label, and the timer that stops\n * the child (SIGTERM, then SIGKILL after a grace period).\n */\n\nimport type { WorkerStatus } from \"./status.js\";\n\nexport interface RunLimit {\n /** Epoch ms the run is stopped at. */\n deadlineAt: number;\n /** Minutes from launch to the deadline. */\n minutes: number;\n}\n\n/** Local HH:MM → epoch ms: today, or tomorrow when already past. */\nexport function parseDeadline(hhmm: string, now: Date = new Date()): number {\n const m = /^(\\d{1,2}):(\\d{2})$/.exec(hhmm.trim());\n if (!m || Number(m[1]) > 23 || Number(m[2]) > 59) {\n throw new Error(`--deadline: expected HH:MM (local time, 00:00-23:59), got \"${hhmm}\"`);\n }\n const at = new Date(now);\n at.setHours(Number(m[1]), Number(m[2]), 0, 0);\n if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 1);\n return at.getTime();\n}\n\n/** The run's limit, or null when disabled (--max-minutes 0 / default 0). */\nexport function resolveLimit(\n flags: { maxMinutes?: number; deadline?: string },\n defaultMinutes: number,\n now: Date = new Date()\n): RunLimit | null {\n if (flags.maxMinutes !== undefined && flags.deadline !== undefined) {\n throw new Error(\"--max-minutes and --deadline are mutually exclusive\");\n }\n if (flags.deadline !== undefined) {\n const deadlineAt = parseDeadline(flags.deadline, now);\n return { deadlineAt, minutes: Math.max(1, Math.round((deadlineAt - now.getTime()) / 60_000)) };\n }\n const minutes = flags.maxMinutes ?? defaultMinutes;\n if (!Number.isFinite(minutes) || minutes < 0) throw new Error(`--max-minutes: expected a non-negative number, got ${minutes}`);\n return minutes === 0 ? null : { deadlineAt: now.getTime() + minutes * 60_000, minutes };\n}\n\nconst pad = (n: number): string => String(n).padStart(2, \"0\");\n\n/** The one line injected into the worker's system prompt. */\nexport function deadlinePromptLine(limit: RunLimit): string {\n const d = new Date(limit.deadlineAt);\n return (\n `Hard deadline ${pad(d.getHours())}:${pad(d.getMinutes())} (${limit.minutes} min). ` +\n \"Finish and report before it; at the deadline the run is stopped and whatever you have is salvaged.\"\n );\n}\n\n/** ps column: \"36s left\", \"12m left\", \"1h05m left\" for running; \"OVERDUE ...\" for overdue; \"timed out\" for stopped; null otherwise. */\nexport function deadlineLabel(\n s: Pick<WorkerStatus, \"state\" | \"deadlineAt\" | \"timedOut\">,\n now: number = Date.now()\n): string | null {\n if (s.timedOut) return \"timed out\";\n if (s.state !== \"running\" || !s.deadlineAt) return null;\n const secs = Math.abs(s.deadlineAt - now) / 1_000;\n let label: string;\n if (secs < 60) {\n label = `${Math.floor(secs)}s`;\n } else if (secs < 3600) {\n label = `${Math.floor(secs / 60)}m`;\n } else {\n const hours = Math.floor(secs / 3600);\n const mins = Math.floor((secs % 3600) / 60);\n label = `${hours}h${String(mins).padStart(2, \"0\")}m`;\n }\n return s.deadlineAt >= now ? `${label} left` : `OVERDUE ${label}`;\n}\n\n/** Grace between SIGTERM and SIGKILL. */\nexport const DEADLINE_GRACE_MS = 10_000;\n\n/**\n * Stop `proc` at the deadline: `onFire`, SIGTERM, SIGKILL after `graceMs`\n * when it is still alive. Returns the disarm function.\n */\nexport function armDeadline(\n proc: { kill(sig?: NodeJS.Signals): boolean; exitCode: number | null; signalCode: NodeJS.Signals | null },\n limit: RunLimit,\n onFire: () => void,\n graceMs: number = DEADLINE_GRACE_MS\n): () => void {\n let kill: NodeJS.Timeout | null = null;\n const t = setTimeout(() => {\n onFire();\n try {\n proc.kill(\"SIGTERM\");\n } catch {\n /* already gone */\n }\n kill = setTimeout(() => {\n if (proc.exitCode === null && proc.signalCode === null) {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n }\n }, graceMs);\n kill.unref();\n }, Math.max(0, limit.deadlineAt - Date.now()));\n return () => {\n clearTimeout(t);\n if (kill) clearTimeout(kill);\n };\n}\n","/**\n * browser-tools.ts — what `--no-browser` strips: --chrome, every browser MCP\n * tool grant (mcp__claude-in-chrome__*, browsr, playwright, …) and browser\n * server names, from the caller's claude args. The PreToolUse worker guard\n * denies the same tools (hooks/ts/lib/worker-guard.ts) when\n * PAI_WORKER_NO_BROWSER=1.\n */\n\nconst BROWSER_SERVER = /chrome|browsr|browser|playwright|puppeteer/i;\n\n/** A browser MCP server name (bare, e.g. \"browsr\") or tool name (mcp__browsr__open). */\nexport function isBrowserTool(name: string): boolean {\n const server = /^mcp__(.+?)(__|$)/.exec(name)?.[1] ?? name;\n return BROWSER_SERVER.test(server) && (name.startsWith(\"mcp__\") || !name.includes(\"__\"));\n}\n\nconst keepNonBrowser = (list: string): string[] =>\n list.split(\",\").map((s) => s.trim()).filter((s) => s && !isBrowserTool(s));\n\n/** claude args without --chrome and browser tool grants (an emptied --allowedTools goes too). */\nexport function stripBrowserArgs(rest: string[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i];\n if (a === \"--chrome\") continue;\n if (a === \"--allowedTools\" && i + 1 < rest.length && !rest[i + 1].startsWith(\"-\")) {\n const kept = keepNonBrowser(rest[++i]);\n if (kept.length) out.push(a, kept.join(\",\"));\n } else if (a.startsWith(\"--allowedTools=\")) {\n const kept = keepNonBrowser(a.slice(\"--allowedTools=\".length));\n if (kept.length) out.push(`--allowedTools=${kept.join(\",\")}`);\n } else out.push(a);\n }\n return out;\n}\n\n/** Comma-list entries (--mcp values, class MCP) without browser servers. */\nexport function stripBrowserNames(names: string[]): string[] {\n return names.map((n) => keepNonBrowser(n).join(\",\")).filter(Boolean);\n}\n","/**\n * codex.ts — the \"codex\" runner engine.\n *\n * A ChatGPT plan gives no API key, only Codex CLI access, so a provider may\n * set `engine: \"codex\"`: `run` then shells out to `codex exec --json <prompt>`\n * (non-interactive) instead of Claude Code. The JSONL event stream is parsed\n * into the same status-file fields (turns, tools, last) and ledger lines as a\n * Claude run, and is normalised into claude-code-shaped events in the\n * worker's .jsonl so follow/replay/the pane render it unchanged.\n *\n * Implemented against the documented `codex exec --json` interface\n * (thread.started / item.completed / turn.completed / turn.failed lines);\n * verify against the installed CLI when one is present.\n * `--allowedTools` has no Codex equivalent and is dropped (ledgered).\n */\n\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { providerKeyPath, resolveProviderKey, type WorkerProvider } from \"./config.js\";\n\n/** Build the codex exec argument vector (without the binary itself). */\nexport function buildCodexArgs(prompt: string, model: string | undefined): string[] {\n return [\n \"exec\",\n \"--json\",\n \"--skip-git-repo-check\",\n ...(model ? [\"-m\", model] : []),\n \"--\",\n prompt,\n ];\n}\n\n/** Env for a codex run: caller's env, Anthropic vars stripped, key applied. */\nexport function buildCodexEnv(provider: WorkerProvider): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n delete env.ANTHROPIC_API_KEY;\n delete env.ANTHROPIC_BASE_URL;\n delete env.ANTHROPIC_AUTH_TOKEN;\n delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;\n delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;\n delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;\n // API-key providers: OpenAI env from key/keyFile/baseUrl; ChatGPT-login\n // codex (neither set) keeps its own auth from ~/.codex. A key_file that\n // does not exist is tolerated here (unlike resolveProviderKey's other\n // callers) — that is the ChatGPT-login shape, not a broken config.\n const keyPath = providerKeyPath(provider);\n if (provider.key || (keyPath && existsSync(keyPath))) {\n env.OPENAI_API_KEY = resolveProviderKey(provider) ?? \"\";\n }\n if (provider.upstreamUrl) env.OPENAI_BASE_URL = provider.upstreamUrl;\n for (const [k, v] of Object.entries(provider.env)) env[k] = v;\n env.PAI_WORKER = \"1\";\n return env;\n}\n\n/** Codex \"not applicable\" flags the runner drops with a ledger note. */\nexport function codexDroppedFlags(argv: string[]): string[] {\n const dropped: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a === \"--allowedTools\" || a === \"--disallowedTools\" || a === \"--mcp-config\" || a === \"--mcp\") {\n dropped.push(a);\n if (i + 1 < argv.length && !argv[i + 1].startsWith(\"-\")) i++; // and its value\n } else if (a.startsWith(\"--allowedTools=\") || a.startsWith(\"--mcp-config=\")) {\n dropped.push(a.split(\"=\")[0]);\n }\n }\n return dropped;\n}\n\n// ---------------------------------------------------------------------------\n// Event stream parsing (pure — tested with recorded JSONL lines)\n// ---------------------------------------------------------------------------\n\n/** One normalised claude-code-shaped event + its effect on the status. */\nexport interface CodexEventResult {\n events: Array<Record<string, unknown>>;\n turns: number;\n tools: number;\n last: string | null;\n isError: boolean;\n contextTokens: number | null;\n finalText: string | null;\n threadId: string | null;\n}\n\nexport const emptyCodexResult = (): CodexEventResult => ({\n events: [],\n turns: 0,\n tools: 0,\n last: null,\n isError: false,\n contextTokens: null,\n finalText: null,\n threadId: null,\n});\n\n/**\n * Fold one parsed `codex exec --json` line into the running result: appends\n * normalised transcript events and updates the counters in place.\n */\nexport function foldCodexLine(line: unknown, r: CodexEventResult): void {\n if (typeof line !== \"object\" || line === null) return;\n const e = line as Record<string, unknown>;\n const type = typeof e.type === \"string\" ? e.type : \"\";\n\n if (type === \"thread.started\" && typeof e.thread_id === \"string\") {\n r.threadId = e.thread_id;\n return;\n }\n if (type === \"item.completed\" && typeof e.item === \"object\" && e.item !== null) {\n const item = e.item as Record<string, unknown>;\n const kind = typeof item.type === \"string\" ? item.type : \"\";\n if (kind === \"agent_message\" && typeof item.text === \"string\") {\n r.turns += 1;\n r.last = `says: ${item.text.slice(0, 70)}`;\n r.finalText = item.text;\n r.events.push({\n type: \"assistant\",\n message: { content: [{ type: \"text\", text: item.text }] },\n });\n } else if (kind === \"command_execution\") {\n r.tools += 1;\n const cmd = typeof item.command === \"string\" ? item.command : \"?\";\n const rc = typeof item.exit_code === \"number\" ? item.exit_code : 0;\n r.last = `Bash: ${cmd.slice(0, 70)}`;\n r.events.push({\n type: \"assistant\",\n message: { content: [{ type: \"tool_use\", name: \"Bash\", input: { command: cmd } }] },\n });\n r.events.push({\n type: \"user\",\n message: {\n content: [\n {\n type: \"tool_result\",\n tool_use_id: \"\",\n is_error: rc !== 0,\n content: item.aggregated_output ?? \"\",\n },\n ],\n },\n });\n } else if (kind === \"file_change\") {\n r.tools += 1;\n const changes = Array.isArray(item.changes) ? item.changes : [];\n const files = changes\n .map((c) => (typeof c === \"object\" && c !== null && typeof (c as { path?: unknown }).path === \"string\" ? (c as { path: string }).path : \"?\"))\n .join(\", \");\n r.last = `files: ${files.slice(0, 70)}`;\n r.events.push({\n type: \"assistant\",\n message: { content: [{ type: \"tool_use\", name: \"Write\", input: { file_path: files } }] },\n });\n } else if (kind === \"mcp_tool_call\") {\n r.tools += 1;\n r.last = `mcp: ${String(item.tool ?? \"?\")}`;\n }\n return;\n }\n if (type === \"turn.completed\") {\n const usage = (typeof e.usage === \"object\" && e.usage !== null ? e.usage : {}) as {\n input_tokens?: number;\n output_tokens?: number;\n cached_input_tokens?: number;\n };\n const tokens =\n (usage.input_tokens ?? 0) + (usage.cached_input_tokens ?? 0) + (usage.output_tokens ?? 0);\n if (tokens > 0) r.contextTokens = tokens;\n return;\n }\n if (type === \"turn.failed\" || type === \"error\") {\n r.isError = true;\n const err = typeof e.error === \"object\" && e.error !== null ? e.error : {};\n r.last = String((err as { message?: unknown }).message ?? e.message ?? \"codex turn failed\");\n }\n}\n\n/** Parse one JSONL line; null for blanks and non-JSON noise. */\nexport function parseCodexLine(line: string): unknown | null {\n const t = line.trim();\n if (!t.startsWith(\"{\")) return null;\n try {\n return JSON.parse(t) as unknown;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// The engine probe used by `providers test`\n// ---------------------------------------------------------------------------\n\n/** True when the Codex CLI is on PATH (test reports \"not installed\" else). */\nexport function codexInstalled(): boolean {\n try {\n execFileSync(\"codex\", [\"--version\"], { timeout: 5000, stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Spawn helper shared by run and test; caller wires stdout. */\nexport function spawnCodex(args: string[], env: NodeJS.ProcessEnv, cwd: string) {\n return spawn(\"codex\", args, { env, cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n}\n","/**\n * run.ts — the worker runner (port of the glm / glm-run pair, provider-neutral).\n *\n * One claude-code process per call, pointed at the chosen provider:\n *\n * - env: ANTHROPIC_BASE_URL/AUTH_TOKEN from the provider (token from its\n * key file, never from the environment), the three DEFAULT_*_MODEL vars,\n * the provider's extra env, nonessential traffic off, and ANTHROPIC_API_KEY\n * stripped so nothing can fall back to Anthropic billing. Headless runs\n * also drop the spawner's session identity (messaging socket, session id,\n * nesting markers) — inherited, a worktree child starts tool-blind.\n * OpenAI-protocol providers point at the PAI proxy instead (started on\n * demand, the provider name in the URL path); codex-engine providers run\n * the Codex CLI.\n * - headless (-p): strict empty MCP config unless the caller brings one or\n * names servers via --mcp / a role (then a filtered <id>.mcp.json), the\n * core tool grants the caller did not bring (a headless run cannot\n * approve a permission-gated tool mid-flight — with no grant at all,\n * claude drops the file/shell tools entirely and the worker is\n * tool-blind), PAI_WORKER=1 so PAI's per-session hooks leave it alone,\n * the worker\n * contract appended to the system prompt, `--input-format stream-json`\n * with the prompt as the first stdin user message (the operator socket\n * can add more mid-run), stream-json mirroring (every line stamped `_ts`)\n * into <logDir>/<id>.jsonl, a live <id>.status for ps/follow/status line\n * (context meter included), ledger lines, result printed in the caller's\n * --output-format (json adds the parsed `report`), and the follow pane\n * (unless --no-pane).\n * - interactive: no MCP/tool restriction by default, no pane,\n * ENABLE_TOOL_SEARCH=true. A project that pinned a list with `pai\n * project mcp` / `pai project tools` gets `--strict-mcp-config\n * --mcp-config <filtered>` / `--tools <list>` built the same way a\n * headless run's allowlist is; an explicit --mcp or --tools on this\n * command line still wins over the project's pin.\n *\n * Auto-routed runs that die of a quota error before the first tool call are\n * restarted on the next provider in routing order (WORKER-REROUTE ledger line).\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport {\n existsSync,\n mkdirSync,\n openSync,\n writeFileSync,\n closeSync,\n writeSync,\n} from \"node:fs\";\nimport { appendSystemPromptArgs, parseRunnerArgs, shortText, stripPromptValues } from \"./args.js\";\nimport {\n assertProviderRunnable,\n classModelCapability,\n isModelCapability,\n NATIVE_ANTHROPIC_MODELS,\n readWorkersSection,\n resolveModelCapability,\n type WorkerProvider,\n} from \"./config.js\";\nimport { buildRunEnv, claudeCommand } from \"./run-env.js\";\n\nexport { buildRunEnv } from \"./run-env.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { decideEndReason, endFields, parentInfo, type EndInfo } from \"./endreason.js\";\nimport {\n ensureNoMcpConfig,\n eventsPath,\n ledgerPath,\n noMcpConfigPath,\n workersLogDir,\n} from \"./paths.js\";\nimport {\n type WorkerStatus,\n newWorkerId,\n saveStatus,\n describeTool,\n nowStamp,\n parseSleepSecs,\n UNLABELED,\n} from \"./status.js\";\nimport { resolveSession, resolveSpawnerSession } from \"./scope.js\";\nimport { returnControlsIfHeld } from \"./controls.js\";\nimport {\n capabilityForRun,\n isQuotaFailure,\n nextAutoProvider,\n resolveCapabilityRun,\n resolveTarget,\n setCooldown,\n type CapabilityRunResolution,\n} from \"./routing.js\";\nimport { runImageCapability } from \"./engines/image.js\";\nimport { openPaneForWorker, paneBackend } from \"./pane.js\";\nimport { assertChildAllowed, checkOrphanedChildren, isWorkerId, launchParent } from \"./tree.js\";\nimport { deliverHandoff, isHandoffMessage } from \"./handoff.js\";\nimport {\n addWorktree,\n salvageOnExit,\n gcWorktreesThrottled,\n inPlaceSystemPrompt,\n recordWorktree,\n worktreeSystemPrompt,\n worktreeWanted,\n type WorktreeInfo,\n} from \"./worktree.js\";\nimport {\n OPERATOR_MARK,\n AG2_REASK_TEXT,\n promptTrailer,\n workerContractPrompt,\n parseWorkerReport,\n verifyReportChanges,\n verifyFailNote,\n type WorkerReport,\n} from \"./report.js\";\nimport { validateAg2 } from \"./agentish.js\";\nimport { armDeadline, deadlinePromptLine, resolveLimit, type RunLimit } from \"./deadline.js\";\nimport { stripBrowserArgs, stripBrowserNames } from \"./browser-tools.js\";\nimport { expandMcpNames, grantsChrome, mcpServersFromToolGrants, writeMcpConfig } from \"./mcp.js\";\nimport { pinNotices, projectLaunchConfig, type ProjectLaunchConfig } from \"./project-config.js\";\nimport { createOperatorServer } from \"./operator.js\";\nimport { DEFAULT_PROXY_PORT, ensureProxyRunning } from \"./proxy/server.js\";\nimport {\n buildCodexArgs,\n buildCodexEnv,\n codexDroppedFlags,\n codexInstalled,\n emptyCodexResult,\n foldCodexLine,\n parseCodexLine,\n} from \"./codex.js\";\n\nexport interface RunOptions {\n providerFlag?: string;\n /** --class value (the old --role): a task class from workers.classes. */\n className?: string;\n modelFlag?: string;\n label?: string;\n noPane?: boolean;\n /** --spec value, resolved for display/recording (\"-\" for stdin). */\n specPath?: string;\n /** --mcp value: server/set names, comma-separated. */\n mcpFlag?: string;\n /** Everything after `--` (the claude args). */\n claudeArgs: string[];\n /** Working directory for the run (default: this process's cwd). */\n cwd?: string;\n /** Chain stage bookkeeping: the chain id this stage belongs to. */\n parent?: string;\n /** Chain stage bookkeeping: the class name of this stage. */\n stage?: string;\n /** Suppress result printing (MCP worker_run: its stdout is the RPC channel). */\n quiet?: boolean;\n /** Internal: notified with the worker id once it exists (resume uses it). */\n onWorkerStart?: (wid: string) => void;\n /** Internal: preset worker id (the planner mints its id before phase 1). */\n id?: string;\n /** --worktree/--no-worktree; undefined lets the class default decide. */\n worktreeFlag?: boolean;\n /** --max-minutes: stop the run after N minutes (0 = no limit; default workers.defaultMaxMinutes). */\n maxMinutes?: number;\n /** --deadline HH:MM (local): stop the run then; exclusive with maxMinutes. */\n deadline?: string;\n /** --browser / --no-browser: undefined follows workers.noBrowserByDefault. */\n browser?: boolean;\n /** --print-cmd: print the assembled claude argv as JSON and exit, no spawn. */\n printCmd?: boolean;\n /** Internal: suppress recursion depth on reroute. */\n _reroutes?: number;\n /** Internal: this run is the planner's phase-1 worker, not a new orchestration. */\n _planner?: boolean;\n /** --report json|ag2: which contract/parser the run uses (default: ag2, or PAI_WORKER_REPORT). */\n reportFormatFlag?: \"json\" | \"ag2\";\n /** --no-report-retry: skip the one bounded re-ask on an invalid AG2 final message. */\n noReportRetry?: boolean;\n /** --capability/--for: run through resolveCapability instead of resolveTarget. */\n capabilityFlag?: string;\n /** --out: image-engine output path (default: <logDir>/<id>.png). */\n imageOut?: string;\n /** --size: image-engine \"WIDTHxHEIGHT\" (default: 1024x1024). */\n imageSize?: string;\n /** --timeout-ms: image-engine request timeout (default: 120000). */\n imageTimeoutMs?: number;\n}\n\n/**\n * ag2 unless the caller passed --report json or PAI_WORKER_REPORT=json is\n * set in the environment; an explicit flag always wins over the env default.\n */\nexport function resolveReportFormat(\n flag: \"json\" | \"ag2\" | undefined,\n env: NodeJS.ProcessEnv = process.env\n): \"json\" | \"ag2\" {\n if (flag) return flag;\n return env.PAI_WORKER_REPORT === \"json\" ? \"json\" : \"ag2\";\n}\n\n/** The strict empty MCP config for headless workers, written on demand. */\nexport { ensureNoMcpConfig } from \"./paths.js\";\n\n/** A stream-json user message for the child's stdin. */\nexport function stdinUserMessage(text: string): string {\n return JSON.stringify({ type: \"user\", message: { role: \"user\", content: text } });\n}\n\n/** An operator line as the worker sees it: carrying the contract's marker. */\nexport function operatorUserText(text: string): string {\n return `${OPERATOR_MARK} ${text}`;\n}\n\n/**\n * ISO stamp with seconds, attached to every mirrored event (2g): local time\n * with its offset (`2026-09-17T14:19:23+02:00`), so the viewer can render the\n * wall clock the operator lives in. `offMin` is east-positive minutes —\n * injectable so tests do not depend on the machine's zone.\n */\nexport function isoStamp(d = new Date(), offMin = -d.getTimezoneOffset()): string {\n const t = new Date(d.getTime() + offMin * 60_000);\n const sign = offMin < 0 ? \"-\" : \"+\";\n const abs = Math.abs(offMin);\n const hh = String(Math.floor(abs / 60)).padStart(2, \"0\");\n const mm = String(abs % 60).padStart(2, \"0\");\n return `${t.toISOString().slice(0, 19)}${sign}${hh}:${mm}`;\n}\n\nexport interface UsageBlock {\n input_tokens?: number;\n output_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n}\n\n// a type alias (not an interface): it must stay assignable to\n// Record<string, unknown> when written into the event transcript\nexport type StreamEvent = {\n type?: string;\n subtype?: string;\n session_id?: string;\n model?: string;\n cwd?: string;\n context_window?: number;\n model_info?: { context_window?: number } | null;\n message?: {\n content?: Array<{\n type?: string;\n text?: string;\n name?: string;\n id?: string;\n input?: unknown;\n /** tool_result blocks: the result payload (string or text blocks). */\n content?: unknown;\n is_error?: boolean;\n }>;\n usage?: UsageBlock;\n };\n usage?: UsageBlock;\n result?: string;\n is_error?: boolean;\n is_compact?: boolean;\n num_turns?: number;\n duration_ms?: number;\n /** API time of the final turn — the TTFT proxy on zeroed per-turn usage. */\n duration_api_ms?: number;\n};\n\n/** Context tokens of an assistant/result usage block (input+cache+output). */\nexport function usageContextTokens(u: UsageBlock | undefined): number | null {\n if (!u) return null;\n const t =\n (u.input_tokens ?? 0) +\n (u.cache_read_input_tokens ?? 0) +\n (u.cache_creation_input_tokens ?? 0) +\n (u.output_tokens ?? 0);\n return t > 0 ? t : null;\n}\n\n/**\n * Context never shrinks mid-segment, so the status keeps the high-water\n * mark of the usage it has seen: a smaller later reading (short reply,\n * sidechain answer) must not drag the meter down. Compaction is the one\n * legitimate drop — see `resetContextTokensOnCompact`.\n */\nexport function bumpContextTokens(status: Pick<WorkerStatus, \"contextTokens\">, tokens: number | null): void {\n if (tokens === null || tokens <= 0) return;\n status.contextTokens = Math.max(status.contextTokens ?? 0, tokens);\n}\n\n/**\n * A compact boundary (`system`/`compact_boundary`, the shape Claude Code\n * writes with `compactMetadata.preTokens`; `compact` kept as the older\n * spelling) legitimately restarts the context at a lower size: the floor\n * drops to the event's own usage — usually none — so the next usage\n * reading re-seeds the meter at the fresh, smaller context.\n */\nexport function resetContextTokensOnCompact(status: Pick<WorkerStatus, \"contextTokens\">, e: StreamEvent): void {\n status.contextTokens = usageContextTokens(e.usage) ?? null;\n}\n\n/** A compact boundary in a worker's stream, in either event spelling. */\nexport function isCompactBoundary(e: StreamEvent): boolean {\n return e.type === \"system\" && (e.subtype === \"compact_boundary\" || e.subtype === \"compact\");\n}\n\n/** The command string of a Bash tool_use input, \"\" when there is none. */\nfunction bashCommand(input: unknown): string {\n if (typeof input === \"object\" && input !== null && typeof (input as { command?: unknown }).command === \"string\") {\n return (input as { command: string }).command;\n }\n return \"\";\n}\n\nfunction toolResultText(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return content\n .map((c) =>\n typeof c === \"object\" && c !== null && typeof (c as { text?: unknown }).text === \"string\"\n ? (c as { text: string }).text\n : \"\"\n )\n .join(\"\\n\");\n }\n return \"\";\n}\n\n/**\n * Whether a tool_result block is a failed execution: the harness's own\n * is_error flag, or — the older Bash spelling — an \"Exit code N\" line with a\n * non-zero N in the result text. Feeds the consecFails counter supervision's\n * thrashing detector reads.\n */\nexport function toolResultFailed(block: { is_error?: boolean; content?: unknown }): boolean {\n if (block.is_error === true) return true;\n const m = toolResultText(block.content).match(/Exit code (\\d+)/);\n return m !== null && Number(m[1]) !== 0;\n}\n\n/** CLI aliases the built-in provider hands to `--model` (NATIVE_ANTHROPIC_MODELS'\n * values): a bare name like \"sonnet\", not a concrete id, so the init event's\n * resolved id is always the more useful thing to display. */\nconst NATIVE_MODEL_ALIASES = new Set(Object.values(NATIVE_ANTHROPIC_MODELS));\n\n/**\n * The model the init event announces, adopted into the status when the spawn\n * could not name one (a caller that passed `--model` itself in the claude\n * args, or a provider whose table has no entry for the capability) or named\n * only a CLI alias (the built-in provider's \"sonnet\"/\"haiku\", which tracks\n * whatever release is current — the init event says which one actually ran).\n * The run's real model is then only knowable from the first event it sends.\n * Never overwrites a concrete resolved or pinned model — that stays the\n * recorded truth.\n */\nexport function adoptInitModel(status: Pick<WorkerStatus, \"model\">, e: StreamEvent): void {\n if (status.model && !NATIVE_MODEL_ALIASES.has(status.model)) return;\n const m = (e.model ?? \"\").trim();\n if (m) status.model = m;\n}\n\n/**\n * The model a run goes out on: an explicit `--model` wins; else the class\n * target's alias (\"glm/fast\") names the capability; else the capability the\n * class implies (image → image model, spotcheck/simple → fast, everything\n * else → default), resolved against the provider's model table. The built-in\n * anthropic provider resolves through the same table as any other, so a\n * worker never inherits the orchestrator session's model.\n */\nexport function resolveRunModel(\n target: { provider: WorkerProvider; modelAlias: string | null },\n className?: string,\n modelFlag?: string\n): string {\n if (modelFlag) return modelFlag;\n const alias = target.modelAlias;\n const capability =\n alias && isModelCapability(alias) ? alias : classModelCapability(className);\n return resolveModelCapability(target.provider, capability);\n}\n\n/**\n * The `--model` part of the claude argv. Skipped when the caller already\n * pinned one in the claude args (it must not be clobbered) or when nothing\n * resolved — `--model \"\"` would break the spawn.\n */\nexport function modelArgs(model: string, callerPinned: boolean): string[] {\n return !callerPinned && model ? [\"--model\", model] : [];\n}\n\n/**\n * Whether the run's assembled cmd gets a forced --model. Headless workers\n * always take the resolved class/provider model — that table exists so\n * spawned subagents stay cheap. An interactive launch (`pai worker run` with\n * no -p) IS the chat pane, not a subagent: with no --model of the caller's\n * own it gets none here either, so Claude Code's own settings.json model\n * applies, exactly as launching `claude` by hand would (2026-09-20: an\n * interactive supervisor came up on the class model instead of the\n * settings.json [1m] variant, then needed a manual /model switch that\n * rebuilt the whole cache). A caller's own --model, headless or not, is\n * already in the passthrough args and must not be duplicated here.\n */\nexport function modelFlagArgs(headless: boolean, model: string, callerPinned: boolean): string[] {\n return headless ? modelArgs(model, callerPinned) : [];\n}\n\n/**\n * The prompt text actually sent to the worker and whether the trailer got\n * appended. An instruction at the END of the user turn outweighs one buried\n * in the system prompt (measured 2026-09-20: the AG2 contract alone was\n * ignored by both Haiku and Sonnet). Never applied to an interactive launch\n * (no -p) or when the caller brought its own --append-system-prompt — the\n * trailer's \"format in system prompt\" would then point at nothing.\n */\nexport function headlessPromptText(\n prompt: string | null,\n headless: boolean,\n callerSystemPrompt: boolean,\n format: \"ag2\" | \"json\"\n): { text: string | null; applied: boolean } {\n const applied = headless && prompt !== null && !callerSystemPrompt;\n return { text: applied ? prompt! + promptTrailer(format) : prompt, applied };\n}\n\n/**\n * Whether an invalid final report earns the one bounded re-ask: only when a\n * real validator caught the problem (a missing/broken aibroker must never\n * block the run further), the caller did not opt out, and there is a session\n * to resume into. Cap of exactly one is enforced by the caller never calling\n * this twice for the same run.\n */\nexport function shouldRetryReport(\n validation: { ok: boolean; validator: \"aibroker\" | \"none\" },\n noReportRetry: boolean,\n hasSession: boolean\n): boolean {\n return !validation.ok && validation.validator === \"aibroker\" && !noReportRetry && hasSession;\n}\n\n/** Context window announced by the init event, when the endpoint sends one. */\n/**\n * Did the run end well? A headless run must have produced a stream-json\n * result event that is not an error; an interactive launch IS the chat pane\n * and never emits one, so its clean exit (rc 0) is the whole verdict —\n * without this every interactive session that ends normally was recorded\n * as `failed rc=0` and supervision pinged the spawner about a non-failure.\n */\nexport function runSucceeded(\n headless: boolean,\n rc: number,\n resultEvent: Pick<StreamEvent, \"is_error\"> | null,\n): boolean {\n if (rc !== 0) return false;\n if (!headless) return true;\n return resultEvent !== null && !resultEvent.is_error;\n}\n\nexport function initContextWindow(e: StreamEvent): number | null {\n if (typeof e.context_window === \"number\" && e.context_window > 0) return e.context_window;\n if (e.model_info && typeof e.model_info.context_window === \"number\" && e.model_info.context_window > 0) {\n return e.model_info.context_window;\n }\n // the `[1m]` model variant announces a 1M-token window by suffix\n if (/\\[1m\\]$/.test((e.model ?? \"\").trim())) return 1_000_000;\n return null;\n}\n\n/**\n * Run one worker. Returns the process exit code to pass through.\n * Throws WorkersConfigError-shaped Errors for configuration problems.\n */\nexport async function runWorker(opts: RunOptions): Promise<number> {\n const { raw: _raw, workers: config } = readWorkersSection();\n void _raw;\n if (!config.enabled) {\n throw new Error(\n `workers are off. Turn them on with: pai worker on` +\n `\\n(then the Agent-tool hook stops denying Anthropic subagents only when you do)`\n );\n }\n const logDir = workersLogDir(config);\n mkdirSync(logDir, { recursive: true });\n try {\n const gc = gcWorktreesThrottled(logDir)?.filter((r) => r.archive);\n if (gc?.length) process.stderr.write(`pai worker: archived ${gc.length} leftover worktree(s) (pai worker gc)\\n`);\n } catch {\n // best effort\n }\n\n // class plan is not one worker but the planner orchestration (planner.ts)\n if (opts.className === \"plan\" && !opts._planner) {\n const { runPlanner } = await import(\"./planner.js\");\n return runPlanner(opts);\n }\n\n // Sub-worker bookkeeping: an explicit parent (chain stage, planner child)\n // wins, else the worker this process runs inside (PAI_WORKER_ID). Both\n // caps from workers.tree apply to parents that are workers themselves.\n const parent = launchParent(opts.parent);\n if (parent) assertChildAllowed(logDir, parent, config.tree);\n\n // An explicit --provider always bypasses capability resolution entirely;\n // otherwise --capability, or a class whose implied capability is not\n // \"default\" and names no provider of its own, resolves cross-provider.\n const capability = opts.providerFlag\n ? null\n : capabilityForRun(config, { capabilityFlag: opts.capabilityFlag, className: opts.className });\n const capabilityResolution: CapabilityRunResolution | null = capability\n ? resolveCapabilityRun(config, capability)\n : null;\n\n const target = capabilityResolution\n ? {\n providerName: capabilityResolution.providerName,\n provider: capabilityResolution.provider,\n modelAlias: null,\n classMcp: null,\n via: \"class\" as const,\n }\n : resolveTarget(config, logDir, {\n flagProvider: opts.providerFlag,\n className: opts.className,\n });\n assertProviderRunnable(target.providerName, target.provider);\n\n const noBrowser = opts.browser === undefined ? config.noBrowserByDefault : !opts.browser;\n const parsed = parseRunnerArgs(noBrowser ? stripBrowserArgs(opts.claudeArgs) : opts.claudeArgs);\n // The config default is a headless-run guard; an interactive launch is the operator's own session and is\n // limited only by an explicit --max-minutes / --deadline.\n const limit = resolveLimit(opts, parsed.headless ? config.defaultMaxMinutes : 0);\n const label =\n opts.label ??\n shortText(parsed.prompt ?? UNLABELED, 70);\n\n const model = capabilityResolution\n ? capabilityResolution.model\n : resolveRunModel(target, opts.className, opts.modelFlag);\n const reportFormat = resolveReportFormat(opts.reportFormatFlag);\n\n // tracked across every engine (including a quota reroute's recursive\n // runWorker call) so the exit hook below knows which status to check,\n // whatever opts.id/onWorkerStart the caller did or didn't pass\n let workerId: string | undefined = opts.id;\n const trackStart = (wid: string) => {\n workerId = wid;\n opts.onWorkerStart?.(wid);\n };\n\n try {\n let rc: number;\n if (target.provider.engine === \"image\") {\n rc = await executeImageRun({\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n capability: capability ?? \"image\",\n outPath: opts.imageOut,\n size: opts.imageSize,\n timeoutMs: opts.imageTimeoutMs,\n cwd: opts.cwd,\n specPath: opts.specPath,\n parent: parent ?? undefined,\n stage: opts.stage,\n quiet: opts.quiet,\n onWorkerStart: trackStart,\n id: opts.id,\n });\n } else if (target.provider.engine === \"codex\") {\n rc = await executeCodexRun({\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n claudeArgs: opts.claudeArgs,\n noPane: opts.noPane ?? false,\n cwd: opts.cwd,\n specPath: opts.specPath,\n parent: parent ?? undefined,\n stage: opts.stage,\n quiet: opts.quiet,\n onWorkerStart: trackStart,\n id: opts.id,\n worktreeFlag: opts.worktreeFlag,\n className: opts.className,\n reportFormat,\n capability: capability ?? undefined,\n limit,\n noBrowser,\n });\n } else {\n rc = await executeRun({\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n claudeArgs: opts.claudeArgs,\n noPane: opts.noPane ?? false,\n mcpFlag: opts.mcpFlag,\n cwd: opts.cwd,\n specPath: opts.specPath,\n parent: parent ?? undefined,\n stage: opts.stage,\n quiet: opts.quiet,\n onWorkerStart: trackStart,\n id: opts.id,\n capability: capability ?? undefined,\n worktreeFlag: opts.worktreeFlag,\n className: opts.className,\n reroutes: opts._reroutes ?? 0,\n printCmd: opts.printCmd,\n reportFormat,\n noReportRetry: opts.noReportRetry ?? false,\n limit,\n noBrowser,\n });\n }\n await returnControlsIfHeld(logDir, workerId);\n return rc;\n } catch (e) {\n await returnControlsIfHeld(logDir, workerId);\n if (e instanceof Error && e.message.startsWith(\"key file\")) {\n throw new Error(\n `provider \"${target.providerName}\": ${e.message}` +\n `\\nPut the token in that file (chmod 600) or point keyFile elsewhere.`\n );\n }\n throw e;\n }\n}\n\n/**\n * The core tools a headless run grants when the caller brings none of its\n * own. Permission-gated tools (Bash, Read, Write, …) are never offered to a\n * headless session that has no allow rule for them — the child starts with\n * only the tools that never ask (tool search, web, cron, tasks) and cannot\n * touch a file or shell (2026-09-18). A caller's own --allowedTools is a\n * deliberate restriction and passes through untouched.\n */\nconst DEFAULT_WORKER_TOOLS = \"Read,Edit,Write,Bash,Grep,Glob\";\n\n/** The --allowedTools args a headless run needs; [] when the caller granted. */\nexport function headlessToolGrants(allowedTools: string[]): string[] {\n return allowedTools.length ? [] : [\"--allowedTools\", DEFAULT_WORKER_TOOLS];\n}\n\n/**\n * `--tools` narrows which built-in tool *schemas* claude loads into the\n * system prompt — distinct from `--allowedTools`, which narrows which of\n * those loaded tools may run without asking. A headless worker with a small\n * grant (say, just Read) still pays for every built-in schema (Bash, Web*,\n * NotebookEdit, …) it never uses: ~9k tokens per spawn (29,270 -> 20,352,\n * measured 2026-09-20). Passing --tools built from the same grant removes\n * the unused schemas.\n *\n * MCP grants (`mcp__server__tool` / `mcp__server__*`) are left out of the\n * --tools list: they are not built-in tool names, --tools does not\n * recognize them, and the MCP server's tools load through --mcp-config\n * regardless. Tested 2026-09-20: with an --allowedTools grant of\n * `Read,mcp__pai__memory_search`, passing `--tools Read` (mcp name\n * omitted) still let the worker call memory_search successfully; the tool\n * loads from the filtered MCP config, not from --tools.\n */\nexport function headlessToolsFlag(allowedTools: string[]): string[] {\n const names: string[] = [];\n for (const entry of allowedTools) {\n for (const raw of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n if (raw.startsWith(\"mcp__\")) continue; // loads via --mcp-config, not --tools\n const patterned = raw.match(/^([A-Za-z]+)\\(.*\\)$/); // e.g. Bash(git *) -> Bash\n const name = patterned ? patterned[1] : raw;\n if (!names.includes(name)) names.push(name);\n }\n }\n return names.length ? [\"--tools\", names.join(\",\")] : [];\n}\n\n/**\n * A `--tools` value with no ToolSearch silently disables MCP tool deferral\n * (measured 2026-09-20: a run with `--tools \"Bash,Read,Grep,Glob,Agent\"` paid\n * 65,300 first-turn tokens with 5 MCP servers, 230,448 with 16, versus\n * 24,889 / 37,133 with ToolSearch present). Applied to the fully-assembled\n * argv so it catches a `--tools` from any source — the computed\n * `headlessToolsFlag`/project-pin flag, or one the caller put directly on\n * the command line.\n */\nexport function ensureToolSearch(cmd: string[]): string[] {\n const flagIdx = cmd.findIndex((a) => a === \"--tools\");\n if (flagIdx >= 0 && flagIdx + 1 < cmd.length) {\n const value = cmd[flagIdx + 1];\n if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n const out = [...cmd];\n out[flagIdx + 1] = `${value},ToolSearch`;\n process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n return out;\n }\n return cmd;\n }\n const eqIdx = cmd.findIndex((a) => a.startsWith(\"--tools=\"));\n if (eqIdx >= 0) {\n const value = cmd[eqIdx].slice(\"--tools=\".length);\n if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n const out = [...cmd];\n out[eqIdx] = `--tools=${value},ToolSearch`;\n process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n return out;\n }\n }\n return cmd;\n}\n\n/**\n * The claude flag that turns on the browser bridge, when the run asked for it.\n *\n * The bridge is not an MCP server — it rides the Chrome native-host channel,\n * is absent from `mcpServers`, and is off in a spawned claude until `--chrome`\n * is passed. A grant such as `mcp__claude-in-chrome__tabs_context_mcp` is\n * therefore a request for that flag, not for a server to load; without this\n * the grant names a tool that never exists. `rest` is the caller's own argv:\n * a `--chrome` they passed themselves is kept rather than duplicated.\n */\nexport function chromeGrantArgs(wanted: string[], rest: string[] = []): string[] {\n if (rest.includes(\"--chrome\")) return [];\n return grantsChrome(wanted) ? [\"--chrome\"] : [];\n}\n\n/**\n * Which MCP server names and --tools list an interactive launch (`pai\n * worker run`, no -p) uses: an explicit --mcp on this command line beats\n * the project's `pai project mcp` pin; an explicit --tools beats `pai\n * project tools`. Neither given: [] — today's \"everything loads\" default.\n */\nexport function interactiveMcpTools(\n explicitMcp: string[],\n callerTools: boolean,\n projectLaunch: Omit<ProjectLaunchConfig, \"slug\"> | null\n): { mcpNames: string[]; tools: string[] } {\n return {\n mcpNames: explicitMcp.length ? explicitMcp : (projectLaunch?.mcp ?? []),\n tools: !callerTools && projectLaunch?.tools?.length ? projectLaunch.tools : [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// image engine (2i) — no process spawn: one HTTP request, one file written.\n// ---------------------------------------------------------------------------\n\ninterface ImageExecuteArgs {\n logDir: string;\n target: ReturnType<typeof resolveTarget>;\n model: string;\n label: string;\n parsed: ReturnType<typeof parseRunnerArgs>;\n capability: string;\n outPath?: string;\n size?: string;\n timeoutMs?: number;\n cwd?: string;\n specPath?: string;\n parent?: string;\n stage?: string;\n quiet?: boolean;\n onWorkerStart?: (wid: string) => void;\n id?: string;\n}\n\ninterface FinaliseArgs {\n logDir: string;\n ledger: string;\n status: WorkerStatus;\n /** The process's real outcome; recordWorktree sees this, never the orphan verdict. */\n ok: boolean;\n rc: number;\n secs: number;\n /** Engine-specific WORKER-END fields (provider, mode, engine, model, label, ...). */\n end: Record<string, unknown>;\n report: WorkerReport | null;\n /** Handoff text when the report has no notes. */\n fallbackText?: string;\n worktree?: WorktreeInfo | null;\n /** The deadline stopped the run after this many minutes. */\n timedOut?: number;\n /**\n * Runs after the worktree outcome is recorded, before the parent handoff\n * (print the result, quota reroute). A returned number ends the run with\n * it, skipping the handoff — the reroute's own run reports instead.\n */\n beforeHandoff?: (status: WorkerStatus, report: WorkerReport | null) => Promise<number | void> | number | void;\n}\n\n/**\n * The finalisation every engine shares: orphaned-children check, state, the\n * WORKER-END ledger line, worktree outcome, parent handoff, final rc. A worker\n * owns its children's results (the worker contract says so): if it\n * backgrounded a sub-worker and exited without waiting, the run must not\n * report success, whatever the process returned.\n */\n/** Per-git-call bound for the salvage a kill signal runs before exiting. */\nconst SIGNAL_SALVAGE_MS = 5_000;\n\n/** The deadline fired: record it on the status and in the ledger (the caller then stops the child). */\nexport function fireDeadline(logDir: string, status: WorkerStatus, ledger: string, limit: RunLimit): void {\n status.timedOut = true;\n status.last = `deadline reached after ${limit.minutes} min`;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-DEADLINE\", { id: status.id, minutes: limit.minutes });\n}\n\n/** Decide why a signal ended the run and put it on the status; the caller writes WORKER-END. */\nexport function recordSignalEnd(logDir: string, status: WorkerStatus, signal: string): EndInfo {\n const end = decideEndReason({\n logDir,\n id: status.id,\n signal,\n timedOut: status.timedOut,\n parentPid: status.parentPid,\n parentComm: status.parentComm,\n parentBackground: status.parentBackground,\n });\n status.endReason = end.reason;\n status.endBy = end.by;\n status.endNote = end.note;\n return end;\n}\n\nexport async function finaliseRun(f: FinaliseArgs): Promise<number> {\n const { logDir, ledger, ok, rc } = f;\n let { status, report } = f;\n const wid = status.id;\n // first, whatever the outcome (rc 143 included): uncommitted edits in the\n // worktree must reach the branch before anything can drop the worktree\n const salvaged = f.worktree\n ? salvageOnExit(ledger, wid, status.label || UNLABELED, f.worktree.dir)\n : [];\n if (salvaged.length) {\n const msg = `WORKER-SALVAGE: committed uncommitted edits (${salvaged.join(\", \")})`;\n report = { ...(report ?? {}), notes: [report?.notes ?? f.fallbackText, msg].filter(Boolean).join(\"\\n\"), result: report?.result ?? \"-\" };\n }\n if (f.timedOut) {\n const reason = `deadline reached after ${f.timedOut} min`;\n status.timedOut = true;\n status.last = reason;\n // partial state = the salvage summary (r=~: stopped, not failed outright)\n report = { ...(report ?? {}), result: \"~\", why: reason, notes: [reason, report?.notes ?? f.fallbackText].filter(Boolean).join(\"\\n\") };\n }\n const orphanReason = checkOrphanedChildren(logDir, wid);\n const succeeded = ok && !orphanReason && !f.timedOut;\n status.state = succeeded ? \"done\" : \"failed\";\n status.rc = rc;\n status.secs = f.secs;\n if (orphanReason) {\n status.last = shortText(orphanReason, 90);\n report = { ...(report ?? {}), notes: orphanReason, result: \"-\" };\n appendLedger(ledger, \"WORKER-ORPHAN-CHILDREN\", { id: wid, reason: orphanReason });\n }\n const end = decideEndReason({ logDir, id: wid, timedOut: f.timedOut !== undefined });\n status.endReason = end.reason;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-END\", { id: wid, ...f.end, rc, secs: f.secs, ...endFields(end) });\n\n if (f.worktree) status = recordWorktree(logDir, status, f.worktree, ok);\n\n const early = await f.beforeHandoff?.(status, report);\n if (typeof early === \"number\") return early;\n\n const finalRc = rc !== 0 ? rc : succeeded ? 0 : 1;\n if (status.parent && isWorkerId(logDir, status.parent)) {\n try {\n await deliverHandoff(logDir, {\n from: wid,\n to: status.parent,\n kind: \"result\",\n text: shortText(report?.notes ?? f.fallbackText ?? (succeeded ? \"done\" : \"failed\"), 400),\n data: {\n rc: finalRc,\n ok: succeeded,\n ...(status.branch ? { branch: status.branch, commits: status.commits ?? 0 } : {}),\n ...(report ? { report } : {}),\n },\n });\n } catch {\n // the inbox line is best effort; it must never fail the exit path\n }\n }\n return finalRc;\n}\n\nasync function executeImageRun(a: ImageExecuteArgs & { config: ReturnType<typeof readWorkersSection>[\"workers\"] }): Promise<number> {\n const { logDir, target, model, label, parsed } = a;\n if (!parsed.headless || parsed.prompt === null) {\n throw new Error(\n `provider \"${target.providerName}\" (engine image) supports headless runs only: ` +\n `pass the task with -p '<prompt>'`\n );\n }\n const wid = a.id ?? newWorkerId();\n const cwd = a.cwd ?? process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const outPath = a.outPath ?? join(logDir, `${wid}.png`);\n\n const status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(),\n updated: nowStamp(),\n turns: 0,\n tools: 0,\n last: \"generating image\",\n rc: null,\n secs: null,\n origin: \"spawn\",\n outputFormat: parsed.outputFormat,\n reportFormat: \"json\",\n ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n };\n saveStatus(logDir, status);\n a.onWorkerStart?.(wid);\n const ledger = ledgerPath(logDir);\n appendLedger(ledger, \"WORKER-START\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n engine: \"image\",\n model,\n cwd,\n label,\n capability: a.capability,\n ...(a.specPath ? { spec: a.specPath } : {}),\n });\n\n const imageEnd = {\n provider: target.providerName,\n mode: \"headless\",\n engine: \"image\",\n model,\n label,\n capability: a.capability,\n };\n const t0 = Date.now();\n try {\n const result = await runImageCapability({\n providerName: target.providerName,\n provider: target.provider,\n model,\n prompt: parsed.prompt,\n outPath,\n size: a.size,\n timeoutMs: a.timeoutMs,\n });\n const secs = Math.round((Date.now() - t0) / 1000);\n status.last = `wrote ${result.path}`;\n return await finaliseRun({\n logDir,\n ledger,\n status,\n ok: true,\n rc: 0,\n secs,\n end: imageEnd,\n report: null,\n beforeHandoff: () => {\n if (!a.quiet) process.stdout.write(JSON.stringify(result) + \"\\n\");\n },\n });\n } catch (e) {\n const secs = Math.round((Date.now() - t0) / 1000);\n status.last = e instanceof Error ? e.message : String(e);\n await finaliseRun({ logDir, ledger, status, ok: false, rc: 1, secs, end: imageEnd, report: null });\n throw e;\n }\n}\n\n/**\n * Follow pane, made observable on stderr (stdout stays pure JSON for\n * `--output-format json`): one line naming the outcome — opened, skipped\n * (with reason), or failed (with reason, alongside the existing PANE-FAIL\n * ledger line). Fire-and-forget, never blocks the worker.\n */\nexport async function announcePane(\n logDir: string,\n config: ReturnType<typeof readWorkersSection>[\"workers\"],\n wid: string,\n term: string,\n noPane: boolean,\n ledger: string\n): Promise<void> {\n const skip = !noPane\n ? !config.pane.enabled\n ? \"workers.pane.enabled is false\"\n : process.env.PAI_WORKER_AUTOPANE === \"0\"\n ? \"PAI_WORKER_AUTOPANE=0\"\n : paneBackend() === \"none\"\n ? \"no tmux ($TMUX) or iTerm2 (ITERM_SESSION_ID) here\"\n : null\n : \"--no-pane\";\n if (skip) {\n process.stderr.write(`[pai worker] no pane for ${wid}: ${skip}\\n`);\n return;\n }\n try {\n const r = await openPaneForWorker(logDir, config, wid, term);\n const suffix = r.session ? ` (iTerm session ${r.session})` : \"\";\n process.stderr.write(`[pai worker] pane opened for ${wid}${suffix}\\n`);\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n process.stderr.write(`[pai worker] no pane for ${wid}: ${message.slice(0, 200)}\\n`);\n appendLedger(ledger, \"PANE-FAIL\", { id: wid, error: message.slice(0, 200) });\n }\n}\n\ninterface ExecuteArgs {\n config: ReturnType<typeof readWorkersSection>[\"workers\"];\n logDir: string;\n target: ReturnType<typeof resolveTarget>;\n model: string;\n label: string;\n parsed: ReturnType<typeof parseRunnerArgs>;\n claudeArgs: string[];\n noPane: boolean;\n mcpFlag?: string;\n cwd?: string;\n specPath?: string;\n parent?: string;\n stage?: string;\n quiet?: boolean;\n onWorkerStart?: (wid: string) => void;\n id?: string;\n worktreeFlag?: boolean;\n className?: string;\n reroutes: number;\n printCmd?: boolean;\n reportFormat: \"json\" | \"ag2\";\n noReportRetry?: boolean;\n /** Capability name this run resolved through (--capability, or an implied class capability), when it did. */\n capability?: string;\n /** Time limit of the run; null = none. */\n limit: RunLimit | null;\n /** Browser tools are stripped from this run (and denied by the worker guard). */\n noBrowser: boolean;\n}\n\nasync function executeRun(a: ExecuteArgs): Promise<number> {\n const { config, logDir, target, model, label, parsed, noPane, limit, noBrowser } = a;\n const headless = parsed.headless;\n\n // openai-protocol providers run through the local proxy (started on demand)\n let proxyUrl: string | undefined;\n if (target.provider.protocol === \"openai\") {\n const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n proxyUrl = `${base}/${target.providerName}`;\n }\n const env = buildRunEnv(target.provider, headless, proxyUrl);\n\n const wid = a.id ?? newWorkerId();\n const cwd = a.cwd ?? process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const session = resolveSession(term);\n // orchestrator Bash children have no terminal identity (see scope.ts) — the\n // session map supplies the claude session they were spawned by instead\n const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n // One worktree per run whose tools can write (Edit/Write/Bash or no\n // restriction), in a git cwd; --worktree/--no-worktree override. A git refusal\n // degrades to an in-place run — the worker itself must still run.\n // Skipped for --print-cmd: it is a dry-run and must not touch git.\n let worktree: WorktreeInfo | null = null;\n if (\n headless &&\n !a.printCmd &&\n worktreeWanted(a.worktreeFlag, { cwd, allowedTools: parsed.allowedTools })\n ) {\n try {\n worktree = addWorktree(logDir, wid, cwd);\n } catch (e) {\n const why = (e as Error).message;\n process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n }\n }\n // sub-workers detect themselves (and their parent) through this variable\n env.PAI_WORKER_ID = wid;\n if (noBrowser) env.PAI_WORKER_NO_BROWSER = \"1\";\n\n const mcpFlag = noBrowser ? stripBrowserNames(a.mcpFlag ? [a.mcpFlag] : [])[0] : a.mcpFlag;\n const classMcp = noBrowser && target.classMcp ? stripBrowserNames(target.classMcp) : target.classMcp;\n if (noBrowser) parsed.mcp = stripBrowserNames(parsed.mcp);\n const chromeArgs = noBrowser\n ? []\n : chromeGrantArgs(\n [...(mcpFlag ? [mcpFlag] : []), ...parsed.mcp, ...(classMcp ?? []), ...parsed.allowedTools],\n parsed.rest\n );\n\n // MCP / tools: caller config > allowlist (--mcp flag / --mcp args / role /\n // mcp__ grants in --allowedTools) > project pin (interactive only, `pai\n // project mcp` / `pai project tools`) > the strict empty set (headless) /\n // everything (interactive, today's default).\n let mcpArgs: string[] = [];\n let toolsFlag: string[] = headless ? headlessToolsFlag(parsed.allowedTools) : [];\n if (headless && !parsed.callerMcpConfig) {\n const wanted = [\n ...(mcpFlag ? [mcpFlag] : []),\n ...parsed.mcp,\n ...(classMcp ?? []),\n ...mcpServersFromToolGrants(parsed.allowedTools),\n ];\n if (wanted.length) {\n const names = expandMcpNames(wanted, config); // unknown names fail fast\n mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n } else {\n mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", ensureNoMcpConfig(logDir)];\n }\n } else if (!headless && !parsed.callerMcpConfig) {\n const explicitMcp = [...(mcpFlag ? [mcpFlag] : []), ...parsed.mcp];\n const projectLaunch = await projectLaunchConfig(cwd);\n const picked = interactiveMcpTools(explicitMcp, parsed.callerTools, projectLaunch);\n // applied pins only: an explicit --mcp / --tools overrides its pin\n const applied = projectLaunch && {\n ...projectLaunch,\n mcp: explicitMcp.length ? undefined : projectLaunch.mcp,\n tools: picked.tools.length ? projectLaunch.tools : undefined,\n };\n for (const line of pinNotices(applied)) console.error(line);\n if (picked.mcpNames.length) {\n const names = expandMcpNames(picked.mcpNames, config);\n mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n }\n if (picked.tools.length) toolsFlag = [\"--tools\", picked.tools.join(\",\")];\n }\n\n // In stdin mode the prompt moves to the first user message on stdin, so it\n // must come off the command line (bare -p stays: stream-json needs --print).\n const restArgs = headless ? stripPromptValues(parsed.rest) : parsed.rest;\n const toolArgs = headless ? headlessToolGrants(parsed.allowedTools) : [];\n const { text: promptText, applied: trailerApplied } = headlessPromptText(\n parsed.prompt,\n headless,\n parsed.callerSystemPrompt,\n a.reportFormat\n );\n const claudeCmd = claudeCommand(env, config.caveman);\n let cmd: string[] = [...claudeCmd];\n cmd.push(...modelFlagArgs(headless, model, Boolean(parsed.callerModel)));\n cmd.push(...chromeArgs, ...mcpArgs, ...toolArgs, ...toolsFlag, ...restArgs);\n if (headless) {\n cmd.push(\"--output-format\", \"stream-json\", \"--verbose\", \"--input-format\", \"stream-json\");\n }\n // ONE flag: contract (unless the caller brought their own), placement, caller text\n cmd.push(\n ...appendSystemPromptArgs([\n ...(headless && !parsed.callerSystemPrompt ? [workerContractPrompt(a.className, a.reportFormat)] : []),\n ...(headless\n ? [worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) : inPlaceSystemPrompt()]\n : []),\n ...(headless && limit ? [deadlinePromptLine(limit)] : []),\n ...parsed.callerSystemPrompts,\n ])\n );\n cmd = ensureToolSearch(cmd);\n\n // Dry run: the argv audit, no status/ledger/pane, no spawn.\n if (a.printCmd) {\n console.log(\n JSON.stringify({\n cmd,\n cwd: worktree?.dir ?? cwd,\n browser: noBrowser ? \"off\" : \"on\",\n ...(limit ? { deadline: new Date(limit.deadlineAt).toISOString(), maxMinutes: limit.minutes } : { limit: \"none\" }),\n })\n );\n return 0;\n }\n\n let status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n ...parentInfo(),\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(),\n updated: nowStamp(),\n turns: 0,\n tools: 0,\n last: headless ? \"starting\" : \"interactive\",\n rc: null,\n secs: null,\n // interactive runs ARE the chat pane, not a spawned subagent of it\n origin: headless ? \"spawn\" : \"chat\",\n outputFormat: parsed.outputFormat,\n ...(headless ? { reportFormat: a.reportFormat } : {}),\n ...(trailerApplied ? { promptTrailer: true } : {}),\n ...(session ? { session } : {}),\n ...(spawnerSession ? { spawnerSession } : {}),\n // no window seed: contextWindow comes from the init event only, and the\n // meter stays hidden until one is announced (never a guessed default)\n ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n ...(worktree\n ? {\n worktreeDir: worktree.dir,\n branch: worktree.branch,\n worktreeBase: worktree.base,\n worktreeSnapshot: worktree.snapshot,\n }\n : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n ...(limit ? { deadlineAt: limit.deadlineAt, maxMinutes: limit.minutes } : {}),\n };\n saveStatus(logDir, status);\n a.onWorkerStart?.(wid);\n const ledger = ledgerPath(logDir);\n appendLedger(ledger, \"WORKER-START\", {\n id: wid,\n provider: target.providerName,\n mode: headless ? \"headless\" : \"interactive\",\n model,\n cwd,\n label,\n ...(a.capability ? { capability: a.capability } : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n });\n\n // Follow pane: headless only (interactive runs ARE the chat pane), best\n // effort, never blocking the worker.\n if (headless) void announcePane(logDir, config, wid, term, noPane, ledger);\n\n const t0 = Date.now();\n const proc = spawn(cmd[0], cmd.slice(1), {\n env,\n cwd: worktree?.dir ?? cwd,\n stdio: headless ? [\"pipe\", \"pipe\", \"inherit\"] : \"inherit\",\n });\n\n // --- stdin lifecycle (2i): prompt in, socket forwards, close 2 s after result\n let operatorInFlight = 0;\n let closeTimer: NodeJS.Timeout | null = null;\n const armStdinClose = () => {\n if (closeTimer) clearTimeout(closeTimer);\n closeTimer = setTimeout(() => {\n if (operatorInFlight === 0) {\n try {\n proc.stdin?.end();\n } catch {\n /* already closed */\n }\n }\n }, 2_000);\n };\n\n let eventsFd: number | null = null;\n const writeEvent = (obj: Record<string, unknown>): void => {\n if (eventsFd === null) return;\n try {\n writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n } catch {\n // a full disk must not take the worker transcript's process down\n }\n };\n\n const operatorServer = headless\n ? createOperatorServer(logDir, wid, async (text) => {\n const stdin = proc.stdin;\n if (!stdin || !stdin.writable || stdin.writableEnded) {\n return \"the worker's stdin is already closed (its turn ended) — use: pai worker resume\";\n }\n operatorInFlight += 1;\n if (closeTimer) {\n clearTimeout(closeTimer);\n closeTimer = null;\n }\n // a handoff delivery's mirror carries a flag: the viewer shows the\n // inbox ◆ line instead, never both\n writeEvent({ type: \"operator\", text, handoff: isHandoffMessage(text) });\n return new Promise<void | string>((resolve) => {\n try {\n stdin.write(stdinUserMessage(operatorUserText(text)) + \"\\n\", (e) =>\n resolve(e ? `write to the worker failed: ${e.message}` : undefined)\n );\n } catch (e) {\n resolve(`write to the worker failed: ${(e as Error).message}`);\n }\n });\n })\n : null;\n\n let killed = false;\n const stopDeadline = limit ? armDeadline(proc, limit, () => fireDeadline(logDir, status, ledger, limit)) : null;\n const cleanup = () => {\n stopDeadline?.();\n if (closeTimer) clearTimeout(closeTimer);\n operatorServer?.close();\n };\n const onSignal = (sig: string) => {\n killed = true;\n status.state = \"killed\";\n status.rc = 143;\n status.secs = Math.floor((Date.now() - t0) / 1000);\n const end = recordSignalEnd(logDir, status, sig);\n status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : \"\"})`;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-END\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n model,\n rc: 143,\n secs: status.secs,\n killed: 1,\n ...endFields(end),\n note: end.note,\n label,\n });\n // a killed worktree run leaves nothing to merge — drop its worktree and\n // branch too, or every kill strands them for hand-pruning (2026-09-18)\n if (worktree) {\n salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);\n try {\n recordWorktree(logDir, status, worktree, false);\n } catch {\n /* best effort: the status already says killed */\n }\n }\n cleanup();\n try {\n proc.kill();\n } catch {\n /* already gone */\n }\n process.exit(143);\n };\n process.once(\"SIGTERM\", () => onSignal(\"SIGTERM\"));\n process.once(\"SIGINT\", () => onSignal(\"SIGINT\"));\n process.once(\"SIGHUP\", () => onSignal(\"SIGHUP\"));\n\n // Holder for the last result event + its parsed report: assigned inside the\n // readline callback below, read after the await.\n const ctx: { resultEvent: StreamEvent | null; resultReport: WorkerReport | null } = {\n resultEvent: null,\n resultReport: null,\n };\n\n if (headless) {\n // the first user message carries the prompt (the -p value was stripped)\n if (promptText !== null) {\n try {\n proc.stdin!.write(stdinUserMessage(promptText) + \"\\n\");\n } catch {\n /* child died instantly; the close handler reports it */\n }\n }\n eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n const rl = createInterface({ input: proc.stdout! });\n rl.on(\"line\", (line) => {\n if (parsed.outputFormat === \"stream-json\") {\n process.stdout.write(line + \"\\n\");\n }\n if (!line.startsWith(\"{\")) return;\n let e: StreamEvent;\n try {\n e = JSON.parse(line) as StreamEvent;\n } catch {\n return;\n }\n writeEvent(e as Record<string, unknown>);\n if (e.type === \"system\" && e.subtype === \"init\") {\n if (e.session_id) status.claudeSession = e.session_id;\n adoptInitModel(status, e);\n const cw = initContextWindow(e);\n if (cw) status.contextWindow = cw;\n saveStatus(logDir, status);\n } else if (isCompactBoundary(e)) {\n resetContextTokensOnCompact(status, e);\n saveStatus(logDir, status);\n } else if (e.type === \"assistant\") {\n status.turns += 1;\n bumpContextTokens(status, usageContextTokens(e.message?.usage));\n for (const block of e.message?.content ?? []) {\n if (block.type === \"tool_use\") {\n status.tools += 1;\n status.last = describeTool(block.name ?? \"?\", block.input);\n // a long single sleep is worth seeing while it runs (supervision's\n // \"sleeping\" kind) — set when issued, cleared when it completes\n if (block.name === \"Bash\") status.sleepSec = parseSleepSecs(bashCommand(block.input));\n } else if (block.type === \"text\" && (block.text ?? \"\").trim()) {\n status.last = \"says: \" + shortText(block.text, 70);\n }\n }\n saveStatus(logDir, status);\n } else if (e.type === \"user\") {\n // tool results arrive as user events: each one advances (or resets)\n // the consecutive-failure counter supervision's thrashing detector\n // reads, and retires a pending sleep observation. Plain user turns\n // (operator input) have no tool_result blocks and change nothing.\n const blocks = e.message?.content;\n if (Array.isArray(blocks)) {\n let sawResult = false;\n for (const block of blocks) {\n if (block.type !== \"tool_result\") continue;\n sawResult = true;\n status.consecFails = toolResultFailed(block) ? (status.consecFails ?? 0) + 1 : 0;\n status.sleepSec = null;\n }\n if (sawResult) saveStatus(logDir, status);\n }\n } else if (e.type === \"result\") {\n const tokens = usageContextTokens(e.usage);\n // a compact result legitimately restarts the context lower\n if (e.is_compact) status.contextTokens = tokens ?? status.contextTokens;\n else bumpContextTokens(status, tokens);\n const report = parseWorkerReport(e.result ?? \"\");\n if (report?.notes) status.last = shortText(report.notes, 90);\n else if (e.result) status.last = shortText(e.result, 90);\n saveStatus(logDir, status);\n operatorInFlight = 0;\n armStdinClose();\n ctx.resultEvent = e;\n ctx.resultReport = report;\n }\n });\n }\n\n const rc = await new Promise<number>((resolve, reject) => {\n proc.on(\"error\", reject);\n proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n });\n if (eventsFd !== null) closeSync(eventsFd);\n cleanup();\n\n const secs = Math.floor((Date.now() - t0) / 1000);\n const resultEvent = ctx.resultEvent;\n const ok = runSucceeded(headless, rc, resultEvent);\n if (resultEvent) status.last = shortText(resultEvent.result ?? \"\", 90);\n if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n if (headless && a.reportFormat === \"ag2\" && resultEvent?.result) {\n const v = validateAg2(resultEvent.result);\n status.reportValid = v.ok;\n status.reportErrors = v.errors;\n // one bounded re-ask, cap exactly one — see shouldRetryReport\n if (shouldRetryReport(v, a.noReportRetry ?? false, Boolean(status.claudeSession))) {\n status.reportRetried = true;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-REPORT-RETRY\", { id: wid, reason: v.errors.join(\"; \") || \"invalid AG2 message\" });\n try {\n const reaskText = await reaskAg2Report({\n env,\n cwd: worktree?.dir ?? cwd,\n model,\n callerPinnedModel: Boolean(parsed.callerModel),\n chromeArgs,\n mcpArgs,\n toolArgs,\n toolsFlag,\n sessionId: status.claudeSession!,\n claudeCmd,\n });\n const v2 = validateAg2(reaskText);\n status.reportValid = v2.ok;\n status.reportErrors = v2.errors;\n if (v2.ok) {\n resultEvent.result = reaskText;\n ctx.resultReport = parseWorkerReport(reaskText);\n if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n }\n } catch {\n // the re-ask is best effort; the original invalid report stands\n }\n }\n }\n if (headless && ctx.resultReport?.changed?.length) {\n const verify = verifyReportChanges(ctx.resultReport.changed, worktree?.dir ?? cwd, t0);\n if (!verify.ok) {\n status.reportValid = false;\n const note = verifyFailNote(verify);\n status.last = shortText(note, 90);\n ctx.resultReport.notes = ctx.resultReport.notes ? `${ctx.resultReport.notes} — ${note}` : note;\n appendLedger(ledger, \"REPORT-UNVERIFIED\", {\n id: wid,\n failures: verify.failures.length,\n paths: verify.failures.map((f) => f.path).slice(0, 5).join(\",\"),\n });\n }\n }\n return finaliseRun({\n logDir,\n ledger,\n status,\n ok,\n rc,\n secs,\n end: {\n provider: target.providerName,\n mode: headless ? \"headless\" : \"interactive\",\n model,\n turns: status.turns,\n tools: status.tools,\n label,\n },\n report: ctx.resultReport,\n fallbackText: resultEvent?.result,\n worktree,\n timedOut: status.timedOut ? limit?.minutes : undefined,\n beforeHandoff: async (st, rep) => {\n if (headless && !a.quiet) {\n printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n }\n\n // Quota reroute: only auto-routed runs, dead before the first tool call.\n const resultText = resultEvent?.result ?? \"\";\n if (\n !ok &&\n headless &&\n a.target.via === \"auto\" &&\n config.routing.retryOnQuota &&\n status.turns <= 1 &&\n status.tools === 0 &&\n isQuotaFailure(resultText)\n ) {\n setCooldown(logDir, target.providerName, config.routing.cooldownMinutes);\n const next = nextAutoProvider(config, logDir, target.providerName);\n if (next && a.reroutes < config.routing.order.length) {\n appendLedger(ledger, \"WORKER-REROUTE\", {\n from: target.providerName,\n to: next,\n reason: \"quota\",\n });\n return runWorker({\n providerFlag: next,\n label,\n noPane: a.noPane,\n mcpFlag: a.mcpFlag,\n cwd: a.cwd,\n specPath: a.specPath,\n parent: a.parent,\n stage: a.stage,\n claudeArgs: a.claudeArgs,\n onWorkerStart: a.onWorkerStart,\n worktreeFlag: a.worktreeFlag,\n className: a.className,\n _reroutes: a.reroutes + 1,\n });\n }\n }\n },\n });\n}\n\n/** The worktree fields printResult adds to a json payload, when there is one. */\nexport function worktreeExtras(s: WorkerStatus): Record<string, unknown> | undefined {\n return s.branch ? { branch: s.branch, commits: s.commits ?? 0 } : undefined;\n}\n\nexport interface Ag2ReaskArgs {\n env: NodeJS.ProcessEnv;\n cwd: string;\n model: string;\n callerPinnedModel: boolean;\n chromeArgs: string[];\n mcpArgs: string[];\n toolArgs: string[];\n toolsFlag: string[];\n sessionId: string;\n timeoutMs?: number;\n /** Argv head of the original launch (claudeCommand); defaults to plain claude. */\n claudeCmd?: string[];\n}\n\n/**\n * The one bounded re-ask, cap exactly one: `claude --resume <session>` with\n * AG2_REASK_TEXT, single-shot `--output-format json` (one turn, no streaming\n * plumbing needed). Same env/model/mcp/tool flags as the original launch —\n * only --resume and the fixed re-ask prompt are new — so the resumed turn\n * sees the same grants the worker ran with.\n */\nexport async function reaskAg2Report(a: Ag2ReaskArgs): Promise<string> {\n let cmd: string[] = [...(a.claudeCmd ?? [\"claude\"])];\n cmd.push(...modelFlagArgs(true, a.model, a.callerPinnedModel));\n cmd.push(...a.chromeArgs, ...a.mcpArgs, ...a.toolArgs, ...a.toolsFlag);\n cmd.push(\"--resume\", a.sessionId, \"-p\", AG2_REASK_TEXT, \"--output-format\", \"json\");\n cmd = ensureToolSearch(cmd);\n const proc = spawn(cmd[0], cmd.slice(1), { env: a.env, cwd: a.cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n const timer = setTimeout(() => {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n /* already gone */\n }\n }, a.timeoutMs ?? 60_000);\n let out = \"\";\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n out += chunk.toString(\"utf8\");\n });\n await new Promise<void>((resolve) => {\n proc.on(\"error\", () => resolve());\n proc.on(\"close\", () => resolve());\n });\n clearTimeout(timer);\n return resultFromOutput(out);\n}\n\n// ---------------------------------------------------------------------------\n// codex engine (2d)\n// ---------------------------------------------------------------------------\n\ninterface CodexArgs extends Omit<ExecuteArgs, \"reroutes\" | \"mcpFlag\"> {}\n\nasync function executeCodexRun(a: CodexArgs): Promise<number> {\n const { config, logDir, target, model, label, parsed, noPane } = a;\n if (!parsed.headless || parsed.prompt === null) {\n throw new Error(\n `provider \"${target.providerName}\" (engine codex) supports headless runs only: ` +\n `pass the task with -p '<prompt>'`\n );\n }\n const env = buildCodexEnv(target.provider);\n const wid = a.id ?? newWorkerId();\n const cwd = a.cwd ?? process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const session = resolveSession(term);\n const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n let worktree: WorktreeInfo | null = null;\n if (worktreeWanted(a.worktreeFlag, { cwd, allowedTools: parsed.allowedTools })) {\n try {\n worktree = addWorktree(logDir, wid, cwd);\n } catch (e) {\n const why = (e as Error).message;\n process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n }\n }\n env.PAI_WORKER_ID = wid;\n // codex takes instructions through the prompt, not a system prompt flag\n const prompt =\n (a.limit ? deadlinePromptLine(a.limit) + \"\\n\\n\" : \"\") +\n (worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) + \"\\n\\n\" : \"\") +\n parsed.prompt;\n\n let status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n ...parentInfo(),\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(),\n updated: nowStamp(),\n turns: 0,\n tools: 0,\n last: \"starting\",\n rc: null,\n secs: null,\n origin: \"spawn\",\n outputFormat: parsed.outputFormat,\n // codex takes instructions through the prompt, not the AG2 contract flag\n reportFormat: \"json\",\n ...(session ? { session } : {}),\n ...(spawnerSession ? { spawnerSession } : {}),\n // codex has no init event of its own: the synthetic one below announces\n // an explicitly configured window (never a guessed default)\n ...(target.provider.contextWindow ? { contextWindow: target.provider.contextWindow } : {}),\n ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n ...(worktree\n ? {\n worktreeDir: worktree.dir,\n branch: worktree.branch,\n worktreeBase: worktree.base,\n worktreeSnapshot: worktree.snapshot,\n }\n : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n ...(a.limit ? { deadlineAt: a.limit.deadlineAt, maxMinutes: a.limit.minutes } : {}),\n };\n saveStatus(logDir, status);\n a.onWorkerStart?.(wid);\n const ledger = ledgerPath(logDir);\n appendLedger(ledger, \"WORKER-START\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n engine: \"codex\",\n model,\n cwd,\n label,\n ...(a.capability ? { capability: a.capability } : {}),\n });\n const dropped = codexDroppedFlags(a.claudeArgs);\n if (dropped.length) {\n appendLedger(ledger, \"WORKER-NOTE\", {\n id: wid,\n note: `dropped for codex: ${dropped.join(\", \")}`,\n });\n }\n\n void announcePane(logDir, config, wid, term, noPane, ledger);\n\n const t0 = Date.now();\n const proc = spawn(\"codex\", buildCodexArgs(prompt, parsed.callerModel ? undefined : model), {\n env,\n cwd: worktree?.dir ?? cwd,\n stdio: [\"ignore\", \"pipe\", \"inherit\"],\n });\n\n const fold = emptyCodexResult();\n const eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n const writeEvent = (obj: Record<string, unknown>) => {\n try {\n writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n } catch {\n // best effort transcript\n }\n };\n writeEvent({\n type: \"system\",\n subtype: \"init\",\n model,\n cwd,\n ...(target.provider.contextWindow ? { context_window: target.provider.contextWindow } : {}),\n });\n\n let killed = false;\n const stopDeadline = a.limit ? armDeadline(proc, a.limit, () => fireDeadline(logDir, status, ledger, a.limit!)) : null;\n process.once(\"SIGTERM\", onCodexSignal(\"SIGTERM\"));\n process.once(\"SIGINT\", onCodexSignal(\"SIGINT\"));\n process.once(\"SIGHUP\", onCodexSignal(\"SIGHUP\"));\n function onCodexSignal(sig: string) {\n return () => {\n killed = true;\n status.state = \"killed\";\n status.rc = 143;\n status.secs = Math.floor((Date.now() - t0) / 1000);\n const end = recordSignalEnd(logDir, status, sig);\n status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : \"\"})`;\n saveStatus(logDir, status);\n appendLedger(ledger, \"WORKER-END\", {\n id: wid,\n provider: target.providerName,\n mode: \"headless\",\n model,\n rc: 143,\n secs: status.secs,\n killed: 1,\n ...endFields(end),\n note: end.note,\n label,\n });\n // same as the claude path: a killed run's worktree and branch go now\n if (worktree) {\n salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);\n try {\n recordWorktree(logDir, status, worktree, false);\n } catch {\n /* best effort: the status already says killed */\n }\n }\n try {\n proc.kill();\n } catch {\n /* already gone */\n }\n process.exit(143);\n };\n }\n\n const rl = createInterface({ input: proc.stdout! });\n rl.on(\"line\", (line) => {\n if (parsed.outputFormat === \"stream-json\") process.stdout.write(line + \"\\n\");\n const parsedLine = parseCodexLine(line);\n if (parsedLine === null) return;\n foldCodexLine(parsedLine, fold);\n if (fold.threadId && !status.claudeSession) status.claudeSession = fold.threadId;\n status.turns = fold.turns;\n status.tools = fold.tools;\n if (fold.last) status.last = shortText(fold.last, 90);\n bumpContextTokens(status, fold.contextTokens);\n saveStatus(logDir, status);\n for (const ev of fold.events.splice(0)) writeEvent(ev);\n });\n\n const rc = await new Promise<number>((resolve, reject) => {\n proc.on(\"error\", reject);\n proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n });\n stopDeadline?.();\n closeSync(eventsFd);\n\n const secs = Math.floor((Date.now() - t0) / 1000);\n const finalText = fold.finalText ?? \"\";\n const report = parseWorkerReport(finalText);\n const resultEvent: StreamEvent = {\n type: \"result\",\n result: finalText,\n is_error: fold.isError || rc !== 0,\n num_turns: fold.turns,\n duration_ms: secs * 1000,\n };\n writeEvent(resultEvent);\n\n const ok = rc === 0 && !fold.isError;\n status.last = shortText(report?.notes ?? finalText, 90) || (ok ? \"done\" : \"failed\");\n if (report?.changed?.length) {\n const verify = verifyReportChanges(report.changed, worktree?.dir ?? cwd, t0);\n if (!verify.ok) {\n status.reportValid = false;\n const note = verifyFailNote(verify);\n status.last = shortText(note, 90);\n report.notes = report.notes ? `${report.notes} — ${note}` : note;\n appendLedger(ledger, \"REPORT-UNVERIFIED\", {\n id: wid,\n failures: verify.failures.length,\n paths: verify.failures.map((f) => f.path).slice(0, 5).join(\",\"),\n });\n }\n }\n return finaliseRun({\n logDir,\n ledger,\n status,\n ok,\n rc,\n secs,\n end: {\n provider: target.providerName,\n mode: \"headless\",\n engine: \"codex\",\n model,\n turns: status.turns,\n tools: status.tools,\n label,\n },\n report,\n fallbackText: finalText,\n worktree,\n timedOut: status.timedOut ? a.limit?.minutes : undefined,\n beforeHandoff: (st, rep) => {\n if (!a.quiet) printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// result printing\n// ---------------------------------------------------------------------------\n\nexport function printResult(\n fmt: \"text\" | \"json\" | \"stream-json\",\n resultEvent: StreamEvent | null,\n rc: number,\n logDir: string,\n wid: string,\n report?: WorkerReport | null,\n extras?: Record<string, unknown>\n): void {\n if (fmt === \"stream-json\") return; // already mirrored live\n if (fmt === \"json\") {\n const payload = report\n ? { ...(resultEvent ?? { is_error: true, result: \"no result event\", rc }), report, ...extras }\n : { ...(resultEvent ?? { is_error: true, result: \"no result event\", rc }), ...extras };\n console.log(JSON.stringify(payload));\n return;\n }\n if (resultEvent) {\n console.log(resultEvent.result ?? \"\");\n } else {\n process.stderr.write(\n `pai worker: run produced no result (rc=${rc}); see ${eventsPath(logDir, wid)}\\n`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// providers test: the 90-second pong probe\n// ---------------------------------------------------------------------------\n\nexport interface ProviderTestResult {\n provider: string;\n model: string;\n latencyMs: number;\n result: string;\n ok: boolean;\n /** Set when the probe could not run (e.g. \"codex not installed\"). */\n skipped?: string;\n}\n\n/**\n * Run a one-word pong probe through the provider (headless, no pane) and\n * report provider, model, latency and the reply. ok is false unless the reply\n * was exactly \"pong\" (case-insensitive, whitespace-trimmed).\n */\nexport async function testProvider(\n providerName: string,\n provider: WorkerProvider,\n logDir: string,\n timeoutMs = 90_000,\n caveman = false\n): Promise<ProviderTestResult> {\n assertProviderRunnable(providerName, provider);\n const model = provider.models.default;\n\n if (provider.engine === \"codex\") {\n const skipped = codexInstalled() ? undefined : \"codex not installed\";\n return {\n provider: providerName,\n model,\n latencyMs: 0,\n result: skipped ?? \"codex engine: pong probe through codex exec not implemented\",\n ok: false,\n ...(skipped ? { skipped } : {}),\n };\n }\n\n let proxyUrl: string | undefined;\n if (provider.protocol === \"openai\") {\n const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n proxyUrl = `${base}/${providerName}`;\n }\n const env = buildRunEnv(provider, true, proxyUrl);\n const [bin, ...head] = claudeCommand(env, caveman);\n const t0 = Date.now();\n const proc = spawn(\n bin,\n [\n ...head,\n \"--model\", model,\n \"--strict-mcp-config\", \"--mcp-config\", ensureNoMcpConfig(logDir),\n \"-p\", \"Reply with exactly one word: pong\",\n \"--output-format\", \"json\",\n ],\n { env, stdio: [\"ignore\", \"pipe\", \"inherit\"] }\n );\n const timer = setTimeout(() => {\n try { proc.kill(\"SIGKILL\"); } catch { /* already gone */ }\n }, timeoutMs);\n\n let out = \"\";\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n out += chunk.toString(\"utf8\");\n });\n const rc = await new Promise<number>((resolve) => {\n proc.on(\"error\", () => resolve(1));\n proc.on(\"close\", (code) => resolve(code ?? 1));\n });\n clearTimeout(timer);\n\n return {\n provider: providerName,\n model,\n latencyMs: Date.now() - t0,\n result: resultFromOutput(out),\n ok: rc === 0 && resultFromOutput(out).trim().toLowerCase() === \"pong\",\n };\n}\n\n/**\n * Pull the reply out of claude's stdout. With --verbose, `--output-format\n * json` dumps a JSON array of stream events and the reply sits in the last\n * \"result\" event; without it, stdout is the single result object. Accept\n * either shape, plus a bare-text fallback for error output.\n */\nexport function resultFromOutput(out: string): string {\n const trimmed = out.trim();\n if (!trimmed) return \"\";\n const parse = (s: string): unknown => {\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n };\n const fromValue = (v: unknown): string | null => {\n if (Array.isArray(v)) {\n for (let i = v.length - 1; i >= 0; i--) {\n const r = fromValue(v[i]);\n if (r !== null) return r;\n }\n return null;\n }\n if (typeof v === \"object\" && v !== null) {\n const o = v as StreamEvent;\n if (o.type === \"result\" && typeof o.result === \"string\") return o.result;\n }\n return null;\n };\n const direct = fromValue(parse(trimmed));\n if (direct !== null) return direct;\n const lines = trimmed.split(\"\\n\");\n for (let i = lines.length - 1; i >= 0; i--) {\n const r = fromValue(parse(lines[i]));\n if (r !== null) return r;\n }\n return trimmed.slice(0, 200);\n}\n"],"mappings":"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"}
|