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,103 @@
|
|
|
1
|
+
// src/server/project-agent.test.ts
|
|
2
|
+
import { describe, expect, test, afterEach } from "bun:test"
|
|
3
|
+
import { WorkspaceAgent } from "./workspace-agent"
|
|
4
|
+
import { SessionIndex } from "./session-index"
|
|
5
|
+
import { EventStore } from "./event-store"
|
|
6
|
+
import { TranscriptSearchIndex } from "./transcript-search"
|
|
7
|
+
import { mkdtemp, rm } from "node:fs/promises"
|
|
8
|
+
import { tmpdir } from "node:os"
|
|
9
|
+
import path from "node:path"
|
|
10
|
+
|
|
11
|
+
let tempDirs: string[] = []
|
|
12
|
+
|
|
13
|
+
async function createAgent() {
|
|
14
|
+
const dir = await mkdtemp(path.join(tmpdir(), "pa-test-"))
|
|
15
|
+
tempDirs.push(dir)
|
|
16
|
+
const store = new EventStore(dir)
|
|
17
|
+
await store.initialize()
|
|
18
|
+
const sessions = new SessionIndex()
|
|
19
|
+
const search = new TranscriptSearchIndex()
|
|
20
|
+
|
|
21
|
+
const project = await store.openProject("/tmp/test", "Test Project")
|
|
22
|
+
const workspaceId = project.id
|
|
23
|
+
|
|
24
|
+
const agent = new WorkspaceAgent({ sessions, store, search, workspaceId })
|
|
25
|
+
return { agent, sessions, store, search, workspaceId }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
for (const d of tempDirs) await rm(d, { recursive: true, force: true })
|
|
30
|
+
tempDirs = []
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe("WorkspaceAgent", () => {
|
|
34
|
+
describe("querySessions", () => {
|
|
35
|
+
test("returns sessions for project", async () => {
|
|
36
|
+
const { agent, workspaceId } = await createAgent()
|
|
37
|
+
const result = agent.querySessions(workspaceId)
|
|
38
|
+
expect(result).toEqual([])
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe("searchWork", () => {
|
|
43
|
+
test("delegates to transcript search", async () => {
|
|
44
|
+
const { agent, search } = await createAgent()
|
|
45
|
+
search.addEntry("chat-1", {
|
|
46
|
+
_id: "1",
|
|
47
|
+
createdAt: Date.now(),
|
|
48
|
+
kind: "user_prompt",
|
|
49
|
+
content: "implement auth middleware",
|
|
50
|
+
} as never)
|
|
51
|
+
|
|
52
|
+
const results = agent.searchWork("auth middleware", 10)
|
|
53
|
+
expect(results.length).toBe(1)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
describe("claimTask", () => {
|
|
58
|
+
test("creates and claims task in event store", async () => {
|
|
59
|
+
const { agent } = await createAgent()
|
|
60
|
+
const task = await agent.claimTask("implement auth", "chat-1", "feat/auth")
|
|
61
|
+
expect(task.status).toBe("claimed")
|
|
62
|
+
expect(task.description).toBe("implement auth")
|
|
63
|
+
expect(task.claimedBy).toBe("chat-1")
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe("completeTask", () => {
|
|
68
|
+
test("completes task", async () => {
|
|
69
|
+
const { agent } = await createAgent()
|
|
70
|
+
const task = await agent.claimTask("task", "chat-1", null)
|
|
71
|
+
const completed = await agent.completeTask(task.id, ["file.ts"])
|
|
72
|
+
expect(completed).not.toBeNull()
|
|
73
|
+
expect(completed!.status).toBe("complete")
|
|
74
|
+
expect(completed!.outputs).toEqual(["file.ts"])
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe("listTasks", () => {
|
|
79
|
+
test("returns all tasks", async () => {
|
|
80
|
+
const { agent } = await createAgent()
|
|
81
|
+
await agent.claimTask("a", "c1", null)
|
|
82
|
+
await agent.claimTask("b", "c2", null)
|
|
83
|
+
expect(agent.listTasks().length).toBe(2)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe("delegate", () => {
|
|
88
|
+
test("returns task info for task query", async () => {
|
|
89
|
+
const { agent } = await createAgent()
|
|
90
|
+
await agent.claimTask("implement auth", "chat-1", "feat/auth")
|
|
91
|
+
|
|
92
|
+
const result = await agent.delegate("who is working on auth?")
|
|
93
|
+
expect(result.status).toBe("ok")
|
|
94
|
+
expect(result.message).toContain("auth")
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test("returns ok with summary when no data found", async () => {
|
|
98
|
+
const { agent } = await createAgent()
|
|
99
|
+
const result = await agent.delegate("what is going on?")
|
|
100
|
+
expect(result.status).toBe("ok")
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/server/project-agent.ts
|
|
2
|
+
import { randomUUID } from "node:crypto"
|
|
3
|
+
import type { WorkspaceTodo, SessionRecord, SearchResult, DelegationResult } from "../shared/workspace-types"
|
|
4
|
+
import type { SessionIndex } from "./session-index"
|
|
5
|
+
import type { EventStore } from "./event-store"
|
|
6
|
+
import type { TranscriptSearchIndex } from "./transcript-search"
|
|
7
|
+
|
|
8
|
+
const TASK_KEYWORDS = ["task", "working on", "who", "claimed"]
|
|
9
|
+
const SEARCH_KEYWORDS = ["search", "find", "implemented", "where"]
|
|
10
|
+
|
|
11
|
+
interface WorkspaceAgentArgs {
|
|
12
|
+
sessions: SessionIndex
|
|
13
|
+
store: EventStore
|
|
14
|
+
search: TranscriptSearchIndex
|
|
15
|
+
workspaceId: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class WorkspaceAgent {
|
|
19
|
+
private readonly sessions: SessionIndex
|
|
20
|
+
private readonly store: EventStore
|
|
21
|
+
private readonly search: TranscriptSearchIndex
|
|
22
|
+
private readonly workspaceId: string
|
|
23
|
+
|
|
24
|
+
constructor(args: WorkspaceAgentArgs) {
|
|
25
|
+
this.sessions = args.sessions
|
|
26
|
+
this.store = args.store
|
|
27
|
+
this.search = args.search
|
|
28
|
+
this.workspaceId = args.workspaceId
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
querySessions(workspaceId: string): SessionRecord[] {
|
|
32
|
+
return this.sessions.getSessionsByProject(workspaceId)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getSessionSummary(chatId: string): SessionRecord | null {
|
|
36
|
+
return this.sessions.getSession(chatId)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
searchWork(query: string, limit: number): SearchResult[] {
|
|
40
|
+
return this.search.search(query, limit)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
listTasks(): WorkspaceTodo[] {
|
|
44
|
+
const coord = this.store.state.coordinationByWorkspace.get(this.workspaceId)
|
|
45
|
+
if (!coord) return []
|
|
46
|
+
return Array.from(coord.todos.values())
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getTask(taskId: string): WorkspaceTodo | null {
|
|
50
|
+
const coord = this.store.state.coordinationByWorkspace.get(this.workspaceId)
|
|
51
|
+
if (!coord) return null
|
|
52
|
+
return coord.todos.get(taskId) ?? null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async claimTask(description: string, claimedBy: string, _branch: string | null): Promise<WorkspaceTodo> {
|
|
56
|
+
const todoId = randomUUID()
|
|
57
|
+
await this.store.addTodo(this.workspaceId, todoId, description, "normal", claimedBy)
|
|
58
|
+
await this.store.claimTodo(this.workspaceId, todoId, claimedBy)
|
|
59
|
+
const todo = this.getTask(todoId)
|
|
60
|
+
if (!todo) throw new Error(`Todo ${todoId} not found after creation`)
|
|
61
|
+
return todo
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async completeTask(taskId: string, outputs: string[]): Promise<WorkspaceTodo | null> {
|
|
65
|
+
await this.store.completeTodo(this.workspaceId, taskId, outputs)
|
|
66
|
+
return this.getTask(taskId)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async abandonTask(taskId: string): Promise<WorkspaceTodo | null> {
|
|
70
|
+
await this.store.abandonTodo(this.workspaceId, taskId)
|
|
71
|
+
return this.getTask(taskId)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async delegate(request: string): Promise<DelegationResult> {
|
|
75
|
+
const sessions = this.querySessions(this.workspaceId)
|
|
76
|
+
const tasks = this.listTasks()
|
|
77
|
+
const lower = request.toLowerCase()
|
|
78
|
+
|
|
79
|
+
if (TASK_KEYWORDS.some((kw) => lower.includes(kw))) {
|
|
80
|
+
if (tasks.length === 0) {
|
|
81
|
+
return { status: "ok", message: "No tasks claimed." }
|
|
82
|
+
}
|
|
83
|
+
const summary = tasks.map((t) => `[${t.status}] "${t.description}" owned by ${t.claimedBy ?? t.createdBy}`).join("; ")
|
|
84
|
+
return { status: "ok", message: summary, data: { tasks } }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (SEARCH_KEYWORDS.some((kw) => lower.includes(kw))) {
|
|
88
|
+
const searchResults = this.search.search(request, 5)
|
|
89
|
+
if (searchResults.length === 0) {
|
|
90
|
+
return { status: "ok", message: "No matching transcript entries found." }
|
|
91
|
+
}
|
|
92
|
+
const summary = searchResults.map((r) => `[${r.chatId}] ${r.fragment.slice(0, 100)}`).join("\n")
|
|
93
|
+
return { status: "ok", message: summary, data: { searchResults } }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// General — summarize known state
|
|
97
|
+
const parts: string[] = []
|
|
98
|
+
if (sessions.length > 0) parts.push(`${sessions.length} session(s) active`)
|
|
99
|
+
if (tasks.length > 0) parts.push(`${tasks.length} task(s) tracked`)
|
|
100
|
+
return { status: "ok", message: parts.length > 0 ? parts.join(", ") + "." : "No project activity recorded yet." }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/server/project-cli.test.ts
|
|
2
|
+
import { describe, expect, test } from "bun:test"
|
|
3
|
+
import { parseProjectCliArgs, formatOutput } from "./project-cli"
|
|
4
|
+
|
|
5
|
+
describe("parseProjectCliArgs", () => {
|
|
6
|
+
test("parses 'sessions' command", () => {
|
|
7
|
+
const result = parseProjectCliArgs(["sessions"])
|
|
8
|
+
expect(result).toEqual({ command: "sessions", args: {} })
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test("parses 'sessions <id>' command", () => {
|
|
12
|
+
const result = parseProjectCliArgs(["sessions", "chat-1"])
|
|
13
|
+
expect(result).toEqual({ command: "session-detail", args: { chatId: "chat-1" } })
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test("parses 'search <query>' command", () => {
|
|
17
|
+
const result = parseProjectCliArgs(["search", "auth", "middleware"])
|
|
18
|
+
expect(result).toEqual({ command: "search", args: { query: "auth middleware" } })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("parses 'tasks' command", () => {
|
|
22
|
+
const result = parseProjectCliArgs(["tasks"])
|
|
23
|
+
expect(result).toEqual({ command: "tasks", args: {} })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("parses 'tasks <id>' command", () => {
|
|
27
|
+
const result = parseProjectCliArgs(["tasks", "t-1"])
|
|
28
|
+
expect(result).toEqual({ command: "task-detail", args: { taskId: "t-1" } })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("parses 'claim <description>' with flags", () => {
|
|
32
|
+
const result = parseProjectCliArgs(["claim", "implement auth", "--session", "c1", "--branch", "feat/auth"])
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
command: "claim",
|
|
35
|
+
args: { description: "implement auth", session: "c1", branch: "feat/auth" },
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("parses 'complete <id>'", () => {
|
|
40
|
+
const result = parseProjectCliArgs(["complete", "t-1"])
|
|
41
|
+
expect(result).toEqual({ command: "complete", args: { taskId: "t-1" } })
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test("parses 'delegate <request>'", () => {
|
|
45
|
+
const result = parseProjectCliArgs(["delegate", "ensure", "postgres", "running"])
|
|
46
|
+
expect(result).toEqual({ command: "delegate", args: { request: "ensure postgres running" } })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test("parses --project flag", () => {
|
|
50
|
+
const result = parseProjectCliArgs(["sessions", "--project", "p1"])
|
|
51
|
+
expect(result).toEqual({ command: "sessions", args: { workspaceId: "p1" } })
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("returns help for no args", () => {
|
|
55
|
+
const result = parseProjectCliArgs([])
|
|
56
|
+
expect(result.command).toBe("help")
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test("returns help for --help", () => {
|
|
60
|
+
const result = parseProjectCliArgs(["--help"])
|
|
61
|
+
expect(result.command).toBe("help")
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe("formatOutput", () => {
|
|
66
|
+
test("formats sessions as table when not --json", () => {
|
|
67
|
+
const output = formatOutput("sessions", [
|
|
68
|
+
{ chatId: "c1", intent: "auth work", status: "active", provider: "claude", branch: null, filesTouched: [], commandsRun: [], lastActivity: "2026-04-04T10:00:00Z" },
|
|
69
|
+
], false)
|
|
70
|
+
expect(output).toContain("c1")
|
|
71
|
+
expect(output).toContain("auth work")
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("formats as JSON when json=true", () => {
|
|
75
|
+
const data = [{ chatId: "c1" }]
|
|
76
|
+
const output = formatOutput("sessions", data, true)
|
|
77
|
+
expect(JSON.parse(output)).toEqual(data)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, test, expect, afterEach } from "bun:test"
|
|
2
|
+
import { mkdtemp, rm, stat, writeFile } from "node:fs/promises"
|
|
3
|
+
import os from "node:os"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { WorkspaceConfigManager } from "./workspace-config-manager"
|
|
6
|
+
import type { AgentConfig } from "../shared/agent-config-types"
|
|
7
|
+
|
|
8
|
+
const makeConfig = (id: string): AgentConfig => ({
|
|
9
|
+
id,
|
|
10
|
+
name: `Agent ${id}`,
|
|
11
|
+
description: `Test agent ${id}`,
|
|
12
|
+
provider: "claude",
|
|
13
|
+
model: "opus-4",
|
|
14
|
+
systemPrompt: "You are helpful.",
|
|
15
|
+
tools: ["bash", "read"],
|
|
16
|
+
temperature: 0.7,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe("WorkspaceConfigManager", () => {
|
|
20
|
+
let tmpDir: string
|
|
21
|
+
let mgr: WorkspaceConfigManager
|
|
22
|
+
|
|
23
|
+
const setup = async () => {
|
|
24
|
+
tmpDir = await mkdtemp(path.join(os.tmpdir(), "wcm-test-"))
|
|
25
|
+
mgr = new WorkspaceConfigManager(tmpDir)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
if (tmpDir) {
|
|
30
|
+
await rm(tmpDir, { recursive: true, force: true })
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("initWorkspaceDir creates .git, agents/, workflows/", async () => {
|
|
35
|
+
await setup()
|
|
36
|
+
const wsPath = await mgr.initWorkspaceDir("ws-1")
|
|
37
|
+
expect(wsPath).toBe(path.join(tmpDir, "ws-1"))
|
|
38
|
+
const gitStat = await stat(path.join(wsPath, ".git"))
|
|
39
|
+
expect(gitStat.isDirectory()).toBe(true)
|
|
40
|
+
const agentsStat = await stat(path.join(wsPath, "agents"))
|
|
41
|
+
expect(agentsStat.isDirectory()).toBe(true)
|
|
42
|
+
const workflowsStat = await stat(path.join(wsPath, "workflows"))
|
|
43
|
+
expect(workflowsStat.isDirectory()).toBe(true)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test("initWorkspaceDir is idempotent", async () => {
|
|
47
|
+
await setup()
|
|
48
|
+
await mgr.initWorkspaceDir("ws-2")
|
|
49
|
+
const wsPath = await mgr.initWorkspaceDir("ws-2")
|
|
50
|
+
expect(wsPath).toBe(path.join(tmpDir, "ws-2"))
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test("saveAgentConfig writes agents/<id>.yaml", async () => {
|
|
54
|
+
await setup()
|
|
55
|
+
await mgr.initWorkspaceDir("ws-3")
|
|
56
|
+
const config = makeConfig("agent-a")
|
|
57
|
+
await mgr.saveAgentConfig("ws-3", config)
|
|
58
|
+
const filePath = path.join(tmpDir, "ws-3", "agents", "agent-a.yaml")
|
|
59
|
+
const fileStat = await stat(filePath)
|
|
60
|
+
expect(fileStat.isFile()).toBe(true)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test("readAgentConfig roundtrips saved config", async () => {
|
|
64
|
+
await setup()
|
|
65
|
+
await mgr.initWorkspaceDir("ws-4")
|
|
66
|
+
const config = makeConfig("agent-b")
|
|
67
|
+
await mgr.saveAgentConfig("ws-4", config)
|
|
68
|
+
const read = await mgr.readAgentConfig("ws-4", "agent-b")
|
|
69
|
+
expect(read).toEqual(config)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test("readAgentConfig returns null for missing agent", async () => {
|
|
73
|
+
await setup()
|
|
74
|
+
await mgr.initWorkspaceDir("ws-5")
|
|
75
|
+
const read = await mgr.readAgentConfig("ws-5", "nope")
|
|
76
|
+
expect(read).toBeNull()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test("listAgentConfigs returns all saved, skips non-yaml", async () => {
|
|
80
|
+
await setup()
|
|
81
|
+
await mgr.initWorkspaceDir("ws-6")
|
|
82
|
+
await mgr.saveAgentConfig("ws-6", makeConfig("x"))
|
|
83
|
+
await mgr.saveAgentConfig("ws-6", makeConfig("y"))
|
|
84
|
+
// write a non-yaml file that should be skipped
|
|
85
|
+
await writeFile(path.join(tmpDir, "ws-6", "agents", "readme.txt"), "ignore me")
|
|
86
|
+
const configs = await mgr.listAgentConfigs("ws-6")
|
|
87
|
+
expect(configs).toHaveLength(2)
|
|
88
|
+
const ids = configs.map((c: AgentConfig) => c.id).sort()
|
|
89
|
+
expect(ids).toEqual(["x", "y"])
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("removeAgentConfig deletes the file", async () => {
|
|
93
|
+
await setup()
|
|
94
|
+
await mgr.initWorkspaceDir("ws-7")
|
|
95
|
+
await mgr.saveAgentConfig("ws-7", makeConfig("del-me"))
|
|
96
|
+
await mgr.removeAgentConfig("ws-7", "del-me")
|
|
97
|
+
const configs = await mgr.listAgentConfigs("ws-7")
|
|
98
|
+
expect(configs).toHaveLength(0)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test("commitConfig returns a non-empty hash", async () => {
|
|
102
|
+
await setup()
|
|
103
|
+
await mgr.initWorkspaceDir("ws-8")
|
|
104
|
+
await mgr.saveAgentConfig("ws-8", makeConfig("committed"))
|
|
105
|
+
const hash = await mgr.commitConfig("ws-8", "initial config")
|
|
106
|
+
expect(typeof hash).toBe("string")
|
|
107
|
+
expect(hash.length).toBeGreaterThanOrEqual(7)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { mkdir, rm, readdir, readFile, writeFile } from "node:fs/promises"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { $ } from "bun"
|
|
4
|
+
import yaml from "js-yaml"
|
|
5
|
+
import type { AgentConfig } from "../shared/agent-config-types"
|
|
6
|
+
|
|
7
|
+
const LOG_PREFIX = "[WorkspaceConfigManager]"
|
|
8
|
+
|
|
9
|
+
export class WorkspaceConfigManager {
|
|
10
|
+
constructor(private basePath: string) {}
|
|
11
|
+
|
|
12
|
+
async initWorkspaceDir(workspaceId: string): Promise<string> {
|
|
13
|
+
const wsPath = this.getWorkspacePath(workspaceId)
|
|
14
|
+
await mkdir(path.join(wsPath, "agents"), { recursive: true })
|
|
15
|
+
await mkdir(path.join(wsPath, "workflows"), { recursive: true })
|
|
16
|
+
await $`git -C ${wsPath} init`.quiet()
|
|
17
|
+
await $`git -C ${wsPath} config user.email "workspace@tinkaria.local"`.quiet()
|
|
18
|
+
await $`git -C ${wsPath} config user.name "Tinkaria Workspace"`.quiet()
|
|
19
|
+
return wsPath
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async saveAgentConfig(workspaceId: string, config: AgentConfig): Promise<void> {
|
|
23
|
+
const agentDir = path.join(this.getWorkspacePath(workspaceId), "agents")
|
|
24
|
+
await mkdir(agentDir, { recursive: true })
|
|
25
|
+
const filePath = this.getAgentConfigPath(workspaceId, config.id)
|
|
26
|
+
const content = yaml.dump(config, { sortKeys: true })
|
|
27
|
+
await writeFile(filePath, content, "utf-8")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async readAgentConfig(workspaceId: string, agentId: string): Promise<AgentConfig | null> {
|
|
31
|
+
const filePath = this.getAgentConfigPath(workspaceId, agentId)
|
|
32
|
+
try {
|
|
33
|
+
const content = await readFile(filePath, "utf-8")
|
|
34
|
+
return yaml.load(content) as AgentConfig
|
|
35
|
+
} catch (error) {
|
|
36
|
+
const msg = error instanceof Error ? error.message : String(error)
|
|
37
|
+
if (msg.includes("ENOENT") || error instanceof SyntaxError) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
console.warn(`${LOG_PREFIX} readAgentConfig failed: ${msg}`)
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async listAgentConfigs(workspaceId: string): Promise<AgentConfig[]> {
|
|
46
|
+
const agentsDir = path.join(this.getWorkspacePath(workspaceId), "agents")
|
|
47
|
+
const entries = await readdir(agentsDir)
|
|
48
|
+
const configs: AgentConfig[] = []
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (!entry.endsWith(".yaml")) continue
|
|
51
|
+
try {
|
|
52
|
+
const content = await readFile(path.join(agentsDir, entry), "utf-8")
|
|
53
|
+
const parsed = yaml.load(content) as AgentConfig
|
|
54
|
+
configs.push(parsed)
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const msg = error instanceof Error ? error.message : String(error)
|
|
57
|
+
console.warn(`${LOG_PREFIX} skipping unparseable ${entry}: ${msg}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return configs
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async removeAgentConfig(workspaceId: string, agentId: string): Promise<void> {
|
|
64
|
+
const filePath = this.getAgentConfigPath(workspaceId, agentId)
|
|
65
|
+
await rm(filePath, { force: true })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async commitConfig(workspaceId: string, message: string): Promise<string> {
|
|
69
|
+
const wsPath = this.getWorkspacePath(workspaceId)
|
|
70
|
+
await $`git -C ${wsPath} add -A`.quiet()
|
|
71
|
+
await $`git -C ${wsPath} commit -m ${message} --allow-empty`.quiet()
|
|
72
|
+
const result = await $`git -C ${wsPath} rev-parse HEAD`.quiet()
|
|
73
|
+
return result.text().trim()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private getWorkspacePath(workspaceId: string): string {
|
|
77
|
+
return path.join(this.basePath, workspaceId)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private getAgentConfigPath(workspaceId: string, agentId: string): string {
|
|
81
|
+
return path.join(this.getWorkspacePath(workspaceId), "agents", `${agentId}.yaml`)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
|
+
import { mkdtemp, rm, stat } from "node:fs/promises"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { tmpdir } from "node:os"
|
|
5
|
+
import { EventStore } from "./event-store"
|
|
6
|
+
import { WorkspaceConfigManager } from "./workspace-config-manager"
|
|
7
|
+
import { WorkspaceDirectoryPolicy } from "./workspace-directory-policy"
|
|
8
|
+
import type { AgentConfig } from "../shared/agent-config-types"
|
|
9
|
+
|
|
10
|
+
const tempDirs: string[] = []
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })))
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
async function createTempDir(prefix: string) {
|
|
17
|
+
const dir = await mkdtemp(join(tmpdir(), prefix))
|
|
18
|
+
tempDirs.push(dir)
|
|
19
|
+
return dir
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const makeConfig = (id: string): AgentConfig => ({
|
|
23
|
+
id,
|
|
24
|
+
name: `Agent ${id}`,
|
|
25
|
+
description: `Test agent ${id}`,
|
|
26
|
+
provider: "claude",
|
|
27
|
+
model: "opus-4",
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe("WorkspaceDirectoryPolicy", () => {
|
|
31
|
+
test("onWorkspaceOpened creates workspace dir", async () => {
|
|
32
|
+
const storeDir = await createTempDir("wdp-store-")
|
|
33
|
+
const wsBase = await createTempDir("wdp-ws-")
|
|
34
|
+
const store = new EventStore(storeDir)
|
|
35
|
+
await store.initialize()
|
|
36
|
+
const mgr = new WorkspaceConfigManager(wsBase)
|
|
37
|
+
const policy = new WorkspaceDirectoryPolicy(store, mgr)
|
|
38
|
+
|
|
39
|
+
await policy.onWorkspaceOpened("ws-test")
|
|
40
|
+
|
|
41
|
+
const gitStat = await stat(join(wsBase, "ws-test", ".git"))
|
|
42
|
+
expect(gitStat.isDirectory()).toBe(true)
|
|
43
|
+
const agentsStat = await stat(join(wsBase, "ws-test", "agents"))
|
|
44
|
+
expect(agentsStat.isDirectory()).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("onAgentConfigSaved writes YAML and records commit hash", async () => {
|
|
48
|
+
const storeDir = await createTempDir("wdp-store-")
|
|
49
|
+
const wsBase = await createTempDir("wdp-ws-")
|
|
50
|
+
const store = new EventStore(storeDir)
|
|
51
|
+
await store.initialize()
|
|
52
|
+
const workspace = await store.openProject("/tmp/wdp-project")
|
|
53
|
+
const mgr = new WorkspaceConfigManager(wsBase)
|
|
54
|
+
const policy = new WorkspaceDirectoryPolicy(store, mgr)
|
|
55
|
+
|
|
56
|
+
// Init the workspace dir first
|
|
57
|
+
await mgr.initWorkspaceDir(workspace.id)
|
|
58
|
+
|
|
59
|
+
// Save a config via EventStore so the record exists for commitAgentConfig
|
|
60
|
+
await store.saveAgentConfig(workspace.id, "agent-1", makeConfig("agent-1"))
|
|
61
|
+
|
|
62
|
+
// Now run the policy
|
|
63
|
+
await policy.onAgentConfigSaved(workspace.id, "agent-1", makeConfig("agent-1"))
|
|
64
|
+
|
|
65
|
+
// YAML file should exist
|
|
66
|
+
const yamlStat = await stat(join(wsBase, workspace.id, "agents", "agent-1.yaml"))
|
|
67
|
+
expect(yamlStat.isFile()).toBe(true)
|
|
68
|
+
|
|
69
|
+
// Commit hash should be recorded in EventStore
|
|
70
|
+
const record = store.state.agentConfigsByWorkspace.get(workspace.id)?.get("agent-1")
|
|
71
|
+
expect(record).toBeDefined()
|
|
72
|
+
expect(record!.lastCommitHash).toBeDefined()
|
|
73
|
+
expect(typeof record!.lastCommitHash).toBe("string")
|
|
74
|
+
expect(record!.lastCommitHash!.length).toBeGreaterThanOrEqual(7)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test("onAgentConfigRemoved deletes YAML from disk", async () => {
|
|
78
|
+
const storeDir = await createTempDir("wdp-store-")
|
|
79
|
+
const wsBase = await createTempDir("wdp-ws-")
|
|
80
|
+
const store = new EventStore(storeDir)
|
|
81
|
+
await store.initialize()
|
|
82
|
+
const mgr = new WorkspaceConfigManager(wsBase)
|
|
83
|
+
const policy = new WorkspaceDirectoryPolicy(store, mgr)
|
|
84
|
+
|
|
85
|
+
// Setup: init dir, write a config
|
|
86
|
+
await mgr.initWorkspaceDir("ws-rm")
|
|
87
|
+
await mgr.saveAgentConfig("ws-rm", makeConfig("agent-del"))
|
|
88
|
+
await mgr.commitConfig("ws-rm", "setup")
|
|
89
|
+
|
|
90
|
+
// Verify file exists
|
|
91
|
+
const yamlPath = join(wsBase, "ws-rm", "agents", "agent-del.yaml")
|
|
92
|
+
const before = await stat(yamlPath)
|
|
93
|
+
expect(before.isFile()).toBe(true)
|
|
94
|
+
|
|
95
|
+
// Remove via policy
|
|
96
|
+
await policy.onAgentConfigRemoved("ws-rm", "agent-del")
|
|
97
|
+
|
|
98
|
+
// File should be gone
|
|
99
|
+
let exists = true
|
|
100
|
+
try {
|
|
101
|
+
await stat(yamlPath)
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (err instanceof Error && "code" in err && (err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
104
|
+
exists = false
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
expect(exists).toBe(false)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { WorkspaceConfigManager } from "./workspace-config-manager"
|
|
2
|
+
import type { EventStore } from "./event-store"
|
|
3
|
+
import type { AgentConfig } from "../shared/agent-config-types"
|
|
4
|
+
|
|
5
|
+
const LOG_PREFIX = "[WorkspaceDirectoryPolicy]"
|
|
6
|
+
|
|
7
|
+
export class WorkspaceDirectoryPolicy {
|
|
8
|
+
constructor(
|
|
9
|
+
private store: EventStore,
|
|
10
|
+
private configManager: WorkspaceConfigManager,
|
|
11
|
+
private onStateChange?: () => void,
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
/** Called after workspace_opened — creates physical workspace directory + git init */
|
|
15
|
+
async onWorkspaceOpened(workspaceId: string): Promise<void> {
|
|
16
|
+
try {
|
|
17
|
+
await this.configManager.initWorkspaceDir(workspaceId)
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.warn(
|
|
20
|
+
`${LOG_PREFIX} Failed to init workspace dir:`,
|
|
21
|
+
err instanceof Error ? err.message : String(err),
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Called after agent_config_saved — writes YAML to disk + git commit, then records commit hash */
|
|
27
|
+
async onAgentConfigSaved(workspaceId: string, agentId: string, config: AgentConfig): Promise<void> {
|
|
28
|
+
try {
|
|
29
|
+
await this.configManager.saveAgentConfig(workspaceId, config)
|
|
30
|
+
const commitHash = await this.configManager.commitConfig(
|
|
31
|
+
workspaceId,
|
|
32
|
+
`Save agent config: ${config.name}`,
|
|
33
|
+
)
|
|
34
|
+
await this.store.commitAgentConfig(workspaceId, agentId, commitHash)
|
|
35
|
+
this.onStateChange?.()
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.warn(
|
|
38
|
+
`${LOG_PREFIX} Failed to commit agent config:`,
|
|
39
|
+
err instanceof Error ? err.message : String(err),
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Called after agent_config_removed — deletes YAML from disk + git commit */
|
|
45
|
+
async onAgentConfigRemoved(workspaceId: string, agentId: string): Promise<void> {
|
|
46
|
+
try {
|
|
47
|
+
await this.configManager.removeAgentConfig(workspaceId, agentId)
|
|
48
|
+
await this.configManager.commitConfig(workspaceId, `Remove agent config: ${agentId}`)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.warn(
|
|
51
|
+
`${LOG_PREFIX} Failed to remove agent config from disk:`,
|
|
52
|
+
err instanceof Error ? err.message : String(err),
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface AgentConfig {
|
|
2
|
+
id: string
|
|
3
|
+
name: string
|
|
4
|
+
description: string
|
|
5
|
+
provider: "claude" | "codex"
|
|
6
|
+
model: string
|
|
7
|
+
systemPrompt?: string
|
|
8
|
+
tools?: string[]
|
|
9
|
+
temperature?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AgentConfigRecord {
|
|
13
|
+
id: string
|
|
14
|
+
workspaceId: string
|
|
15
|
+
config: AgentConfig
|
|
16
|
+
createdAt: number
|
|
17
|
+
updatedAt: number
|
|
18
|
+
lastCommitHash?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AgentConfigSnapshot {
|
|
22
|
+
workspaceId: string
|
|
23
|
+
configs: AgentConfigRecord[]
|
|
24
|
+
lastUpdated: string
|
|
25
|
+
}
|