@tekmidian/pai 0.65.2 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chain-CT3SI-3z.mjs → chain-D4PuQ2c-.mjs} +4 -4
- package/dist/{chain-CT3SI-3z.mjs.map → chain-D4PuQ2c-.mjs.map} +1 -1
- package/dist/cli/index.mjs +9 -9
- package/dist/cli/program.mjs +9 -9
- package/dist/{context-handover-cache-CRSFdkNi.mjs → context-handover-cache-pkHzmL3c.mjs} +3 -3
- package/dist/{context-handover-cache-CRSFdkNi.mjs.map → context-handover-cache-pkHzmL3c.mjs.map} +1 -1
- package/dist/daemon/index.mjs +7 -7
- package/dist/{daemon-SaPnmoyg.mjs → daemon-BEoVgVfJ.mjs} +9 -9
- package/dist/{daemon-SaPnmoyg.mjs.map → daemon-BEoVgVfJ.mjs.map} +1 -1
- package/dist/{daemon-DoTVK9z6.mjs → daemon-DPAYU9hp.mjs} +7 -7
- package/dist/daemon-mcp/index.mjs +6 -6
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{fallback-vHjFZP0Y.mjs → fallback-B5svk75f.mjs} +15 -7
- package/dist/fallback-B5svk75f.mjs.map +1 -0
- package/dist/hooks/block-sleep-poll.mjs.map +2 -2
- package/dist/hooks/load-project-context.mjs +16 -0
- package/dist/hooks/load-project-context.mjs.map +2 -2
- package/dist/hooks/route-agents-to-worker.mjs +16 -0
- package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
- package/dist/hooks/security-validator.mjs +16 -0
- package/dist/hooks/security-validator.mjs.map +2 -2
- package/dist/hooks/worker-guard.mjs +10 -0
- package/dist/hooks/worker-guard.mjs.map +3 -3
- package/dist/hooks/worker-proxy.mjs +16 -0
- package/dist/hooks/worker-proxy.mjs.map +2 -2
- package/dist/hooks/worker-status-line.mjs +16 -0
- package/dist/hooks/worker-status-line.mjs.map +2 -2
- package/dist/hooks/worker-supervision.mjs +16 -0
- package/dist/hooks/worker-supervision.mjs.map +2 -2
- package/dist/{main-resolver-VSM5ytnA.mjs → main-resolver-BKz_OuEh.mjs} +2 -2
- package/dist/{main-resolver-CKNGux1B.mjs → main-resolver-DPPwHNtn.mjs} +2 -2
- package/dist/{main-resolver-CKNGux1B.mjs.map → main-resolver-DPPwHNtn.mjs.map} +1 -1
- package/dist/{planner-Dzs9-8AS.mjs → planner-EJPZnnif.mjs} +5 -5
- package/dist/{planner-Dzs9-8AS.mjs.map → planner-EJPZnnif.mjs.map} +1 -1
- package/dist/{program-DK0mRRlf.mjs → program-DSETzrWg.mjs} +56 -16
- package/dist/{program-DK0mRRlf.mjs.map → program-DSETzrWg.mjs.map} +1 -1
- package/dist/{run-DMaCpvMW.mjs → run-Z4akoa8G.mjs} +325 -23
- package/dist/run-Z4akoa8G.mjs.map +1 -0
- package/dist/{run-env-BHWnXqle.mjs → run-env-DRcp7A8K.mjs} +16 -2
- package/dist/run-env-DRcp7A8K.mjs.map +1 -0
- package/dist/{server-DlL1QI2k.mjs → server-DgmAHyFK.mjs} +2 -2
- package/dist/{server-DlL1QI2k.mjs.map → server-DgmAHyFK.mjs.map} +1 -1
- package/dist/{session-keepalive-DVZ9-7qg.mjs → session-keepalive-BDhytXEo.mjs} +4 -4
- package/dist/{session-keepalive-DVZ9-7qg.mjs.map → session-keepalive-BDhytXEo.mjs.map} +1 -1
- package/dist/{work-queue-worker-DZiEMvBQ.mjs → work-queue-worker-4tLa5gpQ.mjs} +2 -2
- package/dist/{work-queue-worker-DZiEMvBQ.mjs.map → work-queue-worker-4tLa5gpQ.mjs.map} +1 -1
- package/dist/{work-queue-worker-D7GHuj2I.mjs → work-queue-worker-rFZvTO3p.mjs} +4 -4
- package/docs/commands/README.md +1 -0
- package/docs/commands/worker.md +30 -0
- package/package.json +1 -1
- package/src/hooks/ts/lib/worker-guard.ts +4 -0
- package/dist/fallback-vHjFZP0Y.mjs.map +0 -1
- package/dist/run-DMaCpvMW.mjs.map +0 -1
- package/dist/run-env-BHWnXqle.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { _ as writeYamlFileAtomic, d as readMainConfigRaw, f as writeMainConfigRaw, l as paiConfigFilePath, m as migrateMainConfigToYaml, n as CONFIG_FILE, r as DEFAULTS, s as loadConfig, u as paiConfigYamlFilePath, v as readJsonStrict, y as writeJsonAtomic } from "./config-BbLFD7Uf.mjs";
|
|
2
|
-
import { A as
|
|
3
|
-
import { B as
|
|
4
|
-
import { a as clearCooldown, c as probeQuota, d as resolveTarget, h as ledgerPath, l as quotaSkipThreshold, m as eventsPath, v as workersLogDir } from "./server-
|
|
2
|
+
import { A as resolveProviderKey, C as maskKey, D as readWorkersSection, O as resolveCapability, S as keysDir, T as providerCostTier, a as DEFAULT_CACHE_KEEPALIVE_SECS, b as getProviderOrNative, c as DEFAULT_PANE, d as MODEL_CAPABILITIES, h as WorkersConfigError, j as writeWorkersSection, k as resolveModelCapability, l as DEFAULT_ROUTING, o as DEFAULT_LOG_DIR, p as PROVIDER_TAGS, r as ANTHROPIC_NATIVE, s as DEFAULT_MAX_MINUTES, u as DEFAULT_TREE, w as nativeAnthropicProvider, x as isModelCapability, y as expandHome, z as workersYamlPath } from "./run-env-DRcp7A8K.mjs";
|
|
3
|
+
import { B as isLive, C as workerInScope, F as alive, H as loadStatuses, I as contextLabel, L as contextPercent, N as UNLABELED, P as ageOf, R as elapsedOf, S as sessionTag, Z as shortText, _ as currentTabKey, a as deadlineLabel, b as resolveSession, d as workerDepth, g as markControlsHeld, h as clickrControlsArgv, j as appendLedger, l as readInbox, o as parseWorkerReport, s as renderReport, u as sayToWorker, z as isChatPane } from "./run-Z4akoa8G.mjs";
|
|
4
|
+
import { a as clearCooldown, c as probeQuota, d as resolveTarget, h as ledgerPath, l as quotaSkipThreshold, m as eventsPath, v as workersLogDir } from "./server-DgmAHyFK.mjs";
|
|
5
5
|
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { basename, join, relative, resolve } from "node:path";
|
|
@@ -104,6 +104,8 @@ function workersMainConfigShape() {
|
|
|
104
104
|
routing: workers.routing,
|
|
105
105
|
tree: workers.tree,
|
|
106
106
|
cacheKeepaliveSecs: workers.cacheKeepaliveSecs,
|
|
107
|
+
defaultMaxMinutes: workers.defaultMaxMinutes,
|
|
108
|
+
noBrowserByDefault: workers.noBrowserByDefault,
|
|
107
109
|
...workers.fallback ? { fallback: workers.fallback } : {}
|
|
108
110
|
};
|
|
109
111
|
}
|
|
@@ -116,7 +118,9 @@ function schemaRoot() {
|
|
|
116
118
|
logDir: DEFAULT_LOG_DIR,
|
|
117
119
|
routing: DEFAULT_ROUTING,
|
|
118
120
|
tree: DEFAULT_TREE,
|
|
119
|
-
cacheKeepaliveSecs: DEFAULT_CACHE_KEEPALIVE_SECS
|
|
121
|
+
cacheKeepaliveSecs: DEFAULT_CACHE_KEEPALIVE_SECS,
|
|
122
|
+
defaultMaxMinutes: DEFAULT_MAX_MINUTES,
|
|
123
|
+
noBrowserByDefault: false
|
|
120
124
|
}
|
|
121
125
|
};
|
|
122
126
|
}
|
|
@@ -1069,6 +1073,10 @@ function renderTable(c, statuses, scopeLabel, now = /* @__PURE__ */ new Date(),
|
|
|
1069
1073
|
const branchMark = (s) => s.branch && !s.merged ? " " + c("yellow", "⎇" + (s.commits ? String(s.commits) : "")) : "";
|
|
1070
1074
|
const inboxMark = (s) => inbox[s.id] ? " " + c("mag", `◆${inbox[s.id]}`) : "";
|
|
1071
1075
|
const reportMark = (s) => s.reportValid === false ? " " + c("red", s.reportFormat === "ag2" && (s.reportErrors ?? []).length ? "R!" : "R?") : "";
|
|
1076
|
+
const timeMark = (s) => {
|
|
1077
|
+
const l = deadlineLabel(s, now.getTime());
|
|
1078
|
+
return l ? " " + c(l === "timed out" || l.startsWith("OVERDUE") ? "red" : "dim", l) : "";
|
|
1079
|
+
};
|
|
1072
1080
|
const lines = [c("bold", `Workers ${`${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}`}`), ""];
|
|
1073
1081
|
lines.push(c("bold", `RUNNING (${running.length})`));
|
|
1074
1082
|
if (!running.length) lines.push(" none");
|
|
@@ -1088,7 +1096,7 @@ function renderTable(c, statuses, scopeLabel, now = /* @__PURE__ */ new Date(),
|
|
|
1088
1096
|
const meter = contextMeter(c, s, 60);
|
|
1089
1097
|
const p = chain ? " " : rowPrefix(depth, subLast);
|
|
1090
1098
|
const q = chain ? " " : rowCont(depth, subLast);
|
|
1091
|
-
lines.push(treeLine(`${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 : ""}`, chain, last));
|
|
1099
|
+
lines.push(treeLine(`${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)}${timeMark(s)}${meter ? " " + meter : ""}`, chain, last));
|
|
1092
1100
|
lines.push(treeLine(`${q}task: ${s.label}`, chain, last));
|
|
1093
1101
|
lines.push(treeLine(`${q}now: ${c("yellow", s.last)} (${ageOf(s.updated, now)} ago)`, chain, last));
|
|
1094
1102
|
}
|
|
@@ -1110,7 +1118,7 @@ function renderTable(c, statuses, scopeLabel, now = /* @__PURE__ */ new Date(),
|
|
|
1110
1118
|
const subLast = !chain && (i + 1 >= doneSlice.length || doneSlice[i + 1].parent !== s.parent);
|
|
1111
1119
|
const col = s.state === "done" ? "green" : "red";
|
|
1112
1120
|
const tag = sessionTag(s);
|
|
1113
|
-
lines.push(treeLine(`${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}`, chain, last));
|
|
1121
|
+
lines.push(treeLine(`${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)}${timeMark(s)}${reportMark(s)} ${s.label}`, chain, last));
|
|
1114
1122
|
}
|
|
1115
1123
|
lines.push("");
|
|
1116
1124
|
lines.push(c("dim", "worker follow live transcript of running workers"));
|
|
@@ -2205,4 +2213,4 @@ function fallbackStatusText(s) {
|
|
|
2205
2213
|
|
|
2206
2214
|
//#endregion
|
|
2207
2215
|
export { getConfigValueOp as A, setWorkersEnabled as C, useProvider as D, updateProvider as E, setConfigValueOp as M, unsetConfigValueOp as N, MainConfigOpsError as O, setProviderModel as S, unsetClass as T, removeProvider as _, followWorkers as a, setClass as b, statusLineOutput as c, addProvider as d, classTargetText as f, modelPrefsText as g, describeProviders as h, fallbackStatusText as i, listConfigOp as j, formatConfigGetOutput as k, readSpecPrompt as l, describeModels as m, fallbackOn as n, psOutput as o, describeCapabilities as p, fallbackStatus as r, replayOutput as s, fallbackOff as t, resolveSpecPath as u, resolveProviderName as v, unsetCapabilityPreference as w, setProviderEnabled as x, setCapabilityPreference as y };
|
|
2208
|
-
//# sourceMappingURL=fallback-
|
|
2216
|
+
//# sourceMappingURL=fallback-B5svk75f.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback-B5svk75f.mjs","names":[],"sources":["../src/config/main-config-ops.ts","../src/workers/providers.ts","../src/workers/specfile.ts","../src/workers/chatui.ts","../src/workers/render.ts","../src/workers/viewer.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 DEFAULT_MAX_MINUTES,\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 defaultMaxMinutes: workers.defaultMaxMinutes,\n noBrowserByDefault: workers.noBrowserByDefault,\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 defaultMaxMinutes: DEFAULT_MAX_MINUTES,\n noBrowserByDefault: false,\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 * 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 getProviderOrNative,\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\n/**\n * Point a class target at `name`, keeping every other field. Only touches\n * targets that already pin a provider — a constraint-only object (no\n * `provider`) only narrows auto-routing and is left alone. A string target's\n * `/alias` is dropped (falling back to `name`'s default model) when `name`\n * has no such model configured — the class must move to `name` either way,\n * since leaving it on the old provider is exactly the bug this fixes.\n */\nfunction repointClassTarget(\n target: ClassTarget,\n name: string,\n providers: WorkersConfig[\"providers\"]\n): ClassTarget | null {\n if (typeof target === \"string\") {\n const [provider, alias] = target.split(\"/\");\n if (provider === name) return null;\n if (alias && name !== ANTHROPIC_NATIVE && alias !== \"default\" && !providers[name]?.models[alias]) {\n return name;\n }\n return alias ? `${name}/${alias}` : name;\n }\n if (!target.provider || target.provider === name) return null;\n return { ...target, provider: name };\n}\n\n/**\n * Make `name` the active provider. Unless `name` is \"auto\", also re-points\n * every class whose target pins a provider (string or `{provider: …}`) at\n * it — a class pinned to a provider otherwise wins over `active` at run time\n * (routing.ts resolveTarget), so leaving classes untouched would make `use`\n * silently do nothing for any class already pinned elsewhere.\n */\nexport function useProvider(\n name: string,\n configPath?: string\n): { workers: WorkersConfig; repointed: number } {\n const { raw, workers } = readWorkersSection(configPath);\n if (name !== ANTHROPIC_NATIVE && name !== \"auto\" && !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 let repointed = 0;\n if (name !== \"auto\") {\n for (const [cls, target] of Object.entries(workers.classes)) {\n const next = repointClassTarget(target, name, workers.providers);\n if (next !== null) {\n workers.classes[cls] = next;\n repointed++;\n }\n }\n }\n writeWorkersSection(raw, workers, configPath);\n return { workers, repointed };\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 (target === ANTHROPIC_NATIVE || 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: ${[ANTHROPIC_NATIVE, ...Object.keys(workers.providers)].join(\", \")}`\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 = getProviderOrNative(workers, name);\n if (!p) {\n throw new WorkersConfigError(\n `no provider named \"${name}\". Configured: ${[ANTHROPIC_NATIVE, ...Object.keys(workers.providers)].join(\", \")}`\n );\n }\n // the built-in provider is synthesized on read; its table is workers.nativeModels\n (name === ANTHROPIC_NATIVE ? workers.nativeModels : 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 lines = [`active provider: ${workers.active ?? \"(none)\"}`];\n for (const name of [...Object.keys(workers.providers), ANTHROPIC_NATIVE]) {\n const p = getProviderOrNative(workers, 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 const nativeDefault = !workers.active;\n lines.push(\n `${ANTHROPIC_NATIVE} [built-in${nativeActive ? \", active\" : \"\"}]${nativeDefault ? \" (active: default)\" : \"\"} 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 * 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 \"your controls exclusive clickr control to this worker (my controls takes it back)\",\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 | { kind: \"controls\"; who: \"you\" | \"me\" };\n\nconst CONTROLS_LINE = /^(your|my) controls\\.?$/i;\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); \"your controls\" /\n * \"my controls\" is the same `pai worker controls <id> you|me` action, never\n * forwarded to the worker as a normal chat line; anything else, including\n * 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 const controls = text.match(CONTROLS_LINE);\n if (controls) return { kind: \"controls\", who: controls[1]!.toLowerCase() === \"your\" ? \"you\" : \"me\" };\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 { deadlineLabel } from \"./deadline.js\";\nimport { relative, basename } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { ageOf, elapsedOf, 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 // R! an AG2 report that failed `aibroker agentish check`; R? either the\n // validator couldn't confirm it or a claimed changed path failed\n // verification against the filesystem (see reportVerify in run.ts)\n const reportBit =\n s.reportValid === false\n ? \" \" + c(\"red\", s.reportFormat === \"ag2\" && (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!\" an AG2 report that failed `aibroker agentish check`, \"R?\" either\n // the validator couldn't confirm it (missing/unreadable CLI) or a claimed\n // changed path failed verification against the filesystem — shown for\n // json reports too in the verify-failure case\n const reportMark = (s: WorkerStatus): string =>\n s.reportValid === false\n ? \" \" + c(\"red\", s.reportFormat === \"ag2\" && (s.reportErrors ?? []).length ? \"R!\" : \"R?\")\n : \"\";\n\n const timeMark = (s: WorkerStatus): string => {\n const l = deadlineLabel(s, now.getTime());\n return l ? \" \" + c(l === \"timed out\" || l.startsWith(\"OVERDUE\") ? \"red\" : \"dim\", l) : \"\";\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)}${timeMark(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)}${timeMark(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 elapsed = elapsedOf(s.started, now);\n const tail = [model, started ? `started ${started}` : \"\", elapsed].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, ledgerPath } 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 { clickrControlsArgv, markControlsHeld } from \"./controls.js\";\nimport { appendLedger } from \"./ledger.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/** The child of a clickr controls spawn (tests inject a fake). */\nexport interface ControlsChild {\n stdout?: { on(event: \"data\", cb: (chunk: Buffer) => void): unknown };\n stderr?: { 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 /** replaces the clickr controls spawn (tests record instead of run). */\n spawnControls?: (id: string, who: \"you\" | \"me\", pid: number | undefined) => ControlsChild;\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 // A write to a pane that just closed (EPIPE/ENXIO) or is mid-resize throws\n // synchronously; unguarded, that crashes the whole follow process, which\n // the pai-worker profile's Close Sessions On End then closes with nothing\n // left on screen to explain why. Every direct write to `out_` goes through\n // here instead, so the pane can only ever go quiet, never vanish for it.\n const safeWrite = (s: string): void => {\n try {\n out_.write(s);\n } catch {\n /* unwritable pane — the tick loop's PANE-CLOSED ledger line is the record */\n }\n };\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 // why this pane's follow last exited — recorded in the ledger's PANE-CLOSED\n // line on every exit (a crash closes the pane instantly under the\n // Close-Sessions-On-End profile, so the ledger is often the only surviving\n // record of why)\n let exitReason = \"aborted\";\n let consecutiveErrors = 0;\n const MAX_CONSECUTIVE_ERRORS = 10;\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 safeWrite(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 safeWrite(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) safeWrite(\"\\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 safeWrite(chatTickerRow(text, rows, promptCursorCol()));\n } else {\n const meter = meterStatus ? contextMeter(c, meterStatus) : null;\n safeWrite(\"\\r\\x1b[K\");\n safeWrite(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 // same argv `pai worker controls` builds — the pane just spawns clickr\n // itself instead of shelling out to the CLI, so it can capture the result\n // for the pane's own echo rather than inheriting stdio into the transcript\n const spawnControls =\n io?.spawnControls ??\n ((id: string, who: \"you\" | \"me\", pid: number | undefined): ControlsChild =>\n spawn(\"clickr\", clickrControlsArgv(id, who, pid), {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n } as SpawnOptions) as unknown as ControlsChild);\n const controlsTarget = (id: string, who: \"you\" | \"me\") => {\n const pid = loadStatuses(logDir).find((s) => s.id === id)?.pid;\n const child = spawnControls(id, who, pid);\n let errText = \"\";\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n errText += chunk.toString(\"utf8\");\n });\n child.on(\"close\", (rc: number | null) => {\n if (rc === 0) {\n if (who === \"you\") markControlsHeld(logDir, id);\n noteLine(\n c(\"dim\", `» controls → ${who === \"you\" ? `${id} (exclusive; needs clickr with --agent support)` : \"operator\"}`)\n );\n } else {\n noteLine(c(\"red\", `» controls failed (rc=${rc})${errText.trim() ? `: ${errText.trim()}` : \"\"}`));\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 safeWrite(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 \"controls\":\n if (target !== null) controlsTarget(target, act.who);\n else out(c(\"dim\", \"no target worker for controls\"));\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 safeWrite(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 safeWrite(\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 let outcome: TickOutcome;\n try {\n outcome = await runOneTick();\n consecutiveErrors = 0;\n } catch (e) {\n consecutiveErrors++;\n const msg = e instanceof Error ? e.message : String(e);\n // best-effort: the pane itself may be exactly what is unwritable\n try {\n noteLine(c(\"red\", `» follow error: ${msg}`));\n } catch {\n /* pane unwritable — the ledger line in finally is the record */\n }\n process.stderr.write(`pai worker follow: ${msg}\\n`);\n if (consecutiveErrors < MAX_CONSECUTIVE_ERRORS) {\n await sleep(200);\n continue;\n }\n exitReason = `error: ${msg}`.slice(0, 200);\n // a crash would otherwise close this pane the instant the process\n // exits (Close Sessions On End) — hold it open so the error above is\n // readable, not just a pane that vanished for no visible reason\n if (chat && rlIn) {\n try {\n noteLine(c(\"dim\", \"» follow crashed — press enter to close this pane\"));\n } catch {\n /* already unwritable */\n }\n await Promise.race([\n new Promise<void>((resolve) => rlIn!.once(\"line\", () => resolve())),\n sleep(60_000),\n ]);\n }\n return;\n }\n if (outcome.done) {\n exitReason = outcome.reason;\n return;\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) safeWrite(chatLeave(rows));\n for (const h of handles.values()) {\n try {\n closeSync(h.fd);\n } catch {\n /* already closed */\n }\n }\n if (target !== null) {\n appendLedger(ledgerPath(logDir), \"PANE-CLOSED\", {\n id: target,\n reason: exitReason,\n secs: Math.floor((Date.now() - started) / 1000),\n });\n }\n }\n\n /** What one poll decided: keep following, or the reason it is over. */\n type TickOutcome = { done: true; reason: string } | { done: false };\n\n /**\n * One poll of the loop: attach new handles, drain them, check exit\n * conditions. Returning (rather than throwing) `done: true` is how a\n * normal end is told apart from the error path above — everything in here\n * that can throw (rendering, terminal writes) is still covered by that\n * path, so one bad event or write never takes the whole pane down with it.\n */\n async function runOneTick(): Promise<TickOutcome> {\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 return { done: false };\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())) return { done: false };\n eraseLiveness();\n out(c(\"dim\", \"closing\"));\n return { done: true, reason: \"worker-ended\" };\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 { done: true, reason: \"worker-ended\" };\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 { done: true, reason: \"idle\" };\n }\n }\n if (!progressed) {\n writeLiveness();\n await sleep(500);\n }\n return { done: false };\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 * 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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"}
|