{"version":3,"file":"run-TChOnMO_.mjs","names":[],"sources":["../src/workers/mcp.ts","../src/workers/args.ts","../src/workers/status.ts","../src/workers/ledger.ts","../src/workers/worktree.ts","../src/workers/scope.ts","../src/workers/engines/image.ts","../src/workers/pane.ts","../src/workers/tree.ts","../src/workers/operator.ts","../src/workers/handoff.ts","../src/workers/agentish.ts","../src/workers/report.ts","../src/workers/project-config.ts","../src/workers/codex.ts","../src/workers/run.ts"],"sourcesContent":["/**\n * mcp.ts — the MCP allowlist for headless workers.\n *\n * Headless workers start with NO MCP servers: every server definition is a\n * prompt-time tool inventory the model pays to know, and a worker that only\n * reads and edits files needs none of it. A run may opt in with `--mcp\n * name[,name…]`, a role carrying `\"mcp\": [...]`, or implicitly by naming\n * `mcp__server__tool` in --allowedTools (a grant without its server loaded is\n * a dead letter). Names may be single servers from ~/.claude.json's\n * `mcpServers` or `workers.mcpSets` set names, which expand to their member\n * list. The filtered config lands in\n * `<logDir>/<id>.mcp.json` and is passed with `--strict-mcp-config\n * --mcp-config` so exactly those servers load. MCP servers are chosen at\n * launch only — a mid-run `say` cannot add any.\n *\n * One name in that space is not a server at all: `claude-in-chrome` is the\n * Chrome native-host bridge, which no config file can load and which the\n * `--chrome` flag switches on instead. It is recognised here so a grant\n * naming it becomes that flag rather than an \"unknown MCP server\" rejection.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { WorkersConfigError, type WorkersConfig } from \"./config.js\";\n\n/** ~/.claude.json — the user's MCP server definitions (top-level mcpServers). */\nexport const CLAUDE_JSON = join(homedir(), \".claude.json\");\n\n/**\n * The browser bridge is not an MCP server. It rides the Chrome native-host\n * channel, so it never appears in `mcpServers`; a spawned claude has it off\n * and the `--chrome` flag switches it on. Tool grants naming it must\n * therefore neither resolve to a server nor be rejected as unknown — they\n * select a flag. See `grantsChrome`.\n */\nexport const CHROME_SERVER = \"claude-in-chrome\";\n\n/**\n * True for a real server definition, false for anything else that happens to\n * sit under `mcpServers`.\n *\n * The vendor config is not ours and has been observed carrying non-server\n * entries under that key — tool-usage records keyed by tool name\n * (`Read`, `Bash`, `mcp__server__tool`, …). Those are not servers: handing\n * one to `--mcp-config` yields \"invalid MCP server config\", and listing one\n * as available invites a run that cannot work. A server has a `command` (or a\n * `url` for remote transports); an `mcp__…` key is a tool name whatever its\n * shape.\n */\nfunction isServerDefinition(name: string, value: unknown): boolean {\n  if (name.startsWith(\"mcp__\")) return false;\n  if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n  const v = value as Record<string, unknown>;\n  return (\n    (typeof v.command === \"string\" && v.command.length > 0) ||\n    (typeof v.url === \"string\" && v.url.length > 0)\n  );\n}\n\nexport function readMcpServers(claudeJson = CLAUDE_JSON): Record<string, unknown> {\n  try {\n    if (!existsSync(claudeJson)) return {};\n    const parsed = JSON.parse(readFileSync(claudeJson, \"utf8\")) as Record<string, unknown>;\n    const servers = parsed.mcpServers;\n    if (typeof servers !== \"object\" || servers === null || Array.isArray(servers)) return {};\n    const out: Record<string, unknown> = {};\n    for (const [name, value] of Object.entries(servers as Record<string, unknown>)) {\n      if (isServerDefinition(name, value)) out[name] = value;\n    }\n    return out;\n  } catch {\n    // a damaged ~/.claude.json must not take workers down with it\n    return {};\n  }\n}\n\n/**\n * True when any `--mcp` name or `--allowedTools` grant asks for the browser\n * bridge: the bare server name, `mcp__claude-in-chrome`, or any\n * `mcp__claude-in-chrome__<tool>`. The runner turns this into `--chrome` on\n * the claude argv.\n */\nexport function grantsChrome(entries: string[]): boolean {\n  for (const entry of entries) {\n    for (const name of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n      if (name === CHROME_SERVER) return true;\n      if (name === `mcp__${CHROME_SERVER}`) return true;\n      if (name.startsWith(`mcp__${CHROME_SERVER}__`)) return true;\n    }\n  }\n  return false;\n}\n\n/**\n * Split a `--mcp a,b,c` flag value (also accepts repeated flags already split\n * by the caller) and expand set names from workers.mcpSets.\n */\nexport function expandMcpNames(\n  names: string[],\n  config: Pick<WorkersConfig, \"mcpSets\">,\n  claudeJson = CLAUDE_JSON\n): string[] {\n  const available = readMcpServers(claudeJson);\n  const out: string[] = [];\n  for (const raw of names) {\n    for (const name of raw.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n      if (name === CHROME_SERVER) continue; // a flag, not a server — see grantsChrome\n      if (name in config.mcpSets) {\n        for (const member of config.mcpSets[name]) {\n          if (!out.includes(member)) out.push(member);\n        }\n        continue;\n      }\n      if (!(name in available)) {\n        const sets = Object.keys(config.mcpSets);\n        throw new WorkersConfigError(\n          `unknown MCP server \"${name}\". Available servers: ` +\n            `${Object.keys(available).join(\", \") || \"(none in ~/.claude.json)\"}` +\n            `${sets.length ? `; sets: ${sets.join(\", \")}` : \"\"}`\n        );\n      }\n      if (!out.includes(name)) out.push(name);\n    }\n  }\n  return out;\n}\n\n/**\n * Derive server names from tool grants: every `mcp__<server>__<tool>` (or bare\n * `mcp__<server>`, or `mcp__<server>__*`) in an --allowedTools list names a\n * server the run expects to be loaded. A grant is a dead letter unless its\n * server is in the filtered config, so the runner treats these as implicit\n * --mcp names — and only these; non-mcp grants load nothing.\n */\nexport function mcpServersFromToolGrants(tools: string[]): string[] {\n  const out: string[] = [];\n  for (const entry of tools) {\n    for (const name of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n      if (!name.startsWith(\"mcp__\")) continue;\n      const server = name.slice(\"mcp__\".length).split(\"__\")[0];\n      if (server === CHROME_SERVER) continue; // a flag, not a server — see grantsChrome\n      if (server && !out.includes(server)) out.push(server);\n    }\n  }\n  return out;\n}\n\n/** Path of a run's filtered MCP config. */\nexport function runMcpConfigPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.mcp.json`);\n}\n\n/**\n * Write a config containing only `names` (already expanded) and return its\n * path. Unknown names fail fast with the available list.\n */\nexport function writeMcpConfig(\n  logDir: string,\n  id: string,\n  names: string[],\n  claudeJson = CLAUDE_JSON\n): string {\n  const available = readMcpServers(claudeJson);\n  const unknown = names.filter((n) => !(n in available));\n  if (unknown.length) {\n    throw new WorkersConfigError(\n      `unknown MCP server(s): ${unknown.join(\", \")}. Available: ` +\n        `${Object.keys(available).join(\", \") || \"(none in ~/.claude.json)\"}`\n    );\n  }\n  const servers: Record<string, unknown> = {};\n  for (const n of names) servers[n] = available[n];\n  const path = runMcpConfigPath(logDir, id);\n  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n  writeFileSync(path, JSON.stringify({ mcpServers: servers }, null, 2) + \"\\n\", \"utf8\");\n  return path;\n}\n\n/** `pai worker mcp list`: the servers and sets a run could name. */\nexport function describeMcp(config: Pick<WorkersConfig, \"mcpSets\">, claudeJson = CLAUDE_JSON): string[] {\n  const servers = Object.keys(readMcpServers(claudeJson));\n  const lines: string[] = [];\n  if (servers.length) {\n    lines.push(`servers (~/.claude.json):`);\n    for (const s of servers) lines.push(`  ${s}`);\n  } else {\n    lines.push(`no MCP servers defined in ~/.claude.json`);\n  }\n  const sets = Object.entries(config.mcpSets);\n  if (sets.length) {\n    lines.push(`sets (workers.mcpSets):`);\n    for (const [name, members] of sets) lines.push(`  ${name} = ${members.join(\", \")}`);\n  }\n  lines.push(`usage: pai worker run --mcp <name>[,<name>…] -p '<task>'`);\n  return lines;\n}\n","/**\n * args.ts — parse the claude-args tail that `pai worker run` receives.\n *\n * The runner needs a few things out of the caller's argument vector: the\n * prompt (for the default label), the requested --output-format (so the final\n * print matches what plain `claude -p` would have produced), whether the\n * caller already chose a --model or an --mcp-config (both suppress the\n * defaults the runner would otherwise force), any --mcp allowlist names, any\n * --allowedTools grants (their mcp__server__… entries decide which servers\n * load), and whether they appended their own system prompt (the worker\n * contract is then added alongside, not instead). Everything is passed\n * through untouched — the runner never rewrites the caller's task.\n */\n\nexport interface ParsedRunnerArgs {\n  /** Prompt string after -p/--print, when the run is headless. */\n  prompt: string | null;\n  /** Caller's --output-format: text (default), json, or stream-json. */\n  outputFormat: \"text\" | \"json\" | \"stream-json\";\n  /** Args to hand to claude (minus --output-format/--verbose, which we add). */\n  rest: string[];\n  /** Headless: a -p/--print flag is present. */\n  headless: boolean;\n  /** Caller passed --model (or --model=…): do not force the provider model. */\n  callerModel: boolean;\n  /** Caller passed --mcp-config (or --mcp-config=…): keep their MCP setup. */\n  callerMcpConfig: boolean;\n  /** Caller passed --append-system-prompt: the contract is added alongside. */\n  callerSystemPrompt: boolean;\n  /** Caller passed --tools (or --tools=…): a project's `pai project tools` pin does not apply. */\n  callerTools: boolean;\n  /** --mcp values (repeatable, comma-separated inside one flag). */\n  mcp: string[];\n  /** --allowedTools values (repeatable, comma-separated inside one flag). */\n  allowedTools: string[];\n}\n\nexport function parseRunnerArgs(argv: string[]): ParsedRunnerArgs {\n  let prompt: string | null = null;\n  let outputFormat: ParsedRunnerArgs[\"outputFormat\"] = \"text\";\n  const rest: string[] = [];\n  let headless = false;\n  let callerModel = false;\n  let callerMcpConfig = false;\n  let callerSystemPrompt = false;\n  let callerTools = false;\n  const mcp: string[] = [];\n  const allowedTools: string[] = [];\n\n  let i = 0;\n  while (i < argv.length) {\n    const a = argv[i];\n    if (a === \"-p\" || a === \"--print\") {\n      headless = true;\n      rest.push(a);\n      if (i + 1 < argv.length && !argv[i + 1].startsWith(\"-\")) {\n        prompt = argv[i + 1];\n        rest.push(prompt);\n        i += 1;\n      }\n    } else if (a === \"--output-format\") {\n      const v = argv[i + 1];\n      if (v === \"json\" || v === \"stream-json\") outputFormat = v;\n      i += 1; // dropped; the runner prints the result in this format itself\n    } else if (a.startsWith(\"--output-format=\")) {\n      const v = a.slice(\"--output-format=\".length);\n      if (v === \"json\" || v === \"stream-json\") outputFormat = v;\n    } else if (a === \"--verbose\") {\n      // dropped; re-added by the runner\n    } else if (a === \"--mcp\") {\n      const v = argv[i + 1];\n      if (v !== undefined && !v.startsWith(\"-\")) {\n        mcp.push(v);\n        i += 1;\n      }\n    } else if (a.startsWith(\"--mcp=\")) {\n      mcp.push(a.slice(\"--mcp=\".length));\n    } else if (a === \"--allowedTools\") {\n      // captured for the MCP derivation, but still claude's flag: it passes through\n      rest.push(a);\n      const v = argv[i + 1];\n      if (v !== undefined && !v.startsWith(\"-\")) {\n        allowedTools.push(v);\n        rest.push(v);\n        i += 1;\n      }\n    } else if (a.startsWith(\"--allowedTools=\")) {\n      allowedTools.push(a.slice(\"--allowedTools=\".length));\n      rest.push(a);\n    } else {\n      if (a === \"--model\") callerModel = true;\n      if (a.startsWith(\"--model=\")) callerModel = true;\n      if (a === \"--mcp-config\") callerMcpConfig = true;\n      if (a.startsWith(\"--mcp-config=\")) callerMcpConfig = true;\n      if (a === \"--append-system-prompt\") callerSystemPrompt = true;\n      if (a.startsWith(\"--append-system-prompt=\")) callerSystemPrompt = true;\n      if (a === \"--tools\") callerTools = true;\n      if (a.startsWith(\"--tools=\")) callerTools = true;\n      if (\n        prompt === null && !a.startsWith(\"-\") && rest.length > 0 &&\n        (rest[rest.length - 1] === \"-p\" || rest[rest.length - 1] === \"--print\")\n      ) {\n        prompt = a;\n      }\n      rest.push(a);\n    }\n    i += 1;\n  }\n\n  return {\n    prompt,\n    outputFormat,\n    rest,\n    headless,\n    callerModel,\n    callerMcpConfig,\n    callerSystemPrompt,\n    callerTools,\n    mcp,\n    allowedTools,\n  };\n}\n\n/**\n * Turn `-p \"<prompt>\"` into bare `-p` (same for --print): for stream-json\n * stdin runs the prompt moves to the first user message on stdin, so the\n * value must not stay on the command line. Only prompt values directly\n * following the flag are touched; everything else passes through.\n */\nexport function stripPromptValues(argv: string[]): string[] {\n  const out: string[] = [];\n  for (let i = 0; i < argv.length; i++) {\n    const a = argv[i];\n    const isPrint = a === \"-p\" || a === \"--print\";\n    out.push(a);\n    if (isPrint && i + 1 < argv.length && !argv[i + 1].startsWith(\"-\")) {\n      i += 1; // drop the prompt value; the flag itself stays\n    }\n  }\n  return out;\n}\n\n/**\n * A one-line stderr hint (never blocking) for an inline -p prompt long or\n * multi-line enough to be a shell-quoting risk — write it to a file and pass\n * --spec instead. null when the prompt is short/simple enough to be fine.\n */\nexport function longInlinePromptHint(prompt: string | null): string | null {\n  if (!prompt) return null;\n  const newlines = (prompt.match(/\\n/g) ?? []).length;\n  if (prompt.length > 600 || newlines > 3) {\n    return \"hint: long inline prompts break on shell quoting — write the spec to a file and use --spec <file>\";\n  }\n  return null;\n}\n\n/** Collapse whitespace and cut to n chars with an ellipsis (label rendering). */\nexport function shortText(s: unknown, n: number): string {\n  const t = String(s ?? \"\").split(/\\s+/).filter(Boolean).join(\" \");\n  return t.length <= n ? t : t.slice(0, n - 1) + \"…\";\n}\n","/**\n * status.ts — the live per-worker status file.\n *\n * One JSON file per run, rewritten atomically on every turn, read by `ps`,\n * `follow`, the status line and the pane logic. Same field set the Python\n * runner wrote, plus `provider` (routing is multi-provider now) and `session`\n * (the AIBroker identity of the launching session, see scope.ts).\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport {\n  existsSync,\n  mkdirSync,\n  readFileSync,\n  readdirSync,\n  renameSync,\n  rmSync,\n  writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { statusPath } from \"./paths.js\";\n\nexport interface WorkerSessionRef {\n  /** AIBroker/iTerm session id (the iTerm UUID). */\n  id: string;\n  /** AIBroker name of that session, when it has one. */\n  name: string;\n}\n\nexport interface WorkerStatus {\n  id: string;\n  pid: number;\n  label: string;\n  cwd: string;\n  /** iTerm session id of the launching terminal (\"\" outside iTerm). */\n  term: string;\n  provider: string;\n  model: string;\n  state: \"running\" | \"done\" | \"failed\" | \"killed\" | \"lost\";\n  started: string;\n  updated: string;\n  turns: number;\n  tools: number;\n  last: string;\n  rc: number | null;\n  secs: number | null;\n  /** Launching session resolved through AIBroker, when it was. */\n  session?: WorkerSessionRef | null;\n  /** Claude Code session id (system/init) — what `resume` continues. */\n  claudeSession?: string | null;\n  /**\n   * Claude Code session id of the orchestrator whose Bash launched this run,\n   * when the status line's session map knew it (see scope.ts). Distinct from\n   * claudeSession, which is the run's own session.\n   */\n  spawnerSession?: string | null;\n  /** Context meter: tokens of the last assistant turn (input+cache+output). */\n  contextTokens?: number | null;\n  /** Context meter: window size (init model info or provider default). */\n  contextWindow?: number | null;\n  /** Path the prompt was read from via --spec (\"-\" for stdin), when it was. */\n  spec?: string | null;\n  /**\n   * Caller's --output-format, when the run was headless. A json/stream-json\n   * caller reads its own result and is notified on process exit by the\n   * harness — supervision.ts uses this to skip the duplicate relay send.\n   */\n  outputFormat?: \"text\" | \"json\" | \"stream-json\";\n  /** Chain this stage belongs to (the chain id), when it is a chain stage. */\n  parent?: string;\n  /** Class name of the chain stage (\"draft\", \"implement\", …). */\n  stage?: string;\n  /** Worktree this run executed in, when it got one (see worktree.ts). */\n  worktreeDir?: string | null;\n  /** Branch the worker committed on (worker/<id>), set on worktree runs. */\n  branch?: string | null;\n  /** Commit the branch started from (worktree base) for the commits count. */\n  worktreeBase?: string | null;\n  /** Commits the worker made on its branch; set with `branch` on success. */\n  commits?: number | null;\n  /** Set by `pai worker merge` once the branch landed in the original checkout. */\n  merged?: boolean;\n  /**\n   * How the run came to be: \"spawn\" (a `pai worker run` subagent) or \"chat\"\n   * (the terminal's interactive pane itself, tracked like a worker). Absent\n   * on statuses written before the flag existed — read as a spawn.\n   */\n  origin?: \"spawn\" | \"chat\";\n  /** Which contract/parser this run used for its final report (see report.ts). */\n  reportFormat?: \"json\" | \"ag2\";\n  /** Whether the final AG2 report passed `aibroker agentish check`; unset for json reports or when no validator ran. */\n  reportValid?: boolean;\n  /** Validation error messages, when reportValid is false. */\n  reportErrors?: string[];\n  /** Whether the headless prompt got the end-of-turn \"final message must be…\" trailer line. */\n  promptTrailer?: boolean;\n  /** Whether an invalid AG2 final message triggered the one bounded re-ask (see run.ts). */\n  reportRetried?: boolean;\n}\n\n/** Label a worker gets when launched with neither --label nor a prompt. */\nexport const UNLABELED = \"unlabeled\";\n\n/**\n * Whether a status is the terminal's interactive chat pane, not a spawned\n * task worker. `origin` is the real discriminator; the fallback catches\n * running entries from before the flag existed (unlabeled, no turns yet).\n * Removable once every pane runs code that writes `origin`.\n */\nexport function isChatPane(s: Pick<WorkerStatus, \"origin\" | \"label\" | \"turns\">): boolean {\n  return (\n    s.origin === \"chat\" ||\n    (!s.origin && (s.label === UNLABELED || s.label === \"(no prompt)\") && s.turns === 0)\n  );\n}\n\n/** Context-meter percentage 0–100, null when the numbers are missing. */\nexport function contextPercent(s: Pick<WorkerStatus, \"contextTokens\" | \"contextWindow\">): number | null {\n  if (!s.contextTokens || !s.contextWindow) return null;\n  return Math.round((s.contextTokens / s.contextWindow) * 100);\n}\n\n/** Compact token count: 84k, 200k, 900. */\nexport function fmtContextK(n: number): string {\n  return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);\n}\n\n/**\n * The one context-meter label, shared by every renderer so identical\n * numbers always render identically: `ctx 84k/200k (42%)` — used-style\n * percent. Empty when the numbers are missing (callers drop the part).\n */\nexport function contextLabel(\n  s: Pick<WorkerStatus, \"contextTokens\" | \"contextWindow\">\n): string {\n  const pct = contextPercent(s);\n  if (pct === null) return \"\";\n  return `ctx ${fmtContextK(s.contextTokens ?? 0)}/${fmtContextK(s.contextWindow ?? 0)} (${pct}%)`;\n}\n\n/** Timestamp format shared by status files and the ledger. */\nexport function nowStamp(d: Date = new Date()): string {\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  return (\n    `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` +\n    `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`\n  );\n}\n\n// second-resolution ids repeat when two workers/chains start together in one\n// process; a repeat gets a monotonic suffix so files never collide\nlet lastId = \"\";\nlet idSeq = 0;\n\nexport function newWorkerId(d: Date = new Date(), pid = process.pid): string {\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  const base = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}-${pid}`;\n  if (base === lastId) {\n    idSeq += 1;\n    return `${base}-${String(idSeq).padStart(2, \"0\")}`;\n  }\n  lastId = base;\n  idSeq = 0;\n  return base;\n}\n\n// Temp names must be unique per writer, not just per worker: two processes\n// legitimately write the same id at the same moment (`pai worker kill` marking\n// a run killed while the run's own SIGTERM handler writes its terminal state).\n// With one shared `<id>.status.tmp` they clobber each other's temp file and the\n// loser's rename fails with ENOENT — observed live on 2026-09-19.\nlet tmpSeq = 0;\n\n/** A temp path only this call owns — never the same string twice. */\nexport function statusTmpPath(logDir: string, id: string): string {\n  return `${statusPath(logDir, id)}.${process.pid}.${tmpSeq++}.tmp`;\n}\n\n// The label this process first wrote for a given worker id: run.ts holds one\n// unchanging label in memory for the whole run and passes it to every\n// periodic saveStatus call, so a later call whose label still equals this\n// baseline is that unmodified write, never an intentional change — on-disk\n// then wins, so an external `pai worker goal` relabel between two of a\n// worker's own writes survives instead of being reverted by the next one. A\n// call whose label differs from the baseline (or the first call for an id in\n// this process, which has no baseline yet) IS the intentional change and\n// always wins — exactly the one-shot `pai worker goal` process itself.\nconst firstWrittenLabel = new Map<string, string>();\n\n/** Write status atomically (temp + rename) and stamp `updated`. */\nexport function saveStatus(logDir: string, status: WorkerStatus, d: Date = new Date()): void {\n  status.updated = nowStamp(d);\n  const path = statusPath(logDir, status.id);\n  const baseline = firstWrittenLabel.get(status.id);\n  if (baseline === undefined) {\n    firstWrittenLabel.set(status.id, status.label);\n  } else if (status.label === baseline) {\n    const onDisk = loadStatus(logDir, status.id);\n    if (onDisk && onDisk.label !== status.label) status.label = onDisk.label;\n  }\n  const tmp = statusTmpPath(logDir, status.id);\n  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n  try {\n    writeFileSync(tmp, JSON.stringify(status), \"utf8\");\n    renameSync(tmp, path);\n  } catch (e) {\n    try {\n      rmSync(tmp, { force: true });\n    } catch {\n      // nothing to clean up\n    }\n    throw e;\n  }\n}\n\n/**\n * Set a worker's goal (the operator's `--label`), atomically, through the\n * same saveStatus path everything else writes with. Used by `pai worker\n * goal` and the `--goal` option of `say`: a fresh call in a fresh process, so\n * it always carries no baseline yet and always wins over the worker's own\n * next periodic write (see saveStatus above).\n */\nexport function setWorkerLabel(logDir: string, id: string, label: string): WorkerStatus {\n  const status = loadStatus(logDir, id);\n  if (!status) throw new Error(`no worker named \"${id}\"`);\n  status.label = label;\n  saveStatus(logDir, status);\n  return status;\n}\n\n/** Load every status file in the logDir, oldest id first, skipping damage. */\nexport function loadStatuses(logDir: string): WorkerStatus[] {\n  if (!existsSync(logDir)) return [];\n  const out: WorkerStatus[] = [];\n  for (const name of readdirSync(logDir).sort()) {\n    if (!name.endsWith(\".status\")) continue;\n    try {\n      out.push(JSON.parse(readFileSync(join(logDir, name), \"utf8\")) as WorkerStatus);\n    } catch {\n      // a half-written or damaged status file is not worth a crash\n    }\n  }\n  return out;\n}\n\nexport function loadStatus(logDir: string, id: string): WorkerStatus | null {\n  const path = statusPath(logDir, id);\n  if (!existsSync(path)) return null;\n  try {\n    return JSON.parse(readFileSync(path, \"utf8\")) as WorkerStatus;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Wait for a worker to record its own terminal state (the run's signal\n * handler writes killed/rc/secs). Returns that status, or null when it is\n * still \"running\" after `timeoutMs` — the caller then writes the state itself.\n */\nexport async function waitForTerminalStatus(\n  logDir: string,\n  id: string,\n  timeoutMs = 2000,\n  stepMs = 50\n): Promise<WorkerStatus | null> {\n  const deadline = Date.now() + timeoutMs;\n  for (;;) {\n    const s = loadStatus(logDir, id);\n    if (s && s.state !== \"running\") return s;\n    if (Date.now() >= deadline) return null;\n    await new Promise((r) => setTimeout(r, stepMs));\n  }\n}\n\nexport function alive(pid: number | null | undefined): boolean {\n  if (!pid || pid <= 0) return false;\n  try {\n    process.kill(pid, 0);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Whether `status.pid` still names the process that was started as this\n * worker, not a later, unrelated process the OS handed the same pid to after\n * the worker exited. `alive()` alone can't tell these apart — a pid observed\n * live on 2026-09-19 (worker 20260919-083549-90913) had been reused, and\n * `pai worker kill` signalled the wrong, unrelated process. `ps -o lstart=`\n * reads the running process's actual start time and compares it against the\n * status file's own `started` stamp; a reused pid almost never starts within\n * 120s of the original, so a wider drift means \"different process\".\n */\nexport function ownsPid(status: Pick<WorkerStatus, \"pid\" | \"started\">): boolean {\n  if (status.pid <= 0 || !alive(status.pid)) return false;\n  try {\n    const out = execFileSync(\"ps\", [\"-o\", \"lstart=\", \"-p\", String(status.pid)], {\n      encoding: \"utf8\",\n      env: { ...process.env, LC_ALL: \"C\" },\n    }).trim();\n    const psStart = Date.parse(out);\n    const started = Date.parse(status.started.replace(\" \", \"T\"));\n    if (Number.isNaN(psStart) || Number.isNaN(started)) return alive(status.pid);\n    return Math.abs(psStart - started) <= 120_000;\n  } catch {\n    // pid exited between the alive() check and the ps call, or ps failed for\n    // another reason — the pid-existence check is still better than nothing.\n    return alive(status.pid);\n  }\n}\n\n/** Whether a status is both marked \"running\" and still owns its recorded pid. */\nexport function isLive(status: Pick<WorkerStatus, \"pid\" | \"started\" | \"state\">): boolean {\n  return status.state === \"running\" && ownsPid(status);\n}\n\n/** \"42s\" under 90s, \"7m\" above — the coarse age the table and bar show. */\nexport function ageOf(ts: string, now: Date = new Date()): string {\n  const t = Date.parse(ts.replace(\" \", \"T\"));\n  if (Number.isNaN(t)) return \"?\";\n  const s = Math.max(0, Math.floor((now.getTime() - t) / 1000));\n  return s < 90 ? `${s}s` : `${Math.floor(s / 60)}m`;\n}\n\n/** One-line description of a tool call, e.g. \"Bash: npm test\". */\nexport function describeTool(name: string, inp: unknown): string {\n  if (typeof inp !== \"object\" || inp === null) return name;\n  const i = inp as Record<string, unknown>;\n  const get = (k: string) => (typeof i[k] === \"string\" ? (i[k] as string) : \"\");\n  if (name === \"Bash\") return `Bash: ${shortText(get(\"command\"), 70)}`;\n  if (name === \"Read\" || name === \"Edit\" || name === \"Write\" || name === \"MultiEdit\") {\n    const file = get(\"file_path\").split(\"/\").pop() ?? \"\";\n    return `${name}: ${file}`;\n  }\n  if (name === \"Grep\" || name === \"Glob\") return `${name}: ${shortText(get(\"pattern\"), 50)}`;\n  if (name === \"WebSearch\") return `${name}: ${shortText(get(\"query\"), 50)}`;\n  if (name === \"WebFetch\") return `${name}: ${shortText(get(\"url\"), 60)}`;\n  return name;\n}\n","/**\n * ledger.ts — the routing ledger, one append-only log for every worker event.\n *\n * Line shapes (whitespace-aligned so `tail` reads as a table):\n *\n *   2026-09-17 12:00:00 WORKER-START id=<id> provider=<p> mode=headless model=<m> cwd=<cwd> label=<label>\n *   2026-09-17 12:01:00 WORKER-END   id=<id> provider=<p> mode=headless model=<m> rc=0 secs=60 turns=3 tools=8 label=<label>\n *   2026-09-17 12:00:20 WORKER-REROUTE from=<a> to=<b> reason=quota\n *   2026-09-17 12:00:00 DENIED-ANTHROPIC-AGENT cwd=<cwd> desc=<desc>\n *   2026-09-17 12:00:00 ALLOWED-ANTHROPIC-AGENT cwd=<cwd> desc=<desc>\n *\n * `pai worker log` (and glm-log before it) counts these; keep the tags stable.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nexport function ledgerStamp(d: Date = new Date()): string {\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  return (\n    `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` +\n    `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`\n  );\n}\n\nexport interface LedgerLine {\n  stamp: string;\n  event: string;\n  fields: Record<string, string>;\n}\n\n/** Parse `2026-09-17 12:00:00 TAG key=value …` into its parts. */\nexport function parseLedgerLine(line: string): LedgerLine | null {\n  const m = line.match(/^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) (\\S+)(?: (.*))?$/);\n  if (!m) return null;\n  const fields: Record<string, string> = {};\n  // values are whitespace-collapsed by the writers, so spaces safely delimit\n  for (const part of (m[3] ?? \"\").split(\" \")) {\n    if (!part) continue;\n    const eq = part.indexOf(\"=\");\n    if (eq <= 0) continue;\n    fields[part.slice(0, eq)] = part.slice(eq + 1);\n  }\n  return { stamp: m[1], event: m[2], fields };\n}\n\nexport function parseLedger(text: string): LedgerLine[] {\n  const out: LedgerLine[] = [];\n  for (const line of text.split(\"\\n\")) {\n    if (!line.trim()) continue;\n    const parsed = parseLedgerLine(line);\n    if (parsed) out.push(parsed);\n  }\n  return out;\n}\n\n/** Append one ledger line. `kv` order is the caller's; values are flattened. */\nexport function appendLedger(\n  path: string,\n  event: string,\n  kv: Record<string, string | number | null | undefined>,\n  now: Date = new Date()\n): void {\n  const parts: string[] = [];\n  for (const [k, v] of Object.entries(kv)) {\n    if (v === undefined || v === null) continue;\n    parts.push(`${k}=${String(v).replace(/\\s+/g, \" \")}`);\n  }\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  const tail = parts.length ? \" \" + parts.join(\" \") : \"\";\n  appendFileSync(path, `${ledgerStamp(now)} ${event}${tail}\\n`, \"utf8\");\n}\n\nexport interface LedgerSummary {\n  scope: string;\n  started: number;\n  endedOk: number;\n  endedFailed: number;\n  denied: number;\n  allowed: number;\n  reroutes: number;\n  lastLines: string[];\n}\n\n/** The counts `pai worker log` prints, over today's lines or the whole file. */\nexport function ledgerSummary(\n  path: string,\n  scope: \"today\" | \"all\",\n  lastN = 15,\n  now: Date = new Date()\n): LedgerSummary | null {\n  if (!existsSync(path)) return null;\n  const text = readFileSync(path, \"utf8\");\n  const today = ledgerStamp(now).slice(0, 10);\n  const lines = text.split(\"\\n\").filter((l) => l.trim());\n  const scoped =\n    scope === \"all\" ? lines : lines.filter((l) => l.startsWith(today));\n  const parsed = scoped.map(parseLedgerLine).filter((x): x is LedgerLine => x !== null);\n  const count = (ev: string) => parsed.filter((p) => p.event === ev).length;\n  const ends = parsed.filter((p) => p.event === \"WORKER-END\");\n  const ok = ends.filter((p) => p.fields.rc === \"0\").length;\n  return {\n    scope: scope === \"all\" ? \"all time\" : `today ${today}`,\n    started: count(\"WORKER-START\"),\n    endedOk: ok,\n    endedFailed: ends.length - ok,\n    denied: count(\"DENIED-ANTHROPIC-AGENT\"),\n    allowed: count(\"ALLOWED-ANTHROPIC-AGENT\"),\n    reroutes: count(\"WORKER-REROUTE\"),\n    lastLines: lines.slice(-lastN),\n  };\n}\n","/**\n * worktree.ts — one git worktree per writing worker.\n *\n * A run whose class edits files (implement, complex, plan) and whose cwd is a\n * git repository gets its own worktree by default: `git worktree add\n * <logDir>/worktrees/<id> -b worker/<id>` from the current HEAD. The worker\n * commits its own work on that branch — the no-commit rule applies to the\n * main branch only — and the parent (or the operator) merges the result back:\n *\n *   pai worker merge <id>     git merge --no-ff worker/<id> + remove worktree + delete branch\n *   pai worker discard <id>   remove worktree and branch, keep nothing\n *\n * `ps` marks a worker with an unmerged branch `⎇`. Draft and review run in\n * place; `--no-worktree` opts out, `--worktree` forces one on.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readdirSync, rmSync, statSync } from \"node:fs\";\nimport { isAbsolute, join, resolve } from \"node:path\";\nimport { loadStatus, saveStatus, UNLABELED, type WorkerStatus } from \"./status.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { ledgerPath, statusPath } from \"./paths.js\";\n\nexport function worktreesDir(logDir: string): string {\n  return join(logDir, \"worktrees\");\n}\n\nexport function worktreeBranch(id: string): string {\n  return `worker/${id}`;\n}\n\nexport function worktreePath(logDir: string, id: string): string {\n  return join(worktreesDir(logDir), id);\n}\n\n/** Run git in `cwd`, returning trimmed stdout; throws with stderr on failure. */\nexport function git(cwd: string, args: string[]): string {\n  try {\n    return execFileSync(\"git\", [\"-C\", cwd, ...args], {\n      encoding: \"utf8\",\n      timeout: 30_000,\n      stdio: [\"ignore\", \"pipe\", \"pipe\"],\n    }).trim();\n  } catch (e) {\n    const err = e as { stderr?: Buffer | string; message?: string };\n    const why =\n      (typeof err.stderr === \"string\" ? err.stderr : err.stderr?.toString(\"utf8\")) ||\n      err.message ||\n      String(e);\n    throw new Error(`git ${args.join(\" \")} in ${cwd}: ${why.trim()}`);\n  }\n}\n\n/** Is `cwd` inside a git repository (a .git dir — worktrees: a .git file)? */\nexport function isGitRepo(cwd: string): boolean {\n  try {\n    return git(cwd, [\"rev-parse\", \"--git-dir\"]) !== \"\";\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Does the prompt read as a read-only task? Writing classes default to a\n * worktree; a prompt that only asks to look at things should not pay for one.\n * First-word verbs plus the explicit markers people actually write.\n */\nexport function promptLooksReadonly(prompt: string): boolean {\n  const p = prompt.trim();\n  if (!p) return true;\n  if (/\\b(read[- ]only|do not (modify|change|edit|write)|don'?t (modify|change|edit|write)|no changes)\\b/i.test(p)) {\n    return true;\n  }\n  return /^(review|read|analy[sz]e|research|summar[iy]|inspect|investigate|spotcheck|report|find|list|check|verify|describe|explain|show)\\b/i.test(\n    p\n  );\n}\n\n/** The classes whose runs write files and therefore default to a worktree. */\nexport const WORKTREE_CLASSES = [\"implement\", \"complex\", \"plan\"] as const;\n\n/** How the run flags decide the worktree question; undefined = decide by default. */\nexport type WorktreeFlag = boolean | undefined;\n\n/** Should this run get a worktree? Explicit flag first, then the default rule. */\nexport function worktreeWanted(\n  flag: WorktreeFlag,\n  opts: { cwd: string; className?: string; prompt: string | null }\n): boolean {\n  if (flag !== undefined) return flag;\n  if (!opts.className || !(WORKTREE_CLASSES as readonly string[]).includes(opts.className)) {\n    return false;\n  }\n  if (!isGitRepo(opts.cwd)) return false;\n  return !promptLooksReadonly(opts.prompt ?? \"\");\n}\n\nexport interface WorktreeInfo {\n  dir: string;\n  branch: string;\n  base: string;\n}\n\n/**\n * Create the worktree and branch for `id` from `cwd`'s HEAD. Throws when git\n * refuses (no commits yet, branch exists, …) — the caller decides whether to\n * degrade to an in-place run.\n */\nexport function addWorktree(logDir: string, id: string, cwd: string): WorktreeInfo {\n  const swept = sweepOrphanWorktrees(logDir);\n  if (swept.length) {\n    appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", {\n      id,\n      note: `swept orphan worktree(s): ${swept.join(\", \")}`,\n    });\n  }\n  const dir = worktreePath(logDir, id);\n  const branch = worktreeBranch(id);\n  const base = git(cwd, [\"rev-parse\", \"HEAD\"]);\n  git(cwd, [\"worktree\", \"add\", dir, \"-b\", branch]);\n  return { dir, branch, base };\n}\n\n/**\n * Remove worktrees (and their `worker/<id>` branches) whose worker no longer\n * exists — no status file left in the log dir. Killed and failed runs clean\n * up after themselves, but a `kill -9` or a crash strands a directory and a\n * branch; every worktree creation sweeps first so they cannot accumulate.\n * Directories younger than `minAgeMin` minutes are left alone: a run that is\n * just starting owns its worktree a moment before its status file exists.\n */\nexport function sweepOrphanWorktrees(logDir: string, minAgeMin = 10): string[] {\n  const root = worktreesDir(logDir);\n  if (!existsSync(root)) return [];\n  const swept: string[] = [];\n  for (const ent of readdirSync(root, { withFileTypes: true })) {\n    if (!ent.isDirectory()) continue;\n    const id = ent.name;\n    const dir = join(root, id);\n    if (existsSync(statusPath(logDir, id))) continue; // a known worker owns it\n    try {\n      const ageMin = (Date.now() - statSync(dir).mtimeMs) / 60_000;\n      if (ageMin < minAgeMin) continue;\n    } catch {\n      /* vanished mid-sweep; nothing to do */\n    }\n    let gitDir: string | null = null;\n    try {\n      const raw = git(dir, [\"rev-parse\", \"--git-common-dir\"]);\n      gitDir = isAbsolute(raw) ? raw : resolve(dir, raw);\n    } catch {\n      gitDir = null; // not a worktree anymore; just drop the directory\n    }\n    removeWorktree(gitDir ?? dir, dir, true);\n    if (gitDir) {\n      try {\n        git(gitDir, [\"worktree\", \"prune\"]);\n        git(gitDir, [\"branch\", \"-D\", worktreeBranch(id)]);\n      } catch {\n        // branch already gone or kept by git for a reason; the directory is\n      }\n    }\n    swept.push(id);\n  }\n  return swept;\n}\n\n/** Commits the branch collected on top of its base. */\nexport function commitsSince(dir: string, base: string): number {\n  try {\n    return parseInt(git(dir, [\"rev-list\", \"--count\", `${base}..HEAD`]), 10) || 0;\n  } catch {\n    return 0;\n  }\n}\n\n/**\n * Record the worktree result in the status file: branch and commit count on\n * success, the worktree cleaned up on failure. Returns the updated status.\n */\nexport function recordWorktree(\n  logDir: string,\n  status: WorkerStatus,\n  info: WorktreeInfo,\n  ok: boolean\n): WorkerStatus {\n  const s = { ...status };\n  if (ok) {\n    s.branch = info.branch;\n    s.commits = commitsSince(info.dir, info.base);\n    s.worktreeDir = info.dir;\n    s.worktreeBase = info.base;\n  } else {\n    // a failed run leaves nothing to merge; the branch dies with the worktree\n    removeWorktree(s.cwd, info.dir, true);\n    try {\n      git(s.cwd, [\"branch\", \"-D\", info.branch]);\n    } catch {\n      // already gone or never created\n    }\n    s.branch = null;\n    s.worktreeDir = null;\n    s.worktreeBase = null;\n    s.commits = null;\n  }\n  saveStatus(logDir, s);\n  return s;\n}\n\n/** Remove a worktree directory from git's books and the filesystem. */\nfunction removeWorktree(cwd: string, dir: string, force: boolean): void {\n  try {\n    git(cwd, [\"worktree\", \"remove\", ...(force ? [\"--force\"] : []), dir]);\n    return;\n  } catch {\n    // fall through to the manual cleanup\n  }\n  if (existsSync(dir)) {\n    try {\n      rmSync(dir, { recursive: true, force: true });\n      git(cwd, [\"worktree\", \"prune\"]);\n    } catch {\n      // best effort: a leftover directory is visible in the logDir\n    }\n  }\n}\n\n/**\n * Tracked changes (staged or not) plus untracked files — everything a\n * `git add -A` in `wtDir` would commit. The same two calls the old patch\n * carry used; name-only, not porcelain: a worktree-only change renders as\n * \" M path\" and the shared git() helper trims that leading space away.\n */\nexport function uncommittedPaths(wtDir: string): string[] {\n  const tracked = git(wtDir, [\"diff\", \"--name-only\", \"HEAD\"]).split(\"\\n\").filter(Boolean);\n  const untracked = git(wtDir, [\"ls-files\", \"--others\", \"--exclude-standard\"])\n    .split(\"\\n\")\n    .filter(Boolean);\n  return [...tracked, ...untracked];\n}\n\n/**\n * Commit a worktree's uncommitted changes to its branch so the merge carries\n * them. `git merge` only moves committed work, so a worker that stopped\n * without committing would lose its edits to `worktree remove` — exactly\n * what happened live on 2026-09-17. Returns the salvaged paths, [] when the\n * worktree is clean. A failed commit throws with the worktree untouched:\n * its edits are still on disk, so nothing is lost.\n */\nexport function salvageUncommitted(wtDir: string, label: string): string[] {\n  const paths = uncommittedPaths(wtDir);\n  if (!paths.length) return [];\n  try {\n    git(wtDir, [\"add\", \"-A\"]);\n    git(wtDir, [\"commit\", \"-m\", `salvaged: ${label}`]);\n  } catch (e) {\n    throw new Error(\n      `cannot salvage the uncommitted changes in ${wtDir} — ${(e as Error).message}; ` +\n        `nothing was lost: commit them there by hand, then re-run merge`\n    );\n  }\n  return paths;\n}\n\n/**\n * The dirty paths of a checkout, parsed from `git status --porcelain -z`:\n * NUL-separated (a path with a newline in it cannot corrupt the parse),\n * rename entries contribute both sides, and any quoting is stripped.\n */\nfunction dirtyPaths(cwd: string): string[] {\n  // raw execFileSync, not the shared git(): it trims stdout, which eats the\n  // leading space of a worktree-only \" M path\" record and breaks the parse\n  const raw = execFileSync(\"git\", [\"-C\", cwd, \"status\", \"--porcelain\", \"-z\"], {\n    encoding: \"utf8\",\n    timeout: 30_000,\n    stdio: [\"ignore\", \"pipe\", \"pipe\"],\n  });\n  const fields = raw.split(\"\\0\");\n  const strip = (p: string) => (p.startsWith('\"') && p.endsWith('\"') ? p.slice(1, -1) : p);\n  const out: string[] = [];\n  for (let i = 0; i < fields.length; i++) {\n    const f = fields[i];\n    if (!f || f.length < 4 || f.charAt(2) !== \" \") continue; // not an \"XY path\" record\n    out.push(strip(f.slice(3)));\n    const xy = f.slice(0, 2);\n    if (xy.includes(\"R\") || xy.includes(\"C\")) {\n      const orig = fields[i + 1]; // rename/copy records carry the source path next\n      if (orig && orig.charAt(2) !== \" \") {\n        out.push(strip(orig));\n        i += 1;\n      }\n    }\n  }\n  return out;\n}\n\n/**\n * Refuse the merge when the original checkout is dirty in paths the branch\n * touches: the merge would overwrite those edits or fail on them, either way\n * leaving a half-state. No merge is made, the worktree stays.\n */\nfunction assertNoDirtyOverlap(\n  cwd: string,\n  incoming: string[],\n  id: string,\n  branch: string\n): void {\n  const dirty = new Set(dirtyPaths(cwd));\n  const overlap = [...new Set(incoming)].filter((p) => dirty.has(p)).sort();\n  if (!overlap.length) return;\n  throw new Error(\n    `worker ${id}: the checkout ${cwd} has uncommitted changes in paths ${branch} touches: ` +\n      `${overlap.join(\", \")}. Commit or stash them in the checkout, then re-run: pai worker merge ${id}. ` +\n      `No merge was made; the worktree was kept.`\n  );\n}\n\n/** Never remove a worktree that still holds uncommitted changes. */\nexport function assertWorktreeClean(wtDir: string, id: string): void {\n  const leftover = uncommittedPaths(wtDir);\n  if (leftover.length) {\n    throw new Error(\n      `worker ${id}: the worktree ${wtDir} still holds uncommitted changes ` +\n        `(${leftover.join(\", \")}) — it was NOT removed; commit or copy them by hand, then re-run merge`\n    );\n  }\n}\n\n/**\n * `pai worker merge <id>`: salvage whatever the worker left uncommitted onto\n * its branch, refuse when the original checkout is dirty in paths the branch\n * touches, merge with --no-ff (the merge commit names the worker), then\n * remove the worktree and delete the branch; the status gains `merged: true`.\n * A branch with nothing to merge is refused loudly — after salvage that\n * genuinely means it holds nothing, and reporting success there would\n * destroy the worktree for no gain.\n */\nexport function mergeWorker(logDir: string, id: string): string {\n  const st = mustHaveBranch(logDir, id);\n  if (st.merged) return `worker ${id}: branch ${st.branch} already merged`;\n  // salvage first: only a commit can carry uncommitted work through the merge\n  const salvaged = existsSync(st.worktreeDir!)\n    ? salvageUncommitted(st.worktreeDir!, st.label || UNLABELED)\n    : [];\n  const incoming = parseInt(git(st.cwd, [\"rev-list\", \"--count\", `HEAD..${st.branch}`]), 10) || 0;\n  if (incoming <= 0) {\n    throw new Error(\n      `worker ${id}: branch ${st.branch} has no commits to merge. ` +\n        `The worktree ${st.worktreeDir} was NOT removed — uncommitted work there would be destroyed. ` +\n        `Commit it yourself, or drop everything with: pai worker discard ${id}`\n    );\n  }\n  const mergeBase = git(st.cwd, [\"merge-base\", \"HEAD\", st.branch!]);\n  const incomingPaths = git(st.cwd, [\"diff\", \"--name-only\", mergeBase, st.branch!])\n    .split(\"\\n\")\n    .filter(Boolean);\n  assertNoDirtyOverlap(st.cwd, incomingPaths, id, st.branch!);\n  try {\n    git(st.cwd, [\"merge\", \"--no-ff\", st.branch!, \"-m\", `merge worker ${id} (${st.label})`]);\n  } catch (e) {\n    throw new Error(\n      `worker ${id}: git refused the merge of ${st.branch} — ${(e as Error).message}. ` +\n        `The worktree ${st.worktreeDir} was kept: resolve the conflict, then re-run merge`\n    );\n  }\n  if (existsSync(st.worktreeDir!)) assertWorktreeClean(st.worktreeDir!, id);\n  removeWorktree(st.cwd, st.worktreeDir!, false);\n  let branchGone = true;\n  try {\n    git(st.cwd, [\"branch\", \"-d\", st.branch!]);\n  } catch {\n    branchGone = false; // -d refuses anything not fully merged; keep the branch, say so\n  }\n  const s = { ...st, merged: true };\n  saveStatus(logDir, s);\n  const base = `merged ${st.branch} into ${st.cwd} (worktree removed${branchGone ? \", branch deleted\" : \"; branch kept: git refused -d\"})`;\n  return salvaged.length\n    ? `${base}; salvaged ${salvaged.length} uncommitted change(s): ${salvaged.join(\", \")}`\n    : base;\n}\n\n/** `pai worker discard <id>`: drop worktree and branch, keep nothing. */\nexport function discardWorker(logDir: string, id: string): string {\n  const st = mustHaveBranch(logDir, id);\n  removeWorktree(st.cwd, st.worktreeDir!, true);\n  let branchGone = false;\n  try {\n    git(st.cwd, [\"branch\", \"-D\", st.branch!]);\n    branchGone = true;\n  } catch {\n    branchGone = false;\n  }\n  const s = { ...st, branch: null, worktreeDir: null, worktreeBase: null, commits: null, merged: false };\n  saveStatus(logDir, s);\n  return `discarded worker ${id}: worktree removed${branchGone ? `, branch ${st.branch} deleted` : \"\"}`;\n}\n\nfunction mustHaveBranch(logDir: string, id: string): WorkerStatus & Required<Pick<WorkerStatus, \"branch\" | \"worktreeDir\">> {\n  const st = loadStatus(logDir, id);\n  if (!st) throw new Error(`no worker named \"${id}\"`);\n  if (!st.branch || !st.worktreeDir) {\n    throw new Error(\n      `worker ${id} has no worktree branch to ${st.branch ? \"clean up\" : \"merge\"} — ` +\n        `it ran in place or its branch was already handled`\n    );\n  }\n  return st as WorkerStatus & Required<Pick<WorkerStatus, \"branch\" | \"worktreeDir\">>;\n}\n\n/**\n * The paragraph a worktree run's system prompt gains: it may commit on its\n * own branch (the no-commit rule holds for the main branch only), it must not\n * merge or push itself, and the parent or operator merges.\n */\nexport function worktreeSystemPrompt(id: string, branch: string, dir: string): string {\n  return [\n    \"You are running in your own git worktree:\",\n    `  ${dir} on branch ${branch} (worker id ${id}).`,\n    \"Commit your work on that branch as you go (git add / git commit) — committing here is expected;\",\n    \"the no-commit rule applies to the main branch only, and this is not it.\",\n    \"Do not merge, rebase or push; the operator merges your branch back with `pai worker merge`.\",\n    \"Use ONLY relative paths inside the worktree, never absolute worktree paths — absolute paths break after merge and leak machine layout.\",\n  ].join(\"\\n\");\n}\n","/**\n * scope.ts — which workers \"belong\" to the terminal asking about them.\n *\n * Two identities, in priority order:\n *\n *   1. AIBroker's session registry (~/.aibroker/session-names.json): iTerm\n *      session UUID → session name. The launching session's UUID is stored in\n *      each worker's status file as `session.id`, so every pane of a named\n *      session sees its workers — regardless of tab layout.\n *   2. The iTerm tab key (`w<window>t<tab>` prefix of ITERM_SESSION_ID): the\n *      original heuristic, kept as the fallback when no registry entry\n *      matches (AIBroker absent, unnamed session, non-iTerm terminal).\n *\n * If neither is available, viewers fall back to \"all workers\".\n *\n * A third case: workers spawned from a Claude Code Bash tool (the\n * orchestrator pattern) have neither — Claude Code exports no terminal or\n * session identity to its Bash children. Those record `spawnerSession`, the\n * orchestrator's claude session id bridged through the status line's\n * session map (see below), so their tab still claims them.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { loadStatus, type WorkerStatus } from \"./status.js\";\n\nexport const AIBROKER_REGISTRY = join(homedir(), \".aibroker\", \"session-names.json\");\n\n/**\n * `w<window>t<tab>` prefix of an iTerm session id, \"\" if empty or malformed.\n * Panes split from the same tab share this prefix and differ only in `p<n>`.\n */\nexport function tabKey(term: string): string {\n  if (!term) return \"\";\n  const head = term.split(\"p\", 1)[0];\n  const parts = head.slice(1).split(\"t\");\n  if (head.startsWith(\"w\") && parts.length === 2 && parts.every((p) => /^\\d+$/.test(p))) {\n    return head;\n  }\n  return \"\";\n}\n\n/** Tab key of the iTerm tab this process runs in, \"\" if not inside iTerm2. */\nexport function currentTabKey(env: NodeJS.ProcessEnv = process.env): string {\n  return tabKey(env.ITERM_SESSION_ID ?? \"\");\n}\n\n/** The iTerm UUID part of an ITERM_SESSION_ID (after the last colon). */\nexport function itermUuid(term: string): string {\n  if (!term) return \"\";\n  return term.split(\":\").pop() ?? \"\";\n}\n\nexport interface SessionIdentity {\n  id: string;\n  name: string;\n}\n\n/**\n * Resolve the AIBroker session for an ITERM_SESSION_ID. Reads the persistent\n * name registry (the same store `aibroker_rename` writes); returns null when\n * AIBroker is absent or the session is not in it — callers then use the tab\n * key, which is the pre-AIBroker behaviour.\n */\nexport function resolveSession(\n  term: string,\n  registryPath: string = AIBROKER_REGISTRY\n): SessionIdentity | null {\n  const uuid = itermUuid(term);\n  if (!uuid) return null;\n  let names: Record<string, unknown>;\n  try {\n    if (!existsSync(registryPath)) return null;\n    names = JSON.parse(readFileSync(registryPath, \"utf8\")) as Record<string, unknown>;\n  } catch {\n    return null;\n  }\n  const name = names[uuid];\n  if (typeof name !== \"string\" || !name) return null;\n  return { id: uuid, name };\n}\n\n/**\n * True when `worker` was launched from `term` (the terminal asking about it).\n * Session-id match first; the tab key only when no registry entry matched —\n * a session without a name keeps the tab-scoped behaviour it always had.\n */\nexport function workerInScope(worker: WorkerStatus, term: string): boolean {\n  if (!term) return false;\n  const uuid = itermUuid(term);\n  if (worker.session?.id && uuid) return worker.session.id === uuid;\n  return tabKey(worker.term) === tabKey(term) && tabKey(term) !== \"\";\n}\n\n/**\n * Registry key for pane files: the session id when AIBroker knows this\n * terminal, else the tab key, else the raw iTerm UUID.\n */\nexport function scopeKey(term: string): string {\n  const session = resolveSession(term);\n  if (session) return session.id;\n  return currentTabKey() || itermUuid(term);\n}\n\n/** `[Name]` when the worker has an AIBroker session name, else \"\". */\nexport function sessionTag(worker: { session?: { name?: string } | null }): string {\n  return worker.session?.name ? `[${worker.session.name}]` : \"\";\n}\n\n// ---------------------------------------------------------------------------\n// spawner sessions — attribution for workers launched from a Claude Code Bash\n// ---------------------------------------------------------------------------\n\n/**\n * Claude Code exports neither ITERM_SESSION_ID nor its own session id to the\n * Bash tool, so a `pai worker run` from an orchestrator has no terminal to\n * record and its status would be unattributable. The status line is the one\n * process that sees both identities at once — the payload's session_id and\n * the tab's ITERM_SESSION_ID — so it bridges them: every refresh writes this\n * cwd-keyed map, and the runner reads it back at spawn time.\n */\nexport interface SessionMapEntry {\n  session: string;\n  ts: number;\n  /**\n   * The tab's ITERM_SESSION_ID, when the status line could see one — the\n   * bridge back from a claude session id to the terminal it runs in\n   * (supervision uses it to push worker events into that terminal).\n   * Absent on entries written before the field existed.\n   */\n  term?: string;\n}\n\n/** How old a map entry may be for a spawn to adopt it (status lines refresh constantly while a session lives). */\nexport const SPAWNER_SESSION_TTL_MS = 10 * 60_000;\n\n/** Map entries not refreshed within this window are pruned on write. */\nconst SESSION_MAP_PRUNE_MS = 60 * 60_000;\n\nexport function sessionMapPath(logDir: string): string {\n  return join(logDir, \"claude-session-map.json\");\n}\n\n/**\n * Record that the claude session `session` renders its status line in `cwd`\n * (and, when known, in iTerm session `term`). Never throws — a broken map\n * must not break the bar. Prunes stale entries; skips the write when the\n * entry is unchanged and fresh.\n */\nexport function recordSessionMapEntry(\n  logDir: string,\n  cwd: string,\n  session: string,\n  term?: string,\n  now: number = Date.now()\n): void {\n  if (!cwd || !session) return;\n  const path = sessionMapPath(logDir);\n  let map: Record<string, SessionMapEntry> = {};\n  try {\n    if (existsSync(path)) {\n      map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n    }\n  } catch {\n    map = {}; // a damaged map is rewritten, never fatal\n  }\n  const prev = map[cwd];\n  if (\n    prev &&\n    prev.session === session &&\n    (prev.term ?? \"\") === (term ?? \"\") &&\n    now - prev.ts < 60_000\n  ) {\n    return;\n  }\n  const pruned: Record<string, SessionMapEntry> = {};\n  for (const [dir, e] of Object.entries(map)) {\n    if (now - e.ts < SESSION_MAP_PRUNE_MS) pruned[dir] = e;\n  }\n  pruned[cwd] = { session, ts: now, ...(term ? { term } : {}) };\n  try {\n    mkdirSync(logDir, { recursive: true });\n    const tmp = `${path}.tmp`;\n    writeFileSync(tmp, JSON.stringify(pruned), \"utf8\");\n    renameSync(tmp, path);\n  } catch {\n    // unwritable log dir: no attribution this round, nothing else breaks\n  }\n}\n\n/**\n * The claude session a new run was spawned by, when it can be known: a run\n * inside another worker inherits its spawner (chain stages keep the\n * orchestrator's tab that way); otherwise a fresh map entry for `cwd`.\n */\nexport function resolveSpawnerSession(\n  logDir: string,\n  cwd: string,\n  env: NodeJS.ProcessEnv = process.env,\n  now: number = Date.now()\n): string | null {\n  const parentId = env.PAI_WORKER_ID;\n  if (parentId) {\n    const inherited = loadStatus(logDir, parentId)?.spawnerSession;\n    if (inherited) return inherited;\n  }\n  const path = sessionMapPath(logDir);\n  if (!existsSync(path) || !cwd) return null;\n  try {\n    const entry = (JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>)[cwd];\n    if (entry && entry.session && now - entry.ts < SPAWNER_SESSION_TTL_MS) return entry.session;\n  } catch {\n    // a damaged map simply attributes nothing\n  }\n  return null;\n}\n\n/**\n * The iTerm UUID a claude session last rendered its status line in, when the\n * map knows one: the freshest entry naming that session. This is the reverse\n * of resolveSpawnerSession — worker → orchestrator there, orchestrator →\n * terminal here — and it exists so daemon-side pushes (supervision) can reach\n * a session that has no iTerm identity of its own in any worker status.\n */\nexport function itermForClaudeSession(\n  logDir: string,\n  claudeSession: string,\n  now: number = Date.now()\n): string | null {\n  const path = sessionMapPath(logDir);\n  if (!existsSync(path) || !claudeSession) return null;\n  let map: Record<string, SessionMapEntry>;\n  try {\n    map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n  } catch {\n    return null;\n  }\n  let best: SessionMapEntry | null = null;\n  for (const e of Object.values(map)) {\n    if (e.session !== claudeSession || !e.term) continue;\n    // a stale entry names a tab the session left; freshness orders them\n    if (now - e.ts >= SESSION_MAP_PRUNE_MS) continue;\n    if (!best || e.ts > best.ts) best = e;\n  }\n  const term = best?.term;\n  return term ? itermUuid(term) : null;\n}\n","/**\n * engines/image.ts — the `engine: image` worker.\n *\n * No claude-code process, no worktree, no MCP: `pai worker run --capability\n * image` POSTs one OpenAI-compatible images-generations request straight to\n * the provider's `url` and writes the PNG it gets back. One request, no\n * retries beyond the fetch's own timeout — a caller that wants another try\n * just runs again.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { resolveProviderKey, WorkersConfigError, type WorkerProvider } from \"../config.js\";\n\nexport interface ImageRunOptions {\n  providerName: string;\n  provider: WorkerProvider;\n  model: string;\n  prompt: string;\n  /** Where the PNG is written; caller resolves the default path. */\n  outPath: string;\n  /** \"WIDTHxHEIGHT\", default 1024x1024. */\n  size?: string;\n  /** Default 120000 (2 minutes). */\n  timeoutMs?: number;\n}\n\nexport interface ImageRunResult {\n  ok: true;\n  path: string;\n  model: string;\n  provider: string;\n  durationMs: number;\n  bytes: number;\n}\n\nconst DEFAULT_SIZE = \"1024x1024\";\nexport const DEFAULT_IMAGE_TIMEOUT_MS = 120_000;\n\ninterface ImagesGenerationsResponse {\n  data?: { b64_json?: string }[];\n}\n\n/**\n * `POST {url}/images/generations` with `{model, prompt, size, n: 1,\n * response_format: \"b64_json\"}` and the provider's bearer token, decode the\n * base64 PNG it returns, and write it to `outPath`.\n */\nexport async function runImageCapability(opts: ImageRunOptions): Promise<ImageRunResult> {\n  if (!opts.provider.baseUrl) {\n    throw new WorkersConfigError(\n      `provider \"${opts.providerName}\" (engine image) has no \"url\" configured — set one with: ` +\n        `pai worker providers update ${opts.providerName} --base-url <url>`\n    );\n  }\n  const token = resolveProviderKey(opts.provider);\n  const url = `${opts.provider.baseUrl.replace(/\\/+$/, \"\")}/images/generations`;\n  const timeoutMs = opts.timeoutMs ?? DEFAULT_IMAGE_TIMEOUT_MS;\n  const controller = new AbortController();\n  const timer = setTimeout(() => controller.abort(), timeoutMs);\n  const t0 = Date.now();\n  let res: Response;\n  try {\n    res = await fetch(url, {\n      method: \"POST\",\n      headers: {\n        \"content-type\": \"application/json\",\n        ...(token ? { authorization: `Bearer ${token}` } : {}),\n      },\n      body: JSON.stringify({\n        model: opts.model,\n        prompt: opts.prompt,\n        size: opts.size ?? DEFAULT_SIZE,\n        n: 1,\n        response_format: \"b64_json\",\n      }),\n      signal: controller.signal,\n    });\n  } catch (e) {\n    if (e instanceof Error && e.name === \"AbortError\") {\n      throw new WorkersConfigError(\n        `provider \"${opts.providerName}\": image request timed out after ${timeoutMs}ms`\n      );\n    }\n    throw new WorkersConfigError(\n      `provider \"${opts.providerName}\": image request failed: ${e instanceof Error ? e.message : String(e)}`\n    );\n  } finally {\n    clearTimeout(timer);\n  }\n  const durationMs = Date.now() - t0;\n  const text = await res.text();\n  if (!res.ok) {\n    throw new WorkersConfigError(\n      `provider \"${opts.providerName}\": image request failed (${res.status}): ${text.slice(0, 500)}`\n    );\n  }\n  let parsed: ImagesGenerationsResponse;\n  try {\n    parsed = JSON.parse(text) as ImagesGenerationsResponse;\n  } catch {\n    throw new WorkersConfigError(`provider \"${opts.providerName}\": image response was not JSON`);\n  }\n  const b64 = parsed.data?.[0]?.b64_json;\n  if (!b64) {\n    throw new WorkersConfigError(`provider \"${opts.providerName}\": image response had no data[0].b64_json`);\n  }\n  const bytes = Buffer.from(b64, \"base64\");\n  mkdirSync(dirname(opts.outPath), { recursive: true });\n  writeFileSync(opts.outPath, bytes);\n  return {\n    ok: true,\n    path: opts.outPath,\n    model: opts.model,\n    provider: opts.providerName,\n    durationMs,\n    bytes: bytes.length,\n  };\n}\n","/**\n * pane.ts — the per-worker follow pane in iTerm2.\n *\n * One small-font pane per worker, stacked in a right-hand column: the first\n * worker of a scope splits the launching session vertically, every further\n * one splits the lowest live worker pane horizontally, so panes stack top to\n * bottom. The split never sizes the new session — that would grow the whole\n * window; instead the window's bounds are read before the split and restored\n * right after, so the panes share the space the window already had. The follow\n * command is part of the split itself — iTerm creates the new session already\n * running it — so no text is ever typed into any session afterwards; a separate\n * typing step raced the operator's keystrokes (focus briefly sits on the new\n * pane after a split, and in one observed failure the command landed in the\n * operator's own shell). A split still makes the new session the tab's active\n * one — the scripts re-select the launching session right after, so a pane\n * opening never steals the keystrokes the operator is typing. Each\n * pane runs `pai worker follow <id> --auto-exit <n>` under the `pai-worker`\n * dynamic profile (Close Sessions On End), so panes disappear by themselves.\n *\n * Panes are tracked per scope (AIBroker session id, else tab key) in\n * <logDir>/panes/<key>.json, keyed by 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  mkdtempSync,\n  readFileSync,\n  renameSync,\n  rmSync,\n  writeFileSync,\n} from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { WorkersConfig } from \"./config.js\";\nimport { panesDir } from \"./paths.js\";\nimport { itermUuid, scopeKey } from \"./scope.js\";\n\nexport const PROFILE_NAME = \"pai-worker\";\n\n/** Where the dynamic profile lives (env override keeps tests off the real one). */\nexport function dynamicProfilePath(): string {\n  return process.env.PAI_WORKER_PROFILE ?? join(\n    homedir(),\n    \"Library\",\n    \"Application Support\",\n    \"iTerm2\",\n    \"DynamicProfiles\",\n    \"pai-worker.json\"\n  );\n}\n\n// ---------------------------------------------------------------------------\n// AppleScripts (arguments are passed as argv items, never interpolated)\n// ---------------------------------------------------------------------------\n\n// One pane per worker: split the launching session vertically, or the lowest\n// live candidate horizontally. Returns \"<live ids>,|<new session id>\".\n// Exported for the script-content tests (window size, argv-only arguments).\nexport const WORKER_SPLIT_SCRIPT = `on run(argv)\n    set targetID to item 1 of argv\n    set candList to item 2 of argv\n    set followCmd to item 3 of argv\n    set profileName to item 4 of argv\n    tell application id \"com.googlecode.iterm2\"\n        if not running then return \"notrunning\"\n        repeat with w in windows\n            repeat with t in tabs of w\n                repeat with s in sessions of t\n                    if id of s is targetID then\n                        -- sizing the new session would grow the whole window:\n                        -- pin the window's bounds now and restore them after\n                        -- the split, so the panes share the existing space.\n                        -- copy, never set: set stores the property\n                        -- reference lazily, so restoring it would re-read the\n                        -- post-split bounds instead of these — observed as the\n                        -- window jumping to the main display\n                        copy bounds of w to winBounds\n                        set sessIDs to {}\n                        repeat with other in sessions of t\n                            set end of sessIDs to (id of other as text)\n                        end repeat\n                        set lived to {}\n                        set splitS to missing value\n                        repeat with cid in my splitIds(candList)\n                            set cidText to (cid as text)\n                            if sessIDs contains cidText then\n                                set end of lived to cidText\n                                if splitS is missing value then\n                                    set splitS to first session of t whose id is cidText\n                                end if\n                            end if\n                        end repeat\n                        -- the follow command is part of the split itself: the\n                        -- pane is born already running it, so no text is ever\n                        -- typed into any session — a typing step raced the\n                        -- operator's keystrokes and could even land in the\n                        -- operator's own session\n                        if profileName is \"\" then\n                            if splitS is missing value then\n                                tell s\n                                    set newS to split vertically with default profile command followCmd\n                                end tell\n                            else\n                                tell splitS\n                                    set newS to split horizontally with default profile command followCmd\n                                end tell\n                            end if\n                        else\n                            if splitS is missing value then\n                                tell s\n                                    set newS to split vertically with profile profileName command followCmd\n                                end tell\n                            else\n                                tell splitS\n                                    set newS to split horizontally with profile profileName command followCmd\n                                end tell\n                            end if\n                        end if\n                        -- a split makes the new session the tab's active one:\n                        -- re-select the launching session so focus returns to\n                        -- where the operator was typing, never the new pane\n                        try\n                            select s\n                        end try\n                        try\n                            set bounds of w to winBounds\n                        end try\n                        set out to \"\"\n                        repeat with lid in lived\n                            set out to out & lid & \",\"\n                        end repeat\n                        return out & \"|\" & (id of newS as text)\n                    end if\n                end repeat\n            end repeat\n        end repeat\n    end tell\n    return \"notfound\"\nend run\n\non splitIds(s)\n    set out to {}\n    if s is \"\" then return out\n    set prevDels to AppleScript's text item delimiters\n    set AppleScript's text item delimiters to \",\"\n    repeat with part in text items of s\n        set end of out to (part as text)\n    end repeat\n    set AppleScript's text item delimiters to prevDels\n    return out\nend splitIds`;\n\n// TTys of every session in the launching session's tab (no-worker pane variant).\nconst TAB_TTYS_SCRIPT = `on run(argv)\n    set targetID to item 1 of argv\n    tell application id \"com.googlecode.iterm2\"\n        if not running then return \"notrunning\"\n        repeat with w in windows\n            repeat with t in tabs of w\n                repeat with s in sessions of t\n                    if id of s is targetID then\n                        set ttys to {}\n                        repeat with other in sessions of t\n                            copy (tty of other) to end of ttys\n                        end repeat\n                        return ttys\n                    end if\n                end repeat\n            end repeat\n        end repeat\n    end tell\n    return \"notfound\"\nend run`;\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 = `on run(argv)\n    set targetID to item 1 of argv\n    tell application id \"com.googlecode.iterm2\"\n        if not running then return \"notrunning\"\n        repeat with w in windows\n            repeat with t in tabs of w\n                repeat with s in sessions of t\n                    if id of s is targetID then\n                        copy bounds of w to winBounds\n                        set prevDels to AppleScript's text item delimiters\n                        set AppleScript's text item delimiters to \", \"\n                        set out to winBounds as text\n                        set AppleScript's text item delimiters to prevDels\n                        return out\n                    end if\n                end repeat\n            end repeat\n        end repeat\n    end tell\n    return \"notfound\"\nend run`;\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 = `on run(argv)\n    set targetID to item 1 of argv\n    set followCmd to item 2 of argv\n    tell application id \"com.googlecode.iterm2\"\n        if not running then return \"notrunning\"\n        repeat with w in windows\n            repeat with t in tabs of w\n                repeat with s in sessions of t\n                    if id of s is targetID then\n                        -- the follow command is part of the split itself (see\n                        -- WORKER_SPLIT_SCRIPT): nothing is ever typed into a\n                        -- session afterwards\n                        tell s\n                            set newS to split vertically with default profile command followCmd\n                        end tell\n                        -- keep the tab's active session where it was (see\n                        -- WORKER_SPLIT_SCRIPT)\n                        try\n                            select s\n                        end try\n                        return \"opened\"\n                    end if\n                end repeat\n            end repeat\n        end repeat\n    end tell\n    return \"notfound\"\nend run`;\n\n// ---------------------------------------------------------------------------\n// osascript / ps / defaults helpers\n// ---------------------------------------------------------------------------\n\n/** Run an AppleScript with argv. Rejects when osascript itself cannot run. */\nfunction osascript(script: string, args: string[]): Promise<{ stdout: string; stderr: string }> {\n  return new Promise((resolve, reject) => {\n    const proc = spawn(\"osascript\", [\"-\", ...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\ninterface PaneEntry {\n  session: string;\n  worker: string;\n  opened: string;\n}\n\nfunction loadRegistry(path: string): PaneEntry[] {\n  try {\n    const reg = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n    return Array.isArray(reg) ? (reg as PaneEntry[]) : [];\n  } catch {\n    return [];\n  }\n}\n\nfunction saveRegistry(path: string, reg: PaneEntry[]): void {\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  const tmp = `${path}.tmp`;\n  writeFileSync(tmp, JSON.stringify(reg, null, 1), \"utf8\");\n  renameSync(tmp, path);\n}\n\n// ---------------------------------------------------------------------------\n// Public entry points\n// ---------------------------------------------------------------------------\n\n/**\n * The command a worker pane runs: one exec-able line with no shell syntax —\n * iTerm's split command parameter execs without a shell (proven live: panes\n * survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on\n * `export …; …` or a redirect), so the command must be one exec-able line.\n * Absolute node on the CLI entry file also sidesteps the panes' bare launchd\n * PATH (no pai, no node) and the `#!/usr/bin/env node` shebang.\n */\nexport function followCommand(wid: string, autoExitSecs: number): string {\n  return `${process.execPath} ${absoluteCliPath()} worker follow ${wid} --auto-exit ${autoExitSecs}`;\n}\n\n/** Absolute path of the running pai CLI; falls back to PATH lookup at spawn. */\nfunction absoluteCliPath(): string {\n  const argv1 = process.argv[1] ?? \"\";\n  if (argv1.endsWith(\"pai\")) return argv1;\n  return \"pai\";\n}\n\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<string> {\n  if (workerPaneOpen(wid)) return `pane for ${wid} already open`;\n  const uid = itermUuid(term);\n  const regPath = join(panesDir(logDir), `${scopeKey(term)}.json`);\n  const reg = loadRegistry(regPath);\n  const profile = await followProfile(config.pane.fontSize);\n  const cmd = followCommand(wid, config.pane.autoExitSecs);\n  // registry newest first: the split lands below the lowest live worker pane\n  const cands = [...reg].reverse().map((e) => e.session).join(\",\");\n  const p = await osascript(WORKER_SPLIT_SCRIPT, [uid, cands, cmd, profile]);\n  const out = p.stdout.trim();\n  if (out === \"notfound\" || out === \"notrunning\") {\n    throw new Error(\n      out === \"notfound\"\n        ? \"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\"\n        : \"pai worker pane: iTerm2 is not running\"\n    );\n  }\n  const bar = out.indexOf(\"|\");\n  if (bar < 0) {\n    throw new Error(\n      `pai worker pane: osascript failed: ${(p.stderr || out).slice(0, 200)}`\n    );\n  }\n  const live = new Set(out.slice(0, bar).split(\",\").filter(Boolean));\n  const pruned = reg.filter((e) => live.has(e.session));\n  pruned.push({\n    session: out.slice(bar + 1),\n    worker: wid,\n    opened: new Date().toISOString().replace(\"T\", \" \").slice(0, 19),\n  });\n  saveRegistry(regPath, pruned);\n  return `pane opened for ${wid}`;\n}\n\n/**\n * One `--check` line with the bounds of the window hosting `term`'s iTerm\n * session — the before/after pair that shows whether a split moved it.\n * Read-only; never touches the window.\n */\nasync function windowBoundsLine(term: string): Promise<string> {\n  const uid = itermUuid(term);\n  if (!uid) return \"window bounds: (not in iTerm2)\";\n  try {\n    const p = await osascript(WINDOW_BOUNDS_SCRIPT, [uid]);\n    const out = p.stdout.trim();\n    if (out && out !== \"notfound\" && out !== \"notrunning\") return `window bounds: ${out}`;\n    const why =\n      out === \"notfound\" ? \"iTerm2 session not found\"\n      : out === \"notrunning\" ? \"iTerm2 not running\"\n      : (p.stderr.trim() || \"no output\").slice(0, 120);\n    return `window bounds: (${why})`;\n  } catch (e) {\n    return `window bounds: (osascript: ${String((e as Error).message ?? e).slice(0, 120)})`;\n  }\n}\n\n/**\n * Report-only variant used by `pai worker pane <id> --check`: whether a pane\n * runs for the worker, the bounds of the window hosting the asking session,\n * plus the dynamic profile's path, its existence, and the font it contains\n * (or, when missing, would write).\n */\nexport async function checkPaneForWorker(wid: string, fontSize: number, term: string): Promise<string> {\n  const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\n  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 uid = itermUuid(term);\n  const p = await osascript(TAB_TTYS_SCRIPT, [uid]);\n  const out = p.stdout.trim();\n  if (out === \"notfound\") throw new Error(\"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\");\n  if (out === \"notrunning\") throw new Error(\"pai worker pane: iTerm2 is not running\");\n  const tabTtys = new Set(out.split(\",\").map((t) => t.trim()).filter(Boolean));\n  const overlap = [...tabTtys].filter((t) => followTtys().has(t));\n  if (overlap.length > 0) return \"follow pane already open\";\n  if (checkOnly) return \"no follow pane\";\n  // one exec-able line, no shell syntax — see followCommand()\n  const cmd = `${process.execPath} ${absoluteCliPath()} worker follow`;\n  const s = await osascript(SPLIT_SCRIPT, [uid, cmd]);\n  const sout = s.stdout.trim();\n  if (sout === \"opened\") return \"follow pane opened\";\n  if (sout === \"notfound\") throw new Error(\"pai worker pane: no open iTerm2 session matches ITERM_SESSION_ID\");\n  throw new Error(`pai worker pane: osascript failed: ${(s.stderr || sout).slice(0, 200)}`);\n}\n","/**\n * tree.ts — sub-workers: parent detection and the tree caps.\n *\n * A worker launched by another worker (the runner exports PAI_WORKER_ID in\n * every worker's environment) carries `parent` in its status, so the worker\n * forest is visible in `ps`, the status line and the ledger. Two caps keep\n * the tree from growing without bound, both under `workers.tree`:\n *\n *   - maxDepth (default 2): how deep sub-workers may nest. A top-level worker\n *     sits at depth 0; its children at 1; grandchildren at 2 — one level past\n *     the cap refuses with a clear message.\n *   - maxChildren (default 4): how many children of one parent may run at the\n *     same time. Finished children do not count; a planner works through its\n *     sub-tasks in waves of this size.\n *\n * Chain stages also carry `parent` (the chain id), but chains are not workers\n * and never get a status file — a parent without a status is not capped.\n */\n\nimport type { WorkersTreeConfig } from \"./config.js\";\nimport { isLive, loadStatus, loadStatuses, type WorkerStatus } from \"./status.js\";\n\n/** The env var the runner sets in every worker's environment. */\nexport const WORKER_ID_ENV = \"PAI_WORKER_ID\";\n\n/** The worker this process runs inside, when it runs inside one. */\nexport function parentFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n  const id = env[WORKER_ID_ENV];\n  return typeof id === \"string\" && id.trim() ? id.trim() : null;\n}\n\n/**\n * Depth of `id` in the worker forest: 0 for a top-level worker, 1 + the\n * parent's depth for a sub-worker. Parents without a status file (chain ids,\n * unknown ids) count as roots; a cycle reads as its own depth and is cut off\n * after the statuses it walked. `chatIsRoot` stops one level short of the\n * chat-pane tracker (origin \"chat\"): the status line treats the pane as the\n * bar itself, so the workers it spawned are its top level.\n */\nexport function workerDepth(\n  statuses: WorkerStatus[],\n  id: string,\n  opts?: { chatIsRoot?: boolean }\n): number {\n  const byId = new Map(statuses.map((s) => [s.id, s]));\n  let depth = 0;\n  let cur = byId.get(id);\n  const seen = new Set<string>([id]);\n  while (cur?.parent && !seen.has(cur.parent)) {\n    seen.add(cur.parent);\n    const next = byId.get(cur.parent);\n    if (!next) break; // chain id or stale parent: a root, not a level\n    if (!(opts?.chatIsRoot && next.origin === \"chat\")) depth += 1;\n    cur = next;\n  }\n  return depth;\n}\n\n/** Children of `parent` that are still running, oldest first. */\nexport function runningChildren(statuses: WorkerStatus[], parent: string): WorkerStatus[] {\n  return statuses.filter((s) => s.parent === parent && isLive(s));\n}\n\n/**\n * Would starting a child of `parent` stay within the caps? Throws with a\n * clear message when it would not; returns silently when `parent` has no\n * status file (a chain id or an unknown id — not a worker, not capped).\n */\nexport function assertChildAllowed(\n  logDir: string,\n  parent: string,\n  caps: WorkersTreeConfig,\n  statuses: WorkerStatus[] = loadStatuses(logDir)\n): void {\n  if (!statuses.some((s) => s.id === parent)) return;\n  const depth = workerDepth(statuses, parent);\n  if (depth + 1 > caps.maxDepth) {\n    throw new Error(\n      `worker tree: ${parent} sits at depth ${depth} and ` +\n        `workers.tree.maxDepth is ${caps.maxDepth} — it cannot start another level of sub-workers. ` +\n        `Hand the task up instead: pai worker handoff '{\"kind\":\"proposal\",\"text\":\"…\"}'`\n    );\n  }\n  const kids = runningChildren(statuses, parent);\n  if (kids.length >= caps.maxChildren) {\n    throw new Error(\n      `worker tree: ${parent} already has ${kids.length} running sub-workers ` +\n        `(${kids.map((k) => k.id).join(\", \")}) and workers.tree.maxChildren is ${caps.maxChildren} — ` +\n        `wait for one to finish, or raise the cap in the workers config`\n    );\n  }\n}\n\n/**\n * The parent a launch should record: an explicit one (chain stage) wins, else\n * the worker this process runs inside. Returns null for top-level runs.\n */\nexport function launchParent(explicit: string | undefined, env: NodeJS.ProcessEnv = process.env): string | null {\n  return explicit ?? parentFromEnv(env);\n}\n\n/** Does a status file exist for `id` (i.e. is it a worker rather than a chain)? */\nexport function isWorkerId(logDir: string, id: string): boolean {\n  return loadStatus(logDir, id) !== null;\n}\n","/**\n * operator.ts — talking to a running worker.\n *\n * Headless workers run with `--input-format stream-json` and their stdin held\n * open, so a run is a conversation: every line sent to the per-worker Unix\n * socket `<logDir>/<id>.sock` is forwarded to the child as a user message and\n * mirrored into the transcript as an `operator` event (rendered with a »\n * marker). Once the worker has finished its turn, stdin closes 2 s later\n * unless a new message arrives — after that, `say` refuses and `resume`\n * continues the same Claude session with the worker's context intact.\n */\n\nimport { createServer, connect, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadStatus, isLive } from \"./status.js\";\n\nexport function operatorSocketPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.sock`);\n}\n\n/**\n * The runner's side: listen on the worker socket, hand every received line to\n * `onLine`. Returns the server (close it when the run ends; the socket file is\n * unlinked on close, best effort).\n */\nexport function createOperatorServer(\n  logDir: string,\n  id: string,\n  onLine: (text: string) => void\n): import(\"node:net\").Server {\n  const path = operatorSocketPath(logDir, id);\n  try {\n    if (existsSync(path)) unlinkSync(path);\n  } catch {\n    // a stale socket from a crashed run must not block the new one\n  }\n  const server = createServer((socket: Socket) => {\n    let buf = \"\";\n    socket.on(\"data\", (chunk: Buffer) => {\n      buf += chunk.toString(\"utf8\");\n      let nl: number;\n      while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n        const line = buf.slice(0, nl).replace(/\\r$/, \"\");\n        buf = buf.slice(nl + 1);\n        if (line.trim()) onLine(line);\n        socket.write(\"ok\\n\");\n      }\n    });\n  });\n  server.listen(path);\n  server.on(\"close\", () => {\n    try {\n      if (existsSync(path)) unlinkSync(path);\n    } catch {\n      // already gone\n    }\n  });\n  return server;\n}\n\n/**\n * `pai worker say <id> \"<text>\"`: forward one line to a running worker.\n * Resolves \"ok\", rejects with a clear message when the worker is not running.\n */\nexport function sayToWorker(logDir: string, id: string, text: string, timeoutMs = 4000): Promise<string> {\n  const status = loadStatus(logDir, id);\n  if (!status) {\n    return Promise.reject(new Error(`no worker named \"${id}\"`));\n  }\n  if (!isLive(status)) {\n    return Promise.reject(\n      new Error(\n        `worker ${id} is not running (state: ${status.state}) — ` +\n          `continue it instead with: pai worker resume ${id} \"<text>\"`\n      )\n    );\n  }\n  const path = operatorSocketPath(logDir, id);\n  if (!existsSync(path)) {\n    return Promise.reject(\n      new Error(`worker ${id} has no operator socket (${path}) — it may predate this PAI version`)\n    );\n  }\n  return new Promise((resolve, reject) => {\n    const sock = connect(path);\n    const fail = (e: Error) => {\n      sock.destroy();\n      reject(new Error(`cannot talk to worker ${id}: ${e.message}`));\n    };\n    sock.setTimeout(timeoutMs, () => fail(new Error(\"timeout\")));\n    sock.once(\"error\", (e: Error) => fail(e));\n    sock.once(\"connect\", () => {\n      sock.write(text.replace(/\\n/g, \" \") + \"\\n\");\n    });\n    sock.once(\"data\", () => {\n      sock.end();\n      resolve(\"ok\");\n    });\n  });\n}\n","/**\n * handoff.ts — upward messages between workers.\n *\n * Coordination in the worker tree is upward only: a child appends a handoff\n * to `<logDir>/<parent>.inbox.jsonl` and (when the parent is a running\n * worker) the same text is said to it, so it lands in the parent's\n * conversation as `[handoff from <child>] …`. The parent's pane renders the\n * inbox as `◆` lines, `ps`/`worker_status` show an inbox count, and a child\n * that finishes delivers its structured report automatically as a\n * `kind: \"result\"` handoff. There is no downward or sideways path — telling\n * a worker something is the operator's job (`pai worker say`).\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { isLive, loadStatus } from \"./status.js\";\nimport { sayToWorker } from \"./operator.js\";\n\nexport const HANDOFF_KINDS = [\"proposal\", \"result\", \"question\", \"blocker\"] as const;\n\nexport type HandoffKind = (typeof HANDOFF_KINDS)[number];\n\nexport interface Handoff {\n  /** Sending worker id. */\n  from: string;\n  /** Receiving worker id (the parent). */\n  to: string;\n  kind: HandoffKind;\n  text: string;\n  /** Structured payload (a report, a proposal's fields, …). */\n  data?: Record<string, unknown>;\n  /** ISO stamp, attached on append. */\n  _ts?: string;\n}\n\n/** Where a worker's handoffs land: <logDir>/<id>.inbox.jsonl. */\nexport function inboxPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.inbox.jsonl`);\n}\n\nexport function isHandoffKind(v: unknown): v is HandoffKind {\n  return typeof v === \"string\" && (HANDOFF_KINDS as readonly string[]).includes(v);\n}\n\n/**\n * Normalize a raw parsed value into a Handoff, or explain what is missing.\n * `from`/`to` may be preset by the caller (the CLI fills them from the\n * environment); everything else must be present and well-formed.\n */\nexport function parseHandoff(\n  v: unknown,\n  preset: Partial<Pick<Handoff, \"from\" | \"to\">> = {}\n): Handoff {\n  if (typeof v !== \"object\" || v === null || Array.isArray(v)) {\n    throw new Error(\"handoff payload must be a JSON object\");\n  }\n  const o = v as Record<string, unknown>;\n  const from = typeof o.from === \"string\" && o.from ? o.from : preset.from;\n  const to = typeof o.to === \"string\" && o.to ? o.to : preset.to;\n  if (!from) throw new Error(`handoff needs \"from\" (the sending worker id)`);\n  if (!to) throw new Error(`handoff needs \"to\" (the parent worker id)`);\n  if (!isHandoffKind(o.kind)) {\n    throw new Error(`handoff \"kind\" must be one of: ${HANDOFF_KINDS.join(\", \")}`);\n  }\n  const text = typeof o.text === \"string\" ? o.text : \"\";\n  if (!text.trim()) throw new Error(`handoff needs a non-empty \"text\"`);\n  const data =\n    typeof o.data === \"object\" && o.data !== null && !Array.isArray(o.data)\n      ? (o.data as Record<string, unknown>)\n      : undefined;\n  return { from, to, kind: o.kind, text, ...(data ? { data } : {}) };\n}\n\n/** Append one handoff to its recipient's inbox, stamped now. */\nexport function appendHandoff(logDir: string, h: Handoff, now: Date = new Date()): void {\n  const path = inboxPath(logDir, h.to);\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  appendFileSync(path, JSON.stringify({ ...h, _ts: now.toISOString() }) + \"\\n\", \"utf8\");\n}\n\n/** Every handoff in a worker's inbox, oldest first; damaged lines skip. */\nexport function readInbox(logDir: string, id: string): Handoff[] {\n  const path = inboxPath(logDir, id);\n  if (!existsSync(path)) return [];\n  const out: Handoff[] = [];\n  for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n    if (!line.trim()) continue;\n    try {\n      out.push(JSON.parse(line) as Handoff);\n    } catch {\n      // a half-written line is not worth a crash; the rest still reads\n    }\n  }\n  return out;\n}\n\n/** How the say path prefixes a handoff so the parent model knows its source. */\nexport function handoffMessage(h: Pick<Handoff, \"from\" | \"kind\" | \"text\">): string {\n  return `[handoff from ${h.from}] (${h.kind}) ${h.text.replace(/\\s+/g, \" \").trim()}`;\n}\n\n/** True for the exact shape of handoffMessage() — the runner marks the mirror. */\nexport function isHandoffMessage(text: string): boolean {\n  return new RegExp(`^\\\\[handoff from \\\\S+\\\\] \\\\((${HANDOFF_KINDS.join(\"|\")})\\\\) `).test(text);\n}\n\n/** Test seam for deliverHandoff: the say path, overridable with a mock. */\nexport interface HandoffDeps {\n  say?: (id: string, text: string) => Promise<string>;\n}\n\n/**\n * Deliver one handoff: append it to the parent's inbox, then — when the\n * parent is a running worker with a live operator socket — say it so it\n * enters the parent's conversation. The say is best effort: a parent that is\n * busy, finished or gone still has the inbox line, and nothing here may make\n * a finishing child's exit path fail.\n */\nexport async function deliverHandoff(\n  logDir: string,\n  h: Handoff,\n  deps: HandoffDeps = {},\n  now: Date = new Date()\n): Promise<Handoff> {\n  appendHandoff(logDir, h, now);\n  const say = deps.say ?? ((id: string, text: string) => sayToWorker(logDir, id, text));\n  const parent = loadStatus(logDir, h.to);\n  if (parent && isLive(parent)) {\n    try {\n      await say(h.to, handoffMessage(h));\n    } catch {\n      // the inbox line is the durable record; a failed say is not an error\n    }\n  }\n  return h;\n}\n\n/**\n * Send a handoff from inside a worker (the `pai worker handoff` CLI and the\n * MCP tool both land here): the sender comes from PAI_WORKER_ID, the\n * recipient from its status file's `parent`. Rejects with a clear message\n * outside a worker or under a parentless worker.\n */\nexport async function handoffFromInside(\n  logDir: string,\n  env: NodeJS.ProcessEnv,\n  payload: unknown,\n  deps: HandoffDeps = {}\n): Promise<Handoff> {\n  const mine = env.PAI_WORKER_ID;\n  if (!mine) {\n    throw new Error(\n      \"not inside a worker — handoffs are sent by workers (PAI_WORKER_ID unset). \" +\n        \"Talk to a running worker with: pai worker say <id> \\\"<text>\\\"\"\n    );\n  }\n  const st = loadStatus(logDir, mine);\n  if (!st) throw new Error(`no status for this worker (${mine}) — cannot find its parent`);\n  if (!st.parent || !loadStatus(logDir, st.parent)) {\n    throw new Error(\n      `worker ${mine} has no worker parent to hand off to ` +\n        `(parent: ${st.parent ?? \"(none)\"}) — handoffs go up the worker tree only`\n    );\n  }\n  const h = parseHandoff(payload, { from: mine, to: st.parent });\n  return deliverHandoff(logDir, h, deps);\n}\n","/**\n * agentish.ts — Agentish v2 (AG2), the wire format a worker's final report\n * uses by default: a kind line plus one-letter k=v lines instead of JSON\n * prose. Spec and validator live in the `aibroker` CLI\n * (see /Users/i052341/Daten/Cloud/Development/ai/AIBroker/docs/agentish.md);\n * this module shells out to it when present and falls back to a frozen copy\n * of the spec text otherwise, so a worker still gets a contract when\n * `aibroker` is not installed on its box.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport type { WorkerReport } from \"./report.js\";\n\n/** Frozen copy of `aibroker agentish spec`'s first line, for when the CLI is absent. */\nexport const AG2_SPEC_FALLBACK =\n  \"AG2. msg=kind line+k=v lines. kinds T R S Q A X. keys i id g goal o own n forbid d steps p proof u out l limits r res c changes t tests G gate I inst m images # nums w worst x next z note(<200ch). sep |. outcomes + - ~ ? !. @n=path declared once then reused; @n:12=file:line. tests as Name+ Name-. no prose, no articles, never restate, unknown=?. r=+ only if all t +.\";\n\n/** Frozen copy of the `y` (why) extension line. */\nexport const AG2_EXTENSIONS_FALLBACK = \"y why(≤600ch)\";\n\ninterface Ag2SpecResult {\n  spec: string;\n  extensions: string;\n  source: \"aibroker\" | \"builtin\";\n}\n\nlet cachedSpec: Ag2SpecResult | null = null;\n\n/**\n * The AG2 spec + extensions lines: from `aibroker agentish spec` when the\n * CLI runs and its output looks right, else the frozen fallback. Memoised —\n * every caller in one process gets the same spec without re-spawning aibroker.\n */\nexport function ag2Spec(): Ag2SpecResult {\n  if (cachedSpec) return cachedSpec;\n  try {\n    const out = execFileSync(\"aibroker\", [\"agentish\", \"spec\"], {\n      timeout: 3000,\n      stdio: [\"ignore\", \"pipe\", \"ignore\"],\n      encoding: \"utf8\",\n    });\n    const lines = out.split(\"\\n\").filter((l) => l.trim());\n    if (lines[0]?.startsWith(\"AG2.\")) {\n      cachedSpec = { spec: lines[0], extensions: lines[1] ?? AG2_EXTENSIONS_FALLBACK, source: \"aibroker\" };\n      return cachedSpec;\n    }\n  } catch {\n    // aibroker missing, not runnable, or timed out — fall through\n  }\n  cachedSpec = { spec: AG2_SPEC_FALLBACK, extensions: AG2_EXTENSIONS_FALLBACK, source: \"builtin\" };\n  return cachedSpec;\n}\n\n/** Only for tests: drop the memoised spec so the next ag2Spec() re-probes. */\nexport function resetAg2SpecCache(): void {\n  cachedSpec = null;\n}\n\nexport interface ParsedAg2 {\n  kind: string;\n  fields: Record<string, string>;\n  symbols: Record<string, string>;\n}\n\n/**\n * Parse one AG2 message: first non-empty line is the kind (T R S Q A X,\n * optionally followed by trailing text — only the first character is the\n * kind), later lines are `key=value` or `@n=path` symbol declarations.\n * ```-fenced lines are ignored so a message pasted inside a code fence still\n * parses. Returns null when the first non-empty line is not a known kind.\n */\nexport function parseAg2Report(text: string): ParsedAg2 | null {\n  const kinds = new Set([\"T\", \"R\", \"S\", \"Q\", \"A\", \"X\"]);\n  const lines = (text ?? \"\")\n    .split(\"\\n\")\n    .map((l) => l.trim())\n    .filter((l) => l && !l.startsWith(\"```\"));\n  if (!lines.length) return null;\n  const kind = lines[0][0];\n  if (!kinds.has(kind)) return null;\n\n  const fields: Record<string, string> = {};\n  const symbols: Record<string, string> = {};\n  for (const line of lines.slice(1)) {\n    const eq = line.indexOf(\"=\");\n    if (eq < 0) continue;\n    const key = line.slice(0, eq).trim();\n    const value = line.slice(eq + 1).trim();\n    if (key.startsWith(\"@\")) {\n      symbols[key.slice(1)] = value;\n    } else {\n      fields[key] = value;\n    }\n  }\n  return { kind, fields, symbols };\n}\n\n/** true iff the value ends with the AG2 pass outcome character. */\nfunction passed(entry: string): boolean {\n  return entry.endsWith(\"+\");\n}\n\n/**\n * Map a parsed AG2 report onto the runner's WorkerReport shape, so the\n * viewer and `printResult` can render an AG2 `R` exactly like a JSON one.\n */\nexport function ag2ToWorkerReport(parsed: ParsedAg2): WorkerReport {\n  const f = parsed.fields;\n  const out: WorkerReport = { format: \"ag2\" };\n\n  if (f.c) {\n    out.changed = f.c\n      .split(\"|\")\n      .map((s) => s.trim())\n      .filter(Boolean)\n      .map((entry) => {\n        const colon = entry.indexOf(\":\");\n        const space = entry.indexOf(\" \");\n        const cut = colon >= 0 && (space < 0 || colon < space) ? colon : space;\n        if (cut < 0) return { path: entry, summary: \"\" };\n        return { path: entry.slice(0, cut).trim(), summary: entry.slice(cut + 1).trim() };\n      });\n  }\n\n  if (f.t) {\n    out.checks = f.t\n      .split(/\\s+/)\n      .filter(Boolean)\n      .map((entry) => ({\n        name: entry.slice(0, -1),\n        ok: passed(entry),\n        detail: entry.slice(-1),\n      }));\n  }\n\n  if (f.p) out.commands = f.p.split(\"|\").map((s) => s.trim()).filter(Boolean);\n\n  const open: string[] = [];\n  if (f.x) open.push(f.x);\n  for (const [k, v] of Object.entries(f)) {\n    if ((v.endsWith(\"!\") || v.endsWith(\"?\")) && k !== \"r\" && k !== \"res\") open.push(`${k}: ${v}`);\n  }\n  if (open.length) out.open = open;\n\n  if (f.z) out.notes = f.z;\n  const res = f.r ?? f.res;\n  if (res === \"+\" || res === \"-\" || res === \"~\" || res === \"?\" || res === \"!\") out.result = res;\n  if (f.y) out.why = f.y;\n\n  return out;\n}\n\nexport interface Ag2ValidationResult {\n  ok: boolean;\n  errors: string[];\n  validator: \"aibroker\" | \"none\";\n}\n\ninterface AibrokerCheckJson {\n  ok?: boolean;\n  errors?: Array<{ code?: string; message?: string; line?: number }>;\n}\n\n/**\n * Validate an AG2 message with `aibroker agentish check - --json`. Never\n * fails the caller: a missing/broken CLI reports ok:true, validator \"none\" —\n * a worker's run is never blocked on a local tooling gap, only its\n * `reportValid` marker goes unset.\n */\nexport function validateAg2(text: string): Ag2ValidationResult {\n  try {\n    const out = execFileSync(\"aibroker\", [\"agentish\", \"check\", \"-\", \"--json\"], {\n      input: text,\n      timeout: 5000,\n      stdio: [\"pipe\", \"pipe\", \"ignore\"],\n      encoding: \"utf8\",\n    });\n    const parsed = JSON.parse(out) as AibrokerCheckJson;\n    const errors = (parsed.errors ?? []).map((e) => e.message ?? e.code ?? \"unknown error\");\n    return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n  } catch (e) {\n    // execFileSync throws on non-zero exit too — stdout still carries the JSON\n    const out = (e as { stdout?: Buffer | string }).stdout;\n    if (out) {\n      try {\n        const parsed = JSON.parse(out.toString()) as AibrokerCheckJson;\n        const errors = (parsed.errors ?? []).map((er) => er.message ?? er.code ?? \"unknown error\");\n        return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n      } catch {\n        // fall through to \"no validator\" below\n      }\n    }\n    return { ok: true, errors: [], validator: \"none\" };\n  }\n}\n","/**\n * report.ts — the worker contract: terse, structured final reports.\n *\n * Headless workers run with an appended system prompt that fixes their output\n * shape: act, verify, then finish with ONE final message — AG2 (see\n * agentish.ts) by default, JSON as the pre-AG2 fallback. The runner parses it\n * out of the result; the viewer renders it as a compact block instead of a\n * wall of text.\n */\n\nimport { relative } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport type { Paint } from \"./render.js\";\nimport { ag2Spec, ag2ToWorkerReport, parseAg2Report } from \"./agentish.js\";\n\n/** The marker the runner puts on operator messages the worker must answer. */\nexport const OPERATOR_MARK = \"[operator]\";\n\n/** Classes that get the short contract — cheap, bounded, single-answer tasks. */\nconst SHORT_CONTRACT_CLASSES = new Set([\"spotcheck\", \"simple\"]);\n\n/**\n * The AG2 report block appended to a contract when format is \"ag2\" — field\n * set matches `aibroker agentish check`'s real R schema (verified 2026-09-20:\n * i/r/t/c are required, not optional as an early draft of this contract had\n * it; r=+ needs G=+ and non-empty p too, not just all-t-passing; R has no\n * `u`/out field at all — u is T-only — so the answer goes in z instead).\n */\nfunction ag2ReportBlock(): string[] {\n  const spec = ag2Spec();\n  return [\n    spec.spec,\n    spec.extensions,\n    \"Final message: one AG2 `R` message only — no prose, no code fence, no markdown.\",\n    \"R requires i (id), r (+ - ~ ? !), t (tests, Name+ Name-), c (path summary|path summary).\",\n    \"r=+ requires G=+ (gate) and p non-empty (proof command), and every t entry +. z= the answer/note,\",\n    \"one line, always present. x= what is left (omit if nothing). y= one line why when r is not +.\",\n    \"Example:\",\n    \"R\",\n    \"i=count-audit-ts\",\n    \"r=+\",\n    \"G=+\",\n    \"c=src/audit summary of files counted\",\n    \"t=Count+\",\n    \"p=wc -l src/audit/*.ts\",\n    \"z=19 files, 2523 lines\",\n  ];\n}\n\n/** The JSON report block appended to a contract when format is \"json\" (pre-AG2 fallback). */\nfunction jsonReportBlock(lead: string): string[] {\n  return [\n    lead,\n    '{\"changed\":[{\"path\":\"…\",\"summary\":\"…\"}],\"commands\":[\"…\"],\"checks\":[{\"name\":\"…\",\"ok\":true,\"detail\":\"…\"}],\"open\":[\"…\"],\"notes\":\"one line\"}',\n  ];\n}\n\n/** The two sentences that stop a worker from breaking on shell quoting — kept in both contracts. */\nfunction fileNotInlineBlock(): string[] {\n  return [\n    \"NEVER put a multi-line or quoted payload inline in a shell command — no heredocs (<<EOF), no long\",\n    \"-p '…' specs for child workers, no inline JSON, no inline scripts. ALWAYS write it to a file with\",\n    \"the Write tool first and pass the file: -p \\\"$(cat spec.txt)\\\", --file spec.txt, or < spec.txt.\",\n    \"Inline quoting fails silently or dies with a parse error and burns a worker launch; there are no\",\n    \"exceptions.\",\n    \"Never inline code one-liners (python3 -c, node -e, ruby -e and friends) — the permission layer\",\n    \"denies them (\\\"ztk: command denied by permission rules\\\"); write the script to a file with the\",\n    \"Write tool and run it (python3 script.py), or use jq for JSON inspection.\",\n  ];\n}\n\n/** The file-not-inline rule, condensed for the short contract's tight length budget. */\nfunction fileNotInlineBlockShort(): string[] {\n  return [\n    \"NEVER put a multi-line or quoted payload inline in a shell command (no heredocs, no inline\",\n    \"scripts): write it to a file with the Write tool and pass it, e.g. -p \\\"$(cat spec.txt)\\\".\",\n    \"Never inline code (python3 -c and friends) — write a script file and run it, or use jq.\",\n  ];\n}\n\n/** spotcheck/simple: headless, bounded, no delegation language — under 1500 chars in ag2 format. */\nfunction shortContractPrompt(format: \"ag2\" | \"json\"): string {\n  const lines = [\n    \"You are a headless implementation worker, run non-interactively.\",\n    \"This task is yours: answer the question or do the bounded task now. Run at most one command\",\n    \"to confirm a result and never re-derive a number you already have. No child workers, no handoff.\",\n    ...fileNotInlineBlockShort(),\n  ];\n  lines.push(\n    ...(format === \"ag2\"\n      ? ag2ReportBlock()\n      : jsonReportBlock(\n          \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n        ))\n  );\n  return lines.join(\"\\n\");\n}\n\n/** Every other class (and no class): the full worker/orchestrator contract. */\nfunction fullContractPrompt(format: \"ag2\" | \"json\"): string {\n  const lines = [\n    \"You are a headless implementation worker, run non-interactively by an orchestrating session.\",\n    \"You are the worker: this task is yours to finish in this process. Never hand your whole task to a\",\n    \"single child worker and step back — that is delegation, not work. Child workers are for two things\",\n    \"only: (1) PARALLELISING independent parts of your task, each part a self-contained spec; (2) CHEAPER\",\n    \"bounded sub-tasks — probes, renders, test runs, lookups — run one tier down with --class spotcheck or\",\n    \"--class simple. Always pin --provider anthropic on children.\",\n    \"You own your children's results: run them in the foreground or poll until each has finished, read\",\n    \"their result, verify it, and fold it into your own report. When your turn ends this run ends and any\",\n    \"running child is killed, so never end with a child still running.\",\n    \"Ending your turn ends this run. There is no later wake-up: never call ScheduleWakeup, never say you\",\n    \"will pick something up later, never leave a background command as your final action. Finish, then\",\n    \"report.\",\n    \"No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.\",\n    ...fileNotInlineBlock(),\n    \"\",\n    `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,\n    \"run (the prompt of a resumed run is the operator's too). Answer it FIRST, in one or two plain lines —\",\n    \"a question gets its answer, an instruction gets one line naming what will change — then continue the\",\n    \"task you were on.\",\n    \"\",\n    \"Sub-workers: you may start your own workers with `pai worker run --class <class> -p '<prompt>'`\",\n    \"(Bash tool). Your children run on their own provider and report back to you automatically when\",\n    \"they finish. Send a handoff UP instead of doing work yourself when a cheaper provider would\",\n    \"suffice, the task is out of your scope, or a decision is needed:\",\n    \"`pai worker handoff '{\\\"kind\\\":\\\"proposal\\\",\\\"text\\\":\\\"…\\\",\\\"data\\\":{…}}'` (kinds: proposal,\",\n    \"question, blocker; results are sent for you when you finish). Sibling workers are not a\",\n    \"coordination path — there is no sideways channel; everything goes up to your parent.\",\n    \"\",\n  ];\n  lines.push(\n    ...(format === \"ag2\"\n      ? ag2ReportBlock()\n      : jsonReportBlock(\n          \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n        ))\n  );\n  if (format === \"json\") {\n    lines.push(\n      \"changed: files you touched (path + one-line summary). commands: the commands that verify the work.\",\n      \"checks: each with ok true/false and the evidence in detail. open: what you could not finish, if anything.\",\n      \"notes: one line, the headline a reviewer reads first.\"\n    );\n  }\n  return lines.join(\"\\n\");\n}\n\n/**\n * The system prompt appended to every headless run, chosen by task class and\n * report format. `spotcheck`/`simple` get a short contract (no delegation\n * language, under 1200 chars in json format / 1500 in ag2); every other class (and no class) gets the\n * full contract. Both end with the AG2 report block unless `format` is\n * \"json\", the pre-AG2 fallback.\n */\nexport function workerContractPrompt(cls: string | undefined, format: \"ag2\" | \"json\"): string {\n  return SHORT_CONTRACT_CLASSES.has(cls ?? \"\") ? shortContractPrompt(format) : fullContractPrompt(format);\n}\n\n/**\n * The line appended to a headless prompt's own text (never the system\n * prompt): models weight an instruction at the end of the user turn more\n * than one buried in a long system-prompt paragraph, and this is the\n * difference between a worker that returns a valid AG2/JSON report and one\n * that answers in prose despite the contract (measured 2026-09-20 on both\n * Haiku and Sonnet with the AG2 contract alone).\n */\nexport function promptTrailer(format: \"ag2\" | \"json\"): string {\n  return format === \"ag2\"\n    ? \"\\n\\nFinal message: one AG2 R message only, no prose (format in system prompt).\"\n    : \"\\n\\nFinal message: a JSON object only, no prose before or after, no code fence (format in system prompt).\";\n}\n\n/** The exact text sent to resend a final message that failed AG2 validation. */\nexport const AG2_REASK_TEXT =\n  \"Your final message was not a valid AG2 R message. Resend it now as a single AG2 R message only: \" +\n  \"first line R, then i=id r=result G=+ (gate, if r=+) t=tests(Name+) c=path summary p=proof z=the \" +\n  \"answer/note. No prose.\";\n\n/** The full-contract AG2 prompt — the keepalive path's identical-construction baseline. */\nexport const WORKER_CONTRACT_PROMPT = fullContractPrompt(\"ag2\");\n\nexport interface WorkerReport {\n  changed?: Array<{ path?: string; summary?: string }>;\n  commands?: string[];\n  checks?: Array<{ name?: string; ok?: boolean; detail?: string }>;\n  open?: string[];\n  notes?: string;\n  /** AG2 `r` (res): the single outcome character, when the report was AG2. */\n  result?: \"+\" | \"-\" | \"~\" | \"?\" | \"!\";\n  /** AG2 `y` (why): one line explaining a non-\"+\" result. */\n  why?: string;\n  /** Which wire format the final message was parsed as. */\n  format?: \"ag2\" | \"json\";\n}\n\n/**\n * Extract the report from a worker's final message: AG2 first (an `R`\n * message, see agentish.ts), then the JSON contract as a fallback for\n * workers still on the pre-AG2 shape. Accepts a bare JSON object, a\n * ```json fenced block, or an object embedded in surrounding text — only\n * objects that look like the contract count (at least one of changed /\n * checks / notes) — any other JSON falls through to null so the raw text is\n * kept as-is.\n */\nexport function parseWorkerReport(text: string): WorkerReport | null {\n  const trimmed = (text ?? \"\").trim();\n  if (!trimmed) return null;\n\n  const ag2 = parseAg2Report(trimmed);\n  if (ag2 && ag2.kind === \"R\") return ag2ToWorkerReport(ag2);\n\n  const candidates: string[] = [];\n  const fence = trimmed.match(/```(?:json)?\\s*(\\{[\\s\\S]*?\\})\\s*```/);\n  if (fence) candidates.push(fence[1]);\n  if (trimmed.startsWith(\"{\")) {\n    // a bare object may still carry trailing punctuation/newlines\n    candidates.push(trimmed);\n  }\n  // object embedded in prose: first \"{\" to the matching last \"}\"\n  const first = trimmed.indexOf(\"{\");\n  const last = trimmed.lastIndexOf(\"}\");\n  if (first >= 0 && last > first) candidates.push(trimmed.slice(first, last + 1));\n  for (const cand of candidates) {\n    try {\n      const v = JSON.parse(cand) as unknown;\n      if (typeof v === \"object\" && v !== null && !Array.isArray(v) && looksLikeReport(v)) {\n        return { ...(v as WorkerReport), format: \"json\" };\n      }\n    } catch {\n      // try the next candidate\n    }\n  }\n  return null;\n}\n\n/** The contract's fingerprint — guards against unrelated JSON in a reply. */\nfunction looksLikeReport(v: object): boolean {\n  const o = v as WorkerReport;\n  return Array.isArray(o.changed) || Array.isArray(o.checks) || typeof o.notes === \"string\";\n}\n\n/**\n * Render a parsed report as the compact block the viewer shows: changed paths\n * with summaries, checks with ✓/✗, open items, notes. Empty report → [].\n */\nexport function renderReport(c: Paint, prefix: string, r: WorkerReport, cwd = \"\"): string[] {\n  const out: string[] = [];\n  const has = (xs: unknown[] | undefined) => (xs ?? []).length > 0;\n  if (has(r.changed)) {\n    out.push(`${prefix}${c(\"bold\", \"changed\")}`);\n    for (const ch of r.changed!) {\n      out.push(`${prefix}  ${relShort(ch.path ?? \"?\", cwd)} — ${shortText(ch.summary ?? \"\", 90)}`);\n    }\n  }\n  if (has(r.checks)) {\n    out.push(`${prefix}${c(\"bold\", \"checks\")}`);\n    for (const ck of r.checks!) {\n      const mark = ck.ok === false ? c(\"red\", \"✗\") : c(\"green\", \"✓\");\n      const detail = ck.detail ? c(\"dim\", ` · ${shortText(ck.detail, 80)}`) : \"\";\n      out.push(`${prefix}  ${mark} ${ck.name ?? \"?\"}${detail}`);\n    }\n  }\n  if (has(r.commands)) {\n    out.push(`${prefix}${c(\"bold\", \"commands\")}`);\n    for (const cmd of r.commands!) out.push(`${prefix}  ${c(\"dim\", shortText(cmd, 100))}`);\n  }\n  if (has(r.open)) {\n    out.push(`${prefix}${c(\"bold\", \"open\")}`);\n    for (const o of r.open!) out.push(`${prefix}  ${c(\"yellow\", shortText(o, 100))}`);\n  }\n  if (r.notes) out.push(`${prefix}${c(\"bold\", \"notes\")}  ${shortText(r.notes, 120)}`);\n  return out;\n}\n\n/** repo-relative display of a changed path when it lies under cwd. */\nfunction relShort(p: string, cwd: string): string {\n  if (!cwd) return p;\n  const r = relative(cwd, p);\n  return r && !r.startsWith(\"..\") ? r : p;\n}\n","/**\n * 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 { openRegistry, registryDbPath } from \"../registry/db.js\";\n\nexport interface ProjectLaunchConfig {\n  mcp?: string[];\n  tools?: string[];\n}\n\ninterface ConfigRow {\n  root_path: string;\n  session_config: string | null;\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. `dbPath` is\n * injectable for tests; production callers take the default.\n */\nexport function projectLaunchConfig(\n  cwd: string,\n  dbPath: string = registryDbPath()\n): ProjectLaunchConfig | null {\n  const target = resolve(cwd);\n  const db = openRegistry(dbPath);\n  try {\n    const rows = db\n      .prepare(\n        `SELECT root_path, session_config FROM projects WHERE status != 'archived' ORDER BY LENGTH(root_path) DESC`\n      )\n      .all() as ConfigRow[];\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 ? { mcp, tools } : null;\n    }\n    return null;\n  } finally {\n    db.close();\n  }\n}\n","/**\n * codex.ts — the \"codex\" runner engine.\n *\n * A ChatGPT plan gives no API key, only Codex CLI access, so a provider may\n * set `engine: \"codex\"`: `run` then shells out to `codex exec --json <prompt>`\n * (non-interactive) instead of Claude Code. The JSONL event stream is parsed\n * into the same status-file fields (turns, tools, last) and ledger lines as a\n * Claude run, and is normalised into claude-code-shaped events in the\n * worker's .jsonl so follow/replay/the pane render it unchanged.\n *\n * Implemented against the documented `codex exec --json` interface\n * (thread.started / item.completed / turn.completed / turn.failed lines);\n * verify against the installed CLI when one is present.\n * `--allowedTools` has no Codex equivalent and is dropped (ledgered).\n */\n\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { providerKeyPath, resolveProviderKey, type WorkerProvider } from \"./config.js\";\n\n/** Build the codex exec argument vector (without the binary itself). */\nexport function buildCodexArgs(prompt: string, model: string | undefined): string[] {\n  return [\n    \"exec\",\n    \"--json\",\n    \"--skip-git-repo-check\",\n    ...(model ? [\"-m\", model] : []),\n    \"--\",\n    prompt,\n  ];\n}\n\n/** Env for a codex run: caller's env, Anthropic vars stripped, key applied. */\nexport function buildCodexEnv(provider: WorkerProvider): NodeJS.ProcessEnv {\n  const env: NodeJS.ProcessEnv = { ...process.env };\n  delete env.ANTHROPIC_API_KEY;\n  delete env.ANTHROPIC_BASE_URL;\n  delete env.ANTHROPIC_AUTH_TOKEN;\n  delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;\n  delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;\n  delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;\n  // API-key providers: OpenAI env from key/keyFile/baseUrl; ChatGPT-login\n  // codex (neither set) keeps its own auth from ~/.codex. A key_file that\n  // does not exist is tolerated here (unlike resolveProviderKey's other\n  // callers) — that is the ChatGPT-login shape, not a broken config.\n  const keyPath = providerKeyPath(provider);\n  if (provider.key || (keyPath && existsSync(keyPath))) {\n    env.OPENAI_API_KEY = resolveProviderKey(provider) ?? \"\";\n  }\n  if (provider.upstreamUrl) env.OPENAI_BASE_URL = provider.upstreamUrl;\n  for (const [k, v] of Object.entries(provider.env)) env[k] = v;\n  env.PAI_WORKER = \"1\";\n  return env;\n}\n\n/** Codex \"not applicable\" flags the runner drops with a ledger note. */\nexport function codexDroppedFlags(argv: string[]): string[] {\n  const dropped: string[] = [];\n  for (let i = 0; i < argv.length; i++) {\n    const a = argv[i];\n    if (a === \"--allowedTools\" || a === \"--disallowedTools\" || a === \"--mcp-config\" || a === \"--mcp\") {\n      dropped.push(a);\n      if (i + 1 < argv.length && !argv[i + 1].startsWith(\"-\")) i++; // and its value\n    } else if (a.startsWith(\"--allowedTools=\") || a.startsWith(\"--mcp-config=\")) {\n      dropped.push(a.split(\"=\")[0]);\n    }\n  }\n  return dropped;\n}\n\n// ---------------------------------------------------------------------------\n// Event stream parsing (pure — tested with recorded JSONL lines)\n// ---------------------------------------------------------------------------\n\n/** One normalised claude-code-shaped event + its effect on the status. */\nexport interface CodexEventResult {\n  events: Array<Record<string, unknown>>;\n  turns: number;\n  tools: number;\n  last: string | null;\n  isError: boolean;\n  contextTokens: number | null;\n  finalText: string | null;\n  threadId: string | null;\n}\n\nexport const emptyCodexResult = (): CodexEventResult => ({\n  events: [],\n  turns: 0,\n  tools: 0,\n  last: null,\n  isError: false,\n  contextTokens: null,\n  finalText: null,\n  threadId: null,\n});\n\n/**\n * Fold one parsed `codex exec --json` line into the running result: appends\n * normalised transcript events and updates the counters in place.\n */\nexport function foldCodexLine(line: unknown, r: CodexEventResult): void {\n  if (typeof line !== \"object\" || line === null) return;\n  const e = line as Record<string, unknown>;\n  const type = typeof e.type === \"string\" ? e.type : \"\";\n\n  if (type === \"thread.started\" && typeof e.thread_id === \"string\") {\n    r.threadId = e.thread_id;\n    return;\n  }\n  if (type === \"item.completed\" && typeof e.item === \"object\" && e.item !== null) {\n    const item = e.item as Record<string, unknown>;\n    const kind = typeof item.type === \"string\" ? item.type : \"\";\n    if (kind === \"agent_message\" && typeof item.text === \"string\") {\n      r.turns += 1;\n      r.last = `says: ${item.text.slice(0, 70)}`;\n      r.finalText = item.text;\n      r.events.push({\n        type: \"assistant\",\n        message: { content: [{ type: \"text\", text: item.text }] },\n      });\n    } else if (kind === \"command_execution\") {\n      r.tools += 1;\n      const cmd = typeof item.command === \"string\" ? item.command : \"?\";\n      const rc = typeof item.exit_code === \"number\" ? item.exit_code : 0;\n      r.last = `Bash: ${cmd.slice(0, 70)}`;\n      r.events.push({\n        type: \"assistant\",\n        message: { content: [{ type: \"tool_use\", name: \"Bash\", input: { command: cmd } }] },\n      });\n      r.events.push({\n        type: \"user\",\n        message: {\n          content: [\n            {\n              type: \"tool_result\",\n              tool_use_id: \"\",\n              is_error: rc !== 0,\n              content: item.aggregated_output ?? \"\",\n            },\n          ],\n        },\n      });\n    } else if (kind === \"file_change\") {\n      r.tools += 1;\n      const changes = Array.isArray(item.changes) ? item.changes : [];\n      const files = changes\n        .map((c) => (typeof c === \"object\" && c !== null && typeof (c as { path?: unknown }).path === \"string\" ? (c as { path: string }).path : \"?\"))\n        .join(\", \");\n      r.last = `files: ${files.slice(0, 70)}`;\n      r.events.push({\n        type: \"assistant\",\n        message: { content: [{ type: \"tool_use\", name: \"Write\", input: { file_path: files } }] },\n      });\n    } else if (kind === \"mcp_tool_call\") {\n      r.tools += 1;\n      r.last = `mcp: ${String(item.tool ?? \"?\")}`;\n    }\n    return;\n  }\n  if (type === \"turn.completed\") {\n    const usage = (typeof e.usage === \"object\" && e.usage !== null ? e.usage : {}) as {\n      input_tokens?: number;\n      output_tokens?: number;\n      cached_input_tokens?: number;\n    };\n    const tokens =\n      (usage.input_tokens ?? 0) + (usage.cached_input_tokens ?? 0) + (usage.output_tokens ?? 0);\n    if (tokens > 0) r.contextTokens = tokens;\n    return;\n  }\n  if (type === \"turn.failed\" || type === \"error\") {\n    r.isError = true;\n    const err = typeof e.error === \"object\" && e.error !== null ? e.error : {};\n    r.last = String((err as { message?: unknown }).message ?? e.message ?? \"codex turn failed\");\n  }\n}\n\n/** Parse one JSONL line; null for blanks and non-JSON noise. */\nexport function parseCodexLine(line: string): unknown | null {\n  const t = line.trim();\n  if (!t.startsWith(\"{\")) return null;\n  try {\n    return JSON.parse(t) as unknown;\n  } catch {\n    return null;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// The engine probe used by `providers test`\n// ---------------------------------------------------------------------------\n\n/** True when the Codex CLI is on PATH (test reports \"not installed\" else). */\nexport function codexInstalled(): boolean {\n  try {\n    execFileSync(\"codex\", [\"--version\"], { timeout: 5000, stdio: \"ignore\" });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/** Spawn helper shared by run and test; caller wires stdout. */\nexport function spawnCodex(args: string[], env: NodeJS.ProcessEnv, cwd: string) {\n  return spawn(\"codex\", args, { env, cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n}\n","/**\n * run.ts — the worker runner (port of the glm / glm-run pair, provider-neutral).\n *\n * One claude-code process per call, pointed at the chosen provider:\n *\n *   - env: ANTHROPIC_BASE_URL/AUTH_TOKEN from the provider (token from its\n *     key file, never from the environment), the three DEFAULT_*_MODEL vars,\n *     the provider's extra env, nonessential traffic off, and ANTHROPIC_API_KEY\n *     stripped so nothing can fall back to Anthropic billing. Headless runs\n *     also drop the spawner's session identity (messaging socket, session id,\n *     nesting markers) — inherited, a worktree child starts tool-blind.\n *     OpenAI-protocol providers point at the PAI proxy instead (started on\n *     demand, the provider name in the URL path); codex-engine providers run\n *     the Codex CLI.\n *   - headless (-p): strict empty MCP config unless the caller brings one or\n *     names servers via --mcp / a role (then a filtered <id>.mcp.json), the\n *     core tool grants the caller did not bring (a headless run cannot\n *     approve a permission-gated tool mid-flight — with no grant at all,\n *     claude drops the file/shell tools entirely and the worker is\n *     tool-blind), PAI_WORKER=1 so PAI's per-session hooks leave it alone,\n *     the worker\n *     contract appended to the system prompt, `--input-format stream-json`\n *     with the prompt as the first stdin user message (the operator socket\n *     can add more mid-run), stream-json mirroring (every line stamped `_ts`)\n *     into <logDir>/<id>.jsonl, a live <id>.status for ps/follow/status line\n *     (context meter included), ledger lines, result printed in the caller's\n *     --output-format (json adds the parsed `report`), and the follow pane\n *     (unless --no-pane).\n *   - interactive: no MCP/tool restriction by default, no pane,\n *     ENABLE_TOOL_SEARCH=true. A project that pinned a list with `pai\n *     project mcp` / `pai project tools` gets `--strict-mcp-config\n *     --mcp-config <filtered>` / `--tools <list>` built the same way a\n *     headless run's allowlist is; an explicit --mcp or --tools on this\n *     command line still wins over the project's pin.\n *\n * Auto-routed runs that die of a quota error before the first tool call are\n * restarted on the next provider in routing order (WORKER-REROUTE ledger line).\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport {\n  existsSync,\n  mkdirSync,\n  openSync,\n  writeFileSync,\n  closeSync,\n  writeSync,\n} from \"node:fs\";\nimport { parseRunnerArgs, shortText, stripPromptValues } from \"./args.js\";\nimport {\n  assertProviderRunnable,\n  classModelCapability,\n  isModelCapability,\n  readWorkersSection,\n  resolveModelCapability,\n  type WorkerProvider,\n} from \"./config.js\";\nimport { buildRunEnv } from \"./run-env.js\";\n\nexport { buildRunEnv } from \"./run-env.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport {\n  ensureNoMcpConfig,\n  eventsPath,\n  ledgerPath,\n  noMcpConfigPath,\n  workersLogDir,\n} from \"./paths.js\";\nimport {\n  type WorkerStatus,\n  newWorkerId,\n  saveStatus,\n  describeTool,\n  nowStamp,\n  UNLABELED,\n} from \"./status.js\";\nimport { resolveSession, resolveSpawnerSession } from \"./scope.js\";\nimport {\n  capabilityForRun,\n  isQuotaFailure,\n  nextAutoProvider,\n  resolveCapabilityRun,\n  resolveTarget,\n  setCooldown,\n  type CapabilityRunResolution,\n} from \"./routing.js\";\nimport { runImageCapability } from \"./engines/image.js\";\nimport { openPaneForWorker } from \"./pane.js\";\nimport { assertChildAllowed, isWorkerId, launchParent } from \"./tree.js\";\nimport { deliverHandoff, isHandoffMessage } from \"./handoff.js\";\nimport {\n  addWorktree,\n  recordWorktree,\n  worktreeSystemPrompt,\n  worktreeWanted,\n  type WorktreeInfo,\n} from \"./worktree.js\";\nimport {\n  OPERATOR_MARK,\n  AG2_REASK_TEXT,\n  promptTrailer,\n  workerContractPrompt,\n  parseWorkerReport,\n  type WorkerReport,\n} from \"./report.js\";\nimport { validateAg2 } from \"./agentish.js\";\nimport { expandMcpNames, grantsChrome, mcpServersFromToolGrants, writeMcpConfig } from \"./mcp.js\";\nimport { projectLaunchConfig, type ProjectLaunchConfig } from \"./project-config.js\";\nimport { createOperatorServer } from \"./operator.js\";\nimport { DEFAULT_PROXY_PORT, ensureProxyRunning } from \"./proxy/server.js\";\nimport {\n  buildCodexArgs,\n  buildCodexEnv,\n  codexDroppedFlags,\n  codexInstalled,\n  emptyCodexResult,\n  foldCodexLine,\n  parseCodexLine,\n} from \"./codex.js\";\n\nexport interface RunOptions {\n  providerFlag?: string;\n  /** --class value (the old --role): a task class from workers.classes. */\n  className?: string;\n  modelFlag?: string;\n  label?: string;\n  noPane?: boolean;\n  /** --spec value, resolved for display/recording (\"-\" for stdin). */\n  specPath?: string;\n  /** --mcp value: server/set names, comma-separated. */\n  mcpFlag?: string;\n  /** Everything after `--` (the claude args). */\n  claudeArgs: string[];\n  /** Working directory for the run (default: this process's cwd). */\n  cwd?: string;\n  /** Chain stage bookkeeping: the chain id this stage belongs to. */\n  parent?: string;\n  /** Chain stage bookkeeping: the class name of this stage. */\n  stage?: string;\n  /** Suppress result printing (MCP worker_run: its stdout is the RPC channel). */\n  quiet?: boolean;\n  /** Internal: notified with the worker id once it exists (resume uses it). */\n  onWorkerStart?: (wid: string) => void;\n  /** Internal: preset worker id (the planner mints its id before phase 1). */\n  id?: string;\n  /** --worktree/--no-worktree; undefined lets the class default decide. */\n  worktreeFlag?: boolean;\n  /** --print-cmd: print the assembled claude argv as JSON and exit, no spawn. */\n  printCmd?: boolean;\n  /** Internal: suppress recursion depth on reroute. */\n  _reroutes?: number;\n  /** Internal: this run is the planner's phase-1 worker, not a new orchestration. */\n  _planner?: boolean;\n  /** --report json|ag2: which contract/parser the run uses (default: ag2, or PAI_WORKER_REPORT). */\n  reportFormatFlag?: \"json\" | \"ag2\";\n  /** --no-report-retry: skip the one bounded re-ask on an invalid AG2 final message. */\n  noReportRetry?: boolean;\n  /** --capability/--for: run through resolveCapability instead of resolveTarget. */\n  capabilityFlag?: string;\n  /** --out: image-engine output path (default: <logDir>/<id>.png). */\n  imageOut?: string;\n  /** --size: image-engine \"WIDTHxHEIGHT\" (default: 1024x1024). */\n  imageSize?: string;\n  /** --timeout-ms: image-engine request timeout (default: 120000). */\n  imageTimeoutMs?: number;\n}\n\n/**\n * ag2 unless the caller passed --report json or PAI_WORKER_REPORT=json is\n * set in the environment; an explicit flag always wins over the env default.\n */\nexport function resolveReportFormat(\n  flag: \"json\" | \"ag2\" | undefined,\n  env: NodeJS.ProcessEnv = process.env\n): \"json\" | \"ag2\" {\n  if (flag) return flag;\n  return env.PAI_WORKER_REPORT === \"json\" ? \"json\" : \"ag2\";\n}\n\n/** The strict empty MCP config for headless workers, written on demand. */\nexport { ensureNoMcpConfig } from \"./paths.js\";\n\n/** A stream-json user message for the child's stdin. */\nexport function stdinUserMessage(text: string): string {\n  return JSON.stringify({ type: \"user\", message: { role: \"user\", content: text } });\n}\n\n/** An operator line as the worker sees it: carrying the contract's marker. */\nexport function operatorUserText(text: string): string {\n  return `${OPERATOR_MARK} ${text}`;\n}\n\n/**\n * ISO stamp with seconds, attached to every mirrored event (2g): local time\n * with its offset (`2026-09-17T14:19:23+02:00`), so the viewer can render the\n * wall clock the operator lives in. `offMin` is east-positive minutes —\n * injectable so tests do not depend on the machine's zone.\n */\nexport function isoStamp(d = new Date(), offMin = -d.getTimezoneOffset()): string {\n  const t = new Date(d.getTime() + offMin * 60_000);\n  const sign = offMin < 0 ? \"-\" : \"+\";\n  const abs = Math.abs(offMin);\n  const hh = String(Math.floor(abs / 60)).padStart(2, \"0\");\n  const mm = String(abs % 60).padStart(2, \"0\");\n  return `${t.toISOString().slice(0, 19)}${sign}${hh}:${mm}`;\n}\n\nexport interface UsageBlock {\n  input_tokens?: number;\n  output_tokens?: number;\n  cache_read_input_tokens?: number;\n  cache_creation_input_tokens?: number;\n}\n\n// a type alias (not an interface): it must stay assignable to\n// Record<string, unknown> when written into the event transcript\nexport type StreamEvent = {\n  type?: string;\n  subtype?: string;\n  session_id?: string;\n  model?: string;\n  cwd?: string;\n  context_window?: number;\n  model_info?: { context_window?: number } | null;\n  message?: {\n    content?: Array<{ type?: string; text?: string; name?: string; id?: string; input?: unknown }>;\n    usage?: UsageBlock;\n  };\n  usage?: UsageBlock;\n  result?: string;\n  is_error?: boolean;\n  is_compact?: boolean;\n  num_turns?: number;\n  duration_ms?: number;\n  /** API time of the final turn — the TTFT proxy on zeroed per-turn usage. */\n  duration_api_ms?: number;\n};\n\n/** Context tokens of an assistant/result usage block (input+cache+output). */\nexport function usageContextTokens(u: UsageBlock | undefined): number | null {\n  if (!u) return null;\n  const t =\n    (u.input_tokens ?? 0) +\n    (u.cache_read_input_tokens ?? 0) +\n    (u.cache_creation_input_tokens ?? 0) +\n    (u.output_tokens ?? 0);\n  return t > 0 ? t : null;\n}\n\n/**\n * Context never shrinks mid-segment, so the status keeps the high-water\n * mark of the usage it has seen: a smaller later reading (short reply,\n * sidechain answer) must not drag the meter down. Compaction is the one\n * legitimate drop — see `resetContextTokensOnCompact`.\n */\nexport function bumpContextTokens(status: Pick<WorkerStatus, \"contextTokens\">, tokens: number | null): void {\n  if (tokens === null || tokens <= 0) return;\n  status.contextTokens = Math.max(status.contextTokens ?? 0, tokens);\n}\n\n/**\n * A compact boundary (`system`/`compact_boundary`, the shape Claude Code\n * writes with `compactMetadata.preTokens`; `compact` kept as the older\n * spelling) legitimately restarts the context at a lower size: the floor\n * drops to the event's own usage — usually none — so the next usage\n * reading re-seeds the meter at the fresh, smaller context.\n */\nexport function resetContextTokensOnCompact(status: Pick<WorkerStatus, \"contextTokens\">, e: StreamEvent): void {\n  status.contextTokens = usageContextTokens(e.usage) ?? null;\n}\n\n/** A compact boundary in a worker's stream, in either event spelling. */\nexport function isCompactBoundary(e: StreamEvent): boolean {\n  return e.type === \"system\" && (e.subtype === \"compact_boundary\" || e.subtype === \"compact\");\n}\n\n/**\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). The run's\n * real model is then only knowable from the first event it sends. Never\n * overwrites an explicit model — a resolved or pinned model stays the\n * recorded truth.\n */\nexport function adoptInitModel(status: Pick<WorkerStatus, \"model\">, e: StreamEvent): void {\n  if (status.model) return;\n  const m = (e.model ?? \"\").trim();\n  if (m) status.model = m;\n}\n\n/**\n * The model a run goes out on: an explicit `--model` wins; else the class\n * target's alias (\"glm/fast\") names the capability; else the capability the\n * class implies (image → image model, spotcheck/simple → fast, everything\n * else → default), resolved against the provider's model table. The built-in\n * anthropic provider resolves through the same table as any other, so a\n * worker never inherits the orchestrator session's model.\n */\nexport function resolveRunModel(\n  target: { provider: WorkerProvider; modelAlias: string | null },\n  className?: string,\n  modelFlag?: string\n): string {\n  if (modelFlag) return modelFlag;\n  const alias = target.modelAlias;\n  const capability =\n    alias && isModelCapability(alias) ? alias : classModelCapability(className);\n  return resolveModelCapability(target.provider, capability);\n}\n\n/**\n * The `--model` part of the claude argv. Skipped when the caller already\n * pinned one in the claude args (it must not be clobbered) or when nothing\n * resolved — `--model \"\"` would break the spawn.\n */\nexport function modelArgs(model: string, callerPinned: boolean): string[] {\n  return !callerPinned && model ? [\"--model\", model] : [];\n}\n\n/**\n * Whether the run's assembled cmd gets a forced --model. Headless workers\n * always take the resolved class/provider model — that table exists so\n * spawned subagents stay cheap. An interactive launch (`pai worker run` with\n * no -p) IS the chat pane, not a subagent: with no --model of the caller's\n * own it gets none here either, so Claude Code's own settings.json model\n * applies, exactly as launching `claude` by hand would (2026-09-20: an\n * interactive supervisor came up on the class model instead of the\n * settings.json [1m] variant, then needed a manual /model switch that\n * rebuilt the whole cache). A caller's own --model, headless or not, is\n * already in the passthrough args and must not be duplicated here.\n */\nexport function modelFlagArgs(headless: boolean, model: string, callerPinned: boolean): string[] {\n  return headless ? modelArgs(model, callerPinned) : [];\n}\n\n/**\n * The prompt text actually sent to the worker and whether the trailer got\n * appended. An instruction at the END of the user turn outweighs one buried\n * in the system prompt (measured 2026-09-20: the AG2 contract alone was\n * ignored by both Haiku and Sonnet). Never applied to an interactive launch\n * (no -p) or when the caller brought its own --append-system-prompt — the\n * trailer's \"format in system prompt\" would then point at nothing.\n */\nexport function headlessPromptText(\n  prompt: string | null,\n  headless: boolean,\n  callerSystemPrompt: boolean,\n  format: \"ag2\" | \"json\"\n): { text: string | null; applied: boolean } {\n  const applied = headless && prompt !== null && !callerSystemPrompt;\n  return { text: applied ? prompt! + promptTrailer(format) : prompt, applied };\n}\n\n/**\n * Whether an invalid final report earns the one bounded re-ask: only when a\n * real validator caught the problem (a missing/broken aibroker must never\n * block the run further), the caller did not opt out, and there is a session\n * to resume into. Cap of exactly one is enforced by the caller never calling\n * this twice for the same run.\n */\nexport function shouldRetryReport(\n  validation: { ok: boolean; validator: \"aibroker\" | \"none\" },\n  noReportRetry: boolean,\n  hasSession: boolean\n): boolean {\n  return !validation.ok && validation.validator === \"aibroker\" && !noReportRetry && hasSession;\n}\n\n/** Context window announced by the init event, when the endpoint sends one. */\n/**\n * Did the run end well? A headless run must have produced a stream-json\n * result event that is not an error; an interactive launch IS the chat pane\n * and never emits one, so its clean exit (rc 0) is the whole verdict —\n * without this every interactive session that ends normally was recorded\n * as `failed rc=0` and supervision pinged the spawner about a non-failure.\n */\nexport function runSucceeded(\n  headless: boolean,\n  rc: number,\n  resultEvent: Pick<StreamEvent, \"is_error\"> | null,\n): boolean {\n  if (rc !== 0) return false;\n  if (!headless) return true;\n  return resultEvent !== null && !resultEvent.is_error;\n}\n\nexport function initContextWindow(e: StreamEvent): number | null {\n  if (typeof e.context_window === \"number\" && e.context_window > 0) return e.context_window;\n  if (e.model_info && typeof e.model_info.context_window === \"number\" && e.model_info.context_window > 0) {\n    return e.model_info.context_window;\n  }\n  // the `[1m]` model variant announces a 1M-token window by suffix\n  if (/\\[1m\\]$/.test((e.model ?? \"\").trim())) return 1_000_000;\n  return null;\n}\n\n/**\n * Run one worker. Returns the process exit code to pass through.\n * Throws WorkersConfigError-shaped Errors for configuration problems.\n */\nexport async function runWorker(opts: RunOptions): Promise<number> {\n  const { raw: _raw, workers: config } = readWorkersSection();\n  void _raw;\n  if (!config.enabled) {\n    throw new Error(\n      `workers are off. Turn them on with: pai worker on` +\n        `\\n(then the Agent-tool hook stops denying Anthropic subagents only when you do)`\n    );\n  }\n  const logDir = workersLogDir(config);\n  mkdirSync(logDir, { recursive: true });\n\n  // class plan is not one worker but the planner orchestration (planner.ts)\n  if (opts.className === \"plan\" && !opts._planner) {\n    const { runPlanner } = await import(\"./planner.js\");\n    return runPlanner(opts);\n  }\n\n  // Sub-worker bookkeeping: an explicit parent (chain stage, planner child)\n  // wins, else the worker this process runs inside (PAI_WORKER_ID). Both\n  // caps from workers.tree apply to parents that are workers themselves.\n  const parent = launchParent(opts.parent);\n  if (parent) assertChildAllowed(logDir, parent, config.tree);\n\n  // An explicit --provider always bypasses capability resolution entirely;\n  // otherwise --capability, or a class whose implied capability is not\n  // \"default\" and names no provider of its own, resolves cross-provider.\n  const capability = opts.providerFlag\n    ? null\n    : capabilityForRun(config, { capabilityFlag: opts.capabilityFlag, className: opts.className });\n  const capabilityResolution: CapabilityRunResolution | null = capability\n    ? resolveCapabilityRun(config, capability)\n    : null;\n\n  const target = capabilityResolution\n    ? {\n        providerName: capabilityResolution.providerName,\n        provider: capabilityResolution.provider,\n        modelAlias: null,\n        classMcp: null,\n        via: \"class\" as const,\n      }\n    : resolveTarget(config, logDir, {\n        flagProvider: opts.providerFlag,\n        className: opts.className,\n      });\n  assertProviderRunnable(target.providerName, target.provider);\n\n  const parsed = parseRunnerArgs(opts.claudeArgs);\n  const label =\n    opts.label ??\n    shortText(parsed.prompt ?? UNLABELED, 70);\n\n  const model = capabilityResolution\n    ? capabilityResolution.model\n    : resolveRunModel(target, opts.className, opts.modelFlag);\n  const reportFormat = resolveReportFormat(opts.reportFormatFlag);\n\n  try {\n    if (target.provider.engine === \"image\") {\n      return 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: opts.onWorkerStart,\n        id: opts.id,\n      });\n    }\n    if (target.provider.engine === \"codex\") {\n      return 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: opts.onWorkerStart,\n        id: opts.id,\n        worktreeFlag: opts.worktreeFlag,\n        className: opts.className,\n        reportFormat,\n        capability: capability ?? undefined,\n      });\n    }\n    return await executeRun({\n      config,\n      logDir,\n      target,\n      model,\n      label,\n      parsed,\n      claudeArgs: opts.claudeArgs,\n      noPane: opts.noPane ?? false,\n      mcpFlag: opts.mcpFlag,\n      cwd: opts.cwd,\n      specPath: opts.specPath,\n      parent: parent ?? undefined,\n      stage: opts.stage,\n      quiet: opts.quiet,\n      onWorkerStart: opts.onWorkerStart,\n      id: opts.id,\n      capability: capability ?? undefined,\n      worktreeFlag: opts.worktreeFlag,\n      className: opts.className,\n      reroutes: opts._reroutes ?? 0,\n      printCmd: opts.printCmd,\n      reportFormat,\n      noReportRetry: opts.noReportRetry ?? false,\n    });\n  } catch (e) {\n    if (e instanceof Error && e.message.startsWith(\"key file\")) {\n      throw new Error(\n        `provider \"${target.providerName}\": ${e.message}` +\n          `\\nPut the token in that file (chmod 600) or point keyFile elsewhere.`\n      );\n    }\n    throw e;\n  }\n}\n\n/**\n * The core tools a headless run grants when the caller brings none of its\n * own. Permission-gated tools (Bash, Read, Write, …) are never offered to a\n * headless session that has no allow rule for them — the child starts with\n * only the tools that never ask (tool search, web, cron, tasks) and cannot\n * touch a file or shell (2026-09-18). A caller's own --allowedTools is a\n * deliberate restriction and passes through untouched.\n */\nconst DEFAULT_WORKER_TOOLS = \"Read,Edit,Write,Bash,Grep,Glob\";\n\n/** The --allowedTools args a headless run needs; [] when the caller granted. */\nexport function headlessToolGrants(allowedTools: string[]): string[] {\n  return allowedTools.length ? [] : [\"--allowedTools\", DEFAULT_WORKER_TOOLS];\n}\n\n/**\n * `--tools` narrows which built-in tool *schemas* claude loads into the\n * system prompt — distinct from `--allowedTools`, which narrows which of\n * those loaded tools may run without asking. A headless worker with a small\n * grant (say, just Read) still pays for every built-in schema (Bash, Web*,\n * NotebookEdit, …) it never uses: ~9k tokens per spawn (29,270 -> 20,352,\n * measured 2026-09-20). Passing --tools built from the same grant removes\n * the unused schemas.\n *\n * MCP grants (`mcp__server__tool` / `mcp__server__*`) are left out of the\n * --tools list: they are not built-in tool names, --tools does not\n * recognize them, and the MCP server's tools load through --mcp-config\n * regardless. Tested 2026-09-20: with an --allowedTools grant of\n * `Read,mcp__pai__memory_search`, passing `--tools Read` (mcp name\n * omitted) still let the worker call memory_search successfully; the tool\n * loads from the filtered MCP config, not from --tools.\n */\nexport function headlessToolsFlag(allowedTools: string[]): string[] {\n  const names: string[] = [];\n  for (const entry of allowedTools) {\n    for (const raw of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n      if (raw.startsWith(\"mcp__\")) continue; // loads via --mcp-config, not --tools\n      const patterned = raw.match(/^([A-Za-z]+)\\(.*\\)$/); // e.g. Bash(git *) -> Bash\n      const name = patterned ? patterned[1] : raw;\n      if (!names.includes(name)) names.push(name);\n    }\n  }\n  return names.length ? [\"--tools\", names.join(\",\")] : [];\n}\n\n/**\n * A `--tools` value with no ToolSearch silently disables MCP tool deferral\n * (measured 2026-09-20: a run with `--tools \"Bash,Read,Grep,Glob,Agent\"` paid\n * 65,300 first-turn tokens with 5 MCP servers, 230,448 with 16, versus\n * 24,889 / 37,133 with ToolSearch present). Applied to the fully-assembled\n * argv so it catches a `--tools` from any source — the computed\n * `headlessToolsFlag`/project-pin flag, or one the caller put directly on\n * the command line.\n */\nexport function ensureToolSearch(cmd: string[]): string[] {\n  const flagIdx = cmd.findIndex((a) => a === \"--tools\");\n  if (flagIdx >= 0 && flagIdx + 1 < cmd.length) {\n    const value = cmd[flagIdx + 1];\n    if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n      const out = [...cmd];\n      out[flagIdx + 1] = `${value},ToolSearch`;\n      process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n      return out;\n    }\n    return cmd;\n  }\n  const eqIdx = cmd.findIndex((a) => a.startsWith(\"--tools=\"));\n  if (eqIdx >= 0) {\n    const value = cmd[eqIdx].slice(\"--tools=\".length);\n    if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n      const out = [...cmd];\n      out[eqIdx] = `--tools=${value},ToolSearch`;\n      process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n      return out;\n    }\n  }\n  return cmd;\n}\n\n/**\n * The claude flag that turns on the browser bridge, when the run asked for it.\n *\n * The bridge is not an MCP server — it rides the Chrome native-host channel,\n * is absent from `mcpServers`, and is off in a spawned claude until `--chrome`\n * is passed. A grant such as `mcp__claude-in-chrome__tabs_context_mcp` is\n * therefore a request for that flag, not for a server to load; without this\n * the grant names a tool that never exists. `rest` is the caller's own argv:\n * a `--chrome` they passed themselves is kept rather than duplicated.\n */\nexport function chromeGrantArgs(wanted: string[], rest: string[] = []): string[] {\n  if (rest.includes(\"--chrome\")) return [];\n  return grantsChrome(wanted) ? [\"--chrome\"] : [];\n}\n\n/**\n * Which MCP server names and --tools list an interactive launch (`pai\n * worker run`, no -p) uses: an explicit --mcp on this command line beats\n * the project's `pai project mcp` pin; an explicit --tools beats `pai\n * project tools`. Neither given: [] — today's \"everything loads\" default.\n */\nexport function interactiveMcpTools(\n  explicitMcp: string[],\n  callerTools: boolean,\n  projectLaunch: ProjectLaunchConfig | 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\nasync function executeImageRun(a: ImageExecuteArgs & { config: ReturnType<typeof readWorkersSection>[\"workers\"] }): Promise<number> {\n  const { logDir, target, model, label, parsed } = a;\n  if (!parsed.headless || parsed.prompt === null) {\n    throw new Error(\n      `provider \"${target.providerName}\" (engine image) supports headless runs only: ` +\n        `pass the task with -p '<prompt>'`\n    );\n  }\n  const wid = a.id ?? newWorkerId();\n  const cwd = a.cwd ?? process.cwd();\n  const term = process.env.ITERM_SESSION_ID ?? \"\";\n  const outPath = a.outPath ?? join(logDir, `${wid}.png`);\n\n  const status: WorkerStatus = {\n    id: wid,\n    pid: process.pid,\n    label,\n    cwd,\n    term,\n    provider: target.providerName,\n    model,\n    state: \"running\",\n    started: nowStamp(),\n    updated: nowStamp(),\n    turns: 0,\n    tools: 0,\n    last: \"generating image\",\n    rc: null,\n    secs: null,\n    origin: \"spawn\",\n    outputFormat: parsed.outputFormat,\n    reportFormat: \"json\",\n    ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n    ...(a.specPath ? { spec: a.specPath } : {}),\n  };\n  saveStatus(logDir, status);\n  a.onWorkerStart?.(wid);\n  const ledger = ledgerPath(logDir);\n  appendLedger(ledger, \"WORKER-START\", {\n    id: wid,\n    provider: target.providerName,\n    mode: \"headless\",\n    engine: \"image\",\n    model,\n    cwd,\n    label,\n    capability: a.capability,\n    ...(a.specPath ? { spec: a.specPath } : {}),\n  });\n\n  const 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.state = \"done\";\n    status.rc = 0;\n    status.secs = secs;\n    status.last = `wrote ${result.path}`;\n    saveStatus(logDir, status);\n    appendLedger(ledger, \"WORKER-END\", {\n      id: wid,\n      provider: target.providerName,\n      mode: \"headless\",\n      engine: \"image\",\n      model,\n      rc: 0,\n      secs,\n      label,\n      capability: a.capability,\n    });\n    if (!a.quiet) process.stdout.write(JSON.stringify(result) + \"\\n\");\n    return 0;\n  } catch (e) {\n    const secs = Math.round((Date.now() - t0) / 1000);\n    const message = e instanceof Error ? e.message : String(e);\n    status.state = \"failed\";\n    status.rc = 1;\n    status.secs = secs;\n    status.last = message;\n    saveStatus(logDir, status);\n    appendLedger(ledger, \"WORKER-END\", {\n      id: wid,\n      provider: target.providerName,\n      mode: \"headless\",\n      engine: \"image\",\n      model,\n      rc: 1,\n      secs,\n      label,\n      capability: a.capability,\n    });\n    throw e;\n  }\n}\n\ninterface ExecuteArgs {\n  config: ReturnType<typeof readWorkersSection>[\"workers\"];\n  logDir: string;\n  target: ReturnType<typeof resolveTarget>;\n  model: string;\n  label: string;\n  parsed: ReturnType<typeof parseRunnerArgs>;\n  claudeArgs: string[];\n  noPane: boolean;\n  mcpFlag?: string;\n  cwd?: string;\n  specPath?: string;\n  parent?: string;\n  stage?: string;\n  quiet?: boolean;\n  onWorkerStart?: (wid: string) => void;\n  id?: string;\n  worktreeFlag?: boolean;\n  className?: string;\n  reroutes: number;\n  printCmd?: boolean;\n  reportFormat: \"json\" | \"ag2\";\n  noReportRetry?: boolean;\n  /** Capability name this run resolved through (--capability, or an implied class capability), when it did. */\n  capability?: string;\n}\n\nasync function executeRun(a: ExecuteArgs): Promise<number> {\n  const { config, logDir, target, model, label, parsed, noPane } = a;\n  const headless = parsed.headless;\n\n  // openai-protocol providers run through the local proxy (started on demand)\n  let proxyUrl: string | undefined;\n  if (target.provider.protocol === \"openai\") {\n    const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n    proxyUrl = `${base}/${target.providerName}`;\n  }\n  const env = buildRunEnv(target.provider, headless, proxyUrl);\n\n  const wid = a.id ?? newWorkerId();\n  const cwd = a.cwd ?? process.cwd();\n  const term = process.env.ITERM_SESSION_ID ?? \"\";\n  const session = resolveSession(term);\n  // orchestrator Bash children have no terminal identity (see scope.ts) — the\n  // session map supplies the claude session they were spawned by instead\n  const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n  // One worktree per writing run (implement/complex/plan, a git cwd, a prompt\n  // that is not read-only; --worktree/--no-worktree override). A git refusal\n  // degrades to an in-place run — the worker itself must still run.\n  // Skipped for --print-cmd: it is a dry-run and must not touch git.\n  let worktree: WorktreeInfo | null = null;\n  if (\n    headless &&\n    !a.printCmd &&\n    worktreeWanted(a.worktreeFlag, { cwd, className: a.className, prompt: parsed.prompt })\n  ) {\n    try {\n      worktree = addWorktree(logDir, wid, cwd);\n    } catch (e) {\n      const why = (e as Error).message;\n      process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n      appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n    }\n  }\n  // sub-workers detect themselves (and their parent) through this variable\n  env.PAI_WORKER_ID = wid;\n\n  const chromeArgs = chromeGrantArgs(\n    [...(a.mcpFlag ? [a.mcpFlag] : []), ...parsed.mcp, ...(target.classMcp ?? []), ...parsed.allowedTools],\n    parsed.rest\n  );\n\n  // MCP / tools: caller config > allowlist (--mcp flag / --mcp args / role /\n  // mcp__ grants in --allowedTools) > project pin (interactive only, `pai\n  // project mcp` / `pai project tools`) > the strict empty set (headless) /\n  // everything (interactive, today's default).\n  let mcpArgs: string[] = [];\n  let toolsFlag: string[] = headless ? headlessToolsFlag(parsed.allowedTools) : [];\n  if (headless && !parsed.callerMcpConfig) {\n    const wanted = [\n      ...(a.mcpFlag ? [a.mcpFlag] : []),\n      ...parsed.mcp,\n      ...(target.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 = [...(a.mcpFlag ? [a.mcpFlag] : []), ...parsed.mcp];\n    const projectLaunch = projectLaunchConfig(cwd);\n    const picked = interactiveMcpTools(explicitMcp, parsed.callerTools, projectLaunch);\n    if (picked.mcpNames.length) {\n      const names = expandMcpNames(picked.mcpNames, config);\n      mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n    }\n    if (picked.tools.length) toolsFlag = [\"--tools\", picked.tools.join(\",\")];\n  }\n\n  // In stdin mode the prompt moves to the first user message on stdin, so it\n  // must come off the command line (bare -p stays: stream-json needs --print).\n  const restArgs = headless ? stripPromptValues(parsed.rest) : parsed.rest;\n  const toolArgs = headless ? headlessToolGrants(parsed.allowedTools) : [];\n  const { text: promptText, applied: trailerApplied } = headlessPromptText(\n    parsed.prompt,\n    headless,\n    parsed.callerSystemPrompt,\n    a.reportFormat\n  );\n  let cmd: string[] = [\"claude\"];\n  cmd.push(...modelFlagArgs(headless, model, Boolean(parsed.callerModel)));\n  cmd.push(...chromeArgs, ...mcpArgs, ...toolArgs, ...toolsFlag, ...restArgs);\n  if (headless) {\n    cmd.push(\"--output-format\", \"stream-json\", \"--verbose\", \"--input-format\", \"stream-json\");\n    if (!parsed.callerSystemPrompt) {\n      cmd.push(\"--append-system-prompt\", workerContractPrompt(a.className, a.reportFormat));\n    }\n    if (worktree) {\n      cmd.push(\n        \"--append-system-prompt\",\n        worktreeSystemPrompt(wid, worktree.branch, worktree.dir)\n      );\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(JSON.stringify({ cmd, cwd: worktree?.dir ?? cwd }));\n    return 0;\n  }\n\n  const status: WorkerStatus = {\n    id: wid,\n    pid: process.pid,\n    label,\n    cwd,\n    term,\n    provider: target.providerName,\n    model,\n    state: \"running\",\n    started: nowStamp(),\n    updated: nowStamp(),\n    turns: 0,\n    tools: 0,\n    last: headless ? \"starting\" : \"interactive\",\n    rc: null,\n    secs: null,\n    // interactive runs ARE the chat pane, not a spawned subagent of it\n    origin: headless ? \"spawn\" : \"chat\",\n    outputFormat: parsed.outputFormat,\n    ...(headless ? { reportFormat: a.reportFormat } : {}),\n    ...(trailerApplied ? { promptTrailer: true } : {}),\n    ...(session ? { session } : {}),\n    ...(spawnerSession ? { spawnerSession } : {}),\n    // no window seed: contextWindow comes from the init event only, and the\n    // meter stays hidden until one is announced (never a guessed default)\n    ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n    ...(worktree ? { worktreeDir: worktree.dir, branch: worktree.branch, worktreeBase: worktree.base } : {}),\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 ? \"headless\" : \"interactive\",\n    model,\n    cwd,\n    label,\n    ...(a.capability ? { capability: a.capability } : {}),\n    ...(a.specPath ? { spec: a.specPath } : {}),\n  });\n\n  // Follow pane: headless only, best effort, never blocking the worker.\n  if (headless && !noPane && config.pane.enabled && term && process.env.PAI_WORKER_AUTOPANE !== \"0\") {\n    void openPaneForWorker(logDir, config, wid, term).catch(() => {});\n  }\n\n  const t0 = Date.now();\n  const proc = spawn(cmd[0], cmd.slice(1), {\n    env,\n    cwd: worktree?.dir ?? cwd,\n    stdio: headless ? [\"pipe\", \"pipe\", \"inherit\"] : \"inherit\",\n  });\n\n  // --- stdin lifecycle (2i): prompt in, socket forwards, close 2 s after result\n  let operatorInFlight = 0;\n  let closeTimer: NodeJS.Timeout | null = null;\n  const armStdinClose = () => {\n    if (closeTimer) clearTimeout(closeTimer);\n    closeTimer = setTimeout(() => {\n      if (operatorInFlight === 0) {\n        try {\n          proc.stdin?.end();\n        } catch {\n          /* already closed */\n        }\n      }\n    }, 2_000);\n  };\n\n  let eventsFd: number | null = null;\n  const writeEvent = (obj: Record<string, unknown>): void => {\n    if (eventsFd === null) return;\n    try {\n      writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n    } catch {\n      // a full disk must not take the worker transcript's process down\n    }\n  };\n\n  const operatorServer = headless\n    ? createOperatorServer(logDir, wid, (text) => {\n        operatorInFlight += 1;\n        if (closeTimer) {\n          clearTimeout(closeTimer);\n          closeTimer = null;\n        }\n        // a handoff delivery's mirror carries a flag: the viewer shows the\n        // inbox ◆ line instead, never both\n        writeEvent({ type: \"operator\", text, handoff: isHandoffMessage(text) });\n        try {\n          proc.stdin?.write(stdinUserMessage(operatorUserText(text)) + \"\\n\");\n        } catch {\n          /* child gone; the socket is closed by the run's cleanup */\n        }\n      })\n    : null;\n\n  let killed = false;\n  const cleanup = () => {\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    status.last = `killed by signal ${sig}`;\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      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      try {\n        recordWorktree(logDir, status, worktree, false);\n      } catch {\n        /* best effort: the status already says killed */\n      }\n    }\n    cleanup();\n    try {\n      proc.kill();\n    } catch {\n      /* already gone */\n    }\n    process.exit(143);\n  };\n  process.once(\"SIGTERM\", () => onSignal(\"SIGTERM\"));\n  process.once(\"SIGINT\", () => onSignal(\"SIGINT\"));\n  process.once(\"SIGHUP\", () => onSignal(\"SIGHUP\"));\n\n  // Holder for the last result event + its parsed report: assigned inside the\n  // readline callback below, read after the await.\n  const ctx: { resultEvent: StreamEvent | null; resultReport: WorkerReport | null } = {\n    resultEvent: null,\n    resultReport: null,\n  };\n\n  if (headless) {\n    // the first user message carries the prompt (the -p value was stripped)\n    if (promptText !== null) {\n      try {\n        proc.stdin!.write(stdinUserMessage(promptText) + \"\\n\");\n      } catch {\n        /* child died instantly; the close handler reports it */\n      }\n    }\n    eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n    const rl = createInterface({ input: proc.stdout! });\n    rl.on(\"line\", (line) => {\n      if (parsed.outputFormat === \"stream-json\") {\n        process.stdout.write(line + \"\\n\");\n      }\n      if (!line.startsWith(\"{\")) return;\n      let e: StreamEvent;\n      try {\n        e = JSON.parse(line) as StreamEvent;\n      } catch {\n        return;\n      }\n      writeEvent(e as Record<string, unknown>);\n      if (e.type === \"system\" && e.subtype === \"init\") {\n        if (e.session_id) status.claudeSession = e.session_id;\n        adoptInitModel(status, e);\n        const cw = initContextWindow(e);\n        if (cw) status.contextWindow = cw;\n        saveStatus(logDir, status);\n      } else if (isCompactBoundary(e)) {\n        resetContextTokensOnCompact(status, e);\n        saveStatus(logDir, status);\n      } else if (e.type === \"assistant\") {\n        status.turns += 1;\n        bumpContextTokens(status, usageContextTokens(e.message?.usage));\n        for (const block of e.message?.content ?? []) {\n          if (block.type === \"tool_use\") {\n            status.tools += 1;\n            status.last = describeTool(block.name ?? \"?\", block.input);\n          } 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 === \"result\") {\n        const tokens = usageContextTokens(e.usage);\n        // a compact result legitimately restarts the context lower\n        if (e.is_compact) status.contextTokens = tokens ?? status.contextTokens;\n        else bumpContextTokens(status, tokens);\n        const report = parseWorkerReport(e.result ?? \"\");\n        if (report?.notes) status.last = shortText(report.notes, 90);\n        else if (e.result) status.last = shortText(e.result, 90);\n        saveStatus(logDir, status);\n        operatorInFlight = 0;\n        armStdinClose();\n        ctx.resultEvent = e;\n        ctx.resultReport = report;\n      }\n    });\n  }\n\n  const rc = await new Promise<number>((resolve, reject) => {\n    proc.on(\"error\", reject);\n    proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n  });\n  if (eventsFd !== null) closeSync(eventsFd);\n  cleanup();\n\n  const secs = Math.floor((Date.now() - t0) / 1000);\n  const resultEvent = ctx.resultEvent;\n  const ok = runSucceeded(headless, rc, resultEvent);\n  status.state = ok ? \"done\" : \"failed\";\n  status.rc = rc;\n  status.secs = secs;\n  if (resultEvent) status.last = shortText(resultEvent.result ?? \"\", 90);\n  if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n  if (headless && a.reportFormat === \"ag2\" && resultEvent?.result) {\n    const v = validateAg2(resultEvent.result);\n    status.reportValid = v.ok;\n    status.reportErrors = v.errors;\n    // one bounded re-ask, cap exactly one — see shouldRetryReport\n    if (shouldRetryReport(v, a.noReportRetry ?? false, Boolean(status.claudeSession))) {\n      status.reportRetried = true;\n      saveStatus(logDir, status);\n      appendLedger(ledger, \"WORKER-REPORT-RETRY\", { id: wid, reason: v.errors.join(\"; \") || \"invalid AG2 message\" });\n      try {\n        const reaskText = await reaskAg2Report({\n          env,\n          cwd: worktree?.dir ?? cwd,\n          model,\n          callerPinnedModel: Boolean(parsed.callerModel),\n          chromeArgs,\n          mcpArgs,\n          toolArgs,\n          toolsFlag,\n          sessionId: status.claudeSession!,\n        });\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  saveStatus(logDir, status);\n  appendLedger(ledger, \"WORKER-END\", {\n    id: wid,\n    provider: target.providerName,\n    mode: headless ? \"headless\" : \"interactive\",\n    model,\n    rc,\n    secs,\n    turns: status.turns,\n    tools: status.tools,\n    label,\n  });\n\n  // worktree outcome: keep branch + commit count on success, clean up on failure\n  if (worktree) recordWorktree(logDir, status, worktree, ok);\n\n  if (headless && !a.quiet) {\n    printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, ctx.resultReport, worktreeExtras(status));\n  }\n\n  // Quota reroute: only auto-routed runs, dead before the first tool call.\n  const resultText = resultEvent?.result ?? \"\";\n  if (\n    !ok &&\n    headless &&\n    a.target.via === \"auto\" &&\n    config.routing.retryOnQuota &&\n    status.turns <= 1 &&\n    status.tools === 0 &&\n    isQuotaFailure(resultText)\n  ) {\n    setCooldown(logDir, target.providerName, config.routing.cooldownMinutes);\n    const next = nextAutoProvider(config, logDir, target.providerName);\n    if (next && a.reroutes < config.routing.order.length) {\n      appendLedger(ledger, \"WORKER-REROUTE\", {\n        from: target.providerName,\n        to: next,\n        reason: \"quota\",\n      });\n      return runWorker({\n        providerFlag: next,\n        label,\n        noPane: a.noPane,\n        mcpFlag: a.mcpFlag,\n        cwd: a.cwd,\n        specPath: a.specPath,\n        parent: a.parent,\n        stage: a.stage,\n        claudeArgs: a.claudeArgs,\n        onWorkerStart: a.onWorkerStart,\n        worktreeFlag: a.worktreeFlag,\n        className: a.className,\n        _reroutes: a.reroutes + 1,\n      });\n    }\n  }\n\n  // A finishing child reports to its worker parent automatically: the report\n  // lands in the parent's inbox and (when the parent still runs) is said to\n  // it so it enters the parent's conversation.\n  const finalRc = rc !== 0 ? rc : ok ? 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(\n          ctx.resultReport?.notes ?? resultEvent?.result ?? (ok ? \"done\" : \"failed\"),\n          400\n        ),\n        data: {\n          rc: finalRc,\n          ok,\n          ...(status.branch ? { branch: status.branch, commits: status.commits ?? 0 } : {}),\n          ...(ctx.resultReport ? { report: ctx.resultReport } : {}),\n        },\n      });\n    } catch {\n      // the inbox line is best effort; it must never fail the exit path\n    }\n  }\n\n  return finalRc;\n}\n\n/** The worktree fields printResult adds to a json payload, when there is one. */\nfunction worktreeExtras(s: WorkerStatus): Record<string, unknown> | undefined {\n  return s.branch ? { branch: s.branch, commits: s.commits ?? 0 } : undefined;\n}\n\nexport interface Ag2ReaskArgs {\n  env: NodeJS.ProcessEnv;\n  cwd: string;\n  model: string;\n  callerPinnedModel: boolean;\n  chromeArgs: string[];\n  mcpArgs: string[];\n  toolArgs: string[];\n  toolsFlag: string[];\n  sessionId: string;\n  timeoutMs?: number;\n}\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[] = [\"claude\"];\n  cmd.push(...modelFlagArgs(true, a.model, a.callerPinnedModel));\n  cmd.push(...a.chromeArgs, ...a.mcpArgs, ...a.toolArgs, ...a.toolsFlag);\n  cmd.push(\"--resume\", a.sessionId, \"-p\", AG2_REASK_TEXT, \"--output-format\", \"json\");\n  cmd = ensureToolSearch(cmd);\n  const proc = spawn(cmd[0], cmd.slice(1), { env: a.env, cwd: a.cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n  const timer = setTimeout(() => {\n    try {\n      proc.kill(\"SIGKILL\");\n    } catch {\n      /* already gone */\n    }\n  }, a.timeoutMs ?? 60_000);\n  let out = \"\";\n  proc.stdout.on(\"data\", (chunk: Buffer) => {\n    out += chunk.toString(\"utf8\");\n  });\n  await new Promise<void>((resolve) => {\n    proc.on(\"error\", () => resolve());\n    proc.on(\"close\", () => resolve());\n  });\n  clearTimeout(timer);\n  return resultFromOutput(out);\n}\n\n// ---------------------------------------------------------------------------\n// codex engine (2d)\n// ---------------------------------------------------------------------------\n\ninterface CodexArgs extends Omit<ExecuteArgs, \"reroutes\" | \"mcpFlag\"> {}\n\nasync function executeCodexRun(a: CodexArgs): Promise<number> {\n  const { config, logDir, target, model, label, parsed, noPane } = a;\n  if (!parsed.headless || parsed.prompt === null) {\n    throw new Error(\n      `provider \"${target.providerName}\" (engine codex) supports headless runs only: ` +\n        `pass the task with -p '<prompt>'`\n    );\n  }\n  const env = buildCodexEnv(target.provider);\n  const wid = a.id ?? newWorkerId();\n  const cwd = a.cwd ?? process.cwd();\n  const term = process.env.ITERM_SESSION_ID ?? \"\";\n  const session = resolveSession(term);\n  const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n  let worktree: WorktreeInfo | null = null;\n  if (worktreeWanted(a.worktreeFlag, { cwd, className: a.className, prompt: parsed.prompt })) {\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 = (worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir) + \"\\n\\n\" : \"\") + parsed.prompt;\n\n  const status: WorkerStatus = {\n    id: wid,\n    pid: process.pid,\n    label,\n    cwd,\n    term,\n    provider: target.providerName,\n    model,\n    state: \"running\",\n    started: nowStamp(),\n    updated: nowStamp(),\n    turns: 0,\n    tools: 0,\n    last: \"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 ? { worktreeDir: worktree.dir, branch: worktree.branch, worktreeBase: worktree.base } : {}),\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: \"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 (!noPane && config.pane.enabled && term && process.env.PAI_WORKER_AUTOPANE !== \"0\") {\n    void openPaneForWorker(logDir, config, wid, term).catch(() => {});\n  }\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  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      status.last = `killed by signal ${sig}`;\n      saveStatus(logDir, status);\n      // same as the claude path: a killed run's worktree and branch go now\n      if (worktree) {\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  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.state = ok ? \"done\" : \"failed\";\n  status.rc = rc;\n  status.secs = secs;\n  status.last = shortText(report?.notes ?? finalText, 90) || (ok ? \"done\" : \"failed\");\n  saveStatus(logDir, status);\n  appendLedger(ledger, \"WORKER-END\", {\n    id: wid,\n    provider: target.providerName,\n    mode: \"headless\",\n    engine: \"codex\",\n    model,\n    rc,\n    secs,\n    turns: status.turns,\n    tools: status.tools,\n    label,\n  });\n\n  if (worktree) recordWorktree(logDir, status, worktree, ok);\n\n  if (!a.quiet) printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, report, worktreeExtras(status));\n\n  // the codex engine reports to its worker parent the same way (handoff.ts)\n  const finalRc = rc !== 0 ? rc : ok ? 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 ?? finalText ?? (ok ? \"done\" : \"failed\"), 400),\n        data: {\n          rc: finalRc,\n          ok,\n          ...(status.branch ? { branch: status.branch, commits: status.commits ?? 0 } : {}),\n          ...(report ? { report } : {}),\n        },\n      });\n    } catch {\n      // best effort; never fail the exit path\n    }\n  }\n  return finalRc;\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): Promise<ProviderTestResult> {\n  assertProviderRunnable(providerName, provider);\n  const model = provider.models.default;\n\n  if (provider.engine === \"codex\") {\n    const skipped = codexInstalled() ? undefined : \"codex not installed\";\n    return {\n      provider: providerName,\n      model,\n      latencyMs: 0,\n      result: skipped ?? \"codex engine: pong probe through codex exec not implemented\",\n      ok: false,\n      ...(skipped ? { skipped } : {}),\n    };\n  }\n\n  let proxyUrl: string | undefined;\n  if (provider.protocol === \"openai\") {\n    const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n    proxyUrl = `${base}/${providerName}`;\n  }\n  const env = buildRunEnv(provider, true, proxyUrl);\n  const t0 = Date.now();\n  const proc = spawn(\n    \"claude\",\n    [\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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"}