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,301 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"
2
+ import { homedir } from "node:os"
3
+ import path from "node:path"
4
+ import type { AgentProvider } from "../shared/types"
5
+ import { resolveLocalPath } from "./paths"
6
+
7
+ export interface DiscoveredProject {
8
+ localPath: string
9
+ title: string
10
+ modifiedAt: number
11
+ }
12
+
13
+ export interface ProviderDiscoveredProject extends DiscoveredProject {
14
+ provider: AgentProvider
15
+ }
16
+
17
+ export interface ProjectDiscoveryAdapter {
18
+ provider: AgentProvider
19
+ scan(homeDir?: string): ProviderDiscoveredProject[]
20
+ }
21
+
22
+ function resolveEncodedClaudePath(folderName: string) {
23
+ const segments = folderName.replace(/^-/, "").split("-").filter(Boolean)
24
+ let currentPath = ""
25
+ let remainingSegments = [...segments]
26
+
27
+ while (remainingSegments.length > 0) {
28
+ let found = false
29
+
30
+ for (let index = remainingSegments.length; index >= 1; index -= 1) {
31
+ const segment = remainingSegments.slice(0, index).join("-")
32
+ const candidate = `${currentPath}/${segment}`
33
+
34
+ if (existsSync(candidate)) {
35
+ currentPath = candidate
36
+ remainingSegments = remainingSegments.slice(index)
37
+ found = true
38
+ break
39
+ }
40
+ }
41
+
42
+ if (!found) {
43
+ const [head, ...tail] = remainingSegments
44
+ currentPath = `${currentPath}/${head}`
45
+ remainingSegments = tail
46
+ }
47
+ }
48
+
49
+ return currentPath || "/"
50
+ }
51
+
52
+ function normalizeExistingDirectory(localPath: string) {
53
+ try {
54
+ const normalized = resolveLocalPath(localPath)
55
+ if (!statSync(normalized).isDirectory()) {
56
+ return null
57
+ }
58
+ return normalized
59
+ } catch {
60
+ return null
61
+ }
62
+ }
63
+
64
+ function mergeDiscoveredProjects(projects: Iterable<DiscoveredProject>): DiscoveredProject[] {
65
+ const merged = new Map<string, DiscoveredProject>()
66
+
67
+ for (const project of projects) {
68
+ const existing = merged.get(project.localPath)
69
+ if (!existing || project.modifiedAt > existing.modifiedAt) {
70
+ merged.set(project.localPath, {
71
+ localPath: project.localPath,
72
+ title: project.title || path.basename(project.localPath) || project.localPath,
73
+ modifiedAt: project.modifiedAt,
74
+ })
75
+ continue
76
+ }
77
+
78
+ if (!existing.title && project.title) {
79
+ existing.title = project.title
80
+ }
81
+ }
82
+
83
+ return [...merged.values()].sort((a, b) => b.modifiedAt - a.modifiedAt)
84
+ }
85
+
86
+ export class ClaudeProjectDiscoveryAdapter implements ProjectDiscoveryAdapter {
87
+ readonly provider = "claude" as const
88
+
89
+ scan(homeDir: string = homedir()): ProviderDiscoveredProject[] {
90
+ const projectsDir = path.join(homeDir, ".claude", "projects")
91
+ if (!existsSync(projectsDir)) {
92
+ return []
93
+ }
94
+
95
+ const entries = readdirSync(projectsDir, { withFileTypes: true })
96
+ const projects: ProviderDiscoveredProject[] = []
97
+
98
+ for (const entry of entries) {
99
+ if (!entry.isDirectory()) continue
100
+
101
+ const resolvedPath = resolveEncodedClaudePath(entry.name)
102
+ const normalizedPath = normalizeExistingDirectory(resolvedPath)
103
+ if (!normalizedPath) {
104
+ continue
105
+ }
106
+
107
+ const stat = statSync(path.join(projectsDir, entry.name))
108
+ projects.push({
109
+ provider: this.provider,
110
+ localPath: normalizedPath,
111
+ title: path.basename(normalizedPath) || normalizedPath,
112
+ modifiedAt: stat.mtimeMs,
113
+ })
114
+ }
115
+
116
+ const mergedProjects = mergeDiscoveredProjects(projects).map((project) => ({
117
+ provider: this.provider,
118
+ ...project,
119
+ }))
120
+
121
+ return mergedProjects
122
+ }
123
+ }
124
+
125
+ function parseJsonRecord(line: string): Record<string, unknown> | null {
126
+ try {
127
+ const parsed = JSON.parse(line)
128
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
129
+ return null
130
+ }
131
+ return parsed as Record<string, unknown>
132
+ } catch {
133
+ return null
134
+ }
135
+ }
136
+
137
+ function readCodexSessionIndex(indexPath: string) {
138
+ const updatedAtById = new Map<string, number>()
139
+ if (!existsSync(indexPath)) {
140
+ return updatedAtById
141
+ }
142
+
143
+ for (const line of readFileSync(indexPath, "utf8").split("\n")) {
144
+ if (!line.trim()) continue
145
+ const record = parseJsonRecord(line)
146
+ if (!record) continue
147
+
148
+ const id = typeof record.id === "string" ? record.id : null
149
+ const updatedAt = typeof record.updated_at === "string" ? Date.parse(record.updated_at) : Number.NaN
150
+ if (!id || Number.isNaN(updatedAt)) continue
151
+
152
+ const existing = updatedAtById.get(id)
153
+ if (existing === undefined || updatedAt > existing) {
154
+ updatedAtById.set(id, updatedAt)
155
+ }
156
+ }
157
+
158
+ return updatedAtById
159
+ }
160
+
161
+ function collectCodexSessionFiles(directory: string): string[] {
162
+ if (!existsSync(directory)) {
163
+ return []
164
+ }
165
+
166
+ const files: string[] = []
167
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
168
+ const fullPath = path.join(directory, entry.name)
169
+ if (entry.isDirectory()) {
170
+ files.push(...collectCodexSessionFiles(fullPath))
171
+ continue
172
+ }
173
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) {
174
+ files.push(fullPath)
175
+ }
176
+ }
177
+ return files
178
+ }
179
+
180
+ function readCodexConfiguredProjects(configPath: string) {
181
+ const projects = new Map<string, number>()
182
+ if (!existsSync(configPath)) {
183
+ return projects
184
+ }
185
+
186
+ const configMtime = statSync(configPath).mtimeMs
187
+ for (const line of readFileSync(configPath, "utf8").split("\n")) {
188
+ const match = line.match(/^\[projects\."(.+)"\]$/)
189
+ if (!match?.[1]) continue
190
+ projects.set(match[1], configMtime)
191
+ }
192
+
193
+ return projects
194
+ }
195
+
196
+ function readCodexSessionMetadata(sessionsDir: string) {
197
+ const metadataById = new Map<string, { cwd: string; modifiedAt: number }>()
198
+
199
+ for (const sessionFile of collectCodexSessionFiles(sessionsDir)) {
200
+ const fileStat = statSync(sessionFile)
201
+ const firstLine = readFileSync(sessionFile, "utf8").split("\n", 1)[0]
202
+ if (!firstLine?.trim()) continue
203
+
204
+ const record = parseJsonRecord(firstLine)
205
+ if (!record || record.type !== "session_meta") continue
206
+
207
+ const payload = record.payload
208
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) continue
209
+
210
+ const payloadRecord = payload as Record<string, unknown>
211
+ const sessionId = typeof payloadRecord.id === "string" ? payloadRecord.id : null
212
+ const cwd = typeof payloadRecord.cwd === "string" ? payloadRecord.cwd : null
213
+ if (!sessionId || !cwd) continue
214
+
215
+ const recordTimestamp = typeof record.timestamp === "string" ? Date.parse(record.timestamp) : Number.NaN
216
+ const payloadTimestamp = typeof payloadRecord.timestamp === "string" ? Date.parse(payloadRecord.timestamp) : Number.NaN
217
+ const modifiedAt = [recordTimestamp, payloadTimestamp, fileStat.mtimeMs].find((value) => !Number.isNaN(value)) ?? fileStat.mtimeMs
218
+
219
+ metadataById.set(sessionId, { cwd, modifiedAt })
220
+ }
221
+
222
+ return metadataById
223
+ }
224
+
225
+ export class CodexProjectDiscoveryAdapter implements ProjectDiscoveryAdapter {
226
+ readonly provider = "codex" as const
227
+
228
+ scan(homeDir: string = homedir()): ProviderDiscoveredProject[] {
229
+ const indexPath = path.join(homeDir, ".codex", "session_index.jsonl")
230
+ const sessionsDir = path.join(homeDir, ".codex", "sessions")
231
+ const configPath = path.join(homeDir, ".codex", "config.toml")
232
+ const updatedAtById = readCodexSessionIndex(indexPath)
233
+ const metadataById = readCodexSessionMetadata(sessionsDir)
234
+ const configuredProjects = readCodexConfiguredProjects(configPath)
235
+ const projects: ProviderDiscoveredProject[] = []
236
+
237
+ for (const [sessionId, metadata] of metadataById.entries()) {
238
+ const modifiedAt = updatedAtById.get(sessionId) ?? metadata.modifiedAt
239
+ const cwd = metadata.cwd
240
+ if (!cwd) {
241
+ continue
242
+ }
243
+ if (!path.isAbsolute(cwd)) {
244
+ continue
245
+ }
246
+
247
+ const normalizedPath = normalizeExistingDirectory(cwd)
248
+ if (!normalizedPath) {
249
+ continue
250
+ }
251
+
252
+ projects.push({
253
+ provider: this.provider,
254
+ localPath: normalizedPath,
255
+ title: path.basename(normalizedPath) || normalizedPath,
256
+ modifiedAt,
257
+ })
258
+ }
259
+
260
+ for (const [configuredPath, modifiedAt] of configuredProjects.entries()) {
261
+ if (!path.isAbsolute(configuredPath)) {
262
+ continue
263
+ }
264
+
265
+ const normalizedPath = normalizeExistingDirectory(configuredPath)
266
+ if (!normalizedPath) {
267
+ continue
268
+ }
269
+
270
+ projects.push({
271
+ provider: this.provider,
272
+ localPath: normalizedPath,
273
+ title: path.basename(normalizedPath) || normalizedPath,
274
+ modifiedAt,
275
+ })
276
+ }
277
+
278
+ const mergedProjects = mergeDiscoveredProjects(projects).map((project) => ({
279
+ provider: this.provider,
280
+ ...project,
281
+ }))
282
+
283
+ return mergedProjects
284
+ }
285
+ }
286
+
287
+ export const DEFAULT_PROJECT_DISCOVERY_ADAPTERS: ProjectDiscoveryAdapter[] = [
288
+ new ClaudeProjectDiscoveryAdapter(),
289
+ new CodexProjectDiscoveryAdapter(),
290
+ ]
291
+
292
+ export function discoverProjects(
293
+ homeDir: string = homedir(),
294
+ adapters: ProjectDiscoveryAdapter[] = DEFAULT_PROJECT_DISCOVERY_ADAPTERS
295
+ ): DiscoveredProject[] {
296
+ const mergedProjects = mergeDiscoveredProjects(
297
+ adapters.flatMap((adapter) => adapter.scan(homeDir).map(({ provider: _provider, ...project }) => project))
298
+ )
299
+
300
+ return mergedProjects
301
+ }
@@ -0,0 +1,124 @@
1
+ import { afterEach, describe, expect, test } from "bun:test"
2
+ import { mkdtemp, readFile, rm } from "node:fs/promises"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { EventStore } from "./event-store"
6
+ import type { SnapshotFile } from "./events"
7
+ import type { AgentConfig } from "../shared/agent-config-types"
8
+
9
+ const tempDirs: string[] = []
10
+
11
+ afterEach(async () => {
12
+ await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })))
13
+ })
14
+
15
+ async function createTempDataDir() {
16
+ const dir = await mkdtemp(join(tmpdir(), "kanna-ac-"))
17
+ tempDirs.push(dir)
18
+ return dir
19
+ }
20
+
21
+ async function createStoreWithWorkspace() {
22
+ const dataDir = await createTempDataDir()
23
+ const store = new EventStore(dataDir)
24
+ await store.initialize()
25
+ const workspace = await store.openProject("/tmp/ac-workspace")
26
+ return { dataDir, store, workspaceId: workspace.id }
27
+ }
28
+
29
+ const makeConfig = (id: string): AgentConfig => ({
30
+ id,
31
+ name: `Agent ${id}`,
32
+ description: `Test agent ${id}`,
33
+ provider: "claude",
34
+ model: "opus-4",
35
+ systemPrompt: "You are helpful.",
36
+ tools: ["bash"],
37
+ temperature: 0.5,
38
+ })
39
+
40
+ describe("EventStore agent config reducers", () => {
41
+ test("saveAgentConfig creates record in agentConfigsByWorkspace", async () => {
42
+ const { store, workspaceId } = await createStoreWithWorkspace()
43
+ const config = makeConfig("a1")
44
+
45
+ await store.saveAgentConfig(workspaceId, "a1", config)
46
+
47
+ const wsMap = store.state.agentConfigsByWorkspace.get(workspaceId)
48
+ expect(wsMap).toBeDefined()
49
+ const record = wsMap!.get("a1")
50
+ expect(record).toBeDefined()
51
+ expect(record!.id).toBe("a1")
52
+ expect(record!.workspaceId).toBe(workspaceId)
53
+ expect(record!.config).toEqual(config)
54
+ expect(record!.createdAt).toBeGreaterThan(0)
55
+ expect(record!.updatedAt).toBeGreaterThan(0)
56
+ expect(record!.lastCommitHash).toBeUndefined()
57
+ })
58
+
59
+ test("commitAgentConfig sets lastCommitHash", async () => {
60
+ const { store, workspaceId } = await createStoreWithWorkspace()
61
+ await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
62
+
63
+ await store.commitAgentConfig(workspaceId, "a1", "abc123def")
64
+
65
+ const record = store.state.agentConfigsByWorkspace.get(workspaceId)!.get("a1")!
66
+ expect(record.lastCommitHash).toBe("abc123def")
67
+ })
68
+
69
+ test("removeAgentConfig deletes record from map", async () => {
70
+ const { store, workspaceId } = await createStoreWithWorkspace()
71
+ await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
72
+
73
+ await store.removeAgentConfig(workspaceId, "a1")
74
+
75
+ const wsMap = store.state.agentConfigsByWorkspace.get(workspaceId)
76
+ expect(wsMap?.has("a1")).toBe(false)
77
+ })
78
+
79
+ test("snapshot round-trip preserves agent configs", async () => {
80
+ const { dataDir, store, workspaceId } = await createStoreWithWorkspace()
81
+ await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
82
+ await store.commitAgentConfig(workspaceId, "a1", "hash1")
83
+ await store.saveAgentConfig(workspaceId, "a2", makeConfig("a2"))
84
+
85
+ await store.compact()
86
+
87
+ // Verify snapshot file has agentConfigs
88
+ const snapshot = JSON.parse(await readFile(join(dataDir, "snapshot.json"), "utf8")) as SnapshotFile
89
+ expect(snapshot.agentConfigs).toBeDefined()
90
+ expect(snapshot.agentConfigs!.length).toBe(1)
91
+ expect(snapshot.agentConfigs![0].workspaceId).toBe(workspaceId)
92
+ expect(snapshot.agentConfigs![0].records.length).toBe(2)
93
+
94
+ // Fresh store from snapshot
95
+ const store2 = new EventStore(dataDir)
96
+ await store2.initialize()
97
+
98
+ const wsMap = store2.state.agentConfigsByWorkspace.get(workspaceId)!
99
+ expect(wsMap.size).toBe(2)
100
+ expect(wsMap.get("a1")!.config.name).toBe("Agent a1")
101
+ expect(wsMap.get("a1")!.lastCommitHash).toBe("hash1")
102
+ expect(wsMap.get("a2")!.config.name).toBe("Agent a2")
103
+ })
104
+
105
+ test("second save updates updatedAt but preserves createdAt", async () => {
106
+ const { store, workspaceId } = await createStoreWithWorkspace()
107
+ await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
108
+
109
+ const record1 = store.state.agentConfigsByWorkspace.get(workspaceId)!.get("a1")!
110
+ const createdAt = record1.createdAt
111
+ const updatedAt1 = record1.updatedAt
112
+
113
+ // Small delay to ensure timestamp differs
114
+ await new Promise((r) => setTimeout(r, 5))
115
+
116
+ const updated = { ...makeConfig("a1"), name: "Renamed Agent" }
117
+ await store.saveAgentConfig(workspaceId, "a1", updated)
118
+
119
+ const record2 = store.state.agentConfigsByWorkspace.get(workspaceId)!.get("a1")!
120
+ expect(record2.createdAt).toBe(createdAt)
121
+ expect(record2.updatedAt).toBeGreaterThan(updatedAt1)
122
+ expect(record2.config.name).toBe("Renamed Agent")
123
+ })
124
+ })
@@ -0,0 +1,149 @@
1
+ import { afterEach, describe, expect, test } from "bun:test"
2
+ import { mkdtemp, readFile, rm } from "node:fs/promises"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { EventStore } from "./event-store"
6
+ import type { SnapshotFile } from "./events"
7
+
8
+ const tempDirs: string[] = []
9
+
10
+ afterEach(async () => {
11
+ await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })))
12
+ })
13
+
14
+ async function createTempDataDir() {
15
+ const dir = await mkdtemp(join(tmpdir(), "kanna-coord-"))
16
+ tempDirs.push(dir)
17
+ return dir
18
+ }
19
+
20
+ async function createStoreWithProject() {
21
+ const dataDir = await createTempDataDir()
22
+ const store = new EventStore(dataDir)
23
+ await store.initialize()
24
+ const project = await store.openProject("/tmp/coord-project")
25
+ return { dataDir, store, workspaceId: project.id }
26
+ }
27
+
28
+ describe("EventStore coordination", () => {
29
+ test("appends todo_added and replays on restart", async () => {
30
+ const { dataDir, store, workspaceId } = await createStoreWithProject()
31
+
32
+ await store.addTodo(workspaceId, "t1", "Build feature X", "high", "agent-1")
33
+
34
+ const coord = store.state.coordinationByWorkspace.get(workspaceId)!
35
+ expect(coord.todos.get("t1")).toMatchObject({
36
+ id: "t1",
37
+ description: "Build feature X",
38
+ priority: "high",
39
+ status: "open",
40
+ claimedBy: null,
41
+ createdBy: "agent-1",
42
+ })
43
+
44
+ // Replay on restart
45
+ const store2 = new EventStore(dataDir)
46
+ await store2.initialize()
47
+ const coord2 = store2.state.coordinationByWorkspace.get(workspaceId)!
48
+ expect(coord2.todos.get("t1")?.description).toBe("Build feature X")
49
+ })
50
+
51
+ test("todo claim, complete, abandon lifecycle", async () => {
52
+ const { store, workspaceId } = await createStoreWithProject()
53
+
54
+ await store.addTodo(workspaceId, "t1", "Task A", "normal", "agent-1")
55
+ await store.claimTodo(workspaceId, "t1", "agent-2")
56
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.status).toBe("claimed")
57
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.claimedBy).toBe("agent-2")
58
+
59
+ await store.completeTodo(workspaceId, "t1", ["output.ts"])
60
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.status).toBe("complete")
61
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.outputs).toEqual(["output.ts"])
62
+
63
+ await store.addTodo(workspaceId, "t2", "Task B", "low", "agent-1")
64
+ await store.claimTodo(workspaceId, "t2", "agent-3")
65
+ await store.abandonTodo(workspaceId, "t2")
66
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t2")?.status).toBe("abandoned")
67
+ })
68
+
69
+ test("claim with file overlap detects conflict", async () => {
70
+ const { store, workspaceId } = await createStoreWithProject()
71
+
72
+ await store.createClaim(workspaceId, "c1", "Refactor auth", ["src/auth.ts", "src/login.ts"], "session-1")
73
+ await store.createClaim(workspaceId, "c2", "Fix login bug", ["src/login.ts", "src/utils.ts"], "session-2")
74
+
75
+ const c2 = store.state.coordinationByWorkspace.get(workspaceId)!.claims.get("c2")!
76
+ expect(c2.status).toBe("conflict")
77
+ expect(c2.conflictsWith).toBe("c1")
78
+ })
79
+
80
+ test("worktree create and assign", async () => {
81
+ const { store, workspaceId } = await createStoreWithProject()
82
+
83
+ await store.createWorktree(workspaceId, "w1", "feat/auth", "main", "/tmp/wt1")
84
+ const wt = store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")!
85
+ expect(wt.status).toBe("ready")
86
+ expect(wt.assignedTo).toBeNull()
87
+
88
+ await store.assignWorktree(workspaceId, "w1", "session-1")
89
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.status).toBe("assigned")
90
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.assignedTo).toBe("session-1")
91
+
92
+ await store.removeWorktree(workspaceId, "w1")
93
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.status).toBe("removed")
94
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.assignedTo).toBeNull()
95
+ })
96
+
97
+ test("rule set and remove", async () => {
98
+ const { store, workspaceId } = await createStoreWithProject()
99
+
100
+ await store.setRule(workspaceId, "r1", "No console.log in production", "lead")
101
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.rules.get("r1")?.content).toBe("No console.log in production")
102
+
103
+ await store.setRule(workspaceId, "r1", "No console.log or debugger", "lead")
104
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.rules.get("r1")?.content).toBe("No console.log or debugger")
105
+
106
+ await store.removeRule(workspaceId, "r1")
107
+ expect(store.state.coordinationByWorkspace.get(workspaceId)!.rules.has("r1")).toBe(false)
108
+ })
109
+
110
+ test("compact preserves coordination state", async () => {
111
+ const { dataDir, store, workspaceId } = await createStoreWithProject()
112
+
113
+ await store.addTodo(workspaceId, "t1", "Survive compaction", "high", "agent-1")
114
+ await store.setRule(workspaceId, "r1", "Be excellent", "lead")
115
+ await store.compact()
116
+
117
+ // Coordination JSONL should be truncated
118
+ expect(await readFile(join(dataDir, "coordination.jsonl"), "utf8")).toBe("")
119
+
120
+ // Snapshot should have coordination
121
+ const snapshot = JSON.parse(await readFile(join(dataDir, "snapshot.json"), "utf8")) as SnapshotFile
122
+ expect(snapshot.coordination).toBeDefined()
123
+ expect(snapshot.coordination!.length).toBe(1)
124
+ expect(snapshot.coordination![0].workspaceId).toBe(workspaceId)
125
+
126
+ // New store should restore from snapshot
127
+ const store2 = new EventStore(dataDir)
128
+ await store2.initialize()
129
+ expect(store2.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.description).toBe("Survive compaction")
130
+ expect(store2.state.coordinationByWorkspace.get(workspaceId)!.rules.get("r1")?.content).toBe("Be excellent")
131
+ })
132
+
133
+ test("clearStorage resets coordination state", async () => {
134
+ const { dataDir, store, workspaceId } = await createStoreWithProject()
135
+
136
+ await store.addTodo(workspaceId, "t1", "Will be cleared", "normal", "agent-1")
137
+
138
+ // Force a version mismatch to trigger clearStorage during replay
139
+ const coordPath = join(dataDir, "coordination.jsonl")
140
+ const { writeFile } = await import("node:fs/promises")
141
+ await writeFile(coordPath, '{"v":999,"type":"todo_added"}\n', "utf8")
142
+
143
+ const store2 = new EventStore(dataDir)
144
+ await store2.initialize()
145
+
146
+ // State should be empty after clearStorage
147
+ expect(store2.state.coordinationByWorkspace.size).toBe(0)
148
+ })
149
+ })