airaknit 1.1.2-rc.9

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 (440) hide show
  1. package/LICENSE +84 -0
  2. package/README.md +202 -0
  3. package/bin/airaknit +9 -0
  4. package/bin/airaknit-project +14 -0
  5. package/bin/kanna +9 -0
  6. package/dist/client/assets/CompactSummaryMessage-Yw0BDWEJ.js +1 -0
  7. package/dist/client/assets/ExitPlanModeMessage-DIdkQ4uF.js +1 -0
  8. package/dist/client/assets/LocalFilePreviewDialog-DQx2eiCc.js +3 -0
  9. package/dist/client/assets/LocalProjectsSection-C4xlWkgS.js +1 -0
  10. package/dist/client/assets/TextMessage-B5G39DEJ.js +1 -0
  11. package/dist/client/assets/UserMessage-CIkWk-0L.js +1 -0
  12. package/dist/client/assets/_basePickBy-CVrAFfnZ.js +1 -0
  13. package/dist/client/assets/_baseUniq-JL-aaF4P.js +1 -0
  14. package/dist/client/assets/arc-B07zg7ol.js +1 -0
  15. package/dist/client/assets/architecture-YZFGNWBL-PSLVJL3p.js +1 -0
  16. package/dist/client/assets/architectureDiagram-Q4EWVU46-DfWIF1G_.js +36 -0
  17. package/dist/client/assets/array-BGFCBI0e.js +1 -0
  18. package/dist/client/assets/blockDiagram-DXYQGD6D-CzwIeo_B.js +132 -0
  19. package/dist/client/assets/bundle-mjs-BDE2gWbQ.js +1 -0
  20. package/dist/client/assets/button-DO50qOGv.js +1 -0
  21. package/dist/client/assets/c4Diagram-AHTNJAMY-CR8DCQRE.js +10 -0
  22. package/dist/client/assets/channel-Dj-UUfaF.js +1 -0
  23. package/dist/client/assets/chunk-2KRD3SAO-dznP-cn8.js +1 -0
  24. package/dist/client/assets/chunk-336JU56O-Dqss5Vu6.js +2 -0
  25. package/dist/client/assets/chunk-426QAEUC-CEKis_0_.js +1 -0
  26. package/dist/client/assets/chunk-4BX2VUAB-BYOv0Gm1.js +1 -0
  27. package/dist/client/assets/chunk-4TB4RGXK-BxzubH5S.js +206 -0
  28. package/dist/client/assets/chunk-55IACEB6-BSlTj03a.js +1 -0
  29. package/dist/client/assets/chunk-5FUZZQ4R-9Au93Bi1.js +62 -0
  30. package/dist/client/assets/chunk-5PVQY5BW-BhksHFEZ.js +2 -0
  31. package/dist/client/assets/chunk-67CJDMHE-BFwhz-8t.js +1 -0
  32. package/dist/client/assets/chunk-7N4EOEYR-BDUPds87.js +1 -0
  33. package/dist/client/assets/chunk-AA7GKIK3-CEWTdyXO.js +1 -0
  34. package/dist/client/assets/chunk-BO2N2NFS-D0LvxnhU.js +103 -0
  35. package/dist/client/assets/chunk-BSJP7CBP-BNJnK6sq.js +1 -0
  36. package/dist/client/assets/chunk-Bj-mKKzh.js +1 -0
  37. package/dist/client/assets/chunk-CIAEETIT-CYhfoCeN.js +1 -0
  38. package/dist/client/assets/chunk-EDXVE4YY-C5ovJLc0.js +1 -0
  39. package/dist/client/assets/chunk-ENJZ2VHE-HOhYaeGr.js +10 -0
  40. package/dist/client/assets/chunk-FMBD7UC4-BLCiKcAQ.js +15 -0
  41. package/dist/client/assets/chunk-FOC6F5B3-B6GtY2ek.js +1 -0
  42. package/dist/client/assets/chunk-ICPOFSXX-DPoIZoC5.js +122 -0
  43. package/dist/client/assets/chunk-K5T4RW27-BsKN63rv.js +94 -0
  44. package/dist/client/assets/chunk-KGLVRYIC-BUGn9uuY.js +1 -0
  45. package/dist/client/assets/chunk-LIHQZDEY-DhaZyo03.js +1 -0
  46. package/dist/client/assets/chunk-ORNJ4GCN-DlpeeJyi.js +1 -0
  47. package/dist/client/assets/chunk-OYMX7WX6-Dc9q7aYA.js +231 -0
  48. package/dist/client/assets/chunk-QZHKN3VN-BEdrPoSb.js +1 -0
  49. package/dist/client/assets/chunk-U2HBQHQK-CIB3Bjjd.js +70 -0
  50. package/dist/client/assets/chunk-X2U36JSP-CtB-o8Yp.js +1 -0
  51. package/dist/client/assets/chunk-XPW4576I-C6iHhX_8.js +32 -0
  52. package/dist/client/assets/chunk-YZCP3GAM-CTmKr6ZH.js +1 -0
  53. package/dist/client/assets/chunk-ZZ45TVLE-BgU8A2RF.js +1 -0
  54. package/dist/client/assets/classDiagram-6PBFFD2Q-Bqk5e679.js +1 -0
  55. package/dist/client/assets/classDiagram-v2-HSJHXN6E-6pSaZOkC.js +1 -0
  56. package/dist/client/assets/client-BrKWI4CM.js +1 -0
  57. package/dist/client/assets/client-CGgNRU9w.js +1 -0
  58. package/dist/client/assets/client-DMSLRzg9.js +6 -0
  59. package/dist/client/assets/clone-DWcL7whJ.js +1 -0
  60. package/dist/client/assets/cose-bilkent-S5V4N54A-CrV5wsV_.js +1 -0
  61. package/dist/client/assets/cytoscape.esm--aLzKuep.js +321 -0
  62. package/dist/client/assets/dagre-CuRxWcrj.js +1 -0
  63. package/dist/client/assets/dagre-KV5264BT-BIDiVnkA.js +4 -0
  64. package/dist/client/assets/defaultLocale-CRZydyG6.js +1 -0
  65. package/dist/client/assets/diagram-5BDNPKRD-i1kjKRCB.js +10 -0
  66. package/dist/client/assets/diagram-G4DWMVQ6-9ZSLuhbl.js +24 -0
  67. package/dist/client/assets/diagram-MMDJMWI5-B4_CUjgv.js +43 -0
  68. package/dist/client/assets/diagram-TYMM5635-Ct5eTGS8.js +24 -0
  69. package/dist/client/assets/dist-CuB4kiSK.js +1 -0
  70. package/dist/client/assets/erDiagram-SMLLAGMA-Cy38ercc.js +85 -0
  71. package/dist/client/assets/flowDiagram-DWJPFMVM-CZKuYl0V.js +162 -0
  72. package/dist/client/assets/ganttDiagram-T4ZO3ILL-DLPjCh7a.js +292 -0
  73. package/dist/client/assets/gitGraph-7Q5UKJZL-DqbrtEp9.js +1 -0
  74. package/dist/client/assets/gitGraphDiagram-UUTBAWPF-BoRBkDhQ.js +106 -0
  75. package/dist/client/assets/graphlib-BcQ6qlQh.js +1 -0
  76. package/dist/client/assets/highlighted-body-OFNGDK62-BEpBVDTX.js +1 -0
  77. package/dist/client/assets/index-CetCiuqP.js +105 -0
  78. package/dist/client/assets/index-N29Mip7A.css +1 -0
  79. package/dist/client/assets/info-OMHHGYJF-D98DRBJX.js +1 -0
  80. package/dist/client/assets/infoDiagram-42DDH7IO-BAcdTWbt.js +2 -0
  81. package/dist/client/assets/init-B8gtcn7T.js +1 -0
  82. package/dist/client/assets/isArrayLikeObject-D8SJFmkN.js +1 -0
  83. package/dist/client/assets/isEmpty-BF3YX5Jk.js +1 -0
  84. package/dist/client/assets/ishikawaDiagram-UXIWVN3A-Ynu2VKdC.js +70 -0
  85. package/dist/client/assets/journeyDiagram-VCZTEJTY-BjfhQaN3.js +139 -0
  86. package/dist/client/assets/jsx-runtime-CyI9ICYU.js +1 -0
  87. package/dist/client/assets/kanban-definition-6JOO6SKY-JLXH9zUJ.js +89 -0
  88. package/dist/client/assets/katex-B94qP8b6.js +265 -0
  89. package/dist/client/assets/lib--QVjyxmL.js +29 -0
  90. package/dist/client/assets/lib-B6rgJiZ9.js +1 -0
  91. package/dist/client/assets/line-DCrYfLBn.js +1 -0
  92. package/dist/client/assets/linear-_4upLmeo.js +1 -0
  93. package/dist/client/assets/mermaid-GHXKKRXX-rwJHYUmW.js +1 -0
  94. package/dist/client/assets/mermaid-parser.core-KZinfW8o.js +4 -0
  95. package/dist/client/assets/mermaid.core-QqY9gSNe.js +11 -0
  96. package/dist/client/assets/mindmap-definition-QFDTVHPH-TWgHDAzp.js +96 -0
  97. package/dist/client/assets/ordinal-CCj7PWgZ.js +1 -0
  98. package/dist/client/assets/packet-4T2RLAQJ-DEvfkn3F.js +1 -0
  99. package/dist/client/assets/path-DZF-JdEe.js +1 -0
  100. package/dist/client/assets/pie-ZZUOXDRM-72e6WVjb.js +1 -0
  101. package/dist/client/assets/pieDiagram-DEJITSTG-Cl8PCsoj.js +30 -0
  102. package/dist/client/assets/preload-helper-rov5CBGT.js +1 -0
  103. package/dist/client/assets/pty-client-DZ27IS00.js +1 -0
  104. package/dist/client/assets/ptyInstancesStore-D9ag7SYd.js +1 -0
  105. package/dist/client/assets/quadrantDiagram-34T5L4WZ-CHyVGp9E.js +7 -0
  106. package/dist/client/assets/radar-PYXPWWZC-Cp7xd_EY.js +1 -0
  107. package/dist/client/assets/react-CClhXMB2.js +1 -0
  108. package/dist/client/assets/react-Dd6D81m0.js +1 -0
  109. package/dist/client/assets/react-dom--G6_6fQ_.js +1 -0
  110. package/dist/client/assets/requirementDiagram-MS252O5E-DaanG2iM.js +84 -0
  111. package/dist/client/assets/rough.esm-BsmKo2S5.js +1 -0
  112. package/dist/client/assets/sankeyDiagram-XADWPNL6-B_fhLY36.js +10 -0
  113. package/dist/client/assets/sequenceDiagram-FGHM5R23-C5FNrveI.js +157 -0
  114. package/dist/client/assets/src-DeTlMJAU.js +1 -0
  115. package/dist/client/assets/stateDiagram-FHFEXIEX-nTTcdjjQ.js +1 -0
  116. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Dw0632j_.js +1 -0
  117. package/dist/client/assets/timeline-definition-GMOUNBTQ-DkQV1yP8.js +120 -0
  118. package/dist/client/assets/treeView-SZITEDCU-ZLIgC7_K.js +1 -0
  119. package/dist/client/assets/treemap-W4RFUUIX-BqaMbB6N.js +1 -0
  120. package/dist/client/assets/uiIdentityOverlay-Ba7GNj7m.js +1 -0
  121. package/dist/client/assets/vennDiagram-DHZGUBPP-DbZ2xgs6.js +34 -0
  122. package/dist/client/assets/wardley-RL74JXVD-DXQS8zf4.js +1 -0
  123. package/dist/client/assets/wardleyDiagram-NUSXRM2D-BzCJ6MAu.js +20 -0
  124. package/dist/client/assets/xychartDiagram-5P7HB3ND-BSlFecop.js +7 -0
  125. package/dist/client/favicon.png +0 -0
  126. package/dist/client/favicon.svg +15 -0
  127. package/dist/client/fonts/body-medium.woff2 +0 -0
  128. package/dist/client/fonts/body-regular-italic.woff2 +0 -0
  129. package/dist/client/fonts/body-regular.woff2 +0 -0
  130. package/dist/client/fonts/body-semibold.woff2 +0 -0
  131. package/dist/client/index.html +31 -0
  132. package/dist/client/manifest.webmanifest +12 -0
  133. package/dist/client/sw.js +32 -0
  134. package/package.json +122 -0
  135. package/src/nats/auth-callout/callout-config.test.ts +93 -0
  136. package/src/nats/auth-callout/callout-config.ts +109 -0
  137. package/src/nats/auth-callout/callout.integration.test.ts +332 -0
  138. package/src/nats/auth-callout/keys.ts +103 -0
  139. package/src/nats/auth-callout/responder.ts +241 -0
  140. package/src/nats/auth-callout/scope-policy.test.ts +159 -0
  141. package/src/nats/auth-callout/scope-policy.ts +210 -0
  142. package/src/nats/auth-callout/token.test.ts +163 -0
  143. package/src/nats/auth-callout/token.ts +157 -0
  144. package/src/nats/nats-daemon-callout.ts +194 -0
  145. package/src/nats/nats-daemon.test.ts +77 -0
  146. package/src/nats/nats-daemon.ts +50 -0
  147. package/src/nats/nats-token.test.ts +61 -0
  148. package/src/nats/nats-token.ts +59 -0
  149. package/src/runner/coordination-mcp-integration.test.ts +134 -0
  150. package/src/runner/nats-coordination-client.test.ts +49 -0
  151. package/src/runner/nats-coordination-client.ts +94 -0
  152. package/src/runner/runner-agent.test.ts +469 -0
  153. package/src/runner/runner-agent.ts +453 -0
  154. package/src/runner/runner-credential.test.ts +93 -0
  155. package/src/runner/runner-credential.ts +82 -0
  156. package/src/runner/runner-nats.test.ts +495 -0
  157. package/src/runner/runner-nats.ts +323 -0
  158. package/src/runner/runner-pair.test.ts +107 -0
  159. package/src/runner/runner-pair.ts +81 -0
  160. package/src/runner/runner.test.ts +135 -0
  161. package/src/runner/runner.ts +212 -0
  162. package/src/runner/turn-factories.test.ts +97 -0
  163. package/src/runner/turn-factories.ts +475 -0
  164. package/src/server/agent-config-journey.test.ts +106 -0
  165. package/src/server/agent.ts +8 -0
  166. package/src/server/auto-continue/auth-error-detector.ts +66 -0
  167. package/src/server/auto-continue/limit-detector.ts +194 -0
  168. package/src/server/bm25.test.ts +92 -0
  169. package/src/server/bm25.ts +101 -0
  170. package/src/server/chat-events-jetstream.test.ts +135 -0
  171. package/src/server/claude-harness.ts +360 -0
  172. package/src/server/claude-pty/agent-normalizers.ts +309 -0
  173. package/src/server/claude-pty/auth.test.ts +38 -0
  174. package/src/server/claude-pty/auth.ts +32 -0
  175. package/src/server/claude-pty/claude-session-registry.adapter.ts +81 -0
  176. package/src/server/claude-pty/claude-session-registry.test.ts +149 -0
  177. package/src/server/claude-pty/driver.test.ts +902 -0
  178. package/src/server/claude-pty/driver.ts +807 -0
  179. package/src/server/claude-pty/jsonl-path.adapter.ts +57 -0
  180. package/src/server/claude-pty/jsonl-path.test.ts +114 -0
  181. package/src/server/claude-pty/jsonl-to-event.test.ts +241 -0
  182. package/src/server/claude-pty/jsonl-to-event.ts +174 -0
  183. package/src/server/claude-pty/output-ring.test.ts +35 -0
  184. package/src/server/claude-pty/output-ring.ts +25 -0
  185. package/src/server/claude-pty/parity-matrix.test.ts +227 -0
  186. package/src/server/claude-pty/pid-registry.adapter.ts +135 -0
  187. package/src/server/claude-pty/pid-registry.test.ts +122 -0
  188. package/src/server/claude-pty/preflight/binary-fingerprint.adapter.ts +20 -0
  189. package/src/server/claude-pty/preflight/binary-fingerprint.test.ts +32 -0
  190. package/src/server/claude-pty/pty-instance-registry.test.ts +177 -0
  191. package/src/server/claude-pty/pty-instance-registry.ts +166 -0
  192. package/src/server/claude-pty/pty-memory-sampler.adapter.test.ts +103 -0
  193. package/src/server/claude-pty/pty-memory-sampler.adapter.ts +85 -0
  194. package/src/server/claude-pty/pty-process.adapter.ts +66 -0
  195. package/src/server/claude-pty/pty-process.test.ts +49 -0
  196. package/src/server/claude-pty/resolve-binary.adapter.ts +106 -0
  197. package/src/server/claude-pty/resolve-binary.test.ts +118 -0
  198. package/src/server/claude-pty/runtime-dir.adapter.ts +19 -0
  199. package/src/server/claude-pty/settings-writer.adapter.ts +27 -0
  200. package/src/server/claude-pty/settings-writer.test.ts +22 -0
  201. package/src/server/claude-pty/smoke-test-io.adapter.ts +28 -0
  202. package/src/server/claude-pty/smoke-test.test.ts +191 -0
  203. package/src/server/claude-pty/smoke-test.ts +185 -0
  204. package/src/server/claude-pty/subagent-orchestrator.ts +887 -0
  205. package/src/server/claude-pty/tool-callback.ts +274 -0
  206. package/src/server/claude-pty/tui-control.test.ts +272 -0
  207. package/src/server/claude-pty/tui-control.ts +182 -0
  208. package/src/server/claude-pty/tui-source.adapter.test.ts +360 -0
  209. package/src/server/claude-pty/tui-source.adapter.ts +343 -0
  210. package/src/server/claude-pty/tunnel-gateway.ts +12 -0
  211. package/src/server/claude-pty-mcp/canonical-args.ts +15 -0
  212. package/src/server/claude-pty-mcp/fs-stat.adapter.ts +8 -0
  213. package/src/server/claude-pty-mcp/history-primer.ts +90 -0
  214. package/src/server/claude-pty-mcp/http-server.adapter.ts +33 -0
  215. package/src/server/claude-pty-mcp/mcp-http.ts +177 -0
  216. package/src/server/claude-pty-mcp/mcp.ts +412 -0
  217. package/src/server/claude-pty-mcp/mention-parser.ts +25 -0
  218. package/src/server/claude-pty-mcp/paths.ts +24 -0
  219. package/src/server/claude-pty-mcp/permission-gate.ts +243 -0
  220. package/src/server/claude-pty-mcp/terminal-pid-registry.adapter.ts +107 -0
  221. package/src/server/claude-pty-mcp/tools/ask-user-question.test.ts +119 -0
  222. package/src/server/claude-pty-mcp/tools/ask-user-question.ts +61 -0
  223. package/src/server/claude-pty-mcp/tools/bash.adapter.ts +76 -0
  224. package/src/server/claude-pty-mcp/tools/bash.test.ts +56 -0
  225. package/src/server/claude-pty-mcp/tools/delegate-subagent.test.ts +155 -0
  226. package/src/server/claude-pty-mcp/tools/delegate-subagent.ts +111 -0
  227. package/src/server/claude-pty-mcp/tools/edit.adapter.ts +95 -0
  228. package/src/server/claude-pty-mcp/tools/edit.test.ts +93 -0
  229. package/src/server/claude-pty-mcp/tools/exit-plan-mode.test.ts +61 -0
  230. package/src/server/claude-pty-mcp/tools/exit-plan-mode.ts +50 -0
  231. package/src/server/claude-pty-mcp/tools/glob.adapter.ts +86 -0
  232. package/src/server/claude-pty-mcp/tools/glob.test.ts +61 -0
  233. package/src/server/claude-pty-mcp/tools/grep.adapter.ts +126 -0
  234. package/src/server/claude-pty-mcp/tools/grep.test.ts +62 -0
  235. package/src/server/claude-pty-mcp/tools/read.adapter.ts +58 -0
  236. package/src/server/claude-pty-mcp/tools/read.test.ts +62 -0
  237. package/src/server/claude-pty-mcp/tools/tool-callback-shim.ts +42 -0
  238. package/src/server/claude-pty-mcp/tools/webfetch.test.ts +81 -0
  239. package/src/server/claude-pty-mcp/tools/webfetch.ts +82 -0
  240. package/src/server/claude-pty-mcp/tools/websearch.test.ts +40 -0
  241. package/src/server/claude-pty-mcp/tools/websearch.ts +42 -0
  242. package/src/server/claude-pty-mcp/tools/write.adapter.ts +60 -0
  243. package/src/server/claude-pty-mcp/tools/write.test.ts +52 -0
  244. package/src/server/claude-pty-mcp/uploads.adapter.ts +98 -0
  245. package/src/server/claude-pty-mcp/uploads.ts +38 -0
  246. package/src/server/claude-turn.test.ts +176 -0
  247. package/src/server/cli-runtime.test.ts +456 -0
  248. package/src/server/cli-runtime.ts +374 -0
  249. package/src/server/cli-supervisor.ts +81 -0
  250. package/src/server/cli.ts +78 -0
  251. package/src/server/client-log-forwarder.test.ts +74 -0
  252. package/src/server/client-log-forwarder.ts +75 -0
  253. package/src/server/codex-app-server-protocol.ts +449 -0
  254. package/src/server/codex-app-server.test.ts +2990 -0
  255. package/src/server/codex-app-server.ts +1713 -0
  256. package/src/server/coordination-integration.test.ts +63 -0
  257. package/src/server/coordination-mcp.test.ts +149 -0
  258. package/src/server/coordination-mcp.ts +197 -0
  259. package/src/server/delegation-coordinator.test.ts +675 -0
  260. package/src/server/delegation-coordinator.ts +454 -0
  261. package/src/server/discovery.test.ts +211 -0
  262. package/src/server/discovery.ts +301 -0
  263. package/src/server/event-store-agent-config.test.ts +124 -0
  264. package/src/server/event-store-coordination.test.ts +149 -0
  265. package/src/server/event-store-profile.test.ts +132 -0
  266. package/src/server/event-store-repo.test.ts +154 -0
  267. package/src/server/event-store-runner-team.test.ts +104 -0
  268. package/src/server/event-store.test.ts +342 -0
  269. package/src/server/event-store.ts +2208 -0
  270. package/src/server/events.ts +379 -0
  271. package/src/server/extension-router.test.ts +183 -0
  272. package/src/server/extension-router.ts +114 -0
  273. package/src/server/extensions/agents/server.test.ts +191 -0
  274. package/src/server/extensions/agents/server.ts +108 -0
  275. package/src/server/extensions/c3/server.test.ts +284 -0
  276. package/src/server/extensions/c3/server.ts +212 -0
  277. package/src/server/extensions/code/server.test.ts +200 -0
  278. package/src/server/extensions/code/server.ts +150 -0
  279. package/src/server/extensions.config.ts +10 -0
  280. package/src/server/external-open.ts +69 -0
  281. package/src/server/generate-fork-context.ts +58 -0
  282. package/src/server/generate-merge-context.test.ts +290 -0
  283. package/src/server/generate-merge-context.ts +141 -0
  284. package/src/server/generate-title.ts +36 -0
  285. package/src/server/git-clone-policy.test.ts +138 -0
  286. package/src/server/git-clone-policy.ts +27 -0
  287. package/src/server/harness-types.ts +1 -0
  288. package/src/server/journey-verification.test.ts +640 -0
  289. package/src/server/journey-verification.ts +195 -0
  290. package/src/server/machine-name.ts +22 -0
  291. package/src/server/nats-auth.test.ts +92 -0
  292. package/src/server/nats-auth.ts +6 -0
  293. package/src/server/nats-bind-guard.test.ts +71 -0
  294. package/src/server/nats-bind-guard.ts +42 -0
  295. package/src/server/nats-bridge.test.ts +141 -0
  296. package/src/server/nats-bridge.ts +111 -0
  297. package/src/server/nats-connector.test.ts +56 -0
  298. package/src/server/nats-connector.ts +71 -0
  299. package/src/server/nats-daemon-manager.test.ts +76 -0
  300. package/src/server/nats-daemon-manager.ts +174 -0
  301. package/src/server/nats-publisher.test.ts +356 -0
  302. package/src/server/nats-publisher.ts +271 -0
  303. package/src/server/nats-responders.test.ts +1018 -0
  304. package/src/server/nats-responders.ts +925 -0
  305. package/src/server/nats-streams.test.ts +112 -0
  306. package/src/server/nats-streams.ts +129 -0
  307. package/src/server/oauth-pool/oauth-responders.ts +185 -0
  308. package/src/server/oauth-pool/oauth-settings-store.ts +173 -0
  309. package/src/server/oauth-pool/oauth-token-pool.ts +303 -0
  310. package/src/server/orchestration.test.ts +1063 -0
  311. package/src/server/orchestration.ts +716 -0
  312. package/src/server/pairing-endpoints.test.ts +171 -0
  313. package/src/server/pairing-store.test.ts +154 -0
  314. package/src/server/pairing-store.ts +124 -0
  315. package/src/server/paths.ts +27 -0
  316. package/src/server/pr3-liveness.test.ts +252 -0
  317. package/src/server/process-utils.ts +10 -0
  318. package/src/server/project-cli.ts +180 -0
  319. package/src/server/provider-catalog.test.ts +177 -0
  320. package/src/server/provider-catalog.ts +146 -0
  321. package/src/server/pty-responders.ts +345 -0
  322. package/src/server/push-notifications.test.ts +234 -0
  323. package/src/server/push-notifications.ts +188 -0
  324. package/src/server/quick-response.test.ts +196 -0
  325. package/src/server/quick-response.ts +154 -0
  326. package/src/server/read-models-agent-config.test.ts +59 -0
  327. package/src/server/read-models-coordination.test.ts +69 -0
  328. package/src/server/read-models.test.ts +332 -0
  329. package/src/server/read-models.ts +258 -0
  330. package/src/server/repo-journey.test.ts +96 -0
  331. package/src/server/repo-manager.test.ts +118 -0
  332. package/src/server/repo-manager.ts +97 -0
  333. package/src/server/repo-status.test.ts +54 -0
  334. package/src/server/repo-status.ts +82 -0
  335. package/src/server/restart.test.ts +27 -0
  336. package/src/server/restart.ts +30 -0
  337. package/src/server/runner-incompatible-gate.test.ts +383 -0
  338. package/src/server/runner-manager.test.ts +72 -0
  339. package/src/server/runner-manager.ts +312 -0
  340. package/src/server/runner-pairing-urls.test.ts +59 -0
  341. package/src/server/runner-pairing-urls.ts +67 -0
  342. package/src/server/runner-proxy.test.ts +456 -0
  343. package/src/server/runner-proxy.ts +494 -0
  344. package/src/server/runner-router.test.ts +429 -0
  345. package/src/server/runner-router.ts +212 -0
  346. package/src/server/runner-routing.test.ts +584 -0
  347. package/src/server/runtime-registry.test.ts +436 -0
  348. package/src/server/runtime-registry.ts +307 -0
  349. package/src/server/sandbox-health.test.ts +127 -0
  350. package/src/server/sandbox-health.ts +94 -0
  351. package/src/server/sandbox-journey.test.ts +232 -0
  352. package/src/server/sandbox-manager.test.ts +146 -0
  353. package/src/server/sandbox-manager.ts +159 -0
  354. package/src/server/server.test.ts +287 -0
  355. package/src/server/server.ts +1108 -0
  356. package/src/server/session-discovery.test.ts +129 -0
  357. package/src/server/session-discovery.ts +475 -0
  358. package/src/server/session-index.test.ts +362 -0
  359. package/src/server/session-index.ts +119 -0
  360. package/src/server/session-seed.ts +288 -0
  361. package/src/server/share.test.ts +108 -0
  362. package/src/server/share.ts +113 -0
  363. package/src/server/skill-discovery.test.ts +201 -0
  364. package/src/server/skill-discovery.ts +77 -0
  365. package/src/server/storage/test-helpers.ts +67 -0
  366. package/src/server/terminal-manager.test.ts +309 -0
  367. package/src/server/terminal-manager.ts +354 -0
  368. package/src/server/transcript-consumer.test.ts +339 -0
  369. package/src/server/transcript-consumer.ts +162 -0
  370. package/src/server/transcript-search.test.ts +193 -0
  371. package/src/server/transcript-search.ts +83 -0
  372. package/src/server/transcript-utils.ts +52 -0
  373. package/src/server/update-manager.test.ts +107 -0
  374. package/src/server/update-manager.ts +230 -0
  375. package/src/server/workflow-engine.test.ts +251 -0
  376. package/src/server/workflow-engine.ts +169 -0
  377. package/src/server/workflow-mcp.ts +49 -0
  378. package/src/server/workflow-store.test.ts +155 -0
  379. package/src/server/workflow-store.ts +139 -0
  380. package/src/server/workspace-agent-integration.test.ts +167 -0
  381. package/src/server/workspace-agent-routes.test.ts +127 -0
  382. package/src/server/workspace-agent-routes.ts +89 -0
  383. package/src/server/workspace-agent.test.ts +103 -0
  384. package/src/server/workspace-agent.ts +102 -0
  385. package/src/server/workspace-cli.test.ts +79 -0
  386. package/src/server/workspace-config-manager.test.ts +109 -0
  387. package/src/server/workspace-config-manager.ts +83 -0
  388. package/src/server/workspace-directory-policy.test.ts +109 -0
  389. package/src/server/workspace-directory-policy.ts +56 -0
  390. package/src/shared/agent-config-types.ts +25 -0
  391. package/src/shared/branding.test.ts +42 -0
  392. package/src/shared/branding.ts +54 -0
  393. package/src/shared/compression.test.ts +85 -0
  394. package/src/shared/compression.ts +42 -0
  395. package/src/shared/coordination-store.test.ts +24 -0
  396. package/src/shared/coordination-store.ts +26 -0
  397. package/src/shared/dev-ports.test.ts +84 -0
  398. package/src/shared/dev-ports.ts +100 -0
  399. package/src/shared/extension-types.ts +45 -0
  400. package/src/shared/fork-presets.ts +54 -0
  401. package/src/shared/harness-types.test.ts +15 -0
  402. package/src/shared/harness-types.ts +21 -0
  403. package/src/shared/log-sink.test.ts +112 -0
  404. package/src/shared/log-sink.ts +185 -0
  405. package/src/shared/mention-pattern.ts +7 -0
  406. package/src/shared/merge-presets.ts +41 -0
  407. package/src/shared/nats-subjects.test.ts +61 -0
  408. package/src/shared/nats-subjects.ts +131 -0
  409. package/src/shared/permission-policy.ts +136 -0
  410. package/src/shared/ports.ts +3 -0
  411. package/src/shared/preset-types.ts +15 -0
  412. package/src/shared/profile-types.ts +49 -0
  413. package/src/shared/projectFileUrl.ts +36 -0
  414. package/src/shared/protocol.ts +153 -0
  415. package/src/shared/pty-instance.ts +43 -0
  416. package/src/shared/puggy/diagnostics/result.ts +18 -0
  417. package/src/shared/puggy/expressions/evaluate.ts +292 -0
  418. package/src/shared/puggy/index.test.ts +101 -0
  419. package/src/shared/puggy/index.ts +69 -0
  420. package/src/shared/puggy/parser/ast.ts +110 -0
  421. package/src/shared/puggy/parser/parser.ts +624 -0
  422. package/src/shared/puggy/renderer/html.ts +447 -0
  423. package/src/shared/runner-protocol.test.ts +277 -0
  424. package/src/shared/runner-protocol.ts +210 -0
  425. package/src/shared/runner-team-types.ts +73 -0
  426. package/src/shared/runtime-types.ts +48 -0
  427. package/src/shared/sandbox-types.ts +53 -0
  428. package/src/shared/tailwind-build.test.ts +12 -0
  429. package/src/shared/tinkaria-system-prompt.ts +101 -0
  430. package/src/shared/tools.test.ts +335 -0
  431. package/src/shared/tools.ts +397 -0
  432. package/src/shared/transcript-entries.ts +27 -0
  433. package/src/shared/transcript-render.test.ts +225 -0
  434. package/src/shared/transcript-render.ts +467 -0
  435. package/src/shared/types.ts +1031 -0
  436. package/src/shared/vite-config.test.ts +47 -0
  437. package/src/shared/web-context.test.ts +110 -0
  438. package/src/shared/web-context.ts +76 -0
  439. package/src/shared/workflow-types.ts +51 -0
  440. package/src/shared/workspace-types.ts +100 -0
@@ -0,0 +1,475 @@
1
+ /**
2
+ * Turn factory wrappers for the runner process.
3
+ *
4
+ * Re-exports startClaudeTurn from claude-harness.ts and provides
5
+ * startCodexTurn / startClaudePtyTurn so the runner can create harness turns
6
+ * for all providers.
7
+ *
8
+ * Lifecycle ownership for claude-pty:
9
+ * • `claudePtySessions` — module-level Map<chatId, ClaudePtySession>.
10
+ * Mirrors kanna's `claudeSessions` map. PTY handle persists across
11
+ * turns so the live `claude` CLI keeps in-memory context.
12
+ * • `OAuthTokenPool` — single instance constructed in `runner.ts` and
13
+ * handed to the factory at boot. `pickActive(chatId)` reserves a token
14
+ * for the chat; `release(chatId)` drops it on turn end / session close.
15
+ * `markLimited` / `markError` rotate the token on rate_limit / stream
16
+ * error events. Mirrors kanna's `AgentCoordinator` rotation discipline.
17
+ * • `ClaudePtyRegistry` — on-disk pidfile registry. Driver writes its pid
18
+ * + runtimeDir before sending the first prompt and unregisters during
19
+ * cleanup. Boot's `reapStale()` SIGKILLs orphans from a previous crash.
20
+ * • Idle sweeper — `sweepIdleClaudePtySessions(now, idleMs)` closes any
21
+ * session whose `lastUsedAt` is past the TTL. Scheduled from runner.ts
22
+ * so the runner controls cadence + can `unref()` the timer.
23
+ */
24
+
25
+ export { startClaudeTurn } from "../server/claude-harness"
26
+
27
+ import { CodexAppServerManager } from "../server/codex-app-server"
28
+ import type { HarnessToolRequest, HarnessTurn } from "../shared/harness-types"
29
+ import type { CodexReasoningEffort, ServiceTier } from "../shared/types"
30
+ import { startClaudeSessionPTY, type PtyUsageSample } from "../server/claude-pty/driver"
31
+ import { ptyDeltaSubject } from "../shared/nats-subjects"
32
+ import { compressPayload } from "../shared/compression"
33
+ import type { NatsConnection } from "@nats-io/transport-node"
34
+ import type { PtyInstanceDelta, PtyInstanceState } from "../shared/pty-instance"
35
+ import type { HarnessEvent } from "../shared/harness-types"
36
+ import type { ClaudeSessionHandle } from "../server/claude-pty/agent-normalizers"
37
+ import type { OAuthTokenPool } from "../server/oauth-pool/oauth-token-pool"
38
+ import type { ClaudePtyRegistry } from "../server/claude-pty/pid-registry.adapter"
39
+ import { spawnSync } from "node:child_process"
40
+
41
+ const ptyDeltaEncoder = new TextEncoder()
42
+ function encodeDelta(delta: PtyInstanceDelta): Uint8Array {
43
+ return compressPayload(ptyDeltaEncoder.encode(JSON.stringify(delta)))
44
+ }
45
+
46
+ interface ClaudePtySession {
47
+ chatId: string
48
+ handle: ClaudeSessionHandle
49
+ baseInstance: PtyInstanceState
50
+ currentTurn: TurnDispatcher | null
51
+ turnCount: number
52
+ /** Pool reservation id; null when no pool was available (test/fake paths). */
53
+ activeTokenId: string | null
54
+ /** Timestamp of the last turn end (or session start). Used by idle sweeper. */
55
+ lastUsedAt: number
56
+ publishDelta: (delta: PtyInstanceDelta) => void
57
+ /** Stops further onUsageSample deltas (called once the session is torn down). */
58
+ stopUsage: () => void
59
+ }
60
+
61
+ interface TurnDispatcher {
62
+ push(event: HarnessEvent): void
63
+ end(): void
64
+ stream: AsyncIterable<HarnessEvent>
65
+ }
66
+
67
+ function createTurnDispatcher(): TurnDispatcher {
68
+ const queue: HarnessEvent[] = []
69
+ const waiters: Array<(r: IteratorResult<HarnessEvent>) => void> = []
70
+ let ended = false
71
+
72
+ function push(event: HarnessEvent): void {
73
+ if (ended) return
74
+ const waiter = waiters.shift()
75
+ if (waiter) waiter({ value: event, done: false })
76
+ else queue.push(event)
77
+ }
78
+
79
+ function end(): void {
80
+ if (ended) return
81
+ ended = true
82
+ while (waiters.length > 0) {
83
+ const waiter = waiters.shift()
84
+ if (waiter) waiter({ value: undefined as unknown as HarnessEvent, done: true })
85
+ }
86
+ }
87
+
88
+ const stream: AsyncIterable<HarnessEvent> = {
89
+ [Symbol.asyncIterator]() {
90
+ return {
91
+ next(): Promise<IteratorResult<HarnessEvent>> {
92
+ if (queue.length > 0) {
93
+ const value = queue.shift() as HarnessEvent
94
+ return Promise.resolve({ value, done: false })
95
+ }
96
+ if (ended) return Promise.resolve({ value: undefined as unknown as HarnessEvent, done: true })
97
+ return new Promise((resolve) => waiters.push(resolve))
98
+ },
99
+ }
100
+ },
101
+ }
102
+
103
+ return { push, end, stream }
104
+ }
105
+
106
+ const claudePtySessions = new Map<string, ClaudePtySession>()
107
+
108
+ // Single pool + registry instance shared across all turns. Set by runner.ts at boot.
109
+ let sharedPool: OAuthTokenPool | null = null
110
+ let sharedRegistry: ClaudePtyRegistry | null = null
111
+
112
+ export function configureClaudePtyFactory(args: {
113
+ pool: OAuthTokenPool | null
114
+ registry: ClaudePtyRegistry | null
115
+ }): void {
116
+ sharedPool = args.pool
117
+ sharedRegistry = args.registry
118
+ }
119
+
120
+ function startSessionReader(session: ClaudePtySession): void {
121
+ void (async () => {
122
+ // `poolRotated` mirrors pty-responders semantics: when rate_limit /
123
+ // stream error marks the token limited/errored, the pool's
124
+ // `takeStaleOwners` already drops the reservation for this chat. The
125
+ // finally{} block must skip its plain `release(chatId)` in that case
126
+ // so it does not touch unrelated tokens (audit #9d).
127
+ let poolRotated = false
128
+ try {
129
+ for await (const event of session.handle.stream) {
130
+ if (event.type === "rate_limit" && event.rateLimit) {
131
+ console.log("[claude-pty/runner] rate_limit chatId=" + session.chatId, event.rateLimit)
132
+ if (session.activeTokenId && sharedPool) {
133
+ try {
134
+ const stale = sharedPool.takeStaleOwners(session.activeTokenId)
135
+ sharedPool.markLimited(session.activeTokenId, event.rateLimit.resetAt)
136
+ poolRotated = true
137
+ console.log(
138
+ "[claude-pty/runner] pool token " + session.activeTokenId
139
+ + " marked limited until " + event.rateLimit.resetAt
140
+ + "; stale owners=" + stale.length,
141
+ )
142
+ } catch (err) {
143
+ console.warn(
144
+ "[claude-pty/runner] markLimited failed token=" + session.activeTokenId,
145
+ err instanceof Error ? err.message : String(err),
146
+ )
147
+ }
148
+ }
149
+ }
150
+ session.currentTurn?.push(event)
151
+ const entry = (event as { entry?: { kind?: string } }).entry
152
+ if (entry?.kind === "result") {
153
+ session.lastUsedAt = Date.now()
154
+ session.currentTurn?.end()
155
+ session.currentTurn = null
156
+ }
157
+ }
158
+ } catch (err) {
159
+ const message = err instanceof Error ? err.message : String(err)
160
+ console.warn("[claude-pty/runner] session reader error:", message)
161
+ if (session.activeTokenId && sharedPool) {
162
+ try {
163
+ const stale = sharedPool.takeStaleOwners(session.activeTokenId)
164
+ sharedPool.markError(session.activeTokenId, message)
165
+ poolRotated = true
166
+ console.log(
167
+ "[claude-pty/runner] pool token " + session.activeTokenId
168
+ + " marked error; stale owners=" + stale.length,
169
+ )
170
+ } catch (markErr) {
171
+ console.warn(
172
+ "[claude-pty/runner] markError failed token=" + session.activeTokenId,
173
+ markErr instanceof Error ? markErr.message : String(markErr),
174
+ )
175
+ }
176
+ }
177
+ } finally {
178
+ session.stopUsage()
179
+ session.currentTurn?.end()
180
+ if (claudePtySessions.get(session.chatId) === session) {
181
+ claudePtySessions.delete(session.chatId)
182
+ }
183
+ if (!poolRotated && session.activeTokenId && sharedPool) {
184
+ try { sharedPool.release(session.chatId) } catch { /* swallow */ }
185
+ }
186
+ session.publishDelta({ type: "removed", chatId: session.chatId })
187
+ }
188
+ })()
189
+ }
190
+
191
+ export function stopClaudePtySession(chatId: string): void {
192
+ const session = claudePtySessions.get(chatId)
193
+ if (!session) return
194
+ try { session.handle.close() } catch { /* swallow */ }
195
+ // Map entry + pool release will be cleared by the background reader's
196
+ // finally{} block once the stream observes the close. Delete eagerly so
197
+ // a racing `start_turn` for the same chatId does not reuse this handle.
198
+ claudePtySessions.delete(chatId)
199
+ }
200
+
201
+ export function stopAllClaudePtySessions(): void {
202
+ for (const [, session] of claudePtySessions) {
203
+ try { session.handle.close() } catch { /* swallow */ }
204
+ }
205
+ claudePtySessions.clear()
206
+ }
207
+
208
+ /** Test-only inspector for the live session map. */
209
+ export function getClaudePtySessionChatIds(): string[] {
210
+ return [...claudePtySessions.keys()]
211
+ }
212
+
213
+ /**
214
+ * Close any session whose `lastUsedAt` is older than `idleMs` and has no
215
+ * active turn. Mirrors kanna's `sweepIdleClaudeSessions`. Runs from a
216
+ * setInterval in runner.ts; the runner owns timer cadence.
217
+ */
218
+ export function sweepIdleClaudePtySessions(now: number, idleMs: number): number {
219
+ let closed = 0
220
+ for (const [chatId, session] of [...claudePtySessions.entries()]) {
221
+ if (session.currentTurn !== null) continue
222
+ if (now - session.lastUsedAt < idleMs) continue
223
+ console.log("[claude-pty/runner] idle TTL eviction chatId=" + chatId + " idleMs=" + (now - session.lastUsedAt))
224
+ try { session.handle.close() } catch { /* swallow */ }
225
+ claudePtySessions.delete(chatId)
226
+ closed += 1
227
+ }
228
+ return closed
229
+ }
230
+
231
+ let codexManager: CodexAppServerManager | null = null
232
+
233
+ /**
234
+ * Resolve the codex binary runner-side by checking PATH, then falling back to
235
+ * the well-known `codex` name (lets the shell find it via the runner's PATH).
236
+ * Always runner-local — never relies on a server-sent binaryPath.
237
+ */
238
+ function resolveCodexBinary(extraEnv?: Record<string, string>): string {
239
+ // Runner-local resolution: NEVER let a server-supplied PATH redirect which
240
+ // binary we run (security M1) — strip PATH so the runner's own PATH decides.
241
+ const { PATH: _serverPath, ...safeEnv } = extraEnv ?? {}
242
+ const env = { ...process.env, ...safeEnv }
243
+ const which = spawnSync("which", ["codex"], { encoding: "utf-8", timeout: 3000, env })
244
+ if (which.status === 0) {
245
+ const p = which.stdout.trim()
246
+ if (p) return p
247
+ }
248
+ // Fall back: let the OS resolve it at spawn time
249
+ return "codex"
250
+ }
251
+
252
+ // NOTE: the codex manager is a once-per-runner-lifetime singleton — the first
253
+ // turn's (PATH-stripped) extraEnv fixes the manager. The binary is always
254
+ // runner-local (PATH stripped above), so this cannot be steered by the server.
255
+ function getCodexManager(extraEnv?: Record<string, string>): CodexAppServerManager {
256
+ if (!codexManager) {
257
+ // Strip server-supplied PATH from the spawned process env too (M1).
258
+ const { PATH: _serverPath, ...safeEnv } = extraEnv ?? {}
259
+ const binaryPath = resolveCodexBinary(safeEnv)
260
+ codexManager = new CodexAppServerManager({ binaryPath, extraEnv: safeEnv })
261
+ }
262
+ return codexManager
263
+ }
264
+
265
+ export async function startCodexTurn(args: {
266
+ chatId: string
267
+ content: string
268
+ localPath: string
269
+ model: string
270
+ effort?: string
271
+ serviceTier?: "fast"
272
+ planMode: boolean
273
+ sessionToken: string | null
274
+ onToolRequest: (request: HarnessToolRequest) => Promise<unknown>
275
+ extraEnv?: Record<string, string>
276
+ }): Promise<HarnessTurn> {
277
+ const manager = getCodexManager(args.extraEnv)
278
+
279
+ await manager.startSession({
280
+ chatId: args.chatId,
281
+ cwd: args.localPath,
282
+ model: args.model,
283
+ serviceTier: args.serviceTier as ServiceTier | undefined,
284
+ sessionToken: args.sessionToken,
285
+ })
286
+
287
+ return await manager.startTurn({
288
+ chatId: args.chatId,
289
+ content: args.content,
290
+ model: args.model,
291
+ effort: args.effort as CodexReasoningEffort | undefined,
292
+ serviceTier: args.serviceTier as ServiceTier | undefined,
293
+ planMode: args.planMode,
294
+ onToolRequest: args.onToolRequest,
295
+ })
296
+ }
297
+
298
+ function buildPoolUnavailableMessage(reservedFor: string): string {
299
+ if (!sharedPool) return "OAuth pool is not configured on the runner."
300
+ if (!sharedPool.hasAnyToken()) {
301
+ return "No OAuth pool tokens configured. Add one under Settings → Claude PTY."
302
+ }
303
+ const summary = sharedPool
304
+ .describeUnavailability(reservedFor)
305
+ .filter((u) => u.reason !== "available")
306
+ .map((u) => (u.label || u.tokenId.slice(0, 8)) + ": " + u.reason)
307
+ .join("; ")
308
+ return "No usable OAuth pool token (" + (summary || "all tokens unavailable") + ")."
309
+ }
310
+
311
+ export async function startClaudePtyTurn(args: {
312
+ chatId: string
313
+ content: string
314
+ projectId?: string
315
+ localPath: string
316
+ model: string
317
+ effort?: string
318
+ planMode: boolean
319
+ sessionToken: string | null
320
+ onToolRequest: (request: HarnessToolRequest) => Promise<unknown>
321
+ nc?: NatsConnection
322
+ }): Promise<HarnessTurn> {
323
+ let session = claudePtySessions.get(args.chatId)
324
+
325
+ if (!session) {
326
+ if (!sharedPool) {
327
+ throw new Error("Claude PTY runner is not wired to an OAuthTokenPool. Configure via configureClaudePtyFactory().")
328
+ }
329
+ if (!sharedPool.hasAnyToken()) {
330
+ throw new Error("No OAuth pool tokens configured. Add one under Settings → Claude PTY.")
331
+ }
332
+ const picked = sharedPool.pickActive(args.chatId)
333
+ if (!picked) {
334
+ throw new Error(buildPoolUnavailableMessage(args.chatId))
335
+ }
336
+ sharedPool.markUsed(picked.id)
337
+
338
+ const now = Date.now()
339
+ const baseInstance: PtyInstanceState = {
340
+ chatId: args.chatId,
341
+ sessionId: null,
342
+ pid: null,
343
+ cwd: args.localPath,
344
+ model: args.model,
345
+ accountLabel: picked.label,
346
+ oauthMasked: null,
347
+ phase: "spawning",
348
+ startedAt: now,
349
+ lastEventAt: now,
350
+ turnCount: 0,
351
+ tokensIn: 0,
352
+ tokensOut: 0,
353
+ planMode: args.planMode,
354
+ smokeTest: null,
355
+ outputRingTail: null,
356
+ exitedAt: null,
357
+ exitCode: null,
358
+ rssBytes: null,
359
+ rssPeakBytes: null,
360
+ cpuPercent: null,
361
+ cpuPeakPercent: null,
362
+ }
363
+
364
+ function publishDelta(delta: PtyInstanceDelta): void {
365
+ if (!args.nc) return
366
+ try {
367
+ args.nc.publish(ptyDeltaSubject(), encodeDelta(delta))
368
+ } catch (err) {
369
+ console.warn("[claude-pty/runner] pty.delta publish failed:", err instanceof Error ? err.message : String(err))
370
+ }
371
+ }
372
+
373
+ publishDelta({ type: "added", instance: baseInstance })
374
+
375
+ // `baseInstance` is the canonical, mutable record for this session: phase
376
+ // transitions and usage samples both write into it so every published
377
+ // delta stays internally coherent. `usageStopped` suppresses late sampler
378
+ // ticks (spawn-failure or post-teardown) that would otherwise resurrect a
379
+ // removed instance in the client store.
380
+ let usageStopped = false
381
+ const onUsageSample = (usage: PtyUsageSample): void => {
382
+ if (usageStopped) return
383
+ baseInstance.rssBytes = usage.rssBytes
384
+ baseInstance.rssPeakBytes = usage.rssPeakBytes
385
+ baseInstance.cpuPercent = usage.cpuPercent
386
+ baseInstance.cpuPeakPercent = usage.cpuPeakPercent
387
+ baseInstance.lastEventAt = Date.now()
388
+ publishDelta({ type: "updated", instance: { ...baseInstance } })
389
+ }
390
+
391
+ let handle: ClaudeSessionHandle
392
+ try {
393
+ handle = await startClaudeSessionPTY({
394
+ chatId: args.chatId,
395
+ projectId: args.projectId ?? args.chatId,
396
+ localPath: args.localPath,
397
+ model: args.model,
398
+ effort: args.effort,
399
+ planMode: args.planMode,
400
+ sessionToken: args.sessionToken,
401
+ forkSession: false,
402
+ oauthToken: picked.token,
403
+ oauthLabel: picked.label,
404
+ ptyRegistry: sharedRegistry ?? undefined,
405
+ onUsageSample,
406
+ onToolRequest: args.onToolRequest as never,
407
+ })
408
+ } catch (err) {
409
+ usageStopped = true
410
+ publishDelta({ type: "removed", chatId: args.chatId })
411
+ try { sharedPool.release(args.chatId) } catch { /* swallow */ }
412
+ throw err
413
+ }
414
+
415
+ baseInstance.phase = "ready"
416
+ baseInstance.lastEventAt = Date.now()
417
+ publishDelta({ type: "updated", instance: { ...baseInstance } })
418
+
419
+ session = {
420
+ chatId: args.chatId,
421
+ handle,
422
+ baseInstance,
423
+ currentTurn: null,
424
+ turnCount: 0,
425
+ activeTokenId: picked.id,
426
+ lastUsedAt: Date.now(),
427
+ publishDelta,
428
+ stopUsage: () => { usageStopped = true },
429
+ }
430
+ claudePtySessions.set(args.chatId, session)
431
+ startSessionReader(session)
432
+ }
433
+
434
+ const dispatcher = createTurnDispatcher()
435
+ session.currentTurn = dispatcher
436
+ session.turnCount += 1
437
+ session.lastUsedAt = Date.now()
438
+ session.baseInstance.phase = "streaming"
439
+ session.baseInstance.lastEventAt = Date.now()
440
+ session.baseInstance.turnCount = session.turnCount
441
+ session.publishDelta({ type: "updated", instance: { ...session.baseInstance } })
442
+
443
+ await session.handle.sendPrompt(args.content)
444
+
445
+ return {
446
+ provider: "claude-pty",
447
+ stream: dispatcher.stream,
448
+ interrupt: async () => {
449
+ try {
450
+ await session!.handle.interrupt()
451
+ } finally {
452
+ dispatcher.end()
453
+ if (session!.currentTurn === dispatcher) {
454
+ session!.currentTurn = null
455
+ session!.lastUsedAt = Date.now()
456
+ }
457
+ }
458
+ },
459
+ close: () => {
460
+ dispatcher.end()
461
+ if (session!.currentTurn === dispatcher) {
462
+ session!.currentTurn = null
463
+ session!.lastUsedAt = Date.now()
464
+ }
465
+ },
466
+ }
467
+ }
468
+
469
+ export function stopCodexSession(chatId: string): void {
470
+ codexManager?.stopSession(chatId)
471
+ }
472
+
473
+ export function stopAllCodexSessions(): void {
474
+ codexManager?.stopAll()
475
+ }
@@ -0,0 +1,106 @@
1
+ import { describe, test, expect, afterEach } from "bun:test"
2
+ import { EventStore } from "./event-store"
3
+ import { deriveAgentConfigSnapshot } from "./read-models"
4
+ import type { AgentConfig } from "../shared/agent-config-types"
5
+ import { rmSync, mkdirSync } from "node:fs"
6
+ import { join } from "node:path"
7
+
8
+ const TEST_DIR = join(import.meta.dir, ".test-agent-config-journey")
9
+
10
+ afterEach(() => {
11
+ rmSync(TEST_DIR, { recursive: true, force: true })
12
+ })
13
+
14
+ async function createStoreWithProject() {
15
+ mkdirSync(TEST_DIR, { recursive: true })
16
+ const store = new EventStore(TEST_DIR)
17
+ await store.initialize()
18
+ const project = await store.openProject("/tmp/agent-config-journey-test", "AgentConfigJourney")
19
+ return { store, workspaceId: project.id }
20
+ }
21
+
22
+ function makeConfig(overrides: Partial<AgentConfig> = {}): AgentConfig {
23
+ return {
24
+ id: "cfg-1",
25
+ name: "Test Agent",
26
+ description: "A test agent config",
27
+ provider: "claude",
28
+ model: "claude-sonnet-4-20250514",
29
+ ...overrides,
30
+ }
31
+ }
32
+
33
+ describe("agent config journey", () => {
34
+ test("stage 3: save agent config appears in snapshot", async () => {
35
+ const { store, workspaceId } = await createStoreWithProject()
36
+ const config = makeConfig()
37
+
38
+ await store.saveAgentConfig(workspaceId, config.id, config)
39
+
40
+ const snapshot = deriveAgentConfigSnapshot(store.state, workspaceId)
41
+ expect(snapshot.workspaceId).toBe(workspaceId)
42
+ expect(snapshot.configs).toHaveLength(1)
43
+ expect(snapshot.configs[0].id).toBe(config.id)
44
+ expect(snapshot.configs[0].config.name).toBe("Test Agent")
45
+ expect(snapshot.configs[0].config.provider).toBe("claude")
46
+ expect(snapshot.configs[0].workspaceId).toBe(workspaceId)
47
+ })
48
+
49
+ test("stage 4: update agent config (save again) updates in snapshot", async () => {
50
+ const { store, workspaceId } = await createStoreWithProject()
51
+ const config = makeConfig()
52
+
53
+ await store.saveAgentConfig(workspaceId, config.id, config)
54
+ await new Promise((r) => setTimeout(r, 5))
55
+ const updated = makeConfig({ name: "Updated Agent", model: "claude-opus-4-20250514" })
56
+ await store.saveAgentConfig(workspaceId, config.id, updated)
57
+
58
+ const snapshot = deriveAgentConfigSnapshot(store.state, workspaceId)
59
+ expect(snapshot.configs).toHaveLength(1)
60
+ expect(snapshot.configs[0].config.name).toBe("Updated Agent")
61
+ expect(snapshot.configs[0].config.model).toBe("claude-opus-4-20250514")
62
+ expect(snapshot.configs[0].updatedAt).toBeGreaterThan(snapshot.configs[0].createdAt)
63
+ })
64
+
65
+ test("stage 5: remove agent config disappears from snapshot", async () => {
66
+ const { store, workspaceId } = await createStoreWithProject()
67
+ const config = makeConfig()
68
+
69
+ await store.saveAgentConfig(workspaceId, config.id, config)
70
+ expect(deriveAgentConfigSnapshot(store.state, workspaceId).configs).toHaveLength(1)
71
+
72
+ await store.removeAgentConfig(workspaceId, config.id)
73
+ const snapshot = deriveAgentConfigSnapshot(store.state, workspaceId)
74
+ expect(snapshot.configs).toHaveLength(0)
75
+ })
76
+
77
+ test("agent config survives compact and replay", async () => {
78
+ const { store, workspaceId } = await createStoreWithProject()
79
+ const config = makeConfig({ id: "cfg-persist", name: "Persistent Agent" })
80
+
81
+ await store.saveAgentConfig(workspaceId, config.id, config)
82
+ const before = deriveAgentConfigSnapshot(store.state, workspaceId)
83
+ expect(before.configs).toHaveLength(1)
84
+
85
+ await store.compact()
86
+
87
+ const store2 = new EventStore(TEST_DIR)
88
+ await store2.initialize()
89
+
90
+ const after = deriveAgentConfigSnapshot(store2.state, workspaceId)
91
+ expect(after.configs).toHaveLength(1)
92
+ expect(after.configs[0].id).toBe("cfg-persist")
93
+ expect(after.configs[0].config.name).toBe("Persistent Agent")
94
+ expect(after.configs[0].config.provider).toBe("claude")
95
+ expect(after.configs[0].workspaceId).toBe(workspaceId)
96
+ })
97
+
98
+ test("empty workspace returns empty configs", async () => {
99
+ const { store, workspaceId } = await createStoreWithProject()
100
+
101
+ const snapshot = deriveAgentConfigSnapshot(store.state, workspaceId)
102
+ expect(snapshot.workspaceId).toBe(workspaceId)
103
+ expect(snapshot.configs).toHaveLength(0)
104
+ expect(snapshot.lastUpdated).toBe(new Date(0).toISOString())
105
+ })
106
+ })
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Backward-compatible re-exports.
3
+ *
4
+ * AgentCoordinator was removed — all turn execution is delegated to the
5
+ * runner process via RunnerProxy. Only the shared transcript helpers
6
+ * survive, now canonical in src/shared/transcript-entries.ts.
7
+ */
8
+ export { timestamped, discardedToolResult } from "../shared/transcript-entries"
@@ -0,0 +1,66 @@
1
+ export interface AuthErrorDetection {
2
+ chatId: string
3
+ reason: string
4
+ raw: unknown
5
+ }
6
+
7
+ interface ErrorLike {
8
+ message?: string
9
+ status?: number
10
+ api_error_status?: number
11
+ }
12
+
13
+ // Strings the Claude CLI / Anthropic API emit when an OAuth token is
14
+ // rejected. Covers both the JSON error envelope (`authentication_error`)
15
+ // and the CLI's surfaced result text (`Failed to authenticate.`). The
16
+ // `api_error_status: 401` form appears in JSONL `result` entries from
17
+ // the CLI when subscription auth fails.
18
+ const AUTH_ERROR_PATTERNS = [
19
+ /api_error_status[^,}]*\s*:\s*401/i,
20
+ /401\s+Invalid authentication credentials/i,
21
+ /Failed to authenticate\.\s*API Error:\s*401/i,
22
+ /"type"\s*:\s*"authentication_error"/i,
23
+ /"error"\s*:\s*"authentication_failed"/i,
24
+ ] as const
25
+
26
+ function isAuthErrorText(text: string): boolean {
27
+ return AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(text))
28
+ }
29
+
30
+ export class ClaudeAuthErrorDetector {
31
+ /**
32
+ * Inspect a thrown error (from the SDK `query()` stream) for OAuth/auth
33
+ * failure signals. Returns a detection when the token used for the
34
+ * spawn has been rejected by the API — caller should mark the token as
35
+ * errored and rotate.
36
+ */
37
+ detect(chatId: string, error: unknown): AuthErrorDetection | null {
38
+ if (!error) return null
39
+ const e = error as ErrorLike
40
+ if (e.status === 401 || e.api_error_status === 401) {
41
+ return { chatId, reason: this.summarize(e.message), raw: error }
42
+ }
43
+ const message = typeof e.message === "string" ? e.message : null
44
+ if (message && isAuthErrorText(message)) {
45
+ return { chatId, reason: this.summarize(message), raw: error }
46
+ }
47
+ return null
48
+ }
49
+
50
+ /**
51
+ * Inspect the textual `result` field of a CLI JSONL `result` entry
52
+ * (Claude Code's subprocess-level error surface). The CLI emits
53
+ * `"api_error_status":401` and `"Failed to authenticate. API Error: 401
54
+ * Invalid authentication credentials"` for OAuth rejection.
55
+ */
56
+ detectFromResultText(chatId: string, text: string): AuthErrorDetection | null {
57
+ if (typeof text !== "string" || text.length === 0) return null
58
+ if (!isAuthErrorText(text)) return null
59
+ return { chatId, reason: this.summarize(text), raw: text }
60
+ }
61
+
62
+ private summarize(message: string | undefined): string {
63
+ if (!message) return "401 authentication error"
64
+ return message.length > 200 ? `${message.slice(0, 200)}…` : message
65
+ }
66
+ }