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,103 @@
1
+ /**
2
+ * Callout signing-key management (Stage A, PR1).
3
+ *
4
+ * The auth-callout requires two nkey pairs:
5
+ * - An **account key pair** — the callout issuer account (public key goes into
6
+ * the nats-server config; the seed is held by the responder to sign user JWTs).
7
+ * - An **auth-service key pair** — the user listed in `auth_users` whose
8
+ * connection bypasses the callout (required by NATS auth-callout spec).
9
+ *
10
+ * Both seeds are stored alongside `nats.token` in NATS_DATA_DIR (same on-disk
11
+ * trust boundary; gitignored). The server reads them at startup; key rotation
12
+ * is a documented follow-up.
13
+ */
14
+
15
+ import { join } from "node:path"
16
+ import { mkdirSync, renameSync, chmodSync } from "node:fs"
17
+ import { createAccount, createUser, fromSeed } from "@nats-io/nkeys"
18
+ import type { KeyPair } from "@nats-io/nkeys"
19
+
20
+ const ACCOUNT_SEED_FILE = "nats.callout-account.seed"
21
+ const AUTH_USER_SEED_FILE = "nats.callout-auth-user.seed"
22
+ const TOKEN_SECRET_FILE = "nats.callout-token.secret"
23
+
24
+ /** Loaded callout key material. */
25
+ export interface CalloutKeys {
26
+ /** KeyPair for the callout issuer account (signs user JWTs). */
27
+ accountKp: KeyPair
28
+ /** Public key of the account (goes into nats-server config). */
29
+ accountPublicKey: string
30
+ /** KeyPair for the auth-service user (bypasses callout). */
31
+ authUserKp: KeyPair
32
+ /** Public key of the auth-service user (goes into nats-server config). */
33
+ authUserPublicKey: string
34
+ /**
35
+ * 32-byte shared secret used to mint and verify stateless credential tokens
36
+ * (Stage B). Both the server process and the daemon child load this from disk.
37
+ */
38
+ tokenSecret: Uint8Array
39
+ }
40
+
41
+ async function readOrCreateSeed(
42
+ seedPath: string,
43
+ generator: () => KeyPair
44
+ ): Promise<string> {
45
+ const file = Bun.file(seedPath)
46
+ if (await file.exists()) {
47
+ const seed = (await file.text()).trim()
48
+ if (seed.length > 0) return seed
49
+ }
50
+
51
+ const kp = generator()
52
+ const seed = Buffer.from(kp.getSeed()).toString()
53
+ const tmp = `${seedPath}.tmp.${process.pid}`
54
+ await Bun.write(tmp, seed + "\n")
55
+ renameSync(tmp, seedPath) // atomic
56
+ chmodSync(seedPath, 0o600) // owner-read/write only
57
+ return seed
58
+ }
59
+
60
+ async function readOrCreateTokenSecret(secretPath: string): Promise<Uint8Array> {
61
+ const file = Bun.file(secretPath)
62
+ if (await file.exists()) {
63
+ const hex = (await file.text()).trim()
64
+ if (hex.length === 64) return new Uint8Array(Buffer.from(hex, "hex"))
65
+ }
66
+
67
+ const secret = new Uint8Array(32)
68
+ crypto.getRandomValues(secret)
69
+ const hex = Buffer.from(secret).toString("hex")
70
+ const tmp = `${secretPath}.tmp.${process.pid}`
71
+ await Bun.write(tmp, hex + "\n")
72
+ renameSync(tmp, secretPath) // atomic
73
+ chmodSync(secretPath, 0o600) // owner-read/write only
74
+ return secret
75
+ }
76
+
77
+ /**
78
+ * Ensure callout key material exists in `dataDir`.
79
+ * Generates both pairs and the token secret on first call; subsequent calls load from disk.
80
+ * Safe for single-writer use (the NATS daemon); not safe for concurrent writers.
81
+ */
82
+ export async function ensureCalloutKeys(dataDir: string): Promise<CalloutKeys> {
83
+ mkdirSync(dataDir, { recursive: true })
84
+
85
+ const accountSeedPath = join(dataDir, ACCOUNT_SEED_FILE)
86
+ const authUserSeedPath = join(dataDir, AUTH_USER_SEED_FILE)
87
+ const tokenSecretPath = join(dataDir, TOKEN_SECRET_FILE)
88
+
89
+ const accountSeed = await readOrCreateSeed(accountSeedPath, createAccount)
90
+ const authUserSeed = await readOrCreateSeed(authUserSeedPath, createUser)
91
+ const tokenSecret = await readOrCreateTokenSecret(tokenSecretPath)
92
+
93
+ const accountKp = fromSeed(Buffer.from(accountSeed.trim()))
94
+ const authUserKp = fromSeed(Buffer.from(authUserSeed.trim()))
95
+
96
+ return {
97
+ accountKp,
98
+ accountPublicKey: accountKp.getPublicKey(),
99
+ authUserKp,
100
+ authUserPublicKey: authUserKp.getPublicKey(),
101
+ tokenSecret,
102
+ }
103
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Auth-callout responder (Stage B, PR1).
3
+ *
4
+ * Subscribes to $SYS.REQ.USER.AUTH. For each request:
5
+ * 1. Decode the AuthorizationRequest JWT from NATS.
6
+ * 2. Validate the credential in connect_opts.auth_token via verifyCredentialToken.
7
+ * 3. Resolve the connection class (and runnerId for runner class).
8
+ * 4. Mint a scoped user JWT and respond with a signed AuthorizationResponse.
9
+ * 5. Audit every decision (grant/deny).
10
+ *
11
+ * The responding connection itself uses the auth-service nkey (authUserKp) and
12
+ * bypasses the callout, per the NATS auth_callout spec.
13
+ *
14
+ * Stage B: replaced in-memory credential registry with stateless HMAC-signed
15
+ * tokens (verifyCredentialToken). No registration step; both the server process
16
+ * (issuer) and this daemon child (verifier) share only the on-disk token secret.
17
+ */
18
+
19
+ import { connect, type NatsConnection, type Subscription } from "@nats-io/transport-node"
20
+ import { nkeyAuthenticator } from "@nats-io/nats-core"
21
+ import {
22
+ encodeUser,
23
+ encodeAuthorizationResponse,
24
+ decode,
25
+ Algorithms,
26
+ fromPublic,
27
+ createUser,
28
+ } from "@nats-io/jwt"
29
+ import type { AuthorizationRequest } from "@nats-io/jwt"
30
+ import type { KeyPair } from "@nats-io/nkeys"
31
+ import { permissionsFor } from "./scope-policy"
32
+ import type { ResolvedIdentity } from "./scope-policy"
33
+ import { verifyCredentialToken } from "./token"
34
+
35
+ const CALLOUT_SUBJECT = "$SYS.REQ.USER.AUTH"
36
+
37
+ // ── Responder ─────────────────────────────────────────────────────────────────
38
+
39
+ export interface ResponderOptions {
40
+ /** NATS URL to connect to (TCP). */
41
+ natsUrl: string
42
+ /** KeyPair for the auth-service user (bypasses callout). */
43
+ authUserKp: KeyPair
44
+ /** KeyPair for the callout issuer account (signs user JWTs). */
45
+ accountKp: KeyPair
46
+ /** Public key of the callout account (needed for encodeUser issuer_account). */
47
+ accountPublicKey: string
48
+ /**
49
+ * Account name as declared in the nats-server config accounts block.
50
+ * This becomes the `aud` (audience) of the issued user JWT so NATS knows
51
+ * which account to place the connection in.
52
+ */
53
+ accountName?: string
54
+ /**
55
+ * 32-byte shared secret loaded from NATS_DATA_DIR by the daemon child.
56
+ * Used to verify stateless credential tokens without any in-memory registry.
57
+ */
58
+ tokenSecret: Uint8Array
59
+ }
60
+
61
+ export class CalloutResponder {
62
+ private nc: NatsConnection | null = null
63
+ private sub: Subscription | null = null
64
+ private readonly opts: ResponderOptions
65
+
66
+ constructor(opts: ResponderOptions) {
67
+ this.opts = opts
68
+ }
69
+
70
+ /**
71
+ * Connect to NATS as the auth-service user (bypasses callout) and
72
+ * start the subscription loop.
73
+ */
74
+ async start(): Promise<void> {
75
+ const { authUserKp, natsUrl } = this.opts
76
+
77
+ // Connect using the auth-service nkey (bypasses the callout per NATS spec).
78
+ // nkeyAuthenticator takes the seed bytes; the server verifies the public key
79
+ // against auth_users in the config.
80
+ this.nc = await connect({
81
+ servers: natsUrl,
82
+ authenticator: nkeyAuthenticator(authUserKp.getSeed()),
83
+ })
84
+
85
+ this.sub = this.nc.subscribe(CALLOUT_SUBJECT)
86
+ void this.listenLoop().catch((err) => {
87
+ console.error("[nats-callout] listenLoop fatal:", err)
88
+ })
89
+ }
90
+
91
+ /** Stop the responder and drain the NATS connection. */
92
+ async stop(): Promise<void> {
93
+ this.sub?.unsubscribe()
94
+ if (this.nc) {
95
+ try { await this.nc.drain() } catch { /* ignore drain errors on shutdown */ }
96
+ this.nc = null
97
+ }
98
+ }
99
+
100
+ private async listenLoop(): Promise<void> {
101
+ if (!this.sub) return
102
+ for await (const msg of this.sub) {
103
+ await this.handleCallout(msg)
104
+ }
105
+ }
106
+
107
+ private async handleCallout(msg: { data: Uint8Array; respond: (data: Uint8Array) => void }): Promise<void> {
108
+ const { accountKp, accountPublicKey, accountName = "CALLOUT_ACCOUNT" } = this.opts
109
+
110
+ // The callout request body is a JWT (not plain JSON).
111
+ // Decode it to get the AuthorizationRequest payload from the `nats` claim.
112
+ let req: AuthorizationRequest
113
+ try {
114
+ const jwt = new TextDecoder().decode(msg.data)
115
+ const claims = decode<AuthorizationRequest>(jwt)
116
+ req = claims.nats as unknown as AuthorizationRequest
117
+ } catch (err) {
118
+ this.audit("deny", "unparseable-request", null, `decode error: ${err}`)
119
+ await this.respondError(msg, accountKp, null, "invalid authorization request")
120
+ return
121
+ }
122
+
123
+ const userNkey = req.user_nkey
124
+ const serverNkey = req.server_id.id
125
+ const presentedToken = req.connect_opts?.auth_token
126
+
127
+ // Verify the stateless signed credential token.
128
+ const identity = await verifyCredentialToken(presentedToken, this.opts.tokenSecret)
129
+ if (!identity) {
130
+ this.audit("deny", userNkey, null, "unknown or invalid credential")
131
+ await this.respondError(msg, accountKp, serverNkey, "unknown credential")
132
+ return
133
+ }
134
+ const scope = permissionsFor(identity)
135
+
136
+ // Mint a fresh user keypair for this connection (the JWT subject).
137
+ // The user nkey from the request is the connecting client's key;
138
+ // we use fromPublic to get a Key that encodeUser can use as ukp.
139
+ let userKey: ReturnType<typeof fromPublic>
140
+ try {
141
+ userKey = fromPublic(userNkey)
142
+ } catch (err) {
143
+ this.audit("deny", userNkey, identity, `invalid user nkey: ${err}`)
144
+ await this.respondError(msg, accountKp, serverNkey, "invalid user nkey")
145
+ return
146
+ }
147
+
148
+ // Encode the scoped user JWT.
149
+ // The `aud` field must be the account name from the nats-server config so
150
+ // NATS knows which account to place this connection in.
151
+ let userJwt: string
152
+ try {
153
+ userJwt = await encodeUser(
154
+ connectionClassName(identity),
155
+ userKey,
156
+ accountKp, // issuer — the account key pair
157
+ {
158
+ pub: scope.pub,
159
+ sub: scope.sub,
160
+ },
161
+ { algorithm: Algorithms.v2, aud: accountName }
162
+ )
163
+ } catch (err) {
164
+ this.audit("deny", userNkey, identity, `jwt encode failed: ${err}`)
165
+ await this.respondError(msg, accountKp, serverNkey, "internal error minting JWT")
166
+ return
167
+ }
168
+
169
+ // Build and sign the authorization response.
170
+ try {
171
+ const serverKp = fromPublic(serverNkey)
172
+ const responseJwt = await encodeAuthorizationResponse(
173
+ userKey,
174
+ serverKp,
175
+ accountKp,
176
+ { jwt: userJwt },
177
+ { algorithm: Algorithms.v2 }
178
+ )
179
+
180
+ this.audit("grant", userNkey, identity, "ok")
181
+ msg.respond(new TextEncoder().encode(responseJwt))
182
+ } catch (err) {
183
+ this.audit("deny", userNkey, identity, `response encode failed: ${err}`)
184
+ await this.respondError(msg, accountKp, serverNkey, "internal error building response")
185
+ }
186
+ }
187
+
188
+ private async respondError(
189
+ msg: { respond: (data: Uint8Array) => void },
190
+ accountKp: KeyPair,
191
+ serverNkey: string | null,
192
+ reason: string
193
+ ): Promise<void> {
194
+ try {
195
+ // For error responses we need a server key; if we don't have one, create
196
+ // a throwaway keypair (NATS only needs a valid structure for the error path).
197
+ const serverKp = serverNkey
198
+ ? fromPublic(serverNkey)
199
+ : createUser()
200
+
201
+ // The user key in an error response is also a throwaway.
202
+ const throwawayUser = createUser()
203
+
204
+ const responseJwt = await encodeAuthorizationResponse(
205
+ throwawayUser,
206
+ serverKp,
207
+ accountKp,
208
+ { error: reason },
209
+ { algorithm: Algorithms.v2 }
210
+ )
211
+ msg.respond(new TextEncoder().encode(responseJwt))
212
+ } catch {
213
+ // Last resort: send empty bytes; NATS will treat as rejection.
214
+ msg.respond(new Uint8Array(0))
215
+ }
216
+ }
217
+
218
+ private audit(
219
+ decision: "grant" | "deny",
220
+ userNkey: string | null,
221
+ identity: ResolvedIdentity | null,
222
+ detail: string
223
+ ): void {
224
+ const ts = new Date().toISOString()
225
+ const cls = identity ? connectionClassName(identity) : "unknown"
226
+ const rid = identity?.class === "runner" ? identity.runnerId : "-"
227
+ console.warn(
228
+ `[nats-callout] ${ts} decision=${decision} class=${cls} runnerId=${rid}`,
229
+ `nkey=${userNkey?.slice(0, 12) ?? "?"}… detail=${detail}`
230
+ )
231
+ }
232
+ }
233
+
234
+ // ── Helpers ───────────────────────────────────────────────────────────────────
235
+
236
+ function connectionClassName(identity: ResolvedIdentity): string {
237
+ return identity.class === "runner"
238
+ ? `runner:${identity.runnerId}`
239
+ : identity.class
240
+ }
241
+
@@ -0,0 +1,159 @@
1
+ import { describe, test, expect } from "bun:test"
2
+ import { permissionsFor, runnerKvKeySubject, runnerCmdWildcard } from "./scope-policy"
3
+
4
+ // Helper: check whether a subject is covered by an allow list.
5
+ // NATS uses prefix-match with ">" wildcard and exact ">" suffix.
6
+ function matchesAny(subject: string, patterns: string[]): boolean {
7
+ for (const pattern of patterns) {
8
+ if (matchesNats(subject, pattern)) return true
9
+ }
10
+ return false
11
+ }
12
+
13
+ function matchesNats(subject: string, pattern: string): boolean {
14
+ if (pattern === subject) return true
15
+ if (pattern.endsWith(".>")) {
16
+ const prefix = pattern.slice(0, -1) // "foo.>" -> "foo."
17
+ if (subject.startsWith(prefix)) return true
18
+ }
19
+ return false
20
+ }
21
+
22
+ // ── server-admin ──────────────────────────────────────────────────────────────
23
+
24
+ describe("server-admin scope", () => {
25
+ const scope = permissionsFor({ class: "server-admin" })
26
+
27
+ test("may publish to runtime.>", () => {
28
+ expect(matchesAny("runtime.runner.cmd.A.start", scope.pub.allow)).toBe(true)
29
+ })
30
+
31
+ test("may publish JetStream ACKs (regression: $JS.ACK is a separate tree from $JS.API)", () => {
32
+ expect(matchesAny("$JS.ACK.KANNA_RUNNER_EVENTS.consumer.1.1.1.1.0", scope.pub.allow)).toBe(true)
33
+ })
34
+
35
+ test("may publish to JetStream API", () => {
36
+ expect(matchesAny("$JS.API.STREAM.INFO", scope.pub.allow)).toBe(true)
37
+ })
38
+
39
+ test("may publish to KV", () => {
40
+ expect(matchesAny("$KV.runtime_runner_registry.someRunner", scope.pub.allow)).toBe(true)
41
+ })
42
+
43
+ test("may subscribe to callout subject", () => {
44
+ expect(matchesAny("$SYS.REQ.USER.AUTH", scope.sub.allow)).toBe(true)
45
+ })
46
+
47
+ test("no deny rules", () => {
48
+ expect(scope.pub.deny).toHaveLength(0)
49
+ expect(scope.sub.deny).toHaveLength(0)
50
+ })
51
+ })
52
+
53
+ // ── ui-client ─────────────────────────────────────────────────────────────────
54
+
55
+ describe("ui-client scope", () => {
56
+ const scope = permissionsFor({ class: "ui-client" })
57
+
58
+ test("may publish to runtime.cmd.>", () => {
59
+ expect(matchesAny("runtime.cmd.someCommand", scope.pub.allow)).toBe(true)
60
+ })
61
+
62
+ test("may subscribe to runner events", () => {
63
+ expect(matchesAny("runtime.runner.evt.chat123", scope.sub.allow)).toBe(true)
64
+ })
65
+
66
+ test("may pull chat messages via JS CONSUMER.MSG.NEXT (regression: was denied)", () => {
67
+ // The browser uses a pull consumer; without this it got Publish Violations
68
+ // and could not read chat messages. See intake #21.
69
+ expect(
70
+ matchesAny("$JS.API.CONSUMER.MSG.NEXT.KANNA_CHAT_MESSAGE_EVENTS.someConsumer_1", scope.pub.allow),
71
+ ).toBe(true)
72
+ })
73
+
74
+ test("pull fetch is still scoped to the chat stream only (not other streams)", () => {
75
+ expect(
76
+ matchesAny("$JS.API.CONSUMER.MSG.NEXT.KANNA_RUNNER_EVENTS.c", scope.pub.allow),
77
+ ).toBe(false)
78
+ })
79
+
80
+ test("may subscribe to snapshots", () => {
81
+ expect(matchesAny("runtime.snap.chat.abc", scope.sub.allow)).toBe(true)
82
+ })
83
+
84
+ test("runtime.runner.cmd.> is NOT in pub allow list (excluded by default)", () => {
85
+ // NATS: not in allow list means denied. No explicit deny needed.
86
+ expect(matchesAny("runtime.runner.cmd.A.start", scope.pub.allow)).toBe(false)
87
+ })
88
+
89
+ test("may NOT subscribe to runner cmd subjects (not in allow list)", () => {
90
+ expect(matchesAny("runtime.runner.cmd.A.>", scope.sub.allow)).toBe(false)
91
+ })
92
+
93
+ test("no deny rules (allow-only policy)", () => {
94
+ expect(scope.pub.deny).toHaveLength(0)
95
+ expect(scope.sub.deny).toHaveLength(0)
96
+ })
97
+ })
98
+
99
+ // ── runner (per-runnerId) ─────────────────────────────────────────────────────
100
+
101
+ describe("runner scope", () => {
102
+ const runnerId = "runner-A"
103
+ const otherRunnerId = "runner-B"
104
+ const scope = permissionsFor({ class: "runner", runnerId })
105
+
106
+ test("may subscribe to its own cmd subject", () => {
107
+ expect(matchesAny(runnerCmdWildcard(runnerId), scope.sub.allow)).toBe(true)
108
+ expect(matchesAny(`runtime.runner.cmd.${runnerId}.start`, scope.sub.allow)).toBe(true)
109
+ })
110
+
111
+ test("another runner cmd subject is NOT in sub allow (excluded by default)", () => {
112
+ // NATS: not in allow list means denied. No explicit deny needed.
113
+ expect(matchesAny(runnerCmdWildcard(otherRunnerId), scope.sub.allow)).toBe(false)
114
+ expect(matchesAny(`runtime.runner.cmd.${otherRunnerId}.start`, scope.sub.allow)).toBe(false)
115
+ })
116
+
117
+ test("may publish its own heartbeat", () => {
118
+ expect(matchesAny(`runtime.runner.heartbeat.${runnerId}`, scope.pub.allow)).toBe(true)
119
+ })
120
+
121
+ test("another runner heartbeat is NOT in pub allow (excluded by default)", () => {
122
+ expect(matchesAny(`runtime.runner.heartbeat.${otherRunnerId}`, scope.pub.allow)).toBe(false)
123
+ })
124
+
125
+ test("may publish runner events", () => {
126
+ expect(matchesAny("runtime.runner.evt.chat1", scope.pub.allow)).toBe(true)
127
+ })
128
+
129
+ test("may publish its own KV registry key", () => {
130
+ const ownKey = runnerKvKeySubject(runnerId)
131
+ expect(matchesAny(ownKey, scope.pub.allow)).toBe(true)
132
+ })
133
+
134
+ test("another runner KV key is NOT in pub allow (excluded by default)", () => {
135
+ // NATS: not in allow means denied. We use specific-allow only (no wildcard allow,
136
+ // no deny list) so other keys are automatically excluded.
137
+ const foreignKey = runnerKvKeySubject(otherRunnerId)
138
+ expect(matchesAny(foreignKey, scope.pub.allow)).toBe(false)
139
+ })
140
+
141
+ test("no deny rules (allow-only policy)", () => {
142
+ expect(scope.pub.deny).toHaveLength(0)
143
+ expect(scope.sub.deny).toHaveLength(0)
144
+ })
145
+ })
146
+
147
+ // ── runnerKvKeySubject helper ─────────────────────────────────────────────────
148
+
149
+ describe("runnerKvKeySubject", () => {
150
+ test("returns expected KV subject", () => {
151
+ expect(runnerKvKeySubject("abc")).toBe("$KV.runtime_runner_registry.abc")
152
+ })
153
+ })
154
+
155
+ describe("runnerCmdWildcard", () => {
156
+ test("returns expected wildcard", () => {
157
+ expect(runnerCmdWildcard("xyz")).toBe("runtime.runner.cmd.xyz.>")
158
+ })
159
+ })