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,640 @@
1
+ import { afterEach, describe, expect, test } from "bun:test"
2
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
3
+ import { tmpdir } from "node:os"
4
+ import path from "node:path"
5
+ import process from "node:process"
6
+ import { createServer } from "node:net"
7
+ import { APP_NAME, getDataDir } from "../shared/branding"
8
+ import {
9
+ buildStageProbeScript,
10
+ getJourneyStage,
11
+ HOME_TO_FORK_DIALOG_JOURNEY,
12
+ HOME_TO_MERGE_DIALOG_JOURNEY,
13
+ HOME_TO_NEW_CHAT_JOURNEY,
14
+ matchesJourneyRoute,
15
+ type StageProbeResult,
16
+ } from "./journey-verification"
17
+
18
+ interface AgentBrowserEnvelope<T> {
19
+ success: boolean
20
+ data: T | null
21
+ error: string | null
22
+ }
23
+
24
+ interface AgentBrowserEvalResult<T> {
25
+ origin: string
26
+ result: T
27
+ }
28
+
29
+ interface AgentBrowserUrlResult {
30
+ url: string
31
+ }
32
+
33
+ interface RunningDevServer {
34
+ clientPort: number
35
+ serverPort: number
36
+ process: Bun.Subprocess<"ignore", "pipe", "pipe">
37
+ stdoutText: Promise<string>
38
+ stderrText: Promise<string>
39
+ }
40
+
41
+ interface FixtureEnvironment {
42
+ homeDir: string
43
+ fixtureProjectDir: string
44
+ fixtureProjectTitle: string
45
+ dataDir: string
46
+ }
47
+
48
+ const activeServers: RunningDevServer[] = []
49
+ const activeHomes: string[] = []
50
+ const activeAgentBrowserSessions = new Set<string>()
51
+
52
+ function getAgentBrowserEnv(session: string): Record<string, string> {
53
+ return {
54
+ ...process.env,
55
+ AGENT_BROWSER_SESSION: session,
56
+ AGENT_BROWSER_DEFAULT_TIMEOUT: "15000",
57
+ } as Record<string, string>
58
+ }
59
+
60
+ function decodeBytes(bytes: Uint8Array<ArrayBufferLike>) {
61
+ return new TextDecoder().decode(bytes)
62
+ }
63
+
64
+ function closeAllAgentBrowsers() {
65
+ Bun.spawnSync(["agent-browser", "close", "--all"], {
66
+ env: process.env,
67
+ stdout: "ignore",
68
+ stderr: "ignore",
69
+ })
70
+ }
71
+
72
+ function runAgentBrowserJson<T>(session: string, args: string[]): T {
73
+ const result = Bun.spawnSync(["agent-browser", "--json", ...args], {
74
+ env: getAgentBrowserEnv(session),
75
+ stdout: "pipe",
76
+ stderr: "pipe",
77
+ })
78
+
79
+ const stdout = decodeBytes(result.stdout)
80
+ const stderr = decodeBytes(result.stderr)
81
+ let parsed: AgentBrowserEnvelope<T> | null = null
82
+
83
+ if (stdout.trim()) {
84
+ parsed = JSON.parse(stdout) as AgentBrowserEnvelope<T>
85
+ }
86
+
87
+ if (result.exitCode !== 0 || !parsed?.success || parsed.data === null) {
88
+ const details = [
89
+ `agent-browser ${args.join(" ")} failed`,
90
+ parsed?.error ? `error: ${parsed.error}` : null,
91
+ stderr.trim() ? `stderr: ${stderr.trim()}` : null,
92
+ stdout.trim() ? `stdout: ${stdout.trim()}` : null,
93
+ ].filter(Boolean).join("\n")
94
+ throw new Error(details)
95
+ }
96
+
97
+ return parsed.data
98
+ }
99
+
100
+ async function readStream(stream: ReadableStream<Uint8Array> | null) {
101
+ if (!stream) return ""
102
+ return await new Response(stream).text()
103
+ }
104
+
105
+ async function isPortAvailable(port: number) {
106
+ return await new Promise<boolean>((resolve) => {
107
+ const server = createServer()
108
+
109
+ server.once("error", () => {
110
+ resolve(false)
111
+ })
112
+
113
+ server.listen(port, "127.0.0.1", () => {
114
+ server.close(() => resolve(true))
115
+ })
116
+ })
117
+ }
118
+
119
+ async function findAvailablePortPair() {
120
+ const basePort = 5600 + Math.floor(Math.random() * 200) * 2
121
+
122
+ for (let offset = 0; offset < 400; offset += 2) {
123
+ const clientPort = basePort + offset
124
+ const serverPort = clientPort + 1
125
+
126
+ if (await isPortAvailable(clientPort) && await isPortAvailable(serverPort)) {
127
+ return { clientPort, serverPort }
128
+ }
129
+ }
130
+
131
+ throw new Error("Unable to find an available client/server port pair for the journey test")
132
+ }
133
+
134
+ async function waitFor<T>(
135
+ label: string,
136
+ fn: () => Promise<T | false>,
137
+ timeoutMs = 30_000,
138
+ pollMs = 250,
139
+ ): Promise<T> {
140
+ const deadline = Date.now() + timeoutMs
141
+
142
+ while (Date.now() < deadline) {
143
+ const value = await fn()
144
+ if (value !== false) {
145
+ return value
146
+ }
147
+
148
+ await Bun.sleep(pollMs)
149
+ }
150
+
151
+ throw new Error(`Timed out waiting for ${label}`)
152
+ }
153
+
154
+ async function createFixtureEnvironment(): Promise<FixtureEnvironment> {
155
+ const homeDir = await mkdtemp(path.join(tmpdir(), "tinkaria-journey-"))
156
+ const fixtureProjectTitle = "journey-fixture"
157
+ const fixtureProjectDir = path.join(homeDir, "workspace", fixtureProjectTitle)
158
+ const codexDir = path.join(homeDir, ".codex")
159
+ const configPath = path.join(codexDir, "config.toml")
160
+
161
+ await mkdir(fixtureProjectDir, { recursive: true })
162
+ await mkdir(codexDir, { recursive: true })
163
+ await writeFile(path.join(fixtureProjectDir, "README.md"), "# journey fixture\n")
164
+ await writeFile(configPath, `[projects."${fixtureProjectDir}"]\n`)
165
+
166
+ return {
167
+ homeDir,
168
+ fixtureProjectDir,
169
+ fixtureProjectTitle,
170
+ dataDir: getDataDir(homeDir, { TINKARIA_RUNTIME_PROFILE: "dev" }),
171
+ }
172
+ }
173
+
174
+ async function collectServerLogs(server: RunningDevServer) {
175
+ const [stdout, stderr] = await Promise.all([server.stdoutText, server.stderrText])
176
+ return [stdout.trim(), stderr.trim()].filter(Boolean).join("\n")
177
+ }
178
+
179
+ async function stopServer(server: RunningDevServer) {
180
+ if (server.process.exitCode === null) {
181
+ server.process.kill("SIGTERM")
182
+ await Promise.race([
183
+ server.process.exited,
184
+ Bun.sleep(2_000).then(() => {
185
+ if (server.process.exitCode === null) {
186
+ server.process.kill("SIGKILL")
187
+ }
188
+ }),
189
+ ])
190
+ }
191
+
192
+ await Promise.allSettled([server.stdoutText, server.stderrText])
193
+ }
194
+
195
+ async function startIsolatedDevServer(homeDir: string, options?: { clientPort?: number }): Promise<RunningDevServer> {
196
+ const ports = options?.clientPort === undefined
197
+ ? await findAvailablePortPair()
198
+ : { clientPort: options.clientPort, serverPort: options.clientPort + 1 }
199
+ const { clientPort, serverPort } = ports
200
+ const processEnv = {
201
+ ...process.env,
202
+ HOME: homeDir,
203
+ TINKARIA_RUNTIME_PROFILE: "dev",
204
+ }
205
+
206
+ const proc = Bun.spawn([process.execPath, "run", "./scripts/dev.ts", "--port", String(clientPort), "--no-open", "--strict-port"], {
207
+ cwd: process.cwd(),
208
+ env: processEnv,
209
+ stdin: "ignore",
210
+ stdout: "pipe",
211
+ stderr: "pipe",
212
+ })
213
+ const server: RunningDevServer = {
214
+ clientPort,
215
+ serverPort,
216
+ process: proc,
217
+ stdoutText: readStream(proc.stdout),
218
+ stderrText: readStream(proc.stderr),
219
+ }
220
+
221
+ try {
222
+ await waitFor("isolated dev server readiness", async () => {
223
+ if (proc.exitCode !== null) {
224
+ throw new Error(`dev server exited before readiness with code ${String(proc.exitCode)}`)
225
+ }
226
+
227
+ try {
228
+ const [clientResponse, healthResponse] = await Promise.all([
229
+ fetch(`http://127.0.0.1:${clientPort}/`),
230
+ fetch(`http://127.0.0.1:${serverPort}/health`),
231
+ ])
232
+
233
+ if (!clientResponse.ok || !healthResponse.ok) {
234
+ return false
235
+ }
236
+
237
+ const healthBody = await healthResponse.json() as { ok?: boolean }
238
+ return healthBody.ok === true ? true : false
239
+ } catch {
240
+ return false
241
+ }
242
+ }, 45_000)
243
+ } catch (error) {
244
+ await stopServer(server)
245
+ const logs = await collectServerLogs(server)
246
+ const message = error instanceof Error ? error.message : String(error)
247
+ throw new Error(`${message}\n${logs}`)
248
+ }
249
+
250
+ activeServers.push(server)
251
+ return server
252
+ }
253
+
254
+ function extractPathname(rawUrl: string) {
255
+ return new URL(rawUrl).pathname
256
+ }
257
+
258
+ function getBrowserUrl(session: string) {
259
+ return runAgentBrowserJson<AgentBrowserUrlResult>(session, ["get", "url"]).url
260
+ }
261
+
262
+ function evalBrowser<T>(session: string, script: string) {
263
+ return runAgentBrowserJson<AgentBrowserEvalResult<T>>(session, ["eval", script]).result
264
+ }
265
+
266
+ function setBrowserOffline(session: string, offline: boolean) {
267
+ runAgentBrowserJson<Record<string, unknown>>(session, ["set", "offline", offline ? "on" : "off"])
268
+ }
269
+
270
+ async function waitForStage(session: string, stageId: string) {
271
+ const stage = getJourneyStage(stageId)
272
+ return await waitFor(`browser stage ${stageId}`, async () => {
273
+ const url = getBrowserUrl(session)
274
+ if (!matchesJourneyRoute(extractPathname(url), stage.route)) {
275
+ return false
276
+ }
277
+
278
+ const probe = evalBrowser<StageProbeResult>(session, buildStageProbeScript(stage))
279
+ if (probe.missing.length > 0) {
280
+ return false
281
+ }
282
+
283
+ return { url, probe }
284
+ }, 30_000)
285
+ }
286
+
287
+ async function readJsonLines(filePath: string) {
288
+ try {
289
+ const text = await readFile(filePath, "utf8")
290
+ return text
291
+ .split("\n")
292
+ .map((line) => line.trim())
293
+ .filter(Boolean)
294
+ .map((line) => JSON.parse(line) as Record<string, unknown>)
295
+ } catch (error) {
296
+ const errorCode = error instanceof Error && "code" in error ? error.code : null
297
+ if (errorCode === "ENOENT") {
298
+ return []
299
+ }
300
+ throw error
301
+ }
302
+ }
303
+
304
+ async function openNewChatFromHomepage(session: string, fixture: FixtureEnvironment) {
305
+ const homeStage = await waitForStage(session, "home.ready")
306
+ expect(homeStage.probe.missing).toEqual([])
307
+ expect(homeStage.probe.c3ByUiId["home.page"]).toBe("c3-117")
308
+
309
+ const overviewDetails = evalBrowser<{
310
+ containsTitle: boolean
311
+ containsPath: boolean
312
+ startButtonLabel: string | null
313
+ }>(session, `(() => {
314
+ const overview = document.querySelector('[data-ui-id="home.project-overview"]');
315
+ const startButton = overview?.querySelector('[data-ui-id="home.project-secondary.action"]');
316
+ const text = overview?.textContent ?? "";
317
+ return {
318
+ containsTitle: text.includes(${JSON.stringify(fixture.fixtureProjectTitle)}),
319
+ containsPath: text.includes(${JSON.stringify(fixture.fixtureProjectDir)}),
320
+ startButtonLabel: startButton?.textContent ? startButton.textContent.trim() : null,
321
+ };
322
+ })()`)
323
+ expect(overviewDetails).toEqual({
324
+ containsTitle: true,
325
+ containsPath: false,
326
+ startButtonLabel: "Start First Task",
327
+ })
328
+
329
+ const clickResult = evalBrowser<{ clicked: boolean; label: string | null }>(session, `(() => {
330
+ const button = document.querySelector('[data-ui-id="home.project-overview"] [data-ui-id="home.project-secondary.action"]');
331
+ if (!(button instanceof HTMLElement)) {
332
+ return { clicked: false, label: null };
333
+ }
334
+ const label = button.textContent ? button.textContent.trim() : null;
335
+ button.click();
336
+ return { clicked: true, label };
337
+ })()`)
338
+ expect(clickResult).toEqual({ clicked: true, label: "Start First Task" })
339
+
340
+ const chatStage = await waitForStage(session, "chat.ready")
341
+ expect(chatStage.probe.missing).toEqual([])
342
+ expect(chatStage.probe.c3ByUiId["chat.page"]).toBe("c3-110")
343
+ expect(chatStage.probe.c3ByUiId["transcript.message-list"]).toBe("c3-111")
344
+ expect(chatStage.probe.c3ByUiId["chat.composer"]).toBe("c3-112")
345
+
346
+ const persistedState = await waitFor("persisted project/chat events", async () => {
347
+ const [projectEvents, chatEvents] = await Promise.all([
348
+ readJsonLines(path.join(fixture.dataDir, "projects.jsonl")),
349
+ readJsonLines(path.join(fixture.dataDir, "chats.jsonl")),
350
+ ])
351
+
352
+ const projectOpened = projectEvents.find((event) =>
353
+ event.type === "workspace_opened" && event.localPath === fixture.fixtureProjectDir
354
+ )
355
+ if (!projectOpened || typeof projectOpened.workspaceId !== "string") {
356
+ return false
357
+ }
358
+
359
+ const chatCreated = chatEvents.find((event) =>
360
+ event.type === "chat_created" && event.workspaceId === projectOpened.workspaceId && typeof event.chatId === "string"
361
+ )
362
+ if (!chatCreated || typeof chatCreated.chatId !== "string") {
363
+ return false
364
+ }
365
+
366
+ return {
367
+ workspaceId: projectOpened.workspaceId,
368
+ chatId: chatCreated.chatId,
369
+ }
370
+ }, 10_000)
371
+
372
+ expect(extractPathname(chatStage.url)).toBe(`/chat/${persistedState.chatId}`)
373
+ return { chatStage, persistedState }
374
+ }
375
+
376
+ afterEach(async () => {
377
+ for (const session of activeAgentBrowserSessions) {
378
+ try {
379
+ setBrowserOffline(session, false)
380
+ } catch {
381
+ // Best effort only; failures here should not hide the real test failure.
382
+ }
383
+ try {
384
+ runAgentBrowserJson<{ closed: boolean }>(session, ["close"])
385
+ } catch {
386
+ // Best effort only; failures here should not hide the real test failure.
387
+ }
388
+ }
389
+ activeAgentBrowserSessions.clear()
390
+
391
+ while (activeServers.length > 0) {
392
+ const server = activeServers.pop()
393
+ if (server) {
394
+ await stopServer(server)
395
+ }
396
+ }
397
+
398
+ while (activeHomes.length > 0) {
399
+ const homeDir = activeHomes.pop()
400
+ if (homeDir) {
401
+ await rm(homeDir, { recursive: true, force: true })
402
+ }
403
+ }
404
+ })
405
+
406
+ const hasAgentBrowser = Boolean(Bun.which("agent-browser"))
407
+
408
+ describe("journey verification inventory", () => {
409
+ test("documents the first homepage -> new task journey in stable screen stages", () => {
410
+ expect(structuredClone(HOME_TO_NEW_CHAT_JOURNEY)).toMatchObject({
411
+ id: "homepage-to-new-chat",
412
+ stages: [
413
+ {
414
+ id: "home.ready",
415
+ owners: ["c3-117"],
416
+ route: { kind: "exact", value: "/" },
417
+ requiredUiIds: expect.arrayContaining(["home.page", "home.project-overview", "home.project-secondary.action"]),
418
+ },
419
+ {
420
+ id: "chat.ready",
421
+ owners: ["c3-110", "c3-111", "c3-112"],
422
+ route: { kind: "prefix", value: "/chat/" },
423
+ requiredUiIds: expect.arrayContaining(["chat.page", "chat.navbar", "transcript.message-list", "chat.composer"]),
424
+ },
425
+ ],
426
+ })
427
+ })
428
+
429
+ test("documents fork and merge dialog journeys as deterministic follow-on stages", () => {
430
+ expect(structuredClone(HOME_TO_FORK_DIALOG_JOURNEY)).toMatchObject({
431
+ id: "homepage-to-fork-dialog",
432
+ stages: [
433
+ expect.anything(),
434
+ expect.anything(),
435
+ {
436
+ id: "fork-dialog.open",
437
+ owners: ["c3-110"],
438
+ route: { kind: "prefix", value: "/chat/" },
439
+ requiredUiIds: expect.arrayContaining([
440
+ "chat.fork-session.dialog",
441
+ "chat.fork-session.context.input",
442
+ "chat.fork-session.submit.action",
443
+ ]),
444
+ },
445
+ ],
446
+ })
447
+ expect(structuredClone(HOME_TO_MERGE_DIALOG_JOURNEY)).toMatchObject({
448
+ id: "homepage-to-merge-dialog",
449
+ stages: [
450
+ expect.anything(),
451
+ expect.anything(),
452
+ {
453
+ id: "merge-dialog.open",
454
+ owners: ["c3-110"],
455
+ route: { kind: "prefix", value: "/chat/" },
456
+ requiredUiIds: expect.arrayContaining([
457
+ "chat.merge-session.dialog",
458
+ "chat.merge-session.sessions.list",
459
+ "chat.merge-session.submit.action",
460
+ ]),
461
+ },
462
+ ],
463
+ })
464
+ })
465
+
466
+ describe.serial("browser integration", () => {
467
+ test.skipIf(!hasAgentBrowser)("verifies the first browser journey against a real isolated dev instance", async () => {
468
+ closeAllAgentBrowsers()
469
+ const session = `journey-${crypto.randomUUID()}`
470
+ activeAgentBrowserSessions.add(session)
471
+
472
+ const fixture = await createFixtureEnvironment()
473
+ activeHomes.push(fixture.homeDir)
474
+
475
+ const server = await startIsolatedDevServer(fixture.homeDir)
476
+ setBrowserOffline(session, false)
477
+ runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
478
+
479
+ await openNewChatFromHomepage(session, fixture)
480
+ }, 90_000)
481
+
482
+ test.skipIf(!hasAgentBrowser)("verifies fork and merge dialog journeys against a real isolated dev instance", async () => {
483
+ closeAllAgentBrowsers()
484
+ const session = `journey-${crypto.randomUUID()}`
485
+ activeAgentBrowserSessions.add(session)
486
+
487
+ const fixture = await createFixtureEnvironment()
488
+ activeHomes.push(fixture.homeDir)
489
+
490
+ const server = await startIsolatedDevServer(fixture.homeDir)
491
+ setBrowserOffline(session, false)
492
+ runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
493
+
494
+ await openNewChatFromHomepage(session, fixture)
495
+
496
+ evalBrowser<{ clicked: boolean }>(session, `(() => {
497
+ const button = document.querySelector('[data-ui-id="chat.navbar.fork-session.action"]');
498
+ if (!(button instanceof HTMLElement)) return { clicked: false };
499
+ button.click();
500
+ return { clicked: true };
501
+ })()`)
502
+
503
+ const forkStage = await waitForStage(session, "fork-dialog.open")
504
+ expect(forkStage.probe.missing).toEqual([])
505
+ expect(forkStage.probe.c3ByUiId["chat.fork-session.dialog"]).toBe("c3-110")
506
+
507
+ evalBrowser<{ clicked: boolean }>(session, `(() => {
508
+ const button = document.querySelector('[data-ui-id="chat.fork-session.cancel.action"]');
509
+ if (!(button instanceof HTMLElement)) return { clicked: false };
510
+ button.click();
511
+ return { clicked: true };
512
+ })()`)
513
+
514
+ await waitForStage(session, "chat.ready")
515
+
516
+ evalBrowser<{ clicked: boolean }>(session, `(() => {
517
+ const button = document.querySelector('[data-ui-id="chat.navbar.merge-session.action"]');
518
+ if (!(button instanceof HTMLElement)) return { clicked: false };
519
+ button.click();
520
+ return { clicked: true };
521
+ })()`)
522
+
523
+ const mergeStage = await waitForStage(session, "merge-dialog.open")
524
+ expect(mergeStage.probe.missing).toEqual([])
525
+ expect(mergeStage.probe.c3ByUiId["chat.merge-session.dialog"]).toBe("c3-110")
526
+ }, 90_000)
527
+
528
+ test.skipIf(!hasAgentBrowser)("creates a new project from the homepage modal and lands in chat", async () => {
529
+ closeAllAgentBrowsers()
530
+ const session = `journey-${crypto.randomUUID()}`
531
+ activeAgentBrowserSessions.add(session)
532
+
533
+ const fixture = await createFixtureEnvironment()
534
+ activeHomes.push(fixture.homeDir)
535
+
536
+ const server = await startIsolatedDevServer(fixture.homeDir)
537
+ setBrowserOffline(session, false)
538
+ runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
539
+
540
+ await waitForStage(session, "home.ready")
541
+
542
+ evalBrowser<{ clicked: boolean }>(session, `(() => {
543
+ const button = document.querySelector('[data-ui-id="home.add-project.action"]');
544
+ if (!(button instanceof HTMLElement)) return { clicked: false };
545
+ button.click();
546
+ return { clicked: true };
547
+ })()`)
548
+
549
+ await waitFor("add project dialog", async () => {
550
+ const visible = evalBrowser<boolean>(session, `Boolean(document.querySelector('[data-ui-id="home.add-project.dialog"]'))`)
551
+ return visible ? true : false
552
+ })
553
+
554
+ const projectName = "Coverage Project"
555
+ const fillResult = evalBrowser<{ filled: boolean }>(session, `(() => {
556
+ const input = document.querySelector('input[placeholder="Project name"]');
557
+ if (!(input instanceof HTMLInputElement)) return { filled: false };
558
+ const valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
559
+ if (!valueSetter) return { filled: false };
560
+ input.focus();
561
+ valueSetter.call(input, ${JSON.stringify(projectName)});
562
+ input.dispatchEvent(new Event("input", { bubbles: true }));
563
+ input.dispatchEvent(new Event("change", { bubbles: true }));
564
+ return { filled: true };
565
+ })()`)
566
+ expect(fillResult).toEqual({ filled: true })
567
+
568
+ await waitFor("create project submit enabled", async () => {
569
+ const submitEnabled = evalBrowser<boolean>(session, `(() => {
570
+ const button = Array.from(document.querySelectorAll('button')).find((candidate) => candidate.textContent?.trim() === "Create");
571
+ return button instanceof HTMLButtonElement && button.disabled === false;
572
+ })()`)
573
+ return submitEnabled ? true : false
574
+ })
575
+
576
+ const submitResult = evalBrowser<{ clicked: boolean }>(session, `(() => {
577
+ const button = Array.from(document.querySelectorAll('button')).find((candidate) => candidate.textContent?.trim() === "Create");
578
+ if (!(button instanceof HTMLElement)) return { clicked: false };
579
+ button.click();
580
+ return { clicked: true };
581
+ })()`)
582
+ expect(submitResult).toEqual({ clicked: true })
583
+
584
+ const chatStage = await waitForStage(session, "chat.ready")
585
+ expect(chatStage.probe.missing).toEqual([])
586
+
587
+ await stopServer(server)
588
+ activeServers.splice(activeServers.indexOf(server), 1)
589
+
590
+ const createdPath = path.join(fixture.homeDir, APP_NAME, "coverage-project")
591
+ const snapshot = JSON.parse(await readFile(path.join(fixture.dataDir, "snapshot.json"), "utf8")) as {
592
+ workspaces?: Array<{ localPath?: string }>
593
+ }
594
+ expect(snapshot.workspaces?.some((ws) => ws.localPath === createdPath)).toBe(true)
595
+ }, 90_000)
596
+
597
+ test.skip("shows reconnecting and reconnected composer states after offline recovery", async () => {
598
+ closeAllAgentBrowsers()
599
+ const session = `journey-${crypto.randomUUID()}`
600
+ activeAgentBrowserSessions.add(session)
601
+
602
+ const fixture = await createFixtureEnvironment()
603
+ activeHomes.push(fixture.homeDir)
604
+
605
+ const server = await startIsolatedDevServer(fixture.homeDir)
606
+ setBrowserOffline(session, false)
607
+ runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
608
+
609
+ await openNewChatFromHomepage(session, fixture)
610
+
611
+ setBrowserOffline(session, true)
612
+
613
+ const reconnecting = await waitFor("reconnecting composer badge", async () => {
614
+ const badge = evalBrowser<boolean>(session, `!!document.querySelector('[data-ui-id="chat.composer.connection.section"] .animate-spin')`)
615
+ return badge || false
616
+ }, 20_000)
617
+ expect(reconnecting).toBe(true)
618
+
619
+ setBrowserOffline(session, false)
620
+
621
+ const recovered = await waitFor("chat composer recovery", async () => {
622
+ const state = evalBrowser<{
623
+ connectionLabel: string
624
+ submitDisabled: boolean | null
625
+ }>(session, `(() => {
626
+ const connectionLabel = (document.querySelector('[data-ui-id="chat.composer.connection.section"]')?.textContent ?? "").trim();
627
+ const submit = document.querySelector('[data-ui-id="chat.composer.submit.action"]');
628
+ return {
629
+ connectionLabel,
630
+ submitDisabled: submit instanceof HTMLButtonElement ? submit.disabled : null,
631
+ };
632
+ })()`)
633
+ if (state.connectionLabel.length > 0) return false
634
+ if (state.submitDisabled !== false) return false
635
+ return state
636
+ }, 20_000)
637
+ expect(recovered.submitDisabled).toBe(false)
638
+ }, 90_000)
639
+ })
640
+ })