{"version":3,"file":"fallback-CIGspCSi.mjs","names":[],"sources":["../src/config/main-config-ops.ts","../src/workers/specfile.ts","../src/workers/chatui.ts","../src/workers/render.ts","../src/workers/viewer.ts","../src/workers/providers.ts","../src/workers/fallback.ts"],"sourcesContent":["/**\n * main-config-ops.ts — the `pai config list/get/set/unset` operations shared\n * by the CLI (src/cli/commands/config.ts) and the MCP tools\n * (src/daemon-mcp/tools/config.ts). One implementation, two thin callers, so\n * a change to masking or validation cannot drift between them.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { Document } from \"yaml\";\nimport {\n  DEFAULTS,\n  loadConfig,\n  paiConfigFilePath,\n  paiConfigYamlFilePath,\n  readMainConfigRaw,\n  writeMainConfigRaw,\n} from \"../daemon/config.js\";\nimport {\n  readWorkersSection,\n  DEFAULT_PANE,\n  DEFAULT_LOG_DIR,\n  DEFAULT_ROUTING,\n  DEFAULT_TREE,\n  DEFAULT_CACHE_KEEPALIVE_SECS,\n  maskKey,\n} from \"../workers/config.js\";\nimport { migrateMainConfigToYaml } from \"./main-config.js\";\nimport { writeYamlFileAtomic } from \"./yaml-store.js\";\n\nexport class MainConfigOpsError extends Error {}\n\n// ---------------------------------------------------------------------------\n// Dotted-path helpers\n// ---------------------------------------------------------------------------\n\nexport function splitConfigPath(dotted: string): string[] {\n  const segments = dotted.split(\".\").filter((s) => s.length > 0);\n  if (!segments.length) throw new MainConfigOpsError(\"path is required\");\n  return segments;\n}\n\nfunction getAtPath(root: unknown, segments: string[]): { value: unknown; found: boolean } {\n  let cur: unknown = root;\n  for (const seg of segments) {\n    if (cur !== null && typeof cur === \"object\" && !Array.isArray(cur) && seg in (cur as Record<string, unknown>)) {\n      cur = (cur as Record<string, unknown>)[seg];\n    } else {\n      return { value: undefined, found: false };\n    }\n  }\n  return { value: cur, found: true };\n}\n\nfunction setAtPath(root: Record<string, unknown>, segments: string[], value: unknown): Record<string, unknown> {\n  const clone = structuredClone(root);\n  let cur: Record<string, unknown> = clone;\n  for (let i = 0; i < segments.length - 1; i++) {\n    const seg = segments[i];\n    const next = cur[seg];\n    if (next === null || typeof next !== \"object\" || Array.isArray(next)) cur[seg] = {};\n    cur = cur[seg] as Record<string, unknown>;\n  }\n  cur[segments[segments.length - 1]] = value;\n  return clone;\n}\n\nfunction deleteAtPath(root: Record<string, unknown>, segments: string[]): Record<string, unknown> {\n  const clone = structuredClone(root);\n  let cur: Record<string, unknown> = clone;\n  for (let i = 0; i < segments.length - 1; i++) {\n    const seg = segments[i];\n    const next = cur[seg];\n    if (next === null || typeof next !== \"object\" || Array.isArray(next)) return clone;\n    cur = next as Record<string, unknown>;\n  }\n  delete cur[segments[segments.length - 1]];\n  return clone;\n}\n\n// ---------------------------------------------------------------------------\n// Value parsing (`pai config set <path> <value>`)\n// ---------------------------------------------------------------------------\n\n/** true/false, null, numbers, `[...]`/`{...}` as JSON, else the raw string. */\nexport function parseConfigValueString(raw: string): unknown {\n  if (raw === \"true\") return true;\n  if (raw === \"false\") return false;\n  if (raw === \"null\") return null;\n  if (/^-?\\d+(\\.\\d+)?$/.test(raw)) return Number(raw);\n  const trimmed = raw.trim();\n  if (trimmed.startsWith(\"[\") || trimmed.startsWith(\"{\")) {\n    try {\n      return JSON.parse(trimmed);\n    } catch (e) {\n      throw new MainConfigOpsError(`invalid JSON value: ${e instanceof Error ? e.message : String(e)}`);\n    }\n  }\n  return raw;\n}\n\n// ---------------------------------------------------------------------------\n// Secret masking\n// ---------------------------------------------------------------------------\n\nconst SECRET_SEGMENT_RE = /key|token|secret|password/i;\n\n/** Paths not caught by SECRET_SEGMENT_RE but whose value routinely embeds a\n *  credential (a Postgres URL's userinfo component). */\nconst SPECIAL_SECRET_PATHS = new Set([\"postgres.connectionString\"]);\n\nfunction isSecretPath(segments: (string | number)[]): boolean {\n  if (SPECIAL_SECRET_PATHS.has(segments.filter((s) => typeof s === \"string\").join(\".\"))) return true;\n  return segments.some((s) => typeof s === \"string\" && SECRET_SEGMENT_RE.test(s));\n}\n\n/** Deep-clone `value`, replacing every string leaf on a secret path with\n *  `maskKey`'s `****<last4>` form. Used by every reader (list/get, MCP). */\nexport function maskSecretsDeep(value: unknown, path: (string | number)[] = []): unknown {\n  if (Array.isArray(value)) return value.map((v, i) => maskSecretsDeep(v, [...path, i]));\n  if (value !== null && typeof value === \"object\") {\n    const out: Record<string, unknown> = {};\n    for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n      out[k] = maskSecretsDeep(v, [...path, k]);\n    }\n    return out;\n  }\n  if (typeof value === \"string\" && value && isSecretPath(path)) return maskKey(value);\n  return value;\n}\n\n// ---------------------------------------------------------------------------\n// Schema (DEFAULTS + the non-provider `workers` shape) for type validation\n// and the effective (defaults-merged) view\n// ---------------------------------------------------------------------------\n\n/** The non-provider `workers` sub-object every writer persists to the main\n *  config (providers/classes/mcp_sets/active live in workers.yaml instead —\n *  see readWorkersSection). Shared by schemaRoot and effectiveConfigRoot. */\nfunction workersMainConfigShape(): Record<string, unknown> {\n  const { workers } = readWorkersSection();\n  return {\n    enabled: workers.enabled,\n    pane: workers.pane,\n    logDir: workers.logDir,\n    routing: workers.routing,\n    tree: workers.tree,\n    cacheKeepaliveSecs: workers.cacheKeepaliveSecs,\n    ...(workers.fallback ? { fallback: workers.fallback } : {}),\n  };\n}\n\nfunction schemaRoot(): Record<string, unknown> {\n  return {\n    ...(DEFAULTS as unknown as Record<string, unknown>),\n    workers: {\n      enabled: false,\n      pane: DEFAULT_PANE,\n      logDir: DEFAULT_LOG_DIR,\n      routing: DEFAULT_ROUTING,\n      tree: DEFAULT_TREE,\n      cacheKeepaliveSecs: DEFAULT_CACHE_KEEPALIVE_SECS,\n    },\n  };\n}\n\n/** loadConfig() (typed, defaults-merged) plus the workers subset — the\n *  \"effective\" view `pai config list --all` / `get` resolve against. */\nfunction effectiveConfigRoot(): Record<string, unknown> {\n  return {\n    ...(loadConfig() as unknown as Record<string, unknown>),\n    workers: workersMainConfigShape(),\n  };\n}\n\n/**\n * Refuse an unset top-level key (typo protection) unless --force, and — when\n * DEFAULTS names a scalar at this exact path — refuse a value of a different\n * JS type unless --force. Nested unknown keys (e.g. under `tasks.providers`)\n * are always allowed: DEFAULTS does not enumerate every provider shape.\n */\nfunction validateAgainstSchema(segments: string[], value: unknown, opts: { force?: boolean }): void {\n  const schema = schemaRoot();\n  const topKey = segments[0];\n  if (!(topKey in schema) && !opts.force) {\n    throw new MainConfigOpsError(\n      `unknown top-level config key \"${topKey}\" (known: ${Object.keys(schema).sort().join(\", \")}) — pass --force to set it anyway`\n    );\n  }\n  const { value: expected, found } = getAtPath(schema, segments);\n  if (found && expected !== null && expected !== undefined) {\n    const expectedType = Array.isArray(expected) ? \"array\" : typeof expected;\n    const actualType = Array.isArray(value) ? \"array\" : typeof value;\n    if (expectedType !== actualType && !opts.force) {\n      throw new MainConfigOpsError(\n        `${segments.join(\".\")}: expected ${expectedType}, got ${actualType} (pass --force to override)`\n      );\n    }\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Public ops\n// ---------------------------------------------------------------------------\n\nexport interface ListConfigResult {\n  /** The section this reflects: file-set values only, or defaults-merged. */\n  scope: \"file\" | \"effective\";\n  yaml: string;\n  data: Record<string, unknown>;\n}\n\n/** `pai config list` / `config_list`. `all=true` shows the defaults-merged\n *  effective config; otherwise only what the file explicitly sets. Secrets\n *  are always masked. */\nexport function listConfigOp(opts: { all?: boolean } = {}): ListConfigResult {\n  const root = opts.all ? effectiveConfigRoot() : readMainConfigRaw();\n  const masked = maskSecretsDeep(root) as Record<string, unknown>;\n  return { scope: opts.all ? \"effective\" : \"file\", yaml: String(new Document(masked)), data: masked };\n}\n\nexport interface GetConfigResult {\n  found: boolean;\n  value: unknown;\n}\n\n/** `pai config get <path>` / `config_get`: resolves against the\n *  defaults-merged effective config (a value not set in the file still\n *  answers with its default), masked if the path looks like a secret. */\nexport function getConfigValueOp(dottedPath: string): GetConfigResult {\n  const segments = splitConfigPath(dottedPath);\n  const { value, found } = getAtPath(effectiveConfigRoot(), segments);\n  return { found, value: found ? maskSecretsDeep(value, segments) : undefined };\n}\n\n/** `pai config get`'s CLI text: a scalar prints as-is, an object/array\n *  subtree prints as YAML (matching `config list`) unless `json` is passed —\n *  the MCP `config_get` tool returns the structured value directly and never\n *  goes through this. */\nexport function formatConfigGetOutput(value: unknown, opts: { json?: boolean } = {}): string {\n  if (opts.json) return JSON.stringify(value, null, 2);\n  if (value !== null && typeof value === \"object\") return String(new Document(value)).trimEnd();\n  return String(value);\n}\n\nexport interface SetConfigResult {\n  yamlCreated: boolean;\n  yamlPath: string;\n  value: unknown;\n}\n\n/**\n * `pai config set <path> <value>` / `config_set`: parses `value`, validates\n * its type against DEFAULTS (refusable with `force`), converts config.json\n * to config.yaml first if neither exists yet, then writes through\n * writeMainConfigRaw (comment-preserving when config.yaml exists).\n */\nexport function setConfigValueOp(dottedPath: string, rawValue: string, opts: { force?: boolean } = {}): SetConfigResult {\n  const segments = splitConfigPath(dottedPath);\n  const value = parseConfigValueString(rawValue);\n  validateAgainstSchema(segments, value, opts);\n\n  const jsonPath = paiConfigFilePath();\n  const yamlPath = paiConfigYamlFilePath();\n  let yamlCreated = false;\n  if (!existsSync(yamlPath)) {\n    if (existsSync(jsonPath)) {\n      migrateMainConfigToYaml(jsonPath, {});\n    } else {\n      writeYamlFileAtomic(yamlPath, \"{}\\n\", { label: yamlPath });\n    }\n    yamlCreated = true;\n  }\n\n  const raw = readMainConfigRaw();\n  writeMainConfigRaw(setAtPath(raw, segments, value));\n  return { yamlCreated, yamlPath, value };\n}\n\nexport interface UnsetConfigResult {\n  existed: boolean;\n}\n\n/** `pai config unset <path>` / `config_unset`: no-ops (existed=false) when\n *  the path was never set explicitly — defaults are never \"unset\". */\nexport function unsetConfigValueOp(dottedPath: string): UnsetConfigResult {\n  const segments = splitConfigPath(dottedPath);\n  const raw = readMainConfigRaw();\n  const { found } = getAtPath(raw, segments);\n  if (!found) return { existed: false };\n  writeMainConfigRaw(deleteAtPath(raw, segments));\n  return { existed: true };\n}\n","/**\n * specfile.ts — `--spec <path>` reads a worker's prompt from a file (or stdin\n * via `--spec -`) instead of an inline `-p '<prompt>'`, which has repeatedly\n * died on shell quoting for anything long or multi-line.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\n/** The path a --spec value resolves to for display/recording; \"-\" (stdin) passes through unchanged. */\nexport function resolveSpecPath(spec: string, cwd: string): string {\n  return spec === \"-\" ? \"-\" : resolve(cwd, spec);\n}\n\n/** The prompt text a --spec value reads: the file's exact bytes, or stdin's. */\nexport function readSpecPrompt(spec: string, cwd: string): string {\n  if (spec === \"-\") {\n    let content: string;\n    try {\n      content = readFileSync(0, \"utf8\");\n    } catch (e) {\n      throw new Error(`--spec -: could not read stdin: ${(e as Error).message}`);\n    }\n    if (!content) throw new Error(\"--spec -: stdin was empty\");\n    return content;\n  }\n  const path = resolveSpecPath(spec, cwd);\n  if (!existsSync(path)) throw new Error(`--spec: file not found: ${path}`);\n  const content = readFileSync(path, \"utf8\");\n  if (!content) throw new Error(`--spec: file is empty: ${path}`);\n  return content;\n}\n","/**\n * chatui.ts — the chat line of a `follow` pane.\n *\n * A follow pane with a target behaves like a small chat, Claude Code style:\n * the transcript lives in a terminal scroll region that ends three rows above\n * the pane's bottom; below it a separator rule row, then the two fixed rows —\n * the prompt row (`› `, readline line editing) and the ticker row. A transcript\n * line is inserted above the fixed rows with a save-cursor / scroll-region /\n * restore-cursor write that never touches them; the scroll region makes the\n * transcript roll inside itself. Everything here builds strings (or parses one\n * line), so the tests assert exact byte sequences — no terminal needed.\n */\n\n/** The prompt marker of the chat row. */\nexport const CHAT_PROMPT = \"› \";\n\n/** Dim hint shown once behind the cursor until the first line is typed. */\nexport const CHAT_HINT = \"type here and press Enter · /help for commands\";\n\n/** What `/help` prints (one command per line, dim). */\nexport const CHAT_HELP = [\n  \"/quit            close this pane\",\n  \"/resume <text>   continue the finished worker with <text>\",\n  \"/status          one-line worker status\",\n  \"anything else is sent to the worker — said while it runs, resumed after\",\n];\n\n// ---------------------------------------------------------------------------\n// visible width & wrapping (the gutter must stay the leftmost column)\n// ---------------------------------------------------------------------------\n\n/** Index just past the escape starting at `i` (CSI, OSC or a two-char one). */\nfunction endOfEscape(s: string, i: number): number {\n  const n = s[i + 1];\n  if (n === \"[\") {\n    let j = i + 2;\n    while (j < s.length && !(s[j]! >= \"@\" && s[j]! <= \"~\")) j++;\n    return Math.min(s.length, j + 1);\n  }\n  if (n === \"]\") {\n    let j = i + 2;\n    while (j < s.length && s[j] !== \"\\x07\") j++;\n    return Math.min(s.length, j + 1);\n  }\n  return i + 2;\n}\n\n/** Printable columns of `s` — ANSI escape sequences measure zero. */\nexport function visibleWidth(s: string): number {\n  let w = 0;\n  let i = 0;\n  while (i < s.length) {\n    if (s[i] === \"\\x1b\") {\n      i = endOfEscape(s, i);\n      continue;\n    }\n    w += 1;\n    i += 1;\n  }\n  return w;\n}\n\n/**\n * The SGR sequences in effect at `upto`: everything opened since the last\n * reset, in order. Anything that is not an SGR escape is ignored (it does\n * not change colour state).\n */\nfunction sgrStateAt(text: string, upto: number): string[] {\n  const open: string[] = [];\n  let i = 0;\n  while (i < Math.min(upto, text.length)) {\n    if (text[i] === \"\\x1b\") {\n      const end = endOfEscape(text, i);\n      const esc = text.slice(i, end);\n      if (/^\\x1b\\[[0-9;]*m$/.test(esc)) {\n        const params = esc.slice(2, -1);\n        const resets = params === \"\" || params.split(\";\").includes(\"0\");\n        if (resets) open.length = 0;\n        if (!(params === \"\" || params === \"0\")) open.push(esc);\n      }\n      i = end;\n      continue;\n    }\n    i += 1;\n  }\n  return open;\n}\n\n/**\n * Wrap one rendered row to `width` printable columns. Breaks on whitespace\n * where possible, hard-wraps words longer than the width, never splits an\n * ANSI escape, and re-opens the colours it wraps inside of, so a diff row\n * keeps its `-`/`+` colour on every continuation row.\n */\nexport function wrapText(text: string, width: number): string[] {\n  if (width < 1 || visibleWidth(text) <= width) return [text];\n\n  // visible characters by their index in `text`\n  const chars: number[] = [];\n  for (let i = 0; i < text.length; i++) {\n    if (text[i] === \"\\x1b\") {\n      i = endOfEscape(text, i) - 1;\n      continue;\n    }\n    chars.push(i);\n  }\n\n  // words as [first, last] indexes into `chars` (escapes ride along later)\n  const words: Array<{ s: number; e: number; w: number }> = [];\n  {\n    let s = -1;\n    let w = 0;\n    for (let k = 0; k <= chars.length; k++) {\n      const ch = k === chars.length ? \" \" : text[chars[k]!]!;\n      if (ch === \" \") {\n        if (s >= 0) {\n          words.push({ s, e: k - 1, w });\n          s = -1;\n          w = 0;\n        }\n      } else {\n        if (s < 0) s = k;\n        w++;\n      }\n    }\n  }\n  // a leading indent (diff rows) belongs to the first word, width counted\n  if (words.length && words[0]!.s > 0) {\n    words[0] = { s: 0, e: words[0]!.e, w: words[0]!.w + words[0]!.s };\n  }\n\n  // chunk spans [start, end] over `chars`, greedy on visible width — the\n  // width of a chunk is simply its span in `chars`, inner spaces included\n  const spans: Array<[number, number]> = [];\n  let cs = -1;\n  let ce = -1;\n  for (const word of words) {\n    if (word.w > width) {\n      // oversized word: fill the rest of the line, then full-width rows.\n      // `take` is the word's chars that still fit — the gap before the word\n      // eats into the room, and when it eats all of it the word starts fresh\n      const room = cs < 0 ? 0 : width - (ce - cs + 1);\n      const take = room > 0 ? ce + room - word.s + 1 : 0;\n      if (take > 0) spans.push([cs, ce + room]);\n      else if (cs >= 0) spans.push([cs, ce]);\n      let pos = word.s + Math.max(0, take);\n      let remaining = word.w - Math.max(0, take);\n      while (remaining > width) {\n        spans.push([pos, pos + width - 1]);\n        pos += width;\n        remaining -= width;\n      }\n      cs = pos;\n      ce = pos + remaining - 1;\n      continue;\n    }\n    if (cs < 0) {\n      cs = word.s;\n      ce = word.e;\n      continue;\n    }\n    if (word.e - cs + 1 <= width) {\n      ce = word.e;\n      continue;\n    }\n    spans.push([cs, ce]);\n    cs = word.s;\n    ce = word.e;\n  }\n  if (cs >= 0) spans.push([cs, ce]);\n\n  const out: string[] = [];\n  for (const [a, b] of spans) {\n    const from = chars[a]!;\n    // take trailing escapes up to the next visible char with the chunk\n    let end = chars[b]! + 1;\n    while (end < text.length && text[end] === \"\\x1b\") end = endOfEscape(text, end);\n    let piece = text.slice(from, end);\n    const reopen = sgrStateAt(text, from);\n    if (reopen.length) piece = reopen.join(\"\") + piece;\n    if (sgrStateAt(text, end).length) piece += \"\\x1b[0m\";\n    out.push(piece);\n  }\n  return out.length ? out : [\"\"];\n}\n\n// ---------------------------------------------------------------------------\n// the layout: scroll region + two fixed rows\n// ---------------------------------------------------------------------------\n\n/** Restrict scrolling to the transcript region (rows 1 … rows-3). */\nexport function chatScrollRegion(rows: number): string {\n  return `\\x1b[1;${Math.max(1, rows - 3)}r`;\n}\n\n/**\n * The separator row between the transcript and the prompt: cleared, then —\n * when the pane's width is known — a dim rule of `cols` box-drawing `─`\n * filling the row. `cols` 0 (unknown) keeps the row blank; `dim` is the\n * pane's colour helper, identity when colours are off.\n */\nexport function chatBlankRow(\n  rows: number,\n  cols = 0,\n  dim: (s: string) => string = (s) => s\n): string {\n  const rule = cols > 0 ? dim(\"─\".repeat(cols)) : \"\";\n  return `\\x1b[${Math.max(1, rows - 2)};1H\\x1b[K` + rule;\n}\n\n/**\n * Enter the chat layout: clear the pane, set the scroll region, draw the\n * separator row and park the cursor at column 3 of the prompt row (rows-1,\n * right after `› `). The ticker owns row `rows`.\n */\nexport function chatEnter(\n  rows: number,\n  cols = 0,\n  dim: (s: string) => string = (s) => s\n): string {\n  return (\n    \"\\x1b[2J\" + chatScrollRegion(rows) + chatBlankRow(rows, cols, dim) +\n    `\\x1b[${Math.max(1, rows - 1)};3H`\n  );\n}\n\n/** Leave it: reset the scroll region, show the cursor, drop to the last row. */\nexport function chatLeave(rows: number): string {\n  return \"\\x1b[r\\x1b[?25h\" + `\\x1b[${Math.max(1, rows)};1H`;\n}\n\n/**\n * Redraw the ticker on its own row and park the cursor back on the prompt\n * row at `parkCol` (default right after `› `): a ticker refresh never leaves\n * the cursor on the bottom row.\n */\nexport function chatTickerRow(text: string, rows: number, parkCol = 3): string {\n  return (\n    \"\\x1b7\" +\n    `\\x1b[${Math.max(1, rows)};1H\\x1b[K` + text +\n    `\\x1b[${Math.max(1, rows - 1)};${Math.max(1, parkCol)}H`\n  );\n}\n\n/**\n * The prompt row: the fixed `› ` prefix, then the input buffer — or, while\n * the buffer is empty, the dim placeholder behind the same prefix. The\n * cursor parks right after the buffer (`cursorAt` chars in, for a cursor\n * mid-draft), so typed text always starts at the column the placeholder\n * occupied, never glued to a hint.\n */\nexport function chatPromptRow(\n  rows: number,\n  buffer = \"\",\n  dim: (s: string) => string = (s) => s,\n  cursorAt?: number\n): string {\n  const rowN = Math.max(1, rows - 1);\n  const body = buffer !== \"\" ? buffer : dim(CHAT_HINT);\n  const col = 3 + Math.max(0, Math.min(cursorAt ?? buffer.length, buffer.length));\n  return `\\x1b[${rowN};1H\\x1b[K` + CHAT_PROMPT + body + `\\x1b[${rowN};${col}H`;\n}\n\nexport interface ChatInsert {\n  seq: string;\n  /** rows filled after this one (caps at regionRows, then it always scrolls). */\n  fill: number;\n}\n\n/**\n * Insert one transcript row above the fixed prompt/ticker rows. While the\n * region is still filling (`fill < regionRows`) the row is placed top-down;\n * once full, the cursor moves to the region's bottom row and a newline\n * scrolls the region up by one — the two fixed rows are never touched. The\n * user's cursor is saved before and restored after, so readline keeps its\n * position on the prompt row.\n */\nexport function chatInsertLine(line: string, fill: number, regionRows: number): ChatInsert {\n  const growing = fill < regionRows;\n  const seq =\n    \"\\x1b7\" +\n    (growing\n      ? `\\x1b[${fill + 1};1H${line}\\x1b[K`\n      : `\\x1b[${regionRows};1H\\n${line}\\x1b[K`) +\n    \"\\x1b8\";\n  return { seq, fill: Math.min(fill + 1, regionRows) };\n}\n\n// ---------------------------------------------------------------------------\n// prompt command parsing\n// ---------------------------------------------------------------------------\n\nexport type ChatAction =\n  | { kind: \"message\"; text: string }\n  | { kind: \"help\" }\n  | { kind: \"quit\" }\n  | { kind: \"status\" }\n  | { kind: \"resume\"; text: string };\n\n/**\n * One submitted prompt line → what to do with it. `/help`, `/quit`,\n * `/status` and `/resume <text>` are commands (a bare `/resume` comes back\n * with empty text so the caller can print its usage); anything else,\n * including any other `/word`, is a message for the worker.\n */\nexport function parseChatLine(raw: string): ChatAction {\n  const text = raw.trim();\n  if (text === \"/help\") return { kind: \"help\" };\n  if (text === \"/quit\") return { kind: \"quit\" };\n  if (text === \"/status\") return { kind: \"status\" };\n  if (text.startsWith(\"/resume\")) return { kind: \"resume\", text: text.slice(\"/resume\".length).trim() };\n  return { kind: \"message\", text };\n}\n\n/**\n * The auto-exit countdown must not fire while the prompt holds unsent text:\n * true while it does. null/undefined (no prompt wired) never holds.\n */\nexport function holdAutoExit(promptText: string | null | undefined): boolean {\n  return typeof promptText === \"string\" && promptText.trim() !== \"\";\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, contextLabel, contextPercent, isChatPane, type WorkerStatus, isLive, UNLABELED } from \"./status.js\";\nimport { workerDepth } from \"./tree.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  /** tool_result blocks only: the call this answers. */\n  tool_use_id?: string;\n  is_error?: boolean;\n  content?: 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  /** Operator mirror of a handoff delivery — shown only as the ◆ inbox line. */\n  handoff?: boolean;\n  /** Handoff fields (type: \"handoff\", from the inbox tail). */\n  from?: string;\n  kind?: string;\n}\n\n/**\n * `HH:MM:SS` at an offset east of UTC in minutes (Date.getTimezoneOffset()\n * negated), from any ISO stamp the runner wrote — `Z` or a local `+HH:MM`.\n * null when the stamp cannot be parsed. Offsets make this testable without\n * depending on the machine's zone; the default is this machine's.\n */\nexport function clockOf(ts: string, offMin = -new Date().getTimezoneOffset()): string | null {\n  const t = Date.parse(ts);\n  if (Number.isNaN(t)) return null;\n  return new Date(t + offMin * 60_000).toISOString().slice(11, 19);\n}\n\n/** `YYYY-MM-DD` at the same offset — the local day a date separator shows. */\nexport function dayOf(ts: string, offMin = -new Date().getTimezoneOffset()): string | null {\n  const t = Date.parse(ts);\n  if (Number.isNaN(t)) return null;\n  return new Date(t + offMin * 60_000).toISOString().slice(0, 10);\n}\n\n/** The gutter of one rendered event: its three shapes and its width. */\nexport interface Gutter {\n  /** first row: `HH:MM:SS │ ` (dim). */\n  first: string;\n  /** later rows of one event, no wrapping: blanks of the same width. */\n  cont: string;\n  /** wrapped continuation rows: blank time, the `│` bar kept (dim). */\n  barCont: string;\n  /** printable columns first/cont/barCont occupy. */\n  width: number;\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; when the viewer\n * wraps lines itself, continuation rows carry the `│` bar instead (barCont)\n * so the bar runs unbroken down the pane. null when the event carries no\n * stamp (logs from before 2g render with the plain prefix). The time is the\n * stamp's wall clock at `offMin` — the default renders local time, whatever\n * zone stamped the log (old logs were stamped in UTC).\n */\nexport function gutterFor(\n  c: Paint,\n  e: { _ts?: string },\n  tag?: string,\n  offMin?: number\n): Gutter | null {\n  if (!e._ts) return null;\n  const time = clockOf(e._ts, offMin) ?? (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    barCont: c(\"dim\", `${\" \".repeat(head.length)} │ `),\n    width,\n  };\n}\n\n/**\n * The ticker's tool part: `$ <command>` for Bash (first 60 chars), the file\n * basename for the file tools, the bare name for everything else.\n */\nexport function tickerTool(name: string, inp: unknown): 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 === \"Bash\") return `$ ${shortText(get(\"command\").replace(/\\s+/g, \" \").trim(), 60)}`;\n  if (name === \"Read\" || name === \"Edit\" || name === \"Write\" || name === \"MultiEdit\") {\n    const base = get(\"file_path\").split(\"/\").pop() ?? \"\";\n    return base || name;\n  }\n  return name;\n}\n\n/**\n * The liveness line: `⋯ 12s · run tests before the fix · $ bun run test` —\n * seconds since the last *rendered* event, the worker's last stated intent\n * (its last assistant text, ≤60 chars) and the tool it is currently running.\n * Parts that are empty drop out.\n */\nexport function tickerText(secs: number, intent: string, tool: string, meter?: string | null): string {\n  const head = `⋯ ${secs}s`;\n  const parts = [intent, tool].map((p) => p.trim()).filter(Boolean);\n  const tail = parts.join(\" · \");\n  const line = tail ? `${head} · ${tail}` : head;\n  return meter ? `${line} · ${meter}` : line;\n}\n\n/** What the chat pane's status row shows (chatStatusRow formats it). */\nexport interface StatusRow {\n  provider: string;\n  model: string;\n  contextTokens?: number | null;\n  contextWindow?: number | null;\n  turns: number;\n  tools: number;\n  /** runtime so far in seconds; the finished row freezes its final value. */\n  elapsed: number;\n  /** seconds since the last rendered event (running only). */\n  idle?: number;\n  intent?: string;\n  tool?: string;\n  /** a finished state freezes the row with ✓/✗ instead of the ticker part. */\n  state?: string | null;\n}\n\n/** `4m12s` — the runtime shape the status row shows. */\nexport function fmtElapsed(secs: number): string {\n  const s = Math.max(0, Math.floor(secs));\n  return `${Math.floor(s / 60)}m${String(s % 60).padStart(2, \"0\")}s`;\n}\n\n/**\n * The chat pane's status row (always visible while following):\n * `[glm/glm-5.3] ctx 84k/200k (42%) · turns 12 · tools 7 · 4m12s · ⋯ 7s · <intent> · <tool>`\n * — context yellow past 70 %, red past 85 %, dropped when unknown; once the\n * worker finished, the ticker part is replaced by `✓ done` / `✗ failed`.\n */\nexport function chatStatusRow(c: Paint, s: StatusRow): string {\n  const parts: string[] = [];\n  const pct = contextPercent(s);\n  if (pct !== null) {\n    const label = contextLabel(s);\n    parts.push(pct > 85 ? c(\"red\", label) : pct > 70 ? c(\"yellow\", label) : label);\n  }\n  parts.push(`turns ${s.turns}`, `tools ${s.tools}`, fmtElapsed(s.elapsed));\n  if (s.state && s.state !== \"running\") {\n    parts.push(s.state === \"done\" ? c(\"green\", \"✓ done\") : c(\"red\", \"✗ failed\"));\n  } else {\n    parts.push(`⋯ ${Math.max(0, Math.floor(s.idle ?? 0))}s`);\n    for (const p of [s.intent, s.tool]) if (p && p.trim()) parts.push(p.trim());\n  }\n  const model = shortModel(s.model);\n  return `[${s.provider}${model ? \"/\" + model : \"\"}] ${parts.join(\" · \")}`;\n}\n\n/**\n * One blank line between turns, none inside one: a blank goes before an\n * assistant message that follows a tool result or an operator message (the\n * worker starting to speak again after its tools were answered / it was told\n * something), not between the text, tool calls and results of one turn.\n */\nexport function blankBetween(prev: { type?: string } | null, e: { type?: string }): boolean {\n  if (!prev) return false;\n  if (e.type !== \"assistant\") return false;\n  return prev.type === \"user\" || prev.type === \"operator\" || prev.type === \"handoff\";\n}\n\n/** The worker's last stated intent: the first line of its last text, ≤60. */\nexport function intentOf(text: string): string {\n  const first = text.trim().split(\"\\n\").find((l) => l.trim()) ?? \"\";\n  return shortText(first.trim().replace(/\\s+/g, \" \"), 60);\n}\n\n/**\n * The context meter `ctx 84k/200k (42%)` (contextLabel, shared with the\n * chat status row), yellow from 70 %, red from 85 %. null when the numbers\n * are missing or below `minPct` (the table only shows it past 60; the pane\n * 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 = contextLabel(s);\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    // a handoff delivery is said to the worker AND tailed from the inbox —\n    // the transcript shows only the ◆ line, its mirror renders nothing\n    if (e.handoff) return out;\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 === \"handoff\") {\n    // a child's message from the inbox: ◆ from <id> · <kind>: <text>\n    for (const ln of String(e.text ?? \"\").split(\"\\n\")) {\n      out.push(`${prefix}${c(\"mag\", `◆ from ${e.from ?? \"?\"} · ${e.kind ?? \"?\"}: ${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: {\n    id: string;\n    label: string;\n    cwd: string;\n    provider?: string;\n    session?: { name?: string } | null;\n    spec?: string | null;\n    reportFormat?: \"json\" | \"ag2\";\n    reportValid?: boolean;\n    reportErrors?: string[];\n  }\n): string {\n  const bits = [s.provider ? `[${s.provider}]` : \"\", sessionTag(s)].filter(Boolean).join(\" \");\n  const sep = bits ? `  ${bits}` : \"\";\n  const specBit = s.spec ? `  spec: ${s.spec}` : \"\";\n  const reportBit =\n    s.reportFormat === \"ag2\" && s.reportValid === false\n      ? \"  \" + c(\"red\", (s.reportErrors ?? []).length ? \"R!\" : \"R?\")\n      : \"\";\n  return c(\"bold\", `━━ ${s.id}${sep}  ${s.label}  (${basename(s.cwd)})${specBit}${reportBit}`);\n}\n\n/** The chain label behind a stage label: strip the trailing \" · <stage>\". */\nfunction chainLabelOf(stages: WorkerStatus[]): string {\n  const first = stages[0];\n  if (!first) return \"\";\n  const suffix = first.stage ? ` · ${first.stage}` : \"\";\n  return first.label.endsWith(suffix) && suffix\n    ? first.label.slice(0, first.label.length - suffix.length)\n    : first.label;\n}\n\n/**\n * The ps table (RUNNING + FINISHED last 8) as a forest: a worker whose parent\n * is another worker renders indented under it (`├`/`└` connectors); a parent\n * that is not a worker is a chain id and gets its old `chain <id>` header.\n * `⎇` marks an unmerged worktree branch, `◆N` an inbox with N handoffs.\n */\nexport function renderTable(\n  c: Paint,\n  statuses: WorkerStatus[],\n  scopeLabel: string,\n  now: Date = new Date(),\n  inbox: Record<string, number> = {}\n): string {\n  const running: WorkerStatus[] = [];\n  const done: WorkerStatus[] = [];\n  for (const s of statuses) {\n    if (isLive(s)) running.push(s);\n    else {\n      if (s.state === \"running\") s.state = \"lost\";\n      done.push(s);\n    }\n  }\n  const isWorker = (id: string): boolean => statuses.some((s) => s.id === id);\n  // a chain id (no status file) groups its stages under a header instead\n  const chainOf = (s: WorkerStatus): string | null => (s.parent && !isWorker(s.parent) ? s.parent : null);\n  const treeLine = (line: string, chain: string | null, last: boolean): string => {\n    if (chain === null) return line;\n    const mark = last ? \"└\" : \"├\";\n    const bar = last ? \" \" : \"│\";\n    return line.startsWith(\"      \")\n      ? `  ${bar}   ${line.slice(6)}`\n      : `  ${mark} ${line.slice(2)}`;\n  };\n  // sub-workers: one connector level per depth under their worker parent\n  const rowPrefix = (depth: number, last: boolean): string =>\n    depth <= 0 ? \"  \" : \"  \" + \"│ \".repeat(depth - 1) + (last ? \"└ \" : \"├ \");\n  const rowCont = (depth: number, last: boolean): string =>\n    depth <= 0 ? \"      \" : \"  \" + \"│ \".repeat(depth - 1) + (last ? \"  \" : \"│ \");\n  const branchMark = (s: WorkerStatus): string =>\n    s.branch && !s.merged ? \"  \" + c(\"yellow\", \"⎇\" + (s.commits ? String(s.commits) : \"\")) : \"\";\n  const inboxMark = (s: WorkerStatus): string =>\n    inbox[s.id] ? \" \" + c(\"mag\", `◆${inbox[s.id]}`) : \"\";\n  // \"R!\" no proof/gate/test-set failure, \"R?\" the aibroker validator itself\n  // could not confirm it (missing/unreadable CLI) — never shown for json reports\n  const reportMark = (s: WorkerStatus): string =>\n    s.reportFormat === \"ag2\" && s.reportValid === false\n      ? \"  \" + c(\"red\", (s.reportErrors ?? []).length ? \"R!\" : \"R?\")\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 (let i = 0; i < running.length; i++) {\n    const s = running[i];\n    const chain = chainOf(s);\n    if (chain) {\n      const prev = running[i - 1];\n      if (!prev || chainOf(prev) !== chain) {\n        const stages = running.filter((x) => chainOf(x) === chain);\n        lines.push(`  ${c(\"bold\", `chain ${chain}`)}  ${chainLabelOf(stages)}`);\n      }\n    }\n    const last =\n      !!chain && (i + 1 >= running.length || chainOf(running[i + 1]) !== chain);\n    const depth = workerDepth(statuses, s.id);\n    const subLast =\n      !chain && (i + 1 >= running.length || running[i + 1].parent !== s.parent);\n    const meter = contextMeter(c, s, 60);\n    const p = chain ? \"  \" : rowPrefix(depth, subLast);\n    const q = chain ? \"      \" : rowCont(depth, subLast);\n    lines.push(\n      treeLine(\n        `${p}${c(\"cyan\", s.id)}${inboxMark(s)} [${s.provider}]  ${ageOf(s.started, now).padStart(4)} old  turns ${String(s.turns).padStart(2)}  tools ${String(s.tools).padStart(2)}  ${basename(s.cwd)}${branchMark(s)}${meter ? \"  \" + meter : \"\"}`,\n        chain,\n        last\n      )\n    );\n    lines.push(treeLine(`${q}task: ${s.label}`, chain, last));\n    lines.push(\n      treeLine(`${q}now:  ${c(\"yellow\", s.last)}  (${ageOf(s.updated, now)} ago)`, chain, last)\n    );\n  }\n  lines.push(\"\");\n  lines.push(c(\"bold\", \"FINISHED (last 8)\"));\n  const doneSlice = done.slice(-8);\n  for (let i = 0; i < doneSlice.length; i++) {\n    const s = doneSlice[i];\n    const chain = chainOf(s);\n    if (chain) {\n      const prev = doneSlice[i - 1];\n      if (!prev || chainOf(prev) !== chain) {\n        const stages = doneSlice.filter((x) => chainOf(x) === chain);\n        lines.push(`  ${c(\"bold\", `chain ${chain}`)}  ${chainLabelOf(stages)}`);\n      }\n    }\n    const last =\n      !!chain && (i + 1 >= doneSlice.length || chainOf(doneSlice[i + 1]) !== chain);\n    const depth = workerDepth(statuses, s.id);\n    const subLast =\n      !chain && (i + 1 >= doneSlice.length || doneSlice[i + 1].parent !== s.parent);\n    const col = s.state === \"done\" ? \"green\" : \"red\";\n    const tag = sessionTag(s);\n    lines.push(\n      treeLine(\n        `${chain ? \"  \" : rowPrefix(depth, subLast)}${s.id}${inboxMark(s)} [${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)}${branchMark(s)}${reportMark(s)}  ${s.label}`,\n        chain,\n        last\n      )\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/**\n * The one place a model id is compacted for display, so every renderer\n * shortens identically: the vendor prefix goes (the provider column already\n * says who serves it), a release-date suffix goes, version segments read as\n * dots, and a `[1m]` window marker is kept because it changes what the run\n * costs. `claude-opus-5[1m]` → `opus-5[1m]`, `claude-haiku-4-5-20251001` →\n * `haiku-4.5`, `glm-5.3[1m]` and `k3[1m]` unchanged. Empty in, empty out.\n */\nexport function shortModel(model: string | null | undefined): string {\n  const raw = String(model ?? \"\").trim();\n  if (!raw) return \"\";\n  const wide = /\\[1m\\]$/i.test(raw);\n  let id = wide ? raw.slice(0, -4) : raw;\n  id = id.replace(/^(?:us\\.|eu\\.|apac\\.)?(?:anthropic|claude|openai|google|models)[./-]/i, \"\");\n  id = id.replace(/-\\d{8}$/, \"\");\n  // trailing numeric segments are one version: haiku-4-5 → haiku-4.5\n  const parts = id.split(\"-\");\n  const nums: string[] = [];\n  while (parts.length > 1 && /^\\d+(?:\\.\\d+)*$/.test(parts[parts.length - 1])) {\n    nums.unshift(parts.pop() as string);\n  }\n  if (nums.length) id = `${parts.join(\"-\")}-${nums.join(\".\")}`;\n  return wide ? `${id}[1m]` : id;\n}\n\n/**\n * The goal of a statusline row: what the worker is FOR, never what it is doing\n * this second. The operator's `--label` is the goal and is preferred over\n * anything the model wrote; an unlabelled run's label already holds its prompt\n * (see run.ts), so the same path shortens that to its first sentence, cut on a\n * word boundary. A run with neither label nor prompt reads \"unlabeled\". Width\n * fitting goes through shortText, the repo's one truncation helper.\n */\nexport function goalOf(s: Pick<WorkerStatus, \"label\">, max = 40): string {\n  const raw = String(s.label ?? \"\").trim();\n  if (!raw || raw === \"(no prompt)\") return UNLABELED;\n  const first = raw.split(/(?<=[.!?])\\s+/)[0] ?? raw;\n  if (first.length <= max) return shortText(first, max);\n  const cut = first.slice(0, max - 1).replace(/\\s+\\S*$/, \"\");\n  return shortText(`${cut || first.slice(0, max - 1)}…`, max);\n}\n\n/**\n * A worker pane's bottom line: `<goal> · <model> · started HH:MM · <age>` —\n * what the pane is FOR and how long it has run, never the tool call it\n * happens to be executing this second (that is `pai worker ps` / `follow`\n * territory). `columns`, when known, shrinks the goal so the fixed\n * `model · started · age` tail always survives — trimming that tail instead\n * would defeat the one thing an operator scanning several panes needs at a\n * glance. A legacy status with no recorded model omits that field rather\n * than rendering a bare gap.\n */\nexport function paneStatusRow(\n  s: Pick<WorkerStatus, \"label\" | \"model\" | \"started\">,\n  now: Date = new Date(),\n  columns: number | null = null\n): string {\n  const model = shortModel(s.model);\n  const started = String(s.started ?? \"\").slice(11, 16);\n  const age = ageOf(s.started, now);\n  const tail = [model, started ? `started ${started}` : \"\", age].filter(Boolean).join(\" · \");\n  const budget = columns !== null ? Math.max(1, columns - tail.length - 3) : 40;\n  return `${goalOf(s, budget)} · ${tail}`;\n}\n\n/**\n * The statusline bar: `provider ▶N · <models> · oldest <age>` and today's\n * ✓/✗ tail. Three workers used to spell out three goals, three ages and three\n * models on one line and none of it fit; each worker's own pane bottom line\n * now carries `goal · model · started · age` (paneStatusRow) and `pai worker\n * ps` has the full table, so the bar only has to answer how many workers are\n * running, on what models, and how long the oldest has been going. `<models>`\n * lists distinct short model names, most-populous first (ties broken by\n * name), each suffixed `×N` past one; an empty model counts under `?` rather\n * than vanishing (a legacy status with no recorded model is still a worker).\n * `columns`, when known, shrinks only the models segment (shortText) so the\n * head, the `▶N` count and the today tally — the three things worth reading\n * at a glance — never get cut for the one segment that can grow without\n * bound. The chat pane contributes nothing but its provider: its age, state\n * and inbox live in `pai worker ps`, not here.\n */\nexport function renderStatusLine(\n  mine: WorkerStatus[],\n  now: Date = new Date(),\n  active: string | null = null,\n  columns: number | null = null\n): string {\n  // `active` is the live routing choice (workers.active). The head names the\n  // provider the next worker goes to, so it has to come from the config on\n  // every refresh: the chat pane's own `provider` is frozen at pane launch and\n  // stops being true the moment `pai worker providers use <name>` runs — the\n  // bar then shows a provider hours out of date (2026-09-19). \"auto\" is not\n  // one provider, so it falls through to what is actually running.\n  const live = active && active !== \"auto\" ? active : null;\n  if (!mine.length) return live ?? \"\";\n  // isChatPane carries the migration shim for pre-`origin` entries (see\n  // status.ts): the chat pane is not a worker row and not counted in ▶N.\n  const isChat = isChatPane;\n  const chat = mine.find((s) => isChat(s) && isLive(s));\n  const running = mine.filter((s) => !isChat(s) && isLive(s));\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\n  let head: string;\n  if (live) head = live;\n  else if (chat) head = chat.provider;\n  else {\n    const providers = new Set(running.map((s) => s.provider));\n    head = providers.size === 1 ? [...providers][0] : \"workers\";\n  }\n  if (running.length) head += ` ▶${running.length}`;\n  const tail = doneToday.length ? `   ✓${ok} ✗${bad} today` : \"\";\n\n  if (!running.length) return head + tail;\n\n  const counts = new Map<string, number>();\n  for (const s of running) {\n    const m = shortModel(s.model) || \"?\";\n    counts.set(m, (counts.get(m) ?? 0) + 1);\n  }\n  const modelsFull = [...counts.entries()]\n    .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n    .map(([m, n]) => (n > 1 ? `${m} ×${n}` : m))\n    .join(\" · \");\n  const oldest = running.reduce((a, b) => (a.started < b.started ? a : b));\n  const oldestPart = `oldest ${ageOf(oldest.started, now)}`;\n\n  let models = modelsFull;\n  const fixedLen = head.length + 3 + oldestPart.length + tail.length; // + \" · \"\n  if (columns !== null && fixedLen + 3 + modelsFull.length > columns) {\n    const budget = columns - fixedLen - 3;\n    models = budget > 0 ? shortText(modelsFull, budget) : \"\";\n  }\n  const mid = [models, oldestPart].filter(Boolean).join(\" · \");\n  return `${head} · ${mid}${tail}`;\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 (local wall clock — stamps carry a local\n * offset and old UTC stamps are converted), the worker tag in front when\n * several run at once, a date separator when the day changes, and — on a TTY —\n * a liveness line (`⋯ 12s · run tests before the fix · $ bun run test`) that\n * is rewritten in place between events. Attaching to a worker that is already\n * running first replays its last events (backfill), then continues live.\n *\n * Following one worker turns the pane into a small chat (see chatui.ts): the\n * transcript scrolls in a region that ends two rows above the bottom, the\n * prompt row (`› `, readline editing) and the ticker row stay fixed, and every\n * submitted line is said to the worker while it runs and resumes it (same\n * Claude session) once it has finished. Lines the pane wraps itself keep the\n * `│` bar on continuation rows, so no content ever lands left of the bar.\n * Non-TTY output keeps the plain scrolling behaviour.\n */\n\nimport { existsSync, openSync, readSync, closeSync, readFileSync } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { spawn, type SpawnOptions } from \"node:child_process\";\nimport { eventsPath } from \"./paths.js\";\nimport { alive, isLive, loadStatuses, type WorkerStatus } from \"./status.js\";\nimport { currentTabKey, resolveSession, workerInScope } from \"./scope.js\";\nimport { readInbox } from \"./handoff.js\";\nimport { sayToWorker } from \"./operator.js\";\nimport {\n  CHAT_HELP,\n  chatBlankRow,\n  chatEnter,\n  chatInsertLine,\n  chatLeave,\n  chatPromptRow,\n  chatScrollRegion,\n  chatTickerRow,\n  holdAutoExit,\n  parseChatLine,\n  wrapText,\n} from \"./chatui.js\";\nimport {\n  blankBetween,\n  contextMeter,\n  dayOf,\n  gutterFor,\n  headerLine,\n  intentOf,\n  makeColor,\n  paneStatusRow,\n  renderEvent,\n  renderStatusLine,\n  renderTable,\n  tickerText,\n  tickerTool,\n  type Gutter,\n  type Paint,\n  type StatusRow,\n  type StreamEventLike,\n} from \"./render.js\";\n\n/** How many existing events a fresh follow pane replays before going live. */\nexport const BACKFILL_EVENTS = 200;\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  const inbox = inboxCounts(logDir, scoped);\n  return renderTable(c, scoped, scopeLabel, new Date(), inbox);\n}\n\n/** Handoffs waiting in each listed worker's inbox: id → count. */\nexport function inboxCounts(logDir: string, statuses: WorkerStatus[]): Record<string, number> {\n  const out: Record<string, number> = {};\n  for (const s of statuses) {\n    const n = readInbox(logDir, s.id).length;\n    if (n) out[s.id] = n;\n  }\n  return out;\n}\n\n// ---------------------------------------------------------------------------\n// one event → rendered lines (shared by replay, backfill and the live tail)\n// ---------------------------------------------------------------------------\n\n/** What applyEvent() carries between the events of one transcript. */\nexport interface FollowState {\n  /** day separator already printed (\"\" before the first stamped event). */\n  lastDay: string;\n  /** tool_use id → tool name (Edit previews and Read trimming need it). */\n  tools: Record<string, string>;\n  /** the worker's last stated intent — its last assistant text, ≤60 chars. */\n  intent: string;\n  /** the ticker's tool part of the last tool_use (\"$ bun run test\"). */\n  tool: string;\n  /** the last event seen, for the blank line between turns. */\n  prev: StreamEventLike | null;\n}\n\nexport function initialFollowState(intent = \"waiting for first event\"): FollowState {\n  return { lastDay: \"\", tools: {}, intent, tool: \"\", prev: null };\n}\n\n/** One applied event: what to print, and how the ticker changes. */\nexport interface FollowStep {\n  /** rendered lines (\"\" among them marks the blank line between turns). */\n  lines: string[];\n  /** day separator to print first, when the stamp's local day changed. */\n  day: string | null;\n  /** the event produced visible output — the ticker clock restarts. */\n  activity: boolean;\n  state: FollowState;\n}\n\n/**\n * Gutter the rendered body of one event, wrapping when the pane width is\n * known. Unwrapped (null `wrapWidth`, e.g. piped output): the first row gets\n * the stamped gutter, later rows of the event blanks — exactly the pre-chat\n * rendering. Wrapped: every row is folded at `wrapWidth` columns and each\n * continuation row carries the blank gutter with the `│` bar, so the bar runs\n * unbroken down the pane and no content ever lands left of it.\n */\nexport function gutterBody(\n  body: string[],\n  gutter: Gutter | null,\n  wrapWidth: number | null\n): string[] {\n  if (!gutter) return body;\n  if (wrapWidth === null || wrapWidth <= gutter.width) {\n    return body.map((ln, i) => (i === 0 ? gutter.first : gutter.cont) + ln);\n  }\n  const out: string[] = [];\n  for (const ln of body) {\n    for (const piece of wrapText(ln, wrapWidth - gutter.width)) {\n      out.push((out.length === 0 ? gutter.first : gutter.barCont) + piece);\n    }\n  }\n  return out;\n}\n\n/**\n * Render one event and advance the follow state. Everything the viewer shows\n * between events of one worker comes from here: replay, the backfill on\n * attach and the live tail all use it, so they space identically — events\n * back to back, one blank line between turns. `activity` is false for events\n * that render nothing (stream noise, empty tool results): they leave the\n * ticker's \"since last event\" clock running. `wrapWidth` (the pane's column\n * count, re-read on resize) makes the viewer wrap rows itself; null keeps\n * the terminal's own wrapping.\n */\nexport function applyEvent(\n  c: Paint,\n  s: FollowState,\n  e: StreamEventLike,\n  cwd: string,\n  tag?: string,\n  offMin?: number,\n  wrapWidth?: number | null\n): FollowStep {\n  const tools = { ...s.tools };\n  if (e.type === \"assistant\") {\n    for (const b of e.message?.content ?? []) {\n      if (b.type === \"tool_use\" && b.id) tools[b.id] = b.name ?? \"?\";\n    }\n  }\n  const day = typeof e._ts === \"string\" ? (dayOf(e._ts, offMin) ?? rawDay(e._ts)) : \"\";\n  const state: FollowState = {\n    lastDay: day && day !== s.lastDay ? day : s.lastDay,\n    tools,\n    intent: s.intent,\n    tool: s.tool,\n    prev: e,\n  };\n  if (e.type === \"assistant\") {\n    for (const b of e.message?.content ?? []) {\n      if (b.type === \"text\" && (b.text ?? \"\").trim()) state.intent = intentOf(b.text ?? \"\");\n      else if (b.type === \"tool_use\") state.tool = tickerTool(b.name ?? \"?\", b.input);\n    }\n  }\n  const gutter = gutterFor(c, e, tag, offMin);\n  const prefix = \"\";\n  const body = gutterBody(renderEvent(c, prefix, e, cwd, tools), gutter, wrapWidth ?? null);\n  // the turn separator carries the gutter's bar (barCont) so the │ runs\n  // unbroken down the pane; \"\" only when the event has no stamp/gutter\n  const lines = blankBetween(s.prev, e) ? [gutter ? gutter.barCont : \"\", ...body] : body;\n  return {\n    lines,\n    day: day && day !== s.lastDay ? day : null,\n    activity: lines.some((ln) => ln !== \"\"),\n    state,\n  };\n}\n\n/** `YYYY-MM-DD` straight out of an unparsable stamp, \"\" when it has none. */\nfunction rawDay(ts: string): string {\n  return /^\\d{4}-\\d{2}-\\d{2}/.test(ts) ? ts.slice(0, 10) : \"\";\n}\n\n/**\n * The events a fresh follow replays before going live: the last `cap`\n * non-empty log lines, oldest first.\n */\nexport function backfillLines(raw: string, cap = BACKFILL_EVENTS): string[] {\n  return raw.split(\"\\n\").filter((l) => l.trim()).slice(-cap);\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 wrapWidth = typeof process.stdout.columns === \"number\" ? process.stdout.columns : null;\n  const out: string[] = [headerLine(c, st)];\n  const raw = readFileSync(path, \"utf8\");\n  const lines = tailLines !== undefined ? raw.split(\"\\n\").slice(-tailLines) : raw.split(\"\\n\");\n  // the worker's inbox handoffs join the transcript where they happened (_ts)\n  const handoffs: StreamEventLike[] = readInbox(logDir, wid).map((h) => ({\n    type: \"handoff\",\n    from: h.from,\n    kind: h.kind,\n    text: h.text,\n    _ts: h._ts,\n  }));\n  let state = initialFollowState(\"\");\n  const events: StreamEventLike[] = [];\n  for (const line of 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    events.push(e);\n  }\n  const stamp = (e: StreamEventLike): number => {\n    const t = e._ts ? Date.parse(e._ts) : NaN;\n    return Number.isNaN(t) ? 0 : t;\n  };\n  const merged = [...events, ...handoffs].sort((a, b) => stamp(a) - stamp(b));\n  for (const e of merged) {\n    const step = applyEvent(c, state, e, st.cwd ?? \"\", undefined, undefined, wrapWidth);\n    if (step.day) out.push(c(\"dim\", `── ${step.day} ──`));\n    out.push(...step.lines);\n    state = step.state;\n  }\n  return out.join(\"\\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// operator input (2i): this pane's stdin drives say / resume\n// ---------------------------------------------------------------------------\n\n/** What makeOperatorInput() needs from the follow around it. */\nexport interface OperatorInputDeps {\n  /** the worker typed lines go to right now (null: none, lines are ignored). */\n  target: () => string | null;\n  /** forwards one message to a running worker. */\n  say: (id: string, text: string) => Promise<string>;\n  /** whether a status file exists for the id (say failed → resume, or note). */\n  workerKnown: (id: string) => boolean;\n  /** continues a finished worker (same Claude session). */\n  resume: (text: string, id: string) => void;\n  /** one line of feedback in the pane. */\n  note: (s: string) => void;\n  paint: Paint;\n  /** chat mode only: the echoed line replaces the \"» sent\" note. */\n  sent?: (id: string) => void;\n}\n\n/**\n * One typed stdin line → say (worker running) or resume (worker finished):\n * the operator channel of a `follow <id>` pane. Trimmed; empty lines and a\n * missing target are ignored.\n */\nexport function makeOperatorInput(d: OperatorInputDeps): (raw: string) => void {\n  return (raw: string) => {\n    const text = raw.trim();\n    if (!text) return;\n    const id = d.target();\n    if (id === null) return;\n    d.say(id, text).then(\n      () => (d.sent ? d.sent(id) : d.note(d.paint(\"dim\", `» sent to ${id}`))),\n      (e: Error) => {\n        if (d.workerKnown(id)) d.resume(text, id);\n        else d.note(d.paint(\"red\", `» ${e.message}`));\n      }\n    );\n  };\n}\n\n/** What followWorkers writes to (process.stdout, or a fake in tests). */\nexport interface FollowStream {\n  write(s: string): boolean;\n  isTTY?: boolean;\n  columns?: number;\n  rows?: number;\n  on?(event: \"resize\", fn: () => void): unknown;\n  removeListener?(event: \"resize\", fn: () => void): unknown;\n}\n\n/** The child of a `pai worker resume` spawn (tests inject a fake). */\nexport interface ResumeChild {\n  stdout?: { on(event: \"data\", cb: (chunk: Buffer) => void): unknown };\n  on(event: \"close\", cb: (code: number | null) => void): unknown;\n}\n\n/** Test seams for followWorkers: the streams, the resume spawn, the prompt. */\nexport interface FollowIO {\n  stdin?: NodeJS.ReadableStream;\n  stdout?: FollowStream;\n  /** replaces the `pai worker resume` spawn (tests record instead of run). */\n  spawnResume?: (id: string, text: string) => ResumeChild;\n  /** current unsent prompt text (tests force a draft to hold the countdown). */\n  promptLine?: () => string;\n}\n\n/**\n * Tail one worker (target) or the running workers of this scope, live.\n * Workers whose event log already exists are first replayed (last\n * BACKFILL_EVENTS events), then tailed. Auto-exit: with a target, wait\n * `autoExit` seconds after its end; without one, exit once no worker in\n * scope has run for that many seconds in a row, never within the first 30 s.\n *\n * With a target on a TTY the pane becomes a chat (chatui.ts): transcript in\n * a scroll region, fixed prompt and ticker rows, submitted lines said to the\n * worker (or resuming it after it finished) and echoed as `»` rows. A draft\n * in the prompt holds the auto-exit countdown. Non-TTY output keeps the\n * plain scrolling behaviour; FORCE_TTY=1 emits the chat layout over a pipe.\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  io?: FollowIO\n): Promise<void> {\n  const c = makeColor(color);\n  const out_ = io?.stdout ?? process.stdout;\n  const in_ = io?.stdin ?? process.stdin;\n  // FORCE_TTY=1: the TTY layout over a pipe (tests, recorded panes)\n  const tty = out_.isTTY === true || env.FORCE_TTY === \"1\";\n  const term = env.ITERM_SESSION_ID ?? \"\";\n  const scopeTab = target || showAll ? \"\" : currentTabKey(env);\n  const handles = new Map<string, FollowHandle>();\n  const seenHeader = new Set<string>();\n  const finished = new Set<string>();\n  // handoffs already rendered per worker (id → inbox lines shown so far)\n  const inboxSeen = new Map<string, number>();\n  const states = new Map<string, FollowState>();\n  const started = Date.now();\n  let idleSince: number | null = null;\n  let aborted = false;\n  // liveness state: rewritten in place between events, TTY only\n  let lastEventAt = Date.now();\n  let meterStatus: WorkerStatus | null = null;\n  // the pane's bottom line (chat mode): the target's current status, kept\n  // fresh every loop tick (not only on a new event) so a `pai worker goal`\n  // relabel shows up on the pane's next refresh, not its next tool call\n  let paneStatus: WorkerStatus | undefined;\n  const onInt = () => {\n    aborted = true;\n  };\n  process.once(\"SIGINT\", onInt);\n\n  // --- the chat layout (target + TTY): transcript region, a blank separator\n  // row, then the two fixed rows (prompt, ticker)\n  const chat = tty && target !== null;\n  let rows = out_.rows ?? 24;\n  const columns = (): number | null => (typeof out_.columns === \"number\" ? out_.columns : null);\n  let fill = 0; // transcript rows filled since the region was (re)set\n  const regionRows = () => Math.max(1, rows - 3);\n  // the rendered transcript, kept so a resize can replay it onto the cleared\n  // pane (chat mode only; capped so a long run cannot grow without bound)\n  const retained: string[] = [];\n  const RETAIN_CAP = 500;\n  /** Every line the pane shows goes through here: plain newline, or a row\n   *  inserted above the fixed prompt/ticker rows (chatui.chatInsertLine). */\n  const out = (line: string, keep = true) => {\n    if (!chat) {\n      out_.write(line + \"\\n\");\n      return;\n    }\n    if (keep) {\n      retained.push(line);\n      if (retained.length > RETAIN_CAP) retained.splice(0, retained.length - RETAIN_CAP);\n    }\n    const r = chatInsertLine(line, fill, regionRows());\n    out_.write(r.seq);\n    fill = r.fill;\n  };\n\n  // The ticker redraws its line in place: in the chat layout that is its own\n  // bottom row (save-cursor, draw, restore-cursor); the plain mode writes\n  // CR + erase-to-end-of-line, never a newline. The control bytes live in\n  // their own string literals — bundling them onto a template literal makes\n  // the bundler fold them in as raw chars, and a raw CR inside a template\n  // literal is normalised to LF by the language, which is how the ticker\n  // once scrolled a blank line per tick.\n  const eraseLiveness = () => {\n    if (chat) return;\n    if (tty) out_.write(\"\\r\\x1b[K\");\n  };\n  let ticker = initialFollowState();\n  // the chat ticker row doubles as the worker's status line: provider/model,\n  // context meter, turns, tools, runtime — frozen with ✓/✗ once it finished\n  const statusRowOf = (st: WorkerStatus, secs: number): StatusRow => {\n    const fin = finished.has(st.id);\n    const startedAt = Date.parse((st.started ?? \"\").replace(\" \", \"T\"));\n    const elapsed =\n      fin && st.secs !== null\n        ? st.secs\n        : Number.isNaN(startedAt)\n          ? 0\n          : Math.floor((Date.now() - startedAt) / 1000);\n    return {\n      provider: st.provider,\n      model: st.model,\n      contextTokens: st.contextTokens,\n      contextWindow: st.contextWindow,\n      turns: st.turns,\n      tools: st.tools,\n      elapsed,\n      idle: secs,\n      intent: ticker.intent,\n      tool: ticker.tool,\n      state: fin ? (st.state === \"running\" ? \"done\" : st.state) : null,\n    };\n  };\n  const writeLiveness = () => {\n    if (!tty) return;\n    const secs = Math.max(0, Math.floor((Date.now() - lastEventAt) / 1000));\n    if (chat) {\n      // the pane's bottom line: goal · model · started · age, never the tool\n      // call this second (see paneStatusRow) — before the first status file\n      // exists there is nothing to show it from yet, so the plain ticker\n      // covers that brief startup window only\n      const text = paneStatus\n        ? paneStatusRow(paneStatus, new Date(), columns())\n        : tickerText(secs, ticker.intent, ticker.tool);\n      out_.write(chatTickerRow(text, rows, promptCursorCol()));\n    } else {\n      const meter = meterStatus ? contextMeter(c, meterStatus) : null;\n      out_.write(\"\\r\\x1b[K\");\n      out_.write(tickerText(secs, ticker.intent, ticker.tool, meter));\n    }\n  };\n\n  const runningIds = (): string[] =>\n    loadStatuses(logDir)\n      .filter(\n        (s) =>\n          isLive(s) &&\n          (target !== null || showAll || (scopeTab ? workerInScope(s, term) : true))\n      )\n      .map((s) => s.id);\n\n  // --- one rendered event, shared by the backfill and the live tail\n  const emitEvent = (e: StreamEventLike, wid: string, st: WorkerStatus, multi: boolean) => {\n    if (e.type === \"operator\" && chat && suppressMirror(String(e.text ?? \"\"))) return;\n    if (!seenHeader.has(wid)) {\n      eraseLiveness();\n      out(headerLine(c, st));\n      seenHeader.add(wid);\n    }\n    const state = states.get(wid) ?? initialFollowState();\n    const step = applyEvent(\n      c,\n      state,\n      e,\n      st.cwd ?? \"\",\n      multi ? c(\"cyan\", wid.slice(-4)) : undefined,\n      undefined,\n      columns()\n    );\n    if (step.day) {\n      out(c(\"dim\", `── ${step.day} ──`));\n    }\n    states.set(wid, step.state);\n    if (wid === target) ticker = step.state;\n    eraseLiveness();\n    for (const ln of step.lines) {\n      out(ln);\n    }\n    if (step.activity) lastEventAt = Date.now();\n    if (e.type === \"result\") {\n      meterStatus = st;\n      finished.add(wid);\n    } else if (wid === target || target === null) {\n      meterStatus = st;\n    }\n  };\n\n  // --- attach: replay what is already in the log, then tail from its end\n  const attachHandle = (wid: string, path: string, st: WorkerStatus, multi: boolean): FollowHandle => {\n    const handle = { fd: openSync(path, \"r\"), buf: \"\" };\n    try {\n      const existing = readFileSync(path, \"utf8\");\n      if (existing.trim()) {\n        for (const line of backfillLines(existing)) {\n          let e: StreamEventLike;\n          try {\n            e = JSON.parse(line) as StreamEventLike;\n          } catch {\n            continue;\n          }\n          emitEvent(e, wid, st, multi);\n        }\n      } else {\n        // no event yet: show the header now, not at the first event\n        if (!seenHeader.has(wid)) {\n          out(headerLine(c, st));\n          seenHeader.add(wid);\n        }\n      }\n      // the backfill already rendered the file; the live tail starts at EOF\n      const sink = Buffer.alloc(65536);\n      for (;;) {\n        let n: number;\n        try {\n          n = readSync(handle.fd, sink, 0, sink.length, null);\n        } catch {\n          break;\n        }\n        if (n <= 0) break;\n      }\n    } catch {\n      // unreadable log: tail from wherever the fd happens to be\n    }\n    return handle;\n  };\n\n  // --- say / resume from this pane's stdin\n  const noteLine = (s: string) => {\n    eraseLiveness();\n    out(s);\n  };\n  // SpawnOptions (not the stdio-tuple overload): we only read stdout and\n  // want the plain ChildProcess shape; tests inject a fake that records\n  const spawnResume =\n    io?.spawnResume ??\n    ((id: string, text: string): ResumeChild =>\n      spawn(\"pai\", [\"worker\", \"resume\", id, text, \"--print-id\", \"--no-pane\"], {\n        stdio: [\"ignore\", \"pipe\", \"inherit\"],\n      } as SpawnOptions) as unknown as ResumeChild);\n  const resumeTarget = (text: string, id: string) => {\n    noteLine(c(\"dim\", `» resuming ${id} …`));\n    const child = spawnResume(id, text);\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 && /^\\d{8}-\\d{6}-\\d+$/.test(newId)) {\n        noteLine(c(\"dim\", `» resumed as ${newId}`));\n        target = newId;\n        finished.delete(newId);\n        seenHeader.delete(newId);\n        states.delete(newId);\n        ticker = initialFollowState(\"resumed\");\n        lastEventAt = Date.now();\n      } else {\n        noteLine(c(\"red\", `» resume failed (rc=${rc})`));\n      }\n    });\n  };\n  const handleOperatorLine = makeOperatorInput({\n    target: () => target,\n    say: (id, text) => sayToWorker(logDir, id, text),\n    workerKnown: (id) => loadStatuses(logDir).some((s) => s.id === id),\n    resume: resumeTarget,\n    note: noteLine,\n    paint: c,\n    ...(chat ? { sent: () => undefined } : {}), // the echo replaces the note\n  });\n\n  // --- the chat line: readline editing on the prompt row\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  const terminalIn = (in_ as { isTTY?: boolean }).isTTY === true;\n  let rlIn: ReturnType<typeof createInterface> | null = null;\n  let onResize: (() => void) | null = null;\n  // an echoed line comes back as a mirrored operator event within moments —\n  // remember what was echoed and swallow the twin, so the pane shows what\n  // was typed exactly once\n  const echoed = new Map<string, { n: number; until: number }>();\n  const suppressMirror = (text: string): boolean => {\n    const g = echoed.get(text);\n    if (!g || Date.now() > g.until) return false;\n    g.n -= 1;\n    if (g.n <= 0) echoed.delete(text);\n    return true;\n  };\n  /** Column the prompt row's cursor parks at: right after the draft's cursor. */\n  const promptCursorCol = (): number => {\n    const line = rlIn?.line ?? \"\";\n    const cur = (rlIn as unknown as { cursor?: number } | null)?.cursor;\n    return 3 + Math.max(0, Math.min(typeof cur === \"number\" ? cur : line.length, line.length));\n  };\n  /**\n   * The pane owns the prompt row's rendering: `› `, the buffer — or the dim\n   * placeholder while the buffer is empty — and the cursor parked after it.\n   */\n  const drawPrompt = () => {\n    if (!chat) return;\n    const line = rlIn?.line ?? \"\";\n    const cur = (rlIn as unknown as { cursor?: number } | null)?.cursor;\n    out_.write(chatPromptRow(rows, line, (s) => c(\"dim\", s), cur));\n  };\n  if (chat) {\n    const echoOperator = (text: string) => {\n      const id = target;\n      if (id === null) return;\n      echoed.set(text, { n: 1, until: Date.now() + 10_000 });\n      const st = states.get(id) ?? initialFollowState();\n      const step = applyEvent(\n        c,\n        st,\n        { type: \"operator\", _ts: new Date().toISOString(), text },\n        \"\",\n        undefined,\n        undefined,\n        columns()\n      );\n      if (step.day) out(c(\"dim\", `── ${step.day} ──`));\n      for (const ln of step.lines) out(ln);\n      states.set(id, step.state);\n    };\n    const handleChatLine = (raw: string) => {\n      const act = parseChatLine(raw);\n      let redrew = false;\n      switch (act.kind) {\n        case \"message\":\n          if (!act.text) break;\n          handleOperatorLine(act.text); // send the line …\n          drawPrompt(); // … placeholder back before the » echo is written\n          redrew = true;\n          echoOperator(act.text);\n          break;\n        case \"resume\":\n          if (!act.text) {\n            out(c(\"dim\", \"usage: /resume <text>\"));\n            break;\n          }\n          if (target !== null) resumeTarget(act.text, target);\n          drawPrompt();\n          redrew = true;\n          echoOperator(act.text);\n          break;\n        case \"help\":\n          for (const ln of CHAT_HELP) out(c(\"dim\", ln));\n          break;\n        case \"status\": {\n          const s =\n            target !== null ? loadStatuses(logDir).find((x) => x.id === target) : undefined;\n          out(c(\"dim\", s ? `${s.id} · ${s.state} · ${s.last}` : `${target ?? \"?\"} · no status`));\n          break;\n        }\n        case \"quit\":\n          aborted = true;\n          break;\n      }\n      if (!redrew) drawPrompt(); // on a pipe readline does not repaint the row\n    };\n    // readline edits silently (its output is a mute stream); the pane draws\n    // the prompt row itself from the live buffer, so the placeholder yields\n    // to the first keystroke and returns when the buffer empties again.\n    // The emitter no-ops matter: with terminal:true readline attaches a\n    // resize listener on its output, and a bare { write } object crashes\n    // follow in a real terminal (\"output.on is not a function\").\n    const silent = {\n      write: () => true,\n      on: () => silent,\n      once: () => silent,\n      off: () => silent,\n      removeListener: () => silent,\n      emit: () => false,\n    } as unknown as NodeJS.WriteStream;\n    rlIn = createInterface({ input: in_, output: silent, terminal: terminalIn });\n    rlIn.on(\"line\", handleChatLine);\n    // Ctrl-C: an empty prompt leaves, a draft clears; Ctrl-D (close) leaves\n    rlIn.on(\"SIGINT\", () => {\n      if ((rlIn?.line ?? \"\").trim() === \"\") aborted = true;\n      else {\n        rlIn?.write(null, { ctrl: true, name: \"u\" });\n        drawPrompt();\n      }\n    });\n    rlIn.on(\"close\", () => {\n      aborted = true;\n    });\n    // the separator rule above the prompt row needs the pane's width and the\n    // pane's dim colour — chatEnter without cols leaves the row blank\n    out_.write(chatEnter(rows, columns() ?? 0, (s) => c(\"dim\", s)));\n    drawPrompt();\n    if (terminalIn) {\n      // every keystroke re-renders the row (and re-parks the cursor) from\n      // the buffer readline now holds\n      (in_ as NodeJS.ReadableStream).on(\"keypress\", () => drawPrompt());\n    }\n    // resize: re-read the geometry, rebuild the region, fill it afresh\n    onResize = () => {\n      if (typeof out_.rows === \"number\") rows = out_.rows;\n      fill = 0;\n      // clear first: the refill lands rows top-down and must not overwrite\n      // the stale transcript left under the old geometry\n      out_.write(\n        \"\\x1b[2J\" + chatScrollRegion(rows) + chatBlankRow(rows, columns() ?? 0, (s) => c(\"dim\", s))\n      );\n      // the retained transcript replays in order, newest regionRows() lines\n      // only - a shrunken pane shows its newest rows, not a scroll replay\n      for (const ln of retained.slice(-regionRows())) out(ln, false);\n      drawPrompt();\n    };\n    out_.on?.(\"resize\", onResize);\n  } else if (target !== null && (in_ as { isTTY?: boolean }).isTTY) {\n    // plain operator channel: TTY stdin, non-TTY stdout\n    rlIn = createInterface({ input: in_ });\n    rlIn.on(\"line\", handleOperatorLine);\n  }\n\n  /** The prompt's unsent text — a draft holds the auto-exit countdown. */\n  const promptText = () => (io?.promptLine ? io.promptLine() : (rlIn?.line ?? \"\"));\n\n  try {\n    for (;;) {\n      if (aborted) return;\n      const statuses = new Map(loadStatuses(logDir).map((s) => [s.id, s]));\n      if (target) paneStatus = statuses.get(target) ?? paneStatus;\n      const wanted = target ? [target] : runningIds();\n      for (const wid of wanted) {\n        if (handles.has(wid) || finished.has(wid)) continue;\n        const path = eventsPath(logDir, wid);\n        const st = statuses.get(wid) ?? ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n        if (existsSync(path)) {\n          const multi = target === null || handles.size > 0;\n          handles.set(wid, attachHandle(wid, path, st, multi));\n          states.set(wid, states.get(wid) ?? initialFollowState());\n        } else if (!seenHeader.has(wid)) {\n          // worker just started, no event yet: name it instead of a blank pane\n          out(headerLine(c, st));\n          seenHeader.add(wid);\n        }\n      }\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        // 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          if (!line.trim()) continue;\n          progressed = true;\n          let e: StreamEventLike;\n          try {\n            e = JSON.parse(line) as StreamEventLike;\n          } catch {\n            continue;\n          }\n          emitEvent(e, wid, st, multi);\n        }\n        const ended = workerEnded(finished.has(wid), st.state, alive(st.pid));\n        if (ended) {\n          if (!finished.has(wid)) {\n            eraseLiveness();\n            out(\n              `${multi ? c(\"cyan\", wid.slice(-4)) + c(\"dim\", \" ┃ \") : \"  \"}${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      // inbox tail: new handoffs render as ◆ lines in the recipient's pane\n      // (they may also arrive via the say mirror — the durable copy is here)\n      for (const wid of [...handles.keys()]) {\n        const st = statuses.get(wid) ?? ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n        const msgs = readInbox(logDir, wid);\n        const seenN = inboxSeen.get(wid) ?? 0;\n        if (msgs.length > seenN) {\n          for (const m of msgs.slice(seenN)) {\n            emitEvent(\n              { type: \"handoff\", from: m.from, kind: m.kind, text: m.text, _ts: m._ts },\n              wid,\n              st,\n              multi\n            );\n          }\n          inboxSeen.set(wid, msgs.length);\n          progressed = true;\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          // a draft in the prompt holds the countdown: the operator may be\n          // about to say or resume something\n          if (holdAutoExit(promptText())) {\n            writeLiveness();\n            await sleep(250);\n            continue;\n          }\n          const until = Date.now() + autoExit * 1000;\n          while (\n            Date.now() < until &&\n            !aborted &&\n            target === lingerOn &&\n            !holdAutoExit(promptText())\n          ) {\n            writeLiveness();\n            await sleep(250);\n          }\n          // interrupted, resumed inside the window, or a draft appeared: follow on\n          if (aborted || target !== lingerOn || holdAutoExit(promptText())) continue;\n          eraseLiveness();\n          out(c(\"dim\", \"closing\"));\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          out(c(\"dim\", \"closing\"));\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    if (onResize) out_.removeListener?.(\"resize\", onResize);\n    if (chat) out_.write(chatLeave(rows));\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  claudeSession: string = \"\",\n  now: Date = new Date(),\n  /** Live routing choice (workers.active) — the head of the line. */\n  active: string | null = null\n): string {\n  const statuses = loadStatuses(logDir);\n  const mine = statuses.filter((s) => {\n    const sameScope = term && workerInScope(s, term);\n    // orchestrator Bash spawns have no terminal identity; their spawner\n    // session (the claude session rendering this bar) claims them instead\n    const spawnedHere = claudeSession && s.spawnerSession === claudeSession;\n    const sameDir = cwd && s.cwd.startsWith(cwd);\n    return sameScope || spawnedHere || (!term && sameDir);\n  });\n  return renderStatusLine(mine, now, active);\n}\n","/**\n * providers.ts — provider, class 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 * The CLI's `--key` writes the token inline as `key:` in workers.yaml\n * (quoted, and the file is kept 0600 — see workers-config.ts); `--key-file`\n * still writes only a path. The MCP `add` tool instead accepts a raw `key`\n * and parks it in ~/.claude/pai/keys/<name>, mode 0600, storing only the\n * path in the config — a chat is not a place to leave a credential lying\n * around.\n */\n\nimport { existsSync, writeFileSync, chmodSync, mkdirSync } from \"node:fs\";\nimport {\n  ANTHROPIC_NATIVE,\n  DEFAULT_LOG_DIR,\n  MODEL_CAPABILITIES,\n  PROVIDER_TAGS,\n  WorkersConfigError,\n  expandHome,\n  isModelCapability,\n  keysDir,\n  maskKey,\n  nativeAnthropicProvider,\n  providerCostTier,\n  readWorkersSection,\n  resolveCapability,\n  writeWorkersSection,\n  type ClassTarget,\n  type ModelCapability,\n  type WorkerProvider,\n  type WorkersConfig,\n} from \"./config.js\";\nimport { clearCooldown, probeQuota, quotaSkipThreshold } from \"./routing.js\";\nimport { workersLogDir } from \"./paths.js\";\nimport { workersYamlLegacyNotice, workersYamlPath } from \"./workers-config.js\";\nimport { CONFIG_FILE } from \"../daemon/config.js\";\n\nexport interface AddProviderInput {\n  name: string;\n  baseUrl: string;\n  keyFile?: string | null;\n  /** Raw token (MCP `add` only): parked in ~/.claude/pai/keys/<name>, only the path stored. */\n  key?: string;\n  /** Raw token (CLI `--key` only): written verbatim as `key:` in workers.yaml. */\n  inlineKey?: 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\" | \"image\";\n  quotaProbe?: string;\n  contextWindow?: number;\n  costTier?: number;\n  tags?: string[];\n  /** Config file override (tests, dry runs); default ~/.claude/pai/config.yaml. */\n  configPath?: string;\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(input.configPath);\n  const provider: WorkerProvider = {\n    enabled: true,\n    protocol: input.protocol ?? \"anthropic\",\n    baseUrl: input.baseUrl,\n    keyFile,\n    ...(input.inlineKey ? { key: input.inlineKey } : {}),\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    ...(input.costTier ? { costTier: input.costTier } : {}),\n    ...(input.tags?.length ? { tags: input.tags as WorkerProvider[\"tags\"] } : {}),\n  };\n  workers.providers[input.name] = provider;\n\n  // First provider takes over the whole section: active, default classes, 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    const fast = input.fastModel ? `${input.name}/fast` : input.name;\n    workers.classes = {\n      draft: fast,\n      plan: input.name,\n      implement: input.name,\n      review: input.name,\n      research: input.name,\n      spotcheck: fast,\n      simple: fast,\n      complex: input.name,\n      image: input.name,\n    };\n  }\n\n  writeWorkersSection(raw, workers, input.configPath);\n  return workers;\n}\n\n/** Change costTier / tags on an existing provider (MCP action \"update\"). */\nexport function updateProvider(\n  name: string,\n  changes: { costTier?: number; tags?: string[] },\n  configPath?: string\n): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  const p = workers.providers[name];\n  if (!p) {\n    throw new WorkersConfigError(\n      `no provider named \"${name}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n    );\n  }\n  if (changes.costTier !== undefined) {\n    if (!Number.isInteger(changes.costTier) || changes.costTier < 1 || changes.costTier > 5) {\n      throw new WorkersConfigError(\"costTier must be an integer 1 (cheapest) … 5 (most expensive)\");\n    }\n    p.costTier = changes.costTier;\n  }\n  if (changes.tags !== undefined) {\n    for (const t of changes.tags) {\n      if (!(PROVIDER_TAGS as readonly string[]).includes(t)) {\n        throw new WorkersConfigError(`\"${t}\" is not a tag (from: ${PROVIDER_TAGS.join(\", \")})`);\n      }\n    }\n    p.tags = changes.tags as WorkerProvider[\"tags\"];\n  }\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\nexport function removeProvider(name: string, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  if (!workers.providers[name]) {\n    throw new WorkersConfigError(`no provider named \"${name}\"`);\n  }\n  delete workers.providers[name];\n  for (const [cls, target] of Object.entries(workers.classes)) {\n    const targetProvider = typeof target === \"string\" ? target.split(\"/\")[0] : target.provider;\n    if (targetProvider === name) delete workers.classes[cls];\n  }\n  if (workers.active === name) workers.active = null;\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\nexport function useProvider(name: string, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  if (name !== ANTHROPIC_NATIVE && !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, configPath);\n  return workers;\n}\n\n/** Which model capability of a provider a set touches (MODEL_CAPABILITIES). */\nexport type ModelSlot = ModelCapability;\n\n/**\n * The provider a model command targets: the named one, else the active one.\n * \"auto\" and an unset active both ask for an explicit name.\n */\nexport function resolveProviderName(workers: WorkersConfig, name?: string): string {\n  const target = name ?? workers.active ?? \"\";\n  if (workers.providers[target]) return target;\n  if (!target) throw new WorkersConfigError(\"no active provider — name one explicitly\");\n  if (target === \"auto\") {\n    throw new WorkersConfigError('active is \"auto\" — name a provider explicitly');\n  }\n  throw new WorkersConfigError(\n    `no provider named \"${target}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n  );\n}\n\n/**\n * Set a provider's model id for a capability — default, fast, image, …\n * (`pai worker model`, MCP worker_model).\n */\nexport function setProviderModel(\n  name: string,\n  capability: ModelCapability,\n  model: string,\n  configPath?: string\n): WorkersConfig {\n  if (!isModelCapability(capability)) {\n    throw new WorkersConfigError(\n      `\"${capability}\" is not a valid capability name (must match ^[a-z][a-z0-9-]*$; well-known: ${MODEL_CAPABILITIES.join(\", \")})`\n    );\n  }\n  const id = model.trim();\n  if (!id) throw new WorkersConfigError(`a ${capability} model id must not be empty`);\n  const { raw, workers } = readWorkersSection(configPath);\n  const p = workers.providers[name];\n  if (!p) {\n    throw new WorkersConfigError(\n      `no provider named \"${name}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n    );\n  }\n  p.models[capability] = id;\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\nexport function setProviderEnabled(name: string, enabled: boolean, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  const p = workers.providers[name];\n  if (!p) throw new WorkersConfigError(`no provider named \"${name}\"`);\n  p.enabled = enabled;\n  writeWorkersSection(raw, workers, configPath);\n  // `enable` is also the manual cooldown-clear (spec: routing)\n  if (enabled) clearCooldown(workersLogDir(workers), name);\n  return workers;\n}\n\n/**\n * Point a class at a target (\"provider\", \"provider/fast\", or an object with\n * provider + optional mcp/maxCostTier/requireTags/order). An object without\n * a provider only constrains auto-routing for that class.\n */\nexport function setClass(name: string, target: ClassTarget, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  if (typeof target === \"string\") {\n    const [provider, alias] = target.split(\"/\");\n    const native = provider === ANTHROPIC_NATIVE;\n    const p = native ? undefined : workers.providers[provider];\n    if (!native && !p) {\n      throw new WorkersConfigError(\n        `no provider named \"${provider}\" in \"${target}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n      );\n    }\n    if (alias) {\n      if (!isModelCapability(alias)) {\n        throw new WorkersConfigError(\n          `unknown model alias \"${alias}\" — providers expose: ${MODEL_CAPABILITIES.join(\", \")}`\n        );\n      }\n      // the native provider has no configurable model slots — any alias\n      // resolves through resolveModelCapability's own default fallback\n      if (!native && alias !== \"default\" && !p!.models[alias]) {\n        throw new WorkersConfigError(`provider \"${provider}\" has no ${alias} model configured`);\n      }\n    }\n  } else if (target.provider) {\n    if (target.provider !== ANTHROPIC_NATIVE && !workers.providers[target.provider]) {\n      throw new WorkersConfigError(\n        `no provider named \"${target.provider}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n      );\n    }\n  }\n  workers.classes[name] = target;\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\nexport function unsetClass(name: string, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  if (!(name in workers.classes)) {\n    throw new WorkersConfigError(`no class named \"${name}\"`);\n  }\n  delete workers.classes[name];\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\nexport function setWorkersEnabled(enabled: boolean, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  workers.enabled = enabled;\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\n/** `glm/fast` | `{provider, mcp, …}` → one printable target line. */\nexport function classTargetText(target: ClassTarget): string {\n  if (typeof target === \"string\") return target;\n  const bits = [\n    target.provider ?? \"(routing)\",\n    ...(target.mcp?.length ? [`mcp(${target.mcp.join(\",\")})`] : []),\n    ...(target.maxCostTier !== undefined ? [`max tier ${target.maxCostTier}`] : []),\n    ...(target.requireTags?.length ? [`needs ${target.requireTags.join(\",\")}`] : []),\n    ...(target.order?.length ? [`order [${target.order.join(\",\")}]`] : []),\n  ];\n  return bits.join(\" \");\n}\n\n/**\n * `default X  fast Y  image Z  …` — every well-known capability on one line\n * (unset ones shown as \"(none)\"), plus any open-set capability this provider\n * actually declares (e.g. `vision`), which is only listed when set.\n */\nexport function modelPrefsText(p: WorkerProvider): string {\n  const known = new Set<string>(MODEL_CAPABILITIES);\n  const extra = Object.keys(p.models).filter((c) => !known.has(c)).sort();\n  return [...MODEL_CAPABILITIES, ...extra].map((c) => `${c} ${p.models[c] ?? \"(none)\"}`).join(\"  \");\n}\n\n/**\n * Compact model listing for `pai worker model` / worker_model get: the active\n * provider, then one line per provider with all its capability preferences.\n */\nexport function describeModels(workers: WorkersConfig): string[] {\n  const names = Object.keys(workers.providers);\n  if (!names.length) {\n    return [\"no providers configured — add one with `pai worker providers add <name> …`\"];\n  }\n  const lines = [`active provider: ${workers.active ?? \"(none)\"}`];\n  for (const name of names) {\n    const p = workers.providers[name];\n    const active = workers.active === name ? \"  [active]\" : \"\";\n    lines.push(`${name}${active}  ${modelPrefsText(p)}`);\n  }\n  return lines;\n}\n\n/** Classes (in declaration order) whose target resolves to this provider. */\nfunction classesForProvider(workers: WorkersConfig, name: string): string[] {\n  return Object.entries(workers.classes)\n    .filter(([, target]) => (typeof target === \"string\" ? target.split(\"/\")[0] : target.provider) === name)\n    .map(([cls]) => cls);\n}\n\n/** Human-readable provider listing (quota probe included when configured). */\nexport function describeProviders(workers: WorkersConfig, configPath: string = CONFIG_FILE): string[] {\n  const lines: string[] = [];\n  const nativeActive = workers.active === ANTHROPIC_NATIVE;\n  lines.push(\n    `${ANTHROPIC_NATIVE}  [built-in${nativeActive ? \", active\" : \"\"}]  Claude Code's own OAuth/Max-plan login — no base URL, no API key`\n  );\n  lines.push(`    ${modelPrefsText(nativeAnthropicProvider(workers.nativeModels))}`);\n  const nativeClasses = classesForProvider(workers, ANTHROPIC_NATIVE);\n  lines.push(`    classes: ${nativeClasses.length ? nativeClasses.join(\", \") : \"(none)\"}`);\n  const names = Object.keys(workers.providers);\n  if (!names.length) {\n    lines.push(`no other providers configured. Add one with:`);\n    lines.push(\n      `  pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n    );\n    lines.push(`config: ${existsSync(workersYamlPath()) ? workersYamlPath() : configPath}`);\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    const tierTags = [`tier ${providerCostTier(p)}`, ...(p.tags ?? [])].join(\", \");\n    lines.push(`${name}  [${flags.join(\", \")}]  ${p.baseUrl}`);\n    lines.push(`    ${modelPrefsText(p)}${quotaNote}`);\n    lines.push(`    ${tierTags}`);\n    if (p.key) lines.push(`    key ${maskKey(p.key)}`);\n    if (p.keyFile) lines.push(`    key file ${expandHome(p.keyFile)}`);\n    if (!p.key && !p.keyFile) 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    if (p.engine === \"image\") {\n      lines.push(`    engine image (no process spawn — POSTs {url}/images/generations directly)`);\n    }\n    const cls = classesForProvider(workers, name);\n    lines.push(`    classes: ${cls.length ? cls.join(\", \") : \"(none)\"}`);\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  lines.push(`config: ${existsSync(workersYamlPath()) ? workersYamlPath() : configPath}`);\n  return lines;\n}\n\n/**\n * Set which provider(s), in order, serve a capability across the whole\n * config (`pai worker capability <name> <provider>[,<provider>…]`, MCP\n * worker_capability). resolveCapability walks this list at run time.\n */\nexport function setCapabilityPreference(\n  capability: string,\n  providers: string[],\n  configPath?: string\n): WorkersConfig {\n  if (!isModelCapability(capability)) {\n    throw new WorkersConfigError(`\"${capability}\" is not a valid capability name (must match ^[a-z][a-z0-9-]*$)`);\n  }\n  if (!providers.length) {\n    throw new WorkersConfigError(\"at least one provider name is required\");\n  }\n  const { raw, workers } = readWorkersSection(configPath);\n  for (const name of providers) {\n    if (name !== ANTHROPIC_NATIVE && !workers.providers[name]) {\n      throw new WorkersConfigError(\n        `no provider named \"${name}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n      );\n    }\n  }\n  workers.capabilities[capability] = providers;\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\nexport function unsetCapabilityPreference(capability: string, configPath?: string): WorkersConfig {\n  const { raw, workers } = readWorkersSection(configPath);\n  if (!(capability in workers.capabilities)) {\n    throw new WorkersConfigError(`no capability preference set for \"${capability}\"`);\n  }\n  delete workers.capabilities[capability];\n  writeWorkersSection(raw, workers, configPath);\n  return workers;\n}\n\n/**\n * `pai worker capability` with no args: every configured `capabilities:`\n * preference and what it resolves to right now — provider/model/engine, or\n * `fallback: <provider>/<model>` when nothing on the list actually declares\n * the capability (see resolveCapability's fellBack flag).\n */\nexport function describeCapabilities(workers: WorkersConfig): string[] {\n  const names = Object.keys(workers.capabilities);\n  if (!names.length) {\n    return [\"no capability preferences set — set one with `pai worker capability <name> <provider>[,<provider>…]`\"];\n  }\n  return names.map((cap) => {\n    const pref = workers.capabilities[cap];\n    try {\n      const r = resolveCapability(workers, cap);\n      const resolved = r.fellBack ? `fallback: ${r.provider}/${r.model}` : `${r.provider}/${r.model}  engine ${r.engine}`;\n      return `${cap}: [${pref.join(\", \")}] -> ${resolved}`;\n    } catch (e) {\n      return `${cap}: [${pref.join(\", \")}] -> unresolved: ${e instanceof Error ? e.message : String(e)}`;\n    }\n  });\n}\n","/**\n * fallback.ts — machine-wide Claude Code fallback to a worker provider.\n *\n * When the Anthropic plan runs out, `pai worker fallback on <provider>`\n * points EVERY new Claude Code process on this machine at that provider:\n * interactive sessions, task-bus sessions, the daemon's headless summarizer.\n * It writes the provider's base URL, token (read from its key file at switch\n * time — so the token then sits in settings.json until `off`), the three\n * DEFAULT_*_MODEL pins, its extra env (API_TIMEOUT_MS …) plus tool search on\n * and nonessential traffic off into the `env` block of ~/.claude/settings.json,\n * and pins the top-level `model` to the provider's default model.\n *\n * What settings.json carried before the switch is saved under\n * `workers.fallback.saved` in the PAI config file (CONFIG_FILE); `fallback off`\n * restores it exactly and removes the added keys. Both files are written\n * atomically; nothing else in them is touched. While fallback is on the\n * Agent hook keeps routing subagents to workers, unchanged — those runs set\n * their own env per provider and are unaffected.\n *\n * Running Claude Code processes keep the provider they started with until\n * restarted; `fallback status` lists them and points at the note `on` leaves\n * in the workers log dir (FALLBACK-ACTIVE.md).\n *\n * CLAUDE_SETTINGS_PATH overrides the settings.json location (dry runs against\n * a copy); the config path is injectable for tests the same way.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { readJsonStrict, writeJsonAtomic } from \"../config/json-store.js\";\nimport {\n  WorkersConfigError,\n  readWorkersSection,\n  resolveModelCapability,\n  resolveProviderKey,\n  writeWorkersSection,\n  type WorkerProvider,\n  type WorkersFallback,\n} from \"./config.js\";\nimport { resolveTarget } from \"./routing.js\";\nimport { workersLogDir } from \"./paths.js\";\n\n/** settings.json location; CLAUDE_SETTINGS_PATH points it at a copy for dry runs. */\nexport function fallbackSettingsPath(): string {\n  return process.env.CLAUDE_SETTINGS_PATH ?? join(homedir(), \".claude\", \"settings.json\");\n}\n\n/** The note `on` writes so a human landing in the log dir sees the switch. */\nexport function fallbackNotePath(logDir: string): string {\n  return join(logDir, \"FALLBACK-ACTIVE.md\");\n}\n\nexport interface FallbackPaths {\n  /** settings.json to switch (default: fallbackSettingsPath()). */\n  settingsPath?: string;\n  /** pai config.yaml (or config.json) holding the workers section (default: the real one). */\n  configPath?: string;\n}\n\nexport interface FallbackOnResult {\n  provider: string;\n  /** True when fallback was already on for this provider (env re-applied, saved state kept). */\n  alreadyOn: boolean;\n  /** Env keys written into settings.json. */\n  envKeys: string[];\n  /** Model the top-level pin was set to. */\n  model: string;\n}\n\nexport interface FallbackOffResult {\n  provider: string;\n  /** Env keys removed or restored in settings.json. */\n  envKeys: string[];\n}\n\n/**\n * The env block fallback writes: the provider's endpoint and token, model\n * pins (fast model as haiku, default as sonnet and opus — the same mapping\n * buildRunEnv uses), the provider's own env, and the two fallback constants.\n */\nexport function fallbackEnv(provider: WorkerProvider): Record<string, string> {\n  const token = resolveProviderKey(provider) ?? \"local\";\n  return {\n    ANTHROPIC_BASE_URL: provider.baseUrl,\n    ANTHROPIC_AUTH_TOKEN: token,\n    ANTHROPIC_DEFAULT_OPUS_MODEL: provider.models.default,\n    ANTHROPIC_DEFAULT_SONNET_MODEL: provider.models.default,\n    ANTHROPIC_DEFAULT_HAIKU_MODEL: resolveModelCapability(provider, \"fast\"),\n    ...provider.env,\n    ENABLE_TOOL_SEARCH: \"true\",\n    CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: \"1\",\n  };\n}\n\nfunction assertFallbackCapable(name: string, p: WorkerProvider): void {\n  if (p.protocol === \"openai\") {\n    throw new WorkersConfigError(\n      `provider \"${name}\" speaks the openai protocol and only works through the live PAI proxy — ` +\n        `fallback needs an anthropic-protocol provider every process can reach directly.`\n    );\n  }\n  if (p.engine === \"codex\") {\n    throw new WorkersConfigError(\n      `provider \"${name}\" runs through the Codex CLI — fallback switches Claude Code itself and needs a claude-engine provider.`\n    );\n  }\n  if (!p.baseUrl) {\n    throw new WorkersConfigError(`provider \"${name}\" has no baseUrl — fallback cannot point at it.`);\n  }\n}\n\n/** The env block of a parsed settings.json record, or null when absent. */\nfunction envBlockOf(settings: Record<string, unknown>): Record<string, unknown> | null {\n  return typeof settings.env === \"object\" && settings.env !== null\n    ? (settings.env as Record<string, unknown>)\n    : null;\n}\n\n/** Apply a saved state to a parsed settings.json record (inverse of the switch). */\nfunction restoreSettings(\n  settings: Record<string, unknown>,\n  saved: WorkersFallback[\"saved\"]\n): string[] {\n  const keys = Object.keys(saved.env);\n  if (saved.envExisted) {\n    const env = { ...(typeof settings.env === \"object\" && settings.env !== null ? (settings.env as Record<string, unknown>) : {}) };\n    for (const k of keys) {\n      const v = saved.env[k];\n      if (v === null) delete env[k];\n      else env[k] = v;\n    }\n    settings.env = env;\n  } else {\n    delete settings.env;\n  }\n  if (saved.model === null) delete settings.model;\n  else settings.model = saved.model;\n  return keys;\n}\n\nfunction writeFallbackNote(logDir: string, r: FallbackOnResult): string {\n  const path = fallbackNotePath(logDir);\n  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n  writeFileSync(\n    path,\n    [\n      `# Fallback active`,\n      ``,\n      `- switched: ${new Date().toISOString().slice(0, 19).replace(\"T\", \" \")} UTC`,\n      `- provider: ${r.provider} — every NEW Claude Code process on this machine runs on it`,\n      `- settings.json: env got ${r.envKeys.join(\", \")}; top-level model pinned to ${r.model}`,\n      `- turn off: pai worker fallback off (restores settings.json exactly)`,\n      ``,\n      `## Running sessions`,\n      ``,\n      `restart this session in its project directory; the new process uses ${r.provider}; keep the same AIBroker name`,\n      ``,\n    ].join(\"\\n\"),\n    \"utf8\"\n  );\n  return path;\n}\n\n/**\n * Switch every new Claude Code process to a worker provider.\n *\n * Write order: the saved state lands in the pai config BEFORE settings.json\n * is touched, so a crash between the two writes leaves `off` able to restore\n * (and `on` re-applies idempotently, healing the gap).\n */\nexport function fallbackOn(providerFlag: string | undefined, paths: FallbackPaths = {}): FallbackOnResult {\n  const settingsPath = paths.settingsPath ?? fallbackSettingsPath();\n  const { raw, workers } = readWorkersSection(paths.configPath);\n  const logDir = workersLogDir(workers);\n  const target = resolveTarget(workers, logDir, { flagProvider: providerFlag });\n  const { providerName, provider } = target;\n  assertFallbackCapable(providerName, provider);\n\n  const env = fallbackEnv(provider);\n  const envKeys = Object.keys(env);\n  const settings = readJsonStrict(settingsPath, settingsPath);\n\n  const prev = workers.fallback;\n  if (prev && prev.provider === providerName) {\n    // Already on for this provider: re-apply (heals a crash between the two\n    // writes) but keep the ORIGINAL saved state and stamp — that is what `off`\n    // must restore to.\n    const eb = envBlockOf(settings);\n    settings.env = { ...(eb ?? {}), ...env };\n    settings.model = provider.models.default;\n    writeJsonAtomic(settingsPath, settings, { label: settingsPath });\n    return { provider: providerName, alreadyOn: true, envKeys, model: provider.models.default };\n  }\n  if (prev) {\n    // Switching providers while on: restore what the first switch saved, then\n    // save fresh from the restored state.\n    restoreSettings(settings, prev.saved);\n  }\n  const envBlock = envBlockOf(settings);\n\n  const saved: WorkersFallback[\"saved\"] = {\n    env: {},\n    model: typeof settings.model === \"string\" ? settings.model : null,\n    envExisted: envBlock !== null,\n  };\n  for (const k of envKeys) {\n    saved.env[k] = envBlock && typeof envBlock[k] === \"string\" ? (envBlock[k] as string) : null;\n  }\n\n  workers.fallback = { provider: providerName, saved, on: new Date().toISOString() };\n  writeWorkersSection(raw, workers, paths.configPath);\n\n  settings.env = { ...(envBlock ?? {}), ...env };\n  settings.model = provider.models.default;\n  writeJsonAtomic(settingsPath, settings, { label: settingsPath });\n\n  const r: FallbackOnResult = { provider: providerName, alreadyOn: false, envKeys, model: provider.models.default };\n  writeFallbackNote(logDir, r);\n  return r;\n}\n\n/** Restore settings.json exactly and clear the switch. Errors when not on. */\nexport function fallbackOff(paths: FallbackPaths = {}): FallbackOffResult {\n  const settingsPath = paths.settingsPath ?? fallbackSettingsPath();\n  const { raw, workers } = readWorkersSection(paths.configPath);\n  const fb = workers.fallback;\n  if (!fb) throw new WorkersConfigError(\"fallback is not on — nothing to restore\");\n\n  const settings = readJsonStrict(settingsPath, settingsPath);\n  const envKeys = restoreSettings(settings, fb.saved);\n  writeJsonAtomic(settingsPath, settings, { label: settingsPath });\n\n  workers.fallback = null;\n  writeWorkersSection(raw, workers, paths.configPath);\n\n  try {\n    const note = fallbackNotePath(workersLogDir(workers));\n    if (existsSync(note)) unlinkSync(note);\n  } catch {\n    /* the note is advisory; its removal must never fail the restore */\n  }\n  return { provider: fb.provider, envKeys };\n}\n\nexport interface FallbackStatus {\n  on: boolean;\n  provider: string | null;\n  /** ISO stamp of the switch, when on. */\n  onAt: string | null;\n  /** FALLBACK-ACTIVE.md path, when on. */\n  notePath: string | null;\n  /** Where the running-session list came from. */\n  sessionsSource: \"aibroker\" | \"ps\";\n  /** One line per running Claude Code process/session. */\n  sessions: string[];\n}\n\n/** Running Claude Code processes: the AIBroker managers registry when it has\n * entries, else `ps`. Informational — sessions keep their provider until\n * restarted whatever this shows. */\nexport function listClaudeSessions(): { source: \"aibroker\" | \"ps\"; lines: string[] } {\n  // AIBroker registry (~/.aibroker/managers.json): manager name → session\n  // record. Best-effort — any parse problem falls through to ps.\n  try {\n    const regPath = join(homedir(), \".aibroker\", \"managers.json\");\n    if (existsSync(regPath)) {\n      const reg = JSON.parse(readFileSync(regPath, \"utf8\")) as Record<string, unknown>;\n      const names = Object.keys(reg);\n      if (names.length) {\n        const lines = names.map((name) => {\n          const rec = typeof reg[name] === \"object\" && reg[name] !== null ? (reg[name] as Record<string, unknown>) : {};\n          const bits = [name];\n          for (const field of [\"session\", \"cwd\", \"pid\"] as const) {\n            const v = rec[field];\n            if (typeof v === \"string\" || typeof v === \"number\") bits.push(`${field} ${v}`);\n          }\n          return bits.join(\"  \");\n        });\n        return { source: \"aibroker\", lines };\n      }\n    }\n  } catch {\n    /* fall through to ps */\n  }\n  try {\n    const out = execFileSync(\"/bin/ps\", [\"-eo\", \"pid=,etime=,command=\"], {\n      encoding: \"utf8\",\n      timeout: 10_000,\n    });\n    // pid, etime, then the command; keep only lines whose command is a\n    // `claude` executable (the `cli.js` shape also matches other daemons)\n    const lines = out\n      .split(\"\\n\")\n      .map((l) => l.trim())\n      .filter(Boolean)\n      .map((l) => {\n        const m = l.match(/^\\d+\\s+\\S+\\s+(.*)$/);\n        return m ? m[1] : \"\";\n      })\n      .filter((cmd) => {\n        const first = cmd.split(/\\s+/)[0] ?? \"\";\n        return first === \"claude\" || first.endsWith(\"/claude\");\n      })\n      .map((cmd) => (cmd.length > 100 ? cmd.slice(0, 97) + \"…\" : cmd))\n      .slice(0, 20);\n    return { source: \"ps\", lines };\n  } catch {\n    return { source: \"ps\", lines: [] };\n  }\n}\n\nexport function fallbackStatus(paths: FallbackPaths = {}): FallbackStatus {\n  const { workers } = readWorkersSection(paths.configPath);\n  const fb = workers.fallback;\n  const sessions = listClaudeSessions();\n  if (!fb) {\n    return {\n      on: false,\n      provider: null,\n      onAt: null,\n      notePath: null,\n      sessionsSource: sessions.source,\n      sessions: sessions.lines,\n    };\n  }\n  const notePath = fallbackNotePath(workersLogDir(workers));\n  return {\n    on: true,\n    provider: fb.provider,\n    onAt: fb.on,\n    notePath,\n    sessionsSource: sessions.source,\n    sessions: sessions.lines,\n  };\n}\n\n/** Human-readable status block for the CLI and the MCP tool. */\nexport function fallbackStatusText(s: FallbackStatus): string[] {\n  const lines: string[] = [];\n  if (!s.on) {\n    lines.push(\"fallback: off — new Claude Code processes run on the Anthropic login\");\n    lines.push(\"turn on with: pai worker fallback on [provider]\");\n  } else {\n    lines.push(`fallback: on — provider ${s.provider} (since ${s.onAt})`);\n    lines.push(\"every NEW Claude Code process runs on it; turn off with: pai worker fallback off\");\n    if (s.notePath) {\n      lines.push(`note: ${s.notePath}${existsSync(s.notePath) ? \"\" : \" (missing)\"}`);\n    }\n  }\n  lines.push(\"\");\n  lines.push(\n    s.sessions.length\n      ? `running Claude Code sessions (${s.sessionsSource}; they keep their current provider until restarted):`\n      : `no running Claude Code sessions detected (${s.sessionsSource})`\n  );\n  lines.push(...(s.sessions.length ? s.sessions.map((l) => `  ${l}`) : []));\n  return lines;\n}\n"],"mappings":"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"}