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,301 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"
|
|
2
|
+
import { homedir } from "node:os"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import type { AgentProvider } from "../shared/types"
|
|
5
|
+
import { resolveLocalPath } from "./paths"
|
|
6
|
+
|
|
7
|
+
export interface DiscoveredProject {
|
|
8
|
+
localPath: string
|
|
9
|
+
title: string
|
|
10
|
+
modifiedAt: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ProviderDiscoveredProject extends DiscoveredProject {
|
|
14
|
+
provider: AgentProvider
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ProjectDiscoveryAdapter {
|
|
18
|
+
provider: AgentProvider
|
|
19
|
+
scan(homeDir?: string): ProviderDiscoveredProject[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolveEncodedClaudePath(folderName: string) {
|
|
23
|
+
const segments = folderName.replace(/^-/, "").split("-").filter(Boolean)
|
|
24
|
+
let currentPath = ""
|
|
25
|
+
let remainingSegments = [...segments]
|
|
26
|
+
|
|
27
|
+
while (remainingSegments.length > 0) {
|
|
28
|
+
let found = false
|
|
29
|
+
|
|
30
|
+
for (let index = remainingSegments.length; index >= 1; index -= 1) {
|
|
31
|
+
const segment = remainingSegments.slice(0, index).join("-")
|
|
32
|
+
const candidate = `${currentPath}/${segment}`
|
|
33
|
+
|
|
34
|
+
if (existsSync(candidate)) {
|
|
35
|
+
currentPath = candidate
|
|
36
|
+
remainingSegments = remainingSegments.slice(index)
|
|
37
|
+
found = true
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!found) {
|
|
43
|
+
const [head, ...tail] = remainingSegments
|
|
44
|
+
currentPath = `${currentPath}/${head}`
|
|
45
|
+
remainingSegments = tail
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return currentPath || "/"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeExistingDirectory(localPath: string) {
|
|
53
|
+
try {
|
|
54
|
+
const normalized = resolveLocalPath(localPath)
|
|
55
|
+
if (!statSync(normalized).isDirectory()) {
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
return normalized
|
|
59
|
+
} catch {
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function mergeDiscoveredProjects(projects: Iterable<DiscoveredProject>): DiscoveredProject[] {
|
|
65
|
+
const merged = new Map<string, DiscoveredProject>()
|
|
66
|
+
|
|
67
|
+
for (const project of projects) {
|
|
68
|
+
const existing = merged.get(project.localPath)
|
|
69
|
+
if (!existing || project.modifiedAt > existing.modifiedAt) {
|
|
70
|
+
merged.set(project.localPath, {
|
|
71
|
+
localPath: project.localPath,
|
|
72
|
+
title: project.title || path.basename(project.localPath) || project.localPath,
|
|
73
|
+
modifiedAt: project.modifiedAt,
|
|
74
|
+
})
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!existing.title && project.title) {
|
|
79
|
+
existing.title = project.title
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [...merged.values()].sort((a, b) => b.modifiedAt - a.modifiedAt)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class ClaudeProjectDiscoveryAdapter implements ProjectDiscoveryAdapter {
|
|
87
|
+
readonly provider = "claude" as const
|
|
88
|
+
|
|
89
|
+
scan(homeDir: string = homedir()): ProviderDiscoveredProject[] {
|
|
90
|
+
const projectsDir = path.join(homeDir, ".claude", "projects")
|
|
91
|
+
if (!existsSync(projectsDir)) {
|
|
92
|
+
return []
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const entries = readdirSync(projectsDir, { withFileTypes: true })
|
|
96
|
+
const projects: ProviderDiscoveredProject[] = []
|
|
97
|
+
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
if (!entry.isDirectory()) continue
|
|
100
|
+
|
|
101
|
+
const resolvedPath = resolveEncodedClaudePath(entry.name)
|
|
102
|
+
const normalizedPath = normalizeExistingDirectory(resolvedPath)
|
|
103
|
+
if (!normalizedPath) {
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const stat = statSync(path.join(projectsDir, entry.name))
|
|
108
|
+
projects.push({
|
|
109
|
+
provider: this.provider,
|
|
110
|
+
localPath: normalizedPath,
|
|
111
|
+
title: path.basename(normalizedPath) || normalizedPath,
|
|
112
|
+
modifiedAt: stat.mtimeMs,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const mergedProjects = mergeDiscoveredProjects(projects).map((project) => ({
|
|
117
|
+
provider: this.provider,
|
|
118
|
+
...project,
|
|
119
|
+
}))
|
|
120
|
+
|
|
121
|
+
return mergedProjects
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function parseJsonRecord(line: string): Record<string, unknown> | null {
|
|
126
|
+
try {
|
|
127
|
+
const parsed = JSON.parse(line)
|
|
128
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
129
|
+
return null
|
|
130
|
+
}
|
|
131
|
+
return parsed as Record<string, unknown>
|
|
132
|
+
} catch {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function readCodexSessionIndex(indexPath: string) {
|
|
138
|
+
const updatedAtById = new Map<string, number>()
|
|
139
|
+
if (!existsSync(indexPath)) {
|
|
140
|
+
return updatedAtById
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const line of readFileSync(indexPath, "utf8").split("\n")) {
|
|
144
|
+
if (!line.trim()) continue
|
|
145
|
+
const record = parseJsonRecord(line)
|
|
146
|
+
if (!record) continue
|
|
147
|
+
|
|
148
|
+
const id = typeof record.id === "string" ? record.id : null
|
|
149
|
+
const updatedAt = typeof record.updated_at === "string" ? Date.parse(record.updated_at) : Number.NaN
|
|
150
|
+
if (!id || Number.isNaN(updatedAt)) continue
|
|
151
|
+
|
|
152
|
+
const existing = updatedAtById.get(id)
|
|
153
|
+
if (existing === undefined || updatedAt > existing) {
|
|
154
|
+
updatedAtById.set(id, updatedAt)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return updatedAtById
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function collectCodexSessionFiles(directory: string): string[] {
|
|
162
|
+
if (!existsSync(directory)) {
|
|
163
|
+
return []
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const files: string[] = []
|
|
167
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
168
|
+
const fullPath = path.join(directory, entry.name)
|
|
169
|
+
if (entry.isDirectory()) {
|
|
170
|
+
files.push(...collectCodexSessionFiles(fullPath))
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
174
|
+
files.push(fullPath)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return files
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function readCodexConfiguredProjects(configPath: string) {
|
|
181
|
+
const projects = new Map<string, number>()
|
|
182
|
+
if (!existsSync(configPath)) {
|
|
183
|
+
return projects
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const configMtime = statSync(configPath).mtimeMs
|
|
187
|
+
for (const line of readFileSync(configPath, "utf8").split("\n")) {
|
|
188
|
+
const match = line.match(/^\[projects\."(.+)"\]$/)
|
|
189
|
+
if (!match?.[1]) continue
|
|
190
|
+
projects.set(match[1], configMtime)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return projects
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function readCodexSessionMetadata(sessionsDir: string) {
|
|
197
|
+
const metadataById = new Map<string, { cwd: string; modifiedAt: number }>()
|
|
198
|
+
|
|
199
|
+
for (const sessionFile of collectCodexSessionFiles(sessionsDir)) {
|
|
200
|
+
const fileStat = statSync(sessionFile)
|
|
201
|
+
const firstLine = readFileSync(sessionFile, "utf8").split("\n", 1)[0]
|
|
202
|
+
if (!firstLine?.trim()) continue
|
|
203
|
+
|
|
204
|
+
const record = parseJsonRecord(firstLine)
|
|
205
|
+
if (!record || record.type !== "session_meta") continue
|
|
206
|
+
|
|
207
|
+
const payload = record.payload
|
|
208
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) continue
|
|
209
|
+
|
|
210
|
+
const payloadRecord = payload as Record<string, unknown>
|
|
211
|
+
const sessionId = typeof payloadRecord.id === "string" ? payloadRecord.id : null
|
|
212
|
+
const cwd = typeof payloadRecord.cwd === "string" ? payloadRecord.cwd : null
|
|
213
|
+
if (!sessionId || !cwd) continue
|
|
214
|
+
|
|
215
|
+
const recordTimestamp = typeof record.timestamp === "string" ? Date.parse(record.timestamp) : Number.NaN
|
|
216
|
+
const payloadTimestamp = typeof payloadRecord.timestamp === "string" ? Date.parse(payloadRecord.timestamp) : Number.NaN
|
|
217
|
+
const modifiedAt = [recordTimestamp, payloadTimestamp, fileStat.mtimeMs].find((value) => !Number.isNaN(value)) ?? fileStat.mtimeMs
|
|
218
|
+
|
|
219
|
+
metadataById.set(sessionId, { cwd, modifiedAt })
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return metadataById
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export class CodexProjectDiscoveryAdapter implements ProjectDiscoveryAdapter {
|
|
226
|
+
readonly provider = "codex" as const
|
|
227
|
+
|
|
228
|
+
scan(homeDir: string = homedir()): ProviderDiscoveredProject[] {
|
|
229
|
+
const indexPath = path.join(homeDir, ".codex", "session_index.jsonl")
|
|
230
|
+
const sessionsDir = path.join(homeDir, ".codex", "sessions")
|
|
231
|
+
const configPath = path.join(homeDir, ".codex", "config.toml")
|
|
232
|
+
const updatedAtById = readCodexSessionIndex(indexPath)
|
|
233
|
+
const metadataById = readCodexSessionMetadata(sessionsDir)
|
|
234
|
+
const configuredProjects = readCodexConfiguredProjects(configPath)
|
|
235
|
+
const projects: ProviderDiscoveredProject[] = []
|
|
236
|
+
|
|
237
|
+
for (const [sessionId, metadata] of metadataById.entries()) {
|
|
238
|
+
const modifiedAt = updatedAtById.get(sessionId) ?? metadata.modifiedAt
|
|
239
|
+
const cwd = metadata.cwd
|
|
240
|
+
if (!cwd) {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
if (!path.isAbsolute(cwd)) {
|
|
244
|
+
continue
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const normalizedPath = normalizeExistingDirectory(cwd)
|
|
248
|
+
if (!normalizedPath) {
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
projects.push({
|
|
253
|
+
provider: this.provider,
|
|
254
|
+
localPath: normalizedPath,
|
|
255
|
+
title: path.basename(normalizedPath) || normalizedPath,
|
|
256
|
+
modifiedAt,
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
for (const [configuredPath, modifiedAt] of configuredProjects.entries()) {
|
|
261
|
+
if (!path.isAbsolute(configuredPath)) {
|
|
262
|
+
continue
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const normalizedPath = normalizeExistingDirectory(configuredPath)
|
|
266
|
+
if (!normalizedPath) {
|
|
267
|
+
continue
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
projects.push({
|
|
271
|
+
provider: this.provider,
|
|
272
|
+
localPath: normalizedPath,
|
|
273
|
+
title: path.basename(normalizedPath) || normalizedPath,
|
|
274
|
+
modifiedAt,
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const mergedProjects = mergeDiscoveredProjects(projects).map((project) => ({
|
|
279
|
+
provider: this.provider,
|
|
280
|
+
...project,
|
|
281
|
+
}))
|
|
282
|
+
|
|
283
|
+
return mergedProjects
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export const DEFAULT_PROJECT_DISCOVERY_ADAPTERS: ProjectDiscoveryAdapter[] = [
|
|
288
|
+
new ClaudeProjectDiscoveryAdapter(),
|
|
289
|
+
new CodexProjectDiscoveryAdapter(),
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
export function discoverProjects(
|
|
293
|
+
homeDir: string = homedir(),
|
|
294
|
+
adapters: ProjectDiscoveryAdapter[] = DEFAULT_PROJECT_DISCOVERY_ADAPTERS
|
|
295
|
+
): DiscoveredProject[] {
|
|
296
|
+
const mergedProjects = mergeDiscoveredProjects(
|
|
297
|
+
adapters.flatMap((adapter) => adapter.scan(homeDir).map(({ provider: _provider, ...project }) => project))
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return mergedProjects
|
|
301
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
|
+
import { mkdtemp, readFile, rm } from "node:fs/promises"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { tmpdir } from "node:os"
|
|
5
|
+
import { EventStore } from "./event-store"
|
|
6
|
+
import type { SnapshotFile } from "./events"
|
|
7
|
+
import type { AgentConfig } from "../shared/agent-config-types"
|
|
8
|
+
|
|
9
|
+
const tempDirs: string[] = []
|
|
10
|
+
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })))
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
async function createTempDataDir() {
|
|
16
|
+
const dir = await mkdtemp(join(tmpdir(), "kanna-ac-"))
|
|
17
|
+
tempDirs.push(dir)
|
|
18
|
+
return dir
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function createStoreWithWorkspace() {
|
|
22
|
+
const dataDir = await createTempDataDir()
|
|
23
|
+
const store = new EventStore(dataDir)
|
|
24
|
+
await store.initialize()
|
|
25
|
+
const workspace = await store.openProject("/tmp/ac-workspace")
|
|
26
|
+
return { dataDir, store, workspaceId: workspace.id }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const makeConfig = (id: string): AgentConfig => ({
|
|
30
|
+
id,
|
|
31
|
+
name: `Agent ${id}`,
|
|
32
|
+
description: `Test agent ${id}`,
|
|
33
|
+
provider: "claude",
|
|
34
|
+
model: "opus-4",
|
|
35
|
+
systemPrompt: "You are helpful.",
|
|
36
|
+
tools: ["bash"],
|
|
37
|
+
temperature: 0.5,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe("EventStore agent config reducers", () => {
|
|
41
|
+
test("saveAgentConfig creates record in agentConfigsByWorkspace", async () => {
|
|
42
|
+
const { store, workspaceId } = await createStoreWithWorkspace()
|
|
43
|
+
const config = makeConfig("a1")
|
|
44
|
+
|
|
45
|
+
await store.saveAgentConfig(workspaceId, "a1", config)
|
|
46
|
+
|
|
47
|
+
const wsMap = store.state.agentConfigsByWorkspace.get(workspaceId)
|
|
48
|
+
expect(wsMap).toBeDefined()
|
|
49
|
+
const record = wsMap!.get("a1")
|
|
50
|
+
expect(record).toBeDefined()
|
|
51
|
+
expect(record!.id).toBe("a1")
|
|
52
|
+
expect(record!.workspaceId).toBe(workspaceId)
|
|
53
|
+
expect(record!.config).toEqual(config)
|
|
54
|
+
expect(record!.createdAt).toBeGreaterThan(0)
|
|
55
|
+
expect(record!.updatedAt).toBeGreaterThan(0)
|
|
56
|
+
expect(record!.lastCommitHash).toBeUndefined()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test("commitAgentConfig sets lastCommitHash", async () => {
|
|
60
|
+
const { store, workspaceId } = await createStoreWithWorkspace()
|
|
61
|
+
await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
|
|
62
|
+
|
|
63
|
+
await store.commitAgentConfig(workspaceId, "a1", "abc123def")
|
|
64
|
+
|
|
65
|
+
const record = store.state.agentConfigsByWorkspace.get(workspaceId)!.get("a1")!
|
|
66
|
+
expect(record.lastCommitHash).toBe("abc123def")
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test("removeAgentConfig deletes record from map", async () => {
|
|
70
|
+
const { store, workspaceId } = await createStoreWithWorkspace()
|
|
71
|
+
await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
|
|
72
|
+
|
|
73
|
+
await store.removeAgentConfig(workspaceId, "a1")
|
|
74
|
+
|
|
75
|
+
const wsMap = store.state.agentConfigsByWorkspace.get(workspaceId)
|
|
76
|
+
expect(wsMap?.has("a1")).toBe(false)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test("snapshot round-trip preserves agent configs", async () => {
|
|
80
|
+
const { dataDir, store, workspaceId } = await createStoreWithWorkspace()
|
|
81
|
+
await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
|
|
82
|
+
await store.commitAgentConfig(workspaceId, "a1", "hash1")
|
|
83
|
+
await store.saveAgentConfig(workspaceId, "a2", makeConfig("a2"))
|
|
84
|
+
|
|
85
|
+
await store.compact()
|
|
86
|
+
|
|
87
|
+
// Verify snapshot file has agentConfigs
|
|
88
|
+
const snapshot = JSON.parse(await readFile(join(dataDir, "snapshot.json"), "utf8")) as SnapshotFile
|
|
89
|
+
expect(snapshot.agentConfigs).toBeDefined()
|
|
90
|
+
expect(snapshot.agentConfigs!.length).toBe(1)
|
|
91
|
+
expect(snapshot.agentConfigs![0].workspaceId).toBe(workspaceId)
|
|
92
|
+
expect(snapshot.agentConfigs![0].records.length).toBe(2)
|
|
93
|
+
|
|
94
|
+
// Fresh store from snapshot
|
|
95
|
+
const store2 = new EventStore(dataDir)
|
|
96
|
+
await store2.initialize()
|
|
97
|
+
|
|
98
|
+
const wsMap = store2.state.agentConfigsByWorkspace.get(workspaceId)!
|
|
99
|
+
expect(wsMap.size).toBe(2)
|
|
100
|
+
expect(wsMap.get("a1")!.config.name).toBe("Agent a1")
|
|
101
|
+
expect(wsMap.get("a1")!.lastCommitHash).toBe("hash1")
|
|
102
|
+
expect(wsMap.get("a2")!.config.name).toBe("Agent a2")
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("second save updates updatedAt but preserves createdAt", async () => {
|
|
106
|
+
const { store, workspaceId } = await createStoreWithWorkspace()
|
|
107
|
+
await store.saveAgentConfig(workspaceId, "a1", makeConfig("a1"))
|
|
108
|
+
|
|
109
|
+
const record1 = store.state.agentConfigsByWorkspace.get(workspaceId)!.get("a1")!
|
|
110
|
+
const createdAt = record1.createdAt
|
|
111
|
+
const updatedAt1 = record1.updatedAt
|
|
112
|
+
|
|
113
|
+
// Small delay to ensure timestamp differs
|
|
114
|
+
await new Promise((r) => setTimeout(r, 5))
|
|
115
|
+
|
|
116
|
+
const updated = { ...makeConfig("a1"), name: "Renamed Agent" }
|
|
117
|
+
await store.saveAgentConfig(workspaceId, "a1", updated)
|
|
118
|
+
|
|
119
|
+
const record2 = store.state.agentConfigsByWorkspace.get(workspaceId)!.get("a1")!
|
|
120
|
+
expect(record2.createdAt).toBe(createdAt)
|
|
121
|
+
expect(record2.updatedAt).toBeGreaterThan(updatedAt1)
|
|
122
|
+
expect(record2.config.name).toBe("Renamed Agent")
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
|
+
import { mkdtemp, readFile, rm } from "node:fs/promises"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { tmpdir } from "node:os"
|
|
5
|
+
import { EventStore } from "./event-store"
|
|
6
|
+
import type { SnapshotFile } from "./events"
|
|
7
|
+
|
|
8
|
+
const tempDirs: string[] = []
|
|
9
|
+
|
|
10
|
+
afterEach(async () => {
|
|
11
|
+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })))
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
async function createTempDataDir() {
|
|
15
|
+
const dir = await mkdtemp(join(tmpdir(), "kanna-coord-"))
|
|
16
|
+
tempDirs.push(dir)
|
|
17
|
+
return dir
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function createStoreWithProject() {
|
|
21
|
+
const dataDir = await createTempDataDir()
|
|
22
|
+
const store = new EventStore(dataDir)
|
|
23
|
+
await store.initialize()
|
|
24
|
+
const project = await store.openProject("/tmp/coord-project")
|
|
25
|
+
return { dataDir, store, workspaceId: project.id }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("EventStore coordination", () => {
|
|
29
|
+
test("appends todo_added and replays on restart", async () => {
|
|
30
|
+
const { dataDir, store, workspaceId } = await createStoreWithProject()
|
|
31
|
+
|
|
32
|
+
await store.addTodo(workspaceId, "t1", "Build feature X", "high", "agent-1")
|
|
33
|
+
|
|
34
|
+
const coord = store.state.coordinationByWorkspace.get(workspaceId)!
|
|
35
|
+
expect(coord.todos.get("t1")).toMatchObject({
|
|
36
|
+
id: "t1",
|
|
37
|
+
description: "Build feature X",
|
|
38
|
+
priority: "high",
|
|
39
|
+
status: "open",
|
|
40
|
+
claimedBy: null,
|
|
41
|
+
createdBy: "agent-1",
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Replay on restart
|
|
45
|
+
const store2 = new EventStore(dataDir)
|
|
46
|
+
await store2.initialize()
|
|
47
|
+
const coord2 = store2.state.coordinationByWorkspace.get(workspaceId)!
|
|
48
|
+
expect(coord2.todos.get("t1")?.description).toBe("Build feature X")
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("todo claim, complete, abandon lifecycle", async () => {
|
|
52
|
+
const { store, workspaceId } = await createStoreWithProject()
|
|
53
|
+
|
|
54
|
+
await store.addTodo(workspaceId, "t1", "Task A", "normal", "agent-1")
|
|
55
|
+
await store.claimTodo(workspaceId, "t1", "agent-2")
|
|
56
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.status).toBe("claimed")
|
|
57
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.claimedBy).toBe("agent-2")
|
|
58
|
+
|
|
59
|
+
await store.completeTodo(workspaceId, "t1", ["output.ts"])
|
|
60
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.status).toBe("complete")
|
|
61
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.outputs).toEqual(["output.ts"])
|
|
62
|
+
|
|
63
|
+
await store.addTodo(workspaceId, "t2", "Task B", "low", "agent-1")
|
|
64
|
+
await store.claimTodo(workspaceId, "t2", "agent-3")
|
|
65
|
+
await store.abandonTodo(workspaceId, "t2")
|
|
66
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t2")?.status).toBe("abandoned")
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test("claim with file overlap detects conflict", async () => {
|
|
70
|
+
const { store, workspaceId } = await createStoreWithProject()
|
|
71
|
+
|
|
72
|
+
await store.createClaim(workspaceId, "c1", "Refactor auth", ["src/auth.ts", "src/login.ts"], "session-1")
|
|
73
|
+
await store.createClaim(workspaceId, "c2", "Fix login bug", ["src/login.ts", "src/utils.ts"], "session-2")
|
|
74
|
+
|
|
75
|
+
const c2 = store.state.coordinationByWorkspace.get(workspaceId)!.claims.get("c2")!
|
|
76
|
+
expect(c2.status).toBe("conflict")
|
|
77
|
+
expect(c2.conflictsWith).toBe("c1")
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test("worktree create and assign", async () => {
|
|
81
|
+
const { store, workspaceId } = await createStoreWithProject()
|
|
82
|
+
|
|
83
|
+
await store.createWorktree(workspaceId, "w1", "feat/auth", "main", "/tmp/wt1")
|
|
84
|
+
const wt = store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")!
|
|
85
|
+
expect(wt.status).toBe("ready")
|
|
86
|
+
expect(wt.assignedTo).toBeNull()
|
|
87
|
+
|
|
88
|
+
await store.assignWorktree(workspaceId, "w1", "session-1")
|
|
89
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.status).toBe("assigned")
|
|
90
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.assignedTo).toBe("session-1")
|
|
91
|
+
|
|
92
|
+
await store.removeWorktree(workspaceId, "w1")
|
|
93
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.status).toBe("removed")
|
|
94
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.worktrees.get("w1")?.assignedTo).toBeNull()
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test("rule set and remove", async () => {
|
|
98
|
+
const { store, workspaceId } = await createStoreWithProject()
|
|
99
|
+
|
|
100
|
+
await store.setRule(workspaceId, "r1", "No console.log in production", "lead")
|
|
101
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.rules.get("r1")?.content).toBe("No console.log in production")
|
|
102
|
+
|
|
103
|
+
await store.setRule(workspaceId, "r1", "No console.log or debugger", "lead")
|
|
104
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.rules.get("r1")?.content).toBe("No console.log or debugger")
|
|
105
|
+
|
|
106
|
+
await store.removeRule(workspaceId, "r1")
|
|
107
|
+
expect(store.state.coordinationByWorkspace.get(workspaceId)!.rules.has("r1")).toBe(false)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test("compact preserves coordination state", async () => {
|
|
111
|
+
const { dataDir, store, workspaceId } = await createStoreWithProject()
|
|
112
|
+
|
|
113
|
+
await store.addTodo(workspaceId, "t1", "Survive compaction", "high", "agent-1")
|
|
114
|
+
await store.setRule(workspaceId, "r1", "Be excellent", "lead")
|
|
115
|
+
await store.compact()
|
|
116
|
+
|
|
117
|
+
// Coordination JSONL should be truncated
|
|
118
|
+
expect(await readFile(join(dataDir, "coordination.jsonl"), "utf8")).toBe("")
|
|
119
|
+
|
|
120
|
+
// Snapshot should have coordination
|
|
121
|
+
const snapshot = JSON.parse(await readFile(join(dataDir, "snapshot.json"), "utf8")) as SnapshotFile
|
|
122
|
+
expect(snapshot.coordination).toBeDefined()
|
|
123
|
+
expect(snapshot.coordination!.length).toBe(1)
|
|
124
|
+
expect(snapshot.coordination![0].workspaceId).toBe(workspaceId)
|
|
125
|
+
|
|
126
|
+
// New store should restore from snapshot
|
|
127
|
+
const store2 = new EventStore(dataDir)
|
|
128
|
+
await store2.initialize()
|
|
129
|
+
expect(store2.state.coordinationByWorkspace.get(workspaceId)!.todos.get("t1")?.description).toBe("Survive compaction")
|
|
130
|
+
expect(store2.state.coordinationByWorkspace.get(workspaceId)!.rules.get("r1")?.content).toBe("Be excellent")
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test("clearStorage resets coordination state", async () => {
|
|
134
|
+
const { dataDir, store, workspaceId } = await createStoreWithProject()
|
|
135
|
+
|
|
136
|
+
await store.addTodo(workspaceId, "t1", "Will be cleared", "normal", "agent-1")
|
|
137
|
+
|
|
138
|
+
// Force a version mismatch to trigger clearStorage during replay
|
|
139
|
+
const coordPath = join(dataDir, "coordination.jsonl")
|
|
140
|
+
const { writeFile } = await import("node:fs/promises")
|
|
141
|
+
await writeFile(coordPath, '{"v":999,"type":"todo_added"}\n', "utf8")
|
|
142
|
+
|
|
143
|
+
const store2 = new EventStore(dataDir)
|
|
144
|
+
await store2.initialize()
|
|
145
|
+
|
|
146
|
+
// State should be empty after clearStorage
|
|
147
|
+
expect(store2.state.coordinationByWorkspace.size).toBe(0)
|
|
148
|
+
})
|
|
149
|
+
})
|