@tekmidian/pai 0.71.0 → 0.71.1
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/chain-CgTRLiOk.mjs +165 -0
- package/dist/chain-CgTRLiOk.mjs.map +1 -0
- package/dist/cli/index.mjs +8 -8
- package/dist/daemon/index.mjs +5 -5
- package/dist/daemon-DTkwXkGA.mjs +6773 -0
- package/dist/daemon-DTkwXkGA.mjs.map +1 -0
- package/dist/daemon-RceHUATC.mjs +37 -0
- package/dist/daemon-mcp/index.mjs +5 -5
- package/dist/hooks/worker-pool-host.mjs.map +2 -2
- package/dist/indexer-backend-1tkR2y1-.mjs +4188 -0
- package/dist/indexer-backend-1tkR2y1-.mjs.map +1 -0
- package/dist/pane-audit-C1QnR1vx.mjs +404 -0
- package/dist/pane-audit-C1QnR1vx.mjs.map +1 -0
- package/dist/planner-D57XwV4N.mjs +303 -0
- package/dist/planner-D57XwV4N.mjs.map +1 -0
- package/dist/pool-CRpTNNWq.mjs +413 -0
- package/dist/pool-CRpTNNWq.mjs.map +1 -0
- package/dist/pool-cli-Dt62g-Sd.mjs +143 -0
- package/dist/pool-cli-Dt62g-Sd.mjs.map +1 -0
- package/dist/project-setup-s3eywtRb.mjs +5475 -0
- package/dist/project-setup-s3eywtRb.mjs.map +1 -0
- package/dist/run-DwHfR1WP.mjs +7307 -0
- package/dist/run-DwHfR1WP.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-DwHfR1WP.mjs","names":["parseYaml","short"],"sources":["../src/workers/edit-assist.ts","../src/workers/note-delivery.ts","../src/router/supervisor.ts","../src/workers/loop-stop.ts","../src/workers/run-lock.ts","../src/workers/plan-status.ts","../src/workers/plan.ts","../src/workers/plan-runner.ts","../src/workers/run-events.ts","../src/workers/worker-trouble.ts","../src/workers/usage.ts","../src/workers/pane.ts","../src/workers/plan-deps.ts","../src/workers/run-report-wiring.ts","../src/workers/slim-profile.ts","../src/workers/controls.ts","../src/workers/policy.ts","../src/workers/session-name.ts","../src/workers/session-profile.ts","../src/workers/engines/image.ts","../src/workers/tree.ts","../src/workers/handoff.ts","../src/workers/denials.ts","../src/workers/contract-addendum.ts","../src/workers/result-grace.ts","../src/workers/deadline.ts","../src/workers/browser-tools.ts","../src/workers/project-config.ts","../src/workers/codex.ts","../src/workers/run.ts"],"sourcesContent":["export interface EditAssistInput {\n oldString: string;\n newString: string;\n errorText: string;\n fileText: string | null;\n}\n\n/**\n * Returns a human-readable hint for an edit error, or null if the error is not\n * one that this helper knows how to handle.\n */\nexport function editAssist(i: EditAssistInput): string | null {\n if (i.errorText.includes(\"No changes to make: old_string and new_string are exactly the same\")) {\n return \"old_string and new_string are identical: the file already contains your intended text; check whether this step is done (grep for it) before editing again.\";\n }\n\n if (i.errorText.includes(\"String to replace not found in file\") && i.fileText !== null) {\n // Split oldString into lines, drop blank (whitespace-only) lines; if none, return null.\n const lines = i.oldString.split(\"\\n\");\n const nonBlankLines = lines.filter(line => line.trim() !== \"\");\n if (nonBlankLines.length === 0) {\n return null;\n }\n\n // first = trimmed first non-blank line, last = trimmed last non-blank line.\n const first = nonBlankLines[0].trim();\n const last = nonBlankLines[nonBlankLines.length - 1].trim();\n\n // Split fileText into lines.\n const fileLines = i.fileText.split(\"\\n\");\n\n // Find the best matching file line index for `first` and for `last`\n const findBestMatch = (target: string): number | null => {\n let bestIndex: number | null = null;\n let bestSimilarity = -1;\n\n for (let i = 0; i < fileLines.length; i++) {\n const trimmedLine = fileLines[i].trim();\n if (trimmedLine === target) {\n return i; // Exact match wins\n }\n\n const similarity = computeSimilarity(target, trimmedLine);\n if (similarity >= 0.5 && similarity > bestSimilarity) {\n bestSimilarity = similarity;\n bestIndex = i;\n }\n }\n\n return bestIndex;\n };\n\n const firstIndex = findBestMatch(first);\n const lastIndex = findBestMatch(last);\n\n // Check if we're deleting\n const deleting = i.newString.trim() === \"\";\n\n // If neither anchor is found return \"old_string not found and no similar line exists in the file\"\n if (firstIndex === null && lastIndex === null) {\n let result = \"old_string not found and no similar line exists in the file; re-read the file (Read) before editing again.\";\n if (deleting) {\n result += \"\\nif you are deleting this, grep for it first: it may already be gone.\";\n }\n return result;\n }\n\n // Otherwise, determine the region\n let startIndex: number = firstIndex !== null ? firstIndex : lastIndex!;\n let endIndex: number = lastIndex !== null ? lastIndex : firstIndex!;\n\n // For `last`, prefer a match at an index >= the first's index when several tie\n if (lastIndex !== null && firstIndex !== null && lastIndex < firstIndex) {\n startIndex = lastIndex;\n endIndex = firstIndex;\n }\n\n // Widen by 3 lines of context each side (clamped), cap at 40 lines\n const context = 3;\n startIndex = Math.max(0, startIndex - context);\n endIndex = Math.min(fileLines.length - 1, endIndex + context);\n\n // Cap at 40 lines\n const regionLength = endIndex - startIndex + 1;\n if (regionLength > 40) {\n const half = Math.floor(40 / 2);\n startIndex = Math.max(0, (firstIndex ?? lastIndex ?? 0) - half);\n endIndex = Math.min(fileLines.length - 1, startIndex + 39);\n }\n\n // Render the region\n const regionLines = [];\n for (let i = startIndex; i <= endIndex; i++) {\n regionLines.push(`${i + 1}\\t${fileLines[i]}`);\n }\n\n const header = `closest region of the file (lines ${startIndex + 1}-${endIndex + 1}), copy old_string from here exactly, including whitespace:`;\n return `${header}\\n${regionLines.join(\"\\n\")}${deleting ? \"\\nif you are deleting this, grep for it first: it may already be gone.\" : \"\"}`;\n }\n\n // Deletion hint for when the edit is a deletion\n if (i.errorText.includes(\"String to replace not found in file\") && i.fileText === null) {\n if (i.newString.trim() === \"\") {\n return \"if you are deleting this, grep for it first: it may already be gone.\";\n }\n return null;\n }\n\n // Any other errorText returns null\n return null;\n}\n\n/**\n * Computes similarity between two strings based on longest common prefix and suffix.\n * Similarity = (length of longest common prefix + length of longest common suffix) / max(length of both strings)\n */\nfunction computeSimilarity(s1: string, s2: string): number {\n if (s1 === s2) return 1;\n if (s1.length === 0 || s2.length === 0) return 0;\n\n // Longest common prefix\n let prefixLength = 0;\n const minLength = Math.min(s1.length, s2.length);\n for (let i = 0; i < minLength; i++) {\n if (s1[i] === s2[i]) {\n prefixLength++;\n } else {\n break;\n }\n }\n\n // Longest common suffix\n let suffixLength = 0;\n const s1Reversed = s1.split('').reverse().join('');\n const s2Reversed = s2.split('').reverse().join('');\n for (let i = 0; i < minLength; i++) {\n if (s1Reversed[i] === s2Reversed[i]) {\n suffixLength++;\n } else {\n break;\n }\n }\n\n const totalLength = Math.max(s1.length, s2.length);\n return totalLength > 0 ? (prefixLength + suffixLength) / totalLength : 0;\n}\n\n/**\n * Checks if an error text is one that editAssist can handle.\n */\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport function isEditAssistError(errorText: string): boolean {\n return errorText.includes(\"No changes to make: old_string and new_string are exactly the same\") ||\n errorText.includes(\"String to replace not found in file\");\n}\n\n/** Hint for a failed Edit tool call: reads the target file under cwd and delegates to editAssist; null when it has nothing to add. */\nexport function editHintFor(input: unknown, errorText: string, cwd: string): string | null {\n if (!isEditAssistError(errorText) || typeof input !== \"object\" || input === null) return null;\n const i = input as Record<string, unknown>;\n const oldString = typeof i.old_string === \"string\" ? i.old_string : \"\";\n const newString = typeof i.new_string === \"string\" ? i.new_string : \"\";\n let fileText: string | null = null;\n if (typeof i.file_path === \"string\") {\n try { fileText = readFileSync(resolve(cwd, i.file_path), \"utf8\"); } catch { fileText = null; }\n }\n return editAssist({ oldString, newString, errorText, fileText });\n}","/**\n * note-delivery.ts — hand `pai worker goal` notes to the running worker.\n *\n * `goal` only stores the note in the status file; nothing reads it back into\n * the worker's conversation. The PostToolUse hook (hooks/ts/post-tool-use/\n * worker-note.ts) calls takePendingNotes after every tool call: each note is\n * returned exactly once and the delivery time is recorded for `pai worker ps`.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { loadStatus, nowStamp, saveStatus } from \"./status.js\";\n\n/** The pending notes of a worker, cleared and stamped as delivered; null when there are none. */\nexport function takePendingNotes(logDir: string, id: string, d: Date = new Date()): string[] | null {\n const status = loadStatus(logDir, id);\n if (!status?.notePending?.length) return null;\n const notes = status.notePending;\n status.notePending = [];\n status.noteDeliveredAt = nowStamp(d);\n saveStatus(logDir, status, d, true);\n return notes;\n}\n\n/** Env var carrying the per-run note token from the runner to the worker's hooks. */\nexport const NOTE_TOKEN_ENV = \"PAI_WORKER_NOTE_TOKEN\";\n\n/** A fresh random per-run token (8 hex chars). */\nexport function newNoteToken(): string {\n return randomBytes(4).toString(\"hex\");\n}\n\n/** The additionalContext text for the delivered notes, each authenticated with the run's token. */\nexport function noteContext(notes: string[], token: string): string {\n return notes.map((n) => `PAI note [${token}]: ${n}`).join(\"\\n\");\n}\n","/**\n * Keeps `pai router` running under the daemon: probe the port each tick and\n * start the router when it is down. Opt-in via `router.enabled` (config).\n */\n\nimport { spawn } from \"node:child_process\";\nimport { closeSync, existsSync, mkdirSync, openSync, readFileSync } from \"node:fs\";\nimport { paiHomePath } from \"../config/pai-home.js\";\nimport { dirname } from \"node:path\";\nimport { connect } from \"node:net\";\nimport { pidAlive } from \"../workers/status.js\";\nimport { ROUTER_PORT } from \"./router.js\";\n\nexport const routerPidFile = (): string => paiHomePath(\"router.pid\");\nexport const routerLogFile = (): string => paiHomePath(\"logs\", \"router.log\");\n\nexport function routerRunningPid(): number | null {\n if (!existsSync(routerPidFile())) return null;\n const pid = parseInt(readFileSync(routerPidFile(), \"utf8\"), 10);\n if (!pid) return null;\n return pidAlive(pid) ? pid : null;\n}\n\n/** Spawn `<cli> router run` detached, logging to the router log. Returns the child pid. */\nexport function spawnRouter(cliEntry: string, extraArgs: string[] = []): number | undefined {\n mkdirSync(dirname(routerLogFile()), { recursive: true });\n const fd = openSync(routerLogFile(), \"a\");\n const child = spawn(process.execPath, [cliEntry, \"router\", \"run\", ...extraArgs], {\n detached: true,\n stdio: [\"ignore\", fd, fd],\n });\n closeSync(fd);\n child.unref();\n return child.pid;\n}\n\n/** True when something accepts TCP connections on the router port. */\nexport function probeRouter(port = ROUTER_PORT, timeoutMs = 2000): Promise<boolean> {\n return new Promise((resolve) => {\n const sock = connect({ host: \"127.0.0.1\", port });\n const done = (up: boolean) => {\n sock.destroy();\n resolve(up);\n };\n sock.setTimeout(timeoutMs, () => done(false));\n sock.once(\"connect\", () => done(true));\n sock.once(\"error\", () => done(false));\n });\n}\n\nexport interface RouterTickDeps {\n probe: () => Promise<boolean>;\n start: () => void;\n}\n\n/** One health check: starts the router when down, never replaces a live router. */\nexport async function runRouterTick(deps: RouterTickDeps): Promise<\"up\" | \"started\"> {\n if (await deps.probe()) {\n return \"up\";\n }\n deps.start();\n return \"started\";\n}\n","/**\n * loop-stop.ts — the loop guard's hard stop, made independent of Claude Code.\n *\n * `\"continue\": false` from a PreToolUse hook does not end a headless\n * (`claude -p`) session, so the hook also leaves a marker the run's finalise\n * step reads (the worker then ends failed with reason \"loop-stopped\") and\n * signals the worker's own claude process.\n */\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadStatus, saveStatus } from \"./status.js\";\nimport { buildsDir, releaseBuildsOf } from \"../builds/queue.js\";\n\nexport const loopStopMarkerPath = (logDir: string, id: string): string => join(logDir, `${id}.loop-stop`);\n\nexport interface LoopStopMarker {\n ts: string;\n reason: string;\n}\n\nexport interface LoopStopDeps {\n kill?: (pid: number, signal: NodeJS.Signals) => void;\n /** Pid of the claude process the hook runs under; default: the hook's parent, skipping a wrapping shell. */\n claudePid?: () => number | undefined;\n}\n\nconst SHELLS = new Set([\"sh\", \"bash\", \"zsh\", \"dash\"]);\n\nfunction psField(pid: number, field: \"comm\" | \"ppid\"): string | undefined {\n try {\n return execFileSync(\"ps\", [\"-o\", `${field}=`, \"-p\", String(pid)], { encoding: \"utf8\" }).trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The hook's parent, or its parent's parent when the hook was started through a shell. */\nexport function defaultClaudePid(): number | undefined {\n let pid = process.ppid;\n const comm = psField(pid, \"comm\")?.split(\"/\").pop() ?? \"\";\n if (SHELLS.has(comm.replace(/^-/, \"\"))) {\n const up = Number(psField(pid, \"ppid\"));\n if (up > 1) pid = up;\n }\n return pid > 1 ? pid : undefined;\n}\n\n/** Mark the worker loop-stopped (marker file + status note) and SIGTERM its claude process. Never throws. */\nexport function requestLoopStop(logDir: string, id: string, reason: string, deps: LoopStopDeps = {}): void {\n const { kill = (pid, sig) => process.kill(pid, sig), claudePid = defaultClaudePid } = deps;\n try {\n const m: LoopStopMarker = { ts: new Date().toISOString(), reason };\n writeFileSync(loopStopMarkerPath(logDir, id), JSON.stringify(m));\n } catch {\n // the signal below still ends the run\n }\n try {\n const st = loadStatus(logDir, id);\n if (st && st.state === \"running\") saveStatus(logDir, { ...st, last: `loop-stopped: ${reason}` });\n } catch {\n // best effort\n }\n try {\n const pid = claudePid();\n if (pid) kill(pid, \"SIGTERM\");\n } catch {\n // process already gone\n }\n // a loop-stopped worker frees the machine-wide build lock and kills its build tree\n void releaseBuildsOf(buildsDir(), id).catch(() => []);\n}\n\n/** The marker of a loop-stopped worker, consumed on read; null when there is none. */\nexport function takeLoopStop(logDir: string, id: string): LoopStopMarker | null {\n const p = loopStopMarkerPath(logDir, id);\n if (!existsSync(p)) return null;\n try {\n return JSON.parse(readFileSync(p, \"utf8\")) as LoopStopMarker;\n } catch {\n return { ts: \"\", reason: \"loop guard\" };\n } finally {\n try {\n unlinkSync(p);\n } catch {\n // already gone\n }\n }\n}\n\n/** The marker without consuming it. */\nexport function peekLoopStop(logDir: string, id: string): LoopStopMarker | null {\n const p = loopStopMarkerPath(logDir, id);\n if (!existsSync(p)) return null;\n try {\n return JSON.parse(readFileSync(p, \"utf8\")) as LoopStopMarker;\n } catch {\n return { ts: \"\", reason: \"loop guard\" };\n }\n}\n","/**\n * run-lock.ts — one plan or queue run at a time. The run writes a lock file\n * with its pid and start time and removes it on exit; a lock whose pid is dead\n * is taken over.\n */\n\nimport { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { pidAlive } from \"./status.js\";\n\nexport interface RunLock {\n pid: number;\n startedAt: number;\n}\n\nexport const planLockPath = (planPath: string): string => `${planPath}.lock`;\nexport const queueLockPath = (queueStatePath: string): string => `${queueStatePath}.lock`;\n\nconst hhmm = (ms: number): string => {\n const d = new Date(ms);\n return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n};\nexport const sinceText = (l: RunLock): string => `since ${hhmm(l.startedAt)} (pid ${l.pid})`;\n\nexport class RunLockedError extends Error {\n constructor(readonly what: string, readonly lock: RunLock) {\n super(`a run of ${what} is already in progress since ${hhmm(lock.startedAt)}`);\n }\n}\n\n/** The lock of a live run, or null (no file, unreadable, or a dead pid). */\nexport function readRunLock(path: string, isAlive: (pid: number) => boolean = (p) => pidAlive(p)): RunLock | null {\n if (!existsSync(path)) return null;\n try {\n const l = JSON.parse(readFileSync(path, \"utf8\")) as RunLock;\n return Number.isInteger(l.pid) && l.pid > 0 && typeof l.startedAt === \"number\" && isAlive(l.pid) ? l : null;\n } catch {\n return null;\n }\n}\n\nexport interface LockDeps {\n pid?: number;\n now?: () => number;\n isAlive?: (pid: number) => boolean;\n /** Runs on SIGTERM/SIGINT before the lock is released and the process exits (stop workers, write the run-end). */\n onSignal?: (sig: NodeJS.Signals) => Promise<void>;\n /** Exit hook; tests replace process.exit. */\n exit?: (code: number) => void;\n}\n\n/** Run `fn` holding the lock at `path`; refuses when a live run holds it. `what` names the run in the message. */\nexport async function withRunLock<T>(path: string | undefined, what: string, fn: () => Promise<T>, deps: LockDeps = {}): Promise<T> {\n if (!path) return fn();\n const pid = deps.pid ?? process.pid;\n const held = readRunLock(path, deps.isAlive);\n if (held && held.pid !== pid) throw new RunLockedError(what, held);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify({ pid, startedAt: (deps.now ?? Date.now)() } satisfies RunLock), \"utf8\");\n const release = (): void => {\n try {\n const l = JSON.parse(readFileSync(path, \"utf8\")) as RunLock;\n if (l.pid === pid) unlinkSync(path);\n } catch {\n /* already gone */\n }\n };\n let signalled = false;\n const onSignal = (sig: NodeJS.Signals): void => {\n if (signalled) return;\n signalled = true;\n void (async () => {\n try {\n await deps.onSignal?.(sig);\n } catch {\n /* the exit must happen anyway */\n }\n release();\n (deps.exit ?? process.exit)(128 + (sig === \"SIGTERM\" ? 15 : 2));\n })();\n };\n process.on(\"SIGTERM\", onSignal);\n process.on(\"SIGINT\", onSignal);\n try {\n return await fn();\n } finally {\n process.off(\"SIGTERM\", onSignal);\n process.off(\"SIGINT\", onSignal);\n release();\n }\n}\n","/**\n * plan-status.ts — the live status entry of a plan run (kind \"plan\").\n *\n * The runner's own work between two workers (merge, gate, unit check,\n * bookkeeping) has no worker to show it. This entry carries the current phase\n * and its start time so `pai worker ps`, the cockpit and plan_status can; the\n * live log beside it feeds the plan pane (`pai worker follow plan-<job>`).\n * Kept apart from the worker `.status` files: nothing that walks workers has\n * to know about it.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { nowStamp, pidAlive } from \"./status.js\";\n\nexport interface PlanRunStatus {\n kind: \"plan\";\n /** `plan-<slug of job>`: also the id the plan pane follows. */\n id: string;\n job: string;\n pid: number;\n started: string;\n updated: string;\n state: \"running\" | \"done\" | \"failed\" | \"stopped\" | \"lost\";\n phase: string;\n /** Epoch ms the current phase began. */\n phaseSince: number;\n parts: { merged: number; total: number };\n /** Ids of the parts whose worker runs now. */\n running: string[];\n /** Worker id per part id, for every worker the run started: the run's workers (each in its own pane). */\n workers?: Record<string, string>;\n /** True once the runner's own pane is open (its workers each open theirs). */\n hasPane?: boolean;\n /** Tests the plan gate counted last time: the denominator of \"tests 214/2600\". */\n testTotal?: number;\n /** Local model idle ms between workers and overlapped part starts so far. */\n idleMs?: number;\n overlaps?: number;\n}\n\nexport const planRunId = (job: string): string => `plan-${job.replace(/[^A-Za-z0-9._-]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"job\"}`;\nexport const planStatusFile = (logDir: string, id: string): string => join(logDir, `${id}.planstatus`);\nexport const planLiveFile = (logDir: string, id: string): string => join(logDir, `${id}.planlog`);\n\nexport function writePlanStatus(logDir: string, s: PlanRunStatus): void {\n s.updated = nowStamp();\n mkdirSync(logDir, { recursive: true });\n const path = planStatusFile(logDir, s.id);\n const tmp = `${path}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(s), \"utf8\");\n renameSync(tmp, path);\n}\n\nexport function loadPlanStatus(logDir: string, id: string): PlanRunStatus | null {\n try {\n const s = JSON.parse(readFileSync(planStatusFile(logDir, id), \"utf8\")) as PlanRunStatus;\n return s.state === \"running\" && !pidAlive(s.pid) ? { ...s, state: \"lost\" } : s;\n } catch {\n return null;\n }\n}\n\nexport function loadPlanStatuses(logDir: string): PlanRunStatus[] {\n if (!existsSync(logDir)) return [];\n return readdirSync(logDir)\n .filter((n) => n.endsWith(\".planstatus\"))\n .sort()\n .map((n) => loadPlanStatus(logDir, n.slice(0, -\".planstatus\".length)))\n .filter((s): s is PlanRunStatus => !!s);\n}\n\nexport function fmtElapsed(ms: number): string {\n const s = Math.max(0, Math.round(ms / 1000));\n return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m${String(s % 60).padStart(2, \"0\")}s`;\n}\n\n/** \"local model idle 1m02s of 7m24s (14%), 2 overlapped starts\". */\nexport function idleLine(idleMs: number, wallMs: number, overlaps: number): string {\n const pct = wallMs > 0 ? Math.round((idleMs / wallMs) * 100) : 0;\n return `local model idle ${fmtElapsed(idleMs)} of ${fmtElapsed(wallMs)} (${pct}%), ${overlaps} overlapped ${overlaps === 1 ? \"start\" : \"starts\"}`;\n}\n\n/** ps lines of the running plans: one per plan, phase and how long it has lasted. */\nexport function renderPlanLines(statuses: PlanRunStatus[], now = Date.now()): string[] {\n return statuses\n .filter((s) => s.state === \"running\")\n .map((s) => `plan ${s.job} ${s.parts.merged}/${s.parts.total} merged ${s.phase} (${fmtElapsed(now - s.phaseSince)})${s.running.length ? ` workers: ${s.running.join(\", \")}` : \"\"}${s.idleMs !== undefined ? ` ${idleLine(s.idleMs, now - Date.parse(s.started.replace(\" \", \"T\")), s.overlaps ?? 0)}` : \"\"}`);\n}\n\n/** Cockpit rows of the running plans. */\nexport function planRows(statuses: PlanRunStatus[], now = Date.now()): Array<{ id: string; job: string; phase: string; phaseSec: number; ageSec: number; merged: number; total: number; running: string[] }> {\n return statuses\n .filter((s) => s.state === \"running\")\n .map((s) => ({\n id: s.id,\n job: s.job,\n phase: s.phase,\n phaseSec: Math.max(0, Math.round((now - s.phaseSince) / 1000)),\n ageSec: Math.max(0, Math.round((now - Date.parse(s.started.replace(\" \", \"T\"))) / 1000)) || 0,\n merged: s.parts.merged,\n total: s.parts.total,\n running: s.running,\n }));\n}\n\n/** The phase a gate command is shown as: \"tsc\", \"build\", \"tests\", else its first words. */\nexport function gatePhaseName(cmd: string): string {\n if (/\\btsc\\b/.test(cmd)) return \"tsc\";\n if (/\\b(tsdown|build)\\b/.test(cmd)) return \"build\";\n if (/\\b(vitest|jest|test)\\b/.test(cmd)) return \"tests\";\n return cmd.trim().split(/\\s+/).slice(0, 3).join(\" \").slice(0, 30);\n}\n\n/** Owns the status entry and the live log of one run; every change goes to both. */\nexport class PlanTracker {\n private status: PlanRunStatus;\n private lastWrite = 0;\n\n constructor(private logDir: string, job: string, total: number, merged: number, private now: () => number = Date.now) {\n const id = planRunId(job);\n const prev = loadPlanStatus(logDir, id);\n this.status = {\n kind: \"plan\",\n id,\n job,\n pid: process.pid,\n started: nowStamp(new Date(now())),\n updated: \"\",\n state: \"running\",\n phase: \"starting\",\n phaseSince: now(),\n parts: { merged, total },\n running: [],\n idleMs: 0,\n overlaps: 0,\n ...(prev?.testTotal ? { testTotal: prev.testTotal } : {}),\n };\n mkdirSync(logDir, { recursive: true });\n writeFileSync(planLiveFile(logDir, id), \"\", \"utf8\");\n this.log(`plan ${job} started: ${merged}/${total} parts already merged`);\n this.save();\n }\n\n get id(): string {\n return this.status.id;\n }\n\n get testTotal(): number | undefined {\n return this.status.testTotal;\n }\n\n /** One timestamped line in the live log. */\n log(line: string): void {\n const t = new Date(this.now()).toTimeString().slice(0, 8);\n try {\n appendFileSync(planLiveFile(this.logDir, this.status.id), line.split(\"\\n\").map((l) => `${t} ${l}`).join(\"\\n\") + \"\\n\", \"utf8\");\n } catch {\n /* the log is a view: never fail the run over it */\n }\n }\n\n private save(): void {\n try {\n writePlanStatus(this.logDir, this.status);\n } catch {\n /* same */\n }\n }\n\n /** A new phase: written to the status entry and the live log. */\n phase(text: string): void {\n if (text === this.status.phase) return;\n this.status.phase = text;\n this.status.phaseSince = this.now();\n this.lastWrite = this.now();\n this.log(`phase: ${text}`);\n this.save();\n }\n\n /** A progress refinement of the current phase (e.g. test counts): status only, at most once a second. */\n progress(text: string): void {\n if (text === this.status.phase || this.now() - this.lastWrite < 1000) return;\n this.status.phase = text;\n this.lastWrite = this.now();\n this.save();\n }\n\n setRunning(ids: string[]): void {\n this.status.running = ids;\n this.save();\n }\n\n /** True once the runner's own pane is open (its workers each open theirs). */\n get hasPane(): boolean {\n return this.status.hasPane === true;\n }\n\n set hasPane(v: boolean) {\n this.status.hasPane = v;\n this.save();\n }\n\n setWorker(part: string, wid: string): void {\n this.status.workers = { ...this.status.workers, [part]: wid };\n this.save();\n }\n\n merged(n: number): void {\n this.status.parts.merged = n;\n this.save();\n }\n\n /** model-idle accounting from the run log: added to the status entry. */\n addIdle(ms: number): void {\n this.status.idleMs = (this.status.idleMs ?? 0) + ms;\n this.save();\n }\n\n overlapped(): void {\n this.status.overlaps = (this.status.overlaps ?? 0) + 1;\n this.status.idleMs ??= 0;\n this.save();\n }\n\n setTestTotal(n: number): void {\n if (n > 0) this.status.testTotal = n;\n }\n\n end(state: \"done\" | \"failed\" | \"stopped\"): void {\n this.status.state = state;\n this.status.phase = state;\n this.status.phaseSince = this.now();\n this.status.running = [];\n this.log(`plan ${this.status.job} ${state}`);\n this.save();\n }\n}\n\n/** The last `n` lines of a plan's live log. */\nexport function planLogTail(logDir: string, id: string, n = 40): string {\n try {\n return readFileSync(planLiveFile(logDir, id), \"utf8\").trimEnd().split(\"\\n\").slice(-n).join(\"\\n\");\n } catch {\n return `no live log for ${id}`;\n }\n}\n\n/** True once the plan's runner has ended, been killed or vanished: its status is not \"running\" any more. */\nexport function planRunEnded(logDir: string, id: string): boolean {\n const s = loadPlanStatus(logDir, id);\n return !s || s.state !== \"running\";\n}\n\n/** Stream the live log into `write` until `stop()` is true; the file's whole content first, then what is appended. */\nexport async function followPlanLog(logDir: string, id: string, write: (s: string) => void, stop: () => boolean = () => false, pollMs = 500): Promise<void> {\n let at = 0;\n for (;;) {\n const done = stop(); // asked before the read, so the last lines are still shown\n try {\n const text = readFileSync(planLiveFile(logDir, id), \"utf8\");\n if (text.length > at) {\n write(text.slice(at));\n at = text.length;\n }\n } catch {\n /* not written yet */\n }\n if (done) return;\n await new Promise((r) => setTimeout(r, pollMs));\n }\n}\n","/**\n * plan.ts — the plan file of `pai plan run`: parse, validate, compose specs.\n *\n * A plan is YAML (see docs/plan-runner.md). Pure functions only; reading the\n * file and the spec files is the caller's job except `loadPlan`, which does\n * both and resolves relative paths against the plan's directory.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\n\nexport interface PlanPart {\n id: string;\n unit: string;\n /** Spec text, preamble(s) already prepended. */\n spec: string;\n files: string[];\n proof?: string;\n dependsOn: string[];\n risky: boolean;\n /** Orchestrator-marked: may start its worker while the previous part's worker has ended but its gate/merge still runs. */\n overlap?: boolean;\n}\n\nexport interface PlanUnit {\n id: string;\n unitCheck?: string;\n parts: PlanPart[];\n}\n\nexport interface Plan {\n job: string;\n repo: string;\n gate: string;\n provider: string;\n /** Explicit max_parallel; undefined = the provider's maxConcurrent. */\n maxParallel?: number;\n units: PlanUnit[];\n /** Non-fatal findings of parsePlan, shown by `pai plan validate`. */\n warnings: string[];\n}\n\n/** JSON-schema-like description of the plan file; `pai plan validate` enforces it. */\nexport const PLAN_SCHEMA = {\n type: \"object\",\n required: [\"job\", \"repo\", \"gate\", \"provider\", \"units\"],\n properties: {\n job: \"string — job name, the prefix of every worker label\",\n repo: \"string — path of the git checkout the workers change (relative to the plan file)\",\n gate: \"string — shell command run on the merged tree; a failure reverts the merge\",\n provider: \"string — local provider the parts run on (--class implement)\",\n max_parallel: \"integer >= 1 — parts running at once (default: the provider's maxConcurrent, else 3)\",\n preamble: \"string — text or file path prepended to every part spec\",\n units: {\n type: \"array\",\n minItems: 1,\n items: {\n required: [\"id\", \"parts\"],\n properties: {\n id: \"string — unit name\",\n preamble: \"string — text or file path prepended to this unit's part specs (after the plan preamble)\",\n unit_check: \"string — command run after the unit's last part merged; failure = needs-decision\",\n parts: {\n type: \"array\",\n minItems: 1,\n items: {\n required: [\"id\", \"spec\"],\n properties: {\n id: \"string — unique in the whole plan\",\n spec: \"string — task text, or a path to a file holding it\",\n files: \"string[] — paths the part touches; parts sharing a file never run together\",\n proof: \"string — command the worker must run and show passing\",\n depends_on: \"string[] — part ids (any unit) that must be merged first\",\n risky: \"boolean — documented intent for review; shown in the schedule\",\n overlap: \"boolean — default false; the part is independent of the part before it, so its worker may start as soon as that part's worker has ended, while its gate and merge still run (its merge waits for it). Rejected when it depends on that part\",\n },\n },\n },\n },\n },\n },\n },\n} as const;\n\nexport class PlanError extends Error {\n constructor(readonly problems: string[]) {\n super(problems.join(\"\\n\"));\n }\n}\n\ntype Obj = Record<string, unknown>;\nconst isObj = (v: unknown): v is Obj => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** `text` as given, or the content of the file it names (single line naming an existing file). */\nfunction textOrFile(value: string, baseDir: string): string {\n const v = value.trim();\n if (v && !v.includes(\"\\n\")) {\n const p = isAbsolute(v) ? v : resolve(baseDir, v);\n if (existsSync(p)) {\n try {\n return readFileSync(p, \"utf8\").trim();\n } catch {\n /* a directory or unreadable file: keep the text */\n }\n }\n }\n return v;\n}\n\n/** A spec value that looks like a path (no spaces, has an extension or slash) but names no file. */\nfunction missingFile(value: string, baseDir: string): boolean {\n const v = value.trim();\n if (!v || /\\s/.test(v)) return false;\n if (!/[\\\\/]|\\.(md|txt|spec)$/.test(v)) return false;\n return !existsSync(isAbsolute(v) ? v : resolve(baseDir, v));\n}\n\nfunction strList(v: unknown, where: string, problems: string[]): string[] {\n if (v === undefined || v === null) return [];\n if (!Array.isArray(v) || v.some((x) => typeof x !== \"string\" || !x)) {\n problems.push(`${where}: must be a list of non-empty strings`);\n return [];\n }\n return v as string[];\n}\n\n/** Parse and validate plan text. Throws PlanError listing every problem. */\nexport function parsePlan(text: string, baseDir: string): Plan {\n const problems: string[] = [];\n let raw: unknown;\n try {\n raw = parseYaml(text);\n } catch (e) {\n throw new PlanError([`not valid YAML: ${e instanceof Error ? e.message : String(e)}`]);\n }\n if (!isObj(raw)) throw new PlanError([\"the plan must be a YAML mapping\"]);\n const reqStr = (o: Obj, k: string, where: string): string => {\n const v = o[k];\n if (typeof v !== \"string\" || !v.trim()) {\n problems.push(`${where}${k}: required, a non-empty string`);\n return \"\";\n }\n return v.trim();\n };\n const job = reqStr(raw, \"job\", \"\");\n const repo = reqStr(raw, \"repo\", \"\");\n const gate = reqStr(raw, \"gate\", \"\");\n const provider = reqStr(raw, \"provider\", \"\");\n let maxParallel: number | undefined;\n if (raw.max_parallel !== undefined) {\n if (typeof raw.max_parallel !== \"number\" || !Number.isInteger(raw.max_parallel) || raw.max_parallel < 1) {\n problems.push(\"max_parallel: must be an integer >= 1\");\n } else maxParallel = raw.max_parallel;\n }\n const planPre = typeof raw.preamble === \"string\" ? raw.preamble : undefined;\n if (raw.preamble !== undefined && planPre === undefined) problems.push(\"preamble: must be a string\");\n if (planPre && missingFile(planPre, baseDir)) problems.push(`preamble: file not found: ${planPre}`);\n\n const units: PlanUnit[] = [];\n const seen = new Map<string, string>();\n if (!Array.isArray(raw.units) || raw.units.length === 0) problems.push(\"units: required, a non-empty list\");\n else {\n raw.units.forEach((u, ui) => {\n const uw = `units[${ui}].`;\n if (!isObj(u)) return void problems.push(`units[${ui}]: must be a mapping`);\n const uid = reqStr(u, \"id\", uw);\n const unitPre = typeof u.preamble === \"string\" ? u.preamble : undefined;\n if (u.preamble !== undefined && unitPre === undefined) problems.push(`${uw}preamble: must be a string`);\n if (unitPre && missingFile(unitPre, baseDir)) problems.push(`${uw}preamble: file not found: ${unitPre}`);\n if (u.unit_check !== undefined && (typeof u.unit_check !== \"string\" || !u.unit_check.trim())) problems.push(`${uw}unit_check: must be a non-empty string`);\n const parts: PlanPart[] = [];\n if (!Array.isArray(u.parts) || u.parts.length === 0) problems.push(`${uw}parts: required, a non-empty list`);\n else {\n u.parts.forEach((p, pi) => {\n const pw = `${uw}parts[${pi}].`;\n if (!isObj(p)) return void problems.push(`${uw}parts[${pi}]: must be a mapping`);\n const id = reqStr(p, \"id\", pw);\n const spec = reqStr(p, \"spec\", pw);\n if (spec && missingFile(spec, baseDir)) problems.push(`${pw}spec: file not found: ${spec}`);\n if (id) {\n if (seen.has(id)) problems.push(`${pw}id: duplicate part id \"${id}\" (also in unit ${seen.get(id)})`);\n else seen.set(id, uid);\n }\n if (p.proof !== undefined && typeof p.proof !== \"string\") problems.push(`${pw}proof: must be a string`);\n if (p.overlap !== undefined && typeof p.overlap !== \"boolean\") problems.push(`${pw}overlap: must be a boolean`);\n if (p.risky !== undefined && typeof p.risky !== \"boolean\") problems.push(`${pw}risky: must be a boolean`);\n const pieces = [planPre, unitPre, spec].filter((x): x is string => !!x).map((x) => textOrFile(x, baseDir));\n parts.push({\n id,\n unit: uid,\n spec: pieces.join(\"\\n\\n\"),\n files: strList(p.files, `${pw}files`, problems),\n ...(typeof p.proof === \"string\" && p.proof.trim() ? { proof: p.proof.trim() } : {}),\n dependsOn: strList(p.depends_on, `${pw}depends_on`, problems),\n risky: p.risky === true,\n ...(p.overlap === true ? { overlap: true } : {}),\n });\n });\n }\n units.push({ id: uid, ...(typeof u.unit_check === \"string\" && u.unit_check.trim() ? { unitCheck: u.unit_check.trim() } : {}), parts });\n });\n }\n const unitIds = units.map((u) => u.id).filter(Boolean);\n unitIds.forEach((id, i) => {\n if (unitIds.indexOf(id) !== i) problems.push(`units: duplicate unit id \"${id}\"`);\n });\n const all = units.flatMap((u) => u.parts);\n for (const p of all) {\n for (const d of p.dependsOn) {\n if (d === p.id) problems.push(`part ${p.id}: depends on itself`);\n else if (!seen.has(d)) problems.push(`part ${p.id}: depends_on unknown part \"${d}\"`);\n }\n }\n const byId = new Map(all.map((p) => [p.id, p]));\n all.forEach((p, i) => {\n if (!p.overlap) return;\n const prev = all[i - 1];\n if (!prev) return void problems.push(`part ${p.id}: overlap: true on the first part of the plan; there is no earlier part to overlap`);\n const via = dependsVia(p, prev.id, byId);\n if (via) problems.push(`part ${p.id}: overlap: true but it depends on ${prev.id}, the part before it that is still being gated when it would start${via.length > 1 ? ` (via ${via.slice(0, -1).join(\" -> \")})` : \"\"}; remove overlap or the dependency`);\n });\n const cycle = findCycle(all);\n if (cycle) problems.push(`dependency cycle: ${cycle.join(\" -> \")}`);\n if (problems.length) throw new PlanError([...new Set(problems)]);\n const repoAbs = isAbsolute(repo) ? repo : resolve(baseDir, repo);\n const warnings: string[] = [];\n for (const p of all) {\n if (mentionsRepoPath(p.spec, repoAbs)) warnings.push(`part ${p.id}: spec names an absolute path into the repo (${repoAbs}); it is rewritten to a relative path, workers must stay in their worktree`);\n if (p.files.some((f) => mentionsRepoPath(f, repoAbs))) warnings.push(`part ${p.id}: files lists an absolute path into the repo; use paths relative to the repo`);\n if (p.proof && mentionsRepoPath(p.proof, repoAbs)) warnings.push(`part ${p.id}: proof names an absolute path into the repo; it is rewritten to a relative path`);\n p.files = p.files.map((f) => stripRepoPaths(f, repoAbs));\n }\n return { job, repo: repoAbs, gate, provider, ...(maxParallel ? { maxParallel } : {}), units, warnings };\n}\n\n/** The dependency path from `part` to `target` (ending in target), or null when `part` does not depend on it, directly or transitively. */\nfunction dependsVia(part: PlanPart, target: string, byId: Map<string, PlanPart>, seen = new Set<string>()): string[] | null {\n for (const d of part.dependsOn) {\n if (d === target) return [d];\n if (seen.has(d)) continue;\n seen.add(d);\n const dep = byId.get(d);\n const sub = dep ? dependsVia(dep, target, byId, seen) : null;\n if (sub) return [d, ...sub];\n }\n return null;\n}\n\nfunction findCycle(parts: PlanPart[]): string[] | null {\n const byId = new Map(parts.map((p) => [p.id, p]));\n const state = new Map<string, 1 | 2>();\n const stack: string[] = [];\n const visit = (id: string): string[] | null => {\n if (state.get(id) === 2) return null;\n if (state.get(id) === 1) return [...stack.slice(stack.indexOf(id)), id];\n state.set(id, 1);\n stack.push(id);\n for (const d of byId.get(id)?.dependsOn ?? []) {\n if (!byId.has(d)) continue;\n const c = visit(d);\n if (c) return c;\n }\n stack.pop();\n state.set(id, 2);\n return null;\n };\n for (const p of parts) {\n const c = visit(p.id);\n if (c) return c;\n }\n return null;\n}\n\n/** Read and validate the plan file at `path`. */\nexport function loadPlan(path: string): Plan {\n let text: string;\n try {\n text = readFileSync(path, \"utf8\");\n } catch (e) {\n throw new PlanError([`cannot read ${path}: ${e instanceof Error ? e.message : String(e)}`]);\n }\n return parsePlan(text, dirname(resolve(path)));\n}\n\n/** Where the run log of a plan lives. */\nexport const runLogPath = (planPath: string): string => `${planPath}.run.jsonl`;\n\n/** Replace absolute paths into the plan's repo by relative ones. */\nexport function stripRepoPaths(text: string, repo: string): string {\n const r = repo.replace(/\\/+$/, \"\");\n if (!r) return text;\n const esc = r.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return text.replace(new RegExp(`${esc}/`, \"g\"), \"\").replace(new RegExp(`${esc}(?![\\\\w.-])`, \"g\"), \".\");\n}\n\n/** Whether `text` names an absolute path inside `repo`. */\nexport function mentionsRepoPath(text: string, repo: string): boolean {\n const r = repo.replace(/\\/+$/, \"\");\n return !!r && r !== \"/\" && stripRepoPaths(text, r) !== text;\n}\n\n/** Fixed footer of every composed spec: keeps the worker in its worktree and makes it commit. */\nexport function specFooter(part: PlanPart, job: string): string {\n return [\n \"---\",\n \"Rules for this task:\",\n \"- Work only with RELATIVE paths inside the current working directory. It is your own git worktree; never use an absolute path to the repository, or you edit the main checkout instead.\",\n `- Git add the files you changed, then commit on the current branch before you report, with the message \"${job}: ${part.unit} ${part.id}\". An empty commit is not a commit; uncommitted work is lost.`,\n \"- Report with an R message.\",\n ].join(\"\\n\");\n}\n\n/** The worker task: the part spec, its file list and proof, the fixed footer, and (on a retry) the failure tail. */\nexport function composeSpec(part: PlanPart, failure: string | undefined, job: string, repo?: string): string {\n const clean = (t: string) => (repo ? stripRepoPaths(t, repo) : t);\n const lines = [clean(part.spec)];\n if (part.files.length) lines.push(\"\", `Files this part touches: ${part.files.map(clean).join(\", \")}`);\n if (part.proof) lines.push(\"\", `Proof required: run \\`${clean(part.proof)}\\` and show it passing.`);\n if (failure) lines.push(\"\", \"The previous attempt failed. Its failure output (tail):\", \"```\", failure.trim(), \"```\", \"Fix the cause and try again.\");\n lines.push(\"\", specFooter(part, job));\n return lines.join(\"\\n\");\n}\n\n/** Dirty paths (from `git status --porcelain`) that a part of the plan touches. */\nexport function dirtyConflicts(plan: Plan, dirty: string[]): string[] {\n const files = plan.units.flatMap((u) => u.parts.flatMap((p) => p.files));\n return dirty.filter((d) => filesOverlap([d], files));\n}\n\n/** Two file lists collide when they share a path or one names a directory containing the other. */\nexport function filesOverlap(a: string[], b: string[]): boolean {\n const norm = (f: string) => f.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n for (const x of a.map(norm))\n for (const y of b.map(norm)) if (x === y || x.startsWith(y + \"/\") || y.startsWith(x + \"/\")) return true;\n return false;\n}\n","/**\n * plan-runner.ts — the scheduler behind `pai plan run`. No model involved.\n *\n * Keeps up to N parts running, starts a part once its dependencies are merged\n * and none of its files is in use by a running part, merges each finished part\n * with the plan gate, and walks the failure ladder: one local retry with the\n * failure tail, then needs-decision (or one Sonnet worker with --escalate).\n * Everything that touches workers, git or the clock goes through RunnerDeps.\n */\n\nimport { withRunLock, type LockDeps } from \"./run-lock.js\";\nimport { appendFileSync, existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { gatePhaseName } from \"./plan-status.js\";\nimport { composeSpec, filesOverlap, type Plan, type PlanPart } from \"./plan.js\";\n\nexport interface WorkerOutcome {\n id: string;\n /** Worker ended with a valid report whose result is \"+\". */\n ok: boolean;\n /** Failure text (report why, open items, or the run's last output) when !ok. */\n failure?: string;\n tokens?: number | null;\n /** The part must not be retried (e.g. the worker had no worktree); it goes straight to needs-decision. */\n fatal?: boolean;\n /** `pai worker run` refused or failed before any worker existed (pane/gate/config): an environment error, never retried. */\n spawnRefused?: boolean;\n /** The loop guard stopped the worker: a failed attempt (retry ladder), never a pass. */\n loopStopped?: boolean;\n /** The worker's worktree, when it has one. */\n worktreeDir?: string;\n /** The worker's final message (its R report). */\n report?: string;\n}\n\nexport interface StartRequest {\n part: PlanPart;\n label: string;\n spec: string;\n provider: string;\n /** \"local\" runs on plan.provider; \"sonnet\" is the escalation. */\n tier: \"local\" | \"sonnet\";\n attempt: number;\n /** Model for a local-tier worker (`pai worker run --model`); the sonnet tier names its own. */\n model?: string;\n /** \"front\": the worker goes ahead of the provider's waiting runs (`--now`). */\n priority?: \"front\";\n}\n\nexport interface MergeOutcome {\n ok: boolean;\n commit?: string;\n /** HEAD before the merge; a merge that leaves HEAD here changed nothing. */\n before?: string;\n /** The worker branch carried no commit, so nothing was merged. */\n noCommit?: boolean;\n output: string;\n}\n\n/** What the gate reports while `worker merge --verify` runs it, one command at a time. */\nexport type GateEvent =\n | { ev: \"gate-start\"; commands: string[] }\n | { ev: \"cmd-start\"; i: number; cmd: string }\n | { ev: \"cmd-end\"; i: number; cmd: string; ms: number; ok: boolean; tail?: string }\n | { ev: \"gate-end\"; ok: boolean; ms: number }\n /** Refinement of the current phase, e.g. \"gate: tests 214/2600\". */\n | { ev: \"progress\"; text: string };\n\nexport interface RunnerDeps {\n startWorker(req: StartRequest): Promise<WorkerOutcome>;\n merge(workerId: string, gate: string, onGate?: (e: GateEvent) => void): Promise<MergeOutcome>;\n unitCheck(cmd: string): Promise<{ ok: boolean; output: string }>;\n now(): number;\n /** The provider a plan/queue value names: `local` becomes the running local server, any other name is returned unchanged. */\n resolveLocal(provider: string): Promise<string>;\n log(entry: RunLogEntry): void;\n /** The runner's current phase (\"merging p1\", \"gate: tsc\"), shown by ps, the cockpit and plan_status. */\n phase?(text: string, refine?: boolean): void;\n /** Parts merged so far / workers running now, for the status entry. */\n progress?(merged: number, running: string[]): void;\n /** A line for the plan pane's live log. */\n note?(line: string): void;\n /** Stop the workers this runner has started (SIGTERM/SIGINT path); resolves once they are told to end. */\n stopActive?(): Promise<void>;\n}\n\nexport interface RunLogEntry {\n ts?: string;\n event: \"run-start\" | \"run-end\" | \"unit-done\" | \"part-start\" | \"part-end\" | \"merge\" | \"gate-start\" | \"gate-end\" | \"escalate\" | \"needs-decision\" | \"unit-check\" | \"worker-start\" | \"worker-end\" | \"model-idle\" | \"overlap-continue\";\n part?: string;\n unit?: string;\n worker?: string;\n attempt?: number;\n tier?: string;\n ok?: boolean;\n /** \"no-commit\" when the worker branch held no commit; the merge counts as not done. */\n status?: string;\n gate?: string;\n commit?: string;\n tokens?: number | null;\n note?: string;\n /** gate-start: the gate's commands; gate-end: each command's duration. */\n commands?: Array<{ cmd: string; ms?: number; ok?: boolean }>;\n /** gate-end: whole gate duration in ms; worker-end: the worker's run; model-idle: the gap. */\n ms?: number;\n /** worker-start/worker-end/model-idle: the provider the worker runs on. */\n provider?: string;\n /** model-idle: what the runner did meanwhile (gate, merge, unit-check, waiting-for-dependency); part-end: \"loop\". */\n reason?: string;\n /** part-start: the part started while an earlier part was still being gated. */\n overlap?: boolean;\n /** run-end: local model idle time, wall time and overlapped starts of the run. */\n idleMs?: number;\n wallMs?: number;\n overlaps?: number;\n}\n\nexport type PartState = \"waiting\" | \"running\" | \"merged\" | \"needs-decision\";\n\nexport interface RunSummary {\n merged: string[];\n needsDecision: string[];\n /** Waiting parts that can never start because a dependency needs a decision. */\n blocked: string[];\n unitFailures: string[];\n /** Parts are waiting, nothing runs and nothing can start: `blockedOn` lists the needs-decision parts and failed units. */\n stuck: boolean;\n blockedOn: string[];\n escalations: number;\n /** The refusal that stopped scheduling (repeated identically), printed once. */\n spawnRefusal?: string;\n /** The run ended on a stop request (marker or signal); `interrupted` lists parts that were in flight. */\n stopped?: boolean;\n interrupted?: string[];\n}\n\nexport interface RunOptions {\n parallel: number;\n escalate?: boolean;\n /** Model every local-tier worker of the run gets (default: the provider's). */\n model?: string;\n /** \"front\": every worker of the run goes ahead of the provider's waiting runs. */\n priority?: \"front\";\n /** Part ids already merged (from the log). */\n merged?: ReadonlySet<string>;\n /** Unit ids whose unit_check already passed. */\n unitsChecked?: ReadonlySet<string>;\n /** Lock file held for the duration of the run (one run per plan). */\n lockPath?: string;\n /** Stop marker (`pai plan stop`): checked before each new part; the run ends \"stopped\" once in-flight parts finish. */\n stopPath?: string;\n /** Lock/signal hooks, replaced by tests. */\n lockDeps?: LockDeps;\n}\n\nexport const planStopPath = (planPath: string): string => `${planPath}.stop`;\n\nexport function requestPlanStop(planPath: string): void {\n writeFileSync(planStopPath(planPath), new Date().toISOString(), \"utf8\");\n}\n\nconst TAIL_LINES = 40;\nexport const tail = (text: string, n = TAIL_LINES): string => text.trim().split(\"\\n\").slice(-n).join(\"\\n\");\n\n/** Parts startable now: waiting, dependencies merged, no file clash with `busy` or each other, up to `slots`. */\nexport function pickStartable(\n parts: PlanPart[],\n state: ReadonlyMap<string, PartState>,\n busy: PlanPart[],\n slots: number,\n): PlanPart[] {\n const picked: PlanPart[] = [];\n const active = [...busy];\n for (const p of parts) {\n if (picked.length >= slots) break;\n if (state.get(p.id) !== \"waiting\") continue;\n if (!p.dependsOn.every((d) => state.get(d) === \"merged\")) continue;\n if (active.some((a) => filesOverlap(a.files, p.files))) continue;\n picked.push(p);\n active.push(p);\n }\n return picked;\n}\n\nexport function runPlan(plan: Plan, opts: RunOptions, deps: RunnerDeps): Promise<RunSummary> {\n const running = new Map<string, PlanPart>();\n let aborted = false;\n // once a signal arrived the killed workers' failures must not be logged, retried or merged: the parts restart on the next run\n const guarded: RunnerDeps = {\n ...deps,\n log: (e) => {\n if (!aborted) deps.log(e);\n },\n startWorker: (req) => (aborted ? new Promise<WorkerOutcome>(() => undefined) : deps.startWorker(req).then((o) => (aborted ? new Promise<WorkerOutcome>(() => undefined) : o))),\n };\n const onSignal = async (): Promise<void> => {\n aborted = true;\n const interrupted = [...running.keys()];\n await deps.stopActive?.();\n deps.log({ event: \"run-end\", ok: false, status: \"stopped\", note: `interrupted=${interrupted.join(\",\") || \"none\"}` });\n };\n return withRunLock(opts.lockPath, `plan ${plan.job}`, () => runPlanLocked(plan, opts, guarded, running), { ...opts.lockDeps, onSignal });\n}\n\nasync function runPlanLocked(plan: Plan, opts: RunOptions, deps: RunnerDeps, active: Map<string, PlanPart>): Promise<RunSummary> {\n // `provider: local` in a plan means resolveLocalProvider() at run start —\n // the loopback server that answers, never a provider picked by name.\n plan = { ...plan, provider: await deps.resolveLocal(plan.provider) };\n const parts = plan.units.flatMap((u) => u.parts);\n const state = new Map<string, PartState>(parts.map((p) => [p.id, opts.merged?.has(p.id) ? \"merged\" : \"waiting\"]));\n const checked = new Set(opts.unitsChecked ?? []);\n const unitFailures: string[] = [];\n const failedParts = new Set<string>();\n let escalations = 0;\n const refusals = new Map<string, number>();\n let haltedBy: string | undefined;\n const running = new Map<string, { part: PlanPart; done: Promise<void> }>();\n let stopping = false;\n const stopRequested = (): boolean => !!opts.stopPath && existsSync(opts.stopPath);\n if (opts.stopPath) {\n try {\n unlinkSync(opts.stopPath); // a marker left from before this run is stale\n } catch {\n /* none */\n }\n }\n // merges and unit checks touch the checkout: one at a time\n let lock: Promise<unknown> = Promise.resolve();\n const exclusive = <T>(fn: () => Promise<T>): Promise<T> => {\n const r = lock.then(fn, fn);\n lock = r.catch(() => undefined);\n return r;\n };\n\n // one worker at a time per provider slot count: `parallel` workers on a provider, never more\n const cap = opts.parallel;\n const activeWorkers = new Map<string, number>();\n const waiters: Array<{ provider: string; grant: () => void }> = [];\n const lastEnd = new Map<string, number>();\n const causes = new Map<string, string[]>();\n let idleTotal = 0;\n let overlaps = 0;\n const startedAt = deps.now();\n let wake: () => void = () => undefined;\n // parts whose worker ended but whose gate, merge or unit check is still going\n const postWorker = new Set<string>();\n // overlapped part -> the parts it overlapped (merge waits for them)\n const overlapOn = new Map<string, string[]>();\n const noteCause = (c: string): void => {\n for (const [prov, list] of causes) if ((activeWorkers.get(prov) ?? 0) === 0 && !list.includes(c)) list.push(c);\n };\n const workerBegin = (p: PlanPart, provider: string, tier: string, attempt: number): void => {\n const t = deps.now();\n if ((activeWorkers.get(provider) ?? 0) === 0 && lastEnd.has(provider)) {\n const ms = t - lastEnd.get(provider)!;\n const list = causes.get(provider) ?? [];\n const why = list.includes(\"gate\") ? list.filter((c) => c !== \"merge\") : list;\n if (tier === \"local\") idleTotal += ms;\n deps.log({ event: \"model-idle\", part: p.id, unit: p.unit, provider, tier, ms, reason: why.join(\"+\") || \"waiting-for-dependency\" });\n }\n causes.set(provider, []);\n activeWorkers.set(provider, (activeWorkers.get(provider) ?? 0) + 1);\n postWorker.delete(p.id);\n deps.log({ event: \"worker-start\", part: p.id, unit: p.unit, provider, tier, attempt });\n };\n const acquire = (p: PlanPart, provider: string, tier: string, attempt: number): Promise<void> => {\n if ((activeWorkers.get(provider) ?? 0) < cap) {\n workerBegin(p, provider, tier, attempt);\n return Promise.resolve();\n }\n return new Promise((resolve) => waiters.push({ provider, grant: () => (workerBegin(p, provider, tier, attempt), resolve()) }));\n };\n const release = (p: PlanPart, provider: string): void => {\n activeWorkers.set(provider, (activeWorkers.get(provider) ?? 1) - 1);\n lastEnd.set(provider, deps.now());\n causes.set(provider, []);\n postWorker.add(p.id);\n const i = waiters.findIndex((w) => w.provider === provider);\n if (i >= 0 && (activeWorkers.get(provider) ?? 0) < cap) waiters.splice(i, 1)[0].grant();\n wake();\n };\n\n const progress = (): void => deps.progress?.([...state.values()].filter((v) => v === \"merged\").length, [...running.keys()]);\n\n const label = (p: PlanPart) => `${plan.job}: ${p.unit} ${p.id}`;\n\n const checkUnit = async (unitId: string): Promise<void> => {\n const unit = plan.units.find((u) => u.id === unitId)!;\n if (checked.has(unitId) || !unit.parts.every((p) => state.get(p.id) === \"merged\")) return;\n checked.add(unitId);\n if (!unit.unitCheck) {\n deps.log({ event: \"unit-done\", unit: unitId });\n return;\n }\n const r = await exclusive(() => {\n noteCause(\"unit-check\");\n deps.phase?.(`unit check ${unitId}`);\n return deps.unitCheck(unit.unitCheck!);\n });\n deps.log({ event: \"unit-check\", unit: unitId, ok: r.ok, note: r.ok ? undefined : tail(r.output, 10) });\n if (r.ok) deps.log({ event: \"unit-done\", unit: unitId });\n if (!r.ok) {\n unitFailures.push(unitId);\n // parts that depend on a unit whose check failed wait for a decision\n for (const up of unit.parts) failedParts.add(up.id);\n deps.log({ event: \"needs-decision\", unit: unitId, note: `unit_check failed: ${tail(r.output, 10)}` });\n }\n };\n\n const attemptPart = async (p: PlanPart): Promise<void> => {\n const ladder: Array<{ tier: \"local\" | \"sonnet\"; provider: string }> = [\n { tier: \"local\", provider: plan.provider },\n { tier: \"local\", provider: plan.provider },\n ...(opts.escalate ? [{ tier: \"sonnet\" as const, provider: \"anthropic\" }] : []),\n ];\n let failure: string | undefined;\n for (let i = 0; i < ladder.length; i++) {\n const { tier, provider } = ladder[i];\n if (tier === \"sonnet\") {\n escalations++;\n deps.log({ event: \"escalate\", part: p.id, unit: p.unit, attempt: i + 1, tier, note: tail(failure ?? \"\", 10) });\n }\n deps.log({ event: \"part-start\", part: p.id, unit: p.unit, attempt: i + 1, tier, ...(overlapOn.has(p.id) && i === 0 ? { overlap: true } : {}) });\n deps.phase?.(`starting ${p.id}`);\n const granted = acquire(p, provider, tier, i + 1);\n const wstart = deps.now();\n await granted;\n let out: WorkerOutcome;\n try {\n out = await deps.startWorker({ part: p, label: label(p), spec: composeSpec(p, failure, plan.job, plan.repo), provider, tier, attempt: i + 1, ...(tier === \"local\" && opts.model ? { model: opts.model } : {}), ...(opts.priority ? { priority: opts.priority } : {}) });\n } finally {\n release(p, provider);\n }\n deps.log({ event: \"worker-end\", part: p.id, unit: p.unit, worker: out.id, provider, tier, attempt: i + 1, ok: out.ok, ms: deps.now() - wstart });\n deps.note?.(`${p.id} worker ${out.id || \"-\"} ${out.ok ? \"finished ok\" : \"failed\"}${out.ok ? \"\" : `: ${tail(out.failure ?? \"\", 4)}`}`);\n deps.log({ event: \"part-end\", part: p.id, unit: p.unit, worker: out.id, attempt: i + 1, tier, ok: out.ok, ...(out.loopStopped ? { reason: \"loop\" } : {}), tokens: out.tokens ?? null, note: out.ok ? undefined : tail(out.failure ?? \"\", 10) });\n if (!out.ok && out.spawnRefused) {\n failure = out.failure || \"worker spawn refused\";\n const n = (refusals.get(failure) ?? 0) + 1;\n refusals.set(failure, n);\n if (n >= 2) haltedBy = failure;\n break;\n }\n if (!out.ok && out.fatal) {\n failure = out.failure || \"the worker could not be isolated\";\n break;\n }\n if (!out.ok) {\n failure = out.failure || \"the worker ended without a valid passing report\";\n continue;\n }\n const gateObserver = (): ((e: GateEvent) => void) => {\n let cmds: Array<{ cmd: string; ms?: number; ok?: boolean }> = [];\n return (e) => {\n if (e.ev === \"gate-start\") {\n cmds = e.commands.map((cmd) => ({ cmd }));\n noteCause(\"gate\");\n deps.log({ event: \"gate-start\", part: p.id, unit: p.unit, worker: out.id, gate: plan.gate, commands: cmds });\n } else if (e.ev === \"cmd-start\") {\n deps.phase?.(`gate: ${gatePhaseName(e.cmd)}`);\n deps.note?.(`gate command ${e.i + 1}/${cmds.length || \"?\"}: ${e.cmd}`);\n } else if (e.ev === \"cmd-end\") {\n cmds[e.i] = { cmd: e.cmd, ms: e.ms, ok: e.ok };\n deps.note?.(`gate command ${e.i + 1} ${e.ok ? \"ok\" : \"FAILED\"} in ${(e.ms / 1000).toFixed(1)}s${e.tail ? `\\n${e.tail}` : \"\"}`);\n } else if (e.ev === \"gate-end\") {\n deps.log({ event: \"gate-end\", part: p.id, unit: p.unit, worker: out.id, ok: e.ok, ms: e.ms, commands: cmds });\n } else deps.phase?.(e.text, true);\n };\n };\n // an overlapped part merges only after the parts it overlapped are through (merged, or out with a decision)\n const gated = (overlapOn.get(p.id) ?? []).map((id) => running.get(id)?.done).filter((d): d is Promise<void> => !!d);\n if (gated.length) await Promise.all(gated);\n let m = await exclusive(() => {\n noteCause(\"merge\");\n deps.phase?.(`merging ${p.id}`);\n return deps.merge(out.id, plan.gate, gateObserver());\n });\n // a \"merge\" that moved nothing is not a merge: the worker never committed\n if (m.ok && (m.noCommit || !m.commit || m.commit === m.before)) m = { ...m, ok: false, noCommit: true };\n deps.log({ event: \"merge\", part: p.id, unit: p.unit, worker: out.id, attempt: i + 1, ok: m.ok, ...(m.noCommit ? { status: \"no-commit\" } : {}), gate: plan.gate, ...(m.noCommit ? {} : { commit: m.commit }), note: m.ok ? undefined : tail(m.output, 10) });\n deps.note?.(`${p.id} ${m.ok ? \"merged\" : \"merge failed\"}${m.commit ? ` ${m.commit.slice(0, 7)}` : \"\"}${m.ok ? \"\" : `\\n${tail(m.output, 8)}`}`);\n if (m.ok) {\n state.set(p.id, \"merged\");\n progress();\n await checkUnit(p.unit);\n return;\n }\n failure = m.noCommit\n ? `no-commit: the worker branch has no commit, nothing was merged. Git add the files you changed, then commit on the current branch (message \"${label(p)}\") and edit files only by relative path inside your worktree.${m.output.trim() ? `\\n${tail(m.output, 10)}` : \"\"}`\n : `gate failed (${plan.gate}):\\n${tail(m.output)}`;\n }\n state.set(p.id, \"needs-decision\");\n deps.log({ event: \"needs-decision\", part: p.id, unit: p.unit, note: tail(failure ?? \"\", 10) });\n for (const [q, over] of overlapOn) {\n if (over.includes(p.id) && running.has(q)) deps.log({ event: \"overlap-continue\", part: q, unit: p.unit, note: `${p.id} failed its gate; ${q} is independent of it and keeps running` });\n }\n };\n\n const idleFields = (): Pick<RunLogEntry, \"idleMs\" | \"wallMs\" | \"overlaps\"> => ({ idleMs: idleTotal, wallMs: deps.now() - startedAt, overlaps });\n deps.log({ event: \"run-start\", note: `parallel=${opts.parallel}${opts.escalate ? \" escalate=sonnet\" : \"\"}` });\n // a resumed run may have units complete but unchecked (killed between merge and check)\n for (const u of plan.units) await checkUnit(u.id);\n for (;;) {\n if (!stopping && stopRequested()) stopping = true;\n const launch = (p: PlanPart): void => {\n state.set(p.id, \"running\");\n // started while earlier parts are past their worker (gate, merge, unit check): an overlap\n const over = [...postWorker].filter((id) => running.has(id));\n if (p.overlap && over.length) {\n overlapOn.set(p.id, over);\n overlaps++;\n }\n const done = attemptPart(p).finally(() => {\n running.delete(p.id);\n active.delete(p.id);\n postWorker.delete(p.id);\n overlapOn.delete(p.id);\n progress();\n });\n running.set(p.id, { part: p, done });\n active.set(p.id, p);\n progress();\n };\n if (!haltedBy && !stopping) {\n const eligible = parts.filter((p) => !p.dependsOn.some((d) => failedParts.has(d)));\n for (const p of pickStartable(eligible, state, [...running.values()].map((r) => r.part), opts.parallel - running.size)) launch(p);\n // an orchestrator-marked part, once every part before it has started, may start once the workers of the running parts have ended, though their gate is still going\n const workers = [...activeWorkers.values()].reduce((a, b) => a + b, 0);\n const free = opts.parallel - workers;\n if (free > 0) {\n for (const p of pickStartable(eligible.filter((q) => q.overlap && parts.slice(0, parts.indexOf(q)).every((e) => state.get(e.id) !== \"waiting\")), state, [...running.values()].map((r) => r.part), free)) launch(p);\n }\n }\n if (running.size === 0) break;\n const woken = new Promise<void>((r) => (wake = r));\n await Promise.race([woken, ...[...running.values()].map((r) => r.done)]);\n }\n if (stopping && opts.stopPath) {\n try {\n unlinkSync(opts.stopPath);\n } catch {\n /* already gone */\n }\n }\n const waiting = parts.filter((p) => state.get(p.id) === \"waiting\").map((p) => p.id);\n const summary: RunSummary = {\n merged: parts.filter((p) => state.get(p.id) === \"merged\").map((p) => p.id),\n needsDecision: parts.filter((p) => state.get(p.id) === \"needs-decision\").map((p) => p.id),\n blocked: waiting,\n unitFailures,\n stuck: !haltedBy && !stopping && waiting.length > 0,\n blockedOn: [...parts.filter((p) => state.get(p.id) === \"needs-decision\").map((p) => p.id), ...unitFailures.map((u) => `unit ${u}`)],\n escalations,\n ...(haltedBy ? { spawnRefusal: haltedBy } : {}),\n ...(stopping ? { stopped: true } : {}),\n };\n if (stopping) {\n deps.log({ event: \"run-end\", ok: false, status: \"stopped\", note: `stop requested; merged=${summary.merged.length} waiting=${waiting.length}`, ...idleFields() });\n return summary;\n }\n deps.log({ event: \"run-end\", ok: !summary.needsDecision.length && !waiting.length && !unitFailures.length, ...(summary.stuck ? { status: \"blocked\" } : {}), note: `merged=${summary.merged.length} needs-decision=${summary.needsDecision.length} blocked=${waiting.length}`, ...idleFields() });\n return summary;\n}\n\n/** Exit code of `pai plan run`: 0 all merged, 4 stopped by `pai plan stop`, 1 failures with everything else done, 3 blocked (parts left that cannot start). */\nexport function runExitCode(s: RunSummary): number {\n if (s.stopped) return 4;\n if (s.stuck) return 3;\n return s.needsDecision.length || s.blocked.length || s.unitFailures.length ? 1 : 0;\n}\n\nexport function blockedMessage(s: RunSummary, planPath: string): string {\n return `blocked: ${s.blocked.length} parts wait on needs-decision items: ${s.blockedOn.join(\", \") || \"none\"}; fix and re-run \\`pai plan run ${planPath}\\` (merged parts are skipped)`;\n}\n\n/** The schedule a run would execute, assuming every part succeeds: waves of at most `parallel` parts. */\nexport function dryRunSchedule(plan: Plan, opts: RunOptions): PlanPart[][] {\n const parts = plan.units.flatMap((u) => u.parts);\n const state = new Map<string, PartState>(parts.map((p) => [p.id, opts.merged?.has(p.id) ? \"merged\" : \"waiting\"]));\n const waves: PlanPart[][] = [];\n for (;;) {\n const wave = pickStartable(parts, state, [], opts.parallel);\n if (!wave.length) break;\n waves.push(wave);\n for (const p of wave) state.set(p.id, \"merged\");\n }\n return waves;\n}\n\nexport function renderSchedule(plan: Plan, opts: RunOptions): string {\n const waves = dryRunSchedule(plan, opts);\n const lines = [`job ${plan.job}: ${plan.units.flatMap((u) => u.parts).length} parts, provider ${plan.provider}, ${opts.parallel} at a time, gate: ${plan.gate}`];\n waves.forEach((w, i) => {\n lines.push(`step ${i + 1}:`);\n for (const p of w) lines.push(` ${p.unit} ${p.id}${p.risky ? \" [risky]\" : \"\"}${p.dependsOn.length ? ` after ${p.dependsOn.join(\", \")}` : \"\"}${p.files.length ? ` files ${p.files.join(\", \")}` : \"\"}`);\n });\n const left = plan.units.flatMap((u) => u.parts).filter((p) => !opts.merged?.has(p.id) && !waves.flat().includes(p));\n if (left.length) lines.push(`never scheduled: ${left.map((p) => p.id).join(\", \")}`);\n for (const u of plan.units) if (u.unitCheck && !opts.unitsChecked?.has(u.id)) lines.push(`after unit ${u.id}: ${u.unitCheck}`);\n if (opts.merged?.size) lines.push(`skipped (already merged): ${[...opts.merged].join(\", \")}`);\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// run log\n\nexport function readRunLog(path: string): RunLogEntry[] {\n if (!existsSync(path)) return [];\n const out: RunLogEntry[] = [];\n for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n if (!line.startsWith(\"{\")) continue;\n try {\n out.push(JSON.parse(line) as RunLogEntry);\n } catch {\n /* torn line */\n }\n }\n return out;\n}\n\nexport function appendRunLog(path: string, entry: RunLogEntry, now: () => number = Date.now): void {\n appendFileSync(path, JSON.stringify({ ts: new Date(now()).toISOString(), ...entry }) + \"\\n\", \"utf8\");\n}\n\n/** What a resumed run skips: merged parts and units whose check passed. */\nexport function resumeState(log: RunLogEntry[]): { merged: Set<string>; unitsChecked: Set<string> } {\n const merged = new Set<string>();\n const unitsChecked = new Set<string>();\n for (const e of log) {\n if (e.event === \"merge\" && e.ok && e.part) merged.add(e.part);\n if (e.event === \"unit-check\" && e.ok && e.unit) unitsChecked.add(e.unit);\n }\n return { merged, unitsChecked };\n}\n\nexport interface PlanStatus {\n done: string[];\n running: string[];\n waiting: string[];\n needsDecision: string[];\n tokens: number;\n wallMs: number;\n /** Local model idle time between workers (sum of model-idle events) and parts that started overlapped. */\n idleMs: number;\n overlaps: number;\n}\n\n/** Summarise the log against the plan's parts. `now` closes an unfinished run segment. */\nexport function planStatus(plan: Plan, log: RunLogEntry[], now: number): PlanStatus {\n const last = new Map<string, RunLogEntry>();\n let tokens = 0;\n let idleMs = 0;\n let overlaps = 0;\n let wallMs = 0;\n let segStart: number | null = null;\n let lastTs = 0;\n for (const e of log) {\n const t = e.ts ? Date.parse(e.ts) : 0;\n lastTs = t || lastTs;\n if (e.event === \"run-start\") segStart = t;\n if (e.event === \"run-end\" && segStart !== null) {\n wallMs += t - segStart;\n segStart = null;\n }\n if (e.part && [\"part-start\", \"part-end\", \"merge\", \"needs-decision\"].includes(e.event)) last.set(e.part, e);\n if (e.event === \"model-idle\" && e.tier === \"local\" && typeof e.ms === \"number\") idleMs += e.ms;\n if (e.event === \"part-start\" && e.overlap) overlaps++;\n if (e.event === \"part-end\" && typeof e.tokens === \"number\") tokens += e.tokens;\n }\n // an open segment is a run in progress when the log is fresh, else a run that died: count up to its last entry\n if (segStart !== null) wallMs += (now - lastTs < 120_000 ? now : lastTs) - segStart;\n const s: PlanStatus = { done: [], running: [], waiting: [], needsDecision: [], tokens, wallMs, idleMs, overlaps };\n const runActive = segStart !== null && now - lastTs < 120_000;\n for (const p of plan.units.flatMap((u) => u.parts)) {\n const e = last.get(p.id);\n if (e?.event === \"merge\" && e.ok) s.done.push(p.id);\n else if (e?.event === \"needs-decision\") s.needsDecision.push(p.id);\n else if (e?.event === \"part-start\" && runActive) s.running.push(p.id);\n else s.waiting.push(p.id);\n }\n return s;\n}\n\n/** \"local model idle 1m02s of 7m24s (14%), 2 overlapped starts\". */\nexport function idleText(idleMs: number, wallMs: number, overlaps: number): string {\n const f = (ms: number): string => {\n const s = Math.round(ms / 1000);\n return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m${String(s % 60).padStart(2, \"0\")}s`;\n };\n const pct = wallMs > 0 ? Math.round((idleMs / wallMs) * 100) : 0;\n return `local model idle ${f(idleMs)} of ${f(wallMs)} (${pct}%), ${overlaps} overlapped ${overlaps === 1 ? \"start\" : \"starts\"}`;\n}\n\nexport function renderStatus(plan: Plan, st: PlanStatus): string {\n const fmt = (ids: string[]) => (ids.length ? `${ids.length} (${ids.join(\", \")})` : \"0\");\n const secs = Math.round(st.wallMs / 1000);\n return [\n `job ${plan.job}`,\n `done ${fmt(st.done)}`,\n `running ${fmt(st.running)}`,\n `waiting ${fmt(st.waiting)}`,\n `needs-decision ${fmt(st.needsDecision)}`,\n `wall so far ${Math.floor(secs / 60)}m${String(secs % 60).padStart(2, \"0\")}s, tokens ${st.tokens}`,\n idleText(st.idleMs, st.wallMs, st.overlaps),\n ].join(\"\\n\");\n}\n","/**\n * run-events.ts — report plan and queue runs back into the conversation.\n *\n * The operator never polls: a run reports the moments that need them to the\n * session that started it (AIBroker send_to_session, so the session wakes and\n * can talk to the operator) and, for blocked/finished, to PAILot. A run\n * started with --detach is registered under <logDir>/runs and keeps going\n * after the starting session ends. Everything outside the process (AIBroker,\n * clock, spawning) goes through injected functions; tests never send.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, openSync, readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { idleText, type RunLogEntry } from \"./plan-runner.js\";\nimport type { QueueItem } from \"./queue.js\";\n\n/** The session that started a run: where its events go. */\nexport interface RunOrigin {\n claudeSession?: string;\n /** iTerm2 session uuid (AIBroker target). */\n iterm?: string;\n /** PAI / AIBroker session name. */\n name?: string;\n}\n\nexport interface Messenger {\n /** Type a message into the originating session. */\n toSession(origin: RunOrigin, text: string): Promise<boolean>;\n /** Short plain sentence to the operator's phone. */\n toPailot(text: string): Promise<boolean>;\n}\n\nexport const PLAN_GROUP_THROTTLE_MS = 15 * 60_000;\nexport const QUEUE_READY_BATCH_MS = 10 * 60_000;\n\n/** The origin of the calling session, from its environment. */\nexport function originFromEnv(env: NodeJS.ProcessEnv = process.env): RunOrigin {\n if (env.PAI_RUN_ORIGIN) {\n try {\n return JSON.parse(env.PAI_RUN_ORIGIN) as RunOrigin;\n } catch {\n /* fall through to the live environment */\n }\n }\n const o: RunOrigin = {};\n if (env.CLAUDE_CODE_SESSION_ID) o.claudeSession = env.CLAUDE_CODE_SESSION_ID;\n const iterm = env.ITERM_SESSION_ID?.split(\":\").pop();\n if (iterm) o.iterm = iterm;\n const name = env.PAI_WORKER_PARENT ?? env.PAI_SESSION_NAME ?? env.AIBROKER_SESSION_NAME ?? env.AIBROKER_SESSION;\n if (name) o.name = name;\n return o;\n}\n\n/** Real messenger over AIBroker's socket; unreachable AIBroker is logged and ignored. */\nexport function aibrokerMessenger(logDir: string, warn: (s: string) => void = (s) => process.stderr.write(s + \"\\n\")): Messenger {\n return {\n async toSession(origin, text) {\n try {\n const { sendToSession } = await import(\"../cli/lib/aibroker-client.js\");\n let target = origin.iterm;\n if (!target && origin.claudeSession) {\n const { itermForClaudeSession } = await import(\"./scope.js\");\n target = itermForClaudeSession(logDir, origin.claudeSession) ?? undefined;\n }\n target = target ?? origin.name;\n if (!target) {\n warn(\"run report not delivered: the run has no originating session\");\n return false;\n }\n const r = await sendToSession(target, text);\n if (!r.ok && !r.timedOut) warn(`run report not delivered to the session: ${r.error}`);\n return r.ok || r.timedOut === true;\n } catch (e) {\n warn(`run report not delivered to the session: ${String(e)}`);\n return false;\n }\n },\n async toPailot(text) {\n try {\n const { callAiBroker } = await import(\"../cli/lib/aibroker-client.js\");\n await callAiBroker(\"pailot_send\", { text }, 8_000);\n return true;\n } catch (e) {\n warn(`run report not delivered to PAILot: ${String(e)}`);\n return false;\n }\n },\n };\n}\n\nconst short = (s: string | undefined, n = 160): string => (s ?? \"\").replace(/\\s+/g, \" \").trim().slice(0, n);\nconst mins = (ms: number): string => {\n const m = Math.round(ms / 60_000);\n return m < 1 ? `${Math.round(ms / 1000)}s` : `${m} min`;\n};\nconst fmtTok = (n: number): string => (n >= 1000 ? `${Math.round(n / 100) / 10}k` : String(n));\n\n/** AG2-style kind line plus a plain sentence, one line (it is typed into a terminal). */\nexport function eventLine(id: string, kind: string, fields: Record<string, string | number>, sentence: string): string {\n const kv = Object.entries(fields).map(([k, v]) => `${k}=${String(v).replace(/[|\\n]/g, \" \")}`);\n return `S i=${id} z=${kind}${kv.length ? \" | \" + kv.join(\" | \") : \"\"} — ${sentence}`;\n}\n\nabstract class Reporter {\n private chain: Promise<unknown> = Promise.resolve();\n constructor(protected readonly msg: Messenger, protected readonly origin: RunOrigin, protected readonly now: () => number) {}\n protected send(text: string, pailot?: string): void {\n this.chain = this.chain.then(async () => {\n await this.msg.toSession(this.origin, text).catch(() => false);\n if (pailot) await this.msg.toPailot(pailot).catch(() => false);\n });\n }\n /** Wait for every queued message; call before the process exits. */\n async flush(): Promise<void> {\n await this.chain;\n }\n}\n\n/** Turns the plan runner's log entries into messages. Feed it every entry via `onLog`. */\nexport class PlanReporter extends Reporter {\n private readonly start: number;\n private localTokens = 0;\n private cloudTokens = 0;\n private idleMs = 0;\n private overlaps = 0;\n private escalations = 0;\n private merged = 0;\n private readonly nd: string[] = [];\n private lastGroup = -Infinity;\n private pendingGroups: string[] = [];\n\n constructor(private readonly job: string, private readonly totalParts: number, msg: Messenger, origin: RunOrigin, now: () => number = Date.now) {\n super(msg, origin, now);\n this.start = now();\n }\n\n private get id(): string {\n return `plan:${this.job}`;\n }\n\n onLog(e: RunLogEntry): void {\n if (e.event === \"part-end\" && typeof e.tokens === \"number\") {\n if (e.tier === \"sonnet\") this.cloudTokens += e.tokens;\n else this.localTokens += e.tokens;\n }\n if (e.event === \"model-idle\" && e.tier === \"local\" && typeof e.ms === \"number\") this.idleMs += e.ms;\n if (e.event === \"part-start\" && e.overlap) this.overlaps++;\n if (e.event === \"escalate\") this.escalations++;\n if (e.event === \"merge\" && e.ok) this.merged++;\n if (e.event === \"needs-decision\" && e.part) {\n this.nd.push(e.part);\n this.send(\n eventLine(this.id, \"needs-decision\", { part: e.part }, `Part ${e.part} of ${this.job} failed twice and needs your decision: ${short(e.note)}`),\n );\n }\n if (e.event === \"unit-check\" && e.ok === false) {\n this.send(eventLine(this.id, \"unit-check-failed\", { unit: e.unit ?? \"?\" }, `The check after unit ${e.unit} of ${this.job} failed: ${short(e.note)}`));\n }\n if (e.event === \"unit-done\" && e.unit) this.unitDone(e.unit);\n if (e.event === \"run-end\") this.runEnd(e);\n }\n\n private unitDone(unit: string): void {\n this.pendingGroups.push(unit);\n const t = this.now();\n if (t - this.lastGroup < PLAN_GROUP_THROTTLE_MS) return;\n this.lastGroup = t;\n const units = this.pendingGroups.splice(0);\n this.send(eventLine(this.id, \"unit-done\", { units: units.join(\",\") }, `${this.job}: ${units.length === 1 ? \"unit\" : \"units\"} ${units.join(\", \")} finished; ${this.merged} of ${this.totalParts} parts merged so far.`));\n }\n\n private runEnd(e: RunLogEntry): void {\n const outcome = e.ok ? \"merged\" : e.status === \"blocked\" ? \"blocked\" : \"failed\";\n const wallMs = this.now() - this.start;\n const wall = mins(wallMs);\n const idle = idleText(this.idleMs, wallMs, this.overlaps);\n const stats = { outcome, wall, merged: `${this.merged}/${this.totalParts}`, escalations: this.escalations, local_tokens: this.localTokens, cloud_tokens: this.cloudTokens, idle: idle.replace(/^local model idle /, \"\") };\n const what =\n outcome === \"merged\"\n ? `${this.job} is done: all ${this.totalParts} parts merged in ${wall}.`\n : outcome === \"blocked\"\n ? `${this.job} is blocked after ${wall}: ${this.merged} of ${this.totalParts} parts merged, the rest wait on ${this.nd.join(\", \") || \"a failed check\"}. It needs your decision.`\n : `${this.job} ended with failures after ${wall}: ${this.merged} of ${this.totalParts} parts merged${this.nd.length ? `, ${this.nd.join(\", \")} need your decision` : \"\"}.`;\n const extra = ` Escalations ${this.escalations}, ${fmtTok(this.localTokens)} local tokens, ${fmtTok(this.cloudTokens)} cloud tokens; ${idle}.`;\n this.send(eventLine(this.id, \"run-\" + outcome, stats, what + extra), what);\n }\n}\n\n/** Queue events: needs-decision at once, ready items batched. */\nexport class QueueReporter extends Reporter {\n private readyBatch: QueueItem[] = [];\n private lastReady = -Infinity;\n\n constructor(private readonly project: string, msg: Messenger, origin: RunOrigin, now: () => number = Date.now) {\n super(msg, origin, now);\n }\n\n private get id(): string {\n return `queue:${this.project}`;\n }\n\n onReady(item: QueueItem): void {\n this.readyBatch.push(item);\n if (this.now() - this.lastReady >= QUEUE_READY_BATCH_MS) this.sendReady();\n }\n\n onNeedsDecision(item: QueueItem): void {\n this.send(eventLine(this.id, \"needs-decision\", { item: item.id }, `Queue item ${item.id} (\"${short(item.text, 80)}\") failed twice and needs your decision: ${short(item.why)}`));\n }\n\n private sendReady(): void {\n const items = this.readyBatch.splice(0);\n if (!items.length) return;\n this.lastReady = this.now();\n const list = items.map((i) => `${i.id} \"${short(i.text, 60)}\"`).join(\", \");\n this.send(eventLine(this.id, \"ready\", { items: items.map((i) => i.id).join(\",\") }, `${items.length === 1 ? \"One queue item is\" : `${items.length} queue items are`} ready for review: ${list}.`));\n }\n\n /** Send what the throttle held back; call once the run is over. */\n async finish(): Promise<void> {\n this.sendReady();\n await this.flush();\n }\n}\n\n// ---------------------------------------------------------------------------\n// detach: registry of runs that outlive their session\n\nexport interface RunRecord {\n id: string;\n kind: \"plan\" | \"queue\";\n pid: number;\n argv: string[];\n cwd: string;\n origin: RunOrigin;\n started: string;\n output: string;\n}\n\nexport const runsDir = (logDir: string): string => join(logDir, \"runs\");\nexport const runRecordPath = (logDir: string, id: string): string => join(runsDir(logDir), `${id}.json`);\n\nexport interface DetachDeps {\n /** Start `bin args` detached with stdout/stderr to `outFd`; returns the pid. */\n spawnDetached(bin: string, args: string[], opts: { cwd: string; env: NodeJS.ProcessEnv; outPath: string }): number;\n now(): number;\n}\n\nexport const realDetachDeps: DetachDeps = {\n spawnDetached(bin, args, { cwd, env, outPath }) {\n const fd = openSync(outPath, \"a\");\n const child = spawn(bin, args, { cwd, env, detached: true, stdio: [\"ignore\", fd, fd] });\n child.unref();\n return child.pid ?? 0;\n },\n now: Date.now,\n};\n\n/**\n * Hand a run to a detached process and register it. `argv` is the pai\n * command line of the run without --detach; the origin travels in the env so\n * the child reports to the session that started it.\n */\nexport function detachRun(\n logDir: string,\n kind: \"plan\" | \"queue\",\n label: string,\n bin: string,\n argv: string[],\n cwd: string,\n origin: RunOrigin,\n deps: DetachDeps = realDetachDeps,\n): RunRecord {\n mkdirSync(runsDir(logDir), { recursive: true });\n const stamp = new Date(deps.now()).toISOString().replace(/[-:T]/g, \"\").slice(0, 14);\n const id = `${kind}-${label.replace(/[^\\w.-]/g, \"_\")}-${stamp}`;\n const output = join(runsDir(logDir), `${id}.out`);\n const pid = deps.spawnDetached(bin, argv, { cwd, env: { ...process.env, PAI_RUN_ORIGIN: JSON.stringify(origin), PAI_RUN_ID: id }, outPath: output });\n const rec: RunRecord = { id, kind, pid, argv: [bin, ...argv], cwd, origin, started: new Date(deps.now()).toISOString(), output };\n writeFileSync(runRecordPath(logDir, id), JSON.stringify(rec, null, 2) + \"\\n\", \"utf8\");\n return rec;\n}\n\nexport function listRuns(logDir: string): RunRecord[] {\n const dir = runsDir(logDir);\n if (!existsSync(dir)) return [];\n const out: RunRecord[] = [];\n for (const f of readdirSync(dir)) {\n if (!f.endsWith(\".json\")) continue;\n try {\n out.push(JSON.parse(readFileSync(join(dir, f), \"utf8\")) as RunRecord);\n } catch {\n /* torn record */\n }\n }\n return out;\n}\n\n/** The detached child calls this on a clean end, so the sweep does not report it as dead. */\nexport function markRunDone(logDir: string, id: string, now: () => number = Date.now): void {\n try {\n writeFileSync(`${runRecordPath(logDir, id)}.done`, new Date(now()).toISOString(), \"utf8\");\n } catch {\n /* the registry is a courtesy; the run's own result stands */\n }\n}\n\n/**\n * Daemon tick: a registered run whose process is gone without a done marker\n * died silently; tell its session (and PAILot) once.\n */\nexport async function sweepRuns(logDir: string, msg: Messenger, isAlive: (pid: number) => boolean, now: () => number = Date.now): Promise<string[]> {\n const told: string[] = [];\n for (const r of listRuns(logDir)) {\n const marker = `${runRecordPath(logDir, r.id)}.done`;\n if (existsSync(marker) || isAlive(r.pid)) continue;\n markRunDone(logDir, r.id, now);\n const what = `The detached ${r.kind} run ${r.id} stopped without finishing; see ${r.output}.`;\n await msg.toSession(r.origin, eventLine(r.id, \"run-died\", { output: r.output }, what)).catch(() => false);\n await msg.toPailot(what).catch(() => false);\n told.push(r.id);\n }\n return told;\n}\n","/**\n * worker-trouble.ts — tell the session that started a worker when PAI's guards\n * hit it. Fed by the supervising `pai worker run` process (the one place each\n * denial is counted once), over the run-events messenger: session only, never\n * PAILot. At most one message per worker per TROUBLE_THROTTLE_MS; the hard\n * stop is always sent. A delivery failure is the messenger's to log.\n */\n\nimport { eventLine, type Messenger, type RunOrigin } from \"./run-events.js\";\n\nexport const TROUBLE_THROTTLE_MS = 5 * 60_000;\nexport const DENIAL_WINDOW_MS = 10 * 60_000;\nexport const DENIAL_THRESHOLD = 3;\nexport const STALL_MS = 10 * 60_000;\n\nconst short = (s: string, n: number): string => s.replace(/\\s+/g, \" \").trim().slice(0, n);\n\nexport class TroubleWatcher {\n private denials: number[] = [];\n private lastSent = -Infinity;\n private lastActivity: number;\n private stallTold = false;\n private chain: Promise<unknown> = Promise.resolve();\n\n constructor(\n private readonly id: string,\n private readonly label: string,\n private readonly msg: Messenger,\n private readonly origin: RunOrigin,\n private readonly now: () => number = Date.now,\n ) {\n this.lastActivity = now();\n }\n\n private send(kind: string, fields: Record<string, string | number>, sentence: string, force = false): void {\n const t = this.now();\n if (!force && t - this.lastSent < TROUBLE_THROTTLE_MS) return;\n this.lastSent = t;\n const text = eventLine(this.id, kind, fields, sentence);\n this.chain = this.chain.then(() => this.msg.toSession(this.origin, text).catch(() => false));\n }\n\n private get who(): string {\n return `worker ${this.id}${this.label ? ` (${short(this.label, 40)})` : \"\"}`;\n }\n\n /** Any sign of life (a transcript line); resets the stall clock. */\n activity(): void {\n this.lastActivity = this.now();\n this.stallTold = false;\n }\n\n /** One guard denial; from the 3rd in the window on, tell the session (throttled). */\n onDenial(hook: string, reason: string): void {\n const t = this.now();\n this.denials = this.denials.filter((d) => t - d < DENIAL_WINDOW_MS);\n this.denials.push(t);\n const n = this.denials.length;\n if (n < DENIAL_THRESHOLD) return;\n const guard = hook.split(\"/\").pop()?.replace(/\\.(mjs|js|ts)$/, \"\") || \"hook\";\n this.send(\"guard-denials\", { denied: n, guard }, `${this.who} blocked ${n}x by ${guard} in ${Math.round(DENIAL_WINDOW_MS / 60_000)} min: ${short(reason, 120)}`);\n }\n\n /** The loop guard's hard stop; always sent. */\n onLoopStop(reason: string): void {\n this.send(\"loop-stopped\", {}, `${this.who} was stopped by the loop guard: ${short(reason, 160)}`, true);\n }\n\n /** Timer tick while running: no activity for STALL_MS tells once per quiet spell (throttled). */\n tick(): void {\n const quiet = this.now() - this.lastActivity;\n if (quiet < STALL_MS || this.stallTold) return;\n this.stallTold = true;\n this.send(\"stalled\", { quiet_min: Math.round(quiet / 60_000) }, `${this.who} has produced no output for ${Math.round(quiet / 60_000)} min while running.`);\n }\n\n async flush(): Promise<void> {\n await this.chain;\n }\n}\n","/**\n * usage.ts — exact token tallies per model from stream events / transcripts.\n *\n * `sumUsage` (each message id once, its last usage) serves the transcript\n * side, `pai audit tokens usage`; the worker side, `pai worker usage`, reads\n * a run log through `sumRunUsage`, which prefers the run's `result` event —\n * the harness books the run's authoritative totals there, and local servers\n * that report no per-turn counts still show their tokens (src/router/\n * prompt-tokens.ts makes the router fill what the server never reported).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { eventsPath } from \"./paths.js\";\nimport { loadStatuses, type WorkerStatus } from \"./status.js\";\nimport { isLocalProvider } from \"./config.js\";\nimport type { UsageBlock } from \"../utils/input-side-tokens.js\";\n\nexport interface CacheCreationSplit {\n ephemeral_5m_input_tokens?: number;\n ephemeral_1h_input_tokens?: number;\n}\n\nexport type RichUsage = UsageBlock & { cache_creation?: CacheCreationSplit };\n\nexport interface Tally {\n input: number;\n cacheRead: number;\n cacheWrite5m: number;\n cacheWrite1h: number;\n output: number;\n}\n\nexport const emptyTally = (): Tally => ({ input: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, output: 0 });\n\nexport function addTally(into: Tally, t: Tally): void {\n into.input += t.input;\n into.cacheRead += t.cacheRead;\n into.cacheWrite5m += t.cacheWrite5m;\n into.cacheWrite1h += t.cacheWrite1h;\n into.output += t.output;\n}\n\n/** One usage block as a tally; a cache write without a 5m/1h split counts as 5m. */\nexport function tallyOf(u: RichUsage): Tally {\n const split5 = u.cache_creation?.ephemeral_5m_input_tokens;\n const split1 = u.cache_creation?.ephemeral_1h_input_tokens;\n const hasSplit = split5 !== undefined || split1 !== undefined;\n return {\n input: u.input_tokens ?? 0,\n cacheRead: u.cache_read_input_tokens ?? 0,\n cacheWrite5m: hasSplit ? (split5 ?? 0) : (u.cache_creation_input_tokens ?? 0),\n cacheWrite1h: hasSplit ? (split1 ?? 0) : 0,\n output: u.output_tokens ?? 0,\n };\n}\n\nexport interface SumResult {\n byModel: Map<string, Tally>;\n /** total_cost_usd of the last result event, when there was one. */\n costUsd: number | null;\n}\n\n/** camelCase per-model totals of a result event's modelUsage block as a tally. */\nfunction tallyOfModelUsage(mu: Record<string, unknown>): Tally {\n const num = (v: unknown): number => (typeof v === \"number\" && Number.isFinite(v) ? v : 0);\n return {\n input: num(mu.inputTokens),\n cacheRead: num(mu.cacheReadInputTokens),\n cacheWrite5m: num(mu.cacheCreationInputTokens),\n cacheWrite1h: 0,\n output: num(mu.outputTokens),\n };\n}\n\nconst tallyIsZero = (t: Tally): boolean => !t.input && !t.cacheRead && !t.cacheWrite5m && !t.cacheWrite1h && !t.output;\n\n/**\n * Sum usage of a *run* event log (a worker's, as opposed to a transcript):\n * the run's `result` event is authoritative when it carries usage or\n * modelUsage — local servers report per-turn counts as 0, and the harness\n * fills them only into the result event. Without a result event (killed run)\n * the assistant turns are summed like a transcript. `fallbackModel` books\n * result-event usage that names no model on the run's own model.\n */\nexport function sumRunUsage(jsonl: string, fallbackModel?: string): SumResult {\n const perTurn = sumUsage(jsonl);\n let result: { model?: string; usage?: RichUsage; modelUsage?: Record<string, Record<string, unknown>> } | undefined;\n for (const line of jsonl.split(\"\\n\")) {\n if (!line.includes('\"result\"')) continue;\n try {\n const e = JSON.parse(line) as { type?: string; model?: string; usage?: RichUsage; modelUsage?: Record<string, Record<string, unknown>> };\n if (e.type === \"result\") result = e;\n } catch {\n /* torn line */\n }\n }\n if (!result) return perTurn;\n const byModel = new Map<string, Tally>();\n const mu = result.modelUsage;\n if (mu && typeof mu === \"object\" && Object.keys(mu).length) {\n const named: [string, Tally][] = [];\n for (const [key, v] of Object.entries(mu)) {\n if (!v || typeof v !== \"object\") continue;\n const model = typeof (v as { canonicalModel?: unknown }).canonicalModel === \"string\" ? (v as { canonicalModel: string }).canonicalModel : key;\n named.push([model, tallyOfModelUsage(v)]);\n }\n const whole = result.usage ? tallyOf(result.usage) : undefined;\n if (named.length === 1 && whole && !tallyIsZero(whole)) {\n // modelUsage has no 5m/1h split; the result's usage block does, and it is the same run total\n byModel.set(named[0][0], whole);\n } else if (named.length > 1 && whole && whole.cacheWrite5m + whole.cacheWrite1h > 0) {\n // modelUsage has per-model totals but no cache TTL split: apply the run-wide 1h share to each\n // model, rounding by cumulative position so the per-model sums stay exact\n const total = whole.cacheWrite5m + whole.cacheWrite1h;\n let seen = 0;\n let given1h = 0;\n for (const [model, t] of named) {\n const write = t.cacheWrite5m;\n seen += write;\n const upTo1h = Math.round((seen * whole.cacheWrite1h) / total);\n const w1h = Math.min(write, upTo1h - given1h);\n given1h += w1h;\n byModel.set(model, { ...t, cacheWrite5m: write - w1h, cacheWrite1h: w1h });\n }\n } else {\n for (const [model, t] of named) byModel.set(model, t);\n }\n } else if (result.usage && !tallyIsZero(tallyOf(result.usage))) {\n const model = result.model ?? fallbackModel ?? (perTurn.byModel.size === 1 ? [...perTurn.byModel.keys()][0] : \"?\");\n byModel.set(model, tallyOf(result.usage));\n }\n return byModel.size ? { byModel, costUsd: perTurn.costUsd } : perTurn;\n}\n\n/** Sum assistant usage of JSONL text: each message id once (its last usage), grouped by model. */\nexport function sumUsage(jsonl: string): SumResult {\n const byId = new Map<string, { model: string; usage: RichUsage }>();\n let anon = 0;\n let costUsd: number | null = null;\n for (const line of jsonl.split(\"\\n\")) {\n if (!line.trim()) continue;\n let e: { type?: string; total_cost_usd?: number; message?: { id?: string; model?: string; usage?: RichUsage } };\n try {\n e = JSON.parse(line);\n } catch {\n continue;\n }\n if (e.type === \"result\" && typeof e.total_cost_usd === \"number\") costUsd = e.total_cost_usd;\n if (e.type !== \"assistant\" || !e.message?.usage) continue;\n const m = e.message;\n byId.set(m.id ?? `anon-${anon++}`, { model: m.model ?? \"?\", usage: m.usage! });\n }\n const byModel = new Map<string, Tally>();\n for (const { model, usage } of byId.values()) {\n const t = byModel.get(model) ?? emptyTally();\n addTally(t, tallyOf(usage));\n byModel.set(model, t);\n }\n return { byModel, costUsd };\n}\n\nexport interface UsageRow {\n provider: string;\n model: string;\n local: boolean;\n workers: number;\n tally: Tally;\n /** Summed total_cost_usd; null for local rows. */\n costUsd: number | null;\n}\n\nexport interface UsageReport {\n rows: UsageRow[];\n cloud: UsageRow;\n local: UsageRow;\n}\n\nconst totalRow = (provider: string, local: boolean): UsageRow => ({\n provider,\n model: \"\",\n local,\n workers: 0,\n tally: emptyTally(),\n costUsd: local ? null : 0,\n});\n\nexport function finishReport(rows: UsageRow[]): UsageReport {\n const cloud = totalRow(\"TOTAL cloud\", false);\n const local = totalRow(\"TOTAL local\", true);\n for (const r of rows) {\n const t = r.local ? local : cloud;\n t.workers += r.workers;\n addTally(t.tally, r.tally);\n if (!r.local) t.costUsd = (t.costUsd ?? 0) + (r.costUsd ?? 0);\n }\n return { rows, cloud, local };\n}\n\nexport interface WorkerSelect {\n labelPrefix?: string;\n since?: Date;\n until?: Date;\n}\n\n/** \"1h\", \"30m\", \"2d\" relative to now, or an ISO timestamp. */\nexport function parseWhen(s: string, now = new Date()): Date {\n const rel = /^(\\d+)([smhd])$/.exec(s.trim());\n if (rel) {\n const mult = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 }[rel[2] as \"s\" | \"m\" | \"h\" | \"d\"];\n return new Date(now.getTime() - Number(rel[1]) * mult);\n }\n const d = new Date(s);\n if (Number.isNaN(d.getTime())) throw new Error(`cannot read \"${s}\" as a time; use an ISO timestamp or 1h / 30m`);\n return d;\n}\n\n/** Status `started` is local \"YYYY-MM-DD HH:MM:SS\" (or ISO). */\nfunction startedAt(s: WorkerStatus): Date | null {\n const d = new Date(s.started.includes(\"T\") ? s.started : s.started.replace(\" \", \"T\"));\n return Number.isNaN(d.getTime()) ? null : d;\n}\n\nexport function selectWorkers(statuses: WorkerStatus[], sel: WorkerSelect): WorkerStatus[] {\n return statuses.filter((s) => {\n if (sel.labelPrefix && !(s.label ?? \"\").startsWith(sel.labelPrefix)) return false;\n const at = startedAt(s);\n if (sel.since && (!at || at < sel.since)) return false;\n if (sel.until && (!at || at > sel.until)) return false;\n return true;\n });\n}\n\n/** Tally the selected workers; `baseUrls` maps provider name to its baseUrl (for the local mark). */\nexport function workerUsage(logDir: string, sel: WorkerSelect, baseUrls: Record<string, string>): UsageReport {\n const groups = new Map<string, UsageRow>();\n for (const st of selectWorkers(loadStatuses(logDir), sel)) {\n const path = eventsPath(logDir, st.id);\n if (!existsSync(path)) continue;\n const { byModel, costUsd } = sumRunUsage(readFileSync(path, \"utf8\"), st.model);\n if (byModel.size === 0) continue;\n const local = isLocalProvider(baseUrls[st.provider] ?? \"\");\n const models = [...byModel.keys()];\n // the run's cost is one number; book it on the run's own model, else the last one seen\n const costModel = models.includes(st.model) ? st.model : models[models.length - 1];\n for (const [model, tally] of byModel) {\n const key = `${st.provider}\\u0000${model}`;\n const row = groups.get(key) ?? { provider: st.provider, model, local, workers: 0, tally: emptyTally(), costUsd: local ? null : 0 };\n row.workers += 1;\n addTally(row.tally, tally);\n if (!local && model === costModel) row.costUsd = (row.costUsd ?? 0) + (costUsd ?? 0);\n groups.set(key, row);\n }\n }\n return finishReport([...groups.values()].sort((a, b) => a.provider.localeCompare(b.provider) || a.model.localeCompare(b.model)));\n}\n\n// ---------------------------------------------------------------------------\n// Transcript side (tier 0): cost from a price table\n// ---------------------------------------------------------------------------\n\n/** USD per million tokens. Edit here when prices change; cache read 0.1x, write 5m 1.25x, write 1h 2x of input. */\nexport const PRICE_TABLE: Record<\"opus\" | \"sonnet\" | \"haiku\", { input: number; output: number }> = {\n opus: { input: 5, output: 25 },\n sonnet: { input: 3, output: 15 },\n haiku: { input: 1, output: 5 },\n};\nexport const CACHE_READ_X = 0.1;\nexport const CACHE_WRITE_5M_X = 1.25;\nexport const CACHE_WRITE_1H_X = 2;\n\nexport function priceTableText(): string {\n const lines = Object.entries(PRICE_TABLE).map(([f, p]) => ` ${f.padEnd(7)} input $${p.input}/M, output $${p.output}/M`);\n return `Prices (USD per million tokens, matched on the model name):\\n${lines.join(\"\\n\")}\\n cache read ${CACHE_READ_X}x input, cache write 5m ${CACHE_WRITE_5M_X}x, cache write 1h ${CACHE_WRITE_1H_X}x`;\n}\n\n/** Cost of a tally by model family, or null when the model matches no family. */\nexport function priceTally(model: string, t: Tally): number | null {\n const fam = (Object.keys(PRICE_TABLE) as Array<keyof typeof PRICE_TABLE>).find((f) => model.toLowerCase().includes(f));\n if (!fam) return null;\n const p = PRICE_TABLE[fam];\n const inp = p.input / 1e6;\n return t.input * inp + t.output * (p.output / 1e6) + t.cacheRead * inp * CACHE_READ_X + t.cacheWrite5m * inp * CACHE_WRITE_5M_X + t.cacheWrite1h * inp * CACHE_WRITE_1H_X;\n}\n\nexport function transcriptUsage(path: string): UsageReport {\n const { byModel } = sumUsage(readFileSync(path, \"utf8\"));\n const rows: UsageRow[] = [...byModel.entries()]\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([model, tally]) => ({ provider: \"transcript\", model, local: false, workers: 0, tally, costUsd: priceTally(model, tally) }));\n const rep = finishReport(rows);\n rep.cloud.provider = \"TOTAL\";\n return rep;\n}\n\n// ---------------------------------------------------------------------------\n// Rendering\n// ---------------------------------------------------------------------------\n\nconst n = (x: number) => x.toLocaleString(\"en-US\");\nconst usd = (x: number | null) => (x === null ? \"—\" : x.toFixed(4));\n\nexport function renderUsage(rep: UsageReport, opts: { workers: boolean }): string {\n const head = [\"PROVIDER\", \"MODEL\", ...(opts.workers ? [\"WORKERS\"] : []), \"INPUT\", \"CACHE READ\", \"CACHE WRITE 5m\", \"CACHE WRITE 1h\", \"OUTPUT\", \"COST USD\"];\n const cells = (r: UsageRow) => [\n r.provider + (r.local && !r.provider.startsWith(\"TOTAL\") ? \" (local)\" : \"\"),\n r.model,\n ...(opts.workers ? [String(r.workers)] : []),\n n(r.tally.input),\n n(r.tally.cacheRead),\n n(r.tally.cacheWrite5m),\n n(r.tally.cacheWrite1h),\n n(r.tally.output),\n r.local ? \"—\" : usd(r.costUsd),\n ];\n const body = [...rep.rows.map(cells)];\n const totals = opts.workers ? [cells(rep.cloud), cells(rep.local)] : [cells(rep.cloud)];\n const all = [head, ...body, ...totals];\n const w = head.map((_, i) => Math.max(...all.map((r) => r[i].length)));\n const textCols = 2;\n const fmt = (r: string[]) => r.map((c, i) => (i < textCols ? c.padEnd(w[i]) : c.padStart(w[i]))).join(\" \");\n return [fmt(head), ...body.map(fmt), fmt(w.map((x) => \"-\".repeat(x))), ...totals.map(fmt)].join(\"\\n\");\n}\n\nexport function usageJson(rep: UsageReport): unknown {\n const row = (r: UsageRow) => ({ provider: r.provider, model: r.model, local: r.local, workers: r.workers, ...r.tally, costUsd: r.costUsd });\n return { rows: rep.rows.map(row), totalCloud: row(rep.cloud), totalLocal: row(rep.local) };\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 { ledgerPath, panesDir, statusPath } from \"./paths.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { paiHomePath } from \"../config/pai-home.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 function arr(f) { try { return f() || []; } catch (e) { return []; } }\n var ws = arr(function () { return app.windows(); });\n for (var i = 0; i < ws.length; i++) {\n if (!ws[i]) continue;\n var ts = arr(function () { return ws[i].tabs(); });\n for (var j = 0; j < ts.length; j++) {\n if (!ts[j]) continue;\n var ss = arr(function () { return ts[j].sessions(); });\n for (var k = 0; k < ss.length; k++) {\n try { if (ss[k] && ss[k].id() === id) return { w: ws[i], t: ts[j], s: ss[k], all: ss }; } catch (e) {}\n }\n }\n }\n return null;\n}`;\n\n// A split or new tab makes the new session the active one. Capture what was\n// active before and put it back afterwards, so typing focus stays with the\n// operator's session. Only restores a selection inside the app's own window:\n// never activates iTerm, raises a window or touches the frontmost app.\nconst CAPTURE_ACTIVE = `var prevTab = null, prevSess = null;\n try { prevTab = f.w.currentTab(); prevSess = prevTab.currentSession(); } catch (e) {}`;\nconst RESTORE_ACTIVE = `try {\n if (prevTab && f.w.currentTab().id() !== prevTab.id()) prevTab.select();\n if (prevSess && prevTab.currentSession().id() !== prevSess.id()) prevSess.select();\n } catch (e) {}`;\n\n// Smallest height a follow pane may be split down to (the visible-pane gate\n// refuses anything under 6 rows).\nconst MIN_ROWS = 6;\n\n// Equalise the follow panes' heights. iTerm exposes no pane position, so panes\n// are grouped by column count: panes stacked in one column share it, and a\n// group of equal width splits its summed rows evenly. ponytail: two columns of\n// equal width but unequal stack height are balanced as one group; upgrade to\n// real geometry if iTerm ever exposes it. Expects the `live` session list.\nconst REBALANCE = `var groups = {};\n live.forEach(function (o) { var c = o.columns(); (groups[c] = groups[c] || []).push([o, o.rows()]); });\n Object.keys(groups).forEach(function (c) {\n var g = groups[c];\n var want = Math.floor(g.reduce(function (a, e) { return a + e[1]; }, 0) / g.length);\n g.forEach(function (e) { try { e[0].rows = want; } catch (x) {} });\n });`;\n\n// \"id<TAB>columns<TAB>rows\" of every session in the launching session's tab.\nexport const TAB_SIZES_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.id() + \"\\\\t\" + o.columns() + \"\\\\t\" + o.rows(); }).join(\"\\\\n\");\n}`;\n\nexport interface TabPane {\n id: string;\n cols: number;\n rows: number;\n}\nexport interface SplitChoice {\n id: string;\n dir: \"h\" | \"v\";\n cols: number;\n rows: number;\n}\n\nconst MIN_COLS = 40;\n\n/** Smaller half of a split: \"h\" stacks top/bottom, \"v\" puts the halves side by side (one cell for the divider). */\nfunction halves(p: TabPane, dir: \"h\" | \"v\"): { cols: number; rows: number } {\n return dir === \"h\" ? { cols: p.cols, rows: Math.floor((p.rows - 1) / 2) } : { cols: Math.floor((p.cols - 1) / 2), rows: p.rows };\n}\nconst fits = (h: { cols: number; rows: number }) => h.cols >= MIN_COLS && h.rows >= MIN_ROWS;\n\n/**\n * Pick the pane to split and the direction from the tab's real sizes: the pane\n * with the most cells whose result keeps >= 40x6, never the host's own pane\n * while another fits. Side by side only if both halves keep >= 80 columns or\n * the pane is wider than 3.5x its height in cells (a cell is ~2x taller than wide), else stacked; the other direction is\n * the fallback when the preferred one leaves too small a pane. null = nothing fits.\n */\nexport function pickSplit(panes: TabPane[], hostId: string): (SplitChoice & { small: boolean }) | null {\n const options: Array<SplitChoice & { host: boolean; cells: number; small: boolean }> = [];\n for (const p of panes) {\n const wide = Math.floor((p.cols - 1) / 2) >= 80 || p.cols > 3.5 * p.rows;\n const order: Array<\"h\" | \"v\"> = wide ? [\"v\", \"h\"] : [\"h\", \"v\"];\n const dir = order.find((d) => fits(halves(p, d)));\n if (dir) options.push({ id: p.id, dir, ...halves(p, dir), host: p.id === hostId, cells: p.cols * p.rows, small: false });\n }\n const pool = options.some((o) => !o.host) ? options.filter((o) => !o.host) : options;\n pool.sort((a, b) => b.cells - a.cells);\n if (pool[0]) return pool[0];\n // nothing reaches the minimum: a small pane never stops a start, so take the split whose smaller half is largest\n let best: (SplitChoice & { small: boolean; score: number }) | null = null;\n for (const p of panes) {\n for (const d of [\"h\", \"v\"] as const) {\n const h = halves(p, d);\n const score = Math.min(h.cols / MIN_COLS, h.rows / MIN_ROWS) - (p.id === hostId ? 0.001 : 0);\n if (h.cols > 0 && h.rows > 0 && (!best || score > best.score)) best = { id: p.id, dir: d, ...h, small: true, score };\n }\n }\n return best ? { id: best.id, dir: best.dir, cols: best.cols, rows: best.rows, small: true } : null;\n}\n\n// One pane per worker: split the pane pickSplit chose (argv[4]) side by side\n// (\"v\") or stacked (\"h\", argv[5]). 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 ${CAPTURE_ACTIVE}\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 live = [];\n (candList === \"\" ? [] : candList.split(\",\")).forEach(function (cid) {\n var at = sessIDs.indexOf(cid);\n if (at < 0) return;\n lived.push(cid);\n live.push(f.all[at]);\n });\n // the pane and direction were chosen in TypeScript from the tab's real sizes (pickSplit)\n var splitS = f.all[sessIDs.indexOf(argv[4])] || f.s;\n var stack = argv[5] === \"h\";\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 (!stack) {\n newS = profileName === \"\"\n ? splitS.splitVerticallyWithDefaultProfile({ command: followCmd })\n : splitS.splitVertically({ withProfile: profileName, command: followCmd });\n } else {\n newS = profileName === \"\"\n ? splitS.splitHorizontallyWithDefaultProfile({ command: followCmd })\n : splitS.splitHorizontally({ withProfile: profileName, command: followCmd });\n }\n live.push(newS);\n ${REBALANCE}\n ${RESTORE_ACTIVE}\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 ${CAPTURE_ACTIVE}\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 ${RESTORE_ACTIVE}\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. */\nexport { osascript as osascriptJxa };\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 /** iTerm uuid of the session that started the process: its tab is where the pane must be */\n host?: 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\nexport function 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, ...(e.host ? { host: e.host } : {}) }, 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\n// One listing for every caller. Each property is read on its own: iTerm 3.7 can\n// throw on name() for a session, which must not drop the others.\nconst LIST_SESSIONS_SCRIPT = `function run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"\";\n function get(s, k) { try { return String(s[k]()); } catch (e) { return \"\"; } }\n function arr(f) { try { return f() || []; } catch (e) { return []; } }\n var out = \"\";\n arr(function () { return app.windows(); }).forEach(function (w) {\n if (!w) return;\n arr(function () { return w.tabs(); }).forEach(function (t) {\n if (!t) return;\n arr(function () { return t.sessions(); }).forEach(function (s) {\n if (!s) return;\n var id = get(s, \"id\");\n function v(n) { try { return String(s.variable({ named: n })); } catch (e) { return \"\"; } }\n if (id) out += id + \"\\\\t\" + get(s, \"tty\") + \"\\\\t\" + v(\"jobPid\") + \"\\\\t\" + v(\"pid\") + \"\\\\t\" + get(s, \"name\") + \"\\\\n\";\n });\n });\n });\n return out;\n}`;\n\nexport interface ItermSession {\n id: string;\n tty: string;\n /** Pid of the session's foreground job (variable \"jobPid\"), undefined when iTerm gave none. */\n jobPid?: number;\n /** Pid of the session's root process (variable \"pid\"). */\n pid?: number;\n /** Undefined when iTerm would not give the session's name. */\n name?: string;\n}\n\n/** Every open iTerm session. [] when iTerm is not running; rejects when the script fails twice. */\nexport async function listItermSessions(\n run: (script: string, args: string[]) => Promise<{ stdout: string }> = osascript\n): Promise<ItermSession[]> {\n let p: { stdout: string };\n try {\n p = await run(LIST_SESSIONS_SCRIPT, []);\n } catch {\n await new Promise((r) => setTimeout(r, 300)); // a window may be mid-close; one retry\n p = await run(LIST_SESSIONS_SCRIPT, []);\n }\n return p.stdout\n .split(\"\\n\")\n .filter(Boolean)\n .map((l) => {\n const [id, tty, jobPid, pid, ...name] = l.split(\"\\t\");\n return { id, tty, jobPid: Number(jobPid) || undefined, pid: Number(pid) || undefined, name: name.join(\"\\t\") || undefined };\n });\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// Re-balance the follow panes (argv[0]: comma-joined session ids) after a close.\nexport const REBALANCE_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var ids = argv[0].split(\",\");\n var app = Application(\"iTerm2\");\n if (!app.running()) return \"notrunning\";\n var f = null;\n for (var i = 0; i < ids.length && !f; i++) f = findSession(app, ids[i]);\n if (!f) return \"notfound\";\n var winBounds = f.w.bounds();\n var live = f.all.filter(function (o) { return ids.indexOf(o.id()) >= 0; });\n ${REBALANCE}\n try { f.w.bounds = winBounds; } catch (e) {}\n return \"balanced\";\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 /** Equalise the heights of the given follow panes (optional: tests omit it). */\n rebalanceIterm?(ids: string[]): Promise<void>;\n}\n\nexport const realPaneOps: PaneOps = {\n async listIterm() {\n if (process.platform !== \"darwin\") return [];\n const sessions = await listItermSessions();\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 sessions.map((x) => ({ id: x.id, name: `${x.name ?? \"\"} ${cmdByTty.get(x.tty) ?? \"\"}`.trim() }));\n },\n async closeIterm(id) {\n const p = await osascript(END_SESSION_SCRIPT, [id]);\n return p.stdout.trim() === \"closed\";\n },\n async rebalanceIterm(ids) {\n if (ids.length) await osascript(REBALANCE_SCRIPT, [ids.join(\",\")]);\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\n/**\n * The follow pane closing its own iTerm session on exit, so a pane closes even\n * when the runner died and even under a profile without Close Sessions On End.\n * No-op outside iTerm.\n */\nexport async function closeOwnPane(env: NodeJS.ProcessEnv = process.env, ops: PaneOps = realPaneOps): Promise<boolean> {\n const uid = itermUuid(env.ITERM_SESSION_ID ?? \"\");\n if (!uid) return false;\n return ops.closeIterm(uid).catch(() => false);\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 // iTerm unreachable: no live-session view, registry entries are still handled\n const sessions = await ops.listIterm().catch(() => []);\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 // closed panes leave uneven neighbours: re-balance what is left\n if (!dryRun && res.closed.length) {\n await ops.rebalanceIterm?.(loadRegistry(logDir).map((e) => e.session)).catch(() => undefined);\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 * No --auto-exit: a finished worker's pane stays until the operator closes it\n * (exit the follow) or the next pane opened in the same scope replaces it.\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): string {\n return `${process.execPath} ${absoluteCliPath()} worker follow ${wid}`;\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\n// ---------------------------------------------------------------------------\n// Host session — which iTerm session launched this process\n// ---------------------------------------------------------------------------\n\n/** Lookups behind resolveHostSession, swappable so tests never touch ps or iTerm. */\nexport interface HostDeps {\n /** [ppid, tty] of a pid, null when ps cannot say. tty is as ps prints it (\"ttys004\", \"??\"). */\n psOf(pid: number): [number, string] | null;\n /** Command line per pid, one batched ps call; pids ps does not know are absent. */\n commandsOf(pids: number[]): Map<number, string>;\n /** Every open iTerm session. Empty when iTerm is not running; rejects when the listing fails. */\n listSessions(): Promise<ItermSession[]>;\n /** Pids of `claude attach <id>` client processes for a Claude session (short or full id). */\n attachClients?(cid: string): number[];\n /** ITERM_SESSION_ID in a process's environment, \"\" when unreadable or unset. */\n itermIdOf?(pid: number): string;\n /** iTerm session uid the AIBroker registry records for a Claude session id, \"\" when none. */\n registryHost?(cid: string): string;\n /** iTerm session uid the statusline recorded for a Claude session id (short or full) in session-claude-map.json, \"\" when none. */\n mapHost?(cid: string): string;\n}\n\n/** Entries older than this are dropped by the statusline writer and ignored here. */\nexport const SESSION_MAP_MAX_AGE_MS = 7 * 24 * 3600_000;\n\n/** The iTerm uid recorded for a Claude session id (prefix match for short ids), newest entry wins, \"\" when none or stale. */\nexport function lookupSessionMap(mapPath: string, cid: string, now: number = Date.now()): string {\n try {\n const m = JSON.parse(readFileSync(mapPath, \"utf8\")) as Record<string, { uid?: unknown; ts?: unknown }>;\n const hits = Object.entries(m)\n .filter(([k, v]) => (k === cid || (cid.length >= 8 && k.startsWith(cid))) && typeof v?.uid === \"string\" && typeof v.ts === \"number\" && now - v.ts < SESSION_MAP_MAX_AGE_MS)\n .sort((a, b) => (b[1].ts as number) - (a[1].ts as number));\n return (hits[0]?.[1].uid as string | undefined) ?? \"\";\n } catch {\n return \"\";\n }\n}\n\nexport const realHostDeps: HostDeps = {\n psOf(pid) {\n try {\n const out = execFileSync(\"ps\", [\"-o\", \"ppid=,tty=\", \"-p\", String(pid)], {\n encoding: \"utf8\",\n env: { ...process.env, LC_ALL: \"C\" },\n }).trim();\n const [ppid, tty] = out.split(/\\s+/);\n return ppid && tty ? [Number(ppid), tty] : null;\n } catch {\n return null;\n }\n },\n commandsOf(pids) {\n const m = new Map<number, string>();\n if (!pids.length) return m;\n let out = \"\";\n try {\n out = execFileSync(\"ps\", [\"-o\", \"pid=,command=\", \"-p\", pids.join(\",\")], {\n encoding: \"utf8\",\n env: { ...process.env, LC_ALL: \"C\" },\n });\n } catch (e) {\n out = (e as { stdout?: string }).stdout ?? \"\"; // ps exits 1 when some pid is gone\n }\n for (const l of out.split(\"\\n\")) {\n const r = /^\\s*(\\d+)\\s+(.*)$/.exec(l);\n if (r) m.set(Number(r[1]), r[2]);\n }\n return m;\n },\n async listSessions() {\n if (process.platform !== \"darwin\") return [];\n return listItermSessions(); // rejects on failure: resolveHostSession reports why\n },\n attachClients(cid) {\n const short = cid.slice(0, 8);\n const re = new RegExp(`\\\\battach\\\\s+(${short}|${cid})\\\\b`, \"i\");\n let out = \"\";\n try {\n out = execFileSync(\"ps\", [\"-axo\", \"pid=,command=\"], { encoding: \"utf8\", env: { ...process.env, LC_ALL: \"C\" }, maxBuffer: 32 << 20 });\n } catch {\n return [];\n }\n return out.split(\"\\n\").flatMap((l) => {\n const r = /^\\s*(\\d+)\\s+(.*)$/.exec(l);\n return r && re.test(r[2]) && !/--bg-pty-host/.test(r[2]) ? [Number(r[1])] : [];\n });\n },\n itermIdOf(pid) {\n try {\n const out = execFileSync(\"ps\", [\"eww\", \"-o\", \"command=\", \"-p\", String(pid)], { encoding: \"utf8\", env: { ...process.env, LC_ALL: \"C\" } });\n return /\\bITERM_SESSION_ID=(\\S+)/.exec(out)?.[1] ?? \"\";\n } catch {\n return \"\";\n }\n },\n mapHost(cid) {\n return lookupSessionMap(paiHomePath(\"session-claude-map.json\"), cid);\n },\n registryHost(cid) {\n try {\n const dir = join(homedir(), \".aibroker\");\n const entries = (JSON.parse(readFileSync(join(dir, \"session-restore.json\"), \"utf8\")) as { entries?: Record<string, unknown>[] }).entries ?? [];\n const hit = entries.find((e) => [e.claudeSessionId, e.sessionId].some((v) => typeof v === \"string\" && (v === cid || v.startsWith(cid))));\n if (!hit || typeof hit.name !== \"string\") return \"\";\n const names = JSON.parse(readFileSync(join(dir, \"session-names.json\"), \"utf8\")) as Record<string, unknown>;\n return Object.keys(names).find((k) => names[k] === hit.name) ?? \"\";\n } catch {\n return \"\";\n }\n },\n};\n\n/** True when the process chain from `start` passes a `--bg-pty-host` process: a session under the Claude background daemon. */\nexport function underBackgroundDaemon(deps: HostDeps = realHostDeps, start: number = process.pid): boolean {\n const cmds = deps.commandsOf(ancestorPids(deps, start));\n return [...cmds.values()].some((c) => /--bg-pty-host\\b/.test(c));\n}\n\n/** Pids from `start` up through its parents (self included), stopping at init or an unknown pid. */\nfunction ancestorPids(deps: HostDeps, start: number): number[] {\n const pids: number[] = [];\n for (let pid = start; pids.length < 64 && pid > 1; ) {\n pids.push(pid);\n const row = deps.psOf(pid);\n if (!row) break;\n pid = row[0];\n }\n return pids;\n}\n\n/** /dev/ttysNNN of the nearest ancestor (self included) that has a real tty, null when none. */\nexport function ancestorTty(deps: HostDeps = realHostDeps, start: number = process.pid): string | null {\n let pid = start;\n for (let i = 0; i < 64 && pid > 1; i++) {\n const row = deps.psOf(pid);\n if (!row) return null;\n const [ppid, tty] = row;\n if (tty && tty !== \"??\" && tty !== \"?\") return tty.startsWith(\"/dev/\") ? tty : `/dev/${tty}`;\n pid = ppid;\n }\n return null;\n}\n\nexport interface HostSession {\n /** iTerm unique id of the host session, \"\" when none was found. */\n uid: string;\n /** ITERM_SESSION_ID-shaped value for status/scope: the env value when it names the host, else the bare uid. \"\" when none. */\n term: string;\n /** Which step resolved it: \"foreground jobPid\", \"attach <short>\", \"tty\", \"env\", or \"none\". */\n via: string;\n reason?: string;\n}\n\nconst UUID_RE = \"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\";\n\n/** Claude session id from a Claude process command line: `--session-id <uuid>` or `--bg-pty-host …/pty/<short>.sock`. */\nfunction claudeIdOf(cmd: string): string | undefined {\n return new RegExp(`--session-id[ =](${UUID_RE})`, \"i\").exec(cmd)?.[1] ?? /--bg-pty-host\\s+\\S*\\/pty\\/([0-9a-f]{8})\\.sock/i.exec(cmd)?.[1];\n}\n\n/**\n * The one place that decides which iTerm session hosts this process; first hit\n * wins:\n * 0. map: the statusline-recorded session-claude-map.json entry for the Claude session id, when live\n * 1. foreground: a session whose jobPid is CLAUDE_PID or an ancestor pid\n * 2. background (`claude attach <short>`): the Claude session id from env or an\n * ancestor's command line, matched against each session's jobPid command;\n * several matches are ambiguous and fail loudly\n * 3. the tty of the nearest ancestor that has one\n * 4. ITERM_SESSION_ID, only when it names a live session — a reattached session\n * keeps its old id in the process env for good\n */\nexport async function resolveHostSession(\n env: NodeJS.ProcessEnv = process.env,\n deps: HostDeps = realHostDeps,\n start: number = process.pid\n): Promise<HostSession> {\n let sessions: ItermSession[] = [];\n let listFail = \"\";\n try {\n sessions = await deps.listSessions();\n } catch (e) {\n listFail = `session listing failed: ${(e as Error).message?.trim().split(\"\\n\")[0] ?? e}`;\n }\n const envTerm = env.ITERM_SESSION_ID ?? \"\";\n const envUid = itermUuid(envTerm);\n const found = (s: { id: string }, via: string): HostSession => ({ uid: s.id, term: s.id === envUid ? envTerm : s.id, via });\n const notes: string[] = listFail ? [listFail] : [];\n\n const ancestors = ancestorPids(deps, start);\n const jobPids = sessions.flatMap((s) => (s.jobPid ? [s.jobPid] : []));\n const cmds = deps.commandsOf([...new Set([...ancestors, ...jobPids])]);\n\n const cid = env.CLAUDE_CODE_SESSION_ID || ancestors.map((p) => claudeIdOf(cmds.get(p) ?? \"\")).find(Boolean);\n if (cid) {\n const mapped = deps.mapHost?.(cid) ?? \"\";\n const mapLive = mapped && sessions.find((s) => s.id === mapped);\n if (mapLive) return found(mapLive, \"map\");\n notes.push(mapped ? `map: entry for ${cid.slice(0, 8)} names ${mapped}, not a live session` : `map: no entry for ${cid.slice(0, 8)}`);\n }\n\n const fg = new Set(ancestors);\n if (Number(env.CLAUDE_PID)) fg.add(Number(env.CLAUDE_PID));\n const byJob = sessions.find((s) => s.jobPid && fg.has(s.jobPid));\n if (byJob) return found(byJob, \"foreground jobPid\");\n notes.push(`foreground: no session jobPid is CLAUDE_PID ${env.CLAUDE_PID || \"(unset)\"} or an ancestor`);\n\n if (cid) {\n const short = cid.slice(0, 8);\n const attach = new RegExp(`\\\\battach\\\\s+(${short}|${cid})\\\\b`, \"i\");\n const hits = sessions.filter((s) => s.jobPid && attach.test(cmds.get(s.jobPid) ?? \"\"));\n if (hits.length === 1) return found(hits[0], `attach ${short}`);\n const why = hits.length ? `ambiguous, ${hits.length} sessions run attach ${short}: ${hits.map((s) => s.id).join(\", \")}` : `no session runs attach ${short}`;\n if (hits.length) return { uid: \"\", term: \"\", via: \"none\", reason: [...notes, `attach: ${why}`].join(\"; \") };\n notes.push(`attach: ${why}`);\n for (const pid of deps.attachClients?.(cid) ?? []) {\n const uid = itermUuid(deps.itermIdOf?.(pid) ?? \"\");\n const live = uid && sessions.find((s) => s.id === uid);\n if (live) return found(live, `attach client ${pid}`);\n }\n notes.push(\"attach client: no attach process with a live ITERM_SESSION_ID\");\n const reg = deps.registryHost?.(cid) ?? \"\";\n const regLive = reg && sessions.find((s) => s.id === reg);\n if (regLive) return found(regLive, \"registry\");\n notes.push(\"registry: no AIBroker entry for this Claude session\");\n } else {\n notes.push(\"attach: no Claude session id in env or ancestors\");\n }\n\n const tty = ancestorTty(deps, start);\n const byTty = tty ? sessions.find((s) => s.tty === tty) : undefined;\n if (byTty) return found(byTty, \"tty\");\n notes.push(tty ? `tty: ancestor tty ${tty} matched no session` : \"tty: no tty ancestor\");\n\n if (envUid && sessions.some((s) => s.id === envUid)) return { uid: envUid, term: envTerm, via: \"env\" };\n notes.push(`env: ITERM_SESSION_ID ${envTerm ? `${envTerm} is not live` : \"is unset\"}`);\n\n const why = sessions.length === 0 ? \"iTerm2 not running or unreachable\" : \"no live iTerm2 session matched\";\n return { uid: \"\", term: \"\", via: \"none\", reason: `${why}; ${notes.join(\"; \")}` };\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(\n env: NodeJS.ProcessEnv = process.env,\n platform: string = process.platform,\n hostTerm: string = \"\"\n): PaneBackend {\n if (env.TMUX) return \"tmux\";\n if (platform === \"darwin\" && (env.ITERM_SESSION_ID || hostTerm)) return \"iterm\";\n return \"none\";\n}\n\nconst NO_HOST = \"pai worker pane: no live iTerm2 host session (foreground, attach, tty lookups found none, ITERM_SESSION_ID not live)\";\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/**\n * Worker state straight from its status file (pane.ts cannot import status.ts: it imports us).\n * Ended = any state but \"running\"/\"queued\", or a live state whose runner pid is gone (a ghost\n * that died before it could write its end state). A missing/unreadable file or a status without\n * a pid is never ended.\n */\nexport function workerEnded(logDir: string, wid: string): boolean {\n if (wid.startsWith(\"plan-\") || wid.startsWith(\"pane-\")) return planEnded(logDir, wid);\n try {\n const st = JSON.parse(readFileSync(statusPath(logDir, wid), \"utf8\")) as { state?: string; pid?: number };\n if (!st.state) return false;\n if (st.state !== \"running\" && st.state !== \"queued\") return true;\n if (typeof st.pid !== \"number\" || st.pid <= 0) return false;\n try {\n process.kill(st.pid, 0);\n return false;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code !== \"EPERM\";\n }\n } catch {\n return false;\n }\n}\n\n/** A plan pane is over once its run wrote an end state or its runner pid is gone. */\nfunction planEnded(logDir: string, wid: string): boolean {\n try {\n const st = JSON.parse(readFileSync(join(logDir, wid.startsWith(\"pane-\") ? `${wid}.panestatus` : `${wid}.planstatus`), \"utf8\")) as { state?: string; pid?: number };\n if (!st.state) return false;\n if (st.state !== \"running\") return true;\n if (typeof st.pid !== \"number\" || st.pid <= 0) return false;\n try {\n process.kill(st.pid, 0);\n return false;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code !== \"EPERM\";\n }\n } catch {\n return false;\n }\n}\n\n/** The replacement rule: a new pane in a scope closes that scope's panes of ended workers. */\nexport async function closeEndedInScope(logDir: string, scope: string, ops: PaneOps = realPaneOps): Promise<void> {\n for (const e of loadRegistry(logDir, scope)) {\n if (!workerEnded(logDir, e.worker)) continue;\n await ops.closeIterm(e.session).catch(() => false);\n dropEntry(logDir, e);\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(process.env, process.platform, term);\n if (backend === \"none\") return { message: noPaneMessage(wid), session: null };\n if (backend === \"tmux\") {\n tmuxSplit(followCommand(wid));\n return { message: `pane opened for ${wid}`, session: null };\n }\n const uid = itermUuid(term);\n if (!uid) throw new Error(NO_HOST);\n const scope = scopeKey(term);\n await closeEndedInScope(logDir, scope);\n const reg = loadRegistry(logDir, scope);\n const profile = await followProfile(config.pane.fontSize);\n const cmd = followCommand(wid);\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 sizes = (await osascript(TAB_SIZES_SCRIPT, [uid])).stdout.trim();\n const panes: TabPane[] = sizes.includes(\"\\t\")\n ? sizes.split(\"\\n\").map((l) => {\n const [id, c, r] = l.split(\"\\t\");\n return { id, cols: Number(c), rows: Number(r) };\n })\n : [];\n const choice = panes.length ? pickSplit(panes, uid) : null;\n if (choice?.small) {\n process.stderr.write(`[pai worker] pane: no pane of the tab splits to ${MIN_COLS}x${MIN_ROWS}; opening a small ${choice.cols}x${choice.rows} pane\\n`);\n }\n const p = await osascript(WORKER_SPLIT_SCRIPT, [uid, cands, cmd, profile, choice?.id ?? \"\", choice?.dir ?? \"v\"]);\n const out = p.stdout.trim();\n if (out === \"notfound\" || out === \"notrunning\") {\n throw new Error(\n out === \"notfound\"\n ? \"pai worker pane: resolved iTerm2 session vanished\"\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 host: itermUuid(term) || undefined,\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(\n wid: string,\n fontSize: number,\n term: string,\n env: NodeJS.ProcessEnv = process.env,\n platform: string = process.platform\n): Promise<string> {\n const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\n if (paneBackend(env, platform, term) !== \"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(process.env, process.platform, term);\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 if (!uid) throw new Error(NO_HOST);\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: resolved iTerm2 session vanished\");\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: resolved iTerm2 session vanished\");\n throw new Error(`pai worker pane: osascript failed: ${(s.stderr || sout).slice(0, 200)}`);\n}\n\n// ---------------------------------------------------------------------------\n// Fail-closed visibility: a headless worker runs only behind a verified pane\n// ---------------------------------------------------------------------------\n\n/**\n * The operator-only switch that lets a run go headless: this file's existence.\n * The PreToolUse Bash hook denies any command naming it, so a Claude session\n * cannot create it through Bash; only the operator's own shell can.\n */\nexport function headlessOverridePath(): string {\n return process.env.PAI_HEADLESS_OK_FILE ?? paiHomePath(\"headless-ok\");\n}\n\n/**\n * Why this process may run a worker without a verified pane, null when it may\n * not. Three reasons only: it is itself a worker (PAI_WORKER=1: a child\n * spawned inside its parent's pane may stay headless), the operator set\n * PAI_WORKER_ALLOW_HIDDEN=1 in their own shell (buildRunEnv strips it from\n * every spawned worker, so the consent ends at the process it was set for),\n * or the operator created the override file.\n */\nexport const ALLOW_HIDDEN_ENV = \"PAI_WORKER_ALLOW_HIDDEN\";\n\nexport function headlessAllowed(env: NodeJS.ProcessEnv = process.env, file: string = headlessOverridePath()): string | null {\n if (env.PAI_WORKER === \"1\") return \"PAI_WORKER=1 (child of a worker, runs inside its parent's pane)\";\n if (env[ALLOW_HIDDEN_ENV] === \"1\") return `${ALLOW_HIDDEN_ENV}=1 (operator override)`;\n if (existsSync(file)) return `operator override file ${file}`;\n return null;\n}\n\n// Reports where the pane sits, without raising or focusing anything: a window\n// need not be frontmost, only visible (several sessions live in several windows). JSON on stdout; `fail` set when the pane is not a visible, usable\n// neighbour of the host session.\nexport const VERIFY_PANE_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return JSON.stringify({ fail: \"iTerm2 is not running\" });\n var h = findSession(app, argv[0]);\n if (!h) return JSON.stringify({ fail: \"host session \" + argv[0] + \" is gone\" });\n var p = findSession(app, argv[1]);\n if (!p) return JSON.stringify({ fail: \"pane session \" + argv[1] + \" is gone (closed right after opening)\" });\n function ids(ss) { return ss.map(function (o) { return o.id(); }); }\n var where = argv[2] || \"split\";\n if (where !== \"window\" && h.w.id() !== p.w.id()) return JSON.stringify({ fail: \"pane is in window \" + p.w.id() + \", host in window \" + h.w.id() });\n if (where === \"split\" && ids(h.all).indexOf(argv[1]) < 0) return JSON.stringify({ fail: \"pane is not in the host's tab\" });\n var tabs = p.w.tabs();\n var tabIdx = 0;\n for (var i = 0; i < tabs.length; i++) if (ids(tabs[i].sessions()).indexOf(argv[1]) >= 0) tabIdx = i + 1;\n var selected = ids(p.w.currentTab().sessions()).indexOf(argv[1]) >= 0;\n var b = p.w.bounds();\n // On screen: the frame intersects a display (NSScreen is bottom-left origin, iTerm top-left).\n ObjC.import(\"AppKit\");\n var scr = $.NSScreen.screens, top = scr.objectAtIndex(0).frame.size.height, onScreen = false;\n for (var k = 0; k < scr.count; k++) {\n var f = scr.objectAtIndex(k).frame, fy = top - f.origin.y - f.size.height;\n if (b.x < f.origin.x + f.size.width && b.x + b.width > f.origin.x && b.y < fy + f.size.height && b.y + b.height > fy) onScreen = true;\n }\n var hidden = false;\n try { hidden = Application(\"System Events\").processes.byName(\"iTerm2\").visible() === false; } catch (e) {}\n return JSON.stringify({\n window: p.w.id(), bounds: [b.x, b.y, b.width, b.height], tab: tabIdx + \"/\" + tabs.length,\n selectedTab: selected, onScreen: onScreen, hidden: hidden, miniaturized: p.w.miniaturized(),\n rows: p.s.rows(), cols: p.s.columns(), tty: p.s.tty(), hostRows: h.s.rows(), hostCols: h.s.columns()\n });\n}`;\n\n// Host session's tab selection and size, read before anything is opened.\n// JSON on stdout.\nconst HOST_TAB_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n var app = Application(\"iTerm2\");\n if (!app.running()) return JSON.stringify({ fail: \"iTerm2 is not running\" });\n var h = findSession(app, argv[0]);\n if (!h) return JSON.stringify({ fail: \"host session \" + argv[0] + \" is gone\" });\n var selected = h.w.currentTab().sessions().map(function (o) { return o.id(); }).indexOf(argv[0]) >= 0;\n return JSON.stringify({ selectedTab: selected, rows: h.s.rows(), cols: h.s.columns() });\n}`;\n\n/** True unless the host session's tab is known not to be its window's selected tab. */\nasync function hostTabSelected(hostUid: string): Promise<boolean> {\n try {\n const p = await osascript(HOST_TAB_SCRIPT, [hostUid]);\n return (JSON.parse(p.stdout.trim()) as { selectedTab?: boolean }).selectedTab !== false;\n } catch {\n return true; // unreadable: split, and let verifyPane judge the result\n }\n}\n\n/** Where a follow pane sits: always a split of the host's tab. */\nexport type PanePlacement = \"split\";\n\nconst placements = new Map<string, PanePlacement>();\n\n/** The placement openVerifiedPane used for a worker in this process, for the status record. */\nexport function panePlacement(wid: string): PanePlacement | undefined {\n return placements.get(wid);\n}\n\nconst MIN_PANE_ROWS = 6;\nconst MIN_PANE_COLS = 40;\n\n/** True while a `worker follow <wid>` process runs on that tty. */\nfunction followRunsOn(tty: string, wid: string): boolean {\n const short = tty.replace(/^\\/dev\\//, \"\");\n return psOutput(\"tty=,command=\")\n .split(\"\\n\")\n .some((ln) => ln.trim().startsWith(`${short} `) && new RegExp(`worker follow ${wid}\\\\b`).test(ln));\n}\n\n/**\n * Check, and bring forward, the iTerm pane of a worker. Resolves with a line\n * naming window, tab and size; rejects with the reason when the pane is not\n * something the operator can see: gone, other window or tab, tab not selected,\n * window off screen, hidden or minimised, too small, or its follow command not running\n * (polled for up to ~7 s while the new session starts it).\n */\nexport async function verifyPane(\n hostUid: string,\n paneUid: string,\n wid: string,\n run: (script: string, args: string[]) => Promise<{ stdout: string; stderr: string }> = osascript,\n running: (tty: string, wid: string) => boolean = followRunsOn,\n tries = 25,\n pollMs = 300,\n placement: PanePlacement = \"split\"\n): Promise<string> {\n let last = \"\";\n for (let i = 0; i < tries; i++) {\n const p = await run(VERIFY_PANE_SCRIPT, [hostUid, paneUid, placement]);\n let r: Record<string, unknown>;\n try {\n r = JSON.parse(p.stdout.trim()) as Record<string, unknown>;\n } catch {\n throw new Error(`pane verification failed: osascript said ${(p.stderr || p.stdout || \"nothing\").trim().slice(0, 200)}`);\n }\n if (typeof r.fail === \"string\") throw new Error(`pane verification failed: ${r.fail}`);\n const rows = Number(r.rows);\n const cols = Number(r.cols);\n const where = `window ${r.window} [${(r.bounds as number[]).join(\",\")}] tab ${r.tab}, pane ${cols}x${rows}, ${r.tty}`;\n if (r.miniaturized === true) throw new Error(`pane verification failed: window is minimised (${where})`);\n if (r.selectedTab !== true) throw new Error(`pane verification failed: its tab is not the selected one (${where})`);\n if (r.hidden === true) throw new Error(`pane verification failed: iTerm2 is hidden (${where})`);\n if (r.onScreen !== true) throw new Error(`pane verification failed: its window is off screen (${where})`);\n // a small pane never stops a start: it is marked SMALL, logged, and audited as SMALL\n const small = rows < MIN_PANE_ROWS || cols < MIN_PANE_COLS;\n if (running(String(r.tty), wid)) return small ? `${where} SMALL (< ${MIN_PANE_COLS}x${MIN_PANE_ROWS})` : where;\n last = where;\n await new Promise((res) => setTimeout(res, pollMs));\n }\n throw new Error(`pane verification failed: \\`worker follow ${wid}\\` is not running in the pane (${last})`);\n}\n\n/** Test seams of openVerifiedPane: the real iTerm-touching steps. */\nexport interface PaneSeam {\n split?: typeof openPaneForWorker;\n verify?: typeof verifyPane;\n hostSelected?: (hostUid: string) => Promise<boolean>;\n daemon?: () => boolean;\n}\n\n/**\n * Open the worker's pane and prove the operator can see it, or throw the\n * reason (the pane just opened is closed again). Needs a live iTerm2 host\n * session that ITERM_SESSION_ID itself names, or tmux; a stale or invented\n * id is refused, whatever the process-tree lookup would find.\n */\nexport async function openVerifiedPane(\n logDir: string,\n config: WorkersConfig,\n wid: string,\n term: string,\n env: NodeJS.ProcessEnv = process.env,\n ops: PaneOps = realPaneOps,\n seam: PaneSeam = {}\n): Promise<string> {\n const split = seam.split ?? openPaneForWorker;\n const verify = seam.verify ?? verifyPane;\n if (env.TMUX) {\n const r = await openPaneForWorker(logDir, config, wid, term);\n if (!r.message.startsWith(\"pane opened\")) throw new Error(`pane verification failed: ${r.message}`);\n return \"tmux pane\";\n }\n const envUid = itermUuid(env.ITERM_SESSION_ID ?? \"\");\n const hostUid = itermUuid(term);\n if (!envUid && !hostUid) {\n const daemon = (seam.daemon ?? (() => underBackgroundDaemon()))();\n throw new Error(\n `neither TMUX nor ITERM_SESSION_ID is set: this process does not run in a terminal pane the operator sees${daemon ? \" (runs under the Claude background daemon; no attached terminal found)\" : \"\"}`\n );\n }\n if (envUid && envUid !== hostUid) {\n throw new Error(`ITERM_SESSION_ID names ${envUid}, which is not a live iTerm2 session${hostUid ? ` (host found: ${hostUid})` : \"\"}`);\n }\n // Every pane is a split of the host's own tab: never a window, never another tab. A host tab that is not\n // the selected one still gets its split (the operator finds it there); only the visibility proof is skipped.\n const hostShown = await (seam.hostSelected ?? hostTabSelected)(hostUid);\n let r = await split(logDir, config, wid, term);\n if (!r.session) throw new Error(`pane verification failed: ${r.message}`);\n if (!hostShown) {\n placements.set(wid, \"split\");\n process.stderr.write(\"[pai worker] pane: split in the host tab (not the selected one)\\n\");\n return `host tab not selected (iTerm session ${r.session})`;\n }\n try {\n const where = await verify(hostUid, r.session, wid);\n placements.set(wid, \"split\");\n if (/ SMALL /.test(where)) {\n appendLedger(ledgerPath(logDir), \"PANE-SMALL\", { id: wid, session: r.session, where });\n process.stderr.write(`[pai worker] pane: small pane for ${wid}: ${where}\\n`);\n }\n return `${where} (iTerm session ${r.session})`;\n } catch (e) {\n await closePaneForWorker(logDir, wid, ops).catch(() => false);\n throw e;\n }\n}\n\n/** CLI entry check: --no-pane and PAI_WORKER_AUTOPANE=0 are refused unless headlessAllowed(). */\nexport function assertPaneNotDisabled(noPaneFlag: boolean, env: NodeJS.ProcessEnv = process.env): void {\n const what = noPaneFlag ? \"--no-pane\" : env.PAI_WORKER_AUTOPANE === \"0\" ? \"PAI_WORKER_AUTOPANE=0\" : \"\";\n if (what && !headlessAllowed(env)) {\n throw new Error(\n `${what} refused: every worker runs behind a visible pane ` +\n `(only a worker's own children, the operator's ${ALLOW_HIDDEN_ENV}=1, ` +\n `or the operator's ${headlessOverridePath()} may skip it)`\n );\n }\n}\n","/**\n * plan-deps.ts — the real RunnerDeps of `pai plan run`: workers, merges and\n * checks are the existing `pai worker run` / `pai worker merge` verbs, run as\n * child processes of this same binary (PAI_BIN, else `pai`).\n */\n\nimport { spawn } from \"node:child_process\";\nimport { mkdtempSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parseWorkerReport } from \"./report.js\";\nimport { eventsPath } from \"./paths.js\";\nimport { loadStatuses } from \"./status.js\";\nimport { killTree } from \"./kill-tree.js\";\nimport { sumRunUsage } from \"./usage.js\";\nimport { headlessAllowed, openVerifiedPane, realPaneOps, resolveHostSession } from \"./pane.js\";\nimport { readWorkersSection } from \"./config.js\";\nimport { GATE_MARK } from \"./worktree.js\";\nimport { PlanTracker, gatePhaseName } from \"./plan-status.js\";\nimport { appendRunLog, tail, type GateEvent, type MergeOutcome, type RunnerDeps, type StartRequest, type WorkerOutcome } from \"./plan-runner.js\";\nimport type { Plan } from \"./plan.js\";\nimport { resolveRunnerProvider } from \"./local-provider.js\";\n\nexport interface Captured {\n code: number | null;\n out: string;\n err: string;\n}\n\nexport function capture(cmd: string, args: string[], cwd: string, shell = false, onData?: (chunk: string) => void, env: NodeJS.ProcessEnv = process.env): Promise<Captured> {\n return new Promise((resolve) => {\n const child = spawn(cmd, args, { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"], env, shell });\n let out = \"\";\n let err = \"\";\n child.stdout.on(\"data\", (d) => {\n out += d;\n onData?.(String(d));\n });\n child.stderr.on(\"data\", (d) => {\n err += d;\n onData?.(String(d));\n });\n child.on(\"error\", (e) => resolve({ code: 1, out, err: err + e.message }));\n child.on(\"close\", (code) => resolve({ code, out, err }));\n });\n}\n\nexport const pai = (): string => process.env.PAI_BIN || \"pai\";\n\n/** Tokens a finished worker used, from its run log; null when unavailable. */\nfunction workerTokens(logDir: string, id: string): number | null {\n try {\n const sum = sumRunUsage(readFileSync(eventsPath(logDir, id), \"utf8\"));\n let n = 0;\n for (const t of sum.byModel.values()) n += t.input + t.cacheRead + t.cacheWrite5m + t.cacheWrite1h + t.output;\n return n || null;\n } catch {\n return null;\n }\n}\n\n/** Paths with uncommitted changes in the plan's repo (`git status --porcelain`). */\nexport async function dirtyPaths(repo: string): Promise<string[]> {\n const r = await capture(\"git\", [\"status\", \"--porcelain\"], repo);\n return r.out\n .split(\"\\n\")\n .filter(Boolean)\n .map((l) => l.slice(3).replace(/^.* -> /, \"\").replace(/^\"|\"$/g, \"\"));\n}\n\n/** Args of the part worker: `--worktree` is explicit so the caller's own mode is never inherited. */\nexport function workerArgs(req: Pick<StartRequest, \"label\" | \"provider\" | \"tier\" | \"model\" | \"priority\">, specFile: string, repo: string): string[] {\n const model = req.tier === \"sonnet\" ? \"sonnet\" : req.model;\n return [\"worker\", \"run\", \"--provider\", req.provider, \"--class\", \"implement\", ...(model ? [\"--model\", model] : []), ...(req.priority ? [\"--now\"] : []), \"--label\", req.label, \"--spec\", specFile, \"--cwd\", repo, \"--worktree\", \"--output-format\", \"json\"];\n}\n\n/** State of the plan checkout: HEAD and the dirty-path listing. */\nexport interface CheckoutState {\n head: string;\n dirty: string;\n}\n\nexport async function checkoutState(repo: string): Promise<CheckoutState> {\n const head = (await capture(\"git\", [\"rev-parse\", \"HEAD\"], repo)).out.trim();\n const dirty = (await capture(\"git\", [\"status\", \"--porcelain\"], repo)).out;\n return { head, dirty };\n}\n\n/** Tests the last successful gate counted, per repo: the denominator of the next run's progress. */\nexport const lastTests = new Map<string, number>();\n\n/** HEADs the runner's own merges produced, per repo: a moved HEAD that is one of them is not a worker edit. */\nconst mergedHeads = new Map<string, Set<string>>();\n\n/** Error text when the plan checkout changed during a part other than by the runner's merges; null when untouched. */\nexport function checkoutProblem(id: string, before: CheckoutState, after: CheckoutState, merged: ReadonlySet<string> = new Set()): string | null {\n const headMoved = after.head !== before.head && !merged.has(after.head);\n if (!headMoved && after.dirty === before.dirty) return null;\n return `worker ${id} changed the plan's checkout (${headMoved ? `HEAD ${before.head.slice(0, 7)} -> ${after.head.slice(0, 7)}` : \"working tree\"})`;\n}\n\n/** Commits on worker/<id> beyond `base` that change something (empty commits do not count). */\nexport async function realCommitsAhead(repo: string, base: string, id: string): Promise<number> {\n const revs = (await capture(\"git\", [\"rev-list\", \"--no-merges\", `${base}..worker/${id}`], repo)).out.split(\"\\n\").filter(Boolean);\n let n = 0;\n for (const sha of revs) if ((await capture(\"git\", [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", sha], repo)).out.trim()) n++;\n return n;\n}\n\n/** Workers this process launched and is waiting on. */\nconst launched = new Set<{ label: string; t0: number }>();\n\n/** Stop every worker this process launched and still waits on, the way `pai worker kill` does (kill-tree). */\nexport async function stopLaunchedWorkers(logDir: string, kill: typeof killTree = killTree): Promise<string[]> {\n const stopped: string[] = [];\n for (const l of launched) {\n for (const s of loadStatuses(logDir)) {\n if (s.label !== l.label || (s.state !== \"running\" && s.state !== \"queued\") || Date.parse(s.started) < l.t0) continue;\n try {\n await kill(logDir, s.id);\n stopped.push(s.id);\n } catch {\n /* already gone */\n }\n }\n }\n return stopped;\n}\n\n/** Run one worker as `pai worker run` and read its outcome: spawn refusal, plan-checkout and report checks included. */\nexport async function launchWorker(req: Pick<StartRequest, \"label\" | \"provider\" | \"tier\" | \"model\" | \"priority\">, specFile: string, repo: string, logDir: string): Promise<WorkerOutcome> {\n const t0 = Date.now() - 1000;\n const before = await checkoutState(repo);\n const mark = { label: req.label, t0 };\n launched.add(mark);\n const r = await capture(pai(), workerArgs(req, specFile, repo), repo, false, undefined, process.env).finally(() => launched.delete(mark));\n const st = loadStatuses(logDir)\n .filter((s) => s.label === req.label && Date.parse(s.started) >= t0)\n .sort((a, b) => b.started.localeCompare(a.started))[0];\n if (!st) return { id: \"\", ok: false, spawnRefused: true, failure: tail(r.err || r.out) || `worker did not start (exit ${r.code})` };\n const problem = checkoutProblem(st.id, before, await checkoutState(repo), mergedHeads.get(repo));\n if (problem) return { id: st.id, ok: false, fatal: true, failure: problem };\n let text = \"\";\n try {\n text = (JSON.parse(r.out) as { result?: string }).result ?? \"\";\n } catch {\n text = r.out;\n }\n const report = parseWorkerReport(text);\n const passed = !!report && (report.result ? report.result === \"+\" : (report.checks ?? []).every((c) => c.ok !== false));\n const ok = st.state === \"done\" && (st.rc ?? 0) === 0 && passed;\n const failure = ok ? undefined : !report ? `worker ended without a valid report (${st.state}): ${tail(text || r.err, 15)}` : [report.why, ...(report.open ?? []), report.notes].filter(Boolean).join(\"\\n\") || `report result ${report.result ?? \"?\"}`;\n return { id: st.id, ok, failure, ...(st.loopStopped ? { loopStopped: true } : {}), tokens: workerTokens(logDir, st.id), worktreeDir: st.worktreeDir ?? undefined, report: text };\n}\n\n/**\n * Reads the merge child's output as it arrives: gate marker lines become GateEvents (and leave the\n * output), the rest is kept as a per-command tail; vitest's \"(N tests)\" lines turn into a\n * \"gate: tests 214/2600\" progress when the denominator is known.\n */\nexport class GateParser {\n private buf = \"\";\n private lines: string[] = [];\n private tests = 0;\n private name = \"\";\n /** Output of the merge without the marker lines. */\n clean = \"\";\n /** Tests counted over the whole gate. */\n total = 0;\n\n constructor(private on: (e: GateEvent) => void, private testTotal?: number, private phaseName: (cmd: string) => string = gatePhaseName) {}\n\n feed(chunk: string): void {\n this.buf += chunk;\n let at: number;\n while ((at = this.buf.indexOf(\"\\n\")) >= 0) {\n this.line(this.buf.slice(0, at));\n this.buf = this.buf.slice(at + 1);\n }\n }\n\n end(): void {\n if (this.buf) this.line(this.buf);\n this.buf = \"\";\n }\n\n private line(l: string): void {\n const m = l.indexOf(GATE_MARK);\n if (m >= 0) {\n try {\n let e = JSON.parse(l.slice(m + GATE_MARK.length)) as GateEvent;\n if (e.ev === \"cmd-start\") {\n this.lines = [];\n this.tests = 0;\n this.name = this.phaseName(e.cmd);\n }\n if (e.ev === \"cmd-end\") {\n this.total += this.tests;\n e = { ...e, tail: tail(this.lines.join(\"\\n\"), e.ok ? 6 : 25) };\n }\n this.on(e);\n return;\n } catch {\n /* not ours: keep as output */\n }\n }\n this.clean += l + \"\\n\";\n this.lines.push(l);\n if (this.lines.length > 200) this.lines.shift();\n const t = /\\((\\d+) tests?\\b/.exec(l);\n if (t && this.name === \"tests\") {\n this.tests += Number(t[1]);\n this.on({ ev: \"progress\", text: `gate: tests ${this.tests}${this.testTotal ? `/${this.testTotal}` : \"\"}` });\n }\n }\n}\n\n/** Merge worker/<id> into `repo`'s checkout with `pai worker merge` (--verify when a gate is given); an empty branch is refused. */\nexport async function mergeBranch(repo: string, id: string, gate: string, onGate?: (e: GateEvent) => void, testTotal?: number): Promise<MergeOutcome> {\n const rev = async (...a: string[]) => (await capture(\"git\", a, repo)).out.trim();\n const before = await rev(\"rev-parse\", \"HEAD\");\n const ahead = await realCommitsAhead(repo, before, id);\n if (!(ahead > 0)) return { ok: false, noCommit: true, before, output: `branch worker/${id} has no non-empty commit beyond ${before.slice(0, 7)}` };\n const parser = onGate ? new GateParser(onGate, testTotal) : null;\n const r = await capture(pai(), [\"worker\", \"merge\", id, ...(gate ? [\"--verify\", gate] : [])], repo, false, parser ? (c) => parser.feed(c) : undefined, parser ? { ...process.env, PAI_GATE_EVENTS: \"1\" } : process.env);\n parser?.end();\n if (parser?.total) lastTests.set(repo, parser.total);\n const output = parser ? parser.clean : r.out + r.err;\n if (r.code !== 0) return { ok: false, before, output };\n const head = await rev(\"rev-parse\", \"HEAD\");\n if (head) mergedHeads.set(repo, (mergedHeads.get(repo) ?? new Set()).add(head));\n return { ok: true, commit: head || undefined, before, output };\n}\n\n/**\n * The runner's own pane for its whole life (a split of the host tab, never a window): `pai worker follow plan-<job>`\n * streams the runner's live log and exits when the run ends. Each worker opens its own pane. A pane that cannot open is\n * logged and the run goes on; its workers' own fail-closed gate still applies.\n */\nexport async function openPlanPane(logDir: string, tracker: PlanTracker, open: typeof openVerifiedPane = openVerifiedPane, host: () => Promise<{ term: string }> = resolveHostSession): Promise<boolean> {\n if (headlessAllowed()) return false;\n try {\n const term = (await host()).term;\n tracker.log(`plan pane: ${await open(logDir, readWorkersSection().workers, tracker.id, term, process.env, realPaneOps)}`);\n tracker.hasPane = true;\n return true;\n } catch (e) {\n tracker.log(`plan pane not opened: ${e instanceof Error ? e.message : String(e)}`);\n return false;\n }\n}\n\nexport function realRunnerDeps(plan: Plan, logDir: string, logPath: string, tracker?: PlanTracker): RunnerDeps {\n const dir = mkdtempSync(join(tmpdir(), \"pai-plan-\"));\n return {\n now: Date.now,\n log: (e) => {\n appendRunLog(logPath, e);\n if (e.event === \"model-idle\" && e.tier === \"local\" && typeof e.ms === \"number\") tracker?.addIdle(e.ms);\n if (e.event === \"part-start\" && e.overlap) tracker?.overlapped();\n },\n ...(tracker\n ? {\n phase: (t: string, refine?: boolean) => (refine ? tracker.progress(t) : tracker.phase(t)),\n progress: (merged: number, running: string[]) => {\n tracker.merged(merged);\n tracker.setRunning(running);\n },\n note: (l: string) => tracker.log(l),\n }\n : {}),\n async startWorker(req: StartRequest): Promise<WorkerOutcome> {\n const specFile = join(dir, `${req.part.id}-${req.attempt}.txt`);\n writeFileSync(specFile, req.spec, \"utf8\");\n const t0 = Date.now() - 1000;\n const watch = tracker\n ? setInterval(() => {\n const st = loadStatuses(logDir).find((s) => s.label === req.label && Date.parse(s.started) >= t0);\n if (!st) return;\n tracker.setWorker(req.part.id, st.id);\n tracker.phase(`running ${req.part.id} (${st.id})`);\n clearInterval(watch);\n }, 1500)\n : undefined;\n try {\n return await launchWorker(req, specFile, plan.repo, logDir);\n } finally {\n if (watch) clearInterval(watch);\n }\n },\n merge: async (id: string, gate: string, onGate?: (e: GateEvent) => void) => {\n const r = await mergeBranch(plan.repo, id, gate, onGate, tracker?.testTotal);\n if (r.ok) tracker?.setTestTotal(lastTests.get(plan.repo) ?? 0);\n return r;\n },\n resolveLocal: resolveRunnerProvider,\n stopActive: async () => {\n tracker?.end(\"stopped\");\n void (await stopLaunchedWorkers(logDir));\n },\n async unitCheck(cmd: string) {\n const r = await capture(cmd, [], plan.repo, true);\n return { ok: r.code === 0, output: r.out + r.err };\n },\n };\n}\n","/**\n * run-report-wiring.ts — the CLI side of run reporting: who reports to whom,\n * and the argv a detached run is restarted with.\n */\n\nimport { aibrokerMessenger, detachRun, markRunDone, originFromEnv, type DetachDeps, type Messenger, type RunOrigin, type RunRecord } from \"./run-events.js\";\nimport { pai } from \"./plan-deps.js\";\n\n/** The origin worth reporting to: none when the caller is a bare shell with no session. */\nexport function reportOrigin(env: NodeJS.ProcessEnv = process.env): RunOrigin | null {\n const o = originFromEnv(env);\n return o.claudeSession || o.iterm || o.name ? o : null;\n}\n\nexport function runMessenger(logDir: string): Messenger {\n return aibrokerMessenger(logDir);\n}\n\n/** A clean end of a detached run, so the daemon sweep does not call it dead. */\nexport function finishDetached(logDir: string, env: NodeJS.ProcessEnv = process.env): void {\n if (env.PAI_RUN_ID) markRunDone(logDir, env.PAI_RUN_ID);\n}\n\n/** argv (after the binary) that re-runs a plan/queue run in the detached child. */\nexport function detachedArgv(group: \"plan\" | \"queue\", positional: string[], opts: Record<string, unknown>, skip: string[] = [\"detach\", \"dry-run\"]): string[] {\n const argv: string[] = [group, \"run\", ...positional];\n for (const [k, v] of Object.entries(opts)) {\n if (skip.includes(k) || v === undefined || v === false) continue;\n argv.push(`--${k}`);\n if (v !== true) argv.push(String(v));\n }\n return argv;\n}\n\nexport interface DetachRequest {\n kind: \"plan\" | \"queue\";\n label: string;\n /** Positional args of the `run` command (the plan path for a plan). */\n positional: string[];\n opts: Record<string, unknown>;\n cwd: string;\n origin: RunOrigin | null;\n}\n\n/** The one place a plan/queue run is handed to a detached process; the CLI --detach and the MCP tools both call it. */\nexport function startDetached(logDir: string, req: DetachRequest, deps?: DetachDeps): RunRecord {\n return detachRun(logDir, req.kind, req.label, pai(), detachedArgv(req.kind, req.positional, req.opts), req.cwd, req.origin ?? {}, deps);\n}\n","/**\n * slim-profile.ts — what a headless worker loads of the operator's setup.\n *\n * A default `claude -p` run reads the user settings: ~/.claude/CLAUDE.md, the\n * enabled plugins' SessionStart context, every SessionStart/UserPromptSubmit\n * injection (Whispr, caveman, handover...), skills, output style. Measured\n * 2026-10-16 on a \"reply OK\" worker, roughly a third of the first request.\n * A worker needs none of it, only the PreToolUse guards and its contract.\n *\n * The slim profile drops the user setting source (`--setting-sources\n * project,local`) and passes a generated `--settings` file instead: the user's\n * env/permissions plus only the guard hooks. The project CLAUDE.md stays.\n * Opt out with PAI_WORKER_FULL_PROFILE=1 or by passing your own\n * --settings/--setting-sources.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { WorkerProvider } from \"./config.js\";\n\n/** Hook commands kept for a worker; everything else is context or bookkeeping. */\nconst GUARD_HOOKS = [\n \"terminal_guard.py\",\n \"route-agents-to-worker\",\n \"block-taskoutput-wait\",\n \"route-edits-to-worker\",\n \"security-validator\",\n \"ztk-auto.sh\",\n \"caseleaf-warm-builds\",\n \"block-sleep-poll\",\n \"local-model-hint\",\n \"worker-guard\",\n \"worker-path-repair\",\n \"worker-edit-repair\",\n \"worker-tool-name-repair\",\n \"ab-levers\",\n];\n\n/** The text a worker needs in place of the user CLAUDE.md. */\nexport const WORKER_BRIEF = [\n \"You are a headless worker: do the task in this process, then report.\",\n \"- Commit or push only if the spec says so; no AI attribution, no --no-verify.\",\n \"- Keep personal data (names, accounts, home paths) out of code, docs and commits.\",\n \"- Stay in the working directory; do not touch other checkouts or the user's shell.\",\n \"- Never sleep-poll: no sleep over 60 s; long jobs run detached and are checked with a bounded wait.\",\n \"- No screen/osascript automation; no email or messaging.\",\n \"- Nothing is done without proof: run the tests, show before/after.\",\n \"- A hook denial is a rule, not a glitch: adjust, do not retry verbatim.\",\n \"- End with the report format given in the system prompt.\",\n].join(\"\\n\");\n\n/**\n * The built PostToolUse hook that delivers `pai worker goal` notes: next to\n * this CLI's own dist/hooks (so a build under test delivers with its own\n * hook), else the installed one. null when neither exists.\n */\nexport function noteHookPath(from: string = fileURLToPath(import.meta.url), home: string = homedir()): string | null {\n for (let dir = dirname(from), i = 0; i < 6; i++, dir = dirname(dir)) {\n const p = join(dir, \"hooks\", \"worker-note.mjs\");\n if (existsSync(p)) return p;\n }\n const installed = join(home, \".claude\", \"Hooks\", \"worker-note.mjs\");\n return existsSync(installed) ? installed : null;\n}\n\nconst PROVIDER_KEYS = [\"ANTHROPIC_BASE_URL\", \"ANTHROPIC_AUTH_TOKEN\", \"ANTHROPIC_API_KEY\"];\n\ntype HookGroup = { matcher?: string; hooks?: Array<{ type?: string; command?: string }> };\n\n/**\n * Settings env outranks the process env, so the user's global tuning for the\n * first-party API must not shadow a non-anthropic provider: its own output\n * cap wins (none set: the user's is dropped), the first-party-base-url flag\n * goes, and thinking is switched off (no `thinking`, no effort in the request)\n * unless the provider asks for it.\n */\nfunction applyProviderEnv(e: Record<string, unknown>, provider: WorkerProvider): void {\n const cap = provider.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS;\n if (cap !== undefined) e.CLAUDE_CODE_MAX_OUTPUT_TOKENS = cap;\n else delete e.CLAUDE_CODE_MAX_OUTPUT_TOKENS;\n delete e._CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL;\n if (!provider.thinking) {\n e.CLAUDE_CODE_DISABLE_THINKING = \"1\";\n e.CLAUDE_CODE_EFFORT_LEVEL = \"unset\";\n }\n}\n\n/** The user settings reduced to env, permissions and the guard hooks. */\nexport function slimSettings(\n user: Record<string, unknown>,\n procEnv: NodeJS.ProcessEnv = {},\n provider?: WorkerProvider,\n noteHook?: string,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const k of [\"env\", \"permissions\", \"model\"]) if (user[k] !== undefined) out[k] = user[k];\n // Settings env overrides the process env, so a provider's endpoint and token\n // (set by the spawner) must not be shadowed by the user's own.\n const nonNative = provider !== undefined && !provider.native;\n if ((out.env && typeof out.env === \"object\") || nonNative) {\n const e = { ...((out.env ?? {}) as Record<string, unknown>) };\n for (const k of PROVIDER_KEYS) if (procEnv[k]) delete e[k];\n if (provider && nonNative) applyProviderEnv(e, provider);\n out.env = e;\n }\n const hooks: Record<string, HookGroup[]> = {};\n for (const [event, groups] of Object.entries((user.hooks ?? {}) as Record<string, HookGroup[]>)) {\n const kept = groups\n .map((g) => ({ ...g, hooks: (g.hooks ?? []).filter((h) => GUARD_HOOKS.some((n) => h.command?.includes(n))) }))\n .filter((g) => g.hooks.length > 0);\n if (kept.length) hooks[event] = kept;\n }\n // Not a user hook: the worker's own channel for `pai worker goal` notes, for every provider.\n if (noteHook) (hooks.PostToolUse ??= []).push({ matcher: \"*\", hooks: [{ type: \"command\", command: noteHook }] });\n out.hooks = hooks;\n // Empty strings switch off Claude Code's Co-Authored-By / \"Generated with\" lines.\n out.attribution = { commit: \"\", pr: \"\" };\n return out;\n}\n\nfunction noteHookCommand(logDir: string): string | undefined {\n const hook = noteHookPath();\n return hook ? `node \"${hook}\" \"${logDir}\"` : undefined;\n}\n\n/**\n * Args for the slim profile, or [] when it does not apply (caller brought\n * their own settings, opted out, or there is no user settings file to slim).\n */\nexport function slimProfileArgs(\n logDir: string,\n wid: string,\n rest: string[],\n env: NodeJS.ProcessEnv = process.env,\n userSettingsPath = join(homedir(), \".claude\", \"settings.json\"),\n provider?: WorkerProvider,\n): string[] {\n if (env.PAI_WORKER_FULL_PROFILE === \"1\") return [];\n if (rest.some((a) => a === \"--settings\" || a.startsWith(\"--settings=\") || a.startsWith(\"--setting-sources\"))) return [];\n if (!existsSync(userSettingsPath)) return [];\n let user: Record<string, unknown>;\n try {\n user = JSON.parse(readFileSync(userSettingsPath, \"utf8\"));\n } catch {\n return [];\n }\n mkdirSync(logDir, { recursive: true });\n const path = join(logDir, `${wid}.settings.json`);\n writeFileSync(path, JSON.stringify(slimSettings(user, env, provider, noteHookCommand(logDir)), null, 2) + \"\\n\", \"utf8\");\n return [\"--setting-sources\", \"project,local\", \"--settings\", path];\n}\n\n/**\n * Settings-file id of a pool slot. The file is read when the slot's claude\n * starts, so slots of different providers must not share one name: the last\n * writer's provider env (or lack of it) would land in every slot.\n */\nexport function poolSlotSettingsId(providerName: string, model: string): string {\n return `pool-slot-${providerName}-${model}`.replace(/[^A-Za-z0-9._-]/g, \"_\");\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 * policy.ts — where workers run: one persistent policy plus per-run hints.\n *\n * cloud-only never local (battery, on the road)\n * local-only never cloud (conserve tokens); fails when no local server answers\n * prefer-local local first, falls through to cloud on failure\n * prefer-cloud cloud first (fast turnaround), local only when cloud is unavailable\n * auto prefer-local; cloud-only on battery; prefer-cloud for an --urgent run\n *\n * The policy lives in <logDir>/routing-policy.json (not workers.yaml: a runtime\n * switch with an expiry, not provider configuration). An explicit --provider\n * (or --capability) bypasses it entirely. chooseRoute and fallThroughReason\n * are pure; the I/O (battery, ping, quota) is gathered by routeRun.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n ANTHROPIC_NATIVE,\n WorkersConfigError,\n getProviderOrNative,\n isLocalProvider,\n type WorkersConfig,\n} from \"./config.js\";\nimport { cooldownRemaining, quotaExceeded, readRoutingState } from \"./routing.js\";\n\nexport const POLICY_MODES = [\"cloud-only\", \"local-only\", \"prefer-local\", \"prefer-cloud\", \"auto\"] as const;\nexport type PolicyMode = (typeof POLICY_MODES)[number];\n\nexport interface PolicyState {\n mode: PolicyMode;\n /** ISO stamp after which the policy no longer applies. */\n until?: string;\n set: string;\n}\n\n/** How a run was routed: shown by `pai worker ps`, kept in the worker status. */\nexport interface RouteInfo {\n policy: PolicyMode;\n reason: string;\n local: boolean;\n}\n\nexport function policyPath(logDir: string): string {\n return join(logDir, \"routing-policy.json\");\n}\n\n/** The active policy, or null when none is set or it has expired. */\nexport function readPolicy(logDir: string, now: Date = new Date()): PolicyState | null {\n const path = policyPath(logDir);\n if (!existsSync(path)) return null;\n try {\n const p = JSON.parse(readFileSync(path, \"utf8\")) as PolicyState;\n if (!(POLICY_MODES as readonly string[]).includes(p.mode)) return null;\n if (p.until && Date.parse(p.until) <= now.getTime()) return null;\n return p;\n } catch {\n return null;\n }\n}\n\nexport function writePolicy(logDir: string, mode: PolicyMode, forMs?: number, now: Date = new Date()): PolicyState {\n const state: PolicyState = {\n mode,\n ...(forMs ? { until: new Date(now.getTime() + forMs).toISOString() } : {}),\n set: now.toISOString(),\n };\n const tmp = `${policyPath(logDir)}.tmp`;\n writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n renameSync(tmp, policyPath(logDir));\n return state;\n}\n\nexport function clearPolicy(logDir: string): void {\n if (existsSync(policyPath(logDir))) unlinkSync(policyPath(logDir));\n}\n\n/** \"30m\", \"2h\", \"1d\" → milliseconds. */\nexport function parseDuration(s: string): number {\n const m = /^(\\d+)([mhd])$/.exec(s.trim());\n if (!m) throw new WorkersConfigError(`--for: expected a duration like 30m, 2h or 1d, got \"${s}\"`);\n return Number(m[1]) * { m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2] as \"m\" | \"h\" | \"d\"];\n}\n\nexport function onBattery(): boolean {\n try {\n return /Battery Power/.test(execFileSync(\"pmset\", [\"-g\", \"batt\"], { encoding: \"utf8\", timeout: 3000 }));\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// route selection\n// ---------------------------------------------------------------------------\n\nexport interface RouteInputs {\n mode: PolicyMode;\n urgent: boolean;\n localOk: boolean;\n cloud: boolean;\n battery: boolean;\n /** Name of an enabled local provider that answered, or null. */\n local: string | null;\n /** Why `local` is null: \"no local provider\" (none enabled) or \"local down\". */\n localWhy: string;\n /** The cloud provider is in cooldown or over its quota. */\n cloudUnavailable: boolean;\n}\n\nexport type RouteChoice = { local: string; reason: string } | { local: null; reason: string };\n\nexport function chooseRoute(i: RouteInputs): RouteChoice {\n const cloud = (reason: string): RouteChoice => ({ local: null, reason });\n const useLocal = (reason: string): RouteChoice => ({ local: i.local!, reason });\n if (i.mode === \"cloud-only\") return cloud(\"policy cloud-only\");\n if (i.mode === \"local-only\") {\n if (!i.local) {\n throw new WorkersConfigError(`policy local-only: ${i.localWhy} — start a local server or change the policy (pai worker policy)`);\n }\n return useLocal(\"policy local-only\");\n }\n if (i.mode === \"prefer-cloud\") {\n if (i.localOk && i.local && !i.cloud) return useLocal(\"--local-ok\");\n if (i.cloudUnavailable && i.local) return useLocal(\"cloud unavailable\");\n return cloud(\"policy prefer-cloud\");\n }\n // prefer-local and auto\n if (i.mode === \"auto\" && i.battery) return cloud(\"battery\");\n if (i.urgent) return cloud(\"urgent\");\n if (i.cloud) return cloud(\"--cloud\");\n if (!i.local) return cloud(i.localWhy);\n return useLocal(`policy ${i.mode}`);\n}\n\n/** One cheap models request; any answer below 500 (a 401 included) means the server is up. */\nexport async function pingLocal(baseUrl: string, timeoutMs = 4000): Promise<boolean> {\n try {\n const r = await fetch(`${baseUrl.replace(/\\/+$/, \"\")}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });\n return r.status < 500;\n } catch {\n return false;\n }\n}\n\n/** First enabled, loopback, answering provider in config order. */\nexport async function findLocal(\n config: WorkersConfig,\n logDir: string,\n ping: (url: string) => Promise<boolean> = pingLocal\n): Promise<{ name: string | null; why: string }> {\n const state = readRoutingState(logDir);\n const candidates = Object.entries(config.providers).filter(\n ([name, p]) => p.enabled && p.engine !== \"image\" && isLocalProvider(p.baseUrl) && cooldownRemaining(state, name) === 0\n );\n if (!candidates.length) return { name: null, why: \"no local provider\" };\n for (const [name, p] of candidates) if (await ping(p.baseUrl)) return { name, why: \"\" };\n return { name: null, why: \"local down\" };\n}\n\nexport interface RouteFlags {\n urgent?: boolean;\n localOk?: boolean;\n cloud?: boolean;\n}\n\n/**\n * The policy decision for one run: null when no policy is set (class/active\n * routing stands), else the provider to pin (null = cloud, normal routing)\n * and the RouteInfo to record.\n */\nexport async function routeRun(\n config: WorkersConfig,\n logDir: string,\n flags: RouteFlags\n): Promise<{ providerFlag: string | null; route: RouteInfo } | null> {\n const policy = readPolicy(logDir);\n if (!policy) return null;\n const mode = policy.mode;\n const needLocal = mode !== \"cloud-only\";\n const found = needLocal ? await findLocal(config, logDir) : { name: null, why: \"\" };\n const native = getProviderOrNative(config, ANTHROPIC_NATIVE);\n const cloudUnavailable =\n mode === \"prefer-cloud\" &&\n (cooldownRemaining(readRoutingState(logDir), ANTHROPIC_NATIVE) > 0 || (native ? quotaExceeded(native) : false));\n const choice = chooseRoute({\n mode,\n urgent: flags.urgent ?? false,\n localOk: flags.localOk ?? false,\n cloud: flags.cloud ?? false,\n battery: mode === \"auto\" ? onBattery() : false,\n local: found.name,\n localWhy: found.why,\n cloudUnavailable,\n });\n return { providerFlag: choice.local, route: { policy: mode, reason: choice.reason, local: choice.local !== null } };\n}\n\n// ---------------------------------------------------------------------------\n// fall-through\n// ---------------------------------------------------------------------------\n\nexport interface EndedRun {\n reportValid?: boolean;\n timedOut?: boolean;\n rc?: number | null;\n state?: string;\n}\n\n/**\n * Why a finished local run should be repeated once on the cloud, or null.\n * Reads the stored result: an invalid report (reportValid false covers a\n * failed AG2 check and the proof-check downgrade), r=- or r=!, a timeout, a\n * crash with no report. A killed run (rc 143) is the operator's call, not a\n * model failure.\n */\nexport function fallThroughReason(\n route: RouteInfo | undefined,\n status: EndedRun,\n report: { result?: string } | null\n): string | null {\n if (!route?.local || route.policy === \"local-only\") return null;\n if (status.timedOut) return \"max-minutes\";\n if (status.rc === 143) return null;\n if (status.reportValid === false) return \"invalid report\";\n if (report?.result === \"-\" || report?.result === \"!\") return `r=${report.result}`;\n if (!report && status.state === \"failed\") return \"no report\";\n return null;\n}\n","/**\n * session-name.ts — the one place that decides which session profile applies:\n * the name `pai` exported at launch, else the persistent name the picker shows\n * (AIBroker registry by iTerm pane, bridged to the claude session id; then the\n * transcript's customTitle), else the project name of the cwd.\n */\n\nimport { currentSessionName } from \"./session-profile-store.js\";\nimport { AIBROKER_REGISTRY, resolveSession } from \"./scope.js\";\n\nexport interface SessionNameDeps {\n registryPath?: string;\n mapPath?: string;\n projectsDir?: string;\n /** Project name for a directory (default: the registry `pai project list` reads). */\n projectName?: (cwd: string) => Promise<string | undefined>;\n cwd?: string;\n}\n\nasync function registryProjectName(cwd: string): Promise<string | undefined> {\n try {\n const { detectProject } = await import(\"../cli/impl/detect.js\");\n return (await detectProject(cwd))?.display_name || undefined;\n } catch {\n return undefined;\n }\n}\n\nlet cached: { key: string; name: string | undefined } | null = null;\n\n/** Env name, else AIBroker/transcript persistent name, else the cwd's project name. Cached per process (per env identity). */\nexport async function detectSessionName(env: NodeJS.ProcessEnv = process.env, deps: SessionNameDeps = {}): Promise<string | undefined> {\n const fromEnv = currentSessionName(env);\n if (fromEnv) return fromEnv;\n const cwd = deps.cwd ?? process.cwd();\n const key = [env.ITERM_SESSION_ID, env.CLAUDE_CODE_SESSION_ID, cwd, deps.registryPath, deps.mapPath, deps.projectsDir].join(\"|\");\n if (cached?.key === key) return cached.name;\n\n let name: string | undefined;\n const registry = deps.registryPath ?? AIBROKER_REGISTRY;\n const claudeId = env.CLAUDE_CODE_SESSION_ID?.trim();\n const { loadBrokerNames, transcriptCustomTitle } = await import(\"../cli/lib/session-scan.js\");\n name =\n resolveSession(env.ITERM_SESSION_ID ?? \"\", registry)?.name ??\n (claudeId ? loadBrokerNames(registry, deps.mapPath).get(claudeId) ?? transcriptCustomTitle(claudeId, deps.projectsDir) : undefined) ??\n (await (deps.projectName ?? registryProjectName)(cwd));\n cached = { key, name };\n return name;\n}\n\n/** Test hook: forget the per-process cache. */\nexport function resetDetectedSessionName(): void {\n cached = null;\n}\n","/**\n * session-profile.ts — one store, one resolver for \"which models does this\n * session use\". A profile is kept per NAMED session (the persistent name:\n * AIBroker name / custom title) in session-profiles.yaml under the PAI home:\n *\n * job-search:\n * chat: { provider: anthropic, model: opus }\n * workers: { provider: kimi, model: fast }\n * fallback: [ { provider: glm, model: fast }, { provider: local } ]\n * parallel_min: 2\n *\n * resolveWorkers is the single function every worker spawn path asks (pai\n * worker run, MCP handoff/queue/plan tools). Precedence:\n *\n * explicit target > session profile workers > env from a setup launch > global routing\n *\n * When the chosen provider is exhausted (a fresh budget reading at or above\n * its wall, default 95% of any window, or a recorded quota/429 cooldown) the\n * profile's fallback list is walked in order, else the global auto order.\n * In a batch (queue run, plan run with parallel > 1, explicit flag) fallback\n * targets whose concurrency cap is below parallel_min are skipped: a cap-1\n * server must not serialise a fan-out. Nothing left reads as `none`, with the\n * reasons, rather than a silent slow path.\n */\n\nimport { resolveCap } from \"./capacity.js\";\nimport type { BudgetMap } from \"./budget.js\";\nimport { budgetFull } from \"./budget.js\";\nimport { getProviderOrNative, type WorkersConfig } from \"./config.js\";\nimport { isLoopbackProvider } from \"./run-env.js\";\nimport { cooldownRemaining, autoOrder, type RoutingState } from \"./routing.js\";\nimport { SESSION_WORKER_PROVIDER_ENV } from \"./session-provider.js\";\nimport { detectSessionName } from \"./session-name.js\";\nimport { BATCH_ENV, currentSessionName, getProfile, type ProfileTarget, type SessionProfile } from \"./session-profile-store.js\";\n\nexport * from \"./session-profile-store.js\";\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\nexport interface ResolveArgs {\n /** Explicit target: wins over everything, never falls back. */\n explicit?: { provider?: string; model?: string };\n /** This spawn is part of a fan-out. */\n batch?: boolean;\n /** Parallelism of the fan-out (plan parallel); informational. */\n parallel?: number;\n}\n\nexport interface ResolveCtx {\n cfg: Pick<WorkersConfig, \"providers\" | \"routing\" | \"active\" | \"nativeModels\">;\n profile: SessionProfile | null;\n env: NodeJS.ProcessEnv;\n budgets: BudgetMap;\n routing: RoutingState;\n now?: Date;\n}\n\nexport type WorkersSource = \"explicit\" | \"profile\" | \"env\" | \"fallback\" | \"global\" | \"none\";\n\nexport interface WorkersResolution {\n source: WorkersSource;\n provider?: string;\n /** Alias default|fast or a model id; undefined = the provider's own choice. */\n model?: string;\n /** One line, with the reason: \"glm fast (fallback: kimi 5h at 100%)\". */\n description: string;\n}\n\nconst label = (provider: string, model?: string): string => `${provider}${model && model !== \"default\" ? ` ${model}` : \"\"}`;\n\n/** Why a provider cannot take work right now; null when it can. */\nexport function exhaustion(ctx: ResolveCtx, name: string): string | null {\n const p = getProviderOrNative(ctx.cfg, name);\n if (!p) return `${name} not configured`;\n if (!p.enabled) return `${name} disabled`;\n const left = cooldownRemaining(ctx.routing, name, ctx.now);\n if (left > 0) return `${name} out of quota, ${Math.ceil(left / 60_000)}m cooldown`;\n const hit = budgetFull(ctx.cfg.routing, name, ctx.budgets[name]);\n if (hit) return `${name} ${hit.window ? `${hit.window} ` : \"\"}at ${hit.percent}%`;\n return null;\n}\n\n/** Concurrency cap of a provider; Infinity when uncapped. */\nexport function capOf(cfg: ResolveCtx[\"cfg\"], name: string): number {\n const p = getProviderOrNative(cfg, name);\n if (!p) return Infinity;\n return resolveCap(p.maxConcurrent, isLoopbackProvider(p)) ?? Infinity;\n}\n\nexport function resolveWorkers(args: ResolveArgs, ctx: ResolveCtx): WorkersResolution {\n const ex = args.explicit;\n if (ex?.provider) return { source: \"explicit\", provider: ex.provider, model: ex.model, description: `${label(ex.provider, ex.model)} (explicit)` };\n\n const profile = ctx.profile;\n const envProvider = ctx.env[SESSION_WORKER_PROVIDER_ENV]?.trim();\n let chosen: ProfileTarget | undefined = profile?.workers;\n let source: WorkersSource = \"profile\";\n if (!chosen && envProvider) {\n chosen = { provider: envProvider };\n source = \"env\";\n }\n // an explicit model alone (no provider) still applies to the chosen provider\n if (chosen && ex?.model) chosen = { ...chosen, model: ex.model };\n if (!chosen) return { source: \"global\", model: ex?.model, description: \"global routing\" };\n\n const why = exhaustion(ctx, chosen.provider);\n if (!why) return { source, provider: chosen.provider, model: chosen.model, description: `${label(chosen.provider, chosen.model)} (${source === \"env\" ? \"setup launch\" : \"session profile\"})` };\n\n const model = chosen.model;\n const skipped: string[] = [];\n const min = args.batch ? profile?.parallel_min : undefined;\n const list: ProfileTarget[] = profile?.fallback?.length\n ? profile.fallback\n : autoOrder(ctx.cfg as WorkersConfig).map((provider) => ({ provider }));\n for (const t of list) {\n if (t.provider === chosen.provider) continue;\n const why2 = exhaustion(ctx, t.provider);\n if (why2) {\n skipped.push(why2);\n continue;\n }\n const cap = capOf(ctx.cfg, t.provider);\n if (min !== undefined && cap < min) {\n skipped.push(`${t.provider} cap ${cap} < parallel_min ${min}`);\n continue;\n }\n const m = t.model ?? model;\n return { source: \"fallback\", provider: t.provider, model: m, description: `${label(t.provider, m)} (fallback: ${why})` };\n }\n return {\n source: \"none\",\n description: `no worker target available: ${why}${skipped.length ? `; skipped ${skipped.join(\", \")}` : \"\"}`,\n };\n}\n\n/** The live context: config, this session's profile, fresh budget readings (warmed best effort) and recorded cooldowns. */\nexport async function liveResolveCtx(env: NodeJS.ProcessEnv = process.env, warm = true): Promise<ResolveCtx> {\n const [{ readWorkersSection }, { budgetCache }, { readRoutingState }, { workersLogDir }] = await Promise.all([\n import(\"./config.js\"),\n import(\"./budget.js\"),\n import(\"./routing.js\"),\n import(\"./paths.js\"),\n ]);\n const { workers } = readWorkersSection();\n const profile = getProfile(await detectSessionName(env));\n if (warm && profile && workers.routing.policy !== \"order\") {\n try {\n await budgetCache.warm(workers);\n } catch {\n // unknown readings never read as full\n }\n }\n return { cfg: workers, profile, env, budgets: budgetCache.snapshot(), routing: readRoutingState(workersLogDir(workers)) };\n}\n\nexport const isBatchEnv = (env: NodeJS.ProcessEnv = process.env): boolean => env[BATCH_ENV] === \"1\";\n\n/**\n * \"this session's workers: <provider/model> (<reason>)\" from the same resolver\n * every spawn uses; null outside a session. Never warms budget readings (cheap\n * enough for a hook).\n */\nexport async function sessionWorkersLine(env: NodeJS.ProcessEnv = process.env): Promise<string | null> {\n try {\n if (!(await detectSessionName(env))) return null;\n const r = resolveWorkers({ batch: env[BATCH_ENV] === \"1\" }, await liveResolveCtx(env, false));\n return `this session's workers: ${r.description}`;\n } catch {\n return null;\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 * 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 { currentWorkerId } from \"./worker-id.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 = currentWorkerId(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\" || s.state === \"queued\") && !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 * 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 { currentWorkerId } from \"./worker-id.js\";\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 = currentWorkerId(env, logDir);\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 * denials.ts — hook/permission denials a worker ran into, recorded by the\n * runner so orchestrators can fix specs that contradict the worker contract\n * and turn recurring ones into deterministic rules.\n *\n * Detection happens on the stream-json tool_result (see run.ts); this file is\n * pure: text → denial, denial list → specConflict flags.\n */\n\nexport interface Denial {\n at: string;\n tool: string;\n hook: string;\n /** First ~120 chars of the denied input (command, file_path, …). */\n input: string;\n /** First ~200 chars of the denial reason. */\n reason: string;\n specConflict: boolean;\n}\n\n/** Entries kept per run; `denialCount` on the status keeps the true total. */\nexport const MAX_DENIALS = 50;\nconst MIN_SPEC_OVERLAP = 20;\n\n/**\n * Claude Code's denial texts: `PreToolUse:<Tool> hook error: [<hook>]: <reason>`\n * (also \"hook blocking error from command\"), the `<prog>: command denied by\n * permission rules` shape, and the plain permission-denied sentences.\n */\nexport function parseDenialText(\n text: string,\n fallbackTool = \"?\"\n): { tool: string; hook: string; reason: string } | null {\n const pre = text.match(/^PreToolUse:(\\S+) hook (?:blocking )?error(?: from command)?:\\s*(?:\\[([^\\]]*)\\]:?\\s*)?([\\s\\S]*)/);\n if (pre) return { tool: pre[1], hook: pre[2] ?? \"hook\", reason: pre[3].trim() };\n const rules = text.match(/(?:^|\\n)(\\S+): command denied by permission rules/);\n if (rules) return { tool: fallbackTool, hook: rules[1], reason: text.trim() };\n if (/Permission to use \\S+ has been denied|requested permissions to use \\S+, but you haven't granted/.test(text)) {\n return { tool: fallbackTool, hook: \"permission\", reason: text.trim() };\n }\n return null;\n}\n\n/** The denied input as one short line: command / file_path / path / pattern / url, else JSON. */\nexport function denialInput(input: unknown): string {\n if (typeof input !== \"object\" || input === null) return \"\";\n const o = input as Record<string, unknown>;\n const key = [\"command\", \"file_path\", \"path\", \"pattern\", \"url\"].find((k) => typeof o[k] === \"string\");\n return (key ? String(o[key]) : JSON.stringify(o)).replace(/\\s+/g, \" \").slice(0, 120);\n}\n\nconst normalizeReason = (r: string): string => r.toLowerCase().replace(/\\d+/g, \"#\").replace(/\\s+/g, \" \").trim();\n\n/**\n * specConflict: the denial is likely the orchestrator's spec contradicting the\n * worker contract, not a worker mistake. True when\n * (a) the same (hook, tool, normalized reason) was denied 2+ times in the run, or\n * (b) the denied input shares a >= 20 char verbatim substring with the spec/prompt.\n * Sets the flag on every entry it matches, so the first occurrence of a\n * repeated denial is flagged too. Cheap enough: O(input × spec) on ≤120 chars.\n */\nexport function markSpecConflicts(denials: Denial[], spec: string | null): void {\n const key = (d: Denial): string => `${d.hook}\\0${d.tool}\\0${normalizeReason(d.reason)}`;\n const seen = new Map<string, number>();\n for (const d of denials) seen.set(key(d), (seen.get(key(d)) ?? 0) + 1);\n for (const d of denials) {\n d.specConflict = (seen.get(key(d)) ?? 0) >= 2 || sharesSubstring(d.input, spec ?? \"\");\n }\n}\n\nfunction sharesSubstring(input: string, spec: string): boolean {\n for (let i = 0; i + MIN_SPEC_OVERLAP <= input.length; i++) {\n if (spec.includes(input.slice(i, i + MIN_SPEC_OVERLAP))) return true;\n }\n return false;\n}\n\n/** Status fields the recorder owns. */\nexport interface DenialStatus {\n denials?: Denial[];\n denialCount?: number;\n}\n\n/**\n * Record one tool_result block when it is a denial. `uses` maps tool_use ids to\n * the tool name and input. Returns whether the status changed.\n */\nexport function recordDenial(\n status: DenialStatus,\n block: { tool_use_id?: string; is_error?: boolean; content?: unknown },\n text: string,\n uses: Map<string, { name: string; input: unknown }>,\n spec: string | null,\n at: string\n): boolean {\n if (block.is_error !== true) return false;\n const use = block.tool_use_id ? uses.get(block.tool_use_id) : undefined;\n const p = parseDenialText(text, use?.name ?? \"?\");\n if (!p) return false;\n const list = (status.denials ??= []);\n status.denialCount = (status.denialCount ?? 0) + 1;\n if (list.length < MAX_DENIALS) {\n list.push({\n at,\n tool: use?.name ?? p.tool,\n hook: p.hook,\n input: denialInput(use?.input),\n reason: p.reason.slice(0, 200),\n specConflict: false,\n });\n }\n markSpecConflicts(list, spec);\n return true;\n}\n\n/** `denied N` plus `spec?` when any entry looks like a spec conflict; \"\" when none. */\nexport function denialMark(s: DenialStatus): string {\n const n = s.denialCount ?? s.denials?.length ?? 0;\n if (n <= 0) return \"\";\n return `denied ${n}${s.denials?.some((d) => d.specConflict) ? \" spec?\" : \"\"}`;\n}\n\n/** Replay lines: one per recorded denial, `!` marking a likely spec conflict. */\nexport function denialLines(s: DenialStatus): string[] {\n const list = s.denials ?? [];\n if (!list.length) return [];\n const more = (s.denialCount ?? list.length) - list.length;\n return [\n ` ${denialMark(s)}:`,\n ...list.map((d) => ` ${d.specConflict ? \"!\" : \" \"} ${d.at.slice(11, 19)} ${d.tool} [${d.hook}] ${d.input} — ${d.reason}`),\n ...(more > 0 ? [` … ${more} more`] : []),\n ];\n}\n","/**\n * Per-provider worker-contract addendum: lessons learned for a model class,\n * appended to the system prompt right after the generic contract. File is\n * `providers.<name>.contract` from the config, else `<PAI_HOME>/contracts/<name>.md`.\n * Missing or empty: no addendum, behaviour unchanged.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { paiHomePath } from \"../config/pai-home.js\";\n\nexport interface ContractAddendum {\n path: string;\n text: string;\n sha: string;\n}\n\nexport function loadContractAddendum(provider: string, override?: string): ContractAddendum | null {\n const path = override ?? paiHomePath(\"contracts\", `${provider}.md`);\n if (!existsSync(path)) return null;\n const text = readFileSync(path, \"utf8\").trim();\n if (!text) return null;\n return { path, text, sha: createHash(\"sha256\").update(text).digest(\"hex\").slice(0, 8) };\n}\n","/**\n * result-grace.ts — end a headless claude that outlives its final result.\n *\n * Headless Claude Code waits for background Bash tasks before it exits, so a\n * leftover `sleep`/scan can keep the run \"running\" long after the result event.\n * After the result the process gets a short grace to exit by itself; if it has\n * not, everything below it and then claude itself is terminated.\n */\n\nimport { processTree, killProcessTree } from \"./wait-on.js\";\n\nexport const RESULT_GRACE_MS = 20_000;\n\nexport interface ResultGraceDeps {\n /** Pids below `pid` (not `pid` itself). */\n descendants: (pid: number) => number[];\n /** End `pid` and its whole tree. */\n terminate: (pid: number) => Promise<void>;\n}\n\nconst defaultDeps: ResultGraceDeps = {\n descendants: (pid) => processTree(pid).slice(1),\n terminate: (pid) => killProcessTree(pid),\n};\n\nexport interface ResultGrace {\n /** The final result event arrived: (re)start the grace clock. */\n onResult(): void;\n /** More work is coming (operator input) or the process exited: stop the clock. */\n cancel(): void;\n /** Resolves once a fired grace has finished terminating; the leftover count. */\n readonly leftover: number;\n readonly fired: boolean;\n settled(): Promise<void>;\n}\n\nexport function armResultGrace(\n pid: number | undefined,\n graceMs = RESULT_GRACE_MS,\n deps: ResultGraceDeps = defaultDeps\n): ResultGrace {\n let timer: NodeJS.Timeout | null = null;\n let done: Promise<void> = Promise.resolve();\n const g = {\n leftover: 0,\n fired: false,\n onResult() {\n if (!pid || g.fired) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n g.fired = true;\n g.leftover = deps.descendants(pid).length;\n done = deps.terminate(pid).catch(() => undefined);\n }, graceMs);\n timer.unref?.();\n },\n cancel() {\n if (timer) clearTimeout(timer);\n timer = null;\n },\n settled: () => done,\n };\n return g;\n}\n\nexport function leftoverNote(n: number): string {\n return `finished; ended ${n} leftover background process(es)`;\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 * 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 * 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 { 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 * 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 { grantedToolNames, recordToolError } from \"./tool-errors.js\";\nimport { editHintFor } from \"./edit-assist.js\";\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 rmSync,\n} from \"node:fs\";\nimport { statusPath } from \"./paths.js\";\nimport { NOTE_TOKEN_ENV, newNoteToken } from \"./note-delivery.js\";\nimport { appendSystemPromptArgs, parseRunnerArgs, shortText, stripPromptValues } from \"./args.js\";\nimport {\n ANTHROPIC_NATIVE,\n assertProviderRunnable,\n classModelCapability,\n getProviderOrNative,\n isLocalProvider,\n isModelCapability,\n NATIVE_ANTHROPIC_MODELS,\n readWorkersSection,\n resolveModelCapability,\n type WorkerProvider,\n type WorkersConfig,\n} from \"./config.js\";\nimport { buildRunEnv, claudeCommand, isLoopbackProvider, routeLoopbackViaRouter } from \"./run-env.js\";\nimport {\n httpModelsCheck,\n LOCAL_MECHANISM_LINE,\n NoLocalServerError,\n PORT_TIMEOUT_MS,\n probeLoopback,\n providerHostPort,\n resolveLocalProvider,\n type PortCheck,\n} from \"./local-provider.js\";\nimport { probeRouter } from \"../router/supervisor.js\";\nimport { detectCurrentModel } from \"./launch.js\";\n\nexport { buildRunEnv } from \"./run-env.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { decideEndReason, endFields, parentInfo, type EndInfo } from \"./endreason.js\";\nimport { peekLoopStop, takeLoopStop } from \"./loop-stop.js\";\nimport { TroubleWatcher } from \"./worker-trouble.js\";\nimport { reportOrigin, runMessenger } from \"./run-report-wiring.js\";\nimport {\n ensureNoMcpConfig,\n eventsPath,\n ledgerPath,\n noMcpConfigPath,\n workersLogDir,\n} from \"./paths.js\";\nimport { slimProfileArgs, WORKER_BRIEF } from \"./slim-profile.js\";\nimport {\n type WorkerStatus,\n newWorkerId,\n loadStatus,\n saveStatus,\n markStartFailed,\n describeTool,\n nowStamp,\n parseSleepSecs,\n UNLABELED,\n} from \"./status.js\";\nimport { certainContextWindow } from \"../utils/model-window.js\";\nimport { resolveSession, resolveSpawnerSession } from \"./scope.js\";\nimport { applySessionProviderEnv } from \"./session-provider.js\";\nimport { returnControlsIfHeld } from \"./controls.js\";\nimport { fallThroughReason, routeRun, type RouteInfo } from \"./policy.js\";\nimport { capacityGate, queuedText, resolveCap, sessionScopeKey, type CapacityOpts } from \"./capacity.js\";\nimport { recordWarmup, staggerGate } from \"./stagger.js\";\nimport {\n autoOrder,\n capabilityForRun,\n isQuotaFailure,\n nextAutoProvider,\n NoProviderError,\n recordBudgetCooldowns,\n resolveCapabilityRun,\n resolveTarget,\n setCooldown,\n type CapabilityRunResolution,\n} from \"./routing.js\";\nimport { detectSessionName } from \"./session-name.js\";\nimport { getProfile, isBatchEnv, liveResolveCtx, resolveWorkers } from \"./session-profile.js\";\nimport { budgetCache } from \"./budget.js\";\nimport { runImageCapability } from \"./engines/image.js\";\nimport { ALLOW_HIDDEN_ENV, headlessAllowed, headlessOverridePath, openPaneForWorker, openVerifiedPane, paneBackend, panePlacement, resolveHostSession } from \"./pane.js\";\nimport { assertChildAllowed, checkOrphanedChildren, isWorkerId, launchParent } from \"./tree.js\";\nimport { deliverHandoff, isHandoffMessage } from \"./handoff.js\";\nimport {\n addWorktree,\n adoptTree,\n markResumed,\n salvageOnExit,\n archiveEmptyResult,\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 noteTokenSentence,\n workerContractPrompt,\n parseWorkerReport,\n verifyReportChanges,\n verifyFailNote,\n checkProof,\n newUntracked,\n untrackedPaths,\n type BashRun,\n type WorkerReport,\n} from \"./report.js\";\nimport { parseDenialText, recordDenial } from \"./denials.js\";\nimport { validateAg2 } from \"./agentish.js\";\nimport { loadContractAddendum, type ContractAddendum } from \"./contract-addendum.js\";\nimport { loadPreamble, preambleBlock, preambleLine, type Preamble } from \"./preamble.js\";\nimport { killTree } from \"./kill-tree.js\";\nimport { armResultGrace, leftoverNote } from \"./result-grace.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 { inputSideTokens, type UsageBlock } from \"../utils/input-side-tokens.js\";\nimport { buildsDir, releaseBuildsOf } from \"../builds/queue.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\";\nimport {\n poolIneligibleReason,\n poolSettings,\n postTaskToPool,\n SHARED_MARKER,\n UNIT_MARKER,\n type PoolTaskResponse,\n} from \"./pool.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 /** --no-stagger: skip the same-provider start gate. */\n noStagger?: boolean;\n /** --no-queue: skip the per-provider concurrency cap. */\n noQueue?: boolean;\n /** --now: wait at the front of the provider's queue (capacity.ts). */\n priority?: \"front\";\n /** --model fast without a provider: the routed provider's fast model. */\n modelFast?: boolean;\n /** --model default without a provider: the routed provider's default model, not its worker_model. */\n modelDefault?: 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 /** Resume: the id whose worktree this run takes over (see adoptTree). */\n resumeOf?: string;\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 /** \"provider/model (why)\" — one line on how the provider was chosen. */\n routeWhy?: string;\n /** --urgent: wants a fast turnaround (policy auto/prefer-local route to the cloud). */\n urgent?: boolean;\n /** --local-ok: this run may go local under policy prefer-cloud. */\n localOk?: boolean;\n /** --cloud: this run goes to the cloud under policy prefer-local/auto. */\n cloud?: boolean;\n /** --local: run on the local model server PAI resolves (resolveLocalProvider); beats --provider. */\n localFlag?: boolean;\n /** Internal: the policy already decided (fall-through re-run); carries its route and the run it repeats. */\n _route?: RouteInfo;\n _fallThroughOf?: string;\n /** Internal: suppress recursion depth on reroute. */\n _reroutes?: number;\n /** Internal: this run is the one automatic re-launch after an empty model response. */\n _emptyRetried?: boolean;\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 /** --orchestrate: the worker plans and reviews; code edits are denied (PAI_WORKER_ORCHESTRATOR=1). */\n orchestrate?: 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 * Port-probe seam for the down/disabled local-provider guard (tests\n * inject; production: httpModelsCheck — the same probe\n * resolveLocalProvider uses).\n */\n _localCheck?: PortCheck;\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\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 tool_use_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/** Current context fill of one assistant turn: the input side, no output. */\nexport function usageContextUsed(u: UsageBlock | undefined): number | null {\n if (!u) return null;\n const t = inputSideTokens(u);\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\nexport function 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 let capability = alias && isModelCapability(alias) ? alias : classModelCapability(className);\n // nothing named a model (no flag, no class role): the provider's worker_model decides\n if (!alias && capability === \"default\" && target.provider.workerModel === \"fast\") capability = \"fast\";\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 * Two model ids name the same model when their canonical forms do: a\n * bracketed suffix is a context-window variant, not another model\n * (\"glm-5.3[1m]\" IS \"glm-5.3\", announced with a 1M window — the same\n * collision the init event's adoptInitModel sees).\n */\nexport function canonicalModelId(id: string): string {\n return id.trim().replace(/\\[[^\\]]*\\]$/, \"\");\n}\n\nexport interface ModelStepDown {\n /** The model id the run goes out on. */\n model: string;\n /** One launch-preamble line, or null when resolution is unchanged. */\n notice: string | null;\n}\n\n/**\n * The model step-down: a worker never runs on the orchestrator's own model —\n * enforced here, not remembered (config.ts states the principle). Until\n * 2026-10-17 it was only enforced where a model id is named explicitly: with\n * no --model, the class map's default tier resolved glm's default model —\n * glm-5.3[1m], the very model the orchestrating session itself ran — and\n * nothing detected the equality. Mechanical now: when the resolved id equals\n * the orchestrating session's (detected from the environment exactly the way\n * `pai launch --list --current` does it, launch.ts's detectCurrentModel), the\n * same provider's fast model replaces it. An explicit operator --model always\n * wins, including \"use your own model\" — the runner's own flag and a --model\n * pinned in the claude args both count as explicit. A provider without a\n * distinct fast model keeps the resolved model with a warning; a run is never\n * refused over this. An undetectable orchestrator model changes nothing.\n */\nexport function stepDownOrchestratorModel(opts: {\n resolved: string;\n providerName: string;\n provider: Pick<WorkerProvider, \"models\">;\n /** Explicit --model (the runner's own flag): the operator's choice wins. */\n modelFlag?: string;\n /** The caller pinned --model in the claude args: also explicit. */\n callerPinned?: boolean;\n env?: NodeJS.ProcessEnv;\n}): ModelStepDown {\n if (opts.modelFlag || opts.callerPinned) return { model: opts.resolved, notice: null };\n const orchestrator = detectCurrentModel(opts.env ?? process.env);\n if (!orchestrator) return { model: opts.resolved, notice: null };\n if (canonicalModelId(opts.resolved) !== canonicalModelId(orchestrator)) {\n return { model: opts.resolved, notice: null };\n }\n const fast = opts.provider.models.fast;\n if (!fast || canonicalModelId(fast) === canonicalModelId(opts.resolved)) {\n return {\n model: opts.resolved,\n notice:\n `pai: worker model == orchestrator model — provider \"${opts.providerName}\" has no distinct ` +\n `fast model, keeping ${opts.resolved} (workers never inherit the orchestrator's model)`,\n };\n }\n return {\n model: fast,\n notice:\n `pai: worker model == orchestrator model — stepped down to ${fast} ` +\n `(workers never inherit the orchestrator's model); pass --model to override`,\n };\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 * Where a loopback run's per-worker lines (worktree path, id, deadline, note\n * token) go in the task: after a `--- shared ---` section, right before the\n * `--- unit ---` marker, so the shared section stays inside the cached prefix;\n * a task without both markers gets them at its head.\n */\nexport function placeVolatile(task: string, volatile: string): string {\n const lines = task.split(\"\\n\");\n const a = lines.findIndex((l) => l.trim() === SHARED_MARKER);\n const b = lines.findIndex((l, i) => i > a && l.trim() === UNIT_MARKER);\n if (a < 0 || b < 0) return volatile + \"\\n\\n\" + task;\n return [...lines.slice(0, b), volatile, \"\", ...lines.slice(b)].join(\"\\n\");\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 * An empty model response: the model \"answered\" without producing anything —\n * no tool ran and the result text is empty. A local model can stop at once with an empty reply;\n * it is a harness failure, not a finished run (rc 0 and a result record\n * notwithstanding). Usage plays no part: local servers report 0/0 on every\n * message, normal ones too.\n */\nexport function isEmptyResponse(resultText: string | undefined, tools: number): boolean {\n return tools === 0 && (resultText ?? \"\").trim() === \"\";\n}\n\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, 60, Date.now(), { olderThanHours: config.gcOlderThanHours, maxGb: config.worktreesMaxGb })?.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 // A disabled provider is refused before anything else reads the config —\n // same style as the down-local refusal below (an explicit --provider\n // naming a disabled provider must never run anyway).\n if (opts.providerFlag) {\n const named = getProviderOrNative(config, opts.providerFlag);\n if (named && !named.enabled) {\n throw new LocalProviderRefusedError(\n `provider \"${opts.providerFlag}\" is disabled — it cannot run this worker. ` +\n `Enable it with: pai worker providers enable ${opts.providerFlag}` +\n (isLoopbackProvider(named) ? `\\n${LOCAL_MECHANISM_LINE}` : \"\")\n );\n }\n }\n\n // Worker routing policy (policy.ts): picks local or cloud unless the caller named a provider.\n let route = opts._route;\n let providerFlag = opts.providerFlag;\n // The route reason names the real source: only a --provider the caller\n // actually typed says \"flag: --provider\"; --local says \"(local)\", a\n // routing-policy pin says \"(routing policy: <mode>)\" — like the session\n // profile case below.\n let providerWhy: string | undefined = opts.providerFlag ? \"flag: --provider\" : undefined;\n // --local: PAI resolves the running local server itself (local-provider.ts);\n // the resolved name wins over --provider — a provider literally named\n // \"local\" is not the mechanism.\n if (opts.localFlag) {\n try {\n const res = await resolveLocalProvider(config, logDir);\n providerFlag = res.name;\n providerWhy = \"local\";\n if (!opts.quiet) process.stderr.write(`pai worker: local provider ${res.name} (${res.why})\\n`);\n } catch (e) {\n // --print-cmd is a dry run: nothing must answer a port for the argv\n // audit, so an unresolved local pin falls back to the first configured\n // loopback provider and the route line still says (local). A real run\n // keeps the hard error — nothing spawns onto a server that is down.\n if (!(opts.printCmd && e instanceof NoLocalServerError)) throw e;\n const first = Object.entries(config.providers).find(\n ([, p]) => p.enabled && isLoopbackProvider(p)\n );\n if (!first) throw e;\n providerFlag = first[0];\n providerWhy = \"local\";\n if (!opts.quiet) process.stderr.write(`pai worker: local provider ${first[0]} (dry run: no local server up)\\n`);\n }\n }\n // The session's profile (workers, budget fallback) comes before routing; an explicit provider/--local skipped it above.\n let profileModel: string | undefined;\n if (!providerFlag && !capability && !route && (await detectSessionName())) {\n const r = resolveWorkers(\n { explicit: { model: opts.modelFlag ?? (opts.modelFast ? \"fast\" : opts.modelDefault ? \"default\" : undefined) }, batch: isBatchEnv() },\n await liveResolveCtx()\n );\n if (r.source === \"none\") throw new NoProviderError(r.description);\n if (r.provider && (r.source === \"profile\" || r.source === \"fallback\")) {\n providerFlag = r.provider;\n profileModel = r.model;\n providerWhy = r.source === \"fallback\" ? \"session profile fallback\" : \"session profile\";\n if (!opts.quiet) process.stderr.write(`pai worker: ${r.description}\\n`);\n }\n }\n if (!route && !providerFlag && !capability && opts.className !== \"plan\" && parseRunnerArgs(opts.claudeArgs).headless) {\n const routed = await routeRun(config, logDir, opts);\n if (routed) {\n route = routed.route;\n providerFlag = routed.providerFlag ?? undefined;\n if (providerFlag) providerWhy = `routing policy: ${route.policy}`;\n }\n }\n\n // Budget-aware fallback: probe live budgets (5-min cache) and cooldown any\n // provider at/above its wall before the walk, so resolveTarget picks the\n // first priority provider that still has room. Probing must never block a\n // spawn: failures read as \"unknown\" inside the cache, never as \"full\".\n if (!providerFlag && !capabilityResolution && config.routing.policy !== \"order\") {\n try {\n await budgetCache.warm(config);\n recordBudgetCooldowns(config, logDir);\n } catch {\n /* no readings: budget routing degrades to plain order */\n }\n }\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: providerFlag,\n className: opts.className,\n cwd: opts.cwd,\n });\n assertProviderRunnable(target.providerName, target.provider);\n // A named provider that is a local (loopback) one gets the same guard\n // --local has: never wait for a server that is down, never run on a\n // disabled one. The probe is resolveLocalProvider's (httpModelsCheck,\n // short timeout); the refusal names the running local provider and the\n // fix (--local). Remote providers are untouched.\n await assertLocalProviderAlive(config, logDir, target.providerName, target.provider, opts);\n if (opts.modelFast && !opts.modelFlag) target.modelAlias = \"fast\";\n if (opts.modelDefault && !opts.modelFlag) target.modelAlias = \"default\";\n const modelFlag = opts.modelFlag ?? (profileModel && profileModel !== \"fast\" && profileModel !== \"default\" ? profileModel : undefined);\n if (profileModel === \"fast\" && !opts.modelFlag) target.modelAlias = \"fast\";\n if (profileModel === \"default\" && !opts.modelFlag) target.modelAlias = \"default\";\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 let model = capabilityResolution\n ? capabilityResolution.model\n : resolveRunModel(target, opts.className, modelFlag);\n if (!capabilityResolution) {\n // capability runs pin their model on purpose (an image prompt on the\n // image model); the step-down guards the class/provider resolution only\n const step = stepDownOrchestratorModel({\n resolved: model,\n providerName: target.providerName,\n provider: target.provider,\n modelFlag,\n callerPinned: parsed.callerModel,\n });\n model = step.model;\n if (step.notice && !opts.quiet) process.stderr.write(`${step.notice}\\n`);\n }\n const reportFormat = resolveReportFormat(opts.reportFormatFlag);\n // the route and why, once (also recorded in the status as routeWhy)\n const routeReason = target.via === \"flag\" && providerWhy ? providerWhy : target.routeWhy;\n const routeWhy = routeReason ? `${target.providerName}/${model} (${routeReason})` : undefined;\n if (routeWhy && !opts.quiet) process.stderr.write(`pai worker: route ${routeWhy}\\n`);\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 const execArgs: ExecuteArgs = {\n config,\n logDir,\n target,\n model,\n label,\n parsed,\n claudeArgs: opts.claudeArgs,\n noPane: opts.noPane ?? false,\n noStagger: opts.noStagger,\n noQueue: opts.noQueue,\n priority: opts.priority,\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 resumeOf: opts.resumeOf,\n className: opts.className,\n reroutes: opts._reroutes ?? 0,\n emptyRetried: opts._emptyRetried ?? false,\n route,\n routeWhy,\n fallThroughOf: opts._fallThroughOf,\n printCmd: opts.printCmd,\n reportFormat,\n noReportRetry: opts.noReportRetry ?? false,\n orchestrate: opts.orchestrate,\n limit,\n noBrowser,\n opts,\n };\n // the session pool (workers.pool) gets first crack: an eligible task\n // runs as one more turn of a warm claude instead of a fresh spawn\n await gateStart(execArgs);\n const pooledRc = await tryPoolRun(execArgs);\n if (pooledRc !== null) {\n await returnControlsIfHeld(logDir, workerId);\n return pooledRc;\n }\n rc = await executeRun(execArgs);\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/** A named local provider that is disabled or whose server does not answer. */\nexport class LocalProviderRefusedError extends Error {}\n\n/**\n * A provider named for this run (--provider, a class pin, a profile, a\n * policy pin, a resume) that is loopback must be enabled and its server\n * must answer the HTTP models probe — the same probe resolveLocalProvider\n * uses. Refused immediately otherwise: never wait for a down local server,\n * never run on a disabled one. The message names the provider, what is\n * wrong with it, which local provider IS running (if any), and the fix.\n * Remote providers pass untouched; a run already resolved by --local passes\n * untouched (its server was probed by resolveLocalProvider itself).\n */\nexport async function assertLocalProviderAlive(\n config: WorkersConfig,\n logDir: string,\n name: string,\n p: WorkerProvider,\n opts: { localFlag?: boolean; _localCheck?: PortCheck } = {}\n): Promise<void> {\n if (opts.localFlag || !isLoopbackProvider(p)) return;\n const check = opts._localCheck ?? httpModelsCheck;\n if (!p.enabled) {\n const running = Object.entries(await probeLoopback(config, check))\n .filter(([, up]) => up)\n .map(([n]) => n);\n throw new LocalProviderRefusedError(\n `provider \"${name}\" is a local (loopback) provider and is disabled — it cannot run this worker.` +\n (running.length ? `\\nRunning local provider: ${running.join(\", \")}.` : \"\\nNo local model server is running.\") +\n `\\n${LOCAL_MECHANISM_LINE}`\n );\n }\n const hp = providerHostPort(p);\n const up = hp ? await check(hp.host, hp.port, PORT_TIMEOUT_MS) : false;\n if (up) return;\n const running = Object.entries(await probeLoopback(config, check))\n .filter(([n, isUp]) => isUp && n !== name)\n .map(([n]) => n);\n throw new LocalProviderRefusedError(\n `provider \"${name}\" is a local (loopback) provider whose server does not answer on ${hp ? `${hp.host}:${hp.port}` : \"its port\"} — ` +\n `it is not running. This run is refused instead of waiting for it.` +\n (running.length ? `\\nRunning local provider: ${running.join(\", \")}.` : \"\\nNo local model server is running.\") +\n `\\n${LOCAL_MECHANISM_LINE}`\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 */\nexport const 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 // the deadline stops the run's own process; its sub-workers go with it\n void killTree(logDir, status.id, { skipRoot: true }).catch(() => undefined);\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 loopStop = takeLoopStop(logDir, wid);\n if (loopStop) {\n const reason = `loop-stopped: ${loopStop.reason}`;\n status.loopStopped = true;\n status.last = shortText(reason, 90);\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 && !loopStop;\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, loopStopped: !!loopStop });\n status.endReason = end.reason;\n // r=~ (partial) means the work continues: saveStatus then keeps the issue claims\n status.claimsContinue = report?.result === \"~\";\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 // nothing to merge: the worktree goes now instead of piling up until gc\n if (f.worktree && status.worktreeDir) archiveEmptyResult(logDir, status);\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 runPid: 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 * The fail-closed pane gate: a headless run proceeds only behind a pane that\n * was opened and verified visible (see openVerifiedPane). Runs BEFORE the\n * worker has an id on disk, so a refusal leaves nothing in `pai worker ps`.\n *\n * Deterministic (no hidden workers): the ONLY skips are headlessAllowed() —\n * a worker's own child (PAI_WORKER=1), the operator's PAI_WORKER_ALLOW_HIDDEN=1\n * or their override file. `dryRun` (--print-cmd audits) skips too: it spawns\n * nothing. An explicit hidden request (noPane from a daemon, keepalive or an\n * in-process caller) without that consent is refused, not silently honoured —\n * a spawning session with no window (daemon) has no pane to attach to and\n * cannot run workers.\n */\nexport async function requireVisiblePane(\n logDir: string,\n config: ReturnType<typeof readWorkersSection>[\"workers\"],\n wid: string,\n term: string,\n noPane: boolean,\n dryRun: boolean,\n ledger: string\n): Promise<boolean> {\n if (dryRun) return false;\n const allow = headlessAllowed();\n if (allow) {\n process.stderr.write(`[pai worker] hidden run allowed for ${wid}: ${allow}\\n`);\n return false;\n }\n const how = `hidden runs need the operator's ${ALLOW_HIDDEN_ENV}=1 or ${headlessOverridePath()}`;\n if (noPane) {\n appendLedger(ledger, \"WORKER-REFUSED\", { id: wid, reason: `headless requested, no override (${how})`.slice(0, 300) });\n throw new Error(`worker not started, no visible pane: headless requested but nothing may run hidden (${how})`);\n }\n try {\n const where = await openVerifiedPane(logDir, config, wid, term);\n process.stderr.write(`[pai worker] pane verified for ${wid}: ${where}\\n`);\n return true;\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n appendLedger(ledger, \"WORKER-REFUSED\", { id: wid, reason: `${reason} (${how})`.slice(0, 300) });\n throw new Error(`worker not started, no visible pane: ${reason} (${how})`);\n }\n}\n\n/** status.last for a claude start that died before any model event: its stderr, not a bare \"starting\". */\nexport function preEventFailure(rc: number, turns: number, stderrTail: string): string | null {\n if (rc === 0 || turns > 0 || !stderrTail.trim()) return null;\n return \"failed before any model event: \" + shortText(stderrTail, 150);\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 status?: WorkerStatus\n): Promise<void> {\n const setPane = (p: NonNullable<WorkerStatus[\"pane\"]>): void => {\n if (status) status.pane = p;\n };\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(process.env, process.platform, term) === \"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 setPane({ opened: false, reason: skip });\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 setPane({ opened: true, ...(r.session ? { session: r.session } : {}) });\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 setPane({ opened: false, reason: message.slice(0, 200) });\n }\n}\n\nexport interface ExecuteArgs {\n /** capacity + stagger gates already passed (set by gateStart) */\n gated?: boolean;\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 noStagger?: boolean;\n noQueue?: boolean;\n priority?: \"front\";\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 resumeOf?: string;\n className?: string;\n reroutes: number;\n emptyRetried?: boolean;\n route?: RouteInfo;\n fallThroughOf?: string;\n printCmd?: boolean;\n /** \"provider/model (why)\" — one line on how the provider was chosen. */\n routeWhy?: string;\n reportFormat: \"json\" | \"ag2\";\n noReportRetry?: boolean;\n /** --orchestrate: PAI_WORKER_ORCHESTRATOR=1 in the env and the orchestrator addendum in the contract. */\n orchestrate?: 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 /** The options this run was launched with; a relaunch starts from these (see relaunchOptions). */\n opts: RunOptions;\n /** Set once the follow pane was handled (tryPoolRun); executeRun then neither opens nor verifies a second one. */\n panePrepared?: { verified: boolean };\n}\n\n/**\n * The worker session pool attempt (src/workers/pool.ts), behind\n * workers.pool.enabled: an eligible headless task is shipped to the pool\n * host and runs as the next user turn of a warm claude slot, so the\n * provider prompt cache carries over. Returns the exit code when the task\n * ran pooled; null sends the caller down the normal fresh-spawn path (host\n * unreachable, or the task needs a setup a slot cannot provide — worktree on a non-loopback provider,\n * own system prompt, MCP, another cwd, native Anthropic).\n */\nexport async function tryPoolRun(a: ExecuteArgs): Promise<number | null> {\n const settings = poolSettings(a.config);\n if (!settings.enabled || a.orchestrate) return null; // an orchestrator needs its own env and contract, not a warm slot\n const cwd = a.cwd ?? process.cwd();\n const reason = poolIneligibleReason({\n headless: a.parsed.headless,\n prompt: a.parsed.prompt,\n providerName: a.target.providerName,\n provider: a.target.provider,\n nativeAllowed: settings.native,\n worktreeWanted:\n a.parsed.headless && !a.printCmd && worktreeWanted(a.worktreeFlag, { cwd, allowedTools: a.parsed.allowedTools }),\n slotWorktree: isLoopbackProvider(a.target.provider),\n resumeOf: a.resumeOf,\n printCmd: a.printCmd,\n parsed: a.parsed,\n mcpFlag: a.mcpFlag,\n noBrowser: a.noBrowser,\n outputFormat: a.parsed.outputFormat,\n cwd,\n });\n if (reason) {\n appendLedger(ledgerPath(a.logDir), \"WORKER-POOL-SKIP\", {\n id: a.id ?? \"-\",\n provider: a.target.providerName,\n reason,\n });\n if (!a.quiet) process.stderr.write(`[pai worker] pool: not pooled (${reason}) — spawning fresh\\n`);\n return null;\n }\n const wid = a.id ?? newWorkerId();\n // same follow pane as a fresh spawn, once per run: a decline falls back to executeRun, which reuses this\n if (!a.panePrepared) {\n const term = (await resolveHostSession()).term;\n const ledger = ledgerPath(a.logDir);\n const verified = await requireVisiblePane(a.logDir, a.config, wid, term, a.noPane, false, ledger);\n a.panePrepared = { verified };\n const status = loadStatus(a.logDir, wid);\n if (status) {\n if (verified) status.pane = { opened: true, placement: panePlacement(wid) };\n else await announcePane(a.logDir, a.config, wid, term, a.noPane, ledger, status);\n saveStatus(a.logDir, status);\n } else if (!verified) {\n await announcePane(a.logDir, a.config, wid, term, a.noPane, ledger);\n }\n }\n const limitMinutes = a.limit && a.limit.minutes > 0 ? a.limit.minutes : null;\n // the socket bounds connect+send only; the answer takes as long as the task\n const timeoutMs = limitMinutes ? limitMinutes * 2 * 60_000 + 120_000 : 7_200_000;\n let resp: PoolTaskResponse | null;\n try {\n resp = await postTaskToPool(\n settings.port,\n {\n id: wid,\n label: a.label,\n prompt: a.parsed.prompt ?? \"\",\n provider: a.target.providerName,\n model: a.model,\n cwd,\n className: a.className,\n capability: a.capability,\n specPath: a.specPath,\n parent: a.parent,\n stage: a.stage,\n clientPid: process.pid,\n reportFormat: a.reportFormat,\n outputFormat: a.parsed.outputFormat === \"json\" ? \"json\" : \"text\",\n maxMinutes: limitMinutes,\n trailer: promptTrailer(a.reportFormat),\n worktree: a.parsed.headless && !a.printCmd && worktreeWanted(a.worktreeFlag, { cwd, allowedTools: a.parsed.allowedTools }),\n },\n timeoutMs\n );\n } catch {\n resp = null;\n }\n if (resp === null) {\n process.stderr.write(`[pai worker] pool: host unreachable on port ${settings.port} — spawning fresh\\n`);\n return null;\n }\n if (!resp.pooled) {\n appendLedger(ledgerPath(a.logDir), \"WORKER-POOL-SKIP\", {\n id: a.id ?? \"-\",\n provider: a.target.providerName,\n reason: resp.reason ?? \"refused\",\n });\n if (!a.quiet) process.stderr.write(`[pai worker] pool: not pooled (${resp.reason ?? \"refused\"}) — spawning fresh\\n`);\n return null;\n }\n a.onWorkerStart?.(wid);\n const rc = resp.rc ?? 1;\n if (!a.quiet) {\n printResult(a.parsed.outputFormat, resp.resultEvent ?? null, rc, a.logDir, wid, resp.report);\n }\n return rc;\n}\n\n/**\n * Capacity gate, then stagger gate, for one headless run — the single place\n * both the pooled and the fresh-spawn path pass through. `status` is the\n * run's saved status; its capacity/warmup/last fields carry the queue line.\n */\nasync function runGates(a: ExecuteArgs, wid: string, status: WorkerStatus): Promise<void> {\n const { logDir, target, config } = a;\n const saved = status.last;\n const gate: CapacityOpts = {\n logDir,\n self: { id: wid, provider: target.providerName },\n cap: resolveCap(target.provider.maxConcurrent, isLocalProvider(target.provider.baseUrl)),\n config: config.capacity,\n disabled: a.noQueue,\n priority: a.priority,\n log: (line) => {\n process.stderr.write(`${line}\\n`);\n },\n onWait: (w) => {\n if (w) {\n // parked at the cap: visible as \"queued\", never as a slot holder\n status.state = \"queued\";\n status.capacity = {\n provider: w.provider,\n cap: w.cap,\n total: w.total,\n position: w.position,\n elapsedSec: w.elapsedSec,\n queued: w.queued,\n ...(w.behind ? { behind: w.behind } : {}),\n sinceMs: w.sinceMs,\n };\n status.last = queuedText(w);\n } else {\n // slot granted: back to running, and `started` now counts from the grant\n // (spawnTimeOf documents the rewrite; wait-on's ceiling and ps ages use it)\n status.state = \"running\";\n status.started = nowStamp();\n delete status.capacity;\n status.last = saved;\n }\n saveStatus(logDir, status);\n },\n };\n // the session's own limit (\"one after the other\"): same gate, counted per session across providers\n const max = status.sessionKey ? getProfile(status.sessionKey)?.max_parallel : undefined;\n if (status.sessionKey && max) {\n const key = status.sessionKey;\n await capacityGate({ ...gate, scope: { key: sessionScopeKey(key), label: `session ${key}`, holds: (s) => s.sessionKey === key }, cap: max });\n }\n await capacityGate(gate);\n await staggerGate({\n logDir,\n self: { id: wid, provider: target.providerName },\n local: isLocalProvider(target.provider.baseUrl),\n config: config.stagger,\n disabled: a.noStagger,\n log: (line) => {\n if (!a.quiet) process.stderr.write(`${line}\\n`);\n },\n onWait: (w) => {\n if (w) {\n status.warmup = { for: w.predecessor, provider: w.provider, elapsedSec: w.elapsedSec, limitSec: w.limitSec };\n status.last = w.line;\n } else {\n delete status.warmup;\n status.last = saved;\n }\n saveStatus(logDir, status);\n },\n });\n}\n\n/**\n * Gate a headless run once, before the pool attempt. A placeholder status\n * (running, this process's pid) is saved first, as a fresh spawn always did,\n * so the run counts as holding a slot the moment it leaves the queue; the pool\n * host or executeRun overwrites it under the same id.\n */\nexport async function gateStart(a: ExecuteArgs): Promise<void> {\n if (!a.parsed.headless || a.printCmd || a.gated) return;\n const wid = a.id ?? newWorkerId();\n a.id = wid;\n const sessionKey = await detectSessionName();\n const status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n runPid: process.pid,\n label: a.label,\n cwd: a.cwd ?? process.cwd(),\n term: \"\",\n provider: a.target.providerName,\n model: a.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: a.parsed.outputFormat,\n ...(sessionKey ? { sessionKey } : {}),\n };\n saveStatus(a.logDir, status);\n // the follow pane opens now, not after the queue: a run parked at a cap shows\n // its place in line there (runGates onWait) and flips to the live view on the grant\n const term = (await resolveHostSession()).term;\n const ledger = ledgerPath(a.logDir);\n let verified: boolean;\n try {\n verified = await requireVisiblePane(a.logDir, a.config, wid, term, a.noPane, false, ledger);\n } catch (e) {\n // a refusal leaves nothing in `pai worker ps`, as before the pane moved up\n rmSync(statusPath(a.logDir, wid), { force: true });\n throw e;\n }\n a.panePrepared = { verified };\n if (verified) status.pane = { opened: true, placement: panePlacement(wid) };\n else await announcePane(a.logDir, a.config, wid, term, a.noPane, ledger, status);\n saveStatus(a.logDir, status);\n await runGates(a, wid, status);\n // a gate that never had to wait leaves the placeholder \"running\" with the\n // spawn stamp; one that queued already flipped back to \"running\" with the\n // grant stamp (runGates onWait)\n a.gated = true;\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 let viaRouter: boolean | undefined;\n if (!proxyUrl && !target.provider.native && isLoopbackProvider(target.provider) && target.provider.router !== false) {\n viaRouter = routeLoopbackViaRouter(target.provider, await probeRouter());\n if (!viaRouter) process.stderr.write(`pai: router down, ${target.providerName} runs direct (no router prefix fix)\\n`);\n }\n const env = buildRunEnv(target.provider, headless, proxyUrl, target.providerName, viaRouter, a.orchestrate);\n\n const wid = a.id ?? newWorkerId();\n // a resume takes over the old id's tree and runs against its repo\n const adopted = a.resumeOf ? adoptTree(logDir, a.resumeOf) : null;\n const cwd = adopted?.cwd ?? a.cwd ?? process.cwd();\n const term = (await resolveHostSession()).term;\n const paneVerified = a.panePrepared\n ? a.panePrepared.verified\n : headless && (await requireVisiblePane(logDir, config, wid, term, noPane, a.printCmd === true, ledgerPath(logDir)));\n const session = resolveSession(term);\n const sessionKey = await detectSessionName();\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 = adopted;\n if (\n !adopted &&\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 // project preamble (.pai/worker.md): headless only, oversize refuses the run\n let preamble: Preamble | null = null;\n if (headless) {\n const loaded = loadPreamble(cwd);\n if (\"refused\" in loaded) {\n process.stderr.write(`pai worker: preamble refused: ${loaded.refused}\\n`);\n appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `preamble refused: ${loaded.refused}` });\n return 1;\n }\n preamble = loaded.preamble;\n if (!a.quiet) process.stderr.write(preambleLine(preamble) + \"\\n\");\n }\n // provider contract addendum (<PAI_HOME>/contracts/<provider>.md): headless, only with the generic contract\n const addendum: ContractAddendum | null =\n headless && !parsed.callerSystemPrompt ? loadContractAddendum(target.providerName, target.provider.contract) : null;\n if (addendum && !a.quiet) process.stderr.write(`contract addendum: ${addendum.path} sha ${addendum.sha}\\n`);\n // sub-workers detect themselves (and their parent) through this variable\n env.PAI_WORKER_ID = wid;\n const noteToken = newNoteToken();\n env[NOTE_TOKEN_ENV] = noteToken;\n applySessionProviderEnv(env, target);\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 // headless without a grant or --tools of its own: load only the default core schemas\n let toolsFlag: string[] = headless ? headlessToolsFlag(parsed.allowedTools) : [];\n if (headless && !parsed.allowedTools.length && !parsed.callerTools) {\n toolsFlag = [\"--tools\", DEFAULT_WORKER_TOOLS];\n }\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 // A local server prefix-caches system prompt + tools, and a worktree's id,\n // path, branch and the deadline minute differ per unit: left in the system\n // prompt they cut the shared prefix before the tool definitions (measured\n // 2026-10-17: 3.1k of 38k tokens shared). They go at the head of the task\n // message instead. Anthropic and remote providers keep them in the system prompt.\n const volatileInTask = headless && !target.provider.native && isLoopbackProvider(target.provider);\n const volatileParts = volatileInTask\n ? [\n ...(worktree ? [worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot)] : []),\n ...(limit ? [deadlinePromptLine(limit)] : []),\n noteTokenSentence(noteToken), // per-run token: measured 2026-10-20, it cut the shared prefix after 2.3k of 9k tokens\n ]\n : [];\n const { text: taskText, applied: trailerApplied } = headlessPromptText(\n parsed.prompt,\n headless,\n parsed.callerSystemPrompt,\n a.reportFormat\n );\n const promptText = taskText !== null && volatileParts.length ? placeVolatile(taskText, volatileParts.join(\"\\n\")) : taskText;\n // Claude Code's own gitStatus block (branch = worker branch, recent commits) opens the first user\n // message and differs per worker; measured 2026-10-20 it was the first differing token after the\n // shared 5,445 (system+tools). The documented switch (env CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS =\n // setting includeGitInstructions:false) drops it, and the git section of the system prompt with it.\n if (volatileInTask) env.CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS = \"1\";\n const claudeCmd = claudeCommand(env, config.caveman);\n let cmd: string[] = [...claudeCmd];\n cmd.push(...modelFlagArgs(headless, model, Boolean(parsed.callerModel)));\n const slimArgs = headless ? slimProfileArgs(logDir, wid, parsed.rest, env, undefined, target.provider) : [];\n cmd.push(...chromeArgs, ...mcpArgs, ...toolArgs, ...toolsFlag, ...slimArgs, ...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, a.orchestrate, volatileInTask ? undefined : noteToken)] : []),\n ...(addendum ? [addendum.text] : []),\n ...(slimArgs.length ? [WORKER_BRIEF] : []),\n ...(preamble ? [preambleBlock(preamble)] : []),\n ...(headless\n ? [worktree ? (volatileInTask ? \"\" : worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot)) : inPlaceSystemPrompt()]\n : []),\n ...(headless && limit && !volatileInTask ? [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 ...(promptText !== null ? { stdin: promptText } : {}),\n browser: noBrowser ? \"off\" : \"on\",\n ...(a.routeWhy ? { route: a.routeWhy } : {}),\n sessionProviderEnv: env.PAI_SESSION_PROVIDER ?? null,\n ...(env.CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS ? { gitStatus: \"off\" } : {}),\n ...(limit ? { deadline: new Date(limit.deadlineAt).toISOString(), maxMinutes: limit.minutes } : { limit: \"none\" }),\n })\n );\n return 0;\n }\n\n // a gate that already ran (gateStart) rewrote `started` at the slot grant;\n // keep that stamp so the deadline and ps age count from the grant\n const gated = a.gated ? loadStatus(logDir, wid) : null;\n const started = gated && Number.isFinite(Date.parse(gated.started.replace(\" \", \"T\")))\n ? new Date(Date.parse(gated.started.replace(\" \", \"T\")))\n : new Date();\n let status: WorkerStatus = {\n id: wid,\n pid: process.pid,\n runPid: process.pid,\n ...parentInfo(),\n label,\n cwd,\n term,\n provider: target.providerName,\n model,\n state: \"running\",\n started: nowStamp(started),\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 ...(a.route ? { route: a.route } : {}),\n ...(a.routeWhy ? { routeWhy: a.routeWhy } : {}),\n ...(target.via === \"auto\" && target.switchReason ? { switchReason: target.switchReason } : {}),\n ...(a.fallThroughOf ? { fallThroughOf: a.fallThroughOf } : {}),\n ...(headless ? { reportFormat: a.reportFormat } : {}),\n ...(trailerApplied ? { promptTrailer: true } : {}),\n ...(preamble ? { preamble: { path: preamble.path, hash: preamble.hash } } : {}),\n ...(addendum ? { contractAddendum: { path: addendum.path, sha: addendum.sha } } : {}),\n ...(session ? { session } : {}),\n ...(sessionKey ? { sessionKey } : {}),\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 ...(adopted ? { resumedFrom: a.resumeOf } : {}),\n ...(a.specPath ? { spec: a.specPath } : {}),\n ...(limit ? { deadlineAt: limit.deadlineAt, maxMinutes: limit.minutes } : {}),\n };\n saveStatus(logDir, status);\n if (adopted && a.resumeOf) markResumed(logDir, a.resumeOf, wid);\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 ...(target.switchReason ? { switch: target.switchReason } : {}),\n });\n\n // Follow pane: headless only (interactive runs ARE the chat pane). Verified\n // above when the gate applies; best effort otherwise.\n if (paneVerified) status.pane = { opened: true, placement: panePlacement(wid) };\n else if (headless && !a.panePrepared) void announcePane(logDir, config, wid, term, noPane, ledger, status);\n\n let proc: ReturnType<typeof spawn>;\n try {\n if (headless && !a.gated) await runGates(a, wid, status);\n proc = spawn(cmd[0], cmd.slice(1), {\n env,\n cwd: worktree?.dir ?? cwd,\n stdio: headless ? [\"pipe\", \"pipe\", \"pipe\"] : \"inherit\",\n });\n } catch (e) {\n markStartFailed(logDir, status, e);\n throw e;\n }\n const t0 = Date.now();\n // claude's stderr: passed through, and the tail kept for a start that dies before any model event\n let stderrTail = \"\";\n proc.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(d);\n stderrTail = (stderrTail + d.toString()).slice(-2000);\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 resultGrace.cancel();\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 // A failed Edit fires no hook, so the closest-region hint rides in as a user turn (harness line, not an operator message to answer).\n const injectHarnessHint = (text: string): void => {\n const stdin = proc.stdin;\n if (!headless || !stdin || !stdin.writable || stdin.writableEnded) return;\n try {\n stdin.write(stdinUserMessage(`[harness] ${text}`) + \"\\n\");\n } catch {\n // the hint is a courtesy\n }\n };\n\n let killed = false;\n const resultGrace = armResultGrace(headless ? proc.pid : undefined);\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 // the build lock and tree of a killed worker go with it\n void releaseBuildsOf(buildsDir(), wid).catch(() => []).finally(() => process.exit(143));\n setTimeout(() => process.exit(143), 2000).unref();\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: {\n resultEvent: StreamEvent | null;\n resultReport: WorkerReport | null;\n } = {\n resultEvent: null,\n resultReport: null,\n };\n // every Bash call + outcome, and the untracked files present at spawn: the\n // end-of-run proof check (checkProof) and stray-file listing read these\n const bashRuns: BashRun[] = [];\n let trouble: TroubleWatcher | undefined;\n let troubleTimer: ReturnType<typeof setInterval> | undefined;\n const untrackedAtStart = new Set(headless ? untrackedPaths(worktree?.dir ?? cwd) : []);\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 const origin = reportOrigin();\n if (origin) {\n trouble = new TroubleWatcher(wid, label, runMessenger(logDir), origin);\n troubleTimer = setInterval(() => trouble?.tick(), 60_000);\n troubleTimer.unref();\n }\n eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n writeEvent({ type: \"preamble\", text: preambleLine(preamble), ...(preamble ? { hash: preamble.hash } : {}) });\n if (addendum) writeEvent({ type: \"contract-addendum\", text: `${addendum.path} sha ${addendum.sha}`, sha: addendum.sha });\n const rl = createInterface({ input: proc.stdout! });\n const toolUses = new Map<string, { name: string; input: unknown }>();\n rl.on(\"line\", (line) => {\n trouble?.activity();\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 =\n initContextWindow(e) ?? target.provider.contextWindow ?? certainContextWindow(status.model);\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 if (status.turns === 1) recordWarmup(logDir, status.provider, (Date.now() - t0) / 1000);\n bumpContextTokens(status, usageContextTokens(e.message?.usage));\n status.contextUsed = usageContextUsed(e.message?.usage) ?? status.contextUsed;\n for (const block of e.message?.content ?? []) {\n if (block.type === \"tool_use\") {\n status.tools += 1;\n if (block.id) toolUses.set(block.id, { name: block.name ?? \"?\", input: block.input });\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 if (recordDenial(status, block, toolResultText(block.content), toolUses, parsed.prompt, new Date().toISOString())) {\n const d = parseDenialText(toolResultText(block.content), \"?\");\n if (d) trouble?.onDenial(d.hook, d.reason);\n }\n if (block.is_error === true) {\n recordToolError(logDir, {\n worker: wid,\n provider: target.providerName,\n label,\n tool: toolUses.get(block.tool_use_id ?? \"\")?.name ?? \"?\",\n text: toolResultText(block.content),\n input: toolUses.get(block.tool_use_id ?? \"\")?.input,\n granted: headless ? grantedToolNames(toolsFlag.slice(1), parsed.allowedTools) : undefined,\n });\n }\n const use = block.tool_use_id ? toolUses.get(block.tool_use_id) : undefined;\n if (use?.name === \"Edit\" && block.is_error === true) {\n const hint = editHintFor(use.input, toolResultText(block.content), worktree?.dir ?? cwd);\n if (hint) injectHarnessHint(hint);\n }\n if (use?.name === \"Bash\") bashRuns.push({ command: bashCommand(use.input), failed: toolResultFailed(block), output: toolResultText(block.content).slice(-400) });\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 resultGrace.onResult();\n }\n });\n }\n\n let rc = await new Promise<number>((resolve, reject) => {\n proc.on(\"error\", (e) => {\n markStartFailed(logDir, status, e);\n reject(e);\n });\n proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n });\n if (troubleTimer) clearInterval(troubleTimer);\n if (trouble) {\n const stop = peekLoopStop(logDir, wid);\n if (stop) trouble.onLoopStop(stop.reason);\n await trouble.flush();\n }\n resultGrace.cancel();\n await resultGrace.settled();\n // the result already received is the outcome; the signal that ended the leftovers is not\n if (resultGrace.fired && ctx.resultEvent) rc = ctx.resultEvent.is_error ? 1 : 0;\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 empty = headless && rc === 0 && resultEvent !== null && isEmptyResponse(resultEvent.result, status.tools);\n const ok = !empty && runSucceeded(headless, rc, resultEvent);\n if (resultEvent) status.last = shortText(resultEvent.result ?? \"\", 90);\n else if (headless) status.last = preEventFailure(rc, status.turns, stderrTail) ?? status.last;\n if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n if (empty) {\n status.last = \"harness: empty-response (model returned nothing)\";\n recordToolError(logDir, {\n worker: wid,\n provider: target.providerName,\n label,\n tool: \"model\",\n text: \"empty-response: model answered with zero usage and no tool call\",\n });\n }\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 slimArgs,\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 && a.reportFormat === \"ag2\" && resultEvent?.result) {\n const stray = newUntracked(worktree?.dir ?? cwd, untrackedAtStart);\n const chk = checkProof(resultEvent.result, bashRuns, stray);\n if (chk.rewritten || stray.length) {\n resultEvent.result = chk.text;\n ctx.resultReport = parseWorkerReport(chk.text);\n if (ctx.resultReport) ctx.resultReport.untracked = stray.length ? stray : undefined;\n }\n if (chk.rewritten) {\n status.reportValid = false;\n status.last = shortText(`claimed proof not run or failed: ${chk.proof}`, 90);\n appendLedger(ledger, \"REPORT-PROOF-UNVERIFIED\", { id: wid, proof: chk.proof });\n }\n if (stray.length) process.stderr.write(`pai worker: warning: ${stray.length} untracked file(s) left by the worker: ${stray.join(\", \")}\\n`);\n }\n if (ctx.resultReport && status.denials?.length) ctx.resultReport.denials = status.denials;\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 if (resultGrace.fired && resultEvent) {\n status.last = leftoverNote(resultGrace.leftover);\n appendLedger(ledger, \"WORKER-LEFTOVER-ENDED\", { id: wid, processes: resultGrace.leftover });\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 // Fall-through: a local run that failed its own report is repeated once on the cloud.\n const why = headless ? fallThroughReason(a.route, st, rep) : null;\n if (why && a.route) {\n appendLedger(ledger, \"WORKER-FALLTHROUGH\", { id: wid, reason: why });\n process.stderr.write(`pai worker: ${wid} on ${target.providerName} ended badly (${why}) — re-running on the cloud\\n`);\n return runWorker({\n providerFlag: ANTHROPIC_NATIVE,\n label: `${label} (fall-through)`,\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 quiet: a.quiet,\n worktreeFlag: a.worktreeFlag,\n className: a.className,\n _route: { policy: a.route.policy, reason: `fall-through (${why})`, local: false },\n _fallThroughOf: wid,\n onWorkerStart: (id2) => {\n st.fallThroughTo = id2;\n saveStatus(logDir, st);\n a.onWorkerStart?.(id2);\n },\n });\n }\n if (headless && !a.quiet) {\n printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n }\n\n // Empty response: one automatic re-launch of the same spec in the same cwd.\n if (empty && !a.emptyRetried) {\n appendLedger(ledger, \"WORKER-EMPTY-RETRY\", { id: wid, provider: target.providerName });\n return runWorker(\n relaunchOptions(a.opts, {\n providerFlag: target.providerName,\n onWorkerStart: a.onWorkerStart,\n _emptyRetried: true,\n })\n );\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 < autoOrder(config).length) {\n appendLedger(ledger, \"WORKER-REROUTE\", {\n from: target.providerName,\n to: next,\n reason: \"quota\",\n });\n return runWorker(\n relaunchOptions(a.opts, {\n providerFlag: next,\n modelFlag: undefined,\n onWorkerStart: a.onWorkerStart,\n _reroutes: a.reroutes + 1,\n })\n );\n }\n }\n },\n });\n}\n\n/**\n * Options for a relaunch (empty-response retry, quota reroute): the original\n * run's options, minus the preset id (the relaunch is a new worker), with the\n * given changes applied. Nothing is copied field by field, so a new RunOptions\n * field is carried over by default.\n */\nexport function relaunchOptions(orig: RunOptions, changes: Partial<RunOptions>): RunOptions {\n return { ...orig, id: undefined, ...changes };\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 /** Slim-profile args of the original launch, so the resumed turn loads the same setup. */\n slimArgs?: 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, ...(a.slimArgs ?? []));\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\" | \"opts\"> {}\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 = (await resolveHostSession()).term;\n const paneVerified = await requireVisiblePane(logDir, config, wid, term, noPane, false, ledgerPath(logDir));\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 runPid: 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 if (paneVerified) status.pane = { opened: true, placement: panePlacement(wid) };\n else void announcePane(logDir, config, wid, term, noPane, ledger, status);\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, providerName);\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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"}
|