@tekmidian/pai 0.39.0 → 0.40.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.
Files changed (126) hide show
  1. package/README.md +41 -0
  2. package/dist/{auto-route-BlWOWT4z.mjs → auto-route-BwzmlW3W.mjs} +2 -2
  3. package/dist/{auto-route-BlWOWT4z.mjs.map → auto-route-BwzmlW3W.mjs.map} +1 -1
  4. package/dist/{chain-CTtHligI.mjs → chain-DhVHVnmT.mjs} +263 -791
  5. package/dist/chain-DhVHVnmT.mjs.map +1 -0
  6. package/dist/cli/index.mjs +10 -8
  7. package/dist/cli/index.mjs.map +1 -1
  8. package/dist/cli/program.mjs +10 -8
  9. package/dist/{clusters-wlK0w41E.mjs → clusters-DRHhfli9.mjs} +1 -1
  10. package/dist/{clusters-wlK0w41E.mjs.map → clusters-DRHhfli9.mjs.map} +1 -1
  11. package/dist/{config-B64vFg14.mjs → config-D1G9IFpn.mjs} +50 -3
  12. package/dist/config-D1G9IFpn.mjs.map +1 -0
  13. package/dist/{config-C_ErGddD.mjs → config-DD52BPB-.mjs} +1 -1
  14. package/dist/config-Dl8lT4Lu.mjs +527 -0
  15. package/dist/config-Dl8lT4Lu.mjs.map +1 -0
  16. package/dist/{context-handover-cache-BpUojjsi.mjs → context-handover-cache-C6_rs7JT.mjs} +112 -24
  17. package/dist/context-handover-cache-C6_rs7JT.mjs.map +1 -0
  18. package/dist/daemon/index.mjs +12 -10
  19. package/dist/daemon/index.mjs.map +1 -1
  20. package/dist/daemon-BSL9sXxm.mjs +22 -0
  21. package/dist/{daemon-CTkN_Vn8.mjs → daemon-CpdVUnl_.mjs} +26 -26
  22. package/dist/{daemon-CTkN_Vn8.mjs.map → daemon-CpdVUnl_.mjs.map} +1 -1
  23. package/dist/daemon-mcp/index.mjs +127 -6
  24. package/dist/daemon-mcp/index.mjs.map +1 -1
  25. package/dist/detector-BLVOCiJ_.mjs +5 -0
  26. package/dist/{detector-Bwk_4Pk4.mjs → detector-smpCm7ku.mjs} +1 -1
  27. package/dist/{detector-Bwk_4Pk4.mjs.map → detector-smpCm7ku.mjs.map} +1 -1
  28. package/dist/{factory-A9x-T2Kg.mjs → factory-Bc5EyN54.mjs} +7 -7
  29. package/dist/{factory-A9x-T2Kg.mjs.map → factory-Bc5EyN54.mjs.map} +1 -1
  30. package/dist/factory-C7ua_3ik.mjs +3 -0
  31. package/dist/{providers-FYkZjn_C.mjs → fallback-CdXv-Np0.mjs} +411 -51
  32. package/dist/fallback-CdXv-Np0.mjs.map +1 -0
  33. package/dist/hooks/context-compression-hook.mjs +136 -22
  34. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  35. package/dist/hooks/post-compact-inject.mjs +98 -7
  36. package/dist/hooks/post-compact-inject.mjs.map +4 -4
  37. package/dist/hooks/route-agents-to-worker.mjs +75 -6
  38. package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
  39. package/dist/hooks/worker-proxy.mjs +75 -6
  40. package/dist/hooks/worker-proxy.mjs.map +2 -2
  41. package/dist/hooks/worker-status-line.mjs +149 -90
  42. package/dist/hooks/worker-status-line.mjs.map +4 -4
  43. package/dist/{indexer-backend-TG64CCQC.mjs → indexer-backend-rohVVKrA.mjs} +1 -1
  44. package/dist/{indexer-backend-TG64CCQC.mjs.map → indexer-backend-rohVVKrA.mjs.map} +1 -1
  45. package/dist/{ipc-client-BmypMNYk.mjs → ipc-client-C7AVpsyv.mjs} +2 -2
  46. package/dist/{ipc-client-BmypMNYk.mjs.map → ipc-client-C7AVpsyv.mjs.map} +1 -1
  47. package/dist/{kg-entity-r8duqhi9.mjs → kg-entity-DbOMPdF9.mjs} +1 -1
  48. package/dist/{kg-entity-r8duqhi9.mjs.map → kg-entity-DbOMPdF9.mjs.map} +1 -1
  49. package/dist/{latent-ideas-B7wq75Pt.mjs → latent-ideas-iNp5s55R.mjs} +2 -2
  50. package/dist/{latent-ideas-B7wq75Pt.mjs.map → latent-ideas-iNp5s55R.mjs.map} +1 -1
  51. package/dist/{link-boost-HkG7JWZR.mjs → link-boost-NNzADHX_.mjs} +1 -1
  52. package/dist/{link-boost-HkG7JWZR.mjs.map → link-boost-NNzADHX_.mjs.map} +1 -1
  53. package/dist/{main-resolver-DlaLOFBA.mjs → main-resolver-CM1IHbuu.mjs} +78 -75
  54. package/dist/main-resolver-CM1IHbuu.mjs.map +1 -0
  55. package/dist/main-resolver-E5WlilhM.mjs +6 -0
  56. package/dist/{neighborhood-lThN-MaQ.mjs → neighborhood-AGL7oXQ2.mjs} +1 -1
  57. package/dist/{neighborhood-lThN-MaQ.mjs.map → neighborhood-AGL7oXQ2.mjs.map} +1 -1
  58. package/dist/{note-context-b6k0mAKi.mjs → note-context-BHwYpSFf.mjs} +1 -1
  59. package/dist/{note-context-b6k0mAKi.mjs.map → note-context-BHwYpSFf.mjs.map} +1 -1
  60. package/dist/{planner-BDI7bE9B.mjs → planner-Cm3g6fWH.mjs} +5 -3
  61. package/dist/{planner-BDI7bE9B.mjs.map → planner-Cm3g6fWH.mjs.map} +1 -1
  62. package/dist/{postgres-mW1n7Vi1.mjs → postgres-Dwxe290H.mjs} +1 -1
  63. package/dist/{postgres-mW1n7Vi1.mjs.map → postgres-Dwxe290H.mjs.map} +1 -1
  64. package/dist/{program-DXBwJV7h.mjs → program-AR0qlYRg.mjs} +334 -78
  65. package/dist/program-AR0qlYRg.mjs.map +1 -0
  66. package/dist/query-feedback-CGcEiGtY.mjs +3 -0
  67. package/dist/{query-feedback-BV4CcxqS.mjs → query-feedback-DZfjI34w.mjs} +1 -1
  68. package/dist/{query-feedback-BV4CcxqS.mjs.map → query-feedback-DZfjI34w.mjs.map} +1 -1
  69. package/dist/router-CAtyVQud.mjs +3 -0
  70. package/dist/{router-DcHKnEPa.mjs → router-DHracTUC.mjs} +1 -1
  71. package/dist/{router-DcHKnEPa.mjs.map → router-DHracTUC.mjs.map} +1 -1
  72. package/dist/server-BOuAOj9b.mjs +386 -0
  73. package/dist/server-BOuAOj9b.mjs.map +1 -0
  74. package/dist/skills/Worker/SKILL.md +19 -1
  75. package/dist/{sources-kLnQsNrW.mjs → sources-ju6AA7lL.mjs} +1 -1
  76. package/dist/{sources-kLnQsNrW.mjs.map → sources-ju6AA7lL.mjs.map} +1 -1
  77. package/dist/{sqlite-BenGr3UP.mjs → sqlite-kFZeWCJ2.mjs} +1 -1
  78. package/dist/{sqlite-BenGr3UP.mjs.map → sqlite-kFZeWCJ2.mjs.map} +1 -1
  79. package/dist/{state-Ca9F_sZc.mjs → state-DH_mp5Xe.mjs} +1 -1
  80. package/dist/{state-CAeyOdfq.mjs → state-ahFAbD8b.mjs} +1 -1
  81. package/dist/{state-CAeyOdfq.mjs.map → state-ahFAbD8b.mjs.map} +1 -1
  82. package/dist/{themes-BI4GMRP9.mjs → themes-DqhNUUQk.mjs} +1 -1
  83. package/dist/{themes-BI4GMRP9.mjs.map → themes-DqhNUUQk.mjs.map} +1 -1
  84. package/dist/{tools-DGcB3o_S.mjs → tools-CMlHa9lZ.mjs} +14 -14
  85. package/dist/{tools-DGcB3o_S.mjs.map → tools-CMlHa9lZ.mjs.map} +1 -1
  86. package/dist/{tools-Bp7hj6OW.mjs → tools-COna9vZR.mjs} +1 -1
  87. package/dist/{trace-bobARFEX.mjs → trace-DTP6k4B_.mjs} +1 -1
  88. package/dist/{trace-bobARFEX.mjs.map → trace-DTP6k4B_.mjs.map} +1 -1
  89. package/dist/{vault-indexer-C3OfXTNF.mjs → vault-indexer-YMH22mLU.mjs} +1 -1
  90. package/dist/{vault-indexer-C3OfXTNF.mjs.map → vault-indexer-YMH22mLU.mjs.map} +1 -1
  91. package/dist/{work-queue-worker-R7UGIag_.mjs → work-queue-worker-B3nl8yyn.mjs} +23 -26
  92. package/dist/work-queue-worker-B3nl8yyn.mjs.map +1 -0
  93. package/dist/work-queue-worker-BM1GcAAq.mjs +14 -0
  94. package/dist/{zettelkasten-m5QPtb-L.mjs → zettelkasten-DDvKRm2V.mjs} +3 -3
  95. package/dist/{zettelkasten-m5QPtb-L.mjs.map → zettelkasten-DDvKRm2V.mjs.map} +1 -1
  96. package/docs/commands/README.md +5 -2
  97. package/docs/commands/worker.md +66 -5
  98. package/docs/images/provider-abstraction-live.png +0 -0
  99. package/docs/images/workers.png +0 -0
  100. package/docs/launcher-independence.md +70 -0
  101. package/docs/provider-abstraction.md +87 -0
  102. package/docs/provider-independence-details.md +330 -0
  103. package/docs/provider-independence.md +75 -0
  104. package/docs/worker.md +72 -11
  105. package/package.json +1 -1
  106. package/scripts/build-hooks.mjs +6 -1
  107. package/scripts/build-skill-stubs.mjs +17 -4
  108. package/src/hooks/ts/lib/context-fill.test.ts +158 -4
  109. package/src/hooks/ts/lib/context-fill.ts +102 -35
  110. package/statusline-command.sh +120 -21
  111. package/dist/chain-CTtHligI.mjs.map +0 -1
  112. package/dist/config-B64vFg14.mjs.map +0 -1
  113. package/dist/context-handover-cache-BpUojjsi.mjs.map +0 -1
  114. package/dist/daemon-CmHHmKpg.mjs +0 -20
  115. package/dist/detector-DO730Zq0.mjs +0 -5
  116. package/dist/factory-BD-np0Vd.mjs +0 -3
  117. package/dist/main-resolver-D6IImXvF.mjs +0 -4
  118. package/dist/main-resolver-DlaLOFBA.mjs.map +0 -1
  119. package/dist/program-DXBwJV7h.mjs.map +0 -1
  120. package/dist/providers-FYkZjn_C.mjs.map +0 -1
  121. package/dist/query-feedback-DSVyHtrG.mjs +0 -3
  122. package/dist/router-Bk77E7hj.mjs +0 -3
  123. package/dist/runtime-paths-rni52zHX.mjs +0 -50
  124. package/dist/runtime-paths-rni52zHX.mjs.map +0 -1
  125. package/dist/work-queue-worker-DW8lz-Oo.mjs +0 -11
  126. package/dist/work-queue-worker-R7UGIag_.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"providers-FYkZjn_C.mjs","names":[],"sources":["../src/workers/chatui.ts","../src/workers/render.ts","../src/workers/viewer.ts","../src/workers/providers.ts"],"sourcesContent":["/**\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 blank separator row, then the two fixed rows —\n * the prompt row (`› `, readline line editing) and the ticker row. A transcript\n * line is inserted above the fixed rows with a save-cursor / scroll-region /\n * restore-cursor write that never touches them; the scroll region makes the\n * transcript roll inside itself. Everything here builds strings (or parses one\n * line), so the tests assert exact byte sequences — no terminal needed.\n */\n\n/** The prompt marker of the chat row. */\nexport const CHAT_PROMPT = \"› \";\n\n/** Dim hint shown once behind the cursor until the first line is typed. */\nexport const CHAT_HINT = \"type here and press Enter · /help for commands\";\n\n/** What `/help` prints (one command per line, dim). */\nexport const CHAT_HELP = [\n \"/quit close this pane\",\n \"/resume <text> continue the finished worker with <text>\",\n \"/status one-line worker status\",\n \"anything else is sent to the worker — said while it runs, resumed after\",\n];\n\n// ---------------------------------------------------------------------------\n// visible width & wrapping (the gutter must stay the leftmost column)\n// ---------------------------------------------------------------------------\n\n/** Index just past the escape starting at `i` (CSI, OSC or a two-char one). */\nfunction endOfEscape(s: string, i: number): number {\n const n = s[i + 1];\n if (n === \"[\") {\n let j = i + 2;\n while (j < s.length && !(s[j]! >= \"@\" && s[j]! <= \"~\")) j++;\n return Math.min(s.length, j + 1);\n }\n if (n === \"]\") {\n let j = i + 2;\n while (j < s.length && s[j] !== \"\\x07\") j++;\n return Math.min(s.length, j + 1);\n }\n return i + 2;\n}\n\n/** Printable columns of `s` — ANSI escape sequences measure zero. */\nexport function visibleWidth(s: string): number {\n let w = 0;\n let i = 0;\n while (i < s.length) {\n if (s[i] === \"\\x1b\") {\n i = endOfEscape(s, i);\n continue;\n }\n w += 1;\n i += 1;\n }\n return w;\n}\n\n/**\n * The SGR sequences in effect at `upto`: everything opened since the last\n * reset, in order. Anything that is not an SGR escape is ignored (it does\n * not change colour state).\n */\nfunction sgrStateAt(text: string, upto: number): string[] {\n const open: string[] = [];\n let i = 0;\n while (i < Math.min(upto, text.length)) {\n if (text[i] === \"\\x1b\") {\n const end = endOfEscape(text, i);\n const esc = text.slice(i, end);\n if (/^\\x1b\\[[0-9;]*m$/.test(esc)) {\n const params = esc.slice(2, -1);\n const resets = params === \"\" || params.split(\";\").includes(\"0\");\n if (resets) open.length = 0;\n if (!(params === \"\" || params === \"0\")) open.push(esc);\n }\n i = end;\n continue;\n }\n i += 1;\n }\n return open;\n}\n\n/**\n * Wrap one rendered row to `width` printable columns. Breaks on whitespace\n * where possible, hard-wraps words longer than the width, never splits an\n * ANSI escape, and re-opens the colours it wraps inside of, so a diff row\n * keeps its `-`/`+` colour on every continuation row.\n */\nexport function wrapText(text: string, width: number): string[] {\n if (width < 1 || visibleWidth(text) <= width) return [text];\n\n // visible characters by their index in `text`\n const chars: number[] = [];\n for (let i = 0; i < text.length; i++) {\n if (text[i] === \"\\x1b\") {\n i = endOfEscape(text, i) - 1;\n continue;\n }\n chars.push(i);\n }\n\n // words as [first, last] indexes into `chars` (escapes ride along later)\n const words: Array<{ s: number; e: number; w: number }> = [];\n {\n let s = -1;\n let w = 0;\n for (let k = 0; k <= chars.length; k++) {\n const ch = k === chars.length ? \" \" : text[chars[k]!]!;\n if (ch === \" \") {\n if (s >= 0) {\n words.push({ s, e: k - 1, w });\n s = -1;\n w = 0;\n }\n } else {\n if (s < 0) s = k;\n w++;\n }\n }\n }\n // a leading indent (diff rows) belongs to the first word, width counted\n if (words.length && words[0]!.s > 0) {\n words[0] = { s: 0, e: words[0]!.e, w: words[0]!.w + words[0]!.s };\n }\n\n // chunk spans [start, end] over `chars`, greedy on visible width — the\n // width of a chunk is simply its span in `chars`, inner spaces included\n const spans: Array<[number, number]> = [];\n let cs = -1;\n let ce = -1;\n for (const word of words) {\n if (word.w > width) {\n // oversized word: fill the rest of the line, then full-width rows.\n // `take` is the word's chars that still fit — the gap before the word\n // eats into the room, and when it eats all of it the word starts fresh\n const room = cs < 0 ? 0 : width - (ce - cs + 1);\n const take = room > 0 ? ce + room - word.s + 1 : 0;\n if (take > 0) spans.push([cs, ce + room]);\n else if (cs >= 0) spans.push([cs, ce]);\n let pos = word.s + Math.max(0, take);\n let remaining = word.w - Math.max(0, take);\n while (remaining > width) {\n spans.push([pos, pos + width - 1]);\n pos += width;\n remaining -= width;\n }\n cs = pos;\n ce = pos + remaining - 1;\n continue;\n }\n if (cs < 0) {\n cs = word.s;\n ce = word.e;\n continue;\n }\n if (word.e - cs + 1 <= width) {\n ce = word.e;\n continue;\n }\n spans.push([cs, ce]);\n cs = word.s;\n ce = word.e;\n }\n if (cs >= 0) spans.push([cs, ce]);\n\n const out: string[] = [];\n for (const [a, b] of spans) {\n const from = chars[a]!;\n // take trailing escapes up to the next visible char with the chunk\n let end = chars[b]! + 1;\n while (end < text.length && text[end] === \"\\x1b\") end = endOfEscape(text, end);\n let piece = text.slice(from, end);\n const reopen = sgrStateAt(text, from);\n if (reopen.length) piece = reopen.join(\"\") + piece;\n if (sgrStateAt(text, end).length) piece += \"\\x1b[0m\";\n out.push(piece);\n }\n return out.length ? out : [\"\"];\n}\n\n// ---------------------------------------------------------------------------\n// the layout: scroll region + two fixed rows\n// ---------------------------------------------------------------------------\n\n/** Restrict scrolling to the transcript region (rows 1 … rows-3). */\nexport function chatScrollRegion(rows: number): string {\n return `\\x1b[1;${Math.max(1, rows - 3)}r`;\n}\n\n/** Clear the blank separator row between the transcript and the prompt. */\nexport function chatBlankRow(rows: number): string {\n return `\\x1b[${Math.max(1, rows - 2)};1H\\x1b[K`;\n}\n\n/**\n * Enter the chat layout: clear the pane, set the scroll region, blank 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(rows: number): string {\n return (\n \"\\x1b[2J\" + chatScrollRegion(rows) + chatBlankRow(rows) +\n `\\x1b[${Math.max(1, rows - 1)};3H`\n );\n}\n\n/** Leave it: reset the scroll region, show the cursor, drop to the last row. */\nexport function chatLeave(rows: number): string {\n return \"\\x1b[r\\x1b[?25h\" + `\\x1b[${Math.max(1, rows)};1H`;\n}\n\n/**\n * Redraw the ticker on its own row and park the cursor back on the prompt\n * row at `parkCol` (default right after `› `): a ticker refresh never leaves\n * the cursor on the bottom row.\n */\nexport function chatTickerRow(text: string, rows: number, parkCol = 3): string {\n return (\n \"\\x1b7\" +\n `\\x1b[${Math.max(1, rows)};1H\\x1b[K` + text +\n `\\x1b[${Math.max(1, rows - 1)};${Math.max(1, parkCol)}H`\n );\n}\n\n/**\n * The prompt row: the fixed `› ` prefix, then the input buffer — or, while\n * the buffer is empty, the dim placeholder behind the same prefix. The\n * cursor parks right after the buffer (`cursorAt` chars in, for a cursor\n * mid-draft), so typed text always starts at the column the placeholder\n * occupied, never glued to a hint.\n */\nexport function chatPromptRow(\n rows: number,\n buffer = \"\",\n dim: (s: string) => string = (s) => s,\n cursorAt?: number\n): string {\n const rowN = Math.max(1, rows - 1);\n const body = buffer !== \"\" ? buffer : dim(CHAT_HINT);\n const col = 3 + Math.max(0, Math.min(cursorAt ?? buffer.length, buffer.length));\n return `\\x1b[${rowN};1H\\x1b[K` + CHAT_PROMPT + body + `\\x1b[${rowN};${col}H`;\n}\n\nexport interface ChatInsert {\n seq: string;\n /** rows filled after this one (caps at regionRows, then it always scrolls). */\n fill: number;\n}\n\n/**\n * Insert one transcript row above the fixed prompt/ticker rows. While the\n * region is still filling (`fill < regionRows`) the row is placed top-down;\n * once full, the cursor moves to the region's bottom row and a newline\n * scrolls the region up by one — the two fixed rows are never touched. The\n * user's cursor is saved before and restored after, so readline keeps its\n * position on the prompt row.\n */\nexport function chatInsertLine(line: string, fill: number, regionRows: number): ChatInsert {\n const growing = fill < regionRows;\n const seq =\n \"\\x1b7\" +\n (growing\n ? `\\x1b[${fill + 1};1H${line}\\x1b[K`\n : `\\x1b[${regionRows};1H\\n${line}\\x1b[K`) +\n \"\\x1b8\";\n return { seq, fill: Math.min(fill + 1, regionRows) };\n}\n\n// ---------------------------------------------------------------------------\n// prompt command parsing\n// ---------------------------------------------------------------------------\n\nexport type ChatAction =\n | { kind: \"message\"; text: string }\n | { kind: \"help\" }\n | { kind: \"quit\" }\n | { kind: \"status\" }\n | { kind: \"resume\"; text: string };\n\n/**\n * One submitted prompt line → what to do with it. `/help`, `/quit`,\n * `/status` and `/resume <text>` are commands (a bare `/resume` comes back\n * with empty text so the caller can print its usage); anything else,\n * including any other `/word`, is a message for the worker.\n */\nexport function parseChatLine(raw: string): ChatAction {\n const text = raw.trim();\n if (text === \"/help\") return { kind: \"help\" };\n if (text === \"/quit\") return { kind: \"quit\" };\n if (text === \"/status\") return { kind: \"status\" };\n if (text.startsWith(\"/resume\")) return { kind: \"resume\", text: text.slice(\"/resume\".length).trim() };\n return { kind: \"message\", text };\n}\n\n/**\n * The auto-exit countdown must not fire while the prompt holds unsent text:\n * true while it does. null/undefined (no prompt wired) never holds.\n */\nexport function holdAutoExit(promptText: string | null | undefined): boolean {\n return typeof promptText === \"string\" && promptText.trim() !== \"\";\n}\n","/**\n * render.ts — turn worker events into the lines a human reads.\n *\n * Ports the glm-ps transcript rendering: the gutter rules (dim `>` for reads\n * and searches, `$` for shell, magenta `~` + red/green diff for edits, green\n * `+` for writes), Read-result trimming (first 3 lines then a count), and the\n * result footer. Colors are applied only when the output is a TTY, so MCP\n * tool output stays plain text.\n */\n\nimport { relative, basename } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { ageOf, contextPercent, type WorkerStatus, alive } 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 = `ctx ${fmtK(s.contextTokens ?? 0)}/${fmtK(s.contextWindow ?? 0)} ${pct}%`;\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 return `[${s.provider}/${s.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/** Compact token count: 84k, 200k, 900. */\nfunction fmtK(n: number): string {\n return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);\n}\n\n/**\n * The context meter `ctx 84k/200k (42%)`, yellow from 70 %, red from 85 %.\n * null when the numbers are missing or below `minPct` (the table only shows\n * it past 60; the pane liveness line always shows it).\n */\nexport function contextMeter(\n c: Paint,\n s: Pick<WorkerStatus, \"contextTokens\" | \"contextWindow\">,\n minPct = 0\n): string | null {\n const pct = contextPercent(s);\n if (pct === null || pct <= minPct) return null;\n const label = `ctx ${fmtK(s.contextTokens ?? 0)}/${fmtK(s.contextWindow ?? 0)} (${pct}%)`;\n if (pct > 85) return c(\"red\", label);\n if (pct > 70) return c(\"yellow\", label);\n return label;\n}\n\nfunction renderToolUse(\n c: Paint,\n prefix: string,\n name: string,\n inp: unknown,\n cwd: string\n): string[] {\n const i = (typeof inp === \"object\" && inp !== null ? inp : {}) as Record<string, unknown>;\n const get = (k: string) => (typeof i[k] === \"string\" ? (i[k] as string) : \"\");\n if (name === \"Read\") {\n return [`${prefix}${c(\"dim\", \">\")} Reading ${relPath(get(\"file_path\"), cwd)}`];\n }\n if (name === \"Grep\" || name === \"Glob\") {\n return [\n `${prefix}${c(\"dim\", \">\")} Searching ${c(\"cyan\", get(\"pattern\"))} in ${relPath(get(\"path\") || \".\", cwd)}`,\n ];\n }\n if (name === \"Bash\") {\n return [`${prefix}${c(\"dim\", \"$\")} ${shortText(get(\"command\"), 160)}`];\n }\n if (name === \"Edit\") {\n const out = [`${prefix}${c(\"mag\", \"~\")} Editing ${relPath(get(\"file_path\"), cwd)}`];\n for (const line of unifiedDiffLines(get(\"old_string\"), get(\"new_string\"))) {\n if (line.startsWith(\"-\")) out.push(`${prefix} ${c(\"red\", line)}`);\n else if (line.startsWith(\"+\")) out.push(`${prefix} ${c(\"green\", line)}`);\n else out.push(`${prefix} ${c(\"dim\", line)}`);\n }\n return out;\n }\n if (name === \"Write\") {\n const n = get(\"content\").split(\"\\n\").length;\n return [`${prefix}${c(\"green\", \"+\")} Writing ${relPath(get(\"file_path\"), cwd)} (${n} lines)`];\n }\n if (name === \"WebSearch\" || name === \"WebFetch\") {\n return [`${prefix}${c(\"dim\", \">\")} ${name} ${shortText(get(\"query\") || get(\"url\"), 100)}`];\n }\n return [`${prefix}${c(\"dim\", \">\")} ${name} ${shortText(JSON.stringify(i), 120)}`];\n}\n\n/** Render one stream-json event; `tools` maps tool_use_id → tool name. */\nexport function renderEvent(\n c: Paint,\n prefix: string,\n e: StreamEventLike,\n cwd: string,\n tools: Record<string, string>,\n gutter?: { first: string; cont: string } | null\n): string[] {\n const out: string[] = [];\n if (e.type === \"system\" && e.subtype === \"init\") {\n out.push(\n `${prefix}${c(\"dim\", `worker started · model ${e.model ?? \"?\"} · cwd ${basename(e.cwd || cwd || \"\")}`)}`\n );\n } else if (e.type === \"operator\") {\n // 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: { id: string; label: string; cwd: string; provider?: string; session?: { name?: string } | null }\n): string {\n const bits = [s.provider ? `[${s.provider}]` : \"\", sessionTag(s)].filter(Boolean).join(\" \");\n const sep = bits ? ` ${bits}` : \"\";\n return c(\"bold\", `━━ ${s.id}${sep} ${s.label} (${basename(s.cwd)})`);\n}\n\n/** The 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 (s.state === \"running\" && alive(s.pid)) running.push(s);\n else {\n if (s.state === \"running\") s.state = \"lost\";\n done.push(s);\n }\n }\n const 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\n const clock = `${String(now.getHours()).padStart(2, \"0\")}:${String(now.getMinutes()).padStart(2, \"0\")}:${String(now.getSeconds()).padStart(2, \"0\")}`;\n const lines: string[] = [c(\"bold\", `Workers ${clock}`), \"\"];\n lines.push(c(\"bold\", `RUNNING (${running.length})`));\n if (!running.length) lines.push(\" none\");\n for (let i = 0; i < running.length; i++) {\n const s = running[i];\n const chain = chainOf(s);\n if (chain) {\n const prev = running[i - 1];\n if (!prev || chainOf(prev) !== chain) {\n const stages = running.filter((x) => chainOf(x) === chain);\n lines.push(` ${c(\"bold\", `chain ${chain}`)} ${chainLabelOf(stages)}`);\n }\n }\n const last =\n !!chain && (i + 1 >= running.length || chainOf(running[i + 1]) !== chain);\n const depth = workerDepth(statuses, s.id);\n const subLast =\n !chain && (i + 1 >= running.length || running[i + 1].parent !== s.parent);\n const meter = contextMeter(c, s, 60);\n const p = chain ? \" \" : rowPrefix(depth, subLast);\n const q = chain ? \" \" : rowCont(depth, subLast);\n lines.push(\n treeLine(\n `${p}${c(\"cyan\", s.id)}${inboxMark(s)} [${s.provider}] ${ageOf(s.started, now).padStart(4)} old turns ${String(s.turns).padStart(2)} tools ${String(s.tools).padStart(2)} ${basename(s.cwd)}${branchMark(s)}${meter ? \" \" + meter : \"\"}`,\n chain,\n last\n )\n );\n lines.push(treeLine(`${q}task: ${s.label}`, chain, last));\n lines.push(\n treeLine(`${q}now: ${c(\"yellow\", s.last)} (${ageOf(s.updated, now)} ago)`, chain, last)\n );\n }\n lines.push(\"\");\n lines.push(c(\"bold\", \"FINISHED (last 8)\"));\n const doneSlice = done.slice(-8);\n for (let i = 0; i < doneSlice.length; i++) {\n const s = doneSlice[i];\n const chain = chainOf(s);\n if (chain) {\n const prev = doneSlice[i - 1];\n if (!prev || chainOf(prev) !== chain) {\n const stages = doneSlice.filter((x) => chainOf(x) === chain);\n lines.push(` ${c(\"bold\", `chain ${chain}`)} ${chainLabelOf(stages)}`);\n }\n }\n const last =\n !!chain && (i + 1 >= doneSlice.length || chainOf(doneSlice[i + 1]) !== chain);\n const depth = workerDepth(statuses, s.id);\n const subLast =\n !chain && (i + 1 >= doneSlice.length || doneSlice[i + 1].parent !== s.parent);\n const col = s.state === \"done\" ? \"green\" : \"red\";\n const tag = sessionTag(s);\n lines.push(\n treeLine(\n `${chain ? \" \" : rowPrefix(depth, subLast)}${s.id}${inboxMark(s)} [${s.provider}]${tag ? \" \" + tag : \"\"} ${c(col, s.state.padEnd(6))} rc=${s.rc} ${String(s.secs ?? \"?\").padStart(4)}s turns ${String(s.turns).padStart(2)} tools ${String(s.tools).padStart(2)} ${basename(s.cwd)}${branchMark(s)} ${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 * One-line status-bar summary (same shape glm-ps --status printed). Sub-workers\n * render after their parent with a `↳` prefix (`all` carries the whole forest\n * so depth is right even when only some workers are this session's);\n * `◆N` marks an inbox with N handoffs.\n */\nexport function renderStatusLine(\n mine: WorkerStatus[],\n now: Date = new Date(),\n c: Paint = makeColor(true),\n all: WorkerStatus[] = mine,\n inbox: Record<string, number> = {}\n): string {\n if (!mine.length) return \"\";\n const running = mine\n .filter((s) => s.state === \"running\" && alive(s.pid))\n .sort((a, b) => workerDepth(all, a.id) - workerDepth(all, b.id)); // stable: parents first\n const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, \"0\")}-${String(now.getDate()).padStart(2, \"0\")}`;\n const doneToday = mine.filter((s) => s.state !== \"running\" && s.started.startsWith(today));\n const ok = doneToday.filter((s) => s.state === \"done\").length;\n const bad = doneToday.length - ok;\n const providers = new Set(running.map((s) => s.provider));\n const providerTag = providers.size === 1 ? [...providers][0] : \"workers\";\n let head = `${providerTag} ▶${running.length}`;\n const parts = running.slice(0, 3).map((s) => {\n // context load joins the summary once it passes 60 % (yellow >70, red >85)\n const meter = contextMeter(c, s, 60);\n const depth = workerDepth(all, s.id);\n const lead = depth > 0 ? \" \".repeat(depth - 1) + \"↳ \" : \"\";\n const box = inbox[s.id] ? ` ◆${inbox[s.id]}` : \"\";\n return (\n `${lead}${s.id.slice(-4)} ${s.label.slice(0, 26)} ${ageOf(s.started, now)} · ${s.last.slice(0, 30)}${box}` +\n (meter ? ` ${meter}` : \"\")\n );\n });\n if (parts.length) head += \" \" + parts.join(\" | \");\n if (doneToday.length) head += ` ✓${ok} ✗${bad} today`;\n return head;\n}\n","/**\n * viewer.ts — ps / follow / replay / status line over the worker logDir.\n *\n * Scoping: workers launched from this terminal's AIBroker session (or, as the\n * fallback, its iTerm tab) unless --all or an explicit worker id is given.\n * Outside iTerm, everything degrades to \"all workers\" — the Python behaviour.\n *\n * Transcripts render with a `HH:MM:SS │ ` gutter (2g): dim, taken from the\n * `_ts` stamp on every mirrored event (local wall clock — stamps carry a local\n * offset and old UTC stamps are converted), the worker tag in front when\n * several run at once, a date separator when the day changes, and — on a TTY —\n * a liveness line (`⋯ 12s · run tests before the fix · $ bun run test`) that\n * is rewritten in place between events. Attaching to a worker that is already\n * running first replays its last events (backfill), then continues live.\n *\n * Following one worker turns the pane into a small chat (see chatui.ts): the\n * transcript scrolls in a region that ends two rows above the bottom, the\n * prompt row (`› `, readline editing) and the ticker row stay fixed, and every\n * submitted line is said to the worker while it runs and resumes it (same\n * Claude session) once it has finished. Lines the pane wraps itself keep the\n * `│` bar on continuation rows, so no content ever lands left of the bar.\n * Non-TTY output keeps the plain scrolling behaviour.\n */\n\nimport { existsSync, openSync, readSync, closeSync, readFileSync } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport { spawn, type SpawnOptions } from \"node:child_process\";\nimport { eventsPath } from \"./paths.js\";\nimport { alive, loadStatuses, type WorkerStatus } from \"./status.js\";\nimport { currentTabKey, resolveSession, workerInScope } from \"./scope.js\";\nimport { readInbox } from \"./handoff.js\";\nimport { sayToWorker } from \"./operator.js\";\nimport {\n CHAT_HELP,\n chatBlankRow,\n chatEnter,\n chatInsertLine,\n chatLeave,\n chatPromptRow,\n chatScrollRegion,\n chatTickerRow,\n holdAutoExit,\n parseChatLine,\n wrapText,\n} from \"./chatui.js\";\nimport {\n blankBetween,\n chatStatusRow,\n contextMeter,\n dayOf,\n gutterFor,\n headerLine,\n intentOf,\n makeColor,\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 const lines = blankBetween(s.prev, e) ? [\"\", ...body] : body;\n return {\n lines,\n day: day && day !== s.lastDay ? day : null,\n activity: lines.some((ln) => ln !== \"\"),\n state,\n };\n}\n\n/** `YYYY-MM-DD` straight out of an unparsable stamp, \"\" when it has none. */\nfunction rawDay(ts: string): string {\n return /^\\d{4}-\\d{2}-\\d{2}/.test(ts) ? ts.slice(0, 10) : \"\";\n}\n\n/**\n * The events a fresh follow replays before going live: the last `cap`\n * non-empty log lines, oldest first.\n */\nexport function backfillLines(raw: string, cap = BACKFILL_EVENTS): string[] {\n return raw.split(\"\\n\").filter((l) => l.trim()).slice(-cap);\n}\n\n// ---------------------------------------------------------------------------\n// replay\n// ---------------------------------------------------------------------------\n\n/** The rendered transcript of one worker, from the start, as one string. */\nexport function replayOutput(\n logDir: string,\n wid: string,\n color = process.stdout.isTTY === true,\n tailLines?: number\n): string {\n const path = eventsPath(logDir, wid);\n if (!existsSync(path)) {\n throw new Error(`no event log for ${wid}`);\n }\n const c = makeColor(color);\n const st =\n loadStatuses(logDir).find((s) => s.id === wid) ??\n ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n const wrapWidth = typeof process.stdout.columns === \"number\" ? process.stdout.columns : null;\n const out: string[] = [headerLine(c, st)];\n const raw = readFileSync(path, \"utf8\");\n const lines = tailLines !== undefined ? raw.split(\"\\n\").slice(-tailLines) : raw.split(\"\\n\");\n // the worker's inbox handoffs join the transcript where they happened (_ts)\n const handoffs: StreamEventLike[] = readInbox(logDir, wid).map((h) => ({\n type: \"handoff\",\n from: h.from,\n kind: h.kind,\n text: h.text,\n _ts: h._ts,\n }));\n let state = initialFollowState(\"\");\n const events: StreamEventLike[] = [];\n for (const line of lines) {\n if (!line.trim()) continue;\n let e: StreamEventLike;\n try {\n e = JSON.parse(line) as StreamEventLike;\n } catch {\n continue;\n }\n events.push(e);\n }\n const stamp = (e: StreamEventLike): number => {\n const t = e._ts ? Date.parse(e._ts) : NaN;\n return Number.isNaN(t) ? 0 : t;\n };\n const merged = [...events, ...handoffs].sort((a, b) => stamp(a) - stamp(b));\n for (const e of merged) {\n const step = applyEvent(c, state, e, st.cwd ?? \"\", undefined, undefined, wrapWidth);\n if (step.day) out.push(c(\"dim\", `── ${step.day} ──`));\n out.push(...step.lines);\n state = step.state;\n }\n return out.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// follow\n// ---------------------------------------------------------------------------\n\ninterface FollowHandle {\n fd: number;\n buf: string;\n}\n\n/**\n * The follow exit decision for one worker: it is over once its result event\n * was rendered, or once its status left \"running\" while its pid is gone — a\n * worker killed without writing a result still ends. A status never read\n * (undefined) means \"not over\": follow keeps waiting for the first event.\n */\nexport function workerEnded(\n resultRendered: boolean,\n state: WorkerStatus[\"state\"] | undefined,\n pidAlive: boolean\n): boolean {\n return resultRendered || (state !== undefined && state !== \"running\" && !pidAlive);\n}\n\n// ---------------------------------------------------------------------------\n// operator input (2i): this pane's stdin drives say / resume\n// ---------------------------------------------------------------------------\n\n/** What makeOperatorInput() needs from the follow around it. */\nexport interface OperatorInputDeps {\n /** the worker typed lines go to right now (null: none, lines are ignored). */\n target: () => string | null;\n /** forwards one message to a running worker. */\n say: (id: string, text: string) => Promise<string>;\n /** whether a status file exists for the id (say failed → resume, or note). */\n workerKnown: (id: string) => boolean;\n /** continues a finished worker (same Claude session). */\n resume: (text: string, id: string) => void;\n /** one line of feedback in the pane. */\n note: (s: string) => void;\n paint: Paint;\n /** chat mode only: the echoed line replaces the \"» sent\" note. */\n sent?: (id: string) => void;\n}\n\n/**\n * One typed stdin line → say (worker running) or resume (worker finished):\n * the operator channel of a `follow <id>` pane. Trimmed; empty lines and a\n * missing target are ignored.\n */\nexport function makeOperatorInput(d: OperatorInputDeps): (raw: string) => void {\n return (raw: string) => {\n const text = raw.trim();\n if (!text) return;\n const id = d.target();\n if (id === null) return;\n d.say(id, text).then(\n () => (d.sent ? d.sent(id) : d.note(d.paint(\"dim\", `» sent to ${id}`))),\n (e: Error) => {\n if (d.workerKnown(id)) d.resume(text, id);\n else d.note(d.paint(\"red\", `» ${e.message}`));\n }\n );\n };\n}\n\n/** What followWorkers writes to (process.stdout, or a fake in tests). */\nexport interface FollowStream {\n write(s: string): boolean;\n isTTY?: boolean;\n columns?: number;\n rows?: number;\n on?(event: \"resize\", fn: () => void): unknown;\n removeListener?(event: \"resize\", fn: () => void): unknown;\n}\n\n/** The child of a `pai worker resume` spawn (tests inject a fake). */\nexport interface ResumeChild {\n stdout?: { on(event: \"data\", cb: (chunk: Buffer) => void): unknown };\n on(event: \"close\", cb: (code: number | null) => void): unknown;\n}\n\n/** Test seams for followWorkers: the streams, the resume spawn, the prompt. */\nexport interface FollowIO {\n stdin?: NodeJS.ReadableStream;\n stdout?: FollowStream;\n /** replaces the `pai worker resume` spawn (tests record instead of run). */\n spawnResume?: (id: string, text: string) => ResumeChild;\n /** current unsent prompt text (tests force a draft to hold the countdown). */\n promptLine?: () => string;\n}\n\n/**\n * Tail one worker (target) or the running workers of this scope, live.\n * Workers whose event log already exists are first replayed (last\n * BACKFILL_EVENTS events), then tailed. Auto-exit: with a target, wait\n * `autoExit` seconds after its end; without one, exit once no worker in\n * scope has run for that many seconds in a row, never within the first 30 s.\n *\n * With a target on a TTY the pane becomes a chat (chatui.ts): transcript in\n * a scroll region, fixed prompt and ticker rows, submitted lines said to the\n * worker (or resuming it after it finished) and echoed as `»` rows. A draft\n * in the prompt holds the auto-exit countdown. Non-TTY output keeps the\n * plain scrolling behaviour; FORCE_TTY=1 emits the chat layout over a pipe.\n */\nexport async function followWorkers(\n logDir: string,\n target: string | null,\n showAll: boolean,\n autoExit: number,\n env: NodeJS.ProcessEnv = process.env,\n color = process.stdout.isTTY === true,\n io?: FollowIO\n): Promise<void> {\n const c = makeColor(color);\n const out_ = io?.stdout ?? process.stdout;\n const in_ = io?.stdin ?? process.stdin;\n // FORCE_TTY=1: the TTY layout over a pipe (tests, recorded panes)\n const tty = out_.isTTY === true || env.FORCE_TTY === \"1\";\n const term = env.ITERM_SESSION_ID ?? \"\";\n const scopeTab = target || showAll ? \"\" : currentTabKey(env);\n const handles = new Map<string, FollowHandle>();\n const seenHeader = new Set<string>();\n const finished = new Set<string>();\n // handoffs already rendered per worker (id → inbox lines shown so far)\n const inboxSeen = new Map<string, number>();\n const states = new Map<string, FollowState>();\n const started = Date.now();\n let idleSince: number | null = null;\n let aborted = false;\n // liveness state: rewritten in place between events, TTY only\n let lastEventAt = Date.now();\n let meterStatus: WorkerStatus | null = null;\n 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 /** 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) => {\n if (!chat) {\n out_.write(line + \"\\n\");\n return;\n }\n const r = chatInsertLine(line, fill, regionRows());\n out_.write(r.seq);\n fill = r.fill;\n };\n\n // The ticker redraws its line in place: in the chat layout that is its own\n // bottom row (save-cursor, draw, restore-cursor); the plain mode writes\n // CR + erase-to-end-of-line, never a newline. The control bytes live in\n // their own string literals — bundling them onto a template literal makes\n // the bundler fold them in as raw chars, and a raw CR inside a template\n // literal is normalised to LF by the language, which is how the ticker\n // once scrolled a blank line per tick.\n const eraseLiveness = () => {\n if (chat) return;\n if (tty) out_.write(\"\\r\\x1b[K\");\n };\n let ticker = initialFollowState();\n // the chat ticker row doubles as the worker's status line: provider/model,\n // context meter, turns, tools, runtime — frozen with ✓/✗ once it finished\n const statusRowOf = (st: WorkerStatus, secs: number): StatusRow => {\n const fin = finished.has(st.id);\n const startedAt = Date.parse((st.started ?? \"\").replace(\" \", \"T\"));\n const elapsed =\n fin && st.secs !== null\n ? st.secs\n : Number.isNaN(startedAt)\n ? 0\n : Math.floor((Date.now() - startedAt) / 1000);\n return {\n provider: st.provider,\n model: st.model,\n contextTokens: st.contextTokens,\n contextWindow: st.contextWindow,\n turns: st.turns,\n tools: st.tools,\n elapsed,\n idle: secs,\n intent: ticker.intent,\n tool: ticker.tool,\n state: fin ? (st.state === \"running\" ? \"done\" : st.state) : null,\n };\n };\n const writeLiveness = () => {\n if (!tty) return;\n const secs = Math.max(0, Math.floor((Date.now() - lastEventAt) / 1000));\n if (chat) {\n const text = meterStatus\n ? chatStatusRow(c, statusRowOf(meterStatus, secs))\n : tickerText(secs, ticker.intent, ticker.tool);\n out_.write(chatTickerRow(text, rows, promptCursorCol()));\n } else {\n const meter = meterStatus ? contextMeter(c, meterStatus) : null;\n out_.write(\"\\r\\x1b[K\");\n out_.write(tickerText(secs, ticker.intent, ticker.tool, meter));\n }\n };\n\n const runningIds = (): string[] =>\n loadStatuses(logDir)\n .filter(\n (s) =>\n s.state === \"running\" &&\n alive(s.pid) &&\n (target !== null || showAll || (scopeTab ? workerInScope(s, term) : true))\n )\n .map((s) => s.id);\n\n // --- one rendered event, shared by the backfill and the live tail\n const emitEvent = (e: StreamEventLike, wid: string, st: WorkerStatus, multi: boolean) => {\n if (e.type === \"operator\" && chat && suppressMirror(String(e.text ?? \"\"))) return;\n if (!seenHeader.has(wid)) {\n eraseLiveness();\n out(headerLine(c, st));\n seenHeader.add(wid);\n }\n const state = states.get(wid) ?? initialFollowState();\n const step = applyEvent(\n c,\n state,\n e,\n st.cwd ?? \"\",\n multi ? c(\"cyan\", wid.slice(-4)) : undefined,\n undefined,\n columns()\n );\n if (step.day) {\n out(c(\"dim\", `── ${step.day} ──`));\n }\n states.set(wid, step.state);\n if (wid === target) ticker = step.state;\n eraseLiveness();\n for (const ln of step.lines) {\n out(ln);\n }\n if (step.activity) lastEventAt = Date.now();\n if (e.type === \"result\") {\n meterStatus = st;\n finished.add(wid);\n } else if (wid === target || target === null) {\n meterStatus = st;\n }\n };\n\n // --- attach: replay what is already in the log, then tail from its end\n const attachHandle = (wid: string, path: string, st: WorkerStatus, multi: boolean): FollowHandle => {\n const handle = { fd: openSync(path, \"r\"), buf: \"\" };\n try {\n const existing = readFileSync(path, \"utf8\");\n if (existing.trim()) {\n for (const line of backfillLines(existing)) {\n let e: StreamEventLike;\n try {\n e = JSON.parse(line) as StreamEventLike;\n } catch {\n continue;\n }\n emitEvent(e, wid, st, multi);\n }\n } else {\n // no event yet: show the header now, not at the first event\n if (!seenHeader.has(wid)) {\n out(headerLine(c, st));\n seenHeader.add(wid);\n }\n }\n // the backfill already rendered the file; the live tail starts at EOF\n const sink = Buffer.alloc(65536);\n for (;;) {\n let n: number;\n try {\n n = readSync(handle.fd, sink, 0, sink.length, null);\n } catch {\n break;\n }\n if (n <= 0) break;\n }\n } catch {\n // unreadable log: tail from wherever the fd happens to be\n }\n return handle;\n };\n\n // --- say / resume from this pane's stdin\n const noteLine = (s: string) => {\n eraseLiveness();\n out(s);\n };\n // SpawnOptions (not the stdio-tuple overload): we only read stdout and\n // want the plain ChildProcess shape; tests inject a fake that records\n const spawnResume =\n io?.spawnResume ??\n ((id: string, text: string): ResumeChild =>\n spawn(\"pai\", [\"worker\", \"resume\", id, text, \"--print-id\", \"--no-pane\"], {\n stdio: [\"ignore\", \"pipe\", \"inherit\"],\n } as SpawnOptions) as unknown as ResumeChild);\n const resumeTarget = (text: string, id: string) => {\n noteLine(c(\"dim\", `» resuming ${id} …`));\n const child = spawnResume(id, text);\n let idOut = \"\";\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n idOut += chunk.toString(\"utf8\");\n });\n child.on(\"close\", (rc: number | null) => {\n const newId = idOut.trim().split(\"\\n\").pop() ?? \"\";\n if (rc === 0 && /^\\d{8}-\\d{6}-\\d+$/.test(newId)) {\n noteLine(c(\"dim\", `» resumed as ${newId}`));\n target = newId;\n finished.delete(newId);\n seenHeader.delete(newId);\n states.delete(newId);\n ticker = initialFollowState(\"resumed\");\n lastEventAt = Date.now();\n } else {\n noteLine(c(\"red\", `» resume failed (rc=${rc})`));\n }\n });\n };\n const handleOperatorLine = makeOperatorInput({\n target: () => target,\n say: (id, text) => sayToWorker(logDir, id, text),\n workerKnown: (id) => loadStatuses(logDir).some((s) => s.id === id),\n resume: resumeTarget,\n note: noteLine,\n paint: c,\n ...(chat ? { sent: () => undefined } : {}), // the echo replaces the note\n });\n\n // --- the chat line: readline editing on the prompt row\n // the readline interface keeps stdin flowing — without a close, the process\n // outlives follow itself (a pane then never closes, however long ago the\n // worker ended), so it is closed in the finally block below\n const terminalIn = (in_ as { isTTY?: boolean }).isTTY === true;\n let rlIn: ReturnType<typeof createInterface> | null = null;\n let onResize: (() => void) | null = null;\n // an echoed line comes back as a mirrored operator event within moments —\n // remember what was echoed and swallow the twin, so the pane shows what\n // was typed exactly once\n const echoed = new Map<string, { n: number; until: number }>();\n const suppressMirror = (text: string): boolean => {\n const g = echoed.get(text);\n if (!g || Date.now() > g.until) return false;\n g.n -= 1;\n if (g.n <= 0) echoed.delete(text);\n return true;\n };\n /** Column the prompt row's cursor parks at: right after the draft's cursor. */\n const promptCursorCol = (): number => {\n const line = rlIn?.line ?? \"\";\n const cur = (rlIn as unknown as { cursor?: number } | null)?.cursor;\n return 3 + Math.max(0, Math.min(typeof cur === \"number\" ? cur : line.length, line.length));\n };\n /**\n * The pane owns the prompt row's rendering: `› `, the buffer — or the dim\n * placeholder while the buffer is empty — and the cursor parked after it.\n */\n const drawPrompt = () => {\n if (!chat) return;\n const line = rlIn?.line ?? \"\";\n const cur = (rlIn as unknown as { cursor?: number } | null)?.cursor;\n out_.write(chatPromptRow(rows, line, (s) => c(\"dim\", s), cur));\n };\n if (chat) {\n const echoOperator = (text: string) => {\n const id = target;\n if (id === null) return;\n echoed.set(text, { n: 1, until: Date.now() + 10_000 });\n const st = states.get(id) ?? initialFollowState();\n const step = applyEvent(\n c,\n st,\n { type: \"operator\", _ts: new Date().toISOString(), text },\n \"\",\n undefined,\n undefined,\n columns()\n );\n if (step.day) out(c(\"dim\", `── ${step.day} ──`));\n for (const ln of step.lines) out(ln);\n states.set(id, step.state);\n };\n const handleChatLine = (raw: string) => {\n const act = parseChatLine(raw);\n let redrew = false;\n switch (act.kind) {\n case \"message\":\n if (!act.text) break;\n handleOperatorLine(act.text); // send the line …\n drawPrompt(); // … placeholder back before the » echo is written\n redrew = true;\n echoOperator(act.text);\n break;\n case \"resume\":\n if (!act.text) {\n out(c(\"dim\", \"usage: /resume <text>\"));\n break;\n }\n if (target !== null) resumeTarget(act.text, target);\n drawPrompt();\n redrew = true;\n echoOperator(act.text);\n break;\n case \"help\":\n for (const ln of CHAT_HELP) out(c(\"dim\", ln));\n break;\n case \"status\": {\n const s =\n target !== null ? loadStatuses(logDir).find((x) => x.id === target) : undefined;\n out(c(\"dim\", s ? `${s.id} · ${s.state} · ${s.last}` : `${target ?? \"?\"} · no status`));\n break;\n }\n case \"quit\":\n aborted = true;\n break;\n }\n if (!redrew) drawPrompt(); // on a pipe readline does not repaint the row\n };\n // readline edits silently (its output is a mute stream); the pane draws\n // the prompt row itself from the live buffer, so the placeholder yields\n // to the first keystroke and returns when the buffer empties again\n const silent = { write: () => true } 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 out_.write(chatEnter(rows));\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 out_.write(chatScrollRegion(rows) + chatBlankRow(rows));\n drawPrompt();\n };\n out_.on?.(\"resize\", onResize);\n } else if (target !== null && (in_ as { isTTY?: boolean }).isTTY) {\n // plain operator channel: TTY stdin, non-TTY stdout\n rlIn = createInterface({ input: in_ });\n rlIn.on(\"line\", handleOperatorLine);\n }\n\n /** The prompt's unsent text — a draft holds the auto-exit countdown. */\n const promptText = () => (io?.promptLine ? io.promptLine() : (rlIn?.line ?? \"\"));\n\n try {\n for (;;) {\n if (aborted) return;\n const statuses = new Map(loadStatuses(logDir).map((s) => [s.id, s]));\n const wanted = target ? [target] : runningIds();\n for (const wid of wanted) {\n if (handles.has(wid) || finished.has(wid)) continue;\n const path = eventsPath(logDir, wid);\n const st = statuses.get(wid) ?? ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n if (existsSync(path)) {\n const multi = target === null || handles.size > 0;\n handles.set(wid, attachHandle(wid, path, st, multi));\n states.set(wid, states.get(wid) ?? initialFollowState());\n } else if (!seenHeader.has(wid)) {\n // worker just started, no event yet: name it instead of a blank pane\n out(headerLine(c, st));\n seenHeader.add(wid);\n }\n }\n const multi = handles.size > 1 || target === null;\n let progressed = false;\n\n for (const [wid, h] of [...handles.entries()]) {\n const st = statuses.get(wid) ?? ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n // read everything appended since the last poll (fd position advances)\n const buffer = Buffer.alloc(65536);\n for (;;) {\n let n: number;\n try {\n n = readSync(h.fd, buffer, 0, buffer.length, null);\n } catch {\n n = 0;\n }\n if (n <= 0) break;\n h.buf += buffer.toString(\"utf8\", 0, n);\n }\n const lines = h.buf.split(\"\\n\");\n h.buf = lines.pop() ?? \"\";\n for (const line of lines) {\n if (!line.trim()) continue;\n progressed = true;\n let e: StreamEventLike;\n try {\n e = JSON.parse(line) as StreamEventLike;\n } catch {\n continue;\n }\n emitEvent(e, wid, st, multi);\n }\n const ended = workerEnded(finished.has(wid), st.state, alive(st.pid));\n if (ended) {\n if (!finished.has(wid)) {\n eraseLiveness();\n out(\n `${multi ? c(\"cyan\", wid.slice(-4)) + c(\"dim\", \" ┃ \") : \" \"}${c(\"red\", \"✗ \" + (st.state || \"ended\"))} · ${st.last ?? \"\"}`\n );\n finished.add(wid);\n }\n closeSync(h.fd);\n handles.delete(wid);\n }\n }\n\n // inbox tail: new handoffs render as ◆ lines in the recipient's pane\n // (they may also arrive via the say mirror — the durable copy is here)\n for (const wid of [...handles.keys()]) {\n const st = statuses.get(wid) ?? ({ id: wid, label: \"\", cwd: \"\", provider: \"\" } as WorkerStatus);\n const msgs = readInbox(logDir, wid);\n const seenN = inboxSeen.get(wid) ?? 0;\n if (msgs.length > seenN) {\n for (const m of msgs.slice(seenN)) {\n emitEvent(\n { type: \"handoff\", from: m.from, kind: m.kind, text: m.text, _ts: m._ts },\n wid,\n st,\n multi\n );\n }\n inboxSeen.set(wid, msgs.length);\n progressed = true;\n }\n }\n\n const lingerOn = target; // resume swaps `target` under us (see above)\n if (lingerOn !== null && finished.has(lingerOn)) {\n if (autoExit) {\n // a draft in the prompt holds the countdown: the operator may be\n // about to say or resume something\n if (holdAutoExit(promptText())) {\n writeLiveness();\n await sleep(250);\n continue;\n }\n const until = Date.now() + autoExit * 1000;\n while (\n Date.now() < until &&\n !aborted &&\n target === lingerOn &&\n !holdAutoExit(promptText())\n ) {\n writeLiveness();\n await sleep(250);\n }\n // interrupted, resumed inside the window, or a draft appeared: follow on\n if (aborted || target !== lingerOn || holdAutoExit(promptText())) continue;\n eraseLiveness();\n out(c(\"dim\", \"closing\"));\n return;\n }\n // no auto-exit: only a terminal follow with no wired stdin is done —\n // an interactive one stays up for say / resume input\n if (rlIn === null) return;\n }\n if (autoExit && !target) {\n if (runningIds().length) {\n idleSince = null;\n } else if (idleSince === null) {\n idleSince = Date.now();\n } else if (Date.now() - started >= 30_000 && Date.now() - idleSince >= autoExit * 1000) {\n eraseLiveness();\n out(c(\"dim\", \"closing\"));\n return;\n }\n }\n if (!progressed) {\n writeLiveness();\n await sleep(500);\n }\n }\n } finally {\n // close before anything else: an open readline keeps the process (and the\n // iTerm pane running it) alive long after follow has decided to end\n rlIn?.close();\n process.removeListener(\"SIGINT\", onInt);\n if (onResize) out_.removeListener?.(\"resize\", onResize);\n if (chat) out_.write(chatLeave(rows));\n for (const h of handles.values()) {\n try {\n closeSync(h.fd);\n } catch {\n /* already closed */\n }\n }\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n// ---------------------------------------------------------------------------\n// status line\n// ---------------------------------------------------------------------------\n\n/** Workers of this terminal for the status bar; \"\" when there are none. */\nexport function statusLineOutput(\n logDir: string,\n term: string,\n cwd: string,\n now: Date = new Date()\n): string {\n const statuses = loadStatuses(logDir);\n const mine = statuses.filter((s) => {\n const sameScope = term && workerInScope(s, term);\n const sameDir = cwd && s.cwd.startsWith(cwd);\n return sameScope || (!term && sameDir);\n });\n return renderStatusLine(mine, now, makeColor(false), statuses, inboxCounts(logDir, mine));\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 * Keys are never stored in the config and never accepted inline from the CLI:\n * only file paths. (The MCP `add` tool additionally accepts a raw `key`, which\n * it immediately parks in ~/.config/pai/keys/<name>, mode 0600, storing only\n * the path — a chat is not a place to leave a credential lying around.)\n */\n\nimport { existsSync, writeFileSync, chmodSync, mkdirSync } from \"node:fs\";\nimport {\n DEFAULT_LOG_DIR,\n PROVIDER_TAGS,\n WorkersConfigError,\n expandHome,\n keysDir,\n parseWorkersConfig,\n providerCostTier,\n readWorkersSection,\n writeWorkersSection,\n type ClassTarget,\n type WorkerProvider,\n type WorkersConfig,\n} from \"./config.js\";\nimport { clearCooldown, probeQuota, quotaSkipThreshold } from \"./routing.js\";\nimport { workersLogDir } from \"./paths.js\";\n\nexport interface AddProviderInput {\n name: string;\n baseUrl: string;\n keyFile?: string | null;\n key?: string;\n model: string;\n fastModel?: string;\n env?: Record<string, string>;\n note?: string;\n protocol?: \"anthropic\" | \"openai\";\n upstreamUrl?: string;\n engine?: \"claude\" | \"codex\";\n quotaProbe?: string;\n contextWindow?: number;\n costTier?: number;\n tags?: 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();\n const provider: WorkerProvider = {\n enabled: true,\n protocol: input.protocol ?? \"anthropic\",\n baseUrl: input.baseUrl,\n keyFile,\n models: input.fastModel\n ? { default: input.model, fast: input.fastModel }\n : { default: input.model },\n env: input.env ?? {},\n ...(input.note ? { note: input.note } : {}),\n ...(input.upstreamUrl ? { upstreamUrl: input.upstreamUrl } : {}),\n ...(input.engine && input.engine !== \"claude\" ? { engine: input.engine } : {}),\n ...(input.quotaProbe ? { quotaProbe: input.quotaProbe } : {}),\n ...(input.contextWindow ? { contextWindow: input.contextWindow } : {}),\n ...(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);\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): WorkersConfig {\n const { raw, workers } = readWorkersSection();\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);\n return workers;\n}\n\nexport function removeProvider(name: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (!workers.providers[name]) {\n throw new WorkersConfigError(`no provider named \"${name}\"`);\n }\n delete workers.providers[name];\n for (const [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);\n return workers;\n}\n\nexport function useProvider(name: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (!workers.providers[name]) {\n throw new WorkersConfigError(\n `no provider named \"${name}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n );\n }\n workers.active = name;\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function setProviderEnabled(name: string, enabled: boolean): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n const p = workers.providers[name];\n if (!p) throw new WorkersConfigError(`no provider named \"${name}\"`);\n p.enabled = enabled;\n writeWorkersSection(raw, workers);\n // `enable` is also the manual cooldown-clear (spec: routing)\n if (enabled) clearCooldown(workersLogDir(parseWorkersConfig(raw.workers)), name);\n return workers;\n}\n\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): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (typeof target === \"string\") {\n const [provider, alias] = target.split(\"/\");\n const p = workers.providers[provider];\n if (!p) {\n throw new WorkersConfigError(\n `no provider named \"${provider}\" in \"${target}\". Configured: ${Object.keys(workers.providers).join(\", \") || \"(none)\"}`\n );\n }\n if (alias && alias !== \"default\" && alias !== \"fast\") {\n throw new WorkersConfigError(\n `unknown model alias \"${alias}\" — providers expose \"default\" and \"fast\"`\n );\n }\n if (alias === \"fast\" && !p.models.fast) {\n throw new WorkersConfigError(`provider \"${provider}\" has no fast model configured`);\n }\n } else if (target.provider) {\n if (!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);\n return workers;\n}\n\nexport function unsetClass(name: string): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n if (!(name in workers.classes)) {\n throw new WorkersConfigError(`no class named \"${name}\"`);\n }\n delete workers.classes[name];\n writeWorkersSection(raw, workers);\n return workers;\n}\n\nexport function setWorkersEnabled(enabled: boolean): WorkersConfig {\n const { raw, workers } = readWorkersSection();\n workers.enabled = enabled;\n writeWorkersSection(raw, workers);\n return workers;\n}\n\n/** `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/** Human-readable provider listing (quota probe included when configured). */\nexport function describeProviders(workers: WorkersConfig): string[] {\n const lines: string[] = [];\n const names = Object.keys(workers.providers);\n if (!names.length) {\n lines.push(`no providers configured. Add one with:`);\n lines.push(\n ` pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n );\n return lines;\n }\n for (const name of names) {\n const p = workers.providers[name];\n const flags = [\n p.enabled ? \"enabled\" : \"disabled\",\n workers.active === name ? \"active\" : null,\n ].filter(Boolean);\n const quota = p.quotaProbe ? probeQuota(p) : null;\n const quotaNote = quota === null ? \"\" : ` quota ${quota}% (skip at ${quotaSkipThreshold(p)})`;\n const tierTags = [`tier ${providerCostTier(p)}`, ...(p.tags ?? [])].join(\", \");\n lines.push(`${name} [${flags.join(\", \")}] ${p.baseUrl}`);\n lines.push(\n ` model ${p.models.default}${p.models.fast ? ` (fast: ${p.models.fast})` : \"\"}${quotaNote}`\n );\n lines.push(` ${tierTags}`);\n if (p.keyFile) lines.push(` key file ${expandHome(p.keyFile)}`);\n else lines.push(` no key file (token \"local\")`);\n if (p.note) lines.push(` ${p.note}`);\n if (p.protocol === \"openai\") {\n lines.push(` via PAI proxy ← ${p.upstreamUrl ?? \"(upstreamUrl missing)\"}`);\n }\n if (p.engine === \"codex\") {\n lines.push(` engine codex (runs through the Codex CLI)`);\n }\n }\n const setNames = Object.keys(workers.mcpSets);\n if (setNames.length) {\n lines.push(`mcp sets: ${setNames.map((s) => `${s}=[${workers.mcpSets[s].join(\",\")}]`).join(\" \")}`);\n }\n const classNames = Object.keys(workers.classes);\n if (classNames.length) {\n lines.push(`classes: ${classNames.map((cl) => `${cl}=${classTargetText(workers.classes[cl])}`).join(\" \")}`);\n }\n if (workers.active === \"auto\") {\n lines.push(`routing: auto — order [${workers.routing.order.join(\", \")}], cooldown ${workers.routing.cooldownMinutes}m`);\n }\n return lines;\n}\n"],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import { t as saveQueryResult } from "./query-feedback-BV4CcxqS.mjs";
2
-
3
- export { saveQueryResult };
@@ -1,3 +0,0 @@
1
- import { t as routeNotification } from "./router-DcHKnEPa.mjs";
2
-
3
- export { routeNotification };
@@ -1,50 +0,0 @@
1
- //#region src/runtime-paths.ts
2
- /**
3
- * runtime-paths.ts — the shared files PAI talks to the running daemon through.
4
- *
5
- * Sockets, logs and pidfiles live at fixed absolute paths under /tmp, and every
6
- * one of them is shared with a *live* daemon. That makes them the same hazard
7
- * as any other piece of real user state a test can reach: writing one from a
8
- * test does not fail, it corrupts something already running, silently.
9
- *
10
- * The home guard in test/setup-home-guard.ts does not cover them. It works by
11
- * redirecting HOME, so it protects paths *derived* from the home directory and
12
- * nothing else — a hardcoded "/tmp/pai.sock" walks straight past it. Verified
13
- * on 2026-08-01 by snapshotting all five and running the suite: nothing writes
14
- * to them today. But "no test does this yet" is not a property, and the next
15
- * daemon test to be written would clobber the live socket with nothing to stop
16
- * it.
17
- *
18
- * So each path is read from an environment variable with the historical /tmp
19
- * value as its default. Behaviour is unchanged for every real invocation; the
20
- * test setup overrides the variables and the whole class becomes unreachable
21
- * rather than merely absent, the same way the home guard works.
22
- *
23
- * Read at call time, not at module load: the test setup file runs before any
24
- * module is imported, but resolving these eagerly would still bake in whatever
25
- * the environment held at import and make the override order matter.
26
- */
27
- /** IPC socket for the PAI daemon. */
28
- function paiSocketPath() {
29
- return process.env.PAI_SOCKET_PATH ?? "/tmp/pai.sock";
30
- }
31
- /** IPC socket for the AIBroker daemon — read by PAI, owned by AIBroker. */
32
- function aibrokerSocketPath() {
33
- return process.env.PAI_AIBROKER_SOCKET_PATH ?? "/tmp/aibroker.sock";
34
- }
35
- /** Where the daemon's stdout and stderr are collected. */
36
- function daemonLogPath() {
37
- return process.env.PAI_DAEMON_LOG_PATH ?? "/tmp/pai-daemon.log";
38
- }
39
- /** Pidfile for the running daemon. */
40
- function daemonPidPath() {
41
- return process.env.PAI_DAEMON_PID_PATH ?? "/tmp/pai-daemon.pid";
42
- }
43
- /** Where the launchd scheduler tick writes its output. */
44
- function schedulerLogPath() {
45
- return process.env.PAI_SCHEDULER_LOG_PATH ?? "/tmp/pai-scheduler.log";
46
- }
47
-
48
- //#endregion
49
- export { schedulerLogPath as a, paiSocketPath as i, daemonLogPath as n, daemonPidPath as r, aibrokerSocketPath as t };
50
- //# sourceMappingURL=runtime-paths-rni52zHX.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-paths-rni52zHX.mjs","names":[],"sources":["../src/runtime-paths.ts"],"sourcesContent":["/**\n * runtime-paths.ts — the shared files PAI talks to the running daemon through.\n *\n * Sockets, logs and pidfiles live at fixed absolute paths under /tmp, and every\n * one of them is shared with a *live* daemon. That makes them the same hazard\n * as any other piece of real user state a test can reach: writing one from a\n * test does not fail, it corrupts something already running, silently.\n *\n * The home guard in test/setup-home-guard.ts does not cover them. It works by\n * redirecting HOME, so it protects paths *derived* from the home directory and\n * nothing else — a hardcoded \"/tmp/pai.sock\" walks straight past it. Verified\n * on 2026-08-01 by snapshotting all five and running the suite: nothing writes\n * to them today. But \"no test does this yet\" is not a property, and the next\n * daemon test to be written would clobber the live socket with nothing to stop\n * it.\n *\n * So each path is read from an environment variable with the historical /tmp\n * value as its default. Behaviour is unchanged for every real invocation; the\n * test setup overrides the variables and the whole class becomes unreachable\n * rather than merely absent, the same way the home guard works.\n *\n * Read at call time, not at module load: the test setup file runs before any\n * module is imported, but resolving these eagerly would still bake in whatever\n * the environment held at import and make the override order matter.\n */\n\n/** IPC socket for the PAI daemon. */\nexport function paiSocketPath(): string {\n return process.env.PAI_SOCKET_PATH ?? \"/tmp/pai.sock\";\n}\n\n/** IPC socket for the AIBroker daemon — read by PAI, owned by AIBroker. */\nexport function aibrokerSocketPath(): string {\n return process.env.PAI_AIBROKER_SOCKET_PATH ?? \"/tmp/aibroker.sock\";\n}\n\n/** Where the daemon's stdout and stderr are collected. */\nexport function daemonLogPath(): string {\n return process.env.PAI_DAEMON_LOG_PATH ?? \"/tmp/pai-daemon.log\";\n}\n\n/** Pidfile for the running daemon. */\nexport function daemonPidPath(): string {\n return process.env.PAI_DAEMON_PID_PATH ?? \"/tmp/pai-daemon.pid\";\n}\n\n/** Where the launchd scheduler tick writes its output. */\nexport function schedulerLogPath(): string {\n return process.env.PAI_SCHEDULER_LOG_PATH ?? \"/tmp/pai-scheduler.log\";\n}\n\n/**\n * Every runtime path, for the test guard to redirect in one place.\n *\n * Listed here rather than in the guard so that adding a path and forgetting to\n * protect it is not possible: the guard iterates this.\n */\nexport const RUNTIME_PATH_ENV_VARS = [\n \"PAI_SOCKET_PATH\",\n \"PAI_AIBROKER_SOCKET_PATH\",\n \"PAI_DAEMON_LOG_PATH\",\n \"PAI_DAEMON_PID_PATH\",\n \"PAI_SCHEDULER_LOG_PATH\",\n] as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,gBAAwB;AACtC,QAAO,QAAQ,IAAI,mBAAmB;;;AAIxC,SAAgB,qBAA6B;AAC3C,QAAO,QAAQ,IAAI,4BAA4B;;;AAIjD,SAAgB,gBAAwB;AACtC,QAAO,QAAQ,IAAI,uBAAuB;;;AAI5C,SAAgB,gBAAwB;AACtC,QAAO,QAAQ,IAAI,uBAAuB;;;AAI5C,SAAgB,mBAA2B;AACzC,QAAO,QAAQ,IAAI,0BAA0B"}
@@ -1,11 +0,0 @@
1
- import "./utils-9Err2RBW.mjs";
2
- import "./migrate-Cjzeefn9.mjs";
3
- import "./db-a1ixZQjr.mjs";
4
- import "./embeddings-DOLZnT1X.mjs";
5
- import "./search-Rpk1cSBC.mjs";
6
- import "./context-handover-cache-BpUojjsi.mjs";
7
- import "./state-CAeyOdfq.mjs";
8
- import "./detector-Bwk_4Pk4.mjs";
9
- import { i as stopWorker, n as notifyNewWork, r as startWorker, t as enqueueRegistryScan } from "./work-queue-worker-R7UGIag_.mjs";
10
-
11
- export { enqueueRegistryScan };