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,191 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test"
2
+ import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { agentsExtension } from "./server"
6
+
7
+ /** Helper: create a temp project dir, return its path */
8
+ async function makeTempProject(): Promise<string> {
9
+ return mkdtemp(join(tmpdir(), "agents-ext-test-"))
10
+ }
11
+
12
+ /** Helper: invoke a route handler by path */
13
+ function findRoute(projectPath: string, method: string, path: string) {
14
+ const routes = agentsExtension.routes({ projectPath })
15
+ return routes.find((r) => r.method === method && r.path === path)
16
+ }
17
+
18
+ /** Helper: build a minimal GET request */
19
+ function fakeRequest(path: string): Request {
20
+ return new Request(`http://localhost${path}`)
21
+ }
22
+
23
+ describe("agents extension", () => {
24
+ let tmpDir: string
25
+
26
+ beforeEach(async () => {
27
+ tmpDir = await makeTempProject()
28
+ })
29
+
30
+ afterEach(async () => {
31
+ await rm(tmpDir, { recursive: true, force: true })
32
+ })
33
+
34
+ // ── /claude-md ──────────────────────────────────────────
35
+
36
+ describe("GET /claude-md", () => {
37
+ test("parses CLAUDE.md into structured sections", async () => {
38
+ const claudeMd = [
39
+ "## Architecture",
40
+ "Event sourcing with JSONL logs.",
41
+ "",
42
+ "## Dev",
43
+ "```bash",
44
+ "bun run dev",
45
+ "```",
46
+ ].join("\n")
47
+
48
+ await writeFile(join(tmpDir, "CLAUDE.md"), claudeMd)
49
+
50
+ const route = findRoute(tmpDir, "GET", "/claude-md")
51
+ expect(route).toBeDefined()
52
+
53
+ const res = await route!.handler(fakeRequest("/claude-md"), {})
54
+ expect(res.status).toBe(200)
55
+ expect(res.headers.get("Content-Type")).toBe("application/json")
56
+
57
+ const body = await res.json()
58
+ expect(body.sections).toBeArrayOfSize(2)
59
+ expect(body.sections[0].heading).toBe("Architecture")
60
+ expect(body.sections[0].content).toBe("Event sourcing with JSONL logs.")
61
+ expect(body.sections[1].heading).toBe("Dev")
62
+ expect(body.sections[1].content).toContain("bun run dev")
63
+ })
64
+
65
+ test("returns empty sections when CLAUDE.md does not exist", async () => {
66
+ const route = findRoute(tmpDir, "GET", "/claude-md")
67
+ expect(route).toBeDefined()
68
+
69
+ const res = await route!.handler(fakeRequest("/claude-md"), {})
70
+ expect(res.status).toBe(200)
71
+
72
+ const body = await res.json()
73
+ expect(body.sections).toBeArrayOfSize(0)
74
+ })
75
+
76
+ test("handles content before the first heading", async () => {
77
+ const claudeMd = [
78
+ "# Tinkaria",
79
+ "",
80
+ "Top-level intro paragraph.",
81
+ "",
82
+ "## Setup",
83
+ "Run bun install.",
84
+ ].join("\n")
85
+
86
+ await writeFile(join(tmpDir, "CLAUDE.md"), claudeMd)
87
+
88
+ const route = findRoute(tmpDir, "GET", "/claude-md")
89
+ const res = await route!.handler(fakeRequest("/claude-md"), {})
90
+ const body = await res.json()
91
+
92
+ // preamble before first ## gets captured with empty heading
93
+ expect(body.sections.length).toBeGreaterThanOrEqual(2)
94
+ expect(body.sections[0].heading).toBe("")
95
+ expect(body.sections[0].content).toContain("Top-level intro paragraph.")
96
+ expect(body.sections[1].heading).toBe("Setup")
97
+ })
98
+ })
99
+
100
+ // ── /skills ─────────────────────────────────────────────
101
+
102
+ describe("GET /skills", () => {
103
+ test("lists .md files from .claude/commands/", async () => {
104
+ const cmdDir = join(tmpDir, ".claude", "commands")
105
+ await mkdir(cmdDir, { recursive: true })
106
+ await writeFile(join(cmdDir, "deploy.md"), "Deploy the app to prod")
107
+ await writeFile(join(cmdDir, "lint.md"), "Run linter and fix issues")
108
+
109
+ const route = findRoute(tmpDir, "GET", "/skills")
110
+ expect(route).toBeDefined()
111
+
112
+ const res = await route!.handler(fakeRequest("/skills"), {})
113
+ expect(res.status).toBe(200)
114
+
115
+ const body = await res.json()
116
+ expect(body.skills).toBeArrayOfSize(2)
117
+
118
+ const names = body.skills.map((s: { name: string }) => s.name).sort()
119
+ expect(names).toEqual(["deploy", "lint"])
120
+
121
+ const deploy = body.skills.find((s: { name: string }) => s.name === "deploy")
122
+ expect(deploy.filename).toBe("deploy.md")
123
+ expect(deploy.content).toBe("Deploy the app to prod")
124
+ })
125
+
126
+ test("returns empty array when .claude/commands/ does not exist", async () => {
127
+ const route = findRoute(tmpDir, "GET", "/skills")
128
+ const res = await route!.handler(fakeRequest("/skills"), {})
129
+ expect(res.status).toBe(200)
130
+
131
+ const body = await res.json()
132
+ expect(body.skills).toBeArrayOfSize(0)
133
+ })
134
+
135
+ test("ignores non-.md files in commands dir", async () => {
136
+ const cmdDir = join(tmpDir, ".claude", "commands")
137
+ await mkdir(cmdDir, { recursive: true })
138
+ await writeFile(join(cmdDir, "valid.md"), "A skill")
139
+ await writeFile(join(cmdDir, "notes.txt"), "Not a skill")
140
+ await writeFile(join(cmdDir, ".hidden"), "Also not a skill")
141
+
142
+ const route = findRoute(tmpDir, "GET", "/skills")
143
+ const res = await route!.handler(fakeRequest("/skills"), {})
144
+ const body = await res.json()
145
+
146
+ expect(body.skills).toBeArrayOfSize(1)
147
+ expect(body.skills[0].name).toBe("valid")
148
+ })
149
+ })
150
+
151
+ // ── /agents-md ──────────────────────────────────────────
152
+
153
+ describe("GET /agents-md", () => {
154
+ test("parses .claude/agents.md into sections", async () => {
155
+ const agentsMd = [
156
+ "## reviewer",
157
+ "You are a code reviewer. Be thorough.",
158
+ "",
159
+ "## deployer",
160
+ "Handle deployment pipelines.",
161
+ ].join("\n")
162
+
163
+ await mkdir(join(tmpDir, ".claude"), { recursive: true })
164
+ await writeFile(join(tmpDir, ".claude", "agents.md"), agentsMd)
165
+
166
+ const route = findRoute(tmpDir, "GET", "/agents-md")
167
+ expect(route).toBeDefined()
168
+
169
+ const res = await route!.handler(fakeRequest("/agents-md"), {})
170
+ expect(res.status).toBe(200)
171
+
172
+ const body = await res.json()
173
+ expect(body.found).toBe(true)
174
+ expect(body.sections).toBeArrayOfSize(2)
175
+ expect(body.sections[0].heading).toBe("reviewer")
176
+ expect(body.sections[0].content).toBe("You are a code reviewer. Be thorough.")
177
+ expect(body.sections[1].heading).toBe("deployer")
178
+ expect(body.sections[1].content).toBe("Handle deployment pipelines.")
179
+ })
180
+
181
+ test("returns found:false and empty sections when agents.md missing", async () => {
182
+ const route = findRoute(tmpDir, "GET", "/agents-md")
183
+ const res = await route!.handler(fakeRequest("/agents-md"), {})
184
+ expect(res.status).toBe(200)
185
+
186
+ const body = await res.json()
187
+ expect(body.found).toBe(false)
188
+ expect(body.sections).toBeArrayOfSize(0)
189
+ })
190
+ })
191
+ })
@@ -0,0 +1,108 @@
1
+ import { readdir } from "node:fs/promises"
2
+ import { join, basename } from "node:path"
3
+ import type { ServerExtension } from "../../../shared/extension-types"
4
+
5
+ // ── Helpers ───────────────────────────────────────────────
6
+
7
+ function jsonResponse(data: unknown, status = 200): Response {
8
+ return new Response(JSON.stringify(data), {
9
+ status,
10
+ headers: { "Content-Type": "application/json" },
11
+ })
12
+ }
13
+
14
+ function parseMarkdownSections(content: string): { heading: string; content: string }[] {
15
+ const sections: { heading: string; content: string }[] = []
16
+ const lines = content.split("\n")
17
+ let currentHeading = ""
18
+ let currentContent: string[] = []
19
+
20
+ for (const line of lines) {
21
+ if (line.startsWith("## ")) {
22
+ if (currentHeading || currentContent.length > 0) {
23
+ sections.push({ heading: currentHeading, content: currentContent.join("\n").trim() })
24
+ }
25
+ currentHeading = line.slice(3).trim()
26
+ currentContent = []
27
+ } else {
28
+ currentContent.push(line)
29
+ }
30
+ }
31
+ if (currentHeading || currentContent.length > 0) {
32
+ sections.push({ heading: currentHeading, content: currentContent.join("\n").trim() })
33
+ }
34
+ return sections
35
+ }
36
+
37
+ // ── Route handlers ────────────────────────────────────────
38
+
39
+ function handleClaudeMd(projectPath: string) {
40
+ return async (_req: Request, _params: Record<string, string>): Promise<Response> => {
41
+ const filePath = join(projectPath, "CLAUDE.md")
42
+ const file = Bun.file(filePath)
43
+
44
+ if (!(await file.exists())) {
45
+ return jsonResponse({ sections: [] })
46
+ }
47
+
48
+ const text = await file.text()
49
+ return jsonResponse({ sections: parseMarkdownSections(text) })
50
+ }
51
+ }
52
+
53
+ function handleSkills(projectPath: string) {
54
+ return async (_req: Request, _params: Record<string, string>): Promise<Response> => {
55
+ const cmdDir = join(projectPath, ".claude", "commands")
56
+
57
+ let entries: string[]
58
+ try {
59
+ entries = await readdir(cmdDir)
60
+ } catch (_e: unknown) {
61
+ return jsonResponse({ skills: [] })
62
+ }
63
+
64
+ const mdFiles = entries.filter((f) => f.endsWith(".md"))
65
+ const skills = await Promise.all(
66
+ mdFiles.map(async (filename) => {
67
+ const content = await Bun.file(join(cmdDir, filename)).text()
68
+ return {
69
+ name: basename(filename, ".md"),
70
+ filename,
71
+ content,
72
+ }
73
+ }),
74
+ )
75
+
76
+ return jsonResponse({ skills })
77
+ }
78
+ }
79
+
80
+ function handleAgentsMd(projectPath: string) {
81
+ return async (_req: Request, _params: Record<string, string>): Promise<Response> => {
82
+ const filePath = join(projectPath, ".claude", "agents.md")
83
+ const file = Bun.file(filePath)
84
+
85
+ if (!(await file.exists())) {
86
+ return jsonResponse({ found: false, sections: [] })
87
+ }
88
+
89
+ const text = await file.text()
90
+ return jsonResponse({ found: true, sections: parseMarkdownSections(text) })
91
+ }
92
+ }
93
+
94
+ // ── Extension export ──────────────────────────────────────
95
+
96
+ export const agentsExtension: ServerExtension = {
97
+ id: "agents",
98
+ name: "Agents",
99
+ icon: "bot",
100
+ detect: ["CLAUDE.md", ".claude/"],
101
+ routes(ctx) {
102
+ return [
103
+ { method: "GET", path: "/claude-md", handler: handleClaudeMd(ctx.projectPath) },
104
+ { method: "GET", path: "/skills", handler: handleSkills(ctx.projectPath) },
105
+ { method: "GET", path: "/agents-md", handler: handleAgentsMd(ctx.projectPath) },
106
+ ]
107
+ },
108
+ }
@@ -0,0 +1,284 @@
1
+ import { describe, test, expect, afterEach, beforeAll } from "bun:test"
2
+ import { mkdtemp, mkdir, rm } from "node:fs/promises"
3
+ import { tmpdir } from "node:os"
4
+ import path from "node:path"
5
+ import { buildC3EntityTree, c3Extension } from "./server"
6
+
7
+ let tempDir: string
8
+ const originalC3xMode = process.env.C3X_MODE
9
+
10
+ async function makeTempProject(): Promise<string> {
11
+ const dir = await mkdtemp(path.join(tmpdir(), "c3-test-"))
12
+ await mkdir(path.join(dir, ".c3"), { recursive: true })
13
+ return dir
14
+ }
15
+
16
+ // Check if c3x is available on this machine
17
+ let c3xAvailable = false
18
+ beforeAll(async () => {
19
+ try {
20
+ const proc = Bun.spawn(["which", "c3x"], { stdout: "pipe", stderr: "pipe" })
21
+ await proc.exited
22
+ c3xAvailable = proc.exitCode === 0
23
+ } catch {
24
+ c3xAvailable = false
25
+ }
26
+ })
27
+
28
+ afterEach(async () => {
29
+ if (originalC3xMode === undefined) {
30
+ delete process.env.C3X_MODE
31
+ } else {
32
+ process.env.C3X_MODE = originalC3xMode
33
+ }
34
+ if (tempDir) {
35
+ await rm(tempDir, { recursive: true, force: true })
36
+ }
37
+ })
38
+
39
+ // ── Manifest shape ──────────────────────────────────────────────────
40
+
41
+ describe("c3Extension manifest", () => {
42
+ test("has correct id, name, icon, detect", () => {
43
+ expect(c3Extension.id).toBe("c3")
44
+ expect(c3Extension.name).toBe("Architecture")
45
+ expect(c3Extension.icon).toBe("building-2")
46
+ expect(c3Extension.detect).toEqual([".c3/"])
47
+ })
48
+ })
49
+
50
+ // ── Route structure ─────────────────────────────────────────────────
51
+
52
+ describe("c3Extension routes", () => {
53
+ test("returns three routes", () => {
54
+ const routes = c3Extension.routes({ projectPath: "/tmp/fake" })
55
+ expect(routes).toHaveLength(3)
56
+ })
57
+
58
+ test("list route is GET /list", () => {
59
+ const routes = c3Extension.routes({ projectPath: "/tmp/fake" })
60
+ const list = routes.find((r) => r.path === "/list")
61
+ expect(list).toBeDefined()
62
+ expect(list!.method).toBe("GET")
63
+ expect(typeof list!.handler).toBe("function")
64
+ })
65
+
66
+ test("read route is GET /read", () => {
67
+ const routes = c3Extension.routes({ projectPath: "/tmp/fake" })
68
+ const read = routes.find((r) => r.path === "/read")
69
+ expect(read).toBeDefined()
70
+ expect(read!.method).toBe("GET")
71
+ })
72
+
73
+ test("graph route is GET /graph", () => {
74
+ const routes = c3Extension.routes({ projectPath: "/tmp/fake" })
75
+ const graph = routes.find((r) => r.path === "/graph")
76
+ expect(graph).toBeDefined()
77
+ expect(graph!.method).toBe("GET")
78
+ })
79
+ })
80
+
81
+ // ── Tree structure ─────────────────────────────────────────────────
82
+
83
+ describe("buildC3EntityTree", () => {
84
+ test("nests parented entities while keeping unparented entities at the root", () => {
85
+ expect(
86
+ buildC3EntityTree([
87
+ { id: "c3-0", type: "system", title: "tinkaria" },
88
+ { id: "c3-1", type: "container", title: "client", parent: "c3-0" },
89
+ { id: "c3-120", type: "component", title: "extensions", parent: "c3-1" },
90
+ { id: "ref-project-context", type: "ref", title: "project-context" },
91
+ ]),
92
+ ).toEqual([
93
+ {
94
+ id: "c3-0",
95
+ type: "system",
96
+ title: "tinkaria",
97
+ children: [
98
+ {
99
+ id: "c3-1",
100
+ type: "container",
101
+ title: "client",
102
+ parent: "c3-0",
103
+ children: [
104
+ {
105
+ id: "c3-120",
106
+ type: "component",
107
+ title: "extensions",
108
+ parent: "c3-1",
109
+ },
110
+ ],
111
+ },
112
+ ],
113
+ },
114
+ { id: "ref-project-context", type: "ref", title: "project-context" },
115
+ ])
116
+ })
117
+ })
118
+
119
+ // ── Parameter validation ────────────────────────────────────────────
120
+
121
+ describe("read handler validation", () => {
122
+ test("returns 400 when id query param is missing", async () => {
123
+ tempDir = await makeTempProject()
124
+ const routes = c3Extension.routes({ projectPath: tempDir })
125
+ const read = routes.find((r) => r.path === "/read")!
126
+
127
+ const req = new Request("http://localhost/api/ext/c3/read?projectPath=" + encodeURIComponent(tempDir))
128
+ const res = await read.handler(req, {})
129
+
130
+ expect(res.status).toBe(400)
131
+ const body = await res.json()
132
+ expect(body.error).toMatch(/id/i)
133
+ })
134
+ })
135
+
136
+ describe("graph handler validation", () => {
137
+ test("returns 400 when id query param is missing", async () => {
138
+ tempDir = await makeTempProject()
139
+ const routes = c3Extension.routes({ projectPath: tempDir })
140
+ const graph = routes.find((r) => r.path === "/graph")!
141
+
142
+ const req = new Request("http://localhost/api/ext/c3/graph?projectPath=" + encodeURIComponent(tempDir))
143
+ const res = await graph.handler(req, {})
144
+
145
+ expect(res.status).toBe(400)
146
+ const body = await res.json()
147
+ expect(body.error).toMatch(/id/i)
148
+ })
149
+ })
150
+
151
+ // ── c3x integration (only when c3x is available) ───────────────────
152
+
153
+ describe("c3x integration", () => {
154
+ test("list handler returns JSON when c3x succeeds", async () => {
155
+ if (!c3xAvailable) {
156
+ console.log("SKIP: c3x not available")
157
+ return
158
+ }
159
+
160
+ // Use the actual project which has .c3/
161
+ const projectPath = path.resolve(import.meta.dir, "../../../..")
162
+ const routes = c3Extension.routes({ projectPath })
163
+ const list = routes.find((r) => r.path === "/list")!
164
+
165
+ const req = new Request("http://localhost/api/ext/c3/list?projectPath=" + encodeURIComponent(projectPath))
166
+ const res = await list.handler(req, {})
167
+
168
+ expect(res.status).toBe(200)
169
+ const body = await res.json()
170
+ expect(Array.isArray(body.data)).toBe(true)
171
+ expect(body.data.length).toBeGreaterThan(0)
172
+ expect(body.data[0]).toMatchObject({
173
+ id: "c3-0",
174
+ type: "system",
175
+ children: expect.arrayContaining([
176
+ expect.objectContaining({
177
+ id: expect.any(String),
178
+ type: "container",
179
+ children: expect.arrayContaining([
180
+ expect.objectContaining({
181
+ id: expect.any(String),
182
+ type: "component",
183
+ }),
184
+ ]),
185
+ }),
186
+ ]),
187
+ })
188
+ })
189
+
190
+ test("list handler ignores inherited agent output mode", async () => {
191
+ if (!c3xAvailable) {
192
+ console.log("SKIP: c3x not available")
193
+ return
194
+ }
195
+
196
+ process.env.C3X_MODE = "agent"
197
+ const projectPath = path.resolve(import.meta.dir, "../../../..")
198
+ const routes = c3Extension.routes({ projectPath })
199
+ const list = routes.find((r) => r.path === "/list")!
200
+
201
+ const req = new Request("http://localhost/api/ext/c3/list?projectPath=" + encodeURIComponent(projectPath))
202
+ const res = await list.handler(req, {})
203
+
204
+ expect(res.status).toBe(200)
205
+ const body = await res.json()
206
+ expect(Array.isArray(body.data)).toBe(true)
207
+ expect(body.data[0]).toMatchObject({
208
+ id: "c3-0",
209
+ type: "system",
210
+ })
211
+ })
212
+
213
+ test("read handler returns content for a valid id", async () => {
214
+ if (!c3xAvailable) {
215
+ console.log("SKIP: c3x not available")
216
+ return
217
+ }
218
+
219
+ const projectPath = path.resolve(import.meta.dir, "../../../..")
220
+ const routes = c3Extension.routes({ projectPath })
221
+ const read = routes.find((r) => r.path === "/read")!
222
+
223
+ // Use a known component id from the project
224
+ const req = new Request(
225
+ "http://localhost/api/ext/c3/read?projectPath=" +
226
+ encodeURIComponent(projectPath) +
227
+ "&id=c3-204",
228
+ )
229
+ const res = await read.handler(req, {})
230
+
231
+ expect(res.status).toBe(200)
232
+ const body = await res.json()
233
+ expect(body.data).toMatchObject({
234
+ id: "c3-204",
235
+ type: "component",
236
+ body: expect.stringContaining("##"),
237
+ })
238
+ expect(body.data.body_truncated).toBeUndefined()
239
+ })
240
+
241
+ test("graph handler returns mermaid content", async () => {
242
+ if (!c3xAvailable) {
243
+ console.log("SKIP: c3x not available")
244
+ return
245
+ }
246
+
247
+ const projectPath = path.resolve(import.meta.dir, "../../../..")
248
+ const routes = c3Extension.routes({ projectPath })
249
+ const graph = routes.find((r) => r.path === "/graph")!
250
+
251
+ const req = new Request(
252
+ "http://localhost/api/ext/c3/graph?projectPath=" +
253
+ encodeURIComponent(projectPath) +
254
+ "&id=c3-204",
255
+ )
256
+ const res = await graph.handler(req, {})
257
+
258
+ expect(res.status).toBe(200)
259
+ const body = await res.json()
260
+ expect(body.data).toBeDefined()
261
+ // Mermaid output should contain graph/flowchart keywords
262
+ expect(typeof body.data).toBe("string")
263
+ })
264
+ })
265
+
266
+ // ── Error handling ──────────────────────────────────────────────────
267
+
268
+ describe("c3x error handling", () => {
269
+ test("returns 503 when c3x fails on a project without .c3/", async () => {
270
+ // Create an empty temp dir with .c3/ but no valid content
271
+ tempDir = await mkdtemp(path.join(tmpdir(), "c3-empty-"))
272
+ await mkdir(path.join(tempDir, ".c3"), { recursive: true })
273
+
274
+ const routes = c3Extension.routes({ projectPath: tempDir })
275
+ const list = routes.find((r) => r.path === "/list")!
276
+
277
+ const req = new Request("http://localhost/api/ext/c3/list?projectPath=" + encodeURIComponent(tempDir))
278
+ const res = await list.handler(req, {})
279
+
280
+ // c3x should fail on empty .c3/ — either 503 or a valid response
281
+ // The key assertion is it doesn't crash (no unhandled exception)
282
+ expect([200, 503]).toContain(res.status)
283
+ })
284
+ })