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.
- package/LICENSE +84 -0
- package/README.md +202 -0
- package/bin/airaknit +9 -0
- package/bin/airaknit-project +14 -0
- package/bin/kanna +9 -0
- package/dist/client/assets/CompactSummaryMessage-Yw0BDWEJ.js +1 -0
- package/dist/client/assets/ExitPlanModeMessage-DIdkQ4uF.js +1 -0
- package/dist/client/assets/LocalFilePreviewDialog-DQx2eiCc.js +3 -0
- package/dist/client/assets/LocalProjectsSection-C4xlWkgS.js +1 -0
- package/dist/client/assets/TextMessage-B5G39DEJ.js +1 -0
- package/dist/client/assets/UserMessage-CIkWk-0L.js +1 -0
- package/dist/client/assets/_basePickBy-CVrAFfnZ.js +1 -0
- package/dist/client/assets/_baseUniq-JL-aaF4P.js +1 -0
- package/dist/client/assets/arc-B07zg7ol.js +1 -0
- package/dist/client/assets/architecture-YZFGNWBL-PSLVJL3p.js +1 -0
- package/dist/client/assets/architectureDiagram-Q4EWVU46-DfWIF1G_.js +36 -0
- package/dist/client/assets/array-BGFCBI0e.js +1 -0
- package/dist/client/assets/blockDiagram-DXYQGD6D-CzwIeo_B.js +132 -0
- package/dist/client/assets/bundle-mjs-BDE2gWbQ.js +1 -0
- package/dist/client/assets/button-DO50qOGv.js +1 -0
- package/dist/client/assets/c4Diagram-AHTNJAMY-CR8DCQRE.js +10 -0
- package/dist/client/assets/channel-Dj-UUfaF.js +1 -0
- package/dist/client/assets/chunk-2KRD3SAO-dznP-cn8.js +1 -0
- package/dist/client/assets/chunk-336JU56O-Dqss5Vu6.js +2 -0
- package/dist/client/assets/chunk-426QAEUC-CEKis_0_.js +1 -0
- package/dist/client/assets/chunk-4BX2VUAB-BYOv0Gm1.js +1 -0
- package/dist/client/assets/chunk-4TB4RGXK-BxzubH5S.js +206 -0
- package/dist/client/assets/chunk-55IACEB6-BSlTj03a.js +1 -0
- package/dist/client/assets/chunk-5FUZZQ4R-9Au93Bi1.js +62 -0
- package/dist/client/assets/chunk-5PVQY5BW-BhksHFEZ.js +2 -0
- package/dist/client/assets/chunk-67CJDMHE-BFwhz-8t.js +1 -0
- package/dist/client/assets/chunk-7N4EOEYR-BDUPds87.js +1 -0
- package/dist/client/assets/chunk-AA7GKIK3-CEWTdyXO.js +1 -0
- package/dist/client/assets/chunk-BO2N2NFS-D0LvxnhU.js +103 -0
- package/dist/client/assets/chunk-BSJP7CBP-BNJnK6sq.js +1 -0
- package/dist/client/assets/chunk-Bj-mKKzh.js +1 -0
- package/dist/client/assets/chunk-CIAEETIT-CYhfoCeN.js +1 -0
- package/dist/client/assets/chunk-EDXVE4YY-C5ovJLc0.js +1 -0
- package/dist/client/assets/chunk-ENJZ2VHE-HOhYaeGr.js +10 -0
- package/dist/client/assets/chunk-FMBD7UC4-BLCiKcAQ.js +15 -0
- package/dist/client/assets/chunk-FOC6F5B3-B6GtY2ek.js +1 -0
- package/dist/client/assets/chunk-ICPOFSXX-DPoIZoC5.js +122 -0
- package/dist/client/assets/chunk-K5T4RW27-BsKN63rv.js +94 -0
- package/dist/client/assets/chunk-KGLVRYIC-BUGn9uuY.js +1 -0
- package/dist/client/assets/chunk-LIHQZDEY-DhaZyo03.js +1 -0
- package/dist/client/assets/chunk-ORNJ4GCN-DlpeeJyi.js +1 -0
- package/dist/client/assets/chunk-OYMX7WX6-Dc9q7aYA.js +231 -0
- package/dist/client/assets/chunk-QZHKN3VN-BEdrPoSb.js +1 -0
- package/dist/client/assets/chunk-U2HBQHQK-CIB3Bjjd.js +70 -0
- package/dist/client/assets/chunk-X2U36JSP-CtB-o8Yp.js +1 -0
- package/dist/client/assets/chunk-XPW4576I-C6iHhX_8.js +32 -0
- package/dist/client/assets/chunk-YZCP3GAM-CTmKr6ZH.js +1 -0
- package/dist/client/assets/chunk-ZZ45TVLE-BgU8A2RF.js +1 -0
- package/dist/client/assets/classDiagram-6PBFFD2Q-Bqk5e679.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-6pSaZOkC.js +1 -0
- package/dist/client/assets/client-BrKWI4CM.js +1 -0
- package/dist/client/assets/client-CGgNRU9w.js +1 -0
- package/dist/client/assets/client-DMSLRzg9.js +6 -0
- package/dist/client/assets/clone-DWcL7whJ.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-CrV5wsV_.js +1 -0
- package/dist/client/assets/cytoscape.esm--aLzKuep.js +321 -0
- package/dist/client/assets/dagre-CuRxWcrj.js +1 -0
- package/dist/client/assets/dagre-KV5264BT-BIDiVnkA.js +4 -0
- package/dist/client/assets/defaultLocale-CRZydyG6.js +1 -0
- package/dist/client/assets/diagram-5BDNPKRD-i1kjKRCB.js +10 -0
- package/dist/client/assets/diagram-G4DWMVQ6-9ZSLuhbl.js +24 -0
- package/dist/client/assets/diagram-MMDJMWI5-B4_CUjgv.js +43 -0
- package/dist/client/assets/diagram-TYMM5635-Ct5eTGS8.js +24 -0
- package/dist/client/assets/dist-CuB4kiSK.js +1 -0
- package/dist/client/assets/erDiagram-SMLLAGMA-Cy38ercc.js +85 -0
- package/dist/client/assets/flowDiagram-DWJPFMVM-CZKuYl0V.js +162 -0
- package/dist/client/assets/ganttDiagram-T4ZO3ILL-DLPjCh7a.js +292 -0
- package/dist/client/assets/gitGraph-7Q5UKJZL-DqbrtEp9.js +1 -0
- package/dist/client/assets/gitGraphDiagram-UUTBAWPF-BoRBkDhQ.js +106 -0
- package/dist/client/assets/graphlib-BcQ6qlQh.js +1 -0
- package/dist/client/assets/highlighted-body-OFNGDK62-BEpBVDTX.js +1 -0
- package/dist/client/assets/index-CetCiuqP.js +105 -0
- package/dist/client/assets/index-N29Mip7A.css +1 -0
- package/dist/client/assets/info-OMHHGYJF-D98DRBJX.js +1 -0
- package/dist/client/assets/infoDiagram-42DDH7IO-BAcdTWbt.js +2 -0
- package/dist/client/assets/init-B8gtcn7T.js +1 -0
- package/dist/client/assets/isArrayLikeObject-D8SJFmkN.js +1 -0
- package/dist/client/assets/isEmpty-BF3YX5Jk.js +1 -0
- package/dist/client/assets/ishikawaDiagram-UXIWVN3A-Ynu2VKdC.js +70 -0
- package/dist/client/assets/journeyDiagram-VCZTEJTY-BjfhQaN3.js +139 -0
- package/dist/client/assets/jsx-runtime-CyI9ICYU.js +1 -0
- package/dist/client/assets/kanban-definition-6JOO6SKY-JLXH9zUJ.js +89 -0
- package/dist/client/assets/katex-B94qP8b6.js +265 -0
- package/dist/client/assets/lib--QVjyxmL.js +29 -0
- package/dist/client/assets/lib-B6rgJiZ9.js +1 -0
- package/dist/client/assets/line-DCrYfLBn.js +1 -0
- package/dist/client/assets/linear-_4upLmeo.js +1 -0
- package/dist/client/assets/mermaid-GHXKKRXX-rwJHYUmW.js +1 -0
- package/dist/client/assets/mermaid-parser.core-KZinfW8o.js +4 -0
- package/dist/client/assets/mermaid.core-QqY9gSNe.js +11 -0
- package/dist/client/assets/mindmap-definition-QFDTVHPH-TWgHDAzp.js +96 -0
- package/dist/client/assets/ordinal-CCj7PWgZ.js +1 -0
- package/dist/client/assets/packet-4T2RLAQJ-DEvfkn3F.js +1 -0
- package/dist/client/assets/path-DZF-JdEe.js +1 -0
- package/dist/client/assets/pie-ZZUOXDRM-72e6WVjb.js +1 -0
- package/dist/client/assets/pieDiagram-DEJITSTG-Cl8PCsoj.js +30 -0
- package/dist/client/assets/preload-helper-rov5CBGT.js +1 -0
- package/dist/client/assets/pty-client-DZ27IS00.js +1 -0
- package/dist/client/assets/ptyInstancesStore-D9ag7SYd.js +1 -0
- package/dist/client/assets/quadrantDiagram-34T5L4WZ-CHyVGp9E.js +7 -0
- package/dist/client/assets/radar-PYXPWWZC-Cp7xd_EY.js +1 -0
- package/dist/client/assets/react-CClhXMB2.js +1 -0
- package/dist/client/assets/react-Dd6D81m0.js +1 -0
- package/dist/client/assets/react-dom--G6_6fQ_.js +1 -0
- package/dist/client/assets/requirementDiagram-MS252O5E-DaanG2iM.js +84 -0
- package/dist/client/assets/rough.esm-BsmKo2S5.js +1 -0
- package/dist/client/assets/sankeyDiagram-XADWPNL6-B_fhLY36.js +10 -0
- package/dist/client/assets/sequenceDiagram-FGHM5R23-C5FNrveI.js +157 -0
- package/dist/client/assets/src-DeTlMJAU.js +1 -0
- package/dist/client/assets/stateDiagram-FHFEXIEX-nTTcdjjQ.js +1 -0
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Dw0632j_.js +1 -0
- package/dist/client/assets/timeline-definition-GMOUNBTQ-DkQV1yP8.js +120 -0
- package/dist/client/assets/treeView-SZITEDCU-ZLIgC7_K.js +1 -0
- package/dist/client/assets/treemap-W4RFUUIX-BqaMbB6N.js +1 -0
- package/dist/client/assets/uiIdentityOverlay-Ba7GNj7m.js +1 -0
- package/dist/client/assets/vennDiagram-DHZGUBPP-DbZ2xgs6.js +34 -0
- package/dist/client/assets/wardley-RL74JXVD-DXQS8zf4.js +1 -0
- package/dist/client/assets/wardleyDiagram-NUSXRM2D-BzCJ6MAu.js +20 -0
- package/dist/client/assets/xychartDiagram-5P7HB3ND-BSlFecop.js +7 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/favicon.svg +15 -0
- package/dist/client/fonts/body-medium.woff2 +0 -0
- package/dist/client/fonts/body-regular-italic.woff2 +0 -0
- package/dist/client/fonts/body-regular.woff2 +0 -0
- package/dist/client/fonts/body-semibold.woff2 +0 -0
- package/dist/client/index.html +31 -0
- package/dist/client/manifest.webmanifest +12 -0
- package/dist/client/sw.js +32 -0
- package/package.json +122 -0
- package/src/nats/auth-callout/callout-config.test.ts +93 -0
- package/src/nats/auth-callout/callout-config.ts +109 -0
- package/src/nats/auth-callout/callout.integration.test.ts +332 -0
- package/src/nats/auth-callout/keys.ts +103 -0
- package/src/nats/auth-callout/responder.ts +241 -0
- package/src/nats/auth-callout/scope-policy.test.ts +159 -0
- package/src/nats/auth-callout/scope-policy.ts +210 -0
- package/src/nats/auth-callout/token.test.ts +163 -0
- package/src/nats/auth-callout/token.ts +157 -0
- package/src/nats/nats-daemon-callout.ts +194 -0
- package/src/nats/nats-daemon.test.ts +77 -0
- package/src/nats/nats-daemon.ts +50 -0
- package/src/nats/nats-token.test.ts +61 -0
- package/src/nats/nats-token.ts +59 -0
- package/src/runner/coordination-mcp-integration.test.ts +134 -0
- package/src/runner/nats-coordination-client.test.ts +49 -0
- package/src/runner/nats-coordination-client.ts +94 -0
- package/src/runner/runner-agent.test.ts +469 -0
- package/src/runner/runner-agent.ts +453 -0
- package/src/runner/runner-credential.test.ts +93 -0
- package/src/runner/runner-credential.ts +82 -0
- package/src/runner/runner-nats.test.ts +495 -0
- package/src/runner/runner-nats.ts +323 -0
- package/src/runner/runner-pair.test.ts +107 -0
- package/src/runner/runner-pair.ts +81 -0
- package/src/runner/runner.test.ts +135 -0
- package/src/runner/runner.ts +212 -0
- package/src/runner/turn-factories.test.ts +97 -0
- package/src/runner/turn-factories.ts +475 -0
- package/src/server/agent-config-journey.test.ts +106 -0
- package/src/server/agent.ts +8 -0
- package/src/server/auto-continue/auth-error-detector.ts +66 -0
- package/src/server/auto-continue/limit-detector.ts +194 -0
- package/src/server/bm25.test.ts +92 -0
- package/src/server/bm25.ts +101 -0
- package/src/server/chat-events-jetstream.test.ts +135 -0
- package/src/server/claude-harness.ts +360 -0
- package/src/server/claude-pty/agent-normalizers.ts +309 -0
- package/src/server/claude-pty/auth.test.ts +38 -0
- package/src/server/claude-pty/auth.ts +32 -0
- package/src/server/claude-pty/claude-session-registry.adapter.ts +81 -0
- package/src/server/claude-pty/claude-session-registry.test.ts +149 -0
- package/src/server/claude-pty/driver.test.ts +902 -0
- package/src/server/claude-pty/driver.ts +807 -0
- package/src/server/claude-pty/jsonl-path.adapter.ts +57 -0
- package/src/server/claude-pty/jsonl-path.test.ts +114 -0
- package/src/server/claude-pty/jsonl-to-event.test.ts +241 -0
- package/src/server/claude-pty/jsonl-to-event.ts +174 -0
- package/src/server/claude-pty/output-ring.test.ts +35 -0
- package/src/server/claude-pty/output-ring.ts +25 -0
- package/src/server/claude-pty/parity-matrix.test.ts +227 -0
- package/src/server/claude-pty/pid-registry.adapter.ts +135 -0
- package/src/server/claude-pty/pid-registry.test.ts +122 -0
- package/src/server/claude-pty/preflight/binary-fingerprint.adapter.ts +20 -0
- package/src/server/claude-pty/preflight/binary-fingerprint.test.ts +32 -0
- package/src/server/claude-pty/pty-instance-registry.test.ts +177 -0
- package/src/server/claude-pty/pty-instance-registry.ts +166 -0
- package/src/server/claude-pty/pty-memory-sampler.adapter.test.ts +103 -0
- package/src/server/claude-pty/pty-memory-sampler.adapter.ts +85 -0
- package/src/server/claude-pty/pty-process.adapter.ts +66 -0
- package/src/server/claude-pty/pty-process.test.ts +49 -0
- package/src/server/claude-pty/resolve-binary.adapter.ts +106 -0
- package/src/server/claude-pty/resolve-binary.test.ts +118 -0
- package/src/server/claude-pty/runtime-dir.adapter.ts +19 -0
- package/src/server/claude-pty/settings-writer.adapter.ts +27 -0
- package/src/server/claude-pty/settings-writer.test.ts +22 -0
- package/src/server/claude-pty/smoke-test-io.adapter.ts +28 -0
- package/src/server/claude-pty/smoke-test.test.ts +191 -0
- package/src/server/claude-pty/smoke-test.ts +185 -0
- package/src/server/claude-pty/subagent-orchestrator.ts +887 -0
- package/src/server/claude-pty/tool-callback.ts +274 -0
- package/src/server/claude-pty/tui-control.test.ts +272 -0
- package/src/server/claude-pty/tui-control.ts +182 -0
- package/src/server/claude-pty/tui-source.adapter.test.ts +360 -0
- package/src/server/claude-pty/tui-source.adapter.ts +343 -0
- package/src/server/claude-pty/tunnel-gateway.ts +12 -0
- package/src/server/claude-pty-mcp/canonical-args.ts +15 -0
- package/src/server/claude-pty-mcp/fs-stat.adapter.ts +8 -0
- package/src/server/claude-pty-mcp/history-primer.ts +90 -0
- package/src/server/claude-pty-mcp/http-server.adapter.ts +33 -0
- package/src/server/claude-pty-mcp/mcp-http.ts +177 -0
- package/src/server/claude-pty-mcp/mcp.ts +412 -0
- package/src/server/claude-pty-mcp/mention-parser.ts +25 -0
- package/src/server/claude-pty-mcp/paths.ts +24 -0
- package/src/server/claude-pty-mcp/permission-gate.ts +243 -0
- package/src/server/claude-pty-mcp/terminal-pid-registry.adapter.ts +107 -0
- package/src/server/claude-pty-mcp/tools/ask-user-question.test.ts +119 -0
- package/src/server/claude-pty-mcp/tools/ask-user-question.ts +61 -0
- package/src/server/claude-pty-mcp/tools/bash.adapter.ts +76 -0
- package/src/server/claude-pty-mcp/tools/bash.test.ts +56 -0
- package/src/server/claude-pty-mcp/tools/delegate-subagent.test.ts +155 -0
- package/src/server/claude-pty-mcp/tools/delegate-subagent.ts +111 -0
- package/src/server/claude-pty-mcp/tools/edit.adapter.ts +95 -0
- package/src/server/claude-pty-mcp/tools/edit.test.ts +93 -0
- package/src/server/claude-pty-mcp/tools/exit-plan-mode.test.ts +61 -0
- package/src/server/claude-pty-mcp/tools/exit-plan-mode.ts +50 -0
- package/src/server/claude-pty-mcp/tools/glob.adapter.ts +86 -0
- package/src/server/claude-pty-mcp/tools/glob.test.ts +61 -0
- package/src/server/claude-pty-mcp/tools/grep.adapter.ts +126 -0
- package/src/server/claude-pty-mcp/tools/grep.test.ts +62 -0
- package/src/server/claude-pty-mcp/tools/read.adapter.ts +58 -0
- package/src/server/claude-pty-mcp/tools/read.test.ts +62 -0
- package/src/server/claude-pty-mcp/tools/tool-callback-shim.ts +42 -0
- package/src/server/claude-pty-mcp/tools/webfetch.test.ts +81 -0
- package/src/server/claude-pty-mcp/tools/webfetch.ts +82 -0
- package/src/server/claude-pty-mcp/tools/websearch.test.ts +40 -0
- package/src/server/claude-pty-mcp/tools/websearch.ts +42 -0
- package/src/server/claude-pty-mcp/tools/write.adapter.ts +60 -0
- package/src/server/claude-pty-mcp/tools/write.test.ts +52 -0
- package/src/server/claude-pty-mcp/uploads.adapter.ts +98 -0
- package/src/server/claude-pty-mcp/uploads.ts +38 -0
- package/src/server/claude-turn.test.ts +176 -0
- package/src/server/cli-runtime.test.ts +456 -0
- package/src/server/cli-runtime.ts +374 -0
- package/src/server/cli-supervisor.ts +81 -0
- package/src/server/cli.ts +78 -0
- package/src/server/client-log-forwarder.test.ts +74 -0
- package/src/server/client-log-forwarder.ts +75 -0
- package/src/server/codex-app-server-protocol.ts +449 -0
- package/src/server/codex-app-server.test.ts +2990 -0
- package/src/server/codex-app-server.ts +1713 -0
- package/src/server/coordination-integration.test.ts +63 -0
- package/src/server/coordination-mcp.test.ts +149 -0
- package/src/server/coordination-mcp.ts +197 -0
- package/src/server/delegation-coordinator.test.ts +675 -0
- package/src/server/delegation-coordinator.ts +454 -0
- package/src/server/discovery.test.ts +211 -0
- package/src/server/discovery.ts +301 -0
- package/src/server/event-store-agent-config.test.ts +124 -0
- package/src/server/event-store-coordination.test.ts +149 -0
- package/src/server/event-store-profile.test.ts +132 -0
- package/src/server/event-store-repo.test.ts +154 -0
- package/src/server/event-store-runner-team.test.ts +104 -0
- package/src/server/event-store.test.ts +342 -0
- package/src/server/event-store.ts +2208 -0
- package/src/server/events.ts +379 -0
- package/src/server/extension-router.test.ts +183 -0
- package/src/server/extension-router.ts +114 -0
- package/src/server/extensions/agents/server.test.ts +191 -0
- package/src/server/extensions/agents/server.ts +108 -0
- package/src/server/extensions/c3/server.test.ts +284 -0
- package/src/server/extensions/c3/server.ts +212 -0
- package/src/server/extensions/code/server.test.ts +200 -0
- package/src/server/extensions/code/server.ts +150 -0
- package/src/server/extensions.config.ts +10 -0
- package/src/server/external-open.ts +69 -0
- package/src/server/generate-fork-context.ts +58 -0
- package/src/server/generate-merge-context.test.ts +290 -0
- package/src/server/generate-merge-context.ts +141 -0
- package/src/server/generate-title.ts +36 -0
- package/src/server/git-clone-policy.test.ts +138 -0
- package/src/server/git-clone-policy.ts +27 -0
- package/src/server/harness-types.ts +1 -0
- package/src/server/journey-verification.test.ts +640 -0
- package/src/server/journey-verification.ts +195 -0
- package/src/server/machine-name.ts +22 -0
- package/src/server/nats-auth.test.ts +92 -0
- package/src/server/nats-auth.ts +6 -0
- package/src/server/nats-bind-guard.test.ts +71 -0
- package/src/server/nats-bind-guard.ts +42 -0
- package/src/server/nats-bridge.test.ts +141 -0
- package/src/server/nats-bridge.ts +111 -0
- package/src/server/nats-connector.test.ts +56 -0
- package/src/server/nats-connector.ts +71 -0
- package/src/server/nats-daemon-manager.test.ts +76 -0
- package/src/server/nats-daemon-manager.ts +174 -0
- package/src/server/nats-publisher.test.ts +356 -0
- package/src/server/nats-publisher.ts +271 -0
- package/src/server/nats-responders.test.ts +1018 -0
- package/src/server/nats-responders.ts +925 -0
- package/src/server/nats-streams.test.ts +112 -0
- package/src/server/nats-streams.ts +129 -0
- package/src/server/oauth-pool/oauth-responders.ts +185 -0
- package/src/server/oauth-pool/oauth-settings-store.ts +173 -0
- package/src/server/oauth-pool/oauth-token-pool.ts +303 -0
- package/src/server/orchestration.test.ts +1063 -0
- package/src/server/orchestration.ts +716 -0
- package/src/server/pairing-endpoints.test.ts +171 -0
- package/src/server/pairing-store.test.ts +154 -0
- package/src/server/pairing-store.ts +124 -0
- package/src/server/paths.ts +27 -0
- package/src/server/pr3-liveness.test.ts +252 -0
- package/src/server/process-utils.ts +10 -0
- package/src/server/project-cli.ts +180 -0
- package/src/server/provider-catalog.test.ts +177 -0
- package/src/server/provider-catalog.ts +146 -0
- package/src/server/pty-responders.ts +345 -0
- package/src/server/push-notifications.test.ts +234 -0
- package/src/server/push-notifications.ts +188 -0
- package/src/server/quick-response.test.ts +196 -0
- package/src/server/quick-response.ts +154 -0
- package/src/server/read-models-agent-config.test.ts +59 -0
- package/src/server/read-models-coordination.test.ts +69 -0
- package/src/server/read-models.test.ts +332 -0
- package/src/server/read-models.ts +258 -0
- package/src/server/repo-journey.test.ts +96 -0
- package/src/server/repo-manager.test.ts +118 -0
- package/src/server/repo-manager.ts +97 -0
- package/src/server/repo-status.test.ts +54 -0
- package/src/server/repo-status.ts +82 -0
- package/src/server/restart.test.ts +27 -0
- package/src/server/restart.ts +30 -0
- package/src/server/runner-incompatible-gate.test.ts +383 -0
- package/src/server/runner-manager.test.ts +72 -0
- package/src/server/runner-manager.ts +312 -0
- package/src/server/runner-pairing-urls.test.ts +59 -0
- package/src/server/runner-pairing-urls.ts +67 -0
- package/src/server/runner-proxy.test.ts +456 -0
- package/src/server/runner-proxy.ts +494 -0
- package/src/server/runner-router.test.ts +429 -0
- package/src/server/runner-router.ts +212 -0
- package/src/server/runner-routing.test.ts +584 -0
- package/src/server/runtime-registry.test.ts +436 -0
- package/src/server/runtime-registry.ts +307 -0
- package/src/server/sandbox-health.test.ts +127 -0
- package/src/server/sandbox-health.ts +94 -0
- package/src/server/sandbox-journey.test.ts +232 -0
- package/src/server/sandbox-manager.test.ts +146 -0
- package/src/server/sandbox-manager.ts +159 -0
- package/src/server/server.test.ts +287 -0
- package/src/server/server.ts +1108 -0
- package/src/server/session-discovery.test.ts +129 -0
- package/src/server/session-discovery.ts +475 -0
- package/src/server/session-index.test.ts +362 -0
- package/src/server/session-index.ts +119 -0
- package/src/server/session-seed.ts +288 -0
- package/src/server/share.test.ts +108 -0
- package/src/server/share.ts +113 -0
- package/src/server/skill-discovery.test.ts +201 -0
- package/src/server/skill-discovery.ts +77 -0
- package/src/server/storage/test-helpers.ts +67 -0
- package/src/server/terminal-manager.test.ts +309 -0
- package/src/server/terminal-manager.ts +354 -0
- package/src/server/transcript-consumer.test.ts +339 -0
- package/src/server/transcript-consumer.ts +162 -0
- package/src/server/transcript-search.test.ts +193 -0
- package/src/server/transcript-search.ts +83 -0
- package/src/server/transcript-utils.ts +52 -0
- package/src/server/update-manager.test.ts +107 -0
- package/src/server/update-manager.ts +230 -0
- package/src/server/workflow-engine.test.ts +251 -0
- package/src/server/workflow-engine.ts +169 -0
- package/src/server/workflow-mcp.ts +49 -0
- package/src/server/workflow-store.test.ts +155 -0
- package/src/server/workflow-store.ts +139 -0
- package/src/server/workspace-agent-integration.test.ts +167 -0
- package/src/server/workspace-agent-routes.test.ts +127 -0
- package/src/server/workspace-agent-routes.ts +89 -0
- package/src/server/workspace-agent.test.ts +103 -0
- package/src/server/workspace-agent.ts +102 -0
- package/src/server/workspace-cli.test.ts +79 -0
- package/src/server/workspace-config-manager.test.ts +109 -0
- package/src/server/workspace-config-manager.ts +83 -0
- package/src/server/workspace-directory-policy.test.ts +109 -0
- package/src/server/workspace-directory-policy.ts +56 -0
- package/src/shared/agent-config-types.ts +25 -0
- package/src/shared/branding.test.ts +42 -0
- package/src/shared/branding.ts +54 -0
- package/src/shared/compression.test.ts +85 -0
- package/src/shared/compression.ts +42 -0
- package/src/shared/coordination-store.test.ts +24 -0
- package/src/shared/coordination-store.ts +26 -0
- package/src/shared/dev-ports.test.ts +84 -0
- package/src/shared/dev-ports.ts +100 -0
- package/src/shared/extension-types.ts +45 -0
- package/src/shared/fork-presets.ts +54 -0
- package/src/shared/harness-types.test.ts +15 -0
- package/src/shared/harness-types.ts +21 -0
- package/src/shared/log-sink.test.ts +112 -0
- package/src/shared/log-sink.ts +185 -0
- package/src/shared/mention-pattern.ts +7 -0
- package/src/shared/merge-presets.ts +41 -0
- package/src/shared/nats-subjects.test.ts +61 -0
- package/src/shared/nats-subjects.ts +131 -0
- package/src/shared/permission-policy.ts +136 -0
- package/src/shared/ports.ts +3 -0
- package/src/shared/preset-types.ts +15 -0
- package/src/shared/profile-types.ts +49 -0
- package/src/shared/projectFileUrl.ts +36 -0
- package/src/shared/protocol.ts +153 -0
- package/src/shared/pty-instance.ts +43 -0
- package/src/shared/puggy/diagnostics/result.ts +18 -0
- package/src/shared/puggy/expressions/evaluate.ts +292 -0
- package/src/shared/puggy/index.test.ts +101 -0
- package/src/shared/puggy/index.ts +69 -0
- package/src/shared/puggy/parser/ast.ts +110 -0
- package/src/shared/puggy/parser/parser.ts +624 -0
- package/src/shared/puggy/renderer/html.ts +447 -0
- package/src/shared/runner-protocol.test.ts +277 -0
- package/src/shared/runner-protocol.ts +210 -0
- package/src/shared/runner-team-types.ts +73 -0
- package/src/shared/runtime-types.ts +48 -0
- package/src/shared/sandbox-types.ts +53 -0
- package/src/shared/tailwind-build.test.ts +12 -0
- package/src/shared/tinkaria-system-prompt.ts +101 -0
- package/src/shared/tools.test.ts +335 -0
- package/src/shared/tools.ts +397 -0
- package/src/shared/transcript-entries.ts +27 -0
- package/src/shared/transcript-render.test.ts +225 -0
- package/src/shared/transcript-render.ts +467 -0
- package/src/shared/types.ts +1031 -0
- package/src/shared/vite-config.test.ts +47 -0
- package/src/shared/web-context.test.ts +110 -0
- package/src/shared/web-context.ts +76 -0
- package/src/shared/workflow-types.ts +51 -0
- package/src/shared/workspace-types.ts +100 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { afterEach, describe, test, expect, mock } from "bun:test"
|
|
2
|
+
import { NatsServer } from "@lagz0ne/nats-embedded"
|
|
3
|
+
import { connect, type NatsConnection, type Subscription } from "@nats-io/transport-node"
|
|
4
|
+
import { createNatsPublisher, type CreateNatsPublisherArgs } from "./nats-publisher"
|
|
5
|
+
import { snapshotSubject, snapshotKvKey, KV_BUCKET } from "../shared/nats-subjects"
|
|
6
|
+
import { createEmptyState } from "./events"
|
|
7
|
+
import { Kvm } from "@nats-io/kv"
|
|
8
|
+
import type { SubscriptionTopic } from "../shared/protocol"
|
|
9
|
+
|
|
10
|
+
let server: NatsServer | null = null
|
|
11
|
+
let nc: NatsConnection | null = null
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
if (nc) {
|
|
15
|
+
await nc.drain()
|
|
16
|
+
nc = null
|
|
17
|
+
}
|
|
18
|
+
if (server) {
|
|
19
|
+
await server.stop()
|
|
20
|
+
server = null
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
function mockArgs(overrides: Partial<CreateNatsPublisherArgs> = {}): CreateNatsPublisherArgs {
|
|
25
|
+
return {
|
|
26
|
+
nc: nc!,
|
|
27
|
+
store: {
|
|
28
|
+
state: createEmptyState(),
|
|
29
|
+
getMessages: () => [],
|
|
30
|
+
} as unknown as CreateNatsPublisherArgs["store"],
|
|
31
|
+
agent: {
|
|
32
|
+
getActiveStatuses: () => new Map(),
|
|
33
|
+
} as unknown as CreateNatsPublisherArgs["agent"],
|
|
34
|
+
terminals: {
|
|
35
|
+
getSnapshot: () => null,
|
|
36
|
+
onEvent: () => () => {},
|
|
37
|
+
} as unknown as CreateNatsPublisherArgs["terminals"],
|
|
38
|
+
refreshDiscovery: async () => [],
|
|
39
|
+
getDiscoveredProjects: () => [],
|
|
40
|
+
machineDisplayName: "test-machine",
|
|
41
|
+
updateManager: null,
|
|
42
|
+
...overrides,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function collectMessages(sub: Subscription, count: number, timeoutMs = 500): Promise<string[]> {
|
|
47
|
+
const messages: string[] = []
|
|
48
|
+
const decoder = new TextDecoder()
|
|
49
|
+
const timeout = setTimeout(() => sub.unsubscribe(), timeoutMs)
|
|
50
|
+
|
|
51
|
+
for await (const msg of sub) {
|
|
52
|
+
messages.push(decoder.decode(msg.data))
|
|
53
|
+
if (messages.length >= count) {
|
|
54
|
+
clearTimeout(timeout)
|
|
55
|
+
sub.unsubscribe()
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
clearTimeout(timeout)
|
|
60
|
+
return messages
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe("createNatsPublisher", () => {
|
|
64
|
+
test("publishes snapshot to NATS subject", async () => {
|
|
65
|
+
server = await NatsServer.start({ jetstream: true })
|
|
66
|
+
nc = await connect({ servers: server.url })
|
|
67
|
+
|
|
68
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
69
|
+
const topic: SubscriptionTopic = { type: "sidebar" }
|
|
70
|
+
const sub = nc.subscribe(snapshotSubject(topic))
|
|
71
|
+
|
|
72
|
+
publisher.addSubscription("sub-1", topic)
|
|
73
|
+
publisher.getSnapshot(topic)
|
|
74
|
+
|
|
75
|
+
const msgs = await collectMessages(sub, 1)
|
|
76
|
+
expect(msgs.length).toBe(1)
|
|
77
|
+
|
|
78
|
+
const data = JSON.parse(msgs[0])
|
|
79
|
+
expect(data).toHaveProperty("workspaceGroups")
|
|
80
|
+
|
|
81
|
+
publisher.dispose()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("runner-teams snapshot derives members + runner labels (US-RTN)", async () => {
|
|
85
|
+
server = await NatsServer.start({ jetstream: true })
|
|
86
|
+
nc = await connect({ servers: server.url })
|
|
87
|
+
|
|
88
|
+
const args = mockArgs()
|
|
89
|
+
args.store.state.teamMembers.set("m1", { id: "m1", name: "Alice" })
|
|
90
|
+
args.store.state.runnerLabels.set("runner-1", { runnerId: "runner-1", name: "Studio Mac", memberId: "m1", updatedAt: 1 })
|
|
91
|
+
|
|
92
|
+
const publisher = await createNatsPublisher(args)
|
|
93
|
+
const topic: SubscriptionTopic = { type: "runner-teams" }
|
|
94
|
+
const sub = nc.subscribe(snapshotSubject(topic))
|
|
95
|
+
|
|
96
|
+
publisher.addSubscription("sub-rt", topic)
|
|
97
|
+
publisher.getSnapshot(topic)
|
|
98
|
+
|
|
99
|
+
const msgs = await collectMessages(sub, 1)
|
|
100
|
+
expect(msgs.length).toBe(1)
|
|
101
|
+
const data = JSON.parse(msgs[0]) as { members: unknown[]; runners: unknown[] }
|
|
102
|
+
expect(data.members).toEqual([{ id: "m1", name: "Alice" }])
|
|
103
|
+
expect(data.runners).toEqual([{ runnerId: "runner-1", name: "Studio Mac", memberId: "m1", updatedAt: 1 }])
|
|
104
|
+
|
|
105
|
+
publisher.dispose()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test("dedup skips identical publishes", async () => {
|
|
109
|
+
server = await NatsServer.start({ jetstream: true })
|
|
110
|
+
nc = await connect({ servers: server.url })
|
|
111
|
+
|
|
112
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
113
|
+
const topic: SubscriptionTopic = { type: "sidebar" }
|
|
114
|
+
const sub = nc.subscribe(snapshotSubject(topic))
|
|
115
|
+
|
|
116
|
+
// First publish delivers
|
|
117
|
+
publisher.getSnapshot(topic)
|
|
118
|
+
// Second publish with same data is skipped
|
|
119
|
+
publisher.getSnapshot(topic)
|
|
120
|
+
await nc.flush()
|
|
121
|
+
|
|
122
|
+
const msgs = await collectMessages(sub, 2, 200)
|
|
123
|
+
// Only 1 message should arrive due to dedup
|
|
124
|
+
expect(msgs.length).toBe(1)
|
|
125
|
+
|
|
126
|
+
publisher.dispose()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test("writes snapshot to KV bucket", async () => {
|
|
130
|
+
server = await NatsServer.start({ jetstream: true })
|
|
131
|
+
nc = await connect({ servers: server.url })
|
|
132
|
+
|
|
133
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
134
|
+
const topic: SubscriptionTopic = { type: "sidebar" }
|
|
135
|
+
|
|
136
|
+
publisher.getSnapshot(topic)
|
|
137
|
+
await nc.flush()
|
|
138
|
+
// Give async KV put time to complete
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
140
|
+
|
|
141
|
+
const kvm = new Kvm(nc)
|
|
142
|
+
const kv = await kvm.create(KV_BUCKET)
|
|
143
|
+
const entry = await kv.get(snapshotKvKey(topic))
|
|
144
|
+
expect(entry).not.toBeNull()
|
|
145
|
+
|
|
146
|
+
const data = entry!.json() as Record<string, unknown>
|
|
147
|
+
expect(data).toHaveProperty("workspaceGroups")
|
|
148
|
+
|
|
149
|
+
publisher.dispose()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test("broadcastSnapshots publishes for all active subscriptions", async () => {
|
|
153
|
+
server = await NatsServer.start({ jetstream: true })
|
|
154
|
+
nc = await connect({ servers: server.url })
|
|
155
|
+
|
|
156
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
157
|
+
|
|
158
|
+
publisher.addSubscription("sub-1", { type: "sidebar" })
|
|
159
|
+
publisher.addSubscription("sub-2", { type: "local-workspaces" })
|
|
160
|
+
|
|
161
|
+
const sidebarSub = nc.subscribe(snapshotSubject({ type: "sidebar" }))
|
|
162
|
+
const localProjectsSub = nc.subscribe(snapshotSubject({ type: "local-workspaces" }))
|
|
163
|
+
|
|
164
|
+
publisher.broadcastSnapshots()
|
|
165
|
+
|
|
166
|
+
const [sidebarMsgs, localProjectsMsgs] = await Promise.all([
|
|
167
|
+
collectMessages(sidebarSub, 1),
|
|
168
|
+
collectMessages(localProjectsSub, 1),
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
expect(sidebarMsgs.length).toBe(1)
|
|
172
|
+
expect(localProjectsMsgs.length).toBe(1)
|
|
173
|
+
|
|
174
|
+
publisher.dispose()
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test("broadcastSnapshots deduplicates same topic from multiple subscriptions", async () => {
|
|
178
|
+
server = await NatsServer.start({ jetstream: true })
|
|
179
|
+
nc = await connect({ servers: server.url })
|
|
180
|
+
|
|
181
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
182
|
+
|
|
183
|
+
// Two subscriptions for same topic
|
|
184
|
+
publisher.addSubscription("sub-1", { type: "sidebar" })
|
|
185
|
+
publisher.addSubscription("sub-2", { type: "sidebar" })
|
|
186
|
+
|
|
187
|
+
const sub = nc.subscribe(snapshotSubject({ type: "sidebar" }))
|
|
188
|
+
|
|
189
|
+
publisher.broadcastSnapshots()
|
|
190
|
+
await nc.flush()
|
|
191
|
+
|
|
192
|
+
const msgs = await collectMessages(sub, 2, 200)
|
|
193
|
+
// Only 1 publish even though 2 subscriptions watch sidebar
|
|
194
|
+
expect(msgs.length).toBe(1)
|
|
195
|
+
|
|
196
|
+
publisher.dispose()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test("removeSubscription prunes dedup cache when last subscriber leaves", async () => {
|
|
200
|
+
server = await NatsServer.start({ jetstream: true })
|
|
201
|
+
nc = await connect({ servers: server.url })
|
|
202
|
+
|
|
203
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
204
|
+
const topic: SubscriptionTopic = { type: "sidebar" }
|
|
205
|
+
|
|
206
|
+
// Subscribe and get initial snapshot (seeds dedup cache)
|
|
207
|
+
publisher.addSubscription("sub-1", topic)
|
|
208
|
+
publisher.getSnapshot(topic)
|
|
209
|
+
|
|
210
|
+
const sub = nc.subscribe(snapshotSubject(topic))
|
|
211
|
+
|
|
212
|
+
// Remove subscription → dedup cache pruned
|
|
213
|
+
publisher.removeSubscription("sub-1")
|
|
214
|
+
|
|
215
|
+
// Re-subscribe → getSnapshot should publish again (cache was pruned)
|
|
216
|
+
publisher.addSubscription("sub-2", topic)
|
|
217
|
+
publisher.getSnapshot(topic)
|
|
218
|
+
|
|
219
|
+
const msgs = await collectMessages(sub, 1)
|
|
220
|
+
expect(msgs.length).toBe(1)
|
|
221
|
+
|
|
222
|
+
publisher.dispose()
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test("terminal events forwarded via JetStream", async () => {
|
|
226
|
+
server = await NatsServer.start({ jetstream: true })
|
|
227
|
+
nc = await connect({ servers: server.url })
|
|
228
|
+
|
|
229
|
+
const eventCallbacks: Array<(event: unknown) => void> = []
|
|
230
|
+
const terminals = {
|
|
231
|
+
getSnapshot: () => null,
|
|
232
|
+
onEvent: (cb: (event: unknown) => void) => {
|
|
233
|
+
eventCallbacks.push(cb)
|
|
234
|
+
return () => {}
|
|
235
|
+
},
|
|
236
|
+
} as unknown as CreateNatsPublisherArgs["terminals"]
|
|
237
|
+
|
|
238
|
+
// Need the terminal events stream for JetStream publish
|
|
239
|
+
const { ensureTerminalEventsStream } = await import("./nats-streams")
|
|
240
|
+
await ensureTerminalEventsStream(nc)
|
|
241
|
+
|
|
242
|
+
const publisher = await createNatsPublisher(mockArgs({ terminals }))
|
|
243
|
+
|
|
244
|
+
const sub = nc.subscribe("runtime.evt.terminal.term-1")
|
|
245
|
+
|
|
246
|
+
// Simulate terminal event
|
|
247
|
+
for (const cb of eventCallbacks) {
|
|
248
|
+
cb({ type: "terminal.output", terminalId: "term-1", data: "hello" })
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const msgs = await collectMessages(sub, 1)
|
|
252
|
+
expect(msgs.length).toBe(1)
|
|
253
|
+
const event = JSON.parse(msgs[0])
|
|
254
|
+
expect(event.terminalId).toBe("term-1")
|
|
255
|
+
|
|
256
|
+
publisher.dispose()
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
test("failed publish does not poison dedup cache — retry delivers", async () => {
|
|
260
|
+
server = await NatsServer.start({ jetstream: true })
|
|
261
|
+
nc = await connect({ servers: server.url })
|
|
262
|
+
|
|
263
|
+
const publisher = await createNatsPublisher(mockArgs())
|
|
264
|
+
const topic: SubscriptionTopic = { type: "sidebar" }
|
|
265
|
+
const subject = snapshotSubject(topic)
|
|
266
|
+
|
|
267
|
+
// Monkey-patch nc.publish to throw only for the snapshot subject
|
|
268
|
+
const realPublish = nc.publish.bind(nc)
|
|
269
|
+
let shouldFail = true
|
|
270
|
+
nc.publish = (subj: string, ...rest: unknown[]) => {
|
|
271
|
+
if (subj === subject && shouldFail) {
|
|
272
|
+
shouldFail = false
|
|
273
|
+
throw new Error("simulated max_payload exceeded")
|
|
274
|
+
}
|
|
275
|
+
return (realPublish as Function)(subj, ...rest)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// First getSnapshot — snapshot publish fails, cache should NOT be poisoned
|
|
279
|
+
publisher.getSnapshot(topic)
|
|
280
|
+
|
|
281
|
+
// Restore real publish and subscribe to verify retry
|
|
282
|
+
nc.publish = realPublish
|
|
283
|
+
const sub = nc.subscribe(subject)
|
|
284
|
+
|
|
285
|
+
// Second getSnapshot with same data — should retry because cache was not poisoned
|
|
286
|
+
publisher.getSnapshot(topic)
|
|
287
|
+
|
|
288
|
+
const msgs = await collectMessages(sub, 1)
|
|
289
|
+
expect(msgs.length).toBe(1)
|
|
290
|
+
|
|
291
|
+
publisher.dispose()
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
test("dispose stops terminal event forwarding", async () => {
|
|
295
|
+
server = await NatsServer.start({ jetstream: true })
|
|
296
|
+
nc = await connect({ servers: server.url })
|
|
297
|
+
|
|
298
|
+
const disposeFn = mock(() => {})
|
|
299
|
+
const terminals = {
|
|
300
|
+
getSnapshot: () => null,
|
|
301
|
+
onEvent: () => disposeFn,
|
|
302
|
+
} as unknown as CreateNatsPublisherArgs["terminals"]
|
|
303
|
+
|
|
304
|
+
const publisher = await createNatsPublisher(mockArgs({ terminals }))
|
|
305
|
+
publisher.dispose()
|
|
306
|
+
|
|
307
|
+
expect(disposeFn).toHaveBeenCalledTimes(1)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
test("broadcastSnapshots publishes orchestration snapshots without throwing", async () => {
|
|
311
|
+
server = await NatsServer.start({ jetstream: true })
|
|
312
|
+
nc = await connect({ servers: server.url })
|
|
313
|
+
|
|
314
|
+
const publisher = await createNatsPublisher(
|
|
315
|
+
mockArgs({
|
|
316
|
+
orchestrator: {
|
|
317
|
+
getHierarchy: () => ({
|
|
318
|
+
children: [
|
|
319
|
+
{
|
|
320
|
+
chatId: "child-1",
|
|
321
|
+
provider: "claude",
|
|
322
|
+
model: "sonnet",
|
|
323
|
+
instruction: "audit the latest release candidate",
|
|
324
|
+
status: "running",
|
|
325
|
+
depth: 1,
|
|
326
|
+
spawnedAt: Date.now(),
|
|
327
|
+
lastStatusAt: Date.now(),
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
}),
|
|
331
|
+
pruneTombstones: () => {},
|
|
332
|
+
} as unknown as CreateNatsPublisherArgs["orchestrator"],
|
|
333
|
+
})
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
const topic: SubscriptionTopic = { type: "orchestration", chatId: "chat-parent" }
|
|
337
|
+
const sub = nc.subscribe(snapshotSubject(topic))
|
|
338
|
+
|
|
339
|
+
publisher.addSubscription("sub-orch", topic)
|
|
340
|
+
await expect(publisher.broadcastSnapshots()).resolves.toBeUndefined()
|
|
341
|
+
|
|
342
|
+
const msgs = await collectMessages(sub, 1)
|
|
343
|
+
expect(msgs.length).toBe(1)
|
|
344
|
+
expect(JSON.parse(msgs[0])).toEqual({
|
|
345
|
+
children: [
|
|
346
|
+
expect.objectContaining({
|
|
347
|
+
chatId: "child-1",
|
|
348
|
+
status: "running",
|
|
349
|
+
instruction: "audit the latest release candidate",
|
|
350
|
+
}),
|
|
351
|
+
],
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
publisher.dispose()
|
|
355
|
+
})
|
|
356
|
+
})
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import type { NatsConnection } from "@nats-io/transport-node"
|
|
2
|
+
import { jetstream } from "@nats-io/jetstream"
|
|
3
|
+
import { Kvm } from "@nats-io/kv"
|
|
4
|
+
import type { SubscriptionTopic } from "../shared/protocol"
|
|
5
|
+
import { snapshotSubject, snapshotKvKey, terminalEventSubject, chatMessageSubject, KV_BUCKET } from "../shared/nats-subjects"
|
|
6
|
+
import { LOG_PREFIX } from "../shared/branding"
|
|
7
|
+
import { compressPayload } from "../shared/compression"
|
|
8
|
+
import type { ChatMessageEvent, TranscriptEntry } from "../shared/types"
|
|
9
|
+
import type { SessionStatus } from "../shared/types"
|
|
10
|
+
import type { DiscoveredProject } from "./discovery"
|
|
11
|
+
import type { EventStore } from "./event-store"
|
|
12
|
+
import { deriveChatSnapshot, deriveLocalWorkspacesSnapshot, deriveWorkspaceCoordinationSnapshot, deriveSidebarData, deriveAgentConfigSnapshot, deriveRepoListSnapshot, deriveWorkflowRunsSnapshot, deriveSandboxSnapshot, deriveTranscriptRenderUnits, TRANSCRIPT_RENDER_WINDOW_SIZE } from "./read-models"
|
|
13
|
+
import type { TerminalManager } from "./terminal-manager"
|
|
14
|
+
import type { UpdateManager } from "./update-manager"
|
|
15
|
+
import type { SkillCache } from "./skill-discovery"
|
|
16
|
+
import type { SessionOrchestrator } from "./orchestration"
|
|
17
|
+
import type { RuntimeRegistry } from "./runtime-registry"
|
|
18
|
+
import { deriveServerProviderCatalog } from "./provider-catalog"
|
|
19
|
+
import type { ProfileSnapshot } from "../shared/profile-types"
|
|
20
|
+
import type { ExtensionPreferencesSnapshot } from "../shared/extension-types"
|
|
21
|
+
import type { RunnerTeamSnapshot } from "../shared/runner-team-types"
|
|
22
|
+
|
|
23
|
+
const encoder = new TextEncoder()
|
|
24
|
+
|
|
25
|
+
function errorMessage(error: unknown): string {
|
|
26
|
+
return error instanceof Error ? error.message : String(error)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DEFAULT_UPDATE_SNAPSHOT = {
|
|
30
|
+
currentVersion: "unknown",
|
|
31
|
+
latestVersion: null,
|
|
32
|
+
status: "idle",
|
|
33
|
+
updateAvailable: false,
|
|
34
|
+
lastCheckedAt: null,
|
|
35
|
+
error: null,
|
|
36
|
+
installAction: "restart",
|
|
37
|
+
} as const
|
|
38
|
+
|
|
39
|
+
export interface CreateNatsPublisherArgs {
|
|
40
|
+
nc: NatsConnection
|
|
41
|
+
store: EventStore
|
|
42
|
+
agent: { getActiveStatuses(): Map<string, SessionStatus> }
|
|
43
|
+
terminals: TerminalManager
|
|
44
|
+
refreshDiscovery: () => Promise<DiscoveredProject[]>
|
|
45
|
+
getDiscoveredProjects: () => DiscoveredProject[]
|
|
46
|
+
machineDisplayName: string
|
|
47
|
+
updateManager: UpdateManager | null
|
|
48
|
+
skillCache?: SkillCache
|
|
49
|
+
orchestrator?: SessionOrchestrator
|
|
50
|
+
runtimeRegistry?: RuntimeRegistry
|
|
51
|
+
hasActiveBlockingDelegations?: (chatId: string) => boolean
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function deriveProfileSnapshot(store: EventStore): ProfileSnapshot {
|
|
55
|
+
return {
|
|
56
|
+
profiles: [...store.state.providerProfiles.values()],
|
|
57
|
+
workspaceOverrides: [...store.state.workspaceProfileOverrides.values()].flatMap(
|
|
58
|
+
(wsMap) => [...wsMap.values()],
|
|
59
|
+
),
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function deriveExtensionPreferencesSnapshot(store: EventStore): ExtensionPreferencesSnapshot {
|
|
64
|
+
return {
|
|
65
|
+
preferences: [...store.state.extensionPreferences.values()],
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function deriveRunnerTeamSnapshot(store: EventStore): RunnerTeamSnapshot {
|
|
70
|
+
return {
|
|
71
|
+
members: [...store.state.teamMembers.values()],
|
|
72
|
+
runners: [...store.state.runnerLabels.values()],
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function createNatsPublisher(args: CreateNatsPublisherArgs) {
|
|
77
|
+
const {
|
|
78
|
+
nc,
|
|
79
|
+
store,
|
|
80
|
+
agent,
|
|
81
|
+
terminals,
|
|
82
|
+
getDiscoveredProjects,
|
|
83
|
+
machineDisplayName,
|
|
84
|
+
updateManager,
|
|
85
|
+
skillCache,
|
|
86
|
+
orchestrator,
|
|
87
|
+
runtimeRegistry,
|
|
88
|
+
hasActiveBlockingDelegations,
|
|
89
|
+
} = args
|
|
90
|
+
|
|
91
|
+
const js = jetstream(nc)
|
|
92
|
+
const kv = await new Kvm(nc).create(KV_BUCKET)
|
|
93
|
+
console.warn(LOG_PREFIX, `KV bucket "${KV_BUCKET}" ready`)
|
|
94
|
+
|
|
95
|
+
const activeSubscriptions = new Map<string, SubscriptionTopic>()
|
|
96
|
+
const lastJsonByKey = new Map<string, string>()
|
|
97
|
+
|
|
98
|
+
function publishSnapshot(topic: SubscriptionTopic, data: unknown): void {
|
|
99
|
+
const kvKey = snapshotKvKey(topic)
|
|
100
|
+
const json = JSON.stringify(data)
|
|
101
|
+
|
|
102
|
+
if (lastJsonByKey.get(kvKey) === json) return
|
|
103
|
+
|
|
104
|
+
const payload = compressPayload(encoder.encode(json))
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
nc.publish(snapshotSubject(topic), payload)
|
|
108
|
+
lastJsonByKey.set(kvKey, json)
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.warn(LOG_PREFIX, `NATS publish failed on ${snapshotSubject(topic)}: ${errorMessage(error)}`)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
void kv.put(kvKey, payload).catch((error) => {
|
|
114
|
+
console.warn(LOG_PREFIX, `KV put failed on ${kvKey}: ${errorMessage(error)}`)
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function computeSnapshot(topic: SubscriptionTopic): Promise<unknown> {
|
|
119
|
+
switch (topic.type) {
|
|
120
|
+
case "sidebar":
|
|
121
|
+
return deriveSidebarData(store.state, agent.getActiveStatuses(), hasActiveBlockingDelegations)
|
|
122
|
+
case "local-workspaces":
|
|
123
|
+
return deriveLocalWorkspacesSnapshot(store.state, getDiscoveredProjects(), machineDisplayName)
|
|
124
|
+
case "update":
|
|
125
|
+
return updateManager?.getSnapshot() ?? DEFAULT_UPDATE_SNAPSHOT
|
|
126
|
+
case "chat": {
|
|
127
|
+
const chat = store.state.chatsById.get(topic.chatId)
|
|
128
|
+
const project = chat ? store.state.workspacesById.get(chat.workspaceId) : undefined
|
|
129
|
+
const skills = project && skillCache ? await skillCache.get(project.localPath) : []
|
|
130
|
+
const messageCount = await store.getMessageCount(topic.chatId)
|
|
131
|
+
const renderEntries = await store.getMessages(topic.chatId, {
|
|
132
|
+
offset: Math.max(0, messageCount - TRANSCRIPT_RENDER_WINDOW_SIZE),
|
|
133
|
+
limit: TRANSCRIPT_RENDER_WINDOW_SIZE,
|
|
134
|
+
})
|
|
135
|
+
const activeStatus = agent.getActiveStatuses().get(topic.chatId)
|
|
136
|
+
const providerCatalog = runtimeRegistry
|
|
137
|
+
? deriveServerProviderCatalog(runtimeRegistry.getProviderCapabilities("claude"))
|
|
138
|
+
: undefined
|
|
139
|
+
return deriveChatSnapshot(
|
|
140
|
+
store.state,
|
|
141
|
+
agent.getActiveStatuses(),
|
|
142
|
+
topic.chatId,
|
|
143
|
+
messageCount,
|
|
144
|
+
skills,
|
|
145
|
+
hasActiveBlockingDelegations,
|
|
146
|
+
deriveTranscriptRenderUnits(renderEntries, activeStatus),
|
|
147
|
+
providerCatalog,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
case "terminal":
|
|
151
|
+
return terminals.getSnapshot(topic.terminalId)
|
|
152
|
+
case "orchestration":
|
|
153
|
+
return orchestrator?.getHierarchy(topic.chatId) ?? { children: [] }
|
|
154
|
+
case "workspace":
|
|
155
|
+
return deriveWorkspaceCoordinationSnapshot(store.state, topic.workspaceId)
|
|
156
|
+
case "agent-config":
|
|
157
|
+
return deriveAgentConfigSnapshot(store.state, topic.workspaceId)
|
|
158
|
+
case "repos":
|
|
159
|
+
return deriveRepoListSnapshot(store.state, topic.workspaceId)
|
|
160
|
+
case "workflow-runs":
|
|
161
|
+
return deriveWorkflowRunsSnapshot(store.state, topic.workspaceId)
|
|
162
|
+
case "sandbox-status":
|
|
163
|
+
return deriveSandboxSnapshot(store.state, topic.workspaceId)
|
|
164
|
+
case "runtime-status":
|
|
165
|
+
return runtimeRegistry?.getSnapshot() ?? { runtimes: [] }
|
|
166
|
+
case "profiles":
|
|
167
|
+
return deriveProfileSnapshot(store)
|
|
168
|
+
case "extension-preferences":
|
|
169
|
+
return deriveExtensionPreferencesSnapshot(store)
|
|
170
|
+
case "runner-teams":
|
|
171
|
+
return deriveRunnerTeamSnapshot(store)
|
|
172
|
+
default: {
|
|
173
|
+
const _exhaustive: never = topic
|
|
174
|
+
throw new Error(`Unknown topic type: ${(_exhaustive as SubscriptionTopic).type}`)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function addSubscription(subscriptionId: string, topic: SubscriptionTopic): void {
|
|
180
|
+
activeSubscriptions.set(subscriptionId, topic)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function removeSubscription(subscriptionId: string): void {
|
|
184
|
+
const topic = activeSubscriptions.get(subscriptionId)
|
|
185
|
+
activeSubscriptions.delete(subscriptionId)
|
|
186
|
+
|
|
187
|
+
// Prune dedup cache if no remaining subscriptions reference this key
|
|
188
|
+
if (topic) {
|
|
189
|
+
const key = snapshotKvKey(topic)
|
|
190
|
+
const stillTracked = [...activeSubscriptions.values()].some((t) => snapshotKvKey(t) === key)
|
|
191
|
+
if (!stillTracked) {
|
|
192
|
+
lastJsonByKey.delete(key)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function getSnapshot(topic: SubscriptionTopic): Promise<unknown> {
|
|
198
|
+
const data = await computeSnapshot(topic)
|
|
199
|
+
publishSnapshot(topic, data)
|
|
200
|
+
return data
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function broadcastSnapshots(changedTypes?: ReadonlySet<string>): Promise<void> {
|
|
204
|
+
const published = new Set<string>()
|
|
205
|
+
for (const [, topic] of activeSubscriptions) {
|
|
206
|
+
if (changedTypes && !changedTypes.has(topic.type)) continue
|
|
207
|
+
const key = snapshotKvKey(topic)
|
|
208
|
+
if (published.has(key)) continue
|
|
209
|
+
published.add(key)
|
|
210
|
+
publishSnapshot(topic, await computeSnapshot(topic))
|
|
211
|
+
}
|
|
212
|
+
orchestrator?.pruneTombstones()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function publishChatMessage(chatId: string, entry: TranscriptEntry): void {
|
|
216
|
+
const subject = chatMessageSubject(chatId)
|
|
217
|
+
const event: ChatMessageEvent = { chatId, entry }
|
|
218
|
+
const payload = compressPayload(encoder.encode(JSON.stringify(event)))
|
|
219
|
+
void js.publish(subject, payload).catch((error) => {
|
|
220
|
+
console.warn(LOG_PREFIX, `JetStream publish failed on ${subject}: ${errorMessage(error)}`)
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const disposeTerminalEvents = terminals.onEvent((event) => {
|
|
225
|
+
const subject = terminalEventSubject(event.terminalId)
|
|
226
|
+
const payload = compressPayload(encoder.encode(JSON.stringify(event)))
|
|
227
|
+
void js.publish(subject, payload).catch((error) => {
|
|
228
|
+
console.warn(LOG_PREFIX, `JetStream publish failed on ${subject}: ${errorMessage(error)}`)
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const disposeUpdateEvents = updateManager?.onChange(() => {
|
|
233
|
+
publishSnapshot({ type: "update" }, updateManager.getSnapshot())
|
|
234
|
+
}) ?? (() => {})
|
|
235
|
+
|
|
236
|
+
// Periodic runtime health checks — updates runtime-status snapshot
|
|
237
|
+
const healthCheckInterval = runtimeRegistry
|
|
238
|
+
? setInterval(async () => {
|
|
239
|
+
try {
|
|
240
|
+
for (const provider of ["claude", "codex"] as const) {
|
|
241
|
+
await runtimeRegistry.healthCheck(provider)
|
|
242
|
+
}
|
|
243
|
+
publishSnapshot({ type: "runtime-status" }, runtimeRegistry.getSnapshot())
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.warn(LOG_PREFIX, `Runtime health check failed: ${errorMessage(error)}`)
|
|
246
|
+
}
|
|
247
|
+
}, 60_000)
|
|
248
|
+
: null
|
|
249
|
+
|
|
250
|
+
if (runtimeRegistry) {
|
|
251
|
+
void runtimeRegistry.probeCapabilities("claude").then(() => {
|
|
252
|
+
publishSnapshot({ type: "runtime-status" }, runtimeRegistry.getSnapshot())
|
|
253
|
+
broadcastSnapshots()
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
addSubscription,
|
|
259
|
+
removeSubscription,
|
|
260
|
+
getSnapshot,
|
|
261
|
+
broadcastSnapshots,
|
|
262
|
+
publishChatMessage,
|
|
263
|
+
dispose() {
|
|
264
|
+
disposeTerminalEvents()
|
|
265
|
+
disposeUpdateEvents()
|
|
266
|
+
if (healthCheckInterval) clearInterval(healthCheckInterval)
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export type NatsPublisher = Awaited<ReturnType<typeof createNatsPublisher>>
|