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,356 @@
1
+ import { afterEach, describe, test, expect, mock } from "bun:test"
2
+ import { NatsServer } from "@lagz0ne/nats-embedded"
3
+ import { connect, type NatsConnection, type Subscription } from "@nats-io/transport-node"
4
+ import { createNatsPublisher, type CreateNatsPublisherArgs } from "./nats-publisher"
5
+ import { snapshotSubject, snapshotKvKey, KV_BUCKET } from "../shared/nats-subjects"
6
+ import { createEmptyState } from "./events"
7
+ import { Kvm } from "@nats-io/kv"
8
+ import type { SubscriptionTopic } from "../shared/protocol"
9
+
10
+ let server: NatsServer | null = null
11
+ let nc: NatsConnection | null = null
12
+
13
+ afterEach(async () => {
14
+ if (nc) {
15
+ await nc.drain()
16
+ nc = null
17
+ }
18
+ if (server) {
19
+ await server.stop()
20
+ server = null
21
+ }
22
+ })
23
+
24
+ function mockArgs(overrides: Partial<CreateNatsPublisherArgs> = {}): CreateNatsPublisherArgs {
25
+ return {
26
+ nc: nc!,
27
+ store: {
28
+ state: createEmptyState(),
29
+ getMessages: () => [],
30
+ } as unknown as CreateNatsPublisherArgs["store"],
31
+ agent: {
32
+ getActiveStatuses: () => new Map(),
33
+ } as unknown as CreateNatsPublisherArgs["agent"],
34
+ terminals: {
35
+ getSnapshot: () => null,
36
+ onEvent: () => () => {},
37
+ } as unknown as CreateNatsPublisherArgs["terminals"],
38
+ refreshDiscovery: async () => [],
39
+ getDiscoveredProjects: () => [],
40
+ machineDisplayName: "test-machine",
41
+ updateManager: null,
42
+ ...overrides,
43
+ }
44
+ }
45
+
46
+ async function collectMessages(sub: Subscription, count: number, timeoutMs = 500): Promise<string[]> {
47
+ const messages: string[] = []
48
+ const decoder = new TextDecoder()
49
+ const timeout = setTimeout(() => sub.unsubscribe(), timeoutMs)
50
+
51
+ for await (const msg of sub) {
52
+ messages.push(decoder.decode(msg.data))
53
+ if (messages.length >= count) {
54
+ clearTimeout(timeout)
55
+ sub.unsubscribe()
56
+ break
57
+ }
58
+ }
59
+ clearTimeout(timeout)
60
+ return messages
61
+ }
62
+
63
+ describe("createNatsPublisher", () => {
64
+ test("publishes snapshot to NATS subject", async () => {
65
+ server = await NatsServer.start({ jetstream: true })
66
+ nc = await connect({ servers: server.url })
67
+
68
+ const publisher = await createNatsPublisher(mockArgs())
69
+ const topic: SubscriptionTopic = { type: "sidebar" }
70
+ const sub = nc.subscribe(snapshotSubject(topic))
71
+
72
+ publisher.addSubscription("sub-1", topic)
73
+ publisher.getSnapshot(topic)
74
+
75
+ const msgs = await collectMessages(sub, 1)
76
+ expect(msgs.length).toBe(1)
77
+
78
+ const data = JSON.parse(msgs[0])
79
+ expect(data).toHaveProperty("workspaceGroups")
80
+
81
+ publisher.dispose()
82
+ })
83
+
84
+ test("runner-teams snapshot derives members + runner labels (US-RTN)", async () => {
85
+ server = await NatsServer.start({ jetstream: true })
86
+ nc = await connect({ servers: server.url })
87
+
88
+ const args = mockArgs()
89
+ args.store.state.teamMembers.set("m1", { id: "m1", name: "Alice" })
90
+ args.store.state.runnerLabels.set("runner-1", { runnerId: "runner-1", name: "Studio Mac", memberId: "m1", updatedAt: 1 })
91
+
92
+ const publisher = await createNatsPublisher(args)
93
+ const topic: SubscriptionTopic = { type: "runner-teams" }
94
+ const sub = nc.subscribe(snapshotSubject(topic))
95
+
96
+ publisher.addSubscription("sub-rt", topic)
97
+ publisher.getSnapshot(topic)
98
+
99
+ const msgs = await collectMessages(sub, 1)
100
+ expect(msgs.length).toBe(1)
101
+ const data = JSON.parse(msgs[0]) as { members: unknown[]; runners: unknown[] }
102
+ expect(data.members).toEqual([{ id: "m1", name: "Alice" }])
103
+ expect(data.runners).toEqual([{ runnerId: "runner-1", name: "Studio Mac", memberId: "m1", updatedAt: 1 }])
104
+
105
+ publisher.dispose()
106
+ })
107
+
108
+ test("dedup skips identical publishes", async () => {
109
+ server = await NatsServer.start({ jetstream: true })
110
+ nc = await connect({ servers: server.url })
111
+
112
+ const publisher = await createNatsPublisher(mockArgs())
113
+ const topic: SubscriptionTopic = { type: "sidebar" }
114
+ const sub = nc.subscribe(snapshotSubject(topic))
115
+
116
+ // First publish delivers
117
+ publisher.getSnapshot(topic)
118
+ // Second publish with same data is skipped
119
+ publisher.getSnapshot(topic)
120
+ await nc.flush()
121
+
122
+ const msgs = await collectMessages(sub, 2, 200)
123
+ // Only 1 message should arrive due to dedup
124
+ expect(msgs.length).toBe(1)
125
+
126
+ publisher.dispose()
127
+ })
128
+
129
+ test("writes snapshot to KV bucket", async () => {
130
+ server = await NatsServer.start({ jetstream: true })
131
+ nc = await connect({ servers: server.url })
132
+
133
+ const publisher = await createNatsPublisher(mockArgs())
134
+ const topic: SubscriptionTopic = { type: "sidebar" }
135
+
136
+ publisher.getSnapshot(topic)
137
+ await nc.flush()
138
+ // Give async KV put time to complete
139
+ await new Promise((resolve) => setTimeout(resolve, 100))
140
+
141
+ const kvm = new Kvm(nc)
142
+ const kv = await kvm.create(KV_BUCKET)
143
+ const entry = await kv.get(snapshotKvKey(topic))
144
+ expect(entry).not.toBeNull()
145
+
146
+ const data = entry!.json() as Record<string, unknown>
147
+ expect(data).toHaveProperty("workspaceGroups")
148
+
149
+ publisher.dispose()
150
+ })
151
+
152
+ test("broadcastSnapshots publishes for all active subscriptions", async () => {
153
+ server = await NatsServer.start({ jetstream: true })
154
+ nc = await connect({ servers: server.url })
155
+
156
+ const publisher = await createNatsPublisher(mockArgs())
157
+
158
+ publisher.addSubscription("sub-1", { type: "sidebar" })
159
+ publisher.addSubscription("sub-2", { type: "local-workspaces" })
160
+
161
+ const sidebarSub = nc.subscribe(snapshotSubject({ type: "sidebar" }))
162
+ const localProjectsSub = nc.subscribe(snapshotSubject({ type: "local-workspaces" }))
163
+
164
+ publisher.broadcastSnapshots()
165
+
166
+ const [sidebarMsgs, localProjectsMsgs] = await Promise.all([
167
+ collectMessages(sidebarSub, 1),
168
+ collectMessages(localProjectsSub, 1),
169
+ ])
170
+
171
+ expect(sidebarMsgs.length).toBe(1)
172
+ expect(localProjectsMsgs.length).toBe(1)
173
+
174
+ publisher.dispose()
175
+ })
176
+
177
+ test("broadcastSnapshots deduplicates same topic from multiple subscriptions", async () => {
178
+ server = await NatsServer.start({ jetstream: true })
179
+ nc = await connect({ servers: server.url })
180
+
181
+ const publisher = await createNatsPublisher(mockArgs())
182
+
183
+ // Two subscriptions for same topic
184
+ publisher.addSubscription("sub-1", { type: "sidebar" })
185
+ publisher.addSubscription("sub-2", { type: "sidebar" })
186
+
187
+ const sub = nc.subscribe(snapshotSubject({ type: "sidebar" }))
188
+
189
+ publisher.broadcastSnapshots()
190
+ await nc.flush()
191
+
192
+ const msgs = await collectMessages(sub, 2, 200)
193
+ // Only 1 publish even though 2 subscriptions watch sidebar
194
+ expect(msgs.length).toBe(1)
195
+
196
+ publisher.dispose()
197
+ })
198
+
199
+ test("removeSubscription prunes dedup cache when last subscriber leaves", async () => {
200
+ server = await NatsServer.start({ jetstream: true })
201
+ nc = await connect({ servers: server.url })
202
+
203
+ const publisher = await createNatsPublisher(mockArgs())
204
+ const topic: SubscriptionTopic = { type: "sidebar" }
205
+
206
+ // Subscribe and get initial snapshot (seeds dedup cache)
207
+ publisher.addSubscription("sub-1", topic)
208
+ publisher.getSnapshot(topic)
209
+
210
+ const sub = nc.subscribe(snapshotSubject(topic))
211
+
212
+ // Remove subscription → dedup cache pruned
213
+ publisher.removeSubscription("sub-1")
214
+
215
+ // Re-subscribe → getSnapshot should publish again (cache was pruned)
216
+ publisher.addSubscription("sub-2", topic)
217
+ publisher.getSnapshot(topic)
218
+
219
+ const msgs = await collectMessages(sub, 1)
220
+ expect(msgs.length).toBe(1)
221
+
222
+ publisher.dispose()
223
+ })
224
+
225
+ test("terminal events forwarded via JetStream", async () => {
226
+ server = await NatsServer.start({ jetstream: true })
227
+ nc = await connect({ servers: server.url })
228
+
229
+ const eventCallbacks: Array<(event: unknown) => void> = []
230
+ const terminals = {
231
+ getSnapshot: () => null,
232
+ onEvent: (cb: (event: unknown) => void) => {
233
+ eventCallbacks.push(cb)
234
+ return () => {}
235
+ },
236
+ } as unknown as CreateNatsPublisherArgs["terminals"]
237
+
238
+ // Need the terminal events stream for JetStream publish
239
+ const { ensureTerminalEventsStream } = await import("./nats-streams")
240
+ await ensureTerminalEventsStream(nc)
241
+
242
+ const publisher = await createNatsPublisher(mockArgs({ terminals }))
243
+
244
+ const sub = nc.subscribe("runtime.evt.terminal.term-1")
245
+
246
+ // Simulate terminal event
247
+ for (const cb of eventCallbacks) {
248
+ cb({ type: "terminal.output", terminalId: "term-1", data: "hello" })
249
+ }
250
+
251
+ const msgs = await collectMessages(sub, 1)
252
+ expect(msgs.length).toBe(1)
253
+ const event = JSON.parse(msgs[0])
254
+ expect(event.terminalId).toBe("term-1")
255
+
256
+ publisher.dispose()
257
+ })
258
+
259
+ test("failed publish does not poison dedup cache — retry delivers", async () => {
260
+ server = await NatsServer.start({ jetstream: true })
261
+ nc = await connect({ servers: server.url })
262
+
263
+ const publisher = await createNatsPublisher(mockArgs())
264
+ const topic: SubscriptionTopic = { type: "sidebar" }
265
+ const subject = snapshotSubject(topic)
266
+
267
+ // Monkey-patch nc.publish to throw only for the snapshot subject
268
+ const realPublish = nc.publish.bind(nc)
269
+ let shouldFail = true
270
+ nc.publish = (subj: string, ...rest: unknown[]) => {
271
+ if (subj === subject && shouldFail) {
272
+ shouldFail = false
273
+ throw new Error("simulated max_payload exceeded")
274
+ }
275
+ return (realPublish as Function)(subj, ...rest)
276
+ }
277
+
278
+ // First getSnapshot — snapshot publish fails, cache should NOT be poisoned
279
+ publisher.getSnapshot(topic)
280
+
281
+ // Restore real publish and subscribe to verify retry
282
+ nc.publish = realPublish
283
+ const sub = nc.subscribe(subject)
284
+
285
+ // Second getSnapshot with same data — should retry because cache was not poisoned
286
+ publisher.getSnapshot(topic)
287
+
288
+ const msgs = await collectMessages(sub, 1)
289
+ expect(msgs.length).toBe(1)
290
+
291
+ publisher.dispose()
292
+ })
293
+
294
+ test("dispose stops terminal event forwarding", async () => {
295
+ server = await NatsServer.start({ jetstream: true })
296
+ nc = await connect({ servers: server.url })
297
+
298
+ const disposeFn = mock(() => {})
299
+ const terminals = {
300
+ getSnapshot: () => null,
301
+ onEvent: () => disposeFn,
302
+ } as unknown as CreateNatsPublisherArgs["terminals"]
303
+
304
+ const publisher = await createNatsPublisher(mockArgs({ terminals }))
305
+ publisher.dispose()
306
+
307
+ expect(disposeFn).toHaveBeenCalledTimes(1)
308
+ })
309
+
310
+ test("broadcastSnapshots publishes orchestration snapshots without throwing", async () => {
311
+ server = await NatsServer.start({ jetstream: true })
312
+ nc = await connect({ servers: server.url })
313
+
314
+ const publisher = await createNatsPublisher(
315
+ mockArgs({
316
+ orchestrator: {
317
+ getHierarchy: () => ({
318
+ children: [
319
+ {
320
+ chatId: "child-1",
321
+ provider: "claude",
322
+ model: "sonnet",
323
+ instruction: "audit the latest release candidate",
324
+ status: "running",
325
+ depth: 1,
326
+ spawnedAt: Date.now(),
327
+ lastStatusAt: Date.now(),
328
+ },
329
+ ],
330
+ }),
331
+ pruneTombstones: () => {},
332
+ } as unknown as CreateNatsPublisherArgs["orchestrator"],
333
+ })
334
+ )
335
+
336
+ const topic: SubscriptionTopic = { type: "orchestration", chatId: "chat-parent" }
337
+ const sub = nc.subscribe(snapshotSubject(topic))
338
+
339
+ publisher.addSubscription("sub-orch", topic)
340
+ await expect(publisher.broadcastSnapshots()).resolves.toBeUndefined()
341
+
342
+ const msgs = await collectMessages(sub, 1)
343
+ expect(msgs.length).toBe(1)
344
+ expect(JSON.parse(msgs[0])).toEqual({
345
+ children: [
346
+ expect.objectContaining({
347
+ chatId: "child-1",
348
+ status: "running",
349
+ instruction: "audit the latest release candidate",
350
+ }),
351
+ ],
352
+ })
353
+
354
+ publisher.dispose()
355
+ })
356
+ })
@@ -0,0 +1,271 @@
1
+ import type { NatsConnection } from "@nats-io/transport-node"
2
+ import { jetstream } from "@nats-io/jetstream"
3
+ import { Kvm } from "@nats-io/kv"
4
+ import type { SubscriptionTopic } from "../shared/protocol"
5
+ import { snapshotSubject, snapshotKvKey, terminalEventSubject, chatMessageSubject, KV_BUCKET } from "../shared/nats-subjects"
6
+ import { LOG_PREFIX } from "../shared/branding"
7
+ import { compressPayload } from "../shared/compression"
8
+ import type { ChatMessageEvent, TranscriptEntry } from "../shared/types"
9
+ import type { SessionStatus } from "../shared/types"
10
+ import type { DiscoveredProject } from "./discovery"
11
+ import type { EventStore } from "./event-store"
12
+ import { deriveChatSnapshot, deriveLocalWorkspacesSnapshot, deriveWorkspaceCoordinationSnapshot, deriveSidebarData, deriveAgentConfigSnapshot, deriveRepoListSnapshot, deriveWorkflowRunsSnapshot, deriveSandboxSnapshot, deriveTranscriptRenderUnits, TRANSCRIPT_RENDER_WINDOW_SIZE } from "./read-models"
13
+ import type { TerminalManager } from "./terminal-manager"
14
+ import type { UpdateManager } from "./update-manager"
15
+ import type { SkillCache } from "./skill-discovery"
16
+ import type { SessionOrchestrator } from "./orchestration"
17
+ import type { RuntimeRegistry } from "./runtime-registry"
18
+ import { deriveServerProviderCatalog } from "./provider-catalog"
19
+ import type { ProfileSnapshot } from "../shared/profile-types"
20
+ import type { ExtensionPreferencesSnapshot } from "../shared/extension-types"
21
+ import type { RunnerTeamSnapshot } from "../shared/runner-team-types"
22
+
23
+ const encoder = new TextEncoder()
24
+
25
+ function errorMessage(error: unknown): string {
26
+ return error instanceof Error ? error.message : String(error)
27
+ }
28
+
29
+ const DEFAULT_UPDATE_SNAPSHOT = {
30
+ currentVersion: "unknown",
31
+ latestVersion: null,
32
+ status: "idle",
33
+ updateAvailable: false,
34
+ lastCheckedAt: null,
35
+ error: null,
36
+ installAction: "restart",
37
+ } as const
38
+
39
+ export interface CreateNatsPublisherArgs {
40
+ nc: NatsConnection
41
+ store: EventStore
42
+ agent: { getActiveStatuses(): Map<string, SessionStatus> }
43
+ terminals: TerminalManager
44
+ refreshDiscovery: () => Promise<DiscoveredProject[]>
45
+ getDiscoveredProjects: () => DiscoveredProject[]
46
+ machineDisplayName: string
47
+ updateManager: UpdateManager | null
48
+ skillCache?: SkillCache
49
+ orchestrator?: SessionOrchestrator
50
+ runtimeRegistry?: RuntimeRegistry
51
+ hasActiveBlockingDelegations?: (chatId: string) => boolean
52
+ }
53
+
54
+ function deriveProfileSnapshot(store: EventStore): ProfileSnapshot {
55
+ return {
56
+ profiles: [...store.state.providerProfiles.values()],
57
+ workspaceOverrides: [...store.state.workspaceProfileOverrides.values()].flatMap(
58
+ (wsMap) => [...wsMap.values()],
59
+ ),
60
+ }
61
+ }
62
+
63
+ function deriveExtensionPreferencesSnapshot(store: EventStore): ExtensionPreferencesSnapshot {
64
+ return {
65
+ preferences: [...store.state.extensionPreferences.values()],
66
+ }
67
+ }
68
+
69
+ function deriveRunnerTeamSnapshot(store: EventStore): RunnerTeamSnapshot {
70
+ return {
71
+ members: [...store.state.teamMembers.values()],
72
+ runners: [...store.state.runnerLabels.values()],
73
+ }
74
+ }
75
+
76
+ export async function createNatsPublisher(args: CreateNatsPublisherArgs) {
77
+ const {
78
+ nc,
79
+ store,
80
+ agent,
81
+ terminals,
82
+ getDiscoveredProjects,
83
+ machineDisplayName,
84
+ updateManager,
85
+ skillCache,
86
+ orchestrator,
87
+ runtimeRegistry,
88
+ hasActiveBlockingDelegations,
89
+ } = args
90
+
91
+ const js = jetstream(nc)
92
+ const kv = await new Kvm(nc).create(KV_BUCKET)
93
+ console.warn(LOG_PREFIX, `KV bucket "${KV_BUCKET}" ready`)
94
+
95
+ const activeSubscriptions = new Map<string, SubscriptionTopic>()
96
+ const lastJsonByKey = new Map<string, string>()
97
+
98
+ function publishSnapshot(topic: SubscriptionTopic, data: unknown): void {
99
+ const kvKey = snapshotKvKey(topic)
100
+ const json = JSON.stringify(data)
101
+
102
+ if (lastJsonByKey.get(kvKey) === json) return
103
+
104
+ const payload = compressPayload(encoder.encode(json))
105
+
106
+ try {
107
+ nc.publish(snapshotSubject(topic), payload)
108
+ lastJsonByKey.set(kvKey, json)
109
+ } catch (error) {
110
+ console.warn(LOG_PREFIX, `NATS publish failed on ${snapshotSubject(topic)}: ${errorMessage(error)}`)
111
+ }
112
+
113
+ void kv.put(kvKey, payload).catch((error) => {
114
+ console.warn(LOG_PREFIX, `KV put failed on ${kvKey}: ${errorMessage(error)}`)
115
+ })
116
+ }
117
+
118
+ async function computeSnapshot(topic: SubscriptionTopic): Promise<unknown> {
119
+ switch (topic.type) {
120
+ case "sidebar":
121
+ return deriveSidebarData(store.state, agent.getActiveStatuses(), hasActiveBlockingDelegations)
122
+ case "local-workspaces":
123
+ return deriveLocalWorkspacesSnapshot(store.state, getDiscoveredProjects(), machineDisplayName)
124
+ case "update":
125
+ return updateManager?.getSnapshot() ?? DEFAULT_UPDATE_SNAPSHOT
126
+ case "chat": {
127
+ const chat = store.state.chatsById.get(topic.chatId)
128
+ const project = chat ? store.state.workspacesById.get(chat.workspaceId) : undefined
129
+ const skills = project && skillCache ? await skillCache.get(project.localPath) : []
130
+ const messageCount = await store.getMessageCount(topic.chatId)
131
+ const renderEntries = await store.getMessages(topic.chatId, {
132
+ offset: Math.max(0, messageCount - TRANSCRIPT_RENDER_WINDOW_SIZE),
133
+ limit: TRANSCRIPT_RENDER_WINDOW_SIZE,
134
+ })
135
+ const activeStatus = agent.getActiveStatuses().get(topic.chatId)
136
+ const providerCatalog = runtimeRegistry
137
+ ? deriveServerProviderCatalog(runtimeRegistry.getProviderCapabilities("claude"))
138
+ : undefined
139
+ return deriveChatSnapshot(
140
+ store.state,
141
+ agent.getActiveStatuses(),
142
+ topic.chatId,
143
+ messageCount,
144
+ skills,
145
+ hasActiveBlockingDelegations,
146
+ deriveTranscriptRenderUnits(renderEntries, activeStatus),
147
+ providerCatalog,
148
+ )
149
+ }
150
+ case "terminal":
151
+ return terminals.getSnapshot(topic.terminalId)
152
+ case "orchestration":
153
+ return orchestrator?.getHierarchy(topic.chatId) ?? { children: [] }
154
+ case "workspace":
155
+ return deriveWorkspaceCoordinationSnapshot(store.state, topic.workspaceId)
156
+ case "agent-config":
157
+ return deriveAgentConfigSnapshot(store.state, topic.workspaceId)
158
+ case "repos":
159
+ return deriveRepoListSnapshot(store.state, topic.workspaceId)
160
+ case "workflow-runs":
161
+ return deriveWorkflowRunsSnapshot(store.state, topic.workspaceId)
162
+ case "sandbox-status":
163
+ return deriveSandboxSnapshot(store.state, topic.workspaceId)
164
+ case "runtime-status":
165
+ return runtimeRegistry?.getSnapshot() ?? { runtimes: [] }
166
+ case "profiles":
167
+ return deriveProfileSnapshot(store)
168
+ case "extension-preferences":
169
+ return deriveExtensionPreferencesSnapshot(store)
170
+ case "runner-teams":
171
+ return deriveRunnerTeamSnapshot(store)
172
+ default: {
173
+ const _exhaustive: never = topic
174
+ throw new Error(`Unknown topic type: ${(_exhaustive as SubscriptionTopic).type}`)
175
+ }
176
+ }
177
+ }
178
+
179
+ function addSubscription(subscriptionId: string, topic: SubscriptionTopic): void {
180
+ activeSubscriptions.set(subscriptionId, topic)
181
+ }
182
+
183
+ function removeSubscription(subscriptionId: string): void {
184
+ const topic = activeSubscriptions.get(subscriptionId)
185
+ activeSubscriptions.delete(subscriptionId)
186
+
187
+ // Prune dedup cache if no remaining subscriptions reference this key
188
+ if (topic) {
189
+ const key = snapshotKvKey(topic)
190
+ const stillTracked = [...activeSubscriptions.values()].some((t) => snapshotKvKey(t) === key)
191
+ if (!stillTracked) {
192
+ lastJsonByKey.delete(key)
193
+ }
194
+ }
195
+ }
196
+
197
+ async function getSnapshot(topic: SubscriptionTopic): Promise<unknown> {
198
+ const data = await computeSnapshot(topic)
199
+ publishSnapshot(topic, data)
200
+ return data
201
+ }
202
+
203
+ async function broadcastSnapshots(changedTypes?: ReadonlySet<string>): Promise<void> {
204
+ const published = new Set<string>()
205
+ for (const [, topic] of activeSubscriptions) {
206
+ if (changedTypes && !changedTypes.has(topic.type)) continue
207
+ const key = snapshotKvKey(topic)
208
+ if (published.has(key)) continue
209
+ published.add(key)
210
+ publishSnapshot(topic, await computeSnapshot(topic))
211
+ }
212
+ orchestrator?.pruneTombstones()
213
+ }
214
+
215
+ function publishChatMessage(chatId: string, entry: TranscriptEntry): void {
216
+ const subject = chatMessageSubject(chatId)
217
+ const event: ChatMessageEvent = { chatId, entry }
218
+ const payload = compressPayload(encoder.encode(JSON.stringify(event)))
219
+ void js.publish(subject, payload).catch((error) => {
220
+ console.warn(LOG_PREFIX, `JetStream publish failed on ${subject}: ${errorMessage(error)}`)
221
+ })
222
+ }
223
+
224
+ const disposeTerminalEvents = terminals.onEvent((event) => {
225
+ const subject = terminalEventSubject(event.terminalId)
226
+ const payload = compressPayload(encoder.encode(JSON.stringify(event)))
227
+ void js.publish(subject, payload).catch((error) => {
228
+ console.warn(LOG_PREFIX, `JetStream publish failed on ${subject}: ${errorMessage(error)}`)
229
+ })
230
+ })
231
+
232
+ const disposeUpdateEvents = updateManager?.onChange(() => {
233
+ publishSnapshot({ type: "update" }, updateManager.getSnapshot())
234
+ }) ?? (() => {})
235
+
236
+ // Periodic runtime health checks — updates runtime-status snapshot
237
+ const healthCheckInterval = runtimeRegistry
238
+ ? setInterval(async () => {
239
+ try {
240
+ for (const provider of ["claude", "codex"] as const) {
241
+ await runtimeRegistry.healthCheck(provider)
242
+ }
243
+ publishSnapshot({ type: "runtime-status" }, runtimeRegistry.getSnapshot())
244
+ } catch (error) {
245
+ console.warn(LOG_PREFIX, `Runtime health check failed: ${errorMessage(error)}`)
246
+ }
247
+ }, 60_000)
248
+ : null
249
+
250
+ if (runtimeRegistry) {
251
+ void runtimeRegistry.probeCapabilities("claude").then(() => {
252
+ publishSnapshot({ type: "runtime-status" }, runtimeRegistry.getSnapshot())
253
+ broadcastSnapshots()
254
+ })
255
+ }
256
+
257
+ return {
258
+ addSubscription,
259
+ removeSubscription,
260
+ getSnapshot,
261
+ broadcastSnapshots,
262
+ publishChatMessage,
263
+ dispose() {
264
+ disposeTerminalEvents()
265
+ disposeUpdateEvents()
266
+ if (healthCheckInterval) clearInterval(healthCheckInterval)
267
+ },
268
+ }
269
+ }
270
+
271
+ export type NatsPublisher = Awaited<ReturnType<typeof createNatsPublisher>>