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,640 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test"
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import process from "node:process"
|
|
6
|
+
import { createServer } from "node:net"
|
|
7
|
+
import { APP_NAME, getDataDir } from "../shared/branding"
|
|
8
|
+
import {
|
|
9
|
+
buildStageProbeScript,
|
|
10
|
+
getJourneyStage,
|
|
11
|
+
HOME_TO_FORK_DIALOG_JOURNEY,
|
|
12
|
+
HOME_TO_MERGE_DIALOG_JOURNEY,
|
|
13
|
+
HOME_TO_NEW_CHAT_JOURNEY,
|
|
14
|
+
matchesJourneyRoute,
|
|
15
|
+
type StageProbeResult,
|
|
16
|
+
} from "./journey-verification"
|
|
17
|
+
|
|
18
|
+
interface AgentBrowserEnvelope<T> {
|
|
19
|
+
success: boolean
|
|
20
|
+
data: T | null
|
|
21
|
+
error: string | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface AgentBrowserEvalResult<T> {
|
|
25
|
+
origin: string
|
|
26
|
+
result: T
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface AgentBrowserUrlResult {
|
|
30
|
+
url: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface RunningDevServer {
|
|
34
|
+
clientPort: number
|
|
35
|
+
serverPort: number
|
|
36
|
+
process: Bun.Subprocess<"ignore", "pipe", "pipe">
|
|
37
|
+
stdoutText: Promise<string>
|
|
38
|
+
stderrText: Promise<string>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface FixtureEnvironment {
|
|
42
|
+
homeDir: string
|
|
43
|
+
fixtureProjectDir: string
|
|
44
|
+
fixtureProjectTitle: string
|
|
45
|
+
dataDir: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const activeServers: RunningDevServer[] = []
|
|
49
|
+
const activeHomes: string[] = []
|
|
50
|
+
const activeAgentBrowserSessions = new Set<string>()
|
|
51
|
+
|
|
52
|
+
function getAgentBrowserEnv(session: string): Record<string, string> {
|
|
53
|
+
return {
|
|
54
|
+
...process.env,
|
|
55
|
+
AGENT_BROWSER_SESSION: session,
|
|
56
|
+
AGENT_BROWSER_DEFAULT_TIMEOUT: "15000",
|
|
57
|
+
} as Record<string, string>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function decodeBytes(bytes: Uint8Array<ArrayBufferLike>) {
|
|
61
|
+
return new TextDecoder().decode(bytes)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function closeAllAgentBrowsers() {
|
|
65
|
+
Bun.spawnSync(["agent-browser", "close", "--all"], {
|
|
66
|
+
env: process.env,
|
|
67
|
+
stdout: "ignore",
|
|
68
|
+
stderr: "ignore",
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function runAgentBrowserJson<T>(session: string, args: string[]): T {
|
|
73
|
+
const result = Bun.spawnSync(["agent-browser", "--json", ...args], {
|
|
74
|
+
env: getAgentBrowserEnv(session),
|
|
75
|
+
stdout: "pipe",
|
|
76
|
+
stderr: "pipe",
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const stdout = decodeBytes(result.stdout)
|
|
80
|
+
const stderr = decodeBytes(result.stderr)
|
|
81
|
+
let parsed: AgentBrowserEnvelope<T> | null = null
|
|
82
|
+
|
|
83
|
+
if (stdout.trim()) {
|
|
84
|
+
parsed = JSON.parse(stdout) as AgentBrowserEnvelope<T>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (result.exitCode !== 0 || !parsed?.success || parsed.data === null) {
|
|
88
|
+
const details = [
|
|
89
|
+
`agent-browser ${args.join(" ")} failed`,
|
|
90
|
+
parsed?.error ? `error: ${parsed.error}` : null,
|
|
91
|
+
stderr.trim() ? `stderr: ${stderr.trim()}` : null,
|
|
92
|
+
stdout.trim() ? `stdout: ${stdout.trim()}` : null,
|
|
93
|
+
].filter(Boolean).join("\n")
|
|
94
|
+
throw new Error(details)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return parsed.data
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function readStream(stream: ReadableStream<Uint8Array> | null) {
|
|
101
|
+
if (!stream) return ""
|
|
102
|
+
return await new Response(stream).text()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function isPortAvailable(port: number) {
|
|
106
|
+
return await new Promise<boolean>((resolve) => {
|
|
107
|
+
const server = createServer()
|
|
108
|
+
|
|
109
|
+
server.once("error", () => {
|
|
110
|
+
resolve(false)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
server.listen(port, "127.0.0.1", () => {
|
|
114
|
+
server.close(() => resolve(true))
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function findAvailablePortPair() {
|
|
120
|
+
const basePort = 5600 + Math.floor(Math.random() * 200) * 2
|
|
121
|
+
|
|
122
|
+
for (let offset = 0; offset < 400; offset += 2) {
|
|
123
|
+
const clientPort = basePort + offset
|
|
124
|
+
const serverPort = clientPort + 1
|
|
125
|
+
|
|
126
|
+
if (await isPortAvailable(clientPort) && await isPortAvailable(serverPort)) {
|
|
127
|
+
return { clientPort, serverPort }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
throw new Error("Unable to find an available client/server port pair for the journey test")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function waitFor<T>(
|
|
135
|
+
label: string,
|
|
136
|
+
fn: () => Promise<T | false>,
|
|
137
|
+
timeoutMs = 30_000,
|
|
138
|
+
pollMs = 250,
|
|
139
|
+
): Promise<T> {
|
|
140
|
+
const deadline = Date.now() + timeoutMs
|
|
141
|
+
|
|
142
|
+
while (Date.now() < deadline) {
|
|
143
|
+
const value = await fn()
|
|
144
|
+
if (value !== false) {
|
|
145
|
+
return value
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await Bun.sleep(pollMs)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
throw new Error(`Timed out waiting for ${label}`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function createFixtureEnvironment(): Promise<FixtureEnvironment> {
|
|
155
|
+
const homeDir = await mkdtemp(path.join(tmpdir(), "tinkaria-journey-"))
|
|
156
|
+
const fixtureProjectTitle = "journey-fixture"
|
|
157
|
+
const fixtureProjectDir = path.join(homeDir, "workspace", fixtureProjectTitle)
|
|
158
|
+
const codexDir = path.join(homeDir, ".codex")
|
|
159
|
+
const configPath = path.join(codexDir, "config.toml")
|
|
160
|
+
|
|
161
|
+
await mkdir(fixtureProjectDir, { recursive: true })
|
|
162
|
+
await mkdir(codexDir, { recursive: true })
|
|
163
|
+
await writeFile(path.join(fixtureProjectDir, "README.md"), "# journey fixture\n")
|
|
164
|
+
await writeFile(configPath, `[projects."${fixtureProjectDir}"]\n`)
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
homeDir,
|
|
168
|
+
fixtureProjectDir,
|
|
169
|
+
fixtureProjectTitle,
|
|
170
|
+
dataDir: getDataDir(homeDir, { TINKARIA_RUNTIME_PROFILE: "dev" }),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function collectServerLogs(server: RunningDevServer) {
|
|
175
|
+
const [stdout, stderr] = await Promise.all([server.stdoutText, server.stderrText])
|
|
176
|
+
return [stdout.trim(), stderr.trim()].filter(Boolean).join("\n")
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function stopServer(server: RunningDevServer) {
|
|
180
|
+
if (server.process.exitCode === null) {
|
|
181
|
+
server.process.kill("SIGTERM")
|
|
182
|
+
await Promise.race([
|
|
183
|
+
server.process.exited,
|
|
184
|
+
Bun.sleep(2_000).then(() => {
|
|
185
|
+
if (server.process.exitCode === null) {
|
|
186
|
+
server.process.kill("SIGKILL")
|
|
187
|
+
}
|
|
188
|
+
}),
|
|
189
|
+
])
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await Promise.allSettled([server.stdoutText, server.stderrText])
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function startIsolatedDevServer(homeDir: string, options?: { clientPort?: number }): Promise<RunningDevServer> {
|
|
196
|
+
const ports = options?.clientPort === undefined
|
|
197
|
+
? await findAvailablePortPair()
|
|
198
|
+
: { clientPort: options.clientPort, serverPort: options.clientPort + 1 }
|
|
199
|
+
const { clientPort, serverPort } = ports
|
|
200
|
+
const processEnv = {
|
|
201
|
+
...process.env,
|
|
202
|
+
HOME: homeDir,
|
|
203
|
+
TINKARIA_RUNTIME_PROFILE: "dev",
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const proc = Bun.spawn([process.execPath, "run", "./scripts/dev.ts", "--port", String(clientPort), "--no-open", "--strict-port"], {
|
|
207
|
+
cwd: process.cwd(),
|
|
208
|
+
env: processEnv,
|
|
209
|
+
stdin: "ignore",
|
|
210
|
+
stdout: "pipe",
|
|
211
|
+
stderr: "pipe",
|
|
212
|
+
})
|
|
213
|
+
const server: RunningDevServer = {
|
|
214
|
+
clientPort,
|
|
215
|
+
serverPort,
|
|
216
|
+
process: proc,
|
|
217
|
+
stdoutText: readStream(proc.stdout),
|
|
218
|
+
stderrText: readStream(proc.stderr),
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
await waitFor("isolated dev server readiness", async () => {
|
|
223
|
+
if (proc.exitCode !== null) {
|
|
224
|
+
throw new Error(`dev server exited before readiness with code ${String(proc.exitCode)}`)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const [clientResponse, healthResponse] = await Promise.all([
|
|
229
|
+
fetch(`http://127.0.0.1:${clientPort}/`),
|
|
230
|
+
fetch(`http://127.0.0.1:${serverPort}/health`),
|
|
231
|
+
])
|
|
232
|
+
|
|
233
|
+
if (!clientResponse.ok || !healthResponse.ok) {
|
|
234
|
+
return false
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const healthBody = await healthResponse.json() as { ok?: boolean }
|
|
238
|
+
return healthBody.ok === true ? true : false
|
|
239
|
+
} catch {
|
|
240
|
+
return false
|
|
241
|
+
}
|
|
242
|
+
}, 45_000)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
await stopServer(server)
|
|
245
|
+
const logs = await collectServerLogs(server)
|
|
246
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
247
|
+
throw new Error(`${message}\n${logs}`)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
activeServers.push(server)
|
|
251
|
+
return server
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function extractPathname(rawUrl: string) {
|
|
255
|
+
return new URL(rawUrl).pathname
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function getBrowserUrl(session: string) {
|
|
259
|
+
return runAgentBrowserJson<AgentBrowserUrlResult>(session, ["get", "url"]).url
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function evalBrowser<T>(session: string, script: string) {
|
|
263
|
+
return runAgentBrowserJson<AgentBrowserEvalResult<T>>(session, ["eval", script]).result
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function setBrowserOffline(session: string, offline: boolean) {
|
|
267
|
+
runAgentBrowserJson<Record<string, unknown>>(session, ["set", "offline", offline ? "on" : "off"])
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function waitForStage(session: string, stageId: string) {
|
|
271
|
+
const stage = getJourneyStage(stageId)
|
|
272
|
+
return await waitFor(`browser stage ${stageId}`, async () => {
|
|
273
|
+
const url = getBrowserUrl(session)
|
|
274
|
+
if (!matchesJourneyRoute(extractPathname(url), stage.route)) {
|
|
275
|
+
return false
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const probe = evalBrowser<StageProbeResult>(session, buildStageProbeScript(stage))
|
|
279
|
+
if (probe.missing.length > 0) {
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return { url, probe }
|
|
284
|
+
}, 30_000)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function readJsonLines(filePath: string) {
|
|
288
|
+
try {
|
|
289
|
+
const text = await readFile(filePath, "utf8")
|
|
290
|
+
return text
|
|
291
|
+
.split("\n")
|
|
292
|
+
.map((line) => line.trim())
|
|
293
|
+
.filter(Boolean)
|
|
294
|
+
.map((line) => JSON.parse(line) as Record<string, unknown>)
|
|
295
|
+
} catch (error) {
|
|
296
|
+
const errorCode = error instanceof Error && "code" in error ? error.code : null
|
|
297
|
+
if (errorCode === "ENOENT") {
|
|
298
|
+
return []
|
|
299
|
+
}
|
|
300
|
+
throw error
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function openNewChatFromHomepage(session: string, fixture: FixtureEnvironment) {
|
|
305
|
+
const homeStage = await waitForStage(session, "home.ready")
|
|
306
|
+
expect(homeStage.probe.missing).toEqual([])
|
|
307
|
+
expect(homeStage.probe.c3ByUiId["home.page"]).toBe("c3-117")
|
|
308
|
+
|
|
309
|
+
const overviewDetails = evalBrowser<{
|
|
310
|
+
containsTitle: boolean
|
|
311
|
+
containsPath: boolean
|
|
312
|
+
startButtonLabel: string | null
|
|
313
|
+
}>(session, `(() => {
|
|
314
|
+
const overview = document.querySelector('[data-ui-id="home.project-overview"]');
|
|
315
|
+
const startButton = overview?.querySelector('[data-ui-id="home.project-secondary.action"]');
|
|
316
|
+
const text = overview?.textContent ?? "";
|
|
317
|
+
return {
|
|
318
|
+
containsTitle: text.includes(${JSON.stringify(fixture.fixtureProjectTitle)}),
|
|
319
|
+
containsPath: text.includes(${JSON.stringify(fixture.fixtureProjectDir)}),
|
|
320
|
+
startButtonLabel: startButton?.textContent ? startButton.textContent.trim() : null,
|
|
321
|
+
};
|
|
322
|
+
})()`)
|
|
323
|
+
expect(overviewDetails).toEqual({
|
|
324
|
+
containsTitle: true,
|
|
325
|
+
containsPath: false,
|
|
326
|
+
startButtonLabel: "Start First Task",
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
const clickResult = evalBrowser<{ clicked: boolean; label: string | null }>(session, `(() => {
|
|
330
|
+
const button = document.querySelector('[data-ui-id="home.project-overview"] [data-ui-id="home.project-secondary.action"]');
|
|
331
|
+
if (!(button instanceof HTMLElement)) {
|
|
332
|
+
return { clicked: false, label: null };
|
|
333
|
+
}
|
|
334
|
+
const label = button.textContent ? button.textContent.trim() : null;
|
|
335
|
+
button.click();
|
|
336
|
+
return { clicked: true, label };
|
|
337
|
+
})()`)
|
|
338
|
+
expect(clickResult).toEqual({ clicked: true, label: "Start First Task" })
|
|
339
|
+
|
|
340
|
+
const chatStage = await waitForStage(session, "chat.ready")
|
|
341
|
+
expect(chatStage.probe.missing).toEqual([])
|
|
342
|
+
expect(chatStage.probe.c3ByUiId["chat.page"]).toBe("c3-110")
|
|
343
|
+
expect(chatStage.probe.c3ByUiId["transcript.message-list"]).toBe("c3-111")
|
|
344
|
+
expect(chatStage.probe.c3ByUiId["chat.composer"]).toBe("c3-112")
|
|
345
|
+
|
|
346
|
+
const persistedState = await waitFor("persisted project/chat events", async () => {
|
|
347
|
+
const [projectEvents, chatEvents] = await Promise.all([
|
|
348
|
+
readJsonLines(path.join(fixture.dataDir, "projects.jsonl")),
|
|
349
|
+
readJsonLines(path.join(fixture.dataDir, "chats.jsonl")),
|
|
350
|
+
])
|
|
351
|
+
|
|
352
|
+
const projectOpened = projectEvents.find((event) =>
|
|
353
|
+
event.type === "workspace_opened" && event.localPath === fixture.fixtureProjectDir
|
|
354
|
+
)
|
|
355
|
+
if (!projectOpened || typeof projectOpened.workspaceId !== "string") {
|
|
356
|
+
return false
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const chatCreated = chatEvents.find((event) =>
|
|
360
|
+
event.type === "chat_created" && event.workspaceId === projectOpened.workspaceId && typeof event.chatId === "string"
|
|
361
|
+
)
|
|
362
|
+
if (!chatCreated || typeof chatCreated.chatId !== "string") {
|
|
363
|
+
return false
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
workspaceId: projectOpened.workspaceId,
|
|
368
|
+
chatId: chatCreated.chatId,
|
|
369
|
+
}
|
|
370
|
+
}, 10_000)
|
|
371
|
+
|
|
372
|
+
expect(extractPathname(chatStage.url)).toBe(`/chat/${persistedState.chatId}`)
|
|
373
|
+
return { chatStage, persistedState }
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
afterEach(async () => {
|
|
377
|
+
for (const session of activeAgentBrowserSessions) {
|
|
378
|
+
try {
|
|
379
|
+
setBrowserOffline(session, false)
|
|
380
|
+
} catch {
|
|
381
|
+
// Best effort only; failures here should not hide the real test failure.
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
runAgentBrowserJson<{ closed: boolean }>(session, ["close"])
|
|
385
|
+
} catch {
|
|
386
|
+
// Best effort only; failures here should not hide the real test failure.
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
activeAgentBrowserSessions.clear()
|
|
390
|
+
|
|
391
|
+
while (activeServers.length > 0) {
|
|
392
|
+
const server = activeServers.pop()
|
|
393
|
+
if (server) {
|
|
394
|
+
await stopServer(server)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
while (activeHomes.length > 0) {
|
|
399
|
+
const homeDir = activeHomes.pop()
|
|
400
|
+
if (homeDir) {
|
|
401
|
+
await rm(homeDir, { recursive: true, force: true })
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
const hasAgentBrowser = Boolean(Bun.which("agent-browser"))
|
|
407
|
+
|
|
408
|
+
describe("journey verification inventory", () => {
|
|
409
|
+
test("documents the first homepage -> new task journey in stable screen stages", () => {
|
|
410
|
+
expect(structuredClone(HOME_TO_NEW_CHAT_JOURNEY)).toMatchObject({
|
|
411
|
+
id: "homepage-to-new-chat",
|
|
412
|
+
stages: [
|
|
413
|
+
{
|
|
414
|
+
id: "home.ready",
|
|
415
|
+
owners: ["c3-117"],
|
|
416
|
+
route: { kind: "exact", value: "/" },
|
|
417
|
+
requiredUiIds: expect.arrayContaining(["home.page", "home.project-overview", "home.project-secondary.action"]),
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
id: "chat.ready",
|
|
421
|
+
owners: ["c3-110", "c3-111", "c3-112"],
|
|
422
|
+
route: { kind: "prefix", value: "/chat/" },
|
|
423
|
+
requiredUiIds: expect.arrayContaining(["chat.page", "chat.navbar", "transcript.message-list", "chat.composer"]),
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
test("documents fork and merge dialog journeys as deterministic follow-on stages", () => {
|
|
430
|
+
expect(structuredClone(HOME_TO_FORK_DIALOG_JOURNEY)).toMatchObject({
|
|
431
|
+
id: "homepage-to-fork-dialog",
|
|
432
|
+
stages: [
|
|
433
|
+
expect.anything(),
|
|
434
|
+
expect.anything(),
|
|
435
|
+
{
|
|
436
|
+
id: "fork-dialog.open",
|
|
437
|
+
owners: ["c3-110"],
|
|
438
|
+
route: { kind: "prefix", value: "/chat/" },
|
|
439
|
+
requiredUiIds: expect.arrayContaining([
|
|
440
|
+
"chat.fork-session.dialog",
|
|
441
|
+
"chat.fork-session.context.input",
|
|
442
|
+
"chat.fork-session.submit.action",
|
|
443
|
+
]),
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
})
|
|
447
|
+
expect(structuredClone(HOME_TO_MERGE_DIALOG_JOURNEY)).toMatchObject({
|
|
448
|
+
id: "homepage-to-merge-dialog",
|
|
449
|
+
stages: [
|
|
450
|
+
expect.anything(),
|
|
451
|
+
expect.anything(),
|
|
452
|
+
{
|
|
453
|
+
id: "merge-dialog.open",
|
|
454
|
+
owners: ["c3-110"],
|
|
455
|
+
route: { kind: "prefix", value: "/chat/" },
|
|
456
|
+
requiredUiIds: expect.arrayContaining([
|
|
457
|
+
"chat.merge-session.dialog",
|
|
458
|
+
"chat.merge-session.sessions.list",
|
|
459
|
+
"chat.merge-session.submit.action",
|
|
460
|
+
]),
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
})
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
describe.serial("browser integration", () => {
|
|
467
|
+
test.skipIf(!hasAgentBrowser)("verifies the first browser journey against a real isolated dev instance", async () => {
|
|
468
|
+
closeAllAgentBrowsers()
|
|
469
|
+
const session = `journey-${crypto.randomUUID()}`
|
|
470
|
+
activeAgentBrowserSessions.add(session)
|
|
471
|
+
|
|
472
|
+
const fixture = await createFixtureEnvironment()
|
|
473
|
+
activeHomes.push(fixture.homeDir)
|
|
474
|
+
|
|
475
|
+
const server = await startIsolatedDevServer(fixture.homeDir)
|
|
476
|
+
setBrowserOffline(session, false)
|
|
477
|
+
runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
|
|
478
|
+
|
|
479
|
+
await openNewChatFromHomepage(session, fixture)
|
|
480
|
+
}, 90_000)
|
|
481
|
+
|
|
482
|
+
test.skipIf(!hasAgentBrowser)("verifies fork and merge dialog journeys against a real isolated dev instance", async () => {
|
|
483
|
+
closeAllAgentBrowsers()
|
|
484
|
+
const session = `journey-${crypto.randomUUID()}`
|
|
485
|
+
activeAgentBrowserSessions.add(session)
|
|
486
|
+
|
|
487
|
+
const fixture = await createFixtureEnvironment()
|
|
488
|
+
activeHomes.push(fixture.homeDir)
|
|
489
|
+
|
|
490
|
+
const server = await startIsolatedDevServer(fixture.homeDir)
|
|
491
|
+
setBrowserOffline(session, false)
|
|
492
|
+
runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
|
|
493
|
+
|
|
494
|
+
await openNewChatFromHomepage(session, fixture)
|
|
495
|
+
|
|
496
|
+
evalBrowser<{ clicked: boolean }>(session, `(() => {
|
|
497
|
+
const button = document.querySelector('[data-ui-id="chat.navbar.fork-session.action"]');
|
|
498
|
+
if (!(button instanceof HTMLElement)) return { clicked: false };
|
|
499
|
+
button.click();
|
|
500
|
+
return { clicked: true };
|
|
501
|
+
})()`)
|
|
502
|
+
|
|
503
|
+
const forkStage = await waitForStage(session, "fork-dialog.open")
|
|
504
|
+
expect(forkStage.probe.missing).toEqual([])
|
|
505
|
+
expect(forkStage.probe.c3ByUiId["chat.fork-session.dialog"]).toBe("c3-110")
|
|
506
|
+
|
|
507
|
+
evalBrowser<{ clicked: boolean }>(session, `(() => {
|
|
508
|
+
const button = document.querySelector('[data-ui-id="chat.fork-session.cancel.action"]');
|
|
509
|
+
if (!(button instanceof HTMLElement)) return { clicked: false };
|
|
510
|
+
button.click();
|
|
511
|
+
return { clicked: true };
|
|
512
|
+
})()`)
|
|
513
|
+
|
|
514
|
+
await waitForStage(session, "chat.ready")
|
|
515
|
+
|
|
516
|
+
evalBrowser<{ clicked: boolean }>(session, `(() => {
|
|
517
|
+
const button = document.querySelector('[data-ui-id="chat.navbar.merge-session.action"]');
|
|
518
|
+
if (!(button instanceof HTMLElement)) return { clicked: false };
|
|
519
|
+
button.click();
|
|
520
|
+
return { clicked: true };
|
|
521
|
+
})()`)
|
|
522
|
+
|
|
523
|
+
const mergeStage = await waitForStage(session, "merge-dialog.open")
|
|
524
|
+
expect(mergeStage.probe.missing).toEqual([])
|
|
525
|
+
expect(mergeStage.probe.c3ByUiId["chat.merge-session.dialog"]).toBe("c3-110")
|
|
526
|
+
}, 90_000)
|
|
527
|
+
|
|
528
|
+
test.skipIf(!hasAgentBrowser)("creates a new project from the homepage modal and lands in chat", async () => {
|
|
529
|
+
closeAllAgentBrowsers()
|
|
530
|
+
const session = `journey-${crypto.randomUUID()}`
|
|
531
|
+
activeAgentBrowserSessions.add(session)
|
|
532
|
+
|
|
533
|
+
const fixture = await createFixtureEnvironment()
|
|
534
|
+
activeHomes.push(fixture.homeDir)
|
|
535
|
+
|
|
536
|
+
const server = await startIsolatedDevServer(fixture.homeDir)
|
|
537
|
+
setBrowserOffline(session, false)
|
|
538
|
+
runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
|
|
539
|
+
|
|
540
|
+
await waitForStage(session, "home.ready")
|
|
541
|
+
|
|
542
|
+
evalBrowser<{ clicked: boolean }>(session, `(() => {
|
|
543
|
+
const button = document.querySelector('[data-ui-id="home.add-project.action"]');
|
|
544
|
+
if (!(button instanceof HTMLElement)) return { clicked: false };
|
|
545
|
+
button.click();
|
|
546
|
+
return { clicked: true };
|
|
547
|
+
})()`)
|
|
548
|
+
|
|
549
|
+
await waitFor("add project dialog", async () => {
|
|
550
|
+
const visible = evalBrowser<boolean>(session, `Boolean(document.querySelector('[data-ui-id="home.add-project.dialog"]'))`)
|
|
551
|
+
return visible ? true : false
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
const projectName = "Coverage Project"
|
|
555
|
+
const fillResult = evalBrowser<{ filled: boolean }>(session, `(() => {
|
|
556
|
+
const input = document.querySelector('input[placeholder="Project name"]');
|
|
557
|
+
if (!(input instanceof HTMLInputElement)) return { filled: false };
|
|
558
|
+
const valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
559
|
+
if (!valueSetter) return { filled: false };
|
|
560
|
+
input.focus();
|
|
561
|
+
valueSetter.call(input, ${JSON.stringify(projectName)});
|
|
562
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
563
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
564
|
+
return { filled: true };
|
|
565
|
+
})()`)
|
|
566
|
+
expect(fillResult).toEqual({ filled: true })
|
|
567
|
+
|
|
568
|
+
await waitFor("create project submit enabled", async () => {
|
|
569
|
+
const submitEnabled = evalBrowser<boolean>(session, `(() => {
|
|
570
|
+
const button = Array.from(document.querySelectorAll('button')).find((candidate) => candidate.textContent?.trim() === "Create");
|
|
571
|
+
return button instanceof HTMLButtonElement && button.disabled === false;
|
|
572
|
+
})()`)
|
|
573
|
+
return submitEnabled ? true : false
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
const submitResult = evalBrowser<{ clicked: boolean }>(session, `(() => {
|
|
577
|
+
const button = Array.from(document.querySelectorAll('button')).find((candidate) => candidate.textContent?.trim() === "Create");
|
|
578
|
+
if (!(button instanceof HTMLElement)) return { clicked: false };
|
|
579
|
+
button.click();
|
|
580
|
+
return { clicked: true };
|
|
581
|
+
})()`)
|
|
582
|
+
expect(submitResult).toEqual({ clicked: true })
|
|
583
|
+
|
|
584
|
+
const chatStage = await waitForStage(session, "chat.ready")
|
|
585
|
+
expect(chatStage.probe.missing).toEqual([])
|
|
586
|
+
|
|
587
|
+
await stopServer(server)
|
|
588
|
+
activeServers.splice(activeServers.indexOf(server), 1)
|
|
589
|
+
|
|
590
|
+
const createdPath = path.join(fixture.homeDir, APP_NAME, "coverage-project")
|
|
591
|
+
const snapshot = JSON.parse(await readFile(path.join(fixture.dataDir, "snapshot.json"), "utf8")) as {
|
|
592
|
+
workspaces?: Array<{ localPath?: string }>
|
|
593
|
+
}
|
|
594
|
+
expect(snapshot.workspaces?.some((ws) => ws.localPath === createdPath)).toBe(true)
|
|
595
|
+
}, 90_000)
|
|
596
|
+
|
|
597
|
+
test.skip("shows reconnecting and reconnected composer states after offline recovery", async () => {
|
|
598
|
+
closeAllAgentBrowsers()
|
|
599
|
+
const session = `journey-${crypto.randomUUID()}`
|
|
600
|
+
activeAgentBrowserSessions.add(session)
|
|
601
|
+
|
|
602
|
+
const fixture = await createFixtureEnvironment()
|
|
603
|
+
activeHomes.push(fixture.homeDir)
|
|
604
|
+
|
|
605
|
+
const server = await startIsolatedDevServer(fixture.homeDir)
|
|
606
|
+
setBrowserOffline(session, false)
|
|
607
|
+
runAgentBrowserJson<Record<string, unknown>>(session, ["open", `http://127.0.0.1:${server.clientPort}/`])
|
|
608
|
+
|
|
609
|
+
await openNewChatFromHomepage(session, fixture)
|
|
610
|
+
|
|
611
|
+
setBrowserOffline(session, true)
|
|
612
|
+
|
|
613
|
+
const reconnecting = await waitFor("reconnecting composer badge", async () => {
|
|
614
|
+
const badge = evalBrowser<boolean>(session, `!!document.querySelector('[data-ui-id="chat.composer.connection.section"] .animate-spin')`)
|
|
615
|
+
return badge || false
|
|
616
|
+
}, 20_000)
|
|
617
|
+
expect(reconnecting).toBe(true)
|
|
618
|
+
|
|
619
|
+
setBrowserOffline(session, false)
|
|
620
|
+
|
|
621
|
+
const recovered = await waitFor("chat composer recovery", async () => {
|
|
622
|
+
const state = evalBrowser<{
|
|
623
|
+
connectionLabel: string
|
|
624
|
+
submitDisabled: boolean | null
|
|
625
|
+
}>(session, `(() => {
|
|
626
|
+
const connectionLabel = (document.querySelector('[data-ui-id="chat.composer.connection.section"]')?.textContent ?? "").trim();
|
|
627
|
+
const submit = document.querySelector('[data-ui-id="chat.composer.submit.action"]');
|
|
628
|
+
return {
|
|
629
|
+
connectionLabel,
|
|
630
|
+
submitDisabled: submit instanceof HTMLButtonElement ? submit.disabled : null,
|
|
631
|
+
};
|
|
632
|
+
})()`)
|
|
633
|
+
if (state.connectionLabel.length > 0) return false
|
|
634
|
+
if (state.submitDisabled !== false) return false
|
|
635
|
+
return state
|
|
636
|
+
}, 20_000)
|
|
637
|
+
expect(recovered.submitDisabled).toBe(false)
|
|
638
|
+
}, 90_000)
|
|
639
|
+
})
|
|
640
|
+
})
|