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,162 @@
1
+ import { jetstream, DeliverPolicy } from "@nats-io/jetstream"
2
+ import type { ConsumerMessages } from "@nats-io/jetstream"
3
+ import type { NatsConnection } from "@nats-io/transport-node"
4
+ import { Kvm } from "@nats-io/kv"
5
+ import { RUNNER_EVENTS_STREAM, type RunnerTurnEvent } from "../shared/runner-protocol"
6
+ import type { SessionStatus, TranscriptEntry, AgentProvider } from "../shared/types"
7
+ import { LOG_PREFIX } from "../shared/branding"
8
+
9
+ const decoder = new TextDecoder()
10
+ const encoder = new TextEncoder()
11
+
12
+ // ── Store interface (subset of EventStore) ──────────────────────────
13
+
14
+ export interface TranscriptConsumerStore {
15
+ appendMessage(chatId: string, entry: TranscriptEntry): void
16
+ recordTurnFinished(chatId: string): Promise<void>
17
+ recordTurnFailed(chatId: string, error: string): Promise<void>
18
+ recordTurnCancelled(chatId: string): Promise<void>
19
+ setSessionToken(chatId: string, token: string | null): Promise<void>
20
+ renameChat(chatId: string, title: string): Promise<void>
21
+ setChatProvider(chatId: string, provider: AgentProvider): Promise<void>
22
+ setPlanMode(chatId: string, planMode: boolean): Promise<void>
23
+ }
24
+
25
+ // ── Options ─────────────────────────────────────────────────────────
26
+
27
+ export interface TranscriptConsumerOptions {
28
+ nc: NatsConnection
29
+ store: TranscriptConsumerStore
30
+ onStateChange: () => void
31
+ onMessageAppended?: (chatId: string, entry: TranscriptEntry) => void
32
+ }
33
+
34
+ // ── TranscriptConsumer ──────────────────────────────────────────────
35
+
36
+ export class TranscriptConsumer {
37
+ private readonly nc: NatsConnection
38
+ private readonly store: TranscriptConsumerStore
39
+ private readonly onStateChange: () => void
40
+ private readonly onMessageAppended: ((chatId: string, entry: TranscriptEntry) => void) | undefined
41
+ private readonly activeStatuses = new Map<string, SessionStatus>()
42
+ private messages: ConsumerMessages | null = null
43
+ private running = false
44
+
45
+ constructor(options: TranscriptConsumerOptions) {
46
+ this.nc = options.nc
47
+ this.store = options.store
48
+ this.onStateChange = options.onStateChange
49
+ this.onMessageAppended = options.onMessageAppended
50
+ }
51
+
52
+ getActiveStatuses(): Map<string, SessionStatus> {
53
+ return new Map(this.activeStatuses)
54
+ }
55
+
56
+ hasActiveChat(chatId: string): boolean {
57
+ return this.activeStatuses.has(chatId)
58
+ }
59
+
60
+ async start(): Promise<void> {
61
+ if (this.running) return
62
+ this.running = true
63
+
64
+ const js = jetstream(this.nc)
65
+
66
+ // Read last-processed sequence from KV for resume
67
+ const kvm = new Kvm(this.nc)
68
+ const stateBucket = await kvm.create("kanna_consumer_state")
69
+ const entry = await stateBucket.get("transcript_last_seq")
70
+ const lastSeq = entry ? Number(decoder.decode(entry.value)) : 0
71
+
72
+ const consumerOpts = lastSeq > 0
73
+ ? { deliver_policy: DeliverPolicy.StartSequence, opt_start_seq: lastSeq + 1 }
74
+ : { deliver_policy: DeliverPolicy.New }
75
+
76
+ const consumer = await js.consumers.get(RUNNER_EVENTS_STREAM, consumerOpts)
77
+ const messages = await consumer.consume()
78
+ this.messages = messages
79
+
80
+ // Process messages in the background — don't block start()
81
+ ;(async () => {
82
+ for await (const msg of messages) {
83
+ if (!this.running) break
84
+ try {
85
+ const event = JSON.parse(decoder.decode(msg.data)) as RunnerTurnEvent
86
+ await this.handleEvent(event)
87
+ msg.ack()
88
+ await stateBucket.put("transcript_last_seq", encoder.encode(String(msg.seq)))
89
+ } catch (err) {
90
+ console.warn(
91
+ LOG_PREFIX,
92
+ "TranscriptConsumer: failed to process event:",
93
+ err instanceof Error ? err.message : String(err),
94
+ )
95
+ }
96
+ }
97
+ })()
98
+ }
99
+
100
+ stop(): void {
101
+ this.running = false
102
+ if (this.messages) {
103
+ this.messages.close().catch(() => {})
104
+ this.messages = null
105
+ }
106
+ }
107
+
108
+ private async handleEvent(event: RunnerTurnEvent): Promise<void> {
109
+ let stateChanged = true
110
+
111
+ switch (event.type) {
112
+ case "transcript":
113
+ this.store.appendMessage(event.chatId, event.entry)
114
+ this.onMessageAppended?.(event.chatId, event.entry)
115
+ break
116
+ case "turn_finished":
117
+ await this.store.recordTurnFinished(event.chatId)
118
+ this.activeStatuses.delete(event.chatId)
119
+ break
120
+ case "turn_failed":
121
+ await this.store.recordTurnFailed(event.chatId, event.error)
122
+ this.activeStatuses.delete(event.chatId)
123
+ break
124
+ case "turn_cancelled":
125
+ await this.store.recordTurnCancelled(event.chatId)
126
+ this.activeStatuses.delete(event.chatId)
127
+ break
128
+ case "session_token":
129
+ await this.store.setSessionToken(event.chatId, event.sessionToken)
130
+ break
131
+ case "title_generated":
132
+ await this.store.renameChat(event.chatId, event.title)
133
+ break
134
+ case "status_change": {
135
+ const prev = this.activeStatuses.get(event.chatId)
136
+ if (prev === event.status) {
137
+ stateChanged = false
138
+ } else {
139
+ this.activeStatuses.set(event.chatId, event.status)
140
+ }
141
+ break
142
+ }
143
+ case "pending_tool":
144
+ // Trigger state change — UI needs to show pending tool
145
+ break
146
+ case "provider_set":
147
+ await this.store.setChatProvider(event.chatId, event.provider)
148
+ break
149
+ case "plan_mode_set":
150
+ await this.store.setPlanMode(event.chatId, event.planMode)
151
+ break
152
+ case "context_cleared":
153
+ // Already handled by session_token event — no store write needed
154
+ stateChanged = false
155
+ break
156
+ }
157
+
158
+ if (stateChanged) {
159
+ this.onStateChange()
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,193 @@
1
+ // src/server/transcript-search.test.ts
2
+ import { describe, expect, test } from "bun:test"
3
+ import { TranscriptSearchIndex } from "./transcript-search"
4
+ import type { TranscriptEntry } from "../shared/types"
5
+
6
+ function timestamped<T extends Omit<TranscriptEntry, "_id" | "createdAt">>(
7
+ entry: T,
8
+ ): TranscriptEntry {
9
+ return { _id: crypto.randomUUID(), createdAt: Date.now(), ...entry } as TranscriptEntry
10
+ }
11
+
12
+ describe("TranscriptSearchIndex", () => {
13
+ test("indexes user_prompt entries and searches", () => {
14
+ const index = new TranscriptSearchIndex()
15
+ index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: "implement auth middleware with JWT tokens" }))
16
+ index.addEntry("chat-2", timestamped({ kind: "user_prompt", content: "fix CSS styling on sidebar" }))
17
+
18
+ const results = index.search("auth middleware")
19
+ expect(results.length).toBeGreaterThanOrEqual(1)
20
+ expect(results[0].chatId).toBe("chat-1")
21
+ expect(results[0].kind).toBe("user_prompt")
22
+ })
23
+
24
+ test("indexes assistant_text entries", () => {
25
+ const index = new TranscriptSearchIndex()
26
+ index.addEntry("chat-1", timestamped({ kind: "assistant_text", text: "I created the users table with email and password_hash columns" }))
27
+
28
+ const results = index.search("users table")
29
+ expect(results.length).toBe(1)
30
+ expect(results[0].chatId).toBe("chat-1")
31
+ })
32
+
33
+ test("indexes tool_call entries with file paths", () => {
34
+ const index = new TranscriptSearchIndex()
35
+ index.addEntry("chat-1", timestamped({
36
+ kind: "tool_call",
37
+ tool: {
38
+ kind: "tool",
39
+ toolKind: "edit_file",
40
+ toolName: "Edit",
41
+ toolId: "tool-1",
42
+ input: { filePath: "/src/server/auth.ts", oldString: "a", newString: "b" },
43
+ },
44
+ } as Omit<TranscriptEntry, "_id" | "createdAt">))
45
+
46
+ const results = index.search("auth.ts")
47
+ expect(results.length).toBe(1)
48
+ })
49
+
50
+ test("indexes tool_call entries with bash commands", () => {
51
+ const index = new TranscriptSearchIndex()
52
+ index.addEntry("chat-1", timestamped({
53
+ kind: "tool_call",
54
+ tool: {
55
+ kind: "tool",
56
+ toolKind: "bash",
57
+ toolName: "Bash",
58
+ toolId: "tool-2",
59
+ input: { command: "bun test src/server/auth.test.ts" },
60
+ },
61
+ } as Omit<TranscriptEntry, "_id" | "createdAt">))
62
+
63
+ const results = index.search("bun test auth")
64
+ expect(results.length).toBe(1)
65
+ })
66
+
67
+ test("indexes tool_call entries with grep patterns", () => {
68
+ const index = new TranscriptSearchIndex()
69
+ index.addEntry("chat-1", timestamped({
70
+ kind: "tool_call",
71
+ tool: {
72
+ kind: "tool",
73
+ toolKind: "grep",
74
+ toolName: "Grep",
75
+ toolId: "tool-3",
76
+ input: { pattern: "handleAuth", outputMode: "content" },
77
+ },
78
+ } as Omit<TranscriptEntry, "_id" | "createdAt">))
79
+
80
+ const results = index.search("handleAuth")
81
+ expect(results.length).toBe(1)
82
+ })
83
+
84
+ test("indexes tool_result entries", () => {
85
+ const index = new TranscriptSearchIndex()
86
+ index.addEntry("chat-1", timestamped({
87
+ kind: "tool_result",
88
+ toolId: "tool-1",
89
+ content: "File written successfully to /src/server/auth.ts",
90
+ }))
91
+
92
+ const results = index.search("auth.ts written")
93
+ expect(results.length).toBe(1)
94
+ })
95
+
96
+ test("indexes tool_result with non-string content via JSON", () => {
97
+ const index = new TranscriptSearchIndex()
98
+ index.addEntry("chat-1", timestamped({
99
+ kind: "tool_result",
100
+ toolId: "tool-1",
101
+ content: { files: ["auth.ts", "middleware.ts"], count: 2 },
102
+ }))
103
+
104
+ const results = index.search("auth.ts middleware")
105
+ expect(results.length).toBe(1)
106
+ })
107
+
108
+ test("returns results with score and fragment", () => {
109
+ const index = new TranscriptSearchIndex()
110
+ index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: "implement error handling for database connections" }))
111
+
112
+ const results = index.search("error handling")
113
+ expect(results[0].score).toBeGreaterThan(0)
114
+ expect(results[0].fragment.length).toBeGreaterThan(0)
115
+ })
116
+
117
+ test("returns empty for no matches", () => {
118
+ const index = new TranscriptSearchIndex()
119
+ index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: "hello world" }))
120
+
121
+ const results = index.search("nonexistent term here")
122
+ expect(results).toEqual([])
123
+ })
124
+
125
+ test("respects limit", () => {
126
+ const index = new TranscriptSearchIndex()
127
+ for (let i = 0; i < 20; i++) {
128
+ index.addEntry(`chat-${i}`, timestamped({ kind: "user_prompt", content: `testing search feature ${i}` }))
129
+ }
130
+
131
+ const results = index.search("testing search", 5)
132
+ expect(results.length).toBe(5)
133
+ })
134
+
135
+ test("skips non-indexable entry kinds", () => {
136
+ const index = new TranscriptSearchIndex()
137
+ index.addEntry("chat-1", timestamped({ kind: "status", status: "running" }))
138
+ index.addEntry("chat-1", timestamped({ kind: "context_cleared" }))
139
+ index.addEntry("chat-1", timestamped({ kind: "interrupted" }))
140
+ index.addEntry("chat-1", timestamped({ kind: "compact_boundary" }))
141
+
142
+ expect(index.size).toBe(0)
143
+ })
144
+
145
+ test("fragment is truncated to 300 chars", () => {
146
+ const index = new TranscriptSearchIndex()
147
+ const longContent = "database ".repeat(100) // 900 chars
148
+ index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: longContent }))
149
+
150
+ const results = index.search("database")
151
+ expect(results[0].fragment.length).toBeLessThanOrEqual(300)
152
+ })
153
+
154
+ test("tool_result content is truncated to 500 chars before indexing", () => {
155
+ const index = new TranscriptSearchIndex()
156
+ // "filler " is 7 chars; 72 repeats = 504 chars, pushing ZZZMARKER past the 500 boundary
157
+ const padding = "filler ".repeat(72)
158
+ const content = padding + "ZZZMARKER"
159
+ expect(content.indexOf("ZZZMARKER")).toBeGreaterThan(500)
160
+
161
+ index.addEntry("chat-1", timestamped({
162
+ kind: "tool_result",
163
+ toolId: "tool-1",
164
+ content,
165
+ }))
166
+
167
+ // ZZZMARKER sits beyond 500 chars and must not be indexed
168
+ const results = index.search("ZZZMARKER")
169
+ expect(results).toEqual([])
170
+ })
171
+
172
+ test("tool_result content within 500 chars is still searchable", () => {
173
+ const index = new TranscriptSearchIndex()
174
+ index.addEntry("chat-1", timestamped({
175
+ kind: "tool_result",
176
+ toolId: "tool-1",
177
+ content: "File written successfully to /src/server/auth.ts",
178
+ }))
179
+
180
+ const results = index.search("auth.ts written")
181
+ expect(results.length).toBe(1)
182
+ })
183
+
184
+ test("timestamp is ISO string derived from createdAt", () => {
185
+ const index = new TranscriptSearchIndex()
186
+ const now = Date.now()
187
+ const entry = { _id: "fixed-id", createdAt: now, kind: "user_prompt" as const, content: "test timestamp" } as TranscriptEntry
188
+ index.addEntry("chat-1", entry)
189
+
190
+ const results = index.search("timestamp")
191
+ expect(results[0].timestamp).toBe(new Date(now).toISOString())
192
+ })
193
+ })
@@ -0,0 +1,83 @@
1
+ // src/server/transcript-search.ts
2
+ import type { TranscriptEntry, NormalizedToolCall } from "../shared/types"
3
+ import type { SearchResult, SearchDocumentKind } from "../shared/workspace-types"
4
+ import { BM25Index } from "./bm25"
5
+
6
+ const TOOL_RESULT_MAX_CHARS = 500
7
+ const SEARCH_FRAGMENT_MAX_CHARS = 300
8
+
9
+ interface IndexedEntry {
10
+ chatId: string
11
+ timestamp: string
12
+ kind: SearchDocumentKind
13
+ text: string
14
+ }
15
+
16
+ function extractTextFromEntry(entry: TranscriptEntry): { text: string; kind: SearchDocumentKind } | null {
17
+ switch (entry.kind) {
18
+ case "user_prompt":
19
+ return { text: entry.content, kind: "user_prompt" }
20
+ case "assistant_text":
21
+ return { text: entry.text, kind: "assistant_text" }
22
+ case "tool_call":
23
+ return { text: toolCallToText(entry.tool), kind: "tool_call" }
24
+ case "tool_result": {
25
+ const raw = typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content)
26
+ return { text: raw.slice(0, TOOL_RESULT_MAX_CHARS), kind: "tool_result" }
27
+ }
28
+ default:
29
+ return null
30
+ }
31
+ }
32
+
33
+ function toolCallToText(tool: NormalizedToolCall): string {
34
+ const parts: string[] = [tool.toolKind]
35
+ const input = tool.input as Record<string, unknown>
36
+ if (typeof input.filePath === "string") parts.push(input.filePath)
37
+ if (typeof input.path === "string") parts.push(input.path)
38
+ if (typeof input.command === "string") parts.push(input.command)
39
+ if (typeof input.pattern === "string") parts.push(input.pattern)
40
+ if (typeof input.query === "string") parts.push(input.query)
41
+ return parts.join(" ")
42
+ }
43
+
44
+ export class TranscriptSearchIndex {
45
+ private readonly bm25 = new BM25Index<string>()
46
+ private readonly entries = new Map<string, IndexedEntry>()
47
+
48
+ get size(): number {
49
+ return this.entries.size
50
+ }
51
+
52
+ addEntry(chatId: string, entry: TranscriptEntry): void {
53
+ const extracted = extractTextFromEntry(entry)
54
+ if (!extracted) return
55
+
56
+ const docId = entry._id
57
+ const indexed: IndexedEntry = {
58
+ chatId,
59
+ timestamp: new Date(entry.createdAt).toISOString(),
60
+ kind: extracted.kind,
61
+ text: extracted.text,
62
+ }
63
+ this.entries.set(docId, indexed)
64
+ this.bm25.add(docId, extracted.text)
65
+ }
66
+
67
+ search(query: string, limit = 10): SearchResult[] {
68
+ const bm25Results = this.bm25.search(query, limit)
69
+ return bm25Results
70
+ .map((r) => {
71
+ const entry = this.entries.get(r.id)
72
+ if (!entry) return null
73
+ return {
74
+ chatId: entry.chatId,
75
+ timestamp: entry.timestamp,
76
+ kind: entry.kind,
77
+ fragment: entry.text.slice(0, SEARCH_FRAGMENT_MAX_CHARS),
78
+ score: r.score,
79
+ }
80
+ })
81
+ .filter((r): r is SearchResult => r !== null)
82
+ }
83
+ }
@@ -0,0 +1,52 @@
1
+ import type { TranscriptEntry } from "../shared/types"
2
+
3
+ /** Collapse whitespace and truncate a single line to `limit` characters. */
4
+ export function truncateLine(text: string, limit: number): string {
5
+ const normalized = text.replace(/\s+/g, " ").trim()
6
+ if (normalized.length <= limit) return normalized
7
+ return `${normalized.slice(0, Math.max(0, limit - 1)).trimEnd()}…`
8
+ }
9
+
10
+ /** Convert a transcript entry into a labeled single line, or null for non-displayable kinds. */
11
+ export function toTranscriptLine(entry: TranscriptEntry, lineCharLimit: number): string | null {
12
+ switch (entry.kind) {
13
+ case "user_prompt":
14
+ return `User: ${truncateLine(entry.content, lineCharLimit)}`
15
+ case "assistant_text":
16
+ return `Assistant: ${truncateLine(entry.text, lineCharLimit)}`
17
+ case "compact_summary":
18
+ return `Summary: ${truncateLine(entry.summary, lineCharLimit)}`
19
+ case "result":
20
+ return `${entry.isError ? "Result error" : "Result"}: ${truncateLine(entry.result, lineCharLimit)}`
21
+ default:
22
+ return null
23
+ }
24
+ }
25
+
26
+ const PROMPT_SCHEMA = {
27
+ type: "object",
28
+ properties: {
29
+ prompt: { type: "string" },
30
+ },
31
+ required: ["prompt"],
32
+ additionalProperties: false,
33
+ } as const
34
+
35
+ export { PROMPT_SCHEMA }
36
+
37
+ /** Collapse whitespace and clamp intent text to 1000 characters. */
38
+ export function normalizeIntent(intent: string): string {
39
+ return intent.replace(/\s+/g, " ").trim().slice(0, 1_000)
40
+ }
41
+
42
+ /** Normalize LLM-generated prompt output, clamping to maxChars. Returns null for empty/invalid. */
43
+ export function normalizeGeneratedPrompt(value: unknown, maxChars: number): string | null {
44
+ if (typeof value !== "string") return null
45
+ const normalized = value
46
+ .replace(/\r\n/g, "\n")
47
+ .replace(/\n{3,}/g, "\n\n")
48
+ .trim()
49
+ .slice(0, maxChars)
50
+ .trim()
51
+ return normalized || null
52
+ }
@@ -0,0 +1,107 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { UpdateManager } from "./update-manager"
3
+
4
+ describe("UpdateManager", () => {
5
+ test("detects available updates", async () => {
6
+ const manager = new UpdateManager({
7
+ currentVersion: "0.12.0",
8
+ fetchLatestVersion: async () => "0.13.0",
9
+ installVersion: () => ({
10
+ ok: true,
11
+ errorCode: null,
12
+ userTitle: null,
13
+ userMessage: null,
14
+ }),
15
+ })
16
+
17
+ const snapshot = await manager.checkForUpdates({ force: true })
18
+
19
+ expect(snapshot.status).toBe("available")
20
+ expect(snapshot.updateAvailable).toBe(true)
21
+ expect(snapshot.latestVersion).toBe("0.13.0")
22
+ expect(snapshot.installAction).toBe("restart")
23
+ })
24
+
25
+ test("bypasses cache when force is true", async () => {
26
+ let calls = 0
27
+ const manager = new UpdateManager({
28
+ currentVersion: "0.12.0",
29
+ fetchLatestVersion: async () => {
30
+ calls += 1
31
+ return calls === 1 ? "0.12.1" : "0.13.0"
32
+ },
33
+ installVersion: () => ({
34
+ ok: true,
35
+ errorCode: null,
36
+ userTitle: null,
37
+ userMessage: null,
38
+ }),
39
+ })
40
+
41
+ await manager.checkForUpdates()
42
+ await manager.checkForUpdates({ force: true })
43
+
44
+ expect(calls).toBe(2)
45
+ expect(manager.getSnapshot().latestVersion).toBe("0.13.0")
46
+ })
47
+
48
+ test("surfaces install failures without clearing the running version", async () => {
49
+ let installedVersion: string | null = null
50
+ const manager = new UpdateManager({
51
+ currentVersion: "0.12.0",
52
+ fetchLatestVersion: async () => "0.13.0",
53
+ installVersion: (_packageName, version) => {
54
+ installedVersion = version
55
+ return {
56
+ ok: false,
57
+ errorCode: "version_not_live_yet",
58
+ userTitle: "Update not live yet",
59
+ userMessage: "This update is still propagating. Try again in a few minutes.",
60
+ }
61
+ },
62
+ })
63
+
64
+ const result = await manager.installUpdate()
65
+
66
+ expect(result).toEqual({
67
+ ok: false,
68
+ action: "restart",
69
+ errorCode: "version_not_live_yet",
70
+ userTitle: "Update not live yet",
71
+ userMessage: "This update is still propagating. Try again in a few minutes.",
72
+ })
73
+ expect(installedVersion === "0.13.0").toBe(true)
74
+ expect(manager.getSnapshot().status).toBe("error")
75
+ expect(manager.getSnapshot().currentVersion).toBe("0.12.0")
76
+ })
77
+
78
+ test("always exposes an available reload action in dev mode", async () => {
79
+ const manager = new UpdateManager({
80
+ currentVersion: "0.12.0",
81
+ fetchLatestVersion: async () => "9.9.9",
82
+ installVersion: () => ({
83
+ ok: true,
84
+ errorCode: null,
85
+ userTitle: null,
86
+ userMessage: null,
87
+ }),
88
+ devMode: true,
89
+ })
90
+
91
+ expect(manager.getSnapshot()).toMatchObject({
92
+ status: "available",
93
+ updateAvailable: true,
94
+ installAction: "restart",
95
+ })
96
+
97
+ const result = await manager.installUpdate()
98
+ expect(result).toEqual({
99
+ ok: true,
100
+ action: "restart",
101
+ errorCode: null,
102
+ userTitle: null,
103
+ userMessage: null,
104
+ })
105
+ expect(manager.getSnapshot().status).toBe("restart_pending")
106
+ })
107
+ })