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,360 @@
1
+ import * as ClaudeAgentSdk from "@anthropic-ai/claude-agent-sdk"
2
+ import type { CanUseTool, McpServerConfig, Options as ClaudeOptions, PermissionResult, Query } from "@anthropic-ai/claude-agent-sdk"
3
+ import { homedir } from "node:os"
4
+ import { existsSync } from "node:fs"
5
+ import { resolveClaudeApiModelId, type TranscriptEntry } from "../shared/types"
6
+ import { getWebContextPrompt } from "../shared/web-context"
7
+ import { normalizeToolCall } from "../shared/tools"
8
+ import type { HarnessEvent, HarnessToolRequest, HarnessTurn } from "./harness-types"
9
+ import type { SessionOrchestrator } from "./orchestration"
10
+ import { createOrchestrationMcpServer } from "./orchestration"
11
+ import { createCoordinationMcpServer } from "./coordination-mcp"
12
+ import type { CoordinationStore } from "../shared/coordination-store"
13
+ import { resolveClaudeBinary } from "./claude-pty/resolve-binary.adapter"
14
+
15
+ const CLAUDE_TOOLSET = [
16
+ "Skill",
17
+ "WebFetch",
18
+ "WebSearch",
19
+ "Task",
20
+ "TaskOutput",
21
+ "Bash",
22
+ "Glob",
23
+ "Grep",
24
+ "Read",
25
+ "Edit",
26
+ "Write",
27
+ "TodoWrite",
28
+ "KillShell",
29
+ "AskUserQuestion",
30
+ "EnterPlanMode",
31
+ "ExitPlanMode",
32
+ ] as const
33
+
34
+ export interface ClaudeSdkBinding {
35
+ query(args: { prompt: string; options?: ClaudeOptions }): Query
36
+ startup?: (args?: { options?: ClaudeOptions }) => Promise<{ query: (prompt: string) => Query }>
37
+ }
38
+
39
+ function timestamped<T extends Omit<TranscriptEntry, "_id" | "createdAt">>(
40
+ entry: T,
41
+ createdAt = Date.now(),
42
+ ): TranscriptEntry {
43
+ return {
44
+ _id: crypto.randomUUID(),
45
+ createdAt,
46
+ ...entry,
47
+ } as TranscriptEntry
48
+ }
49
+
50
+ function stringFromUnknown(value: unknown) {
51
+ if (typeof value === "string") return value
52
+ try {
53
+ return JSON.stringify(value, null, 2)
54
+ } catch {
55
+ return String(value)
56
+ }
57
+ }
58
+
59
+ function createClaudeCanUseTool(
60
+ onToolRequest: (request: HarnessToolRequest) => Promise<unknown>,
61
+ ): CanUseTool {
62
+ return async (toolName, input, options) => {
63
+ if (toolName !== "AskUserQuestion" && toolName !== "ExitPlanMode") {
64
+ return {
65
+ behavior: "allow",
66
+ updatedInput: input,
67
+ }
68
+ }
69
+
70
+ const tool = normalizeToolCall({
71
+ toolName,
72
+ toolId: options.toolUseID,
73
+ input: (input ?? {}) as Record<string, unknown>,
74
+ })
75
+
76
+ if (tool.toolKind !== "ask_user_question" && tool.toolKind !== "exit_plan_mode") {
77
+ return {
78
+ behavior: "deny",
79
+ message: "Unsupported tool request",
80
+ }
81
+ }
82
+
83
+ const result = await onToolRequest({ tool })
84
+
85
+ if (tool.toolKind === "ask_user_question") {
86
+ const record = result && typeof result === "object" ? result as Record<string, unknown> : {}
87
+ return {
88
+ behavior: "allow",
89
+ updatedInput: {
90
+ ...(tool.rawInput ?? {}),
91
+ questions: record.questions ?? tool.input.questions,
92
+ answers: record.answers ?? result,
93
+ },
94
+ } satisfies PermissionResult
95
+ }
96
+
97
+ const record = result && typeof result === "object" ? result as Record<string, unknown> : {}
98
+ const confirmed = Boolean(record.confirmed)
99
+ if (confirmed) {
100
+ return {
101
+ behavior: "allow",
102
+ updatedInput: {
103
+ ...(tool.rawInput ?? {}),
104
+ ...record,
105
+ },
106
+ } satisfies PermissionResult
107
+ }
108
+
109
+ return {
110
+ behavior: "deny",
111
+ message: typeof record.message === "string"
112
+ ? `User wants to suggest edits to the plan: ${record.message}`
113
+ : "User wants to suggest edits to the plan before approving.",
114
+ } satisfies PermissionResult
115
+ }
116
+ }
117
+
118
+ async function createClaudeOptions(args: {
119
+ localPath: string
120
+ model: string
121
+ effort?: string
122
+ planMode: boolean
123
+ sessionToken: string | null
124
+ onToolRequest: (request: HarnessToolRequest) => Promise<unknown>
125
+ orchestrator?: SessionOrchestrator
126
+ chatId?: string
127
+ store?: CoordinationStore
128
+ extraEnv?: Record<string, string>
129
+ /** Injected binary resolver; defaults to the real resolve-binary.adapter. Tests override this. */
130
+ _resolveBinary?: typeof resolveClaudeBinary
131
+ }): Promise<ClaudeOptions> {
132
+ const mcpServers: Record<string, McpServerConfig> = {}
133
+
134
+ if (args.orchestrator && args.chatId) {
135
+ mcpServers["session-orchestration"] = createOrchestrationMcpServer(args.orchestrator, args.chatId)
136
+ }
137
+
138
+ if (args.store) {
139
+ mcpServers["project-coordination"] = createCoordinationMcpServer(args.store)
140
+ }
141
+
142
+ // Resolve the claude binary runner-side — same adapter used by startClaudePtyTurn.
143
+ // Merge extraEnv (non-secret profile env) into the env passed to the resolver so
144
+ // that CLAUDE_EXECUTABLE / CLAUDE_CODE_EXECPATH set via profile are honoured.
145
+ const resolver = args._resolveBinary ?? resolveClaudeBinary
146
+ const mergedEnv = { ...process.env, ...args.extraEnv }
147
+ // DIAGNOSTIC: localPath is the chat's workspace dir; on a remote runner a
148
+ // server-side path won't exist. Log it + cwd existence, and bracket the resolve.
149
+ console.warn("[claude-harness] resolving binary — localPath=" + args.localPath
150
+ + " cwdExists=" + (args.localPath ? existsSync(args.localPath) : false))
151
+ const resolved = await resolver({
152
+ env: mergedEnv,
153
+ homeDir: homedir(),
154
+ cwd: args.localPath,
155
+ })
156
+ console.warn("[claude-harness] binary resolved — path=" + resolved.path)
157
+
158
+ return {
159
+ cwd: args.localPath,
160
+ model: resolveClaudeApiModelId(args.model),
161
+ effort: args.effort as "low" | "medium" | "high" | "max" | undefined,
162
+ resume: args.sessionToken ?? undefined,
163
+ permissionMode: (args.planMode ? "plan" : "acceptEdits") as ClaudeOptions["permissionMode"],
164
+ canUseTool: createClaudeCanUseTool(args.onToolRequest),
165
+ tools: [...CLAUDE_TOOLSET],
166
+ mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
167
+ systemPrompt: {
168
+ type: "preset",
169
+ preset: "claude_code",
170
+ append: getWebContextPrompt("claude"),
171
+ },
172
+ settingSources: ["user", "project", "local"],
173
+ env: (() => {
174
+ const { CLAUDECODE: _, ...env } = process.env
175
+ return { ...env, ...args.extraEnv }
176
+ })(),
177
+ pathToClaudeCodeExecutable: resolved.path,
178
+ } satisfies ClaudeOptions
179
+ }
180
+
181
+ export function normalizeClaudeStreamMessage(message: any): TranscriptEntry[] {
182
+ const debugRaw = process.env.KANNA_DEBUG_RAW ? JSON.stringify(message) : undefined
183
+ const messageId = typeof message.uuid === "string" ? message.uuid : undefined
184
+
185
+ if (message.type === "system" && message.subtype === "init") {
186
+ return [
187
+ timestamped({
188
+ kind: "system_init",
189
+ messageId,
190
+ provider: "claude",
191
+ model: typeof message.model === "string" ? message.model : "unknown",
192
+ tools: Array.isArray(message.tools) ? message.tools : [],
193
+ agents: Array.isArray(message.agents) ? message.agents : [],
194
+ slashCommands: Array.isArray(message.slash_commands)
195
+ ? message.slash_commands.filter((entry: string) => !entry.startsWith("._"))
196
+ : [],
197
+ mcpServers: Array.isArray(message.mcp_servers) ? message.mcp_servers : [],
198
+ debugRaw,
199
+ }),
200
+ ]
201
+ }
202
+
203
+ if (message.type === "assistant" && Array.isArray(message.message?.content)) {
204
+ const entries: TranscriptEntry[] = []
205
+ for (const content of message.message.content) {
206
+ if (content.type === "text" && typeof content.text === "string") {
207
+ entries.push(timestamped({
208
+ kind: "assistant_text",
209
+ messageId,
210
+ text: content.text,
211
+ debugRaw,
212
+ }))
213
+ }
214
+ if (content.type === "tool_use" && typeof content.name === "string" && typeof content.id === "string") {
215
+ entries.push(timestamped({
216
+ kind: "tool_call",
217
+ messageId,
218
+ tool: normalizeToolCall({
219
+ toolName: content.name,
220
+ toolId: content.id,
221
+ input: (content.input ?? {}) as Record<string, unknown>,
222
+ }),
223
+ debugRaw,
224
+ }))
225
+ }
226
+ }
227
+ return entries
228
+ }
229
+
230
+ if (message.type === "user" && Array.isArray(message.message?.content)) {
231
+ const entries: TranscriptEntry[] = []
232
+ for (const content of message.message.content) {
233
+ if (content.type === "tool_result" && typeof content.tool_use_id === "string") {
234
+ entries.push(timestamped({
235
+ kind: "tool_result",
236
+ messageId,
237
+ toolId: content.tool_use_id,
238
+ content: content.content,
239
+ isError: Boolean(content.is_error),
240
+ debugRaw,
241
+ }))
242
+ }
243
+ if (message.message.role === "user" && typeof message.message.content === "string") {
244
+ entries.push(timestamped({
245
+ kind: "compact_summary",
246
+ messageId,
247
+ summary: message.message.content,
248
+ debugRaw,
249
+ }))
250
+ }
251
+ }
252
+ return entries
253
+ }
254
+
255
+ if (message.type === "result") {
256
+ if (message.subtype === "cancelled") {
257
+ return [timestamped({ kind: "interrupted", messageId, debugRaw })]
258
+ }
259
+ return [
260
+ timestamped({
261
+ kind: "result",
262
+ messageId,
263
+ subtype: message.is_error ? "error" : "success",
264
+ isError: Boolean(message.is_error),
265
+ durationMs: typeof message.duration_ms === "number" ? message.duration_ms : 0,
266
+ result: typeof message.result === "string" ? message.result : stringFromUnknown(message.result),
267
+ costUsd: typeof message.total_cost_usd === "number" ? message.total_cost_usd : undefined,
268
+ debugRaw,
269
+ }),
270
+ ]
271
+ }
272
+
273
+ if (message.type === "system" && message.subtype === "status" && typeof message.status === "string") {
274
+ return [timestamped({ kind: "status", messageId, status: message.status, debugRaw })]
275
+ }
276
+
277
+ if (message.type === "system" && message.subtype === "compact_boundary") {
278
+ return [timestamped({ kind: "compact_boundary", messageId, debugRaw })]
279
+ }
280
+
281
+ if (message.type === "system" && message.subtype === "context_cleared") {
282
+ return [timestamped({ kind: "context_cleared", messageId, debugRaw })]
283
+ }
284
+
285
+ if (
286
+ message.type === "user" &&
287
+ message.message?.role === "user" &&
288
+ typeof message.message.content === "string" &&
289
+ message.message.content.startsWith("This session is being continued")
290
+ ) {
291
+ return [timestamped({ kind: "compact_summary", messageId, summary: message.message.content, debugRaw })]
292
+ }
293
+
294
+ return []
295
+ }
296
+
297
+ export async function* createClaudeHarnessStream(q: Query): AsyncGenerator<HarnessEvent> {
298
+ for await (const sdkMessage of q as AsyncIterable<any>) {
299
+ const sessionToken = typeof sdkMessage.session_id === "string" ? sdkMessage.session_id : null
300
+ if (sessionToken) {
301
+ yield { type: "session_token", sessionToken }
302
+ }
303
+ for (const entry of normalizeClaudeStreamMessage(sdkMessage)) {
304
+ yield { type: "transcript", entry }
305
+ }
306
+ }
307
+ }
308
+
309
+ export async function startClaudeTurn(args: {
310
+ content: string
311
+ localPath: string
312
+ model: string
313
+ effort?: string
314
+ planMode: boolean
315
+ sessionToken: string | null
316
+ onToolRequest: (request: HarnessToolRequest) => Promise<unknown>
317
+ orchestrator?: SessionOrchestrator
318
+ chatId?: string
319
+ store?: CoordinationStore
320
+ sdk?: ClaudeSdkBinding
321
+ extraEnv?: Record<string, string>
322
+ /** Injected binary resolver; defaults to the real resolve-binary.adapter. Tests override this. */
323
+ _resolveBinary?: typeof resolveClaudeBinary
324
+ }): Promise<HarnessTurn> {
325
+ console.warn("[claude-harness] startClaudeTurn begin — model=" + args.model + " localPath=" + args.localPath)
326
+ const options = await createClaudeOptions(args)
327
+ console.warn("[claude-harness] options built; starting SDK query (startup=" + Boolean((args.sdk ?? (ClaudeAgentSdk as ClaudeSdkBinding)).startup) + ")")
328
+ const sdk = args.sdk ?? (ClaudeAgentSdk as ClaudeSdkBinding)
329
+
330
+ const q = sdk.startup
331
+ ? (await sdk.startup({ options })).query(args.content)
332
+ : sdk.query({ prompt: args.content, options })
333
+ console.warn("[claude-harness] SDK query handle created")
334
+
335
+ return {
336
+ provider: "claude",
337
+ stream: createClaudeHarnessStream(q),
338
+ getAccountInfo: async () => {
339
+ try {
340
+ return await q.accountInfo()
341
+ } catch {
342
+ return null
343
+ }
344
+ },
345
+ getContextUsage: async () => {
346
+ try {
347
+ const usage = await q.getContextUsage()
348
+ return { percentage: usage.percentage, totalTokens: usage.totalTokens, maxTokens: usage.maxTokens }
349
+ } catch {
350
+ return null
351
+ }
352
+ },
353
+ interrupt: async () => {
354
+ await q.interrupt()
355
+ },
356
+ close: () => {
357
+ q.close()
358
+ },
359
+ }
360
+ }
@@ -0,0 +1,309 @@
1
+ import { normalizeToolCall } from "../../shared/tools"
2
+ import type {
3
+ ContextWindowUsageSnapshot,
4
+ SlashCommand,
5
+ TranscriptEntry,
6
+ } from "../../shared/types"
7
+ import type { HarnessEvent } from "../harness-types"
8
+ import { ClaudeLimitDetector } from "./../auto-continue/limit-detector"
9
+
10
+ export interface ClaudeSessionHandle {
11
+ provider: "claude"
12
+ stream: AsyncIterable<HarnessEvent>
13
+ getAccountInfo?: () => Promise<unknown>
14
+ interrupt: () => Promise<void>
15
+ close: () => void
16
+ sendPrompt: (content: string) => Promise<void>
17
+ setModel: (model: string) => Promise<void>
18
+ setPermissionMode: (planMode: boolean) => Promise<void>
19
+ getSupportedCommands: () => Promise<SlashCommand[]>
20
+ }
21
+
22
+ function asRecord(v: unknown): Record<string, unknown> | null {
23
+ if (v === null || typeof v !== "object") return null
24
+ return v as Record<string, unknown>
25
+ }
26
+
27
+ function asNumber(v: unknown): number | undefined {
28
+ return typeof v === "number" && Number.isFinite(v) ? v : undefined
29
+ }
30
+
31
+ export function timestamped<T extends Omit<TranscriptEntry, "_id" | "createdAt">>(
32
+ entry: T,
33
+ createdAt = Date.now(),
34
+ ): TranscriptEntry {
35
+ return {
36
+ _id: crypto.randomUUID(),
37
+ createdAt,
38
+ ...entry,
39
+ } as TranscriptEntry
40
+ }
41
+
42
+ export function normalizeClaudeUsageSnapshot(
43
+ value: unknown,
44
+ maxTokens?: number,
45
+ ): ContextWindowUsageSnapshot | null {
46
+ const usage = asRecord(value)
47
+ if (!usage) return null
48
+
49
+ const directInputTokens = asNumber(usage.input_tokens) ?? asNumber(usage.inputTokens) ?? 0
50
+ const cacheCreationInputTokens =
51
+ asNumber(usage.cache_creation_input_tokens) ?? asNumber(usage.cacheCreationInputTokens) ?? 0
52
+ const cacheReadInputTokens =
53
+ asNumber(usage.cache_read_input_tokens) ?? asNumber(usage.cacheReadInputTokens) ?? 0
54
+ const outputTokens = asNumber(usage.output_tokens) ?? asNumber(usage.outputTokens) ?? 0
55
+ const reasoningOutputTokens =
56
+ asNumber(usage.reasoning_output_tokens) ?? asNumber(usage.reasoningOutputTokens)
57
+ const toolUses = asNumber(usage.tool_uses) ?? asNumber(usage.toolUses)
58
+ const durationMs = asNumber(usage.duration_ms) ?? asNumber(usage.durationMs)
59
+
60
+ const inputTokens = directInputTokens + cacheCreationInputTokens + cacheReadInputTokens
61
+ const usedTokens = inputTokens + outputTokens
62
+ if (usedTokens <= 0) return null
63
+
64
+ return {
65
+ usedTokens,
66
+ inputTokens,
67
+ ...(cacheReadInputTokens > 0 ? { cachedInputTokens: cacheReadInputTokens } : {}),
68
+ ...(outputTokens > 0 ? { outputTokens } : {}),
69
+ ...(reasoningOutputTokens !== undefined ? { reasoningOutputTokens } : {}),
70
+ lastUsedTokens: usedTokens,
71
+ lastInputTokens: inputTokens,
72
+ ...(cacheReadInputTokens > 0 ? { lastCachedInputTokens: cacheReadInputTokens } : {}),
73
+ ...(outputTokens > 0 ? { lastOutputTokens: outputTokens } : {}),
74
+ ...(reasoningOutputTokens !== undefined ? { lastReasoningOutputTokens: reasoningOutputTokens } : {}),
75
+ ...(toolUses !== undefined ? { toolUses } : {}),
76
+ ...(durationMs !== undefined ? { durationMs } : {}),
77
+ ...(typeof maxTokens === "number" && maxTokens > 0 ? { maxTokens } : {}),
78
+ compactsAutomatically: false,
79
+ } as ContextWindowUsageSnapshot
80
+ }
81
+
82
+ export function resolveFinalTurnUsage(
83
+ latestUsageSnapshot: ContextWindowUsageSnapshot | null,
84
+ accumulatedUsage: ContextWindowUsageSnapshot | null,
85
+ lastKnownContextWindow: number | undefined,
86
+ ): ContextWindowUsageSnapshot | null {
87
+ if (!latestUsageSnapshot) return null
88
+ return {
89
+ ...latestUsageSnapshot,
90
+ ...(typeof lastKnownContextWindow === "number" ? { maxTokens: lastKnownContextWindow } : {}),
91
+ ...(accumulatedUsage && accumulatedUsage.usedTokens > latestUsageSnapshot.usedTokens
92
+ ? { totalProcessedTokens: accumulatedUsage.usedTokens }
93
+ : {}),
94
+ } as ContextWindowUsageSnapshot
95
+ }
96
+
97
+ export function maxClaudeContextWindowFromModelUsage(modelUsage: unknown): number | undefined {
98
+ const record = asRecord(modelUsage)
99
+ if (!record) return undefined
100
+ let maxContextWindow: number | undefined
101
+ for (const value of Object.values(record)) {
102
+ const usage = asRecord(value)
103
+ const contextWindow = asNumber(usage?.contextWindow) ?? asNumber(usage?.context_window)
104
+ if (contextWindow === undefined) continue
105
+ maxContextWindow = Math.max(maxContextWindow ?? 0, contextWindow)
106
+ }
107
+ return maxContextWindow
108
+ }
109
+
110
+ export function parseConfiguredContextWindowFromModelId(modelId: string): number | undefined {
111
+ return modelId.endsWith("[1m]") ? 1_000_000 : undefined
112
+ }
113
+
114
+ export function getClaudeAssistantMessageUsageId(message: unknown): string | null {
115
+ const m = asRecord(message)
116
+ if (!m) return null
117
+ const inner = asRecord(m.message)
118
+ if (inner && typeof inner.id === "string" && inner.id) return inner.id
119
+ if (typeof m.uuid === "string" && m.uuid) return m.uuid as string
120
+ return null
121
+ }
122
+
123
+ /**
124
+ * Stateful SDK → HarnessEvent stream. Implements the same D1/D2/D3 logic as
125
+ * `createJsonlEventParser` so PTY and SDK paths produce identical event
126
+ * sequences for any given message fixture.
127
+ *
128
+ * D1 — assistant usage → context_window_updated (deduped by message id);
129
+ * result → final context_window_updated with resolved context window.
130
+ * D2 — rate_limit_event → rate_limit event.
131
+ * D3 — session_token emitted for every message carrying a session_id.
132
+ */
133
+ export async function* createClaudeHarnessStream(
134
+ source: AsyncIterable<unknown>,
135
+ configuredContextWindow?: number,
136
+ ): AsyncIterable<HarnessEvent> {
137
+ let seenAssistantUsageIds = new Set<string>()
138
+ let latestUsageSnapshot: ContextWindowUsageSnapshot | null = null
139
+ let lastKnownContextWindow: number | undefined = configuredContextWindow
140
+ const detector = new ClaudeLimitDetector()
141
+
142
+ for await (const message of source) {
143
+ const m = asRecord(message)
144
+ if (!m) continue
145
+
146
+ // D3 — session_token for every message carrying a session_id.
147
+ if (typeof m.session_id === "string" && m.session_id.length > 0) {
148
+ yield { type: "session_token", sessionToken: m.session_id }
149
+ }
150
+
151
+ // D2 — rate_limit_event (SDK-native shape).
152
+ if (m.type === "rate_limit_event") {
153
+ const detection = detector.detectFromSdkRateLimitInfo(
154
+ "",
155
+ (m as { rate_limit_info?: unknown }).rate_limit_info,
156
+ )
157
+ if (detection) {
158
+ yield { type: "rate_limit", rateLimit: { resetAt: detection.resetAt, tz: detection.tz } }
159
+ }
160
+ }
161
+
162
+ // D1 — assistant usage delta → context_window_updated (deduped by message id).
163
+ if (m.type === "assistant") {
164
+ const usageId = getClaudeAssistantMessageUsageId(m)
165
+ const usageSnapshot = normalizeClaudeUsageSnapshot(
166
+ (m as { usage?: unknown }).usage,
167
+ lastKnownContextWindow,
168
+ )
169
+ if (usageId && usageSnapshot && !seenAssistantUsageIds.has(usageId)) {
170
+ seenAssistantUsageIds.add(usageId)
171
+ latestUsageSnapshot = usageSnapshot
172
+ yield {
173
+ type: "transcript",
174
+ entry: timestamped({ kind: "context_window_updated", usage: usageSnapshot }),
175
+ }
176
+ }
177
+ }
178
+
179
+ // D1 — result → final context_window_updated, then reset turn state.
180
+ if (m.type === "result") {
181
+ const resultContextWindow = maxClaudeContextWindowFromModelUsage(
182
+ (m as { modelUsage?: unknown }).modelUsage,
183
+ )
184
+ if (resultContextWindow !== undefined) {
185
+ lastKnownContextWindow = Math.max(lastKnownContextWindow ?? 0, resultContextWindow)
186
+ }
187
+ const accumulatedUsage = normalizeClaudeUsageSnapshot(
188
+ (m as { usage?: unknown }).usage,
189
+ lastKnownContextWindow,
190
+ )
191
+ const finalUsage = resolveFinalTurnUsage(latestUsageSnapshot, accumulatedUsage, lastKnownContextWindow)
192
+ if (finalUsage) {
193
+ yield {
194
+ type: "transcript",
195
+ entry: timestamped({ kind: "context_window_updated", usage: finalUsage }),
196
+ }
197
+ }
198
+ seenAssistantUsageIds = new Set<string>()
199
+ latestUsageSnapshot = null
200
+ }
201
+
202
+ // Transcript entries (system_init, assistant_text, tool_call, tool_result, etc.)
203
+ for (const entry of normalizeClaudeStreamMessage(message)) {
204
+ yield { type: "transcript", entry }
205
+ }
206
+ }
207
+ }
208
+
209
+ export function normalizeClaudeStreamMessage(message: unknown): TranscriptEntry[] {
210
+ const m = asRecord(message)
211
+ if (!m) return []
212
+ const uuid = typeof m.uuid === "string" ? m.uuid : undefined
213
+ const inner = asRecord(m.message)
214
+
215
+ if (m.type === "system" && (m as Record<string, unknown>).subtype === "init") {
216
+ return [
217
+ timestamped({
218
+ kind: "system_init",
219
+ messageId: uuid,
220
+ provider: "claude",
221
+ model: typeof m.model === "string" ? m.model : "unknown",
222
+ tools: Array.isArray(m.tools) ? m.tools : [],
223
+ agents: Array.isArray((m as Record<string, unknown>).agents) ? (m as Record<string, unknown>).agents as unknown[] : [],
224
+ slashCommands: Array.isArray((m as Record<string, unknown>).slash_commands)
225
+ ? ((m as Record<string, unknown>).slash_commands as string[]).filter((e) => !e.startsWith("._"))
226
+ : [],
227
+ mcpServers: Array.isArray((m as Record<string, unknown>).mcp_servers)
228
+ ? (m as Record<string, unknown>).mcp_servers as unknown[]
229
+ : [],
230
+ debugRaw: JSON.stringify(m),
231
+ } as Omit<TranscriptEntry, "_id" | "createdAt">),
232
+ ]
233
+ }
234
+
235
+ // claude-code TUI JSONL: assistant message with content array.
236
+ // Emits one entry per content block: text → assistant_text,
237
+ // tool_use → tool_call. Matches kanna's session-mapper.
238
+ if (m.type === "assistant" && inner) {
239
+ const out: TranscriptEntry[] = []
240
+ const content = Array.isArray(inner.content) ? inner.content : []
241
+ const messageId = typeof inner.id === "string" ? inner.id : uuid
242
+ for (const block of content) {
243
+ const b = asRecord(block)
244
+ if (!b) continue
245
+ if (b.type === "text" && typeof b.text === "string" && b.text.length > 0) {
246
+ out.push(timestamped({
247
+ kind: "assistant_text",
248
+ text: b.text,
249
+ messageId,
250
+ } as Omit<TranscriptEntry, "_id" | "createdAt">))
251
+ continue
252
+ }
253
+ if (b.type === "tool_use" && typeof b.name === "string" && typeof b.id === "string") {
254
+ const input = asRecord(b.input) ?? {}
255
+ const tool = normalizeToolCall({ toolName: b.name, toolId: b.id, input })
256
+ out.push(timestamped({
257
+ kind: "tool_call",
258
+ tool,
259
+ messageId,
260
+ } as Omit<TranscriptEntry, "_id" | "createdAt">))
261
+ }
262
+ }
263
+ return out
264
+ }
265
+
266
+ // claude-code TUI JSONL: user record with tool_result blocks (no user_prompt;
267
+ // server's chat.send already records the user message, so a synthetic text
268
+ // user_prompt would duplicate it). tool_result blocks pair with tool_call.
269
+ if (m.type === "user" && inner) {
270
+ const out: TranscriptEntry[] = []
271
+ const content = Array.isArray(inner.content) ? inner.content : []
272
+ for (const block of content) {
273
+ const b = asRecord(block)
274
+ if (!b) continue
275
+ if (b.type !== "tool_result") continue
276
+ const toolId = typeof b.tool_use_id === "string" ? b.tool_use_id : ""
277
+ if (!toolId) continue
278
+ const raw = b.content
279
+ const resultContent = typeof raw === "string"
280
+ ? raw
281
+ : raw === undefined || raw === null
282
+ ? null
283
+ : raw
284
+ out.push(timestamped({
285
+ kind: "tool_result",
286
+ toolId,
287
+ content: resultContent,
288
+ isError: b.is_error === true,
289
+ } as Omit<TranscriptEntry, "_id" | "createdAt">))
290
+ }
291
+ return out
292
+ }
293
+
294
+ // claude-code TUI JSONL: turn end marker.
295
+ if (m.type === "system" && (m as Record<string, unknown>).subtype === "turn_duration") {
296
+ const durationMs = typeof (m as Record<string, unknown>).durationMs === "number"
297
+ ? (m as Record<string, unknown>).durationMs as number
298
+ : 0
299
+ return [timestamped({
300
+ kind: "result",
301
+ success: true,
302
+ result: "",
303
+ durationMs,
304
+ messageId: uuid,
305
+ } as Omit<TranscriptEntry, "_id" | "createdAt">)]
306
+ }
307
+
308
+ return []
309
+ }