@tekmidian/pai 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"providers-sXcK5bDZ.mjs","names":["psOutput","readKey","readKey"],"sources":["../src/config/json-store.ts","../src/workers/config.ts","../src/workers/paths.ts","../src/workers/args.ts","../src/workers/ledger.ts","../src/workers/status.ts","../src/workers/scope.ts","../src/workers/routing.ts","../src/workers/pane.ts","../src/workers/report.ts","../src/workers/mcp.ts","../src/workers/operator.ts","../src/workers/proxy/server.ts","../src/workers/codex.ts","../src/workers/run.ts","../src/workers/render.ts","../src/workers/viewer.ts","../src/workers/providers.ts"],"sourcesContent":["/**\n * json-store.ts — read/write JSON config files without destroying them\n *\n * The failure this exists to prevent:\n *\n * try { return JSON.parse(read(path)); } catch { return {}; }\n * ... later ...\n * write(path, JSON.stringify(ourData));\n *\n * An unreadable file becomes an empty object, and the next write makes that\n * permanent. Silently, exit code 0. This shape appeared three times in this\n * repo against three different files, and twice in AIBroker.\n *\n * The distinction that matters is between *missing* and *unreadable*:\n *\n * missing — legitimate first run. Start fresh; writing is safe.\n * unreadable — the file exists and we could not parse it. Those bytes are\n * the only copy of something. Never overwrite them.\n *\n * Collapsing the second into the first is the bug.\n *\n * NOT everything deserves this guard. For a transient buffer — an undelivered\n * message queue, a cache — starting fresh IS the correct recovery, and\n * refusing to write would disable the feature permanently. Use `writeJsonAtomic`\n * alone there: it still prevents a crash from truncating a good file, without\n * blocking recovery. Reserve `readJsonStrict` for data a user cannot rebuild.\n */\n\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n copyFileSync,\n renameSync,\n unlinkSync,\n mkdirSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\n\n/**\n * Read a JSON file, distinguishing \"absent\" from \"damaged\".\n *\n * @param path file to read\n * @param label how to name it to the user, e.g. \"~/.claude.json\"\n * @throws if the file exists but cannot be read or parsed\n */\nexport function readJsonStrict(path: string, label = path): Record<string, unknown> {\n if (!existsSync(path)) return {};\n\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (e) {\n throw new Error(\n `Could not read ${label}: ${e instanceof Error ? e.message : String(e)}\\n` +\n `Refusing to continue — writing now would replace its contents with ours alone.`\n );\n }\n\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (e) {\n throw new Error(\n `${label} exists but is not valid JSON: ${e instanceof Error ? e.message : String(e)}\\n` +\n `Refusing to continue — overwriting it would destroy whatever it holds.\\n` +\n `Repair the file, or move it aside and re-run this command.`\n );\n }\n}\n\n/**\n * Write JSON without risking the existing file.\n *\n * Keeps a `.bak-pai` copy of the previous contents, then writes to a temp file\n * and renames. Rename is atomic within a filesystem, so a crash mid-write\n * leaves the original intact rather than truncated — which is how these files\n * become corrupt in the first place.\n */\nexport function writeJsonAtomic(\n path: string,\n data: Record<string, unknown>,\n opts: { backup?: boolean; label?: string } = {}\n): void {\n const { backup = true, label = path } = opts;\n const serialized = JSON.stringify(data, null, 2) + \"\\n\";\n\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\n if (backup && existsSync(path)) {\n try {\n copyFileSync(path, `${path}.bak-pai`);\n } catch (e) {\n throw new Error(\n `Could not back up ${label}: ${e instanceof Error ? e.message : String(e)}\\n` +\n `Refusing to write without a backup.`\n );\n }\n }\n\n const tmp = `${path}.tmp-pai-${process.pid}`;\n try {\n writeFileSync(tmp, serialized, \"utf8\");\n renameSync(tmp, path);\n } catch (e) {\n try { if (existsSync(tmp)) unlinkSync(tmp); } catch { /* best effort */ }\n throw new Error(\n `Failed to write ${label}: ${e instanceof Error ? e.message : String(e)}\\n` +\n `The original is unchanged.`\n );\n }\n}\n","/**\n * config.ts — the `workers` section of ~/.config/pai/config.json\n *\n * Everything that knows about worker providers reads this module: the CLI\n * (`pai worker …`), the MCP tools (worker_*), and the Agent-routing hook.\n * Keep it free of commander/MCP imports so all three layers stay thin over it.\n *\n * Keys are never stored here — only paths to 0600 files. A provider without a\n * keyFile is a local server and gets the placeholder token \"local\".\n *\n * `protocol: \"openai\"` providers run through the PAI proxy (src/workers/proxy):\n * `upstreamUrl` is their Chat Completions base, and `run` points\n * ANTHROPIC_BASE_URL at the local proxy with the provider name in the path.\n * `engine: \"codex\"` providers run through the Codex CLI instead of Claude\n * Code.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readJsonStrict, writeJsonAtomic } from \"../config/json-store.js\";\nimport { CONFIG_FILE } from \"../daemon/config.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Wire protocol a provider speaks: \"anthropic\" natively, \"openai\" through the\n * PAI proxy (which translates the Anthropic Messages API to Chat Completions).\n */\nexport type WorkerProtocol = \"anthropic\" | \"openai\";\n\n/** Runner executable behind a provider: Claude Code or the Codex CLI. */\nexport type WorkerEngine = \"claude\" | \"codex\";\n\nexport interface WorkerProvider {\n enabled: boolean;\n /** Wire protocol the baseUrl speaks. */\n protocol: WorkerProtocol;\n /** Anthropic-compatible Messages API base. */\n baseUrl: string;\n /** 0600 file holding the auth token; null for local servers (\"local\"). */\n keyFile: string | null;\n models: {\n default: string;\n /** Alias used for cheap/fast work (spotcheck, research). */\n fast?: string;\n };\n /** Extra environment variables for runs through this provider (string values). */\n env: Record<string, string>;\n note?: string;\n /**\n * OpenAI Chat Completions base (e.g. \"https://api.openai.com/v1\"). Required\n * for protocol \"openai\"; read by the PAI proxy, never by the runner.\n */\n upstreamUrl?: string;\n /** Runner for this provider; \"codex\" goes through the Codex CLI. */\n engine?: WorkerEngine;\n /** Optional shell command printing 0–100 (percent of quota used). */\n quotaProbe?: string;\n /** Auto-routing skips the provider at or above this percentage. Default 95. */\n quotaSkipAt?: number;\n /** Context window of the provider's model, for the context meter. Default 200000. */\n contextWindow?: number;\n}\n\nexport interface WorkersPaneConfig {\n enabled: boolean;\n /** Font size (points) of the follow-pane profile's font. */\n fontSize: number;\n /** Seconds a pane lingers after its worker goes quiet. */\n autoExitSecs: number;\n}\n\nexport interface WorkersRoutingConfig {\n /** Provider names in preference order; first usable one wins. */\n order: string[];\n /** Minutes a provider sits in cooldown after a quota/rate failure. */\n cooldownMinutes: number;\n /** Reroute a failed-before-first-tool run to the next provider. */\n retryOnQuota: boolean;\n}\n\n/**\n * A role target: \"<provider>\", \"<provider>/<modelAlias>\", or an object with a\n * provider and the MCP servers (or mcpSets names) its workers start with.\n */\nexport type RoleTarget = string | { provider: string; mcp?: string[] };\n\nexport interface WorkersConfig {\n enabled: boolean;\n /** Provider name, or \"auto\" for routing.order resolution. */\n active: string | null;\n providers: Record<string, WorkerProvider>;\n /** Role → target; see RoleTarget. */\n roles: Record<string, RoleTarget>;\n /** MCP set name → server names; `--mcp <set>` and role `mcp` expand these. */\n mcpSets: Record<string, string[]>;\n pane: WorkersPaneConfig;\n logDir: string;\n routing: WorkersRoutingConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_LOG_DIR = \"~/.claude/logs/workers\";\n\nexport const DEFAULT_PANE: WorkersPaneConfig = {\n enabled: true,\n fontSize: 13,\n autoExitSecs: 60,\n};\n\nexport const DEFAULT_ROUTING: WorkersRoutingConfig = {\n order: [],\n cooldownMinutes: 30,\n retryOnQuota: true,\n};\n\nexport function defaultWorkersConfig(): WorkersConfig {\n return {\n enabled: false,\n active: null,\n providers: {},\n roles: {},\n mcpSets: {},\n pane: { ...DEFAULT_PANE },\n logDir: DEFAULT_LOG_DIR,\n routing: { ...DEFAULT_ROUTING, order: [] },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nexport class WorkersConfigError extends Error {}\n\nfunction bad(path: string, why: string): never {\n throw new WorkersConfigError(`workers${path}: ${why}`);\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v : \"\";\n}\n\nfunction parseProvider(name: string, raw: unknown): WorkerProvider {\n if (typeof raw !== \"object\" || raw === null) bad(`.providers.${name}`, \"must be an object\");\n const p = raw as Record<string, unknown>;\n\n const protocol = p.protocol === undefined ? \"anthropic\" : str(p.protocol);\n if (protocol !== \"anthropic\" && protocol !== \"openai\") {\n bad(`.providers.${name}.protocol`, `\"${str(p.protocol)}\" is neither \"anthropic\" nor \"openai\"`);\n }\n const engine = p.engine === undefined ? \"claude\" : str(p.engine);\n if (engine !== \"claude\" && engine !== \"codex\") {\n bad(`.providers.${name}.engine`, `\"${str(p.engine)}\" is neither \"claude\" nor \"codex\"`);\n }\n\n const baseUrl = str(p.baseUrl);\n // openai providers reach the model through the PAI proxy; their baseUrl is\n // the proxy URL, filled in by the runner — only anthropic needs one here.\n if (!baseUrl && protocol !== \"openai\") bad(`.providers.${name}.baseUrl`, \"is required\");\n\n const keyFile =\n p.keyFile === undefined || p.keyFile === null || str(p.keyFile) === \"\"\n ? null\n : str(p.keyFile);\n\n const modelsRaw = p.models === undefined ? {} : p.models;\n if (typeof modelsRaw !== \"object\" || modelsRaw === null) {\n bad(`.providers.${name}.models`, \"must be an object\");\n }\n const m = modelsRaw as Record<string, unknown>;\n const defaultModel = str(m.default);\n if (!defaultModel) bad(`.providers.${name}.models.default`, \"is required\");\n const fast = m.fast === undefined ? undefined : str(m.fast);\n\n const env: Record<string, string> = {};\n if (p.env !== undefined) {\n if (typeof p.env !== \"object\" || p.env === null || Array.isArray(p.env)) {\n bad(`.providers.${name}.env`, \"must be an object of string values\");\n }\n for (const [k, v] of Object.entries(p.env as Record<string, unknown>)) {\n if (typeof v !== \"string\") bad(`.providers.${name}.env.${k}`, \"must be a string\");\n env[k] = v;\n }\n }\n\n const quotaSkipAt = p.quotaSkipAt === undefined ? undefined : p.quotaSkipAt;\n if (quotaSkipAt !== undefined) {\n if (typeof quotaSkipAt !== \"number\" || quotaSkipAt < 0 || quotaSkipAt > 100) {\n bad(`.providers.${name}.quotaSkipAt`, \"must be a number between 0 and 100\");\n }\n }\n\n const contextWindow = p.contextWindow === undefined ? undefined : p.contextWindow;\n if (contextWindow !== undefined) {\n if (typeof contextWindow !== \"number\" || contextWindow <= 0) {\n bad(`.providers.${name}.contextWindow`, \"must be a positive number of tokens\");\n }\n }\n\n const upstreamUrl = str(p.upstreamUrl);\n if (protocol === \"openai\" && !upstreamUrl) {\n bad(`.providers.${name}.upstreamUrl`, `is required for protocol \"openai\" (the Chat Completions base, e.g. \"https://api.openai.com/v1\")`);\n }\n\n return {\n enabled: p.enabled === undefined ? true : p.enabled === true,\n protocol,\n baseUrl,\n keyFile,\n models: fast ? { default: defaultModel, fast } : { default: defaultModel },\n env,\n ...(str(p.note) ? { note: str(p.note) } : {}),\n ...(upstreamUrl ? { upstreamUrl } : {}),\n ...(engine !== \"claude\" ? { engine } : {}),\n ...(str(p.quotaProbe) ? { quotaProbe: str(p.quotaProbe) } : {}),\n ...(quotaSkipAt !== undefined ? { quotaSkipAt } : {}),\n ...(contextWindow !== undefined ? { contextWindow } : {}),\n };\n}\n\n/**\n * Parse and validate a raw `workers` value. Missing section → defaults.\n * Unknown-but-typed garbage → WorkersConfigError naming the offending field.\n */\nexport function parseWorkersConfig(raw: unknown): WorkersConfig {\n const d = defaultWorkersConfig();\n if (raw === undefined || raw === null) return d;\n if (typeof raw !== \"object\" || Array.isArray(raw)) {\n bad(\"\", \"section must be an object\");\n }\n const w = raw as Record<string, unknown>;\n\n const providers: Record<string, WorkerProvider> = {};\n if (w.providers !== undefined) {\n if (typeof w.providers !== \"object\" || w.providers === null || Array.isArray(w.providers)) {\n bad(\".providers\", \"must be an object keyed by provider name\");\n }\n for (const [name, p] of Object.entries(w.providers)) {\n providers[name] = parseProvider(name, p);\n }\n }\n\n const roles: Record<string, RoleTarget> = {};\n if (w.roles !== undefined) {\n if (typeof w.roles !== \"object\" || w.roles === null || Array.isArray(w.roles)) {\n bad(\".roles\", \"must be an object of role → provider[/alias] or {provider, mcp}\");\n }\n for (const [role, target] of Object.entries(w.roles)) {\n if (typeof target === \"object\" && target !== null && !Array.isArray(target)) {\n const o = target as Record<string, unknown>;\n const provider = str(o.provider);\n if (!provider || provider.includes(\" \")) {\n bad(`.roles.${role}.provider`, `invalid provider \"${provider}\"`);\n }\n let mcp: string[] | undefined;\n if (o.mcp !== undefined) {\n if (!Array.isArray(o.mcp) || o.mcp.some((x) => typeof x !== \"string\")) {\n bad(`.roles.${role}.mcp`, \"must be an array of MCP server or set names\");\n }\n mcp = o.mcp as string[];\n }\n roles[role] = mcp ? { provider, mcp } : { provider };\n } else {\n const t = str(target);\n if (!t || t.includes(\" \")) bad(`.roles.${role}`, `invalid target \"${t}\"`);\n roles[role] = t;\n }\n }\n }\n\n const mcpSets: Record<string, string[]> = {};\n if (w.mcpSets !== undefined) {\n if (typeof w.mcpSets !== \"object\" || w.mcpSets === null || Array.isArray(w.mcpSets)) {\n bad(\".mcpSets\", \"must be an object of set name → [server names]\");\n }\n for (const [setName, servers] of Object.entries(w.mcpSets)) {\n if (!Array.isArray(servers) || servers.some((x) => typeof x !== \"string\")) {\n bad(`.mcpSets.${setName}`, \"must be an array of MCP server names\");\n }\n mcpSets[setName] = servers as string[];\n }\n }\n\n let pane = { ...DEFAULT_PANE };\n if (w.pane !== undefined) {\n if (typeof w.pane !== \"object\" || w.pane === null) bad(\".pane\", \"must be an object\");\n const pc = w.pane as Record<string, unknown>;\n if (pc.enabled !== undefined && typeof pc.enabled !== \"boolean\") bad(\".pane.enabled\", \"must be boolean\");\n if (pc.fontSize !== undefined && (typeof pc.fontSize !== \"number\" || pc.fontSize <= 0)) {\n bad(\".pane.fontSize\", \"must be a positive number of points\");\n }\n if (pc.autoExitSecs !== undefined && typeof pc.autoExitSecs !== \"number\") {\n bad(\".pane.autoExitSecs\", \"must be a number\");\n }\n // legacy fontScale (a relative scale, superseded by fontSize) is tolerated and ignored\n pane = {\n enabled: pc.enabled === undefined ? DEFAULT_PANE.enabled : pc.enabled === true,\n fontSize: pc.fontSize === undefined ? DEFAULT_PANE.fontSize : pc.fontSize,\n autoExitSecs: pc.autoExitSecs === undefined ? DEFAULT_PANE.autoExitSecs : pc.autoExitSecs,\n };\n }\n\n let routing = { ...DEFAULT_ROUTING, order: [] as string[] };\n if (w.routing !== undefined) {\n if (typeof w.routing !== \"object\" || w.routing === null) bad(\".routing\", \"must be an object\");\n const r = w.routing as Record<string, unknown>;\n if (r.order !== undefined) {\n if (!Array.isArray(r.order) || r.order.some((x) => typeof x !== \"string\")) {\n bad(\".routing.order\", \"must be an array of provider names\");\n }\n routing.order = r.order as string[];\n }\n if (r.cooldownMinutes !== undefined && typeof r.cooldownMinutes !== \"number\") {\n bad(\".routing.cooldownMinutes\", \"must be a number\");\n }\n if (r.retryOnQuota !== undefined && typeof r.retryOnQuota !== \"boolean\") {\n bad(\".routing.retryOnQuota\", \"must be boolean\");\n }\n routing = {\n order: routing.order,\n cooldownMinutes: r.cooldownMinutes === undefined ? DEFAULT_ROUTING.cooldownMinutes : r.cooldownMinutes,\n retryOnQuota: r.retryOnQuota === undefined ? DEFAULT_ROUTING.retryOnQuota : r.retryOnQuota === true,\n };\n }\n\n const active = w.active === undefined || w.active === null ? null : str(w.active);\n if (active !== null && active !== \"auto\" && !(active in providers)) {\n // Tolerated at parse time (a provider may have been removed while active\n // still names it) but every consumer resolves it to a clear error.\n }\n\n return {\n enabled: w.enabled === undefined ? d.enabled : w.enabled === true,\n active,\n providers,\n roles,\n mcpSets,\n pane,\n logDir: str(w.logDir) || d.logDir,\n routing,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Read / write\n// ---------------------------------------------------------------------------\n\n/**\n * Read the whole config file and return (raw, workers) — the raw record so\n * callers can rewrite it preserving every other section, the parsed+validated\n * workers section. Unreadable config throws (readJsonStrict), missing is fine.\n */\nexport function readWorkersSection(): {\n raw: Record<string, unknown>;\n workers: WorkersConfig;\n} {\n const raw = readJsonStrict(CONFIG_FILE, \"~/.config/pai/config.json\");\n return { raw, workers: parseWorkersConfig(raw.workers) };\n}\n\n/** Write the workers section back into the config file, atomically. */\nexport function writeWorkersSection(\n raw: Record<string, unknown>,\n workers: WorkersConfig\n): void {\n raw.workers = workers;\n writeJsonAtomic(CONFIG_FILE, raw, { label: \"~/.config/pai/config.json\" });\n}\n\n/** Expand a leading ~ (config values are written with `~` to stay portable). */\nexport function expandHome(p: string): string {\n if (p === \"~\" || p.startsWith(\"~/\")) return join(homedir(), p.slice(1));\n return p;\n}\n\n// ---------------------------------------------------------------------------\n// Runnability checks (proxy / codex providers included)\n// ---------------------------------------------------------------------------\n\nexport function assertProviderRunnable(name: string, p: WorkerProvider): void {\n if (p.protocol === \"openai\" && !p.upstreamUrl) {\n throw new WorkersConfigError(\n `provider \"${name}\" uses protocol \"openai\" but has no upstreamUrl — ` +\n `set its Chat Completions base (e.g. \"https://api.openai.com/v1\") ` +\n `so the PAI proxy knows where to translate to.`\n );\n }\n}\n\n/** Where a provider's key file lives, or null. Shared by run + test + add. */\nexport function providerKeyPath(p: WorkerProvider): string | null {\n return p.keyFile ? expandHome(p.keyFile) : null;\n}\n\nexport const DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/** Context window used by the meter when the init event carries none. */\nexport function providerContextWindow(p: WorkerProvider): number {\n return p.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n}\n\n/** Directory under which inline keys (MCP `key` field) are stored, 0600. */\nexport function keysDir(): string {\n return join(homedir(), \".config\", \"pai\", \"keys\");\n}\n","/**\n * paths.ts — where worker run artefacts live.\n *\n * Everything a run writes (event mirror, status file, pane registry, routing\n * state, the empty MCP config) sits under one logDir so the whole tree is\n * disposable and configurable: `workers.logDir`, default ~/.claude/logs/workers.\n */\n\nimport { join } from \"node:path\";\nimport { expandHome, type WorkersConfig } from \"./config.js\";\n\n/** Absolute logDir for the given config. */\nexport function workersLogDir(config: WorkersConfig): string {\n return expandHome(config.logDir);\n}\n\nexport function statusPath(logDir: string, id: string): string {\n return join(logDir, `${id}.status`);\n}\n\nexport function eventsPath(logDir: string, id: string): string {\n return join(logDir, `${id}.jsonl`);\n}\n\nexport function ledgerPath(logDir: string): string {\n return join(logDir, \"ledger.log\");\n}\n\nexport function routingStatePath(logDir: string): string {\n return join(logDir, \"routing-state.json\");\n}\n\nexport function panesDir(logDir: string): string {\n return join(logDir, \"panes\");\n}\n\n/**\n * The strict empty MCP config handed to headless workers. Written into the\n * logDir on demand (never into the user's vendor config directory — this is\n * PAI state, not vendor state).\n */\nexport function noMcpConfigPath(logDir: string): string {\n return join(logDir, \"no-mcp.json\");\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, and\n * whether they appended their own system prompt (the worker contract is then\n * added alongside, not instead). Everything is passed through untouched — the\n * 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 /** --mcp values (repeatable, comma-separated inside one flag). */\n mcp: 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 const mcp: 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 {\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 (\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 { prompt, outputFormat, rest, headless, callerModel, callerMcpConfig, callerSystemPrompt, mcp };\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/** 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 * 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 * 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 { existsSync, readFileSync, renameSync, writeFileSync, readdirSync } 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 /** 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}\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/** 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\nexport function newWorkerId(d: Date = new Date(), pid = process.pid): string {\n const p = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}-${pid}`;\n}\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 tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(status), \"utf8\");\n renameSync(tmp, path);\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\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/** \"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 * 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\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport 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 * routing.ts — provider selection: flag > role > active (possibly \"auto\").\n *\n * Auto-routing walks `workers.routing.order` and takes the first provider\n * that is enabled, out of cooldown, and (when it defines a quotaProbe) under\n * its quotaSkipAt threshold. A run that dies of a quota/rate error puts its\n * provider in cooldown for cooldownMinutes; when that happens before the\n * first tool call and retryOnQuota is set, the runner restarts the same task\n * on the next provider (ledger: WORKER-REROUTE).\n *\n * An explicit --provider or --role always bypasses all of this.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n type WorkerProvider,\n type WorkersConfig,\n WorkersConfigError,\n} from \"./config.js\";\nimport { routingStatePath } from \"./paths.js\";\n\nconst QUOTA_SKIP_DEFAULT = 95;\nconst PROBE_TIMEOUT_MS = 10_000;\n\nexport interface RoutingState {\n /** provider name → ISO timestamp when its cooldown ends */\n cooldowns: Record<string, string>;\n}\n\nexport function readRoutingState(logDir: string): RoutingState {\n const path = routingStatePath(logDir);\n if (!existsSync(path)) return { cooldowns: {} };\n try {\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as RoutingState;\n return { cooldowns: parsed.cooldowns ?? {} };\n } catch {\n return { cooldowns: {} };\n }\n}\n\nexport function writeRoutingState(logDir: string, state: RoutingState): void {\n const path = routingStatePath(logDir);\n const dir = dirname(path);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n renameSync(tmp, path);\n}\n\nexport function cooldownRemaining(\n state: RoutingState,\n provider: string,\n now: Date = new Date()\n): number {\n const end = state.cooldowns[provider];\n if (!end) return 0;\n const ms = Date.parse(end) - now.getTime();\n return ms > 0 ? ms : 0;\n}\n\nexport function setCooldown(\n logDir: string,\n provider: string,\n minutes: number,\n now: Date = new Date()\n): void {\n const state = readRoutingState(logDir);\n state.cooldowns[provider] = new Date(now.getTime() + minutes * 60_000).toISOString();\n writeRoutingState(logDir, state);\n}\n\nexport function clearCooldown(logDir: string, provider: string): void {\n const state = readRoutingState(logDir);\n if (!(provider in state.cooldowns)) return;\n delete state.cooldowns[provider];\n writeRoutingState(logDir, state);\n}\n\n/**\n * Run a provider's quotaProbe and return its percentage (0–100), or null when\n * there is no probe or it printed nothing usable. A probe must never break\n * routing: failures read as \"unknown\", not as \"full\".\n */\nexport function probeQuota(provider: WorkerProvider): number | null {\n if (!provider.quotaProbe) return null;\n try {\n const out = execFileSync(\"/bin/sh\", [\"-c\", provider.quotaProbe], {\n timeout: PROBE_TIMEOUT_MS,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n const m = out.match(/(\\d+(?:\\.\\d+)?)/);\n if (!m) return null;\n return Math.min(100, Math.max(0, Math.round(parseFloat(m[1]))));\n } catch {\n return null;\n }\n}\n\nexport function quotaSkipThreshold(provider: WorkerProvider): number {\n return provider.quotaSkipAt ?? QUOTA_SKIP_DEFAULT;\n}\n\n/** Does this provider exceed its quota threshold right now? */\nexport function quotaExceeded(provider: WorkerProvider): boolean {\n const used = probeQuota(provider);\n return used !== null && used >= quotaSkipThreshold(provider);\n}\n\nexport interface ResolvedTarget {\n providerName: string;\n provider: WorkerProvider;\n /** Model alias from the role (\"glm/fast\" → \"fast\"), null = provider default. */\n modelAlias: string | null;\n /** MCP servers (or set names) from the role target, null when it sets none. */\n roleMcp: string[] | null;\n /** How the provider was chosen — names the bypass rule for rerouting. */\n via: \"flag\" | \"role\" | \"active\" | \"auto\";\n}\n\nexport class NoProviderError extends WorkersConfigError {}\n\nfunction mustExist(config: WorkersConfig, name: string): WorkerProvider {\n const p = config.providers[name];\n if (!p) {\n throw new NoProviderError(\n `no worker provider named \"${name}\". Configured: ` +\n `${Object.keys(config.providers).join(\", \") || \"(none)\"}.` +\n `\\nAdd one with: pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n );\n }\n return p;\n}\n\nfunction mustBeRunnable(name: string, p: WorkerProvider): WorkerProvider {\n if (!p.enabled) {\n throw new NoProviderError(\n `provider \"${name}\" is disabled. Enable it with: pai worker providers enable ${name}`\n );\n }\n return p;\n}\n\n/**\n * Resolve which provider (and model alias) a run uses.\n *\n * @param flagProvider --provider value, highest precedence\n * @param role --role value, looked up in workers.roles\n */\nexport function resolveTarget(\n config: WorkersConfig,\n logDir: string,\n opts: { flagProvider?: string; role?: string } = {}\n): ResolvedTarget {\n if (opts.flagProvider) {\n return {\n providerName: opts.flagProvider,\n provider: mustBeRunnable(opts.flagProvider, mustExist(config, opts.flagProvider)),\n modelAlias: null,\n roleMcp: null,\n via: \"flag\",\n };\n }\n\n if (opts.role) {\n const target = config.roles[opts.role];\n if (!target) {\n throw new NoProviderError(\n `no role named \"${opts.role}\". Defined: ${Object.keys(config.roles).join(\", \") || \"(none)\"}.` +\n `\\nSet one with: pai worker roles set ${opts.role}=<provider[/alias]>`\n );\n }\n const name = typeof target === \"string\" ? target.split(\"/\")[0] : target.provider;\n const alias = typeof target === \"string\" ? target.split(\"/\")[1] : undefined;\n const roleMcp = typeof target === \"string\" ? null : target.mcp ?? null;\n return {\n providerName: name,\n provider: mustBeRunnable(name, mustExist(config, name)),\n modelAlias: alias ?? null,\n roleMcp,\n via: \"role\",\n };\n }\n\n if (config.active !== \"auto\") {\n const name = config.active;\n if (!name) {\n throw new NoProviderError(\n `no active worker provider. Add one with: ` +\n `pai worker providers add <name> --base-url <url> --key-file <path> --model <model>` +\n `\\n(or point one that exists at it: pai worker providers use <name>)`\n );\n }\n return {\n providerName: name,\n provider: mustBeRunnable(name, mustExist(config, name)),\n modelAlias: null,\n roleMcp: null,\n via: \"active\",\n };\n }\n\n // auto: first provider in order that is enabled, cooled-down-free, under quota\n const state = readRoutingState(logDir);\n const skipped: string[] = [];\n for (const name of config.routing.order) {\n const p = config.providers[name];\n if (!p || !p.enabled) continue;\n if (cooldownRemaining(state, name) > 0) {\n skipped.push(`${name}(cooldown)`);\n continue;\n }\n if (quotaExceeded(p)) {\n skipped.push(`${name}(quota)`);\n continue;\n }\n return { providerName: name, provider: p, modelAlias: null, roleMcp: null, via: \"auto\" };\n }\n throw new NoProviderError(\n `auto-routing found no usable provider (order: [${config.routing.order.join(\", \")}]` +\n `${skipped.length ? `; skipped: ${skipped.join(\", \")}` : \"\"}).` +\n `\\nClear a cooldown with: pai worker providers enable <name>`\n );\n}\n\n/**\n * Next provider after `from` in auto order, applying the same filters.\n * Used by rerouting; null when the order is exhausted.\n */\nexport function nextAutoProvider(\n config: WorkersConfig,\n logDir: string,\n from: string,\n now: Date = new Date()\n): string | null {\n const state = readRoutingState(logDir);\n const order = config.routing.order;\n const start = order.indexOf(from);\n for (let i = start + 1; i < order.length; i++) {\n const name = order[i];\n const p = config.providers[name];\n if (!p || !p.enabled) continue;\n const end = state.cooldowns[name];\n if (end && Date.parse(end) > now.getTime()) continue;\n if (quotaExceeded(p)) continue;\n return name;\n }\n return null;\n}\n\n/**\n * Was this failure a quota/rate failure? Detected from the result text the\n * endpoint produced (HTTP status is not visible in the stream events).\n */\nexport function isQuotaFailure(resultText: string): boolean {\n return /usage limit reached|rate limit|quota/i.test(resultText);\n}\n","/**\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 (right, ~40% of\n * the columns), every further one splits the lowest live worker pane\n * horizontally, so panes stack top to bottom. Each pane runs\n * `pai worker follow <id> --auto-exit <n>` under the `pai-worker` dynamic\n * 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>\".\nconst 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 set parentCols to missing value\n try\n set parentCols to columns of s\n end try\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 if profileName is \"\" then\n if splitS is missing value then\n tell s\n set newS to split vertically with default profile\n end tell\n else\n tell splitS\n set newS to split horizontally with default profile\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\n end tell\n else\n tell splitS\n set newS to split horizontally with profile profileName\n end tell\n end if\n end if\n tell newS\n write text followCmd\n end tell\n if splitS is missing value and parentCols is not missing value then\n try\n tell newS\n set columns to (round (parentCols * 0.4))\n end tell\n end try\n end if\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// Split the launching session vertically and run followCmd in the new pane.\nconst 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 tell s\n set newS to split vertically with default profile\n end tell\n tell newS\n write text followCmd\n end tell\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/** 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 // leading space keeps the command out of shell history; `exec` replaces the\n // pane's shell with follow itself, so signals reach it directly and no shell\n // lingers once the pane closes\n const cmd = ` exec pai worker follow ${JSON.stringify(wid)} --auto-exit ${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 * Report-only variant used by `pai worker pane <id> --check`: whether a pane\n * runs for the worker, plus the dynamic profile's path, its existence, and\n * the font it contains (or, when missing, would write).\n */\nexport async function checkPaneForWorker(wid: string, fontSize: number): Promise<string> {\n const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\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 const cmd = \" exec pai 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 * 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 that is a JSON object\n * and nothing else. The runner parses it out of the result; the viewer renders\n * it as a compact block instead of a wall of text.\n */\n\nimport { relative } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport type { Paint } from \"./render.js\";\n\n/** Appended to the caller's system prompt on every headless run (2h). */\nexport const WORKER_CONTRACT_PROMPT = [\n \"You are a headless implementation worker, run non-interactively by an orchestrating session.\",\n \"No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.\",\n \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\",\n '{\"changed\":[{\"path\":\"…\",\"summary\":\"…\"}],\"commands\":[\"…\"],\"checks\":[{\"name\":\"…\",\"ok\":true,\"detail\":\"…\"}],\"open\":[\"…\"],\"notes\":\"one line\"}',\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].join(\"\\n\");\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}\n\n/**\n * Extract the JSON report from a worker's final message. Accepts a bare\n * object, a ```json fenced block, or an object embedded in surrounding text.\n * Only 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 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;\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 * 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…]` (or a role carrying `\"mcp\": [...]`); names may be single\n * servers from ~/.claude.json's `mcpServers` or `workers.mcpSets` set names,\n * which expand to their member 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\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\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 return servers as Record<string, unknown>;\n } catch {\n // a damaged ~/.claude.json must not take workers down with it\n return {};\n }\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 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/** 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 * 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, alive } 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 (status.state !== \"running\" || !alive(status.pid)) {\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 * server.ts — the PAI worker proxy: Anthropic Messages API on the front,\n * OpenAI Chat Completions on the back, loopback only.\n *\n * One proxy serves every OpenAI-protocol provider: Claude Code points\n * ANTHROPIC_BASE_URL at `http://127.0.0.1:8797/<provider>` and the provider\n * name in the path selects the upstream (`upstreamUrl` + `keyFile` from the\n * workers config, re-read per request so config edits apply without a\n * restart). `run` starts the proxy on demand (detached, pid file under the\n * logDir) and `pai worker proxy stop` stops it again.\n *\n * All translation lives in translate.ts; this file is only HTTP plumbing.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer, type Server } from \"node:http\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { readWorkersSection, providerKeyPath, type WorkerProvider } from \"../config.js\";\nimport { workersLogDir } from \"../paths.js\";\nimport {\n anthropicError,\n anthropicToOpenAi,\n openAiToAnthropic,\n OpenAiStreamTranslator,\n type AnthropicRequest,\n} from \"./translate.js\";\n\nexport const DEFAULT_PROXY_PORT = 8797;\nconst HOST = \"127.0.0.1\";\n\n/** How providers are looked up — swapped out by the tests. */\nexport type ProviderResolver = () => Record<string, WorkerProvider>;\n\nconst configProviders: ProviderResolver = () => readWorkersSection().workers.providers;\n\nexport function proxyPidPath(logDir: string): string {\n return join(logDir, \"proxy.pid\");\n}\n\n// ---------------------------------------------------------------------------\n// HTTP server\n// ---------------------------------------------------------------------------\n\ninterface ProxyServerOptions {\n port?: number;\n resolveProvider?: ProviderResolver;\n /** Test hook: called with every (translated) upstream request body. */\n onUpstreamRequest?: (url: string, body: unknown) => void;\n}\n\n/**\n * Create (but not start) the proxy server. Routes:\n * GET /healthz → 200 ok\n * POST /<provider>/v1/messages (and /<provider>/messages) → translated call\n */\nexport function createProxyServer(opts: ProxyServerOptions = {}): Server {\n const resolveProvider = opts.resolveProvider ?? configProviders;\n return createServer((req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${HOST}`);\n if (req.method === \"GET\" && url.pathname === \"/healthz\") {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"ok\");\n return;\n }\n const m = url.pathname.match(/^\\/([a-zA-Z0-9_-]+)\\/(v1\\/)?messages$/);\n if (req.method !== \"POST\" || !m) {\n res.writeHead(404, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(404, `no such route: ${req.method} ${url.pathname}`)));\n return;\n }\n const providerName = m[1];\n const chunks: Buffer[] = [];\n req.on(\"data\", (c: Buffer) => chunks.push(c));\n req.on(\"end\", () => {\n void handleMessages(providerName, Buffer.concat(chunks).toString(\"utf8\"), resolveProvider, res, opts);\n });\n req.on(\"error\", () => {\n res.writeHead(400, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(400, \"request body read failed\")));\n });\n });\n}\n\nasync function handleMessages(\n providerName: string,\n bodyText: string,\n resolveProvider: ProviderResolver,\n res: import(\"node:http\").ServerResponse,\n opts: ProxyServerOptions\n): Promise<void> {\n const fail = (status: number, message: string) => {\n res.writeHead(status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(status, JSON.stringify({ message }))));\n };\n let reqBody: AnthropicRequest;\n try {\n reqBody = JSON.parse(bodyText) as AnthropicRequest;\n } catch {\n fail(400, \"request body is not valid JSON\");\n return;\n }\n const provider = resolveProvider()[providerName];\n if (!provider) {\n fail(404, `no worker provider named \"${providerName}\"`);\n return;\n }\n if (provider.protocol !== \"openai\" || !provider.upstreamUrl) {\n fail(400, `provider \"${providerName}\" is not an openai-protocol provider`);\n return;\n }\n\n const model = provider.models.default;\n const openaiBody = anthropicToOpenAi(reqBody, model);\n opts.onUpstreamRequest?.(`${provider.upstreamUrl}/chat/completions`, openaiBody);\n\n let upstream: Response;\n try {\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n const keyPath = providerKeyPath(provider);\n if (keyPath) {\n try {\n headers.authorization = `Bearer ${readFileSync(keyPath, \"utf8\").trim()}`;\n } catch {\n fail(500, `key file not readable: ${keyPath}`);\n return;\n }\n }\n upstream = await fetch(`${provider.upstreamUrl}/chat/completions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(openaiBody),\n });\n } catch (e) {\n fail(502, `upstream unreachable: ${e instanceof Error ? e.message : String(e)}`);\n return;\n }\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n res.writeHead(upstream.status, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(anthropicError(upstream.status, text)));\n return;\n }\n\n if (reqBody.stream) {\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n });\n const translator = new OpenAiStreamTranslator(model);\n let buf = \"\";\n if (!upstream.body) {\n res.end(translator.finish());\n return;\n }\n const reader = upstream.body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += new TextDecoder().decode(value, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line.startsWith(\"data:\")) continue;\n const payload = line.slice(5).trim();\n if (payload === \"[DONE]\") {\n res.write(translator.finish());\n } else {\n try {\n res.write(translator.feed(JSON.parse(payload) as Parameters<typeof translator.feed>[0]));\n } catch {\n // skip a malformed chunk rather than kill the stream\n }\n }\n }\n }\n } finally {\n res.write(translator.finish()); // no-op when [DONE] already finished it\n res.end();\n }\n return;\n }\n\n const json = (await upstream.json().catch(() => null)) as unknown;\n if (!json) {\n fail(502, \"upstream returned a non-JSON body\");\n return;\n }\n res.writeHead(200, { \"content-type\": \"application/json\" });\n res.end(JSON.stringify(openAiToAnthropic(json as Parameters<typeof openAiToAnthropic>[0], model)));\n}\n\n// ---------------------------------------------------------------------------\n// Start / stop\n// ---------------------------------------------------------------------------\n\n/** Listen on host:port (loopback). Resolves with the bound port. */\nexport function listenProxy(server: Server, port = DEFAULT_PROXY_PORT): Promise<number> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, HOST, () => {\n const addr = server.address();\n resolve(typeof addr === \"object\" && addr ? addr.port : port);\n });\n });\n}\n\n/** True when something already accepts connections on the port. */\nexport function proxyListening(port = DEFAULT_PROXY_PORT, timeoutMs = 400): Promise<boolean> {\n return new Promise((resolve) => {\n const sock = connect({ port, host: HOST });\n const done = (ok: boolean) => {\n sock.removeAllListeners();\n sock.destroy();\n resolve(ok);\n };\n sock.setTimeout(timeoutMs, () => done(false));\n sock.once(\"connect\", () => done(true));\n sock.once(\"error\", () => done(false));\n });\n}\n\n/**\n * Where the standalone proxy artefact lives, found from this module's own\n * location (dist/cli bundle → ../hooks/, dev checkout → <repo>/dist/hooks).\n */\nexport function standaloneProxyPath(): string | null {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 6; i++) {\n for (const cand of [\n join(dir, \"worker-proxy.mjs\"),\n join(dir, \"hooks\", \"worker-proxy.mjs\"),\n join(dir, \"dist\", \"hooks\", \"worker-proxy.mjs\"),\n ]) {\n if (existsSync(cand)) return cand;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\n/**\n * Ensure a proxy is listening on the port; start the detached standalone when\n * nothing answers. Returns the base URL (`http://127.0.0.1:<port>`).\n */\nexport async function ensureProxyRunning(port: number, logDir: string): Promise<string> {\n if (await proxyListening(port)) return `http://${HOST}:${port}`;\n const script = standaloneProxyPath();\n if (!script) {\n throw new Error(\n `the PAI worker proxy is not running and its build was not found — ` +\n `run \\`bun run build\\` (or start one with: pai worker proxy --port ${port})`\n );\n }\n if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n const child = spawn(process.execPath, [script, \"--port\", String(port)], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n writeFileSync(proxyPidPath(logDir), `${child.pid}\\n`, \"utf8\");\n for (let i = 0; i < 40; i++) {\n if (await proxyListening(port)) return `http://${HOST}:${port}`;\n await new Promise((r) => setTimeout(r, 250));\n }\n throw new Error(`the PAI worker proxy did not come up on port ${port} (see ${proxyPidPath(logDir)})`);\n}\n\n/** `pai worker proxy stop`: SIGTERM the pid from the pid file. */\nexport function stopProxy(logDir: string): string {\n const path = proxyPidPath(logDir);\n if (!existsSync(path)) return \"no proxy pid file — nothing to stop\";\n const pid = parseInt(readFileSync(path, \"utf8\").trim(), 10);\n unlinkSync(path);\n if (!Number.isFinite(pid)) return \"stale proxy pid file removed\";\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n return `proxy pid ${pid} was not running (pid file removed)`;\n }\n return `proxy pid ${pid} stopped`;\n}\n","/**\n * 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, readFileSync as readKey } from \"node:fs\";\nimport { providerKeyPath, 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 keyFile/baseUrl; ChatGPT-login codex\n // (no keyFile) keeps its own auth from ~/.codex.\n const keyPath = providerKeyPath(provider);\n if (keyPath && existsSync(keyPath)) {\n env.OPENAI_API_KEY = readKey(keyPath, \"utf8\").trim();\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. OpenAI-protocol\n * providers point at the PAI proxy instead (started on demand, the\n * provider name in the URL path); codex-engine providers run 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),\n * PAI_WORKER=1 so PAI's per-session hooks leave it alone, 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 restriction, no pane, ENABLE_TOOL_SEARCH=true.\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 {\n existsSync,\n mkdirSync,\n openSync,\n readFileSync as readKey,\n writeFileSync,\n closeSync,\n writeSync,\n} from \"node:fs\";\nimport { parseRunnerArgs, shortText, stripPromptValues } from \"./args.js\";\nimport {\n assertProviderRunnable,\n providerContextWindow,\n providerKeyPath,\n readWorkersSection,\n type WorkerProvider,\n} from \"./config.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { eventsPath, ledgerPath, noMcpConfigPath, workersLogDir } from \"./paths.js\";\nimport {\n type WorkerStatus,\n newWorkerId,\n saveStatus,\n describeTool,\n nowStamp,\n} from \"./status.js\";\nimport { resolveSession } from \"./scope.js\";\nimport { isQuotaFailure, nextAutoProvider, resolveTarget, setCooldown } from \"./routing.js\";\nimport { openPaneForWorker } from \"./pane.js\";\nimport { WORKER_CONTRACT_PROMPT, parseWorkerReport, type WorkerReport } from \"./report.js\";\nimport { expandMcpNames, writeMcpConfig } from \"./mcp.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 role?: string;\n modelFlag?: string;\n label?: string;\n noPane?: boolean;\n /** --mcp value: server/set names, comma-separated. */\n mcpFlag?: string;\n /** Everything after `--` (the claude args). */\n claudeArgs: string[];\n /** Internal: notified with the worker id once it exists (resume uses it). */\n onWorkerStart?: (wid: string) => void;\n /** Internal: suppress recursion depth on reroute. */\n _reroutes?: number;\n}\n\n/** Environment for a run through `provider`. Caller's env minus the Anthropic key. */\nexport function buildRunEnv(\n provider: WorkerProvider,\n headless: boolean,\n proxyUrl?: string\n): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = { ...process.env };\n delete env.ANTHROPIC_API_KEY;\n\n let token = \"local\";\n if (proxyUrl) {\n // openai-protocol provider: the proxy holds the real key; the runner only\n // needs a placeholder so Claude Code sends an auth header at all\n env.ANTHROPIC_BASE_URL = proxyUrl;\n } else {\n const keyPath = providerKeyPath(provider);\n if (keyPath) {\n try {\n token = readKey(keyPath, \"utf8\").trim();\n } catch {\n // the caller turns this into a clear error before spawning\n throw new Error(`key file not readable: ${keyPath}`);\n }\n if (!token) throw new Error(`key file is empty: ${keyPath}`);\n }\n env.ANTHROPIC_BASE_URL = provider.baseUrl;\n }\n\n env.ANTHROPIC_AUTH_TOKEN = token;\n env.ANTHROPIC_DEFAULT_HAIKU_MODEL = provider.models.fast ?? provider.models.default;\n env.ANTHROPIC_DEFAULT_SONNET_MODEL = provider.models.default;\n env.ANTHROPIC_DEFAULT_OPUS_MODEL = provider.models.default;\n for (const [k, v] of Object.entries(provider.env)) env[k] = v;\n env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = \"1\";\n if (headless) {\n env.PAI_WORKER = \"1\";\n } else {\n env.ENABLE_TOOL_SEARCH = \"true\";\n }\n return env;\n}\n\n/** The strict empty MCP config for headless workers, written on demand. */\nexport function ensureNoMcpConfig(logDir: string): string {\n const path = noMcpConfigPath(logDir);\n if (!existsSync(path)) {\n mkdirSync(logDir, { recursive: true });\n writeFileSync(path, '{ \"mcpServers\": {} }\\n', \"utf8\");\n }\n return path;\n}\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/** ISO stamp with seconds, attached to every mirrored event (2g). */\nfunction isoStamp(d = new Date()): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\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 num_turns?: number;\n duration_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/** Context window announced by the init event, when the endpoint sends one. */\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 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 const target = resolveTarget(config, logDir, {\n flagProvider: opts.providerFlag,\n role: opts.role,\n });\n assertProviderRunnable(target.providerName, target.provider);\n\n const parsed = parseRunnerArgs(opts.claudeArgs);\n const label =\n opts.label ??\n shortText(parsed.prompt ?? \"(no prompt)\", 70);\n\n const model =\n opts.modelFlag ??\n (target.modelAlias === \"fast\"\n ? target.provider.models.fast ?? target.provider.models.default\n : target.provider.models.default);\n\n try {\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 onWorkerStart: opts.onWorkerStart,\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 onWorkerStart: opts.onWorkerStart,\n reroutes: opts._reroutes ?? 0,\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\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 onWorkerStart?: (wid: string) => void;\n reroutes: number;\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 = newWorkerId();\n const cwd = process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const session = resolveSession(term);\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 ...(session ? { session } : {}),\n contextWindow: providerContextWindow(target.provider),\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 });\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 // MCP: caller config > allowlist (--mcp flag / --mcp args / role) > the strict empty set.\n let mcpArgs: string[] = [];\n if (headless && !parsed.callerMcpConfig) {\n const wanted = [\n ...(a.mcpFlag ? [a.mcpFlag] : []),\n ...parsed.mcp,\n ...(target.roleMcp ?? []),\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 }\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 cmd: string[] = [\"claude\"];\n if (!parsed.callerModel) cmd.push(\"--model\", model);\n cmd.push(...mcpArgs, ...restArgs);\n if (headless) {\n cmd.push(\"--output-format\", \"stream-json\", \"--verbose\", \"--input-format\", \"stream-json\");\n if (!parsed.callerSystemPrompt) cmd.push(\"--append-system-prompt\", WORKER_CONTRACT_PROMPT);\n }\n\n const t0 = Date.now();\n const proc = spawn(cmd[0], cmd.slice(1), {\n env,\n 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 writeEvent({ type: \"operator\", text });\n try {\n proc.stdin?.write(stdinUserMessage(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 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\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 (parsed.prompt !== null) {\n try {\n proc.stdin!.write(stdinUserMessage(parsed.prompt) + \"\\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 const cw = initContextWindow(e);\n if (cw) status.contextWindow = cw;\n saveStatus(logDir, status);\n } else if (e.type === \"assistant\") {\n status.turns += 1;\n const tokens = usageContextTokens(e.message?.usage);\n if (tokens) status.contextTokens = tokens;\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 if (tokens) status.contextTokens = 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 = rc === 0 && resultEvent !== null && !resultEvent.is_error;\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 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 if (headless) printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, ctx.resultReport);\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 claudeArgs: a.claudeArgs,\n onWorkerStart: a.onWorkerStart,\n _reroutes: a.reroutes + 1,\n });\n }\n }\n\n return rc !== 0 ? rc : ok ? 0 : 1;\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 = newWorkerId();\n const cwd = process.cwd();\n const term = process.env.ITERM_SESSION_ID ?? \"\";\n const session = resolveSession(term);\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 ...(session ? { session } : {}),\n contextWindow: providerContextWindow(target.provider),\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 });\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(parsed.prompt, parsed.callerModel ? undefined : model), {\n env,\n 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({ type: \"system\", subtype: \"init\", model, cwd });\n\n let killed = false;\n process.once(\"SIGTERM\", onCodexSignal(\"SIGTERM\"));\n process.once(\"SIGINT\", onCodexSignal(\"SIGINT\"));\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 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 if (fold.contextTokens) status.contextTokens = 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 printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, report);\n return rc !== 0 ? rc : ok ? 0 : 1;\n}\n\n// ---------------------------------------------------------------------------\n// result printing\n// ---------------------------------------------------------------------------\n\nfunction 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): 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 }\n : resultEvent ?? { is_error: true, result: \"no result event\", rc };\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","/**\n * render.ts — turn worker events into the lines a human reads.\n *\n * Ports the glm-ps transcript rendering: the gutter rules (dim `>` for reads\n * and searches, `$` for shell, magenta `~` + red/green diff for edits, green\n * `+` for writes), Read-result trimming (first 3 lines then a count), and the\n * result footer. Colors are applied only when the output is a TTY, so MCP\n * tool output stays plain text.\n */\n\nimport { relative, basename } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { ageOf, contextPercent, type WorkerStatus, alive } from \"./status.js\";\nimport { sessionTag } from \"./scope.js\";\nimport { parseWorkerReport, renderReport } from \"./report.js\";\n\nexport type ColorEnabled = boolean;\n\nconst CODES = {\n dim: \"2\",\n bold: \"1\",\n red: \"31\",\n green: \"32\",\n yellow: \"33\",\n blue: \"34\",\n mag: \"35\",\n cyan: \"36\",\n} as const;\n\nexport type ColorName = keyof typeof CODES;\n\nexport function makeColor(enabled: ColorEnabled) {\n return (name: ColorName, s: string): string =>\n enabled ? `\\x1b[${CODES[name]}m${s}\\x1b[0m` : s;\n}\n\nexport type Paint = ReturnType<typeof makeColor>;\n\n/** Path relative to cwd when it lies inside it, otherwise unchanged. */\nexport function relPath(path: string, cwd: string): string {\n if (!path || !cwd) return path;\n let r: string;\n try {\n r = relative(cwd, path);\n } catch {\n return path;\n }\n return r.startsWith(\"..\") ? path : r;\n}\n\n/** Minimal line diff for Edit previews: common prefix/suffix, one hunk. */\nexport function unifiedDiffLines(oldStr: string, newStr: string): string[] {\n const oldL = oldStr.split(\"\\n\");\n const newL = newStr.split(\"\\n\");\n let start = 0;\n while (start < oldL.length && start < newL.length && oldL[start] === newL[start]) start++;\n let endOld = oldL.length;\n let endNew = newL.length;\n while (endOld > start && endNew > start && oldL[endOld - 1] === newL[endNew - 1]) {\n endOld--;\n endNew--;\n }\n const out: string[] = [];\n for (let i = start; i < endOld; i++) out.push(\"-\" + oldL[i]);\n for (let i = start; i < endNew; i++) out.push(\"+\" + newL[i]);\n return out;\n}\n\nexport interface ToolUseBlock {\n type?: string;\n text?: string;\n name?: string;\n id?: string;\n input?: unknown;\n}\n\nexport interface StreamEventLike {\n type?: string;\n subtype?: string;\n model?: string;\n cwd?: string;\n message?: { content?: ToolUseBlock[] };\n result?: string;\n is_error?: boolean;\n num_turns?: number;\n duration_ms?: number;\n /** ISO stamp the runner attaches to every mirrored event (2g). */\n _ts?: string;\n /** Operator text (type: \"operator\"). */\n text?: string;\n}\n\n/**\n * The transcript gutter (2g): `HH:MM:SS │ ` from the event's `_ts`, dim, with\n * the worker tag in front when several run at once. Continuation lines get\n * blanks of the same width so wrapped text stays aligned. null when the event\n * carries no stamp (logs from before 2g render with the plain prefix).\n */\nexport function gutterFor(\n c: Paint,\n e: { _ts?: string },\n tag?: string\n): { first: string; cont: string } | null {\n if (!e._ts) return null;\n const time = e._ts.length >= 19 ? e._ts.slice(11, 19) : e._ts;\n const head = tag ? `${tag} ${time}` : time;\n const width = head.length + 3; // + \" │ \"\n return {\n first: c(\"dim\", `${head} │ `),\n cont: \" \".repeat(width),\n };\n}\n\n/** Compact token count: 84k, 200k, 900. */\nfunction fmtK(n: number): string {\n return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);\n}\n\n/**\n * The context meter `ctx 84k/200k (42%)`, yellow from 70 %, red from 85 %.\n * null when the numbers are missing or below `minPct` (the table only shows\n * it past 60; the pane liveness line always shows it).\n */\nexport function contextMeter(\n c: Paint,\n s: Pick<WorkerStatus, \"contextTokens\" | \"contextWindow\">,\n minPct = 0\n): string | null {\n const pct = contextPercent(s);\n if (pct === null || pct <= minPct) return null;\n const label = `ctx ${fmtK(s.contextTokens ?? 0)}/${fmtK(s.contextWindow ?? 0)} (${pct}%)`;\n if (pct > 85) return c(\"red\", label);\n if (pct > 70) return c(\"yellow\", label);\n return label;\n}\n\nfunction renderToolUse(\n c: Paint,\n prefix: string,\n name: string,\n inp: unknown,\n cwd: string\n): string[] {\n const i = (typeof inp === \"object\" && inp !== null ? inp : {}) as Record<string, unknown>;\n const get = (k: string) => (typeof i[k] === \"string\" ? (i[k] as string) : \"\");\n if (name === \"Read\") {\n return [`${prefix}${c(\"dim\", \">\")} Reading ${relPath(get(\"file_path\"), cwd)}`];\n }\n if (name === \"Grep\" || name === \"Glob\") {\n return [\n `${prefix}${c(\"dim\", \">\")} Searching ${c(\"cyan\", get(\"pattern\"))} in ${relPath(get(\"path\") || \".\", cwd)}`,\n ];\n }\n if (name === \"Bash\") {\n return [`${prefix}${c(\"dim\", \"$\")} ${shortText(get(\"command\"), 160)}`];\n }\n if (name === \"Edit\") {\n const out = [`${prefix}${c(\"mag\", \"~\")} Editing ${relPath(get(\"file_path\"), cwd)}`];\n for (const line of unifiedDiffLines(get(\"old_string\"), get(\"new_string\"))) {\n if (line.startsWith(\"-\")) out.push(`${prefix} ${c(\"red\", line)}`);\n else if (line.startsWith(\"+\")) out.push(`${prefix} ${c(\"green\", line)}`);\n else out.push(`${prefix} ${c(\"dim\", line)}`);\n }\n return out;\n }\n if (name === \"Write\") {\n const n = get(\"content\").split(\"\\n\").length;\n return [`${prefix}${c(\"green\", \"+\")} Writing ${relPath(get(\"file_path\"), cwd)} (${n} lines)`];\n }\n if (name === \"WebSearch\" || name === \"WebFetch\") {\n return [`${prefix}${c(\"dim\", \">\")} ${name} ${shortText(get(\"query\") || get(\"url\"), 100)}`];\n }\n return [`${prefix}${c(\"dim\", \">\")} ${name} ${shortText(JSON.stringify(i), 120)}`];\n}\n\n/** Render one stream-json event; `tools` maps tool_use_id → tool name. */\nexport function renderEvent(\n c: Paint,\n prefix: string,\n e: StreamEventLike,\n cwd: string,\n tools: Record<string, string>,\n gutter?: { first: string; cont: string } | null\n): string[] {\n const out: string[] = [];\n if (e.type === \"system\" && e.subtype === \"init\") {\n out.push(\n `${prefix}${c(\"dim\", `worker started · model ${e.model ?? \"?\"} · cwd ${basename(e.cwd || cwd || \"\")}`)}`\n );\n } else if (e.type === \"operator\") {\n // split per line so the gutter continuation pads wrapped text\n for (const ln of String(e.text ?? \"\").split(\"\\n\")) {\n out.push(`${prefix}${c(\"cyan\", \"» \" + ln)}`);\n }\n } else if (e.type === \"assistant\") {\n for (const b of e.message?.content ?? []) {\n if (b.type === \"text\" && (b.text ?? \"\").trim()) {\n for (const ln of (b.text ?? \"\").trim().split(\"\\n\")) out.push(`${prefix}${ln}`);\n } else if (b.type === \"tool_use\") {\n out.push(...renderToolUse(c, prefix, b.name ?? \"?\", b.input, cwd));\n }\n }\n } else if (e.type === \"user\") {\n for (const b of e.message?.content ?? []) {\n if (b.type !== \"tool_result\") continue;\n let content: unknown = (b as { content?: unknown }).content ?? \"\";\n if (Array.isArray(content)) {\n content = content\n .map((x) => (typeof x === \"object\" && x !== null && \"text\" in x ? String((x as { text?: string }).text ?? \"\") : \"\"))\n .join(\"\\n\");\n }\n const text = String(content);\n const isError = (b as { is_error?: boolean }).is_error === true;\n if (!isError && tools[(b as { tool_use_id?: string }).tool_use_id ?? \"\"] === \"Read\") {\n // keep Read results as the tool returned them (`<lineno>\\t<code>`),\n // only their count is summarised\n const lines = text.split(\"\\n\");\n for (const ln of lines.slice(0, 3)) out.push(`${prefix}${c(\"dim\", ln)}`);\n if (lines.length > 3) {\n out.push(`${prefix}${c(\"dim\", ` … ${lines.length} lines`)}`);\n }\n } else if (isError) {\n out.push(`${prefix} ${c(\"red\", \"! \" + shortText(text, 200))}`);\n } else if (text.trim()) {\n out.push(`${prefix} ${c(\"dim\", shortText(text, 120))}`);\n }\n }\n } else if (e.type === \"result\") {\n const ok = !e.is_error;\n const mark = ok ? c(\"green\", \"✓ done\") : c(\"red\", \"✗ failed\");\n out.push(`${prefix}${mark} · ${e.num_turns ?? \"?\"} turns · ${Math.floor((e.duration_ms ?? 0) / 1000)}s`);\n // a contract-compliant final message renders as the compact report block\n const report = parseWorkerReport(String(e.result ?? \"\"));\n if (report) {\n out.push(...renderReport(c, prefix, report, cwd));\n } else {\n for (const ln of String(e.result ?? \"\").trim().split(\"\\n\")) {\n out.push(`${prefix} ${ln}`);\n }\n }\n }\n if (!gutter) return out;\n return out.map((ln, i) => (i === 0 ? gutter.first : gutter.cont) + ln);\n}\n\n/** Transcript header: id, provider, label, session name, project dir. */\nexport function headerLine(\n c: Paint,\n s: { id: string; label: string; cwd: string; provider?: string; session?: { name?: string } | null }\n): string {\n const bits = [s.provider ? `[${s.provider}]` : \"\", sessionTag(s)].filter(Boolean).join(\" \");\n const sep = bits ? ` ${bits}` : \"\";\n return c(\"bold\", `━━ ${s.id}${sep} ${s.label} (${basename(s.cwd)})`);\n}\n\n/** The ps table (RUNNING + FINISHED last 8). */\nexport function renderTable(\n c: Paint,\n statuses: WorkerStatus[],\n scopeLabel: string,\n now: Date = new Date()\n): string {\n const running: WorkerStatus[] = [];\n const done: WorkerStatus[] = [];\n for (const s of statuses) {\n if (s.state === \"running\" && alive(s.pid)) running.push(s);\n else {\n if (s.state === \"running\") s.state = \"lost\";\n done.push(s);\n }\n }\n const clock = `${String(now.getHours()).padStart(2, \"0\")}:${String(now.getMinutes()).padStart(2, \"0\")}:${String(now.getSeconds()).padStart(2, \"0\")}`;\n const lines: string[] = [c(\"bold\", `Workers ${clock}`), \"\"];\n lines.push(c(\"bold\", `RUNNING (${running.length})`));\n if (!running.length) lines.push(\" none\");\n for (const s of running) {\n const meter = contextMeter(c, s, 60);\n lines.push(\n ` ${c(\"cyan\", s.id)} [${s.provider}] ${ageOf(s.started, now).padStart(4)} old turns ${String(s.turns).padStart(2)} tools ${String(s.tools).padStart(2)} ${basename(s.cwd)}${meter ? \" \" + meter : \"\"}`\n );\n lines.push(` task: ${s.label}`);\n lines.push(` now: ${c(\"yellow\", s.last)} (${ageOf(s.updated, now)} ago)`);\n }\n lines.push(\"\");\n lines.push(c(\"bold\", \"FINISHED (last 8)\"));\n for (const s of done.slice(-8)) {\n const col = s.state === \"done\" ? \"green\" : \"red\";\n const tag = sessionTag(s);\n lines.push(\n ` ${s.id} [${s.provider}]${tag ? \" \" + tag : \"\"} ${c(col, s.state.padEnd(6))} rc=${s.rc} ${String(s.secs ?? \"?\").padStart(4)}s turns ${String(s.turns).padStart(2)} tools ${String(s.tools).padStart(2)} ${basename(s.cwd)} ${s.label}`\n );\n }\n lines.push(\"\");\n lines.push(c(\"dim\", \"worker follow live transcript of running workers\"));\n lines.push(c(\"dim\", \"worker <id> replay one worker\"));\n lines.push(c(\"dim\", scopeLabel));\n return lines.join(\"\\n\");\n}\n\n/** One-line status-bar summary (same shape glm-ps --status printed). */\nexport function renderStatusLine(\n mine: WorkerStatus[],\n now: Date = new Date(),\n c: Paint = makeColor(true)\n): string {\n if (!mine.length) return \"\";\n const running = mine.filter((s) => s.state === \"running\" && alive(s.pid));\n const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, \"0\")}-${String(now.getDate()).padStart(2, \"0\")}`;\n const doneToday = mine.filter((s) => s.state !== \"running\" && s.started.startsWith(today));\n const ok = doneToday.filter((s) => s.state === \"done\").length;\n const bad = doneToday.length - ok;\n const providers = new Set(running.map((s) => s.provider));\n const providerTag = providers.size === 1 ? [...providers][0] : \"workers\";\n let head = `${providerTag} ▶${running.length}`;\n const parts = running.slice(0, 3).map((s) => {\n // context load joins the summary once it passes 60 % (yellow >70, red >85)\n const meter = contextMeter(c, s, 60);\n return (\n `${s.id.slice(-4)} ${s.label.slice(0, 26)} ${ageOf(s.started, now)} · ${s.last.slice(0, 30)}` +\n (meter ? ` ${meter}` : \"\")\n );\n });\n if (parts.length) head += \" \" + parts.join(\" | \");\n if (doneToday.length) head += ` ✓${ok} ✗${bad} today`;\n return head;\n}\n","/**\n * viewer.ts — ps / follow / replay / status line over the worker logDir.\n *\n * Scoping: workers launched from this terminal's AIBroker session (or, as the\n * fallback, its iTerm tab) unless --all or an explicit worker id is given.\n * Outside iTerm, everything degrades to \"all workers\" — the Python behaviour.\n *\n * Transcripts render with a `HH:MM:SS │ ` gutter (2g): dim, taken from the\n * `_ts` stamp on every mirrored event, the worker tag in front when several\n * run at once, a date separator when the day changes, and — on a TTY — a\n * liveness line (`⋯ 12s since last event · Bash: npm test · ctx 84k/200k (42%)`)\n * that is rewritten in place between events. Following one worker also wires\n * this pane's stdin: every typed line is said to the worker while it runs and\n * resumes it (same Claude session) once it has finished.\n */\n\nimport { existsSync, openSync, readSync, closeSync, readFileSync } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { spawn } from \"node:child_process\";\nimport { eventsPath } from \"./paths.js\";\nimport { alive, loadStatuses, type WorkerStatus } from \"./status.js\";\nimport { currentTabKey, resolveSession, workerInScope } from \"./scope.js\";\nimport { sayToWorker } from \"./operator.js\";\nimport {\n contextMeter,\n gutterFor,\n headerLine,\n makeColor,\n renderEvent,\n renderStatusLine,\n renderTable,\n type StreamEventLike,\n type ToolUseBlock,\n} from \"./render.js\";\n\n// ---------------------------------------------------------------------------\n// ps\n// ---------------------------------------------------------------------------\n\nexport function psOutput(\n logDir: string,\n showAll: boolean,\n env: NodeJS.ProcessEnv = process.env,\n color = process.stdout.isTTY === true\n): string {\n const c = makeColor(color);\n const term = env.ITERM_SESSION_ID ?? \"\";\n const statuses = loadStatuses(logDir);\n const scoped = showAll || !term ? statuses : statuses.filter((s) => workerInScope(s, term));\n const scopeLabel =\n showAll || !term\n ? \"scope: all workers\"\n : resolveSession(term)\n ? `scope: session ${resolveSession(term)!.name}`\n : `scope: tab ${currentTabKey(env)} (this iTerm tab)`;\n return renderTable(c, scoped, scopeLabel);\n}\n\n// ---------------------------------------------------------------------------\n// replay\n// ---------------------------------------------------------------------------\n\n/** The rendered transcript of one worker, from the start, as one string. */\nexport function replayOutput(\n logDir: string,\n wid: string,\n color = process.stdout.isTTY === true,\n tailLines?: number\n): string {\n const path = eventsPath(logDir, wid);\n if (!existsSync(path)) {\n throw new Error(`no event log for ${wid}`);\n }\n const c = makeColor(color);\n const st =\n loadStatuses(logDir).find((s) => s.id === wid) ??\n ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n const tools: Record<string, string> = {};\n const out: string[] = [headerLine(c, st)];\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n let lastDay = \"\";\n for (const line of tailLines !== undefined ? lines.slice(-tailLines) : lines) {\n if (!line.trim()) continue;\n let e: StreamEventLike;\n try {\n e = JSON.parse(line) as StreamEventLike;\n } catch {\n continue;\n }\n if (typeof e._ts === \"string\" && e._ts.slice(0, 10) !== lastDay) {\n lastDay = e._ts.slice(0, 10);\n out.push(c(\"dim\", `── ${lastDay} ──`));\n }\n const g = gutterFor(c, e);\n collectTools(e, tools);\n out.push(...renderEvent(c, g ? \"\" : \" \", e, st.cwd ?? \"\", tools, g));\n }\n return out.join(\"\\n\");\n}\n\nfunction collectTools(e: StreamEventLike, tools: Record<string, string>): void {\n if (e.type !== \"assistant\") return;\n for (const b of e.message?.content ?? []) {\n if (b.type === \"tool_use\" && b.id) tools[b.id] = b.name ?? \"?\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// follow\n// ---------------------------------------------------------------------------\n\ninterface FollowHandle {\n fd: number;\n buf: string;\n}\n\n/**\n * The follow exit decision for one worker: it is over once its result event\n * was rendered, or once its status left \"running\" while its pid is gone — a\n * worker killed without writing a result still ends. A status never read\n * (undefined) means \"not over\": follow keeps waiting for the first event.\n */\nexport function workerEnded(\n resultRendered: boolean,\n state: WorkerStatus[\"state\"] | undefined,\n pidAlive: boolean\n): boolean {\n return resultRendered || (state !== undefined && state !== \"running\" && !pidAlive);\n}\n\n/**\n * Tail one worker (target) or the running workers of this scope, live.\n * Auto-exit: with a target, wait `autoExit` seconds after its end; without\n * one, exit once no worker in scope has run for that many seconds in a row,\n * never within the first 30 s. With a target on a TTY, typed stdin lines are\n * said to the worker (or resume it after it finished).\n */\nexport async function followWorkers(\n logDir: string,\n target: string | null,\n showAll: boolean,\n autoExit: number,\n env: NodeJS.ProcessEnv = process.env,\n color = process.stdout.isTTY === true\n): Promise<void> {\n const c = makeColor(color);\n const tty = process.stdout.isTTY === true;\n const term = env.ITERM_SESSION_ID ?? \"\";\n const scopeTab = target || showAll ? \"\" : currentTabKey(env);\n const tools: Record<string, string> = {};\n const handles = new Map<string, FollowHandle>();\n const seenHeader = new Set<string>();\n const finished = new Set<string>();\n const lastDayBy = new Map<string, string>();\n const started = Date.now();\n let idleSince: number | null = null;\n let aborted = false;\n // liveness state (2g): rewritten in place between events, TTY only\n let lastEventAt = Date.now();\n let lastAction = \"waiting for first event\";\n let meterStatus: WorkerStatus | null = null;\n let livenessLen = 0;\n const onInt = () => {\n aborted = true;\n };\n process.once(\"SIGINT\", onInt);\n\n // the plain-length twin of a coloured string (liveness must be erased by width)\n const plainOf = (s: string): string => s.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n const eraseLiveness = () => {\n if (livenessLen > 0) {\n process.stdout.write(\"\\r\" + \" \".repeat(livenessLen) + \"\\r\");\n livenessLen = 0;\n }\n };\n const writeLiveness = () => {\n if (!tty) return;\n eraseLiveness();\n const secs = Math.max(0, Math.floor((Date.now() - lastEventAt) / 1000));\n const meter = meterStatus ? contextMeter(c, meterStatus) : null;\n const plain = `⋯ ${secs}s since last event · ${lastAction}${meter ? ` · ${plainOf(meter)}` : \"\"}`;\n process.stdout.write(\"\\r\" + `⋯ ${c(\"dim\", `${secs}s since last event`)} · ${lastAction}${meter ? ` · ${meter}` : \"\"}`);\n livenessLen = plain.length;\n };\n\n const runningIds = (): string[] =>\n loadStatuses(logDir)\n .filter(\n (s) =>\n s.state === \"running\" &&\n alive(s.pid) &&\n (target !== null || showAll || (scopeTab ? workerInScope(s, term) : true))\n )\n .map((s) => s.id);\n\n // --- operator input (2i): this pane's stdin drives say/resume\n const noteLine = (s: string) => {\n eraseLiveness();\n process.stdout.write(s + \"\\n\");\n };\n const resumeTarget = (text: string, id: string) => {\n noteLine(c(\"dim\", `» resuming ${id} …`));\n // SpawnOptions (not the stdio-tuple overload): we only read stdout and\n // want the plain ChildProcess shape\n const child = spawn(\n \"pai\",\n [\"worker\", \"resume\", id, text, \"--print-id\", \"--no-pane\"],\n { stdio: [\"ignore\", \"pipe\", \"inherit\"] } as import(\"node:child_process\").SpawnOptions\n );\n let idOut = \"\";\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n idOut += chunk.toString(\"utf8\");\n });\n child.on(\"close\", (rc: number | null) => {\n const newId = idOut.trim().split(\"\\n\").pop() ?? \"\";\n if (rc === 0 && /^[0-9]{8}-[0-9]{6}-[0-9]+$/.test(newId)) {\n noteLine(c(\"dim\", `» resumed as ${newId}`));\n target = newId;\n finished.delete(newId);\n seenHeader.delete(newId);\n lastEventAt = Date.now();\n lastAction = \"resumed\";\n } else {\n noteLine(c(\"red\", `» resume failed (rc=${rc})`));\n }\n });\n };\n const handleOperatorLine = (raw: string) => {\n const text = raw.trim();\n if (!text || target === null) return;\n const id = target; // narrowed copy for the async callbacks below\n sayToWorker(logDir, id, text).then(\n () => noteLine(c(\"dim\", `» sent to ${id}`)),\n (e: Error) => {\n if (loadStatuses(logDir).some((s) => s.id === id)) resumeTarget(text, id);\n else noteLine(c(\"red\", `» ${e.message}`));\n }\n );\n };\n // the readline interface keeps stdin flowing — without a close, the process\n // outlives follow itself (a pane then never closes, however long ago the\n // worker ended), so it is closed in the finally block below\n let rlIn: ReturnType<typeof createInterface> | null = null;\n if (target !== null && process.stdin.isTTY) {\n rlIn = createInterface({ input: process.stdin });\n rlIn.on(\"line\", handleOperatorLine);\n }\n\n try {\n for (;;) {\n if (aborted) return;\n for (const wid of target ? [target] : runningIds()) {\n if (handles.has(wid) || finished.has(wid)) continue;\n const path = eventsPath(logDir, wid);\n if (existsSync(path)) handles.set(wid, { fd: openSync(path, \"r\"), buf: \"\" });\n }\n const statuses = new Map(loadStatuses(logDir).map((s) => [s.id, s]));\n const multi = handles.size > 1 || target === null;\n let progressed = false;\n\n for (const [wid, h] of [...handles.entries()]) {\n const st = statuses.get(wid) ?? ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n const prefix = multi ? c(\"cyan\", wid.slice(-4)) + c(\"dim\", \" ┃ \") : \" \";\n // read everything appended since the last poll (fd position advances)\n const buffer = Buffer.alloc(65536);\n for (;;) {\n let n: number;\n try {\n n = readSync(h.fd, buffer, 0, buffer.length, null);\n } catch {\n n = 0;\n }\n if (n <= 0) break;\n h.buf += buffer.toString(\"utf8\", 0, n);\n }\n const lines = h.buf.split(\"\\n\");\n h.buf = lines.pop() ?? \"\";\n for (const line of lines) {\n progressed = true;\n if (!seenHeader.has(wid)) {\n eraseLiveness();\n process.stdout.write(headerLine(c, st) + \"\\n\");\n seenHeader.add(wid);\n }\n if (!line.trim()) continue;\n let e: StreamEventLike;\n try {\n e = JSON.parse(line) as StreamEventLike;\n } catch {\n continue;\n }\n const day = typeof e._ts === \"string\" ? e._ts.slice(0, 10) : \"\";\n if (day && day !== lastDayBy.get(wid)) {\n eraseLiveness();\n process.stdout.write(`${multi ? prefix : \"\"}${c(\"dim\", `── ${day} ──`)}\\n`);\n lastDayBy.set(wid, day);\n }\n collectTools(e, tools);\n const g = gutterFor(c, e, multi ? c(\"cyan\", wid.slice(-4)) : undefined);\n eraseLiveness();\n for (const ln of renderEvent(c, g ? \"\" : prefix, e, st.cwd ?? \"\", tools, g)) {\n process.stdout.write(ln + \"\\n\");\n }\n lastEventAt = Date.now();\n if (e.type === \"operator\") {\n lastAction = \"operator message\";\n } else if (e.type === \"assistant\") {\n const tool = (e.message?.content ?? []).find((b) => b.type === \"tool_use\");\n lastAction = tool ? `${tool.name ?? \"?\"} running` : \"thinking\";\n } else if (e.type === \"result\") {\n lastAction = \"finished\";\n }\n meterStatus = st;\n if (e.type === \"result\") finished.add(wid);\n }\n const ended = workerEnded(finished.has(wid), st.state, alive(st.pid));\n if (ended) {\n if (!finished.has(wid)) {\n eraseLiveness();\n process.stdout.write(`${prefix}${c(\"red\", \"✗ \" + (st.state || \"ended\"))} · ${st.last ?? \"\"}\\n`);\n finished.add(wid);\n }\n closeSync(h.fd);\n handles.delete(wid);\n }\n }\n\n const lingerOn = target; // resume swaps `target` under us (see above)\n if (lingerOn !== null && finished.has(lingerOn)) {\n if (autoExit) {\n const until = Date.now() + autoExit * 1000;\n while (Date.now() < until && !aborted && target === lingerOn) {\n writeLiveness();\n await sleep(250);\n }\n // interrupted, or the worker was resumed inside the window: follow on\n if (aborted || target !== lingerOn) continue;\n eraseLiveness();\n process.stdout.write(c(\"dim\", \"closing\") + \"\\n\");\n return;\n }\n // no auto-exit: only a terminal follow with no wired stdin is done —\n // an interactive one stays up for say / resume input\n if (rlIn === null) return;\n }\n if (autoExit && !target) {\n if (runningIds().length) {\n idleSince = null;\n } else if (idleSince === null) {\n idleSince = Date.now();\n } else if (Date.now() - started >= 30_000 && Date.now() - idleSince >= autoExit * 1000) {\n eraseLiveness();\n process.stdout.write(c(\"dim\", \"closing\") + \"\\n\");\n return;\n }\n }\n if (!progressed) {\n writeLiveness();\n await sleep(500);\n }\n }\n } finally {\n // close before anything else: an open readline keeps the process (and the\n // iTerm pane running it) alive long after follow has decided to end\n rlIn?.close();\n process.removeListener(\"SIGINT\", onInt);\n for (const h of handles.values()) {\n try {\n closeSync(h.fd);\n } catch {\n /* already closed */\n }\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n// ---------------------------------------------------------------------------\n// status line\n// ---------------------------------------------------------------------------\n\n/** Workers of this terminal for the status bar; \"\" when there are none. */\nexport function statusLineOutput(\n logDir: string,\n term: string,\n cwd: string,\n now: Date = new Date()\n): string {\n const statuses = loadStatuses(logDir);\n const mine = statuses.filter((s) => {\n const sameScope = term && workerInScope(s, term);\n const sameDir = cwd && s.cwd.startsWith(cwd);\n return sameScope || (!term && sameDir);\n });\n return renderStatusLine(mine, now);\n}\n","/**\n * providers.ts — provider, role and switch management over the workers config.\n *\n * One layer under both `pai worker providers …` and the MCP worker_providers\n * tool. Every mutation re-reads the config file, changes only the workers\n * section, and writes it back atomically — the file is shared with everything\n * else PAI runs, so a torn write is not an option.\n *\n * Keys are never stored in the config and never accepted inline from the CLI:\n * only file paths. (The MCP `add` tool additionally accepts a raw `key`, which\n * it immediately parks in ~/.config/pai/keys/<name>, mode 0600, storing only\n * the path — a chat is not a place to leave a credential lying around.)\n */\n\nimport { existsSync, writeFileSync, chmodSync, mkdirSync } from \"node:fs\";\nimport {\n DEFAULT_LOG_DIR,\n WorkersConfigError,\n expandHome,\n keysDir,\n parseWorkersConfig,\n readWorkersSection,\n writeWorkersSection,\n type WorkerProvider,\n type WorkersConfig,\n} from \"./config.js\";\nimport { clearCooldown, probeQuota, quotaSkipThreshold } from \"./routing.js\";\nimport { workersLogDir } from \"./paths.js\";\n\nexport interface AddProviderInput {\n name: string;\n baseUrl: string;\n keyFile?: string | null;\n key?: string;\n model: string;\n fastModel?: string;\n env?: Record<string, string>;\n note?: string;\n protocol?: \"anthropic\" | \"openai\";\n upstreamUrl?: string;\n engine?: \"claude\" | \"codex\";\n quotaProbe?: string;\n contextWindow?: number;\n}\n\nexport function addProvider(input: AddProviderInput): WorkersConfig {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(input.name)) {\n throw new WorkersConfigError(\n `provider name \"${input.name}\" may only contain letters, digits, - and _`\n );\n }\n if (input.protocol === \"openai\" && !input.upstreamUrl) {\n throw new WorkersConfigError(\n `protocol \"openai\" needs the upstreamUrl (the Chat Completions base, ` +\n `e.g. \"https://api.openai.com/v1\") — the PAI proxy translates to it.`\n );\n }\n if (!input.baseUrl && input.protocol !== \"openai\") {\n throw new WorkersConfigError(\"baseUrl is required\");\n }\n\n let keyFile = input.keyFile ?? null;\n if (input.key !== undefined) {\n const dir = keysDir();\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const path = `${dir}/${input.name}`;\n writeFileSync(path, input.key.trim() + \"\\n\", { encoding: \"utf8\", mode: 0o600 });\n try {\n chmodSync(path, 0o600);\n } catch {\n /* mode above already applied on create where supported */\n }\n keyFile = path;\n }\n\n const { raw, workers } = readWorkersSection();\n const provider: WorkerProvider = {\n enabled: true,\n protocol: input.protocol ?? \"anthropic\",\n baseUrl: input.baseUrl,\n keyFile,\n models: input.fastModel\n ? { default: input.model, fast: input.fastModel }\n : { default: input.model },\n env: input.env ?? {},\n ...(input.note ? { note: input.note } : {}),\n ...(input.upstreamUrl ? { upstreamUrl: input.upstreamUrl } : {}),\n ...(input.engine && input.engine !== \"claude\" ? { engine: input.engine } : {}),\n ...(input.quotaProbe ? { quotaProbe: input.quotaProbe } : {}),\n ...(input.contextWindow ? { contextWindow: input.contextWindow } : {}),\n };\n workers.providers[input.name] = provider;\n\n // First provider takes over the whole section: active, default roles, pane.\n const first = Object.keys(workers.providers).length === 1;\n if (first) {\n workers.enabled = true;\n workers.active = input.name;\n workers.logDir = workers.logDir || DEFAULT_LOG_DIR;\n workers.roles = {\n implement: input.name,\n research: input.name,\n spotcheck: input.fastModel ? `${input.name}/fast` : input.name,\n };\n }\n\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function removeProvider(name: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (!workers.providers[name]) {\n throw new WorkersConfigError(`no provider named \"${name}\"`);\n }\n delete workers.providers[name];\n for (const [role, target] of Object.entries(workers.roles)) {\n const targetProvider = typeof target === \"string\" ? target.split(\"/\")[0] : target.provider;\n if (targetProvider === name) delete workers.roles[role];\n }\n if (workers.active === name) workers.active = null;\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function useProvider(name: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (!workers.providers[name]) {\n throw new WorkersConfigError(\n `no provider named \"${name}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n );\n }\n workers.active = name;\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function setProviderEnabled(name: string, enabled: boolean): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n const p = workers.providers[name];\n if (!p) throw new WorkersConfigError(`no provider named \"${name}\"`);\n p.enabled = enabled;\n writeWorkersSection(raw, workers);\n // `enable` is also the manual cooldown-clear (spec: routing)\n if (enabled) clearCooldown(workersLogDir(parseWorkersConfig(raw.workers)), name);\n return workers;\n}\n\nexport function setRole(role: string, target: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n const [name, alias] = target.split(\"/\");\n const p = workers.providers[name];\n if (!p) {\n throw new WorkersConfigError(\n `no provider named \"${name}\" in \"${target}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n );\n }\n if (alias && alias !== \"default\" && alias !== \"fast\") {\n throw new WorkersConfigError(\n `unknown model alias \"${alias}\" — providers expose \"default\" and \"fast\"`\n );\n }\n if (alias === \"fast\" && !p.models.fast) {\n throw new WorkersConfigError(`provider \"${name}\" has no fast model configured`);\n }\n workers.roles[role] = target;\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function unsetRole(role: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (!(role in workers.roles)) {\n throw new WorkersConfigError(`no role named \"${role}\"`);\n }\n delete workers.roles[role];\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function setWorkersEnabled(enabled: boolean): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n workers.enabled = enabled;\n writeWorkersSection(raw, workers);\n return workers;\n}\n\n/** Human-readable provider listing (quota probe included when configured). */\nexport function describeProviders(workers: WorkersConfig): string[] {\n const lines: string[] = [];\n const names = Object.keys(workers.providers);\n if (!names.length) {\n lines.push(`no providers configured. Add one with:`);\n lines.push(\n ` pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n );\n return lines;\n }\n for (const name of names) {\n const p = workers.providers[name];\n const flags = [\n p.enabled ? \"enabled\" : \"disabled\",\n workers.active === name ? \"active\" : null,\n ].filter(Boolean);\n const quota = p.quotaProbe ? probeQuota(p) : null;\n const quotaNote = quota === null ? \"\" : ` quota ${quota}% (skip at ${quotaSkipThreshold(p)})`;\n lines.push(`${name} [${flags.join(\", \")}] ${p.baseUrl}`);\n lines.push(\n ` model ${p.models.default}${p.models.fast ? ` (fast: ${p.models.fast})` : \"\"}${quotaNote}`\n );\n if (p.keyFile) lines.push(` key file ${expandHome(p.keyFile)}`);\n else lines.push(` no key file (token \"local\")`);\n if (p.note) lines.push(` ${p.note}`);\n if (p.protocol === \"openai\") {\n lines.push(` via PAI proxy ← ${p.upstreamUrl ?? \"(upstreamUrl missing)\"}`);\n }\n if (p.engine === \"codex\") {\n lines.push(` engine codex (runs through the Codex CLI)`);\n }\n }\n const setNames = Object.keys(workers.mcpSets);\n if (setNames.length) {\n lines.push(`mcp sets: ${setNames.map((s) => `${s}=[${workers.mcpSets[s].join(\",\")}]`).join(\" \")}`);\n }\n if (workers.active === \"auto\") {\n lines.push(`routing: auto — order [${workers.routing.order.join(\", \")}], cooldown ${workers.routing.cooldownMinutes}m`);\n }\n return lines;\n}\n"],"mappings":"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"}