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,57 @@
1
+ import { realpathSync } from "node:fs"
2
+ import path from "node:path"
3
+
4
+ const MAX_SANITIZED_LENGTH = 200
5
+
6
+ function djb2Hash(str: string): number {
7
+ let hash = 0
8
+ for (let i = 0; i < str.length; i++) {
9
+ hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0
10
+ }
11
+ return hash
12
+ }
13
+
14
+ function hashSuffix(name: string): string {
15
+ // Mirror claude-code/src/utils/sessionStoragePortable.ts: prefer Bun.hash
16
+ // (wyhash) when running under Bun, fall back to djb2 elsewhere. Both encode
17
+ // base36. Cross-runtime stability matters only for paths >200 chars.
18
+ const maybeBun = (globalThis as { Bun?: { hash: (s: string) => bigint } }).Bun
19
+ if (maybeBun && typeof maybeBun.hash === "function") {
20
+ return maybeBun.hash(name).toString(36)
21
+ }
22
+ return Math.abs(djb2Hash(name)).toString(36)
23
+ }
24
+
25
+ function sanitizePath(name: string): string {
26
+ const sanitized = name.replace(/[^a-zA-Z0-9]/g, "-")
27
+ if (sanitized.length <= MAX_SANITIZED_LENGTH) return sanitized
28
+ return `${sanitized.slice(0, MAX_SANITIZED_LENGTH)}-${hashSuffix(name)}`
29
+ }
30
+
31
+ export function encodeCwd(cwd: string): string {
32
+ // Ported verbatim from claude-code v2.1.146:
33
+ // bootstrap/state.ts realpath + NFC normalize, then sessionStoragePortable.ts
34
+ // sanitizePath. Throws ENOENT if cwd is missing — callers guarantee an
35
+ // existing directory.
36
+ const real = realpathSync(cwd)
37
+ const normalized = real.normalize("NFC")
38
+ return sanitizePath(normalized)
39
+ }
40
+
41
+ export function computeProjectDir(args: {
42
+ homeDir: string
43
+ cwd: string
44
+ }): string {
45
+ return path.join(args.homeDir, ".claude", "projects", encodeCwd(args.cwd))
46
+ }
47
+
48
+ export function computeJsonlPath(args: {
49
+ homeDir: string
50
+ cwd: string
51
+ sessionId: string
52
+ }): string {
53
+ return path.join(
54
+ computeProjectDir({ homeDir: args.homeDir, cwd: args.cwd }),
55
+ `${args.sessionId}.jsonl`,
56
+ )
57
+ }
@@ -0,0 +1,114 @@
1
+ import { realpathSync } from "node:fs"
2
+ import { mkdtemp, rm } from "node:fs/promises"
3
+ import { homedir, tmpdir } from "node:os"
4
+ import path from "node:path"
5
+ import { describe, expect, test } from "bun:test"
6
+ import { computeJsonlPath, computeProjectDir, encodeCwd } from "./jsonl-path.adapter"
7
+
8
+ describe("encodeCwd", () => {
9
+ test("absolute path: replaces / with -", () => {
10
+ const expected = homedir().replace(/\//g, "-").replace(/\./g, "-")
11
+ expect(encodeCwd(homedir())).toBe(expected)
12
+ })
13
+ test("absolute path with trailing slash: trims it", () => {
14
+ const expected = homedir().replace(/\//g, "-").replace(/\./g, "-")
15
+ expect(encodeCwd(homedir() + "/")).toBe(expected)
16
+ })
17
+ test("nested path", () => {
18
+ const expected = process.cwd().replace(/\//g, "-").replace(/\./g, "-")
19
+ expect(encodeCwd(process.cwd())).toBe(expected)
20
+ })
21
+ test("root path", () => {
22
+ expect(encodeCwd("/")).toBe("-")
23
+ })
24
+ })
25
+
26
+ describe("computeJsonlPath", () => {
27
+ test("combines homeDir + encoded cwd + session uuid", async () => {
28
+ const tmp = await mkdtemp(path.join(tmpdir(), "kanna-jsonlpath-"))
29
+ try {
30
+ const realPath = realpathSync(tmp)
31
+ const encodedCwd = realPath.replace(/\//g, "-").replace(/\./g, "-")
32
+ const result = computeJsonlPath({
33
+ homeDir: "/home/u",
34
+ cwd: tmp,
35
+ sessionId: "abc-123",
36
+ })
37
+ expect(result).toBe(`/home/u/.claude/projects/${encodedCwd}/abc-123.jsonl`)
38
+ } finally {
39
+ await rm(tmp, { recursive: true, force: true })
40
+ }
41
+ })
42
+ })
43
+
44
+ describe("encodeCwd realpath + dot replacement", () => {
45
+ test("resolves macOS /var -> /private/var symlink", async () => {
46
+ const tmp = await mkdtemp(path.join(tmpdir(), "kanna-encodecwd-"))
47
+ try {
48
+ const encoded = encodeCwd(tmp)
49
+ const realPath = realpathSync(tmp)
50
+ const expectedEncoded = realPath.replace(/\//g, "-").replace(/\./g, "-")
51
+ expect(encoded).toBe(expectedEncoded)
52
+ } finally {
53
+ await rm(tmp, { recursive: true, force: true })
54
+ }
55
+ })
56
+
57
+ test("replaces dots with dashes in segment names", async () => {
58
+ // mkdtemp ensures the directory exists so realpathSync succeeds
59
+ const tmp = await mkdtemp(path.join(tmpdir(), "kanna.dot-test-"))
60
+ try {
61
+ const encoded = encodeCwd(tmp)
62
+ expect(encoded).not.toContain(".")
63
+ // The "kanna.dot-test-XXXX" segment dot must be replaced
64
+ expect(encoded).toContain("kanna-dot-test-")
65
+ } finally {
66
+ await rm(tmp, { recursive: true, force: true })
67
+ }
68
+ })
69
+
70
+ test("trailing slash trimmed before encoding", () => {
71
+ const a = encodeCwd("/etc/")
72
+ const b = encodeCwd("/etc")
73
+ expect(a).toBe(b)
74
+ })
75
+
76
+ test("root / encodes to single dash", () => {
77
+ const result = encodeCwd("/")
78
+ expect(result).toBe("-")
79
+ })
80
+
81
+ test("replaces underscore with dash (claude sanitizePath parity)", async () => {
82
+ const tmp = await mkdtemp(path.join(tmpdir(), "kanna_under_"))
83
+ try {
84
+ const encoded = encodeCwd(tmp)
85
+ expect(encoded).not.toContain("_")
86
+ } finally {
87
+ await rm(tmp, { recursive: true, force: true })
88
+ }
89
+ })
90
+
91
+ test("encoded segment matches /[^a-zA-Z0-9-]/ never present", async () => {
92
+ const tmp = await mkdtemp(path.join(tmpdir(), "kanna-charset-"))
93
+ try {
94
+ const encoded = encodeCwd(tmp)
95
+ expect(encoded).toMatch(/^[a-zA-Z0-9-]+$/)
96
+ } finally {
97
+ await rm(tmp, { recursive: true, force: true })
98
+ }
99
+ })
100
+ })
101
+
102
+ describe("computeProjectDir", () => {
103
+ test("returns .claude/projects/<encodedCwd> path", async () => {
104
+ const tmp = await mkdtemp(path.join(tmpdir(), "kanna-projdir-"))
105
+ try {
106
+ const realPath = realpathSync(tmp)
107
+ const encodedCwd = realPath.replace(/\//g, "-").replace(/\./g, "-")
108
+ const result = computeProjectDir({ homeDir: "/home/user", cwd: tmp })
109
+ expect(result).toBe(`/home/user/.claude/projects/${encodedCwd}`)
110
+ } finally {
111
+ await rm(tmp, { recursive: true, force: true })
112
+ }
113
+ })
114
+ })
@@ -0,0 +1,241 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { parseJsonlLine, createJsonlEventParser } from "./jsonl-to-event"
3
+ import type { HarnessEvent } from "../harness-types"
4
+
5
+ describe("parseJsonlLine", () => {
6
+ test("ignores empty lines", () => {
7
+ expect(parseJsonlLine("")).toEqual([])
8
+ expect(parseJsonlLine(" ")).toEqual([])
9
+ })
10
+
11
+ test("ignores malformed JSON (logs but does not throw)", () => {
12
+ expect(parseJsonlLine("{not json")).toEqual([])
13
+ })
14
+
15
+ test("system.init → session_token event", () => {
16
+ const line = JSON.stringify({
17
+ type: "system",
18
+ subtype: "init",
19
+ session_id: "sess-1",
20
+ model: "claude-sonnet-4-6",
21
+ })
22
+ const events = parseJsonlLine(line)
23
+ const sessionTokenEvent = events.find((e) => e.type === "session_token")
24
+ expect(sessionTokenEvent).toBeDefined()
25
+ expect(sessionTokenEvent?.sessionToken).toBe("sess-1")
26
+ })
27
+
28
+ test("assistant message → transcript event with assistant role", () => {
29
+ const line = JSON.stringify({
30
+ type: "assistant",
31
+ message: {
32
+ role: "assistant",
33
+ content: [{ type: "text", text: "hello" }],
34
+ },
35
+ })
36
+ const events = parseJsonlLine(line)
37
+ const transcriptEvents = events.filter((e) => e.type === "transcript")
38
+ expect(transcriptEvents.length).toBeGreaterThan(0)
39
+ })
40
+
41
+ test("system.rate_limit subtype → rate_limit event", () => {
42
+ const line = JSON.stringify({
43
+ type: "system",
44
+ subtype: "rate_limit",
45
+ resetAt: 1748800000000,
46
+ tz: "PT",
47
+ })
48
+ const events = parseJsonlLine(line)
49
+ const rl = events.find((e) => e.type === "rate_limit")
50
+ expect(rl).toBeDefined()
51
+ expect(rl?.rateLimit?.tz).toBe("PT")
52
+ })
53
+
54
+ test("system.informational without rate-limit content → no rate_limit event", () => {
55
+ const line = JSON.stringify({
56
+ type: "system",
57
+ subtype: "informational",
58
+ content: "Remote Control failed to connect",
59
+ })
60
+ const events = parseJsonlLine(line)
61
+ const rl = events.find((e) => e.type === "rate_limit")
62
+ expect(rl).toBeUndefined()
63
+ })
64
+ })
65
+
66
+ describe("createJsonlEventParser", () => {
67
+ function emitTypes(events: HarnessEvent[]): string[] {
68
+ return events.map((e) => e.type)
69
+ }
70
+
71
+ test("D3: emits session_token for every line carrying a session_id (not only system/init)", () => {
72
+ const parser = createJsonlEventParser()
73
+ const initLine = JSON.stringify({
74
+ type: "system",
75
+ subtype: "init",
76
+ session_id: "sess-A",
77
+ })
78
+ const assistantLine = JSON.stringify({
79
+ type: "assistant",
80
+ session_id: "sess-A",
81
+ message: { id: "msg-1", role: "assistant", content: [{ type: "text", text: "hi" }] },
82
+ })
83
+ const initEvents = parser.parse(initLine)
84
+ const assistantEvents = parser.parse(assistantLine)
85
+ expect(initEvents.find((e) => e.type === "session_token")?.sessionToken).toBe("sess-A")
86
+ expect(assistantEvents.find((e) => e.type === "session_token")?.sessionToken).toBe("sess-A")
87
+ })
88
+
89
+ test("D3: lines without session_id do not emit session_token", () => {
90
+ const parser = createJsonlEventParser()
91
+ const noSession = JSON.stringify({ type: "assistant", message: { role: "assistant", content: [] } })
92
+ const events = parser.parse(noSession)
93
+ expect(events.find((e) => e.type === "session_token")).toBeUndefined()
94
+ })
95
+
96
+ test("D2: SDK-native rate_limit_event message → rate_limit event via detector", () => {
97
+ const parser = createJsonlEventParser()
98
+ const line = JSON.stringify({
99
+ type: "rate_limit_event",
100
+ rate_limit_info: {
101
+ status: "rejected",
102
+ // Epoch seconds (detector coerces to ms).
103
+ resetsAt: 1_748_800_000,
104
+ },
105
+ })
106
+ const events = parser.parse(line)
107
+ const rl = events.find((e) => e.type === "rate_limit")
108
+ expect(rl).toBeDefined()
109
+ expect(rl?.rateLimit?.resetAt).toBe(1_748_800_000_000)
110
+ })
111
+
112
+ test("D2: rate_limit_event with status != rejected → no event", () => {
113
+ const parser = createJsonlEventParser()
114
+ const line = JSON.stringify({
115
+ type: "rate_limit_event",
116
+ rate_limit_info: { status: "allowed", resetsAt: 1_748_800_000 },
117
+ })
118
+ const events = parser.parse(line)
119
+ expect(events.find((e) => e.type === "rate_limit")).toBeUndefined()
120
+ })
121
+
122
+ test("D2: legacy system/rate_limit shape still recognised", () => {
123
+ const parser = createJsonlEventParser()
124
+ const line = JSON.stringify({
125
+ type: "system",
126
+ subtype: "rate_limit",
127
+ resetAt: 1748800000000,
128
+ tz: "PT",
129
+ })
130
+ const events = parser.parse(line)
131
+ const rl = events.find((e) => e.type === "rate_limit")
132
+ expect(rl).toBeDefined()
133
+ expect(rl?.rateLimit?.tz).toBe("PT")
134
+ })
135
+
136
+ test("D1: assistant message with usage → context_window_updated transcript", () => {
137
+ const parser = createJsonlEventParser()
138
+ const line = JSON.stringify({
139
+ type: "assistant",
140
+ message: {
141
+ id: "msg-usage-1",
142
+ role: "assistant",
143
+ content: [{ type: "text", text: "hi" }],
144
+ },
145
+ usage: {
146
+ input_tokens: 100,
147
+ cache_creation_input_tokens: 0,
148
+ cache_read_input_tokens: 0,
149
+ output_tokens: 25,
150
+ },
151
+ })
152
+ const events = parser.parse(line)
153
+ const ctxEvents = events.filter(
154
+ (e) => e.type === "transcript" && (e.entry as { kind?: string }).kind === "context_window_updated",
155
+ )
156
+ expect(ctxEvents).toHaveLength(1)
157
+ })
158
+
159
+ test("D1: duplicate assistant usage id is deduped", () => {
160
+ const parser = createJsonlEventParser()
161
+ const line = JSON.stringify({
162
+ type: "assistant",
163
+ message: {
164
+ id: "msg-dedup",
165
+ role: "assistant",
166
+ content: [{ type: "text", text: "hi" }],
167
+ },
168
+ usage: { input_tokens: 50, output_tokens: 10 },
169
+ })
170
+ const first = parser.parse(line)
171
+ const second = parser.parse(line)
172
+ const firstCtx = first.filter(
173
+ (e) => e.type === "transcript" && (e.entry as { kind?: string }).kind === "context_window_updated",
174
+ )
175
+ const secondCtx = second.filter(
176
+ (e) => e.type === "transcript" && (e.entry as { kind?: string }).kind === "context_window_updated",
177
+ )
178
+ expect(firstCtx).toHaveLength(1)
179
+ expect(secondCtx).toHaveLength(0)
180
+ })
181
+
182
+ test("D1: result message after assistant emits final context_window_updated", () => {
183
+ const parser = createJsonlEventParser()
184
+ parser.parse(JSON.stringify({
185
+ type: "assistant",
186
+ message: { id: "msg-1", role: "assistant", content: [{ type: "text", text: "hi" }] },
187
+ usage: { input_tokens: 80, output_tokens: 20 },
188
+ }))
189
+ const resultLine = JSON.stringify({
190
+ type: "result",
191
+ subtype: "success",
192
+ result: "done",
193
+ isError: false,
194
+ durationMs: 1000,
195
+ usage: { input_tokens: 80, output_tokens: 20 },
196
+ modelUsage: {
197
+ "claude-sonnet-4-6": { contextWindow: 200000, inputTokens: 80, outputTokens: 20 },
198
+ },
199
+ })
200
+ const events = parser.parse(resultLine)
201
+ const ctxEvents = events.filter(
202
+ (e) => e.type === "transcript" && (e.entry as { kind?: string }).kind === "context_window_updated",
203
+ )
204
+ expect(ctxEvents).toHaveLength(1)
205
+ })
206
+
207
+ test("D1: 1M context window floor preserved when modelUsage reports 200k", () => {
208
+ const parser = createJsonlEventParser({ configuredContextWindow: 1_000_000 })
209
+ parser.parse(JSON.stringify({
210
+ type: "assistant",
211
+ message: { id: "msg-1m", role: "assistant", content: [{ type: "text", text: "hi" }] },
212
+ usage: { input_tokens: 100, output_tokens: 50 },
213
+ }))
214
+ const resultLine = JSON.stringify({
215
+ type: "result",
216
+ subtype: "success",
217
+ result: "done",
218
+ isError: false,
219
+ durationMs: 500,
220
+ usage: { input_tokens: 100, output_tokens: 50 },
221
+ modelUsage: { "claude-sonnet-4-6": { contextWindow: 200000 } },
222
+ })
223
+ const events = parser.parse(resultLine)
224
+ const ctx = events.find(
225
+ (e) => e.type === "transcript" && (e.entry as { kind?: string }).kind === "context_window_updated",
226
+ )
227
+ const usage = (ctx?.entry as { usage?: { maxTokens?: number } } | undefined)?.usage
228
+ expect(usage?.maxTokens).toBe(1_000_000)
229
+ })
230
+
231
+ test("emitTypes helper produces deterministic order across calls", () => {
232
+ const parser = createJsonlEventParser()
233
+ const types = emitTypes(parser.parse(JSON.stringify({
234
+ type: "system",
235
+ subtype: "init",
236
+ session_id: "sess-X",
237
+ })))
238
+ expect(types[0]).toBe("session_token")
239
+ })
240
+
241
+ })
@@ -0,0 +1,174 @@
1
+ import type { HarnessEvent } from "../harness-types"
2
+ import type { ContextWindowUsageSnapshot } from "../../shared/types"
3
+ import {
4
+ normalizeClaudeStreamMessage,
5
+ normalizeClaudeUsageSnapshot,
6
+ resolveFinalTurnUsage,
7
+ maxClaudeContextWindowFromModelUsage,
8
+ getClaudeAssistantMessageUsageId,
9
+ timestamped,
10
+ } from "./agent-normalizers"
11
+ import { ClaudeLimitDetector } from "./../auto-continue/limit-detector"
12
+
13
+ export interface JsonlEventParser {
14
+ /** Parse one JSONL line; returns zero or more harness events. Stateful — updates internal usage / context-window tracking across calls. */
15
+ parse(rawLine: string): HarnessEvent[]
16
+ }
17
+
18
+ export interface CreateJsonlEventParserOptions {
19
+ /** Per-model context-window floor (e.g. 1_000_000 for `[1m]` models). */
20
+ configuredContextWindow?: number
21
+ }
22
+
23
+ /**
24
+ * Stateful JSONL → HarnessEvent parser. One instance per PTY session so
25
+ * usage snapshots can be diffed across `assistant` → `result` messages,
26
+ * matching the SDK driver's `createClaudeHarnessStream` shape.
27
+ */
28
+ export function createJsonlEventParser(opts: CreateJsonlEventParserOptions = {}): JsonlEventParser {
29
+ let seenAssistantUsageIds = new Set<string>()
30
+ let latestUsageSnapshot: ContextWindowUsageSnapshot | null = null
31
+ let lastKnownContextWindow: number | undefined = opts.configuredContextWindow
32
+ const detector = new ClaudeLimitDetector()
33
+
34
+ return {
35
+ parse(rawLine: string): HarnessEvent[] {
36
+ const trimmed = rawLine.trim()
37
+ if (!trimmed) return []
38
+ let parsed: unknown
39
+ try {
40
+ parsed = JSON.parse(trimmed)
41
+ } catch {
42
+ console.warn("[claude-pty/jsonl] failed to parse line", trimmed.slice(0, 120))
43
+ return []
44
+ }
45
+ if (!parsed || typeof parsed !== "object") return []
46
+ const message = parsed as Record<string, unknown>
47
+ const events: HarnessEvent[] = []
48
+
49
+ // D3 — emit session_token for any message carrying a session_id, not
50
+ // just `system/init`. Matches the SDK driver loop in
51
+ // createClaudeHarnessStream (agent.ts).
52
+ if (typeof message.session_id === "string" && message.session_id.length > 0) {
53
+ events.push({ type: "session_token", sessionToken: message.session_id })
54
+ }
55
+
56
+ // D2 — recognise both shapes:
57
+ // (a) the SDK-native `rate_limit_event` message Claude Code mirrors
58
+ // into JSONL when running under the agent SDK
59
+ // (b) legacy `system/rate_limit` shape kept for any older CLI build
60
+ // that emits it (existing kanna call sites).
61
+ if (message.type === "rate_limit_event") {
62
+ const detection = detector.detectFromSdkRateLimitInfo(
63
+ "",
64
+ (message as { rate_limit_info?: unknown }).rate_limit_info,
65
+ )
66
+ if (detection) {
67
+ events.push({ type: "rate_limit", rateLimit: { resetAt: detection.resetAt, tz: detection.tz } })
68
+ }
69
+ } else if (message.type === "system" && message.subtype === "rate_limit") {
70
+ const resetAt = typeof message.resetAt === "number" ? message.resetAt : Date.now()
71
+ const tz = typeof message.tz === "string" ? message.tz : "UTC"
72
+ events.push({ type: "rate_limit", rateLimit: { resetAt, tz } })
73
+ }
74
+
75
+ // D1 — assistant message usage delta → context_window_updated.
76
+ if (message.type === "assistant") {
77
+ const usageId = getClaudeAssistantMessageUsageId(message)
78
+ const usageSnapshot = normalizeClaudeUsageSnapshot(
79
+ (message as { usage?: unknown }).usage,
80
+ lastKnownContextWindow,
81
+ )
82
+ if (usageId && usageSnapshot && !seenAssistantUsageIds.has(usageId)) {
83
+ seenAssistantUsageIds.add(usageId)
84
+ latestUsageSnapshot = usageSnapshot
85
+ events.push({
86
+ type: "transcript",
87
+ entry: timestamped({ kind: "context_window_updated", usage: usageSnapshot }),
88
+ })
89
+ }
90
+ }
91
+
92
+ // D1 — turn-end context window emit. Preserves the configured-window
93
+ // floor so the SDK-internal `modelUsage.contextWindow` of 200_000
94
+ // can't silently override a 1M-beta opt-in.
95
+ if (message.type === "result") {
96
+ const resultContextWindow = maxClaudeContextWindowFromModelUsage(
97
+ (message as { modelUsage?: unknown }).modelUsage,
98
+ )
99
+ if (resultContextWindow !== undefined) {
100
+ lastKnownContextWindow = Math.max(lastKnownContextWindow ?? 0, resultContextWindow)
101
+ }
102
+ const accumulatedUsage = normalizeClaudeUsageSnapshot(
103
+ (message as { usage?: unknown }).usage,
104
+ lastKnownContextWindow,
105
+ )
106
+ const finalUsage = resolveFinalTurnUsage(
107
+ latestUsageSnapshot,
108
+ accumulatedUsage,
109
+ lastKnownContextWindow,
110
+ )
111
+ if (finalUsage) {
112
+ events.push({
113
+ type: "transcript",
114
+ entry: timestamped({ kind: "context_window_updated", usage: finalUsage }),
115
+ })
116
+ }
117
+ seenAssistantUsageIds = new Set<string>()
118
+ latestUsageSnapshot = null
119
+ }
120
+
121
+ try {
122
+ const entries = normalizeClaudeStreamMessage(parsed)
123
+ for (const entry of entries) {
124
+ events.push({ type: "transcript", entry })
125
+ }
126
+ } catch (err) {
127
+ console.warn("[claude-pty/jsonl] normalizeClaudeStreamMessage threw", err)
128
+ }
129
+
130
+ return events
131
+ },
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Stateless wrapper kept for callers that don't need usage tracking.
137
+ * Behaves the same as before D1/D2/D3 landed: no usage diff, no per-message
138
+ * session_token. New callers should use `createJsonlEventParser` instead.
139
+ */
140
+ export function parseJsonlLine(rawLine: string): HarnessEvent[] {
141
+ const trimmed = rawLine.trim()
142
+ if (!trimmed) return []
143
+ let parsed: unknown
144
+ try {
145
+ parsed = JSON.parse(trimmed)
146
+ } catch {
147
+ console.warn("[claude-pty/jsonl] failed to parse line", trimmed.slice(0, 120))
148
+ return []
149
+ }
150
+ if (!parsed || typeof parsed !== "object") return []
151
+ const message = parsed as Record<string, unknown>
152
+ const events: HarnessEvent[] = []
153
+
154
+ if (message.type === "system" && message.subtype === "init" && typeof message.session_id === "string") {
155
+ events.push({ type: "session_token", sessionToken: message.session_id })
156
+ }
157
+
158
+ if (message.type === "system" && message.subtype === "rate_limit") {
159
+ const resetAt = typeof message.resetAt === "number" ? message.resetAt : Date.now()
160
+ const tz = typeof message.tz === "string" ? message.tz : "UTC"
161
+ events.push({ type: "rate_limit", rateLimit: { resetAt, tz } })
162
+ }
163
+
164
+ try {
165
+ const entries = normalizeClaudeStreamMessage(parsed)
166
+ for (const entry of entries) {
167
+ events.push({ type: "transcript", entry })
168
+ }
169
+ } catch (err) {
170
+ console.warn("[claude-pty/jsonl] normalizeClaudeStreamMessage threw", err)
171
+ }
172
+
173
+ return events
174
+ }
@@ -0,0 +1,35 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { OutputRing, OUTPUT_RING_DEFAULT_BYTES } from "./output-ring"
3
+
4
+ describe("OutputRing", () => {
5
+ test("appends and returns full content under capacity", () => {
6
+ const r = new OutputRing(100)
7
+ r.append("hello ")
8
+ r.append("world")
9
+ expect(r.tail()).toBe("hello world")
10
+ })
11
+
12
+ test("drops oldest bytes once capacity exceeded", () => {
13
+ const r = new OutputRing(5)
14
+ r.append("abcdefgh")
15
+ expect(r.tail()).toBe("defgh")
16
+ })
17
+
18
+ test("default capacity is 256 KB", () => {
19
+ expect(OUTPUT_RING_DEFAULT_BYTES).toBe(256 * 1024)
20
+ })
21
+
22
+ test("contains(needle) returns true when present in tail", () => {
23
+ const r = new OutputRing(100)
24
+ r.append("Please run /login")
25
+ expect(r.contains("/login")).toBe(true)
26
+ expect(r.contains("foobar")).toBe(false)
27
+ })
28
+
29
+ test("contains works after rotation", () => {
30
+ const r = new OutputRing(20)
31
+ r.append("xxxxxxxxxxxxx")
32
+ r.append("Please run /login")
33
+ expect(r.contains("/login")).toBe(true)
34
+ })
35
+ })
@@ -0,0 +1,25 @@
1
+ export const OUTPUT_RING_DEFAULT_BYTES = 256 * 1024
2
+
3
+ export class OutputRing {
4
+ private buf = ""
5
+ private readonly capacity: number
6
+
7
+ constructor(capacityBytes: number = OUTPUT_RING_DEFAULT_BYTES) {
8
+ this.capacity = capacityBytes
9
+ }
10
+
11
+ append(chunk: string): void {
12
+ this.buf += chunk
13
+ if (this.buf.length > this.capacity) {
14
+ this.buf = this.buf.slice(this.buf.length - this.capacity)
15
+ }
16
+ }
17
+
18
+ tail(): string {
19
+ return this.buf
20
+ }
21
+
22
+ contains(needle: string): boolean {
23
+ return this.buf.includes(needle)
24
+ }
25
+ }