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,103 @@
1
+ // src/server/project-agent.test.ts
2
+ import { describe, expect, test, afterEach } from "bun:test"
3
+ import { WorkspaceAgent } from "./workspace-agent"
4
+ import { SessionIndex } from "./session-index"
5
+ import { EventStore } from "./event-store"
6
+ import { TranscriptSearchIndex } from "./transcript-search"
7
+ import { mkdtemp, rm } from "node:fs/promises"
8
+ import { tmpdir } from "node:os"
9
+ import path from "node:path"
10
+
11
+ let tempDirs: string[] = []
12
+
13
+ async function createAgent() {
14
+ const dir = await mkdtemp(path.join(tmpdir(), "pa-test-"))
15
+ tempDirs.push(dir)
16
+ const store = new EventStore(dir)
17
+ await store.initialize()
18
+ const sessions = new SessionIndex()
19
+ const search = new TranscriptSearchIndex()
20
+
21
+ const project = await store.openProject("/tmp/test", "Test Project")
22
+ const workspaceId = project.id
23
+
24
+ const agent = new WorkspaceAgent({ sessions, store, search, workspaceId })
25
+ return { agent, sessions, store, search, workspaceId }
26
+ }
27
+
28
+ afterEach(async () => {
29
+ for (const d of tempDirs) await rm(d, { recursive: true, force: true })
30
+ tempDirs = []
31
+ })
32
+
33
+ describe("WorkspaceAgent", () => {
34
+ describe("querySessions", () => {
35
+ test("returns sessions for project", async () => {
36
+ const { agent, workspaceId } = await createAgent()
37
+ const result = agent.querySessions(workspaceId)
38
+ expect(result).toEqual([])
39
+ })
40
+ })
41
+
42
+ describe("searchWork", () => {
43
+ test("delegates to transcript search", async () => {
44
+ const { agent, search } = await createAgent()
45
+ search.addEntry("chat-1", {
46
+ _id: "1",
47
+ createdAt: Date.now(),
48
+ kind: "user_prompt",
49
+ content: "implement auth middleware",
50
+ } as never)
51
+
52
+ const results = agent.searchWork("auth middleware", 10)
53
+ expect(results.length).toBe(1)
54
+ })
55
+ })
56
+
57
+ describe("claimTask", () => {
58
+ test("creates and claims task in event store", async () => {
59
+ const { agent } = await createAgent()
60
+ const task = await agent.claimTask("implement auth", "chat-1", "feat/auth")
61
+ expect(task.status).toBe("claimed")
62
+ expect(task.description).toBe("implement auth")
63
+ expect(task.claimedBy).toBe("chat-1")
64
+ })
65
+ })
66
+
67
+ describe("completeTask", () => {
68
+ test("completes task", async () => {
69
+ const { agent } = await createAgent()
70
+ const task = await agent.claimTask("task", "chat-1", null)
71
+ const completed = await agent.completeTask(task.id, ["file.ts"])
72
+ expect(completed).not.toBeNull()
73
+ expect(completed!.status).toBe("complete")
74
+ expect(completed!.outputs).toEqual(["file.ts"])
75
+ })
76
+ })
77
+
78
+ describe("listTasks", () => {
79
+ test("returns all tasks", async () => {
80
+ const { agent } = await createAgent()
81
+ await agent.claimTask("a", "c1", null)
82
+ await agent.claimTask("b", "c2", null)
83
+ expect(agent.listTasks().length).toBe(2)
84
+ })
85
+ })
86
+
87
+ describe("delegate", () => {
88
+ test("returns task info for task query", async () => {
89
+ const { agent } = await createAgent()
90
+ await agent.claimTask("implement auth", "chat-1", "feat/auth")
91
+
92
+ const result = await agent.delegate("who is working on auth?")
93
+ expect(result.status).toBe("ok")
94
+ expect(result.message).toContain("auth")
95
+ })
96
+
97
+ test("returns ok with summary when no data found", async () => {
98
+ const { agent } = await createAgent()
99
+ const result = await agent.delegate("what is going on?")
100
+ expect(result.status).toBe("ok")
101
+ })
102
+ })
103
+ })
@@ -0,0 +1,102 @@
1
+ // src/server/project-agent.ts
2
+ import { randomUUID } from "node:crypto"
3
+ import type { WorkspaceTodo, SessionRecord, SearchResult, DelegationResult } from "../shared/workspace-types"
4
+ import type { SessionIndex } from "./session-index"
5
+ import type { EventStore } from "./event-store"
6
+ import type { TranscriptSearchIndex } from "./transcript-search"
7
+
8
+ const TASK_KEYWORDS = ["task", "working on", "who", "claimed"]
9
+ const SEARCH_KEYWORDS = ["search", "find", "implemented", "where"]
10
+
11
+ interface WorkspaceAgentArgs {
12
+ sessions: SessionIndex
13
+ store: EventStore
14
+ search: TranscriptSearchIndex
15
+ workspaceId: string
16
+ }
17
+
18
+ export class WorkspaceAgent {
19
+ private readonly sessions: SessionIndex
20
+ private readonly store: EventStore
21
+ private readonly search: TranscriptSearchIndex
22
+ private readonly workspaceId: string
23
+
24
+ constructor(args: WorkspaceAgentArgs) {
25
+ this.sessions = args.sessions
26
+ this.store = args.store
27
+ this.search = args.search
28
+ this.workspaceId = args.workspaceId
29
+ }
30
+
31
+ querySessions(workspaceId: string): SessionRecord[] {
32
+ return this.sessions.getSessionsByProject(workspaceId)
33
+ }
34
+
35
+ getSessionSummary(chatId: string): SessionRecord | null {
36
+ return this.sessions.getSession(chatId)
37
+ }
38
+
39
+ searchWork(query: string, limit: number): SearchResult[] {
40
+ return this.search.search(query, limit)
41
+ }
42
+
43
+ listTasks(): WorkspaceTodo[] {
44
+ const coord = this.store.state.coordinationByWorkspace.get(this.workspaceId)
45
+ if (!coord) return []
46
+ return Array.from(coord.todos.values())
47
+ }
48
+
49
+ getTask(taskId: string): WorkspaceTodo | null {
50
+ const coord = this.store.state.coordinationByWorkspace.get(this.workspaceId)
51
+ if (!coord) return null
52
+ return coord.todos.get(taskId) ?? null
53
+ }
54
+
55
+ async claimTask(description: string, claimedBy: string, _branch: string | null): Promise<WorkspaceTodo> {
56
+ const todoId = randomUUID()
57
+ await this.store.addTodo(this.workspaceId, todoId, description, "normal", claimedBy)
58
+ await this.store.claimTodo(this.workspaceId, todoId, claimedBy)
59
+ const todo = this.getTask(todoId)
60
+ if (!todo) throw new Error(`Todo ${todoId} not found after creation`)
61
+ return todo
62
+ }
63
+
64
+ async completeTask(taskId: string, outputs: string[]): Promise<WorkspaceTodo | null> {
65
+ await this.store.completeTodo(this.workspaceId, taskId, outputs)
66
+ return this.getTask(taskId)
67
+ }
68
+
69
+ async abandonTask(taskId: string): Promise<WorkspaceTodo | null> {
70
+ await this.store.abandonTodo(this.workspaceId, taskId)
71
+ return this.getTask(taskId)
72
+ }
73
+
74
+ async delegate(request: string): Promise<DelegationResult> {
75
+ const sessions = this.querySessions(this.workspaceId)
76
+ const tasks = this.listTasks()
77
+ const lower = request.toLowerCase()
78
+
79
+ if (TASK_KEYWORDS.some((kw) => lower.includes(kw))) {
80
+ if (tasks.length === 0) {
81
+ return { status: "ok", message: "No tasks claimed." }
82
+ }
83
+ const summary = tasks.map((t) => `[${t.status}] "${t.description}" owned by ${t.claimedBy ?? t.createdBy}`).join("; ")
84
+ return { status: "ok", message: summary, data: { tasks } }
85
+ }
86
+
87
+ if (SEARCH_KEYWORDS.some((kw) => lower.includes(kw))) {
88
+ const searchResults = this.search.search(request, 5)
89
+ if (searchResults.length === 0) {
90
+ return { status: "ok", message: "No matching transcript entries found." }
91
+ }
92
+ const summary = searchResults.map((r) => `[${r.chatId}] ${r.fragment.slice(0, 100)}`).join("\n")
93
+ return { status: "ok", message: summary, data: { searchResults } }
94
+ }
95
+
96
+ // General — summarize known state
97
+ const parts: string[] = []
98
+ if (sessions.length > 0) parts.push(`${sessions.length} session(s) active`)
99
+ if (tasks.length > 0) parts.push(`${tasks.length} task(s) tracked`)
100
+ return { status: "ok", message: parts.length > 0 ? parts.join(", ") + "." : "No project activity recorded yet." }
101
+ }
102
+ }
@@ -0,0 +1,79 @@
1
+ // src/server/project-cli.test.ts
2
+ import { describe, expect, test } from "bun:test"
3
+ import { parseProjectCliArgs, formatOutput } from "./project-cli"
4
+
5
+ describe("parseProjectCliArgs", () => {
6
+ test("parses 'sessions' command", () => {
7
+ const result = parseProjectCliArgs(["sessions"])
8
+ expect(result).toEqual({ command: "sessions", args: {} })
9
+ })
10
+
11
+ test("parses 'sessions <id>' command", () => {
12
+ const result = parseProjectCliArgs(["sessions", "chat-1"])
13
+ expect(result).toEqual({ command: "session-detail", args: { chatId: "chat-1" } })
14
+ })
15
+
16
+ test("parses 'search <query>' command", () => {
17
+ const result = parseProjectCliArgs(["search", "auth", "middleware"])
18
+ expect(result).toEqual({ command: "search", args: { query: "auth middleware" } })
19
+ })
20
+
21
+ test("parses 'tasks' command", () => {
22
+ const result = parseProjectCliArgs(["tasks"])
23
+ expect(result).toEqual({ command: "tasks", args: {} })
24
+ })
25
+
26
+ test("parses 'tasks <id>' command", () => {
27
+ const result = parseProjectCliArgs(["tasks", "t-1"])
28
+ expect(result).toEqual({ command: "task-detail", args: { taskId: "t-1" } })
29
+ })
30
+
31
+ test("parses 'claim <description>' with flags", () => {
32
+ const result = parseProjectCliArgs(["claim", "implement auth", "--session", "c1", "--branch", "feat/auth"])
33
+ expect(result).toEqual({
34
+ command: "claim",
35
+ args: { description: "implement auth", session: "c1", branch: "feat/auth" },
36
+ })
37
+ })
38
+
39
+ test("parses 'complete <id>'", () => {
40
+ const result = parseProjectCliArgs(["complete", "t-1"])
41
+ expect(result).toEqual({ command: "complete", args: { taskId: "t-1" } })
42
+ })
43
+
44
+ test("parses 'delegate <request>'", () => {
45
+ const result = parseProjectCliArgs(["delegate", "ensure", "postgres", "running"])
46
+ expect(result).toEqual({ command: "delegate", args: { request: "ensure postgres running" } })
47
+ })
48
+
49
+ test("parses --project flag", () => {
50
+ const result = parseProjectCliArgs(["sessions", "--project", "p1"])
51
+ expect(result).toEqual({ command: "sessions", args: { workspaceId: "p1" } })
52
+ })
53
+
54
+ test("returns help for no args", () => {
55
+ const result = parseProjectCliArgs([])
56
+ expect(result.command).toBe("help")
57
+ })
58
+
59
+ test("returns help for --help", () => {
60
+ const result = parseProjectCliArgs(["--help"])
61
+ expect(result.command).toBe("help")
62
+ })
63
+ })
64
+
65
+ describe("formatOutput", () => {
66
+ test("formats sessions as table when not --json", () => {
67
+ const output = formatOutput("sessions", [
68
+ { chatId: "c1", intent: "auth work", status: "active", provider: "claude", branch: null, filesTouched: [], commandsRun: [], lastActivity: "2026-04-04T10:00:00Z" },
69
+ ], false)
70
+ expect(output).toContain("c1")
71
+ expect(output).toContain("auth work")
72
+ })
73
+
74
+ test("formats as JSON when json=true", () => {
75
+ const data = [{ chatId: "c1" }]
76
+ const output = formatOutput("sessions", data, true)
77
+ expect(JSON.parse(output)).toEqual(data)
78
+ })
79
+ })
@@ -0,0 +1,109 @@
1
+ import { describe, test, expect, afterEach } from "bun:test"
2
+ import { mkdtemp, rm, stat, writeFile } from "node:fs/promises"
3
+ import os from "node:os"
4
+ import path from "node:path"
5
+ import { WorkspaceConfigManager } from "./workspace-config-manager"
6
+ import type { AgentConfig } from "../shared/agent-config-types"
7
+
8
+ const makeConfig = (id: string): AgentConfig => ({
9
+ id,
10
+ name: `Agent ${id}`,
11
+ description: `Test agent ${id}`,
12
+ provider: "claude",
13
+ model: "opus-4",
14
+ systemPrompt: "You are helpful.",
15
+ tools: ["bash", "read"],
16
+ temperature: 0.7,
17
+ })
18
+
19
+ describe("WorkspaceConfigManager", () => {
20
+ let tmpDir: string
21
+ let mgr: WorkspaceConfigManager
22
+
23
+ const setup = async () => {
24
+ tmpDir = await mkdtemp(path.join(os.tmpdir(), "wcm-test-"))
25
+ mgr = new WorkspaceConfigManager(tmpDir)
26
+ }
27
+
28
+ afterEach(async () => {
29
+ if (tmpDir) {
30
+ await rm(tmpDir, { recursive: true, force: true })
31
+ }
32
+ })
33
+
34
+ test("initWorkspaceDir creates .git, agents/, workflows/", async () => {
35
+ await setup()
36
+ const wsPath = await mgr.initWorkspaceDir("ws-1")
37
+ expect(wsPath).toBe(path.join(tmpDir, "ws-1"))
38
+ const gitStat = await stat(path.join(wsPath, ".git"))
39
+ expect(gitStat.isDirectory()).toBe(true)
40
+ const agentsStat = await stat(path.join(wsPath, "agents"))
41
+ expect(agentsStat.isDirectory()).toBe(true)
42
+ const workflowsStat = await stat(path.join(wsPath, "workflows"))
43
+ expect(workflowsStat.isDirectory()).toBe(true)
44
+ })
45
+
46
+ test("initWorkspaceDir is idempotent", async () => {
47
+ await setup()
48
+ await mgr.initWorkspaceDir("ws-2")
49
+ const wsPath = await mgr.initWorkspaceDir("ws-2")
50
+ expect(wsPath).toBe(path.join(tmpDir, "ws-2"))
51
+ })
52
+
53
+ test("saveAgentConfig writes agents/<id>.yaml", async () => {
54
+ await setup()
55
+ await mgr.initWorkspaceDir("ws-3")
56
+ const config = makeConfig("agent-a")
57
+ await mgr.saveAgentConfig("ws-3", config)
58
+ const filePath = path.join(tmpDir, "ws-3", "agents", "agent-a.yaml")
59
+ const fileStat = await stat(filePath)
60
+ expect(fileStat.isFile()).toBe(true)
61
+ })
62
+
63
+ test("readAgentConfig roundtrips saved config", async () => {
64
+ await setup()
65
+ await mgr.initWorkspaceDir("ws-4")
66
+ const config = makeConfig("agent-b")
67
+ await mgr.saveAgentConfig("ws-4", config)
68
+ const read = await mgr.readAgentConfig("ws-4", "agent-b")
69
+ expect(read).toEqual(config)
70
+ })
71
+
72
+ test("readAgentConfig returns null for missing agent", async () => {
73
+ await setup()
74
+ await mgr.initWorkspaceDir("ws-5")
75
+ const read = await mgr.readAgentConfig("ws-5", "nope")
76
+ expect(read).toBeNull()
77
+ })
78
+
79
+ test("listAgentConfigs returns all saved, skips non-yaml", async () => {
80
+ await setup()
81
+ await mgr.initWorkspaceDir("ws-6")
82
+ await mgr.saveAgentConfig("ws-6", makeConfig("x"))
83
+ await mgr.saveAgentConfig("ws-6", makeConfig("y"))
84
+ // write a non-yaml file that should be skipped
85
+ await writeFile(path.join(tmpDir, "ws-6", "agents", "readme.txt"), "ignore me")
86
+ const configs = await mgr.listAgentConfigs("ws-6")
87
+ expect(configs).toHaveLength(2)
88
+ const ids = configs.map((c: AgentConfig) => c.id).sort()
89
+ expect(ids).toEqual(["x", "y"])
90
+ })
91
+
92
+ test("removeAgentConfig deletes the file", async () => {
93
+ await setup()
94
+ await mgr.initWorkspaceDir("ws-7")
95
+ await mgr.saveAgentConfig("ws-7", makeConfig("del-me"))
96
+ await mgr.removeAgentConfig("ws-7", "del-me")
97
+ const configs = await mgr.listAgentConfigs("ws-7")
98
+ expect(configs).toHaveLength(0)
99
+ })
100
+
101
+ test("commitConfig returns a non-empty hash", async () => {
102
+ await setup()
103
+ await mgr.initWorkspaceDir("ws-8")
104
+ await mgr.saveAgentConfig("ws-8", makeConfig("committed"))
105
+ const hash = await mgr.commitConfig("ws-8", "initial config")
106
+ expect(typeof hash).toBe("string")
107
+ expect(hash.length).toBeGreaterThanOrEqual(7)
108
+ })
109
+ })
@@ -0,0 +1,83 @@
1
+ import { mkdir, rm, readdir, readFile, writeFile } from "node:fs/promises"
2
+ import path from "node:path"
3
+ import { $ } from "bun"
4
+ import yaml from "js-yaml"
5
+ import type { AgentConfig } from "../shared/agent-config-types"
6
+
7
+ const LOG_PREFIX = "[WorkspaceConfigManager]"
8
+
9
+ export class WorkspaceConfigManager {
10
+ constructor(private basePath: string) {}
11
+
12
+ async initWorkspaceDir(workspaceId: string): Promise<string> {
13
+ const wsPath = this.getWorkspacePath(workspaceId)
14
+ await mkdir(path.join(wsPath, "agents"), { recursive: true })
15
+ await mkdir(path.join(wsPath, "workflows"), { recursive: true })
16
+ await $`git -C ${wsPath} init`.quiet()
17
+ await $`git -C ${wsPath} config user.email "workspace@tinkaria.local"`.quiet()
18
+ await $`git -C ${wsPath} config user.name "Tinkaria Workspace"`.quiet()
19
+ return wsPath
20
+ }
21
+
22
+ async saveAgentConfig(workspaceId: string, config: AgentConfig): Promise<void> {
23
+ const agentDir = path.join(this.getWorkspacePath(workspaceId), "agents")
24
+ await mkdir(agentDir, { recursive: true })
25
+ const filePath = this.getAgentConfigPath(workspaceId, config.id)
26
+ const content = yaml.dump(config, { sortKeys: true })
27
+ await writeFile(filePath, content, "utf-8")
28
+ }
29
+
30
+ async readAgentConfig(workspaceId: string, agentId: string): Promise<AgentConfig | null> {
31
+ const filePath = this.getAgentConfigPath(workspaceId, agentId)
32
+ try {
33
+ const content = await readFile(filePath, "utf-8")
34
+ return yaml.load(content) as AgentConfig
35
+ } catch (error) {
36
+ const msg = error instanceof Error ? error.message : String(error)
37
+ if (msg.includes("ENOENT") || error instanceof SyntaxError) {
38
+ return null
39
+ }
40
+ console.warn(`${LOG_PREFIX} readAgentConfig failed: ${msg}`)
41
+ return null
42
+ }
43
+ }
44
+
45
+ async listAgentConfigs(workspaceId: string): Promise<AgentConfig[]> {
46
+ const agentsDir = path.join(this.getWorkspacePath(workspaceId), "agents")
47
+ const entries = await readdir(agentsDir)
48
+ const configs: AgentConfig[] = []
49
+ for (const entry of entries) {
50
+ if (!entry.endsWith(".yaml")) continue
51
+ try {
52
+ const content = await readFile(path.join(agentsDir, entry), "utf-8")
53
+ const parsed = yaml.load(content) as AgentConfig
54
+ configs.push(parsed)
55
+ } catch (error) {
56
+ const msg = error instanceof Error ? error.message : String(error)
57
+ console.warn(`${LOG_PREFIX} skipping unparseable ${entry}: ${msg}`)
58
+ }
59
+ }
60
+ return configs
61
+ }
62
+
63
+ async removeAgentConfig(workspaceId: string, agentId: string): Promise<void> {
64
+ const filePath = this.getAgentConfigPath(workspaceId, agentId)
65
+ await rm(filePath, { force: true })
66
+ }
67
+
68
+ async commitConfig(workspaceId: string, message: string): Promise<string> {
69
+ const wsPath = this.getWorkspacePath(workspaceId)
70
+ await $`git -C ${wsPath} add -A`.quiet()
71
+ await $`git -C ${wsPath} commit -m ${message} --allow-empty`.quiet()
72
+ const result = await $`git -C ${wsPath} rev-parse HEAD`.quiet()
73
+ return result.text().trim()
74
+ }
75
+
76
+ private getWorkspacePath(workspaceId: string): string {
77
+ return path.join(this.basePath, workspaceId)
78
+ }
79
+
80
+ private getAgentConfigPath(workspaceId: string, agentId: string): string {
81
+ return path.join(this.getWorkspacePath(workspaceId), "agents", `${agentId}.yaml`)
82
+ }
83
+ }
@@ -0,0 +1,109 @@
1
+ import { afterEach, describe, expect, test } from "bun:test"
2
+ import { mkdtemp, rm, stat } from "node:fs/promises"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { EventStore } from "./event-store"
6
+ import { WorkspaceConfigManager } from "./workspace-config-manager"
7
+ import { WorkspaceDirectoryPolicy } from "./workspace-directory-policy"
8
+ import type { AgentConfig } from "../shared/agent-config-types"
9
+
10
+ const tempDirs: string[] = []
11
+
12
+ afterEach(async () => {
13
+ await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })))
14
+ })
15
+
16
+ async function createTempDir(prefix: string) {
17
+ const dir = await mkdtemp(join(tmpdir(), prefix))
18
+ tempDirs.push(dir)
19
+ return dir
20
+ }
21
+
22
+ const makeConfig = (id: string): AgentConfig => ({
23
+ id,
24
+ name: `Agent ${id}`,
25
+ description: `Test agent ${id}`,
26
+ provider: "claude",
27
+ model: "opus-4",
28
+ })
29
+
30
+ describe("WorkspaceDirectoryPolicy", () => {
31
+ test("onWorkspaceOpened creates workspace dir", async () => {
32
+ const storeDir = await createTempDir("wdp-store-")
33
+ const wsBase = await createTempDir("wdp-ws-")
34
+ const store = new EventStore(storeDir)
35
+ await store.initialize()
36
+ const mgr = new WorkspaceConfigManager(wsBase)
37
+ const policy = new WorkspaceDirectoryPolicy(store, mgr)
38
+
39
+ await policy.onWorkspaceOpened("ws-test")
40
+
41
+ const gitStat = await stat(join(wsBase, "ws-test", ".git"))
42
+ expect(gitStat.isDirectory()).toBe(true)
43
+ const agentsStat = await stat(join(wsBase, "ws-test", "agents"))
44
+ expect(agentsStat.isDirectory()).toBe(true)
45
+ })
46
+
47
+ test("onAgentConfigSaved writes YAML and records commit hash", async () => {
48
+ const storeDir = await createTempDir("wdp-store-")
49
+ const wsBase = await createTempDir("wdp-ws-")
50
+ const store = new EventStore(storeDir)
51
+ await store.initialize()
52
+ const workspace = await store.openProject("/tmp/wdp-project")
53
+ const mgr = new WorkspaceConfigManager(wsBase)
54
+ const policy = new WorkspaceDirectoryPolicy(store, mgr)
55
+
56
+ // Init the workspace dir first
57
+ await mgr.initWorkspaceDir(workspace.id)
58
+
59
+ // Save a config via EventStore so the record exists for commitAgentConfig
60
+ await store.saveAgentConfig(workspace.id, "agent-1", makeConfig("agent-1"))
61
+
62
+ // Now run the policy
63
+ await policy.onAgentConfigSaved(workspace.id, "agent-1", makeConfig("agent-1"))
64
+
65
+ // YAML file should exist
66
+ const yamlStat = await stat(join(wsBase, workspace.id, "agents", "agent-1.yaml"))
67
+ expect(yamlStat.isFile()).toBe(true)
68
+
69
+ // Commit hash should be recorded in EventStore
70
+ const record = store.state.agentConfigsByWorkspace.get(workspace.id)?.get("agent-1")
71
+ expect(record).toBeDefined()
72
+ expect(record!.lastCommitHash).toBeDefined()
73
+ expect(typeof record!.lastCommitHash).toBe("string")
74
+ expect(record!.lastCommitHash!.length).toBeGreaterThanOrEqual(7)
75
+ })
76
+
77
+ test("onAgentConfigRemoved deletes YAML from disk", async () => {
78
+ const storeDir = await createTempDir("wdp-store-")
79
+ const wsBase = await createTempDir("wdp-ws-")
80
+ const store = new EventStore(storeDir)
81
+ await store.initialize()
82
+ const mgr = new WorkspaceConfigManager(wsBase)
83
+ const policy = new WorkspaceDirectoryPolicy(store, mgr)
84
+
85
+ // Setup: init dir, write a config
86
+ await mgr.initWorkspaceDir("ws-rm")
87
+ await mgr.saveAgentConfig("ws-rm", makeConfig("agent-del"))
88
+ await mgr.commitConfig("ws-rm", "setup")
89
+
90
+ // Verify file exists
91
+ const yamlPath = join(wsBase, "ws-rm", "agents", "agent-del.yaml")
92
+ const before = await stat(yamlPath)
93
+ expect(before.isFile()).toBe(true)
94
+
95
+ // Remove via policy
96
+ await policy.onAgentConfigRemoved("ws-rm", "agent-del")
97
+
98
+ // File should be gone
99
+ let exists = true
100
+ try {
101
+ await stat(yamlPath)
102
+ } catch (err) {
103
+ if (err instanceof Error && "code" in err && (err as NodeJS.ErrnoException).code === "ENOENT") {
104
+ exists = false
105
+ }
106
+ }
107
+ expect(exists).toBe(false)
108
+ })
109
+ })
@@ -0,0 +1,56 @@
1
+ import type { WorkspaceConfigManager } from "./workspace-config-manager"
2
+ import type { EventStore } from "./event-store"
3
+ import type { AgentConfig } from "../shared/agent-config-types"
4
+
5
+ const LOG_PREFIX = "[WorkspaceDirectoryPolicy]"
6
+
7
+ export class WorkspaceDirectoryPolicy {
8
+ constructor(
9
+ private store: EventStore,
10
+ private configManager: WorkspaceConfigManager,
11
+ private onStateChange?: () => void,
12
+ ) {}
13
+
14
+ /** Called after workspace_opened — creates physical workspace directory + git init */
15
+ async onWorkspaceOpened(workspaceId: string): Promise<void> {
16
+ try {
17
+ await this.configManager.initWorkspaceDir(workspaceId)
18
+ } catch (err) {
19
+ console.warn(
20
+ `${LOG_PREFIX} Failed to init workspace dir:`,
21
+ err instanceof Error ? err.message : String(err),
22
+ )
23
+ }
24
+ }
25
+
26
+ /** Called after agent_config_saved — writes YAML to disk + git commit, then records commit hash */
27
+ async onAgentConfigSaved(workspaceId: string, agentId: string, config: AgentConfig): Promise<void> {
28
+ try {
29
+ await this.configManager.saveAgentConfig(workspaceId, config)
30
+ const commitHash = await this.configManager.commitConfig(
31
+ workspaceId,
32
+ `Save agent config: ${config.name}`,
33
+ )
34
+ await this.store.commitAgentConfig(workspaceId, agentId, commitHash)
35
+ this.onStateChange?.()
36
+ } catch (err) {
37
+ console.warn(
38
+ `${LOG_PREFIX} Failed to commit agent config:`,
39
+ err instanceof Error ? err.message : String(err),
40
+ )
41
+ }
42
+ }
43
+
44
+ /** Called after agent_config_removed — deletes YAML from disk + git commit */
45
+ async onAgentConfigRemoved(workspaceId: string, agentId: string): Promise<void> {
46
+ try {
47
+ await this.configManager.removeAgentConfig(workspaceId, agentId)
48
+ await this.configManager.commitConfig(workspaceId, `Remove agent config: ${agentId}`)
49
+ } catch (err) {
50
+ console.warn(
51
+ `${LOG_PREFIX} Failed to remove agent config from disk:`,
52
+ err instanceof Error ? err.message : String(err),
53
+ )
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,25 @@
1
+ export interface AgentConfig {
2
+ id: string
3
+ name: string
4
+ description: string
5
+ provider: "claude" | "codex"
6
+ model: string
7
+ systemPrompt?: string
8
+ tools?: string[]
9
+ temperature?: number
10
+ }
11
+
12
+ export interface AgentConfigRecord {
13
+ id: string
14
+ workspaceId: string
15
+ config: AgentConfig
16
+ createdAt: number
17
+ updatedAt: number
18
+ lastCommitHash?: string
19
+ }
20
+
21
+ export interface AgentConfigSnapshot {
22
+ workspaceId: string
23
+ configs: AgentConfigRecord[]
24
+ lastUpdated: string
25
+ }