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,162 @@
|
|
|
1
|
+
import { jetstream, DeliverPolicy } from "@nats-io/jetstream"
|
|
2
|
+
import type { ConsumerMessages } from "@nats-io/jetstream"
|
|
3
|
+
import type { NatsConnection } from "@nats-io/transport-node"
|
|
4
|
+
import { Kvm } from "@nats-io/kv"
|
|
5
|
+
import { RUNNER_EVENTS_STREAM, type RunnerTurnEvent } from "../shared/runner-protocol"
|
|
6
|
+
import type { SessionStatus, TranscriptEntry, AgentProvider } from "../shared/types"
|
|
7
|
+
import { LOG_PREFIX } from "../shared/branding"
|
|
8
|
+
|
|
9
|
+
const decoder = new TextDecoder()
|
|
10
|
+
const encoder = new TextEncoder()
|
|
11
|
+
|
|
12
|
+
// ── Store interface (subset of EventStore) ──────────────────────────
|
|
13
|
+
|
|
14
|
+
export interface TranscriptConsumerStore {
|
|
15
|
+
appendMessage(chatId: string, entry: TranscriptEntry): void
|
|
16
|
+
recordTurnFinished(chatId: string): Promise<void>
|
|
17
|
+
recordTurnFailed(chatId: string, error: string): Promise<void>
|
|
18
|
+
recordTurnCancelled(chatId: string): Promise<void>
|
|
19
|
+
setSessionToken(chatId: string, token: string | null): Promise<void>
|
|
20
|
+
renameChat(chatId: string, title: string): Promise<void>
|
|
21
|
+
setChatProvider(chatId: string, provider: AgentProvider): Promise<void>
|
|
22
|
+
setPlanMode(chatId: string, planMode: boolean): Promise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ── Options ─────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export interface TranscriptConsumerOptions {
|
|
28
|
+
nc: NatsConnection
|
|
29
|
+
store: TranscriptConsumerStore
|
|
30
|
+
onStateChange: () => void
|
|
31
|
+
onMessageAppended?: (chatId: string, entry: TranscriptEntry) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── TranscriptConsumer ──────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
export class TranscriptConsumer {
|
|
37
|
+
private readonly nc: NatsConnection
|
|
38
|
+
private readonly store: TranscriptConsumerStore
|
|
39
|
+
private readonly onStateChange: () => void
|
|
40
|
+
private readonly onMessageAppended: ((chatId: string, entry: TranscriptEntry) => void) | undefined
|
|
41
|
+
private readonly activeStatuses = new Map<string, SessionStatus>()
|
|
42
|
+
private messages: ConsumerMessages | null = null
|
|
43
|
+
private running = false
|
|
44
|
+
|
|
45
|
+
constructor(options: TranscriptConsumerOptions) {
|
|
46
|
+
this.nc = options.nc
|
|
47
|
+
this.store = options.store
|
|
48
|
+
this.onStateChange = options.onStateChange
|
|
49
|
+
this.onMessageAppended = options.onMessageAppended
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getActiveStatuses(): Map<string, SessionStatus> {
|
|
53
|
+
return new Map(this.activeStatuses)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
hasActiveChat(chatId: string): boolean {
|
|
57
|
+
return this.activeStatuses.has(chatId)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async start(): Promise<void> {
|
|
61
|
+
if (this.running) return
|
|
62
|
+
this.running = true
|
|
63
|
+
|
|
64
|
+
const js = jetstream(this.nc)
|
|
65
|
+
|
|
66
|
+
// Read last-processed sequence from KV for resume
|
|
67
|
+
const kvm = new Kvm(this.nc)
|
|
68
|
+
const stateBucket = await kvm.create("kanna_consumer_state")
|
|
69
|
+
const entry = await stateBucket.get("transcript_last_seq")
|
|
70
|
+
const lastSeq = entry ? Number(decoder.decode(entry.value)) : 0
|
|
71
|
+
|
|
72
|
+
const consumerOpts = lastSeq > 0
|
|
73
|
+
? { deliver_policy: DeliverPolicy.StartSequence, opt_start_seq: lastSeq + 1 }
|
|
74
|
+
: { deliver_policy: DeliverPolicy.New }
|
|
75
|
+
|
|
76
|
+
const consumer = await js.consumers.get(RUNNER_EVENTS_STREAM, consumerOpts)
|
|
77
|
+
const messages = await consumer.consume()
|
|
78
|
+
this.messages = messages
|
|
79
|
+
|
|
80
|
+
// Process messages in the background — don't block start()
|
|
81
|
+
;(async () => {
|
|
82
|
+
for await (const msg of messages) {
|
|
83
|
+
if (!this.running) break
|
|
84
|
+
try {
|
|
85
|
+
const event = JSON.parse(decoder.decode(msg.data)) as RunnerTurnEvent
|
|
86
|
+
await this.handleEvent(event)
|
|
87
|
+
msg.ack()
|
|
88
|
+
await stateBucket.put("transcript_last_seq", encoder.encode(String(msg.seq)))
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.warn(
|
|
91
|
+
LOG_PREFIX,
|
|
92
|
+
"TranscriptConsumer: failed to process event:",
|
|
93
|
+
err instanceof Error ? err.message : String(err),
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
})()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
stop(): void {
|
|
101
|
+
this.running = false
|
|
102
|
+
if (this.messages) {
|
|
103
|
+
this.messages.close().catch(() => {})
|
|
104
|
+
this.messages = null
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async handleEvent(event: RunnerTurnEvent): Promise<void> {
|
|
109
|
+
let stateChanged = true
|
|
110
|
+
|
|
111
|
+
switch (event.type) {
|
|
112
|
+
case "transcript":
|
|
113
|
+
this.store.appendMessage(event.chatId, event.entry)
|
|
114
|
+
this.onMessageAppended?.(event.chatId, event.entry)
|
|
115
|
+
break
|
|
116
|
+
case "turn_finished":
|
|
117
|
+
await this.store.recordTurnFinished(event.chatId)
|
|
118
|
+
this.activeStatuses.delete(event.chatId)
|
|
119
|
+
break
|
|
120
|
+
case "turn_failed":
|
|
121
|
+
await this.store.recordTurnFailed(event.chatId, event.error)
|
|
122
|
+
this.activeStatuses.delete(event.chatId)
|
|
123
|
+
break
|
|
124
|
+
case "turn_cancelled":
|
|
125
|
+
await this.store.recordTurnCancelled(event.chatId)
|
|
126
|
+
this.activeStatuses.delete(event.chatId)
|
|
127
|
+
break
|
|
128
|
+
case "session_token":
|
|
129
|
+
await this.store.setSessionToken(event.chatId, event.sessionToken)
|
|
130
|
+
break
|
|
131
|
+
case "title_generated":
|
|
132
|
+
await this.store.renameChat(event.chatId, event.title)
|
|
133
|
+
break
|
|
134
|
+
case "status_change": {
|
|
135
|
+
const prev = this.activeStatuses.get(event.chatId)
|
|
136
|
+
if (prev === event.status) {
|
|
137
|
+
stateChanged = false
|
|
138
|
+
} else {
|
|
139
|
+
this.activeStatuses.set(event.chatId, event.status)
|
|
140
|
+
}
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
case "pending_tool":
|
|
144
|
+
// Trigger state change — UI needs to show pending tool
|
|
145
|
+
break
|
|
146
|
+
case "provider_set":
|
|
147
|
+
await this.store.setChatProvider(event.chatId, event.provider)
|
|
148
|
+
break
|
|
149
|
+
case "plan_mode_set":
|
|
150
|
+
await this.store.setPlanMode(event.chatId, event.planMode)
|
|
151
|
+
break
|
|
152
|
+
case "context_cleared":
|
|
153
|
+
// Already handled by session_token event — no store write needed
|
|
154
|
+
stateChanged = false
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (stateChanged) {
|
|
159
|
+
this.onStateChange()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// src/server/transcript-search.test.ts
|
|
2
|
+
import { describe, expect, test } from "bun:test"
|
|
3
|
+
import { TranscriptSearchIndex } from "./transcript-search"
|
|
4
|
+
import type { TranscriptEntry } from "../shared/types"
|
|
5
|
+
|
|
6
|
+
function timestamped<T extends Omit<TranscriptEntry, "_id" | "createdAt">>(
|
|
7
|
+
entry: T,
|
|
8
|
+
): TranscriptEntry {
|
|
9
|
+
return { _id: crypto.randomUUID(), createdAt: Date.now(), ...entry } as TranscriptEntry
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("TranscriptSearchIndex", () => {
|
|
13
|
+
test("indexes user_prompt entries and searches", () => {
|
|
14
|
+
const index = new TranscriptSearchIndex()
|
|
15
|
+
index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: "implement auth middleware with JWT tokens" }))
|
|
16
|
+
index.addEntry("chat-2", timestamped({ kind: "user_prompt", content: "fix CSS styling on sidebar" }))
|
|
17
|
+
|
|
18
|
+
const results = index.search("auth middleware")
|
|
19
|
+
expect(results.length).toBeGreaterThanOrEqual(1)
|
|
20
|
+
expect(results[0].chatId).toBe("chat-1")
|
|
21
|
+
expect(results[0].kind).toBe("user_prompt")
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("indexes assistant_text entries", () => {
|
|
25
|
+
const index = new TranscriptSearchIndex()
|
|
26
|
+
index.addEntry("chat-1", timestamped({ kind: "assistant_text", text: "I created the users table with email and password_hash columns" }))
|
|
27
|
+
|
|
28
|
+
const results = index.search("users table")
|
|
29
|
+
expect(results.length).toBe(1)
|
|
30
|
+
expect(results[0].chatId).toBe("chat-1")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test("indexes tool_call entries with file paths", () => {
|
|
34
|
+
const index = new TranscriptSearchIndex()
|
|
35
|
+
index.addEntry("chat-1", timestamped({
|
|
36
|
+
kind: "tool_call",
|
|
37
|
+
tool: {
|
|
38
|
+
kind: "tool",
|
|
39
|
+
toolKind: "edit_file",
|
|
40
|
+
toolName: "Edit",
|
|
41
|
+
toolId: "tool-1",
|
|
42
|
+
input: { filePath: "/src/server/auth.ts", oldString: "a", newString: "b" },
|
|
43
|
+
},
|
|
44
|
+
} as Omit<TranscriptEntry, "_id" | "createdAt">))
|
|
45
|
+
|
|
46
|
+
const results = index.search("auth.ts")
|
|
47
|
+
expect(results.length).toBe(1)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("indexes tool_call entries with bash commands", () => {
|
|
51
|
+
const index = new TranscriptSearchIndex()
|
|
52
|
+
index.addEntry("chat-1", timestamped({
|
|
53
|
+
kind: "tool_call",
|
|
54
|
+
tool: {
|
|
55
|
+
kind: "tool",
|
|
56
|
+
toolKind: "bash",
|
|
57
|
+
toolName: "Bash",
|
|
58
|
+
toolId: "tool-2",
|
|
59
|
+
input: { command: "bun test src/server/auth.test.ts" },
|
|
60
|
+
},
|
|
61
|
+
} as Omit<TranscriptEntry, "_id" | "createdAt">))
|
|
62
|
+
|
|
63
|
+
const results = index.search("bun test auth")
|
|
64
|
+
expect(results.length).toBe(1)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test("indexes tool_call entries with grep patterns", () => {
|
|
68
|
+
const index = new TranscriptSearchIndex()
|
|
69
|
+
index.addEntry("chat-1", timestamped({
|
|
70
|
+
kind: "tool_call",
|
|
71
|
+
tool: {
|
|
72
|
+
kind: "tool",
|
|
73
|
+
toolKind: "grep",
|
|
74
|
+
toolName: "Grep",
|
|
75
|
+
toolId: "tool-3",
|
|
76
|
+
input: { pattern: "handleAuth", outputMode: "content" },
|
|
77
|
+
},
|
|
78
|
+
} as Omit<TranscriptEntry, "_id" | "createdAt">))
|
|
79
|
+
|
|
80
|
+
const results = index.search("handleAuth")
|
|
81
|
+
expect(results.length).toBe(1)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("indexes tool_result entries", () => {
|
|
85
|
+
const index = new TranscriptSearchIndex()
|
|
86
|
+
index.addEntry("chat-1", timestamped({
|
|
87
|
+
kind: "tool_result",
|
|
88
|
+
toolId: "tool-1",
|
|
89
|
+
content: "File written successfully to /src/server/auth.ts",
|
|
90
|
+
}))
|
|
91
|
+
|
|
92
|
+
const results = index.search("auth.ts written")
|
|
93
|
+
expect(results.length).toBe(1)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test("indexes tool_result with non-string content via JSON", () => {
|
|
97
|
+
const index = new TranscriptSearchIndex()
|
|
98
|
+
index.addEntry("chat-1", timestamped({
|
|
99
|
+
kind: "tool_result",
|
|
100
|
+
toolId: "tool-1",
|
|
101
|
+
content: { files: ["auth.ts", "middleware.ts"], count: 2 },
|
|
102
|
+
}))
|
|
103
|
+
|
|
104
|
+
const results = index.search("auth.ts middleware")
|
|
105
|
+
expect(results.length).toBe(1)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test("returns results with score and fragment", () => {
|
|
109
|
+
const index = new TranscriptSearchIndex()
|
|
110
|
+
index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: "implement error handling for database connections" }))
|
|
111
|
+
|
|
112
|
+
const results = index.search("error handling")
|
|
113
|
+
expect(results[0].score).toBeGreaterThan(0)
|
|
114
|
+
expect(results[0].fragment.length).toBeGreaterThan(0)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test("returns empty for no matches", () => {
|
|
118
|
+
const index = new TranscriptSearchIndex()
|
|
119
|
+
index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: "hello world" }))
|
|
120
|
+
|
|
121
|
+
const results = index.search("nonexistent term here")
|
|
122
|
+
expect(results).toEqual([])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test("respects limit", () => {
|
|
126
|
+
const index = new TranscriptSearchIndex()
|
|
127
|
+
for (let i = 0; i < 20; i++) {
|
|
128
|
+
index.addEntry(`chat-${i}`, timestamped({ kind: "user_prompt", content: `testing search feature ${i}` }))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const results = index.search("testing search", 5)
|
|
132
|
+
expect(results.length).toBe(5)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test("skips non-indexable entry kinds", () => {
|
|
136
|
+
const index = new TranscriptSearchIndex()
|
|
137
|
+
index.addEntry("chat-1", timestamped({ kind: "status", status: "running" }))
|
|
138
|
+
index.addEntry("chat-1", timestamped({ kind: "context_cleared" }))
|
|
139
|
+
index.addEntry("chat-1", timestamped({ kind: "interrupted" }))
|
|
140
|
+
index.addEntry("chat-1", timestamped({ kind: "compact_boundary" }))
|
|
141
|
+
|
|
142
|
+
expect(index.size).toBe(0)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test("fragment is truncated to 300 chars", () => {
|
|
146
|
+
const index = new TranscriptSearchIndex()
|
|
147
|
+
const longContent = "database ".repeat(100) // 900 chars
|
|
148
|
+
index.addEntry("chat-1", timestamped({ kind: "user_prompt", content: longContent }))
|
|
149
|
+
|
|
150
|
+
const results = index.search("database")
|
|
151
|
+
expect(results[0].fragment.length).toBeLessThanOrEqual(300)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test("tool_result content is truncated to 500 chars before indexing", () => {
|
|
155
|
+
const index = new TranscriptSearchIndex()
|
|
156
|
+
// "filler " is 7 chars; 72 repeats = 504 chars, pushing ZZZMARKER past the 500 boundary
|
|
157
|
+
const padding = "filler ".repeat(72)
|
|
158
|
+
const content = padding + "ZZZMARKER"
|
|
159
|
+
expect(content.indexOf("ZZZMARKER")).toBeGreaterThan(500)
|
|
160
|
+
|
|
161
|
+
index.addEntry("chat-1", timestamped({
|
|
162
|
+
kind: "tool_result",
|
|
163
|
+
toolId: "tool-1",
|
|
164
|
+
content,
|
|
165
|
+
}))
|
|
166
|
+
|
|
167
|
+
// ZZZMARKER sits beyond 500 chars and must not be indexed
|
|
168
|
+
const results = index.search("ZZZMARKER")
|
|
169
|
+
expect(results).toEqual([])
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test("tool_result content within 500 chars is still searchable", () => {
|
|
173
|
+
const index = new TranscriptSearchIndex()
|
|
174
|
+
index.addEntry("chat-1", timestamped({
|
|
175
|
+
kind: "tool_result",
|
|
176
|
+
toolId: "tool-1",
|
|
177
|
+
content: "File written successfully to /src/server/auth.ts",
|
|
178
|
+
}))
|
|
179
|
+
|
|
180
|
+
const results = index.search("auth.ts written")
|
|
181
|
+
expect(results.length).toBe(1)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
test("timestamp is ISO string derived from createdAt", () => {
|
|
185
|
+
const index = new TranscriptSearchIndex()
|
|
186
|
+
const now = Date.now()
|
|
187
|
+
const entry = { _id: "fixed-id", createdAt: now, kind: "user_prompt" as const, content: "test timestamp" } as TranscriptEntry
|
|
188
|
+
index.addEntry("chat-1", entry)
|
|
189
|
+
|
|
190
|
+
const results = index.search("timestamp")
|
|
191
|
+
expect(results[0].timestamp).toBe(new Date(now).toISOString())
|
|
192
|
+
})
|
|
193
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/server/transcript-search.ts
|
|
2
|
+
import type { TranscriptEntry, NormalizedToolCall } from "../shared/types"
|
|
3
|
+
import type { SearchResult, SearchDocumentKind } from "../shared/workspace-types"
|
|
4
|
+
import { BM25Index } from "./bm25"
|
|
5
|
+
|
|
6
|
+
const TOOL_RESULT_MAX_CHARS = 500
|
|
7
|
+
const SEARCH_FRAGMENT_MAX_CHARS = 300
|
|
8
|
+
|
|
9
|
+
interface IndexedEntry {
|
|
10
|
+
chatId: string
|
|
11
|
+
timestamp: string
|
|
12
|
+
kind: SearchDocumentKind
|
|
13
|
+
text: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function extractTextFromEntry(entry: TranscriptEntry): { text: string; kind: SearchDocumentKind } | null {
|
|
17
|
+
switch (entry.kind) {
|
|
18
|
+
case "user_prompt":
|
|
19
|
+
return { text: entry.content, kind: "user_prompt" }
|
|
20
|
+
case "assistant_text":
|
|
21
|
+
return { text: entry.text, kind: "assistant_text" }
|
|
22
|
+
case "tool_call":
|
|
23
|
+
return { text: toolCallToText(entry.tool), kind: "tool_call" }
|
|
24
|
+
case "tool_result": {
|
|
25
|
+
const raw = typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content)
|
|
26
|
+
return { text: raw.slice(0, TOOL_RESULT_MAX_CHARS), kind: "tool_result" }
|
|
27
|
+
}
|
|
28
|
+
default:
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function toolCallToText(tool: NormalizedToolCall): string {
|
|
34
|
+
const parts: string[] = [tool.toolKind]
|
|
35
|
+
const input = tool.input as Record<string, unknown>
|
|
36
|
+
if (typeof input.filePath === "string") parts.push(input.filePath)
|
|
37
|
+
if (typeof input.path === "string") parts.push(input.path)
|
|
38
|
+
if (typeof input.command === "string") parts.push(input.command)
|
|
39
|
+
if (typeof input.pattern === "string") parts.push(input.pattern)
|
|
40
|
+
if (typeof input.query === "string") parts.push(input.query)
|
|
41
|
+
return parts.join(" ")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class TranscriptSearchIndex {
|
|
45
|
+
private readonly bm25 = new BM25Index<string>()
|
|
46
|
+
private readonly entries = new Map<string, IndexedEntry>()
|
|
47
|
+
|
|
48
|
+
get size(): number {
|
|
49
|
+
return this.entries.size
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
addEntry(chatId: string, entry: TranscriptEntry): void {
|
|
53
|
+
const extracted = extractTextFromEntry(entry)
|
|
54
|
+
if (!extracted) return
|
|
55
|
+
|
|
56
|
+
const docId = entry._id
|
|
57
|
+
const indexed: IndexedEntry = {
|
|
58
|
+
chatId,
|
|
59
|
+
timestamp: new Date(entry.createdAt).toISOString(),
|
|
60
|
+
kind: extracted.kind,
|
|
61
|
+
text: extracted.text,
|
|
62
|
+
}
|
|
63
|
+
this.entries.set(docId, indexed)
|
|
64
|
+
this.bm25.add(docId, extracted.text)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
search(query: string, limit = 10): SearchResult[] {
|
|
68
|
+
const bm25Results = this.bm25.search(query, limit)
|
|
69
|
+
return bm25Results
|
|
70
|
+
.map((r) => {
|
|
71
|
+
const entry = this.entries.get(r.id)
|
|
72
|
+
if (!entry) return null
|
|
73
|
+
return {
|
|
74
|
+
chatId: entry.chatId,
|
|
75
|
+
timestamp: entry.timestamp,
|
|
76
|
+
kind: entry.kind,
|
|
77
|
+
fragment: entry.text.slice(0, SEARCH_FRAGMENT_MAX_CHARS),
|
|
78
|
+
score: r.score,
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
.filter((r): r is SearchResult => r !== null)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { TranscriptEntry } from "../shared/types"
|
|
2
|
+
|
|
3
|
+
/** Collapse whitespace and truncate a single line to `limit` characters. */
|
|
4
|
+
export function truncateLine(text: string, limit: number): string {
|
|
5
|
+
const normalized = text.replace(/\s+/g, " ").trim()
|
|
6
|
+
if (normalized.length <= limit) return normalized
|
|
7
|
+
return `${normalized.slice(0, Math.max(0, limit - 1)).trimEnd()}…`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Convert a transcript entry into a labeled single line, or null for non-displayable kinds. */
|
|
11
|
+
export function toTranscriptLine(entry: TranscriptEntry, lineCharLimit: number): string | null {
|
|
12
|
+
switch (entry.kind) {
|
|
13
|
+
case "user_prompt":
|
|
14
|
+
return `User: ${truncateLine(entry.content, lineCharLimit)}`
|
|
15
|
+
case "assistant_text":
|
|
16
|
+
return `Assistant: ${truncateLine(entry.text, lineCharLimit)}`
|
|
17
|
+
case "compact_summary":
|
|
18
|
+
return `Summary: ${truncateLine(entry.summary, lineCharLimit)}`
|
|
19
|
+
case "result":
|
|
20
|
+
return `${entry.isError ? "Result error" : "Result"}: ${truncateLine(entry.result, lineCharLimit)}`
|
|
21
|
+
default:
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PROMPT_SCHEMA = {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
prompt: { type: "string" },
|
|
30
|
+
},
|
|
31
|
+
required: ["prompt"],
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
} as const
|
|
34
|
+
|
|
35
|
+
export { PROMPT_SCHEMA }
|
|
36
|
+
|
|
37
|
+
/** Collapse whitespace and clamp intent text to 1000 characters. */
|
|
38
|
+
export function normalizeIntent(intent: string): string {
|
|
39
|
+
return intent.replace(/\s+/g, " ").trim().slice(0, 1_000)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Normalize LLM-generated prompt output, clamping to maxChars. Returns null for empty/invalid. */
|
|
43
|
+
export function normalizeGeneratedPrompt(value: unknown, maxChars: number): string | null {
|
|
44
|
+
if (typeof value !== "string") return null
|
|
45
|
+
const normalized = value
|
|
46
|
+
.replace(/\r\n/g, "\n")
|
|
47
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
48
|
+
.trim()
|
|
49
|
+
.slice(0, maxChars)
|
|
50
|
+
.trim()
|
|
51
|
+
return normalized || null
|
|
52
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { UpdateManager } from "./update-manager"
|
|
3
|
+
|
|
4
|
+
describe("UpdateManager", () => {
|
|
5
|
+
test("detects available updates", async () => {
|
|
6
|
+
const manager = new UpdateManager({
|
|
7
|
+
currentVersion: "0.12.0",
|
|
8
|
+
fetchLatestVersion: async () => "0.13.0",
|
|
9
|
+
installVersion: () => ({
|
|
10
|
+
ok: true,
|
|
11
|
+
errorCode: null,
|
|
12
|
+
userTitle: null,
|
|
13
|
+
userMessage: null,
|
|
14
|
+
}),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const snapshot = await manager.checkForUpdates({ force: true })
|
|
18
|
+
|
|
19
|
+
expect(snapshot.status).toBe("available")
|
|
20
|
+
expect(snapshot.updateAvailable).toBe(true)
|
|
21
|
+
expect(snapshot.latestVersion).toBe("0.13.0")
|
|
22
|
+
expect(snapshot.installAction).toBe("restart")
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("bypasses cache when force is true", async () => {
|
|
26
|
+
let calls = 0
|
|
27
|
+
const manager = new UpdateManager({
|
|
28
|
+
currentVersion: "0.12.0",
|
|
29
|
+
fetchLatestVersion: async () => {
|
|
30
|
+
calls += 1
|
|
31
|
+
return calls === 1 ? "0.12.1" : "0.13.0"
|
|
32
|
+
},
|
|
33
|
+
installVersion: () => ({
|
|
34
|
+
ok: true,
|
|
35
|
+
errorCode: null,
|
|
36
|
+
userTitle: null,
|
|
37
|
+
userMessage: null,
|
|
38
|
+
}),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
await manager.checkForUpdates()
|
|
42
|
+
await manager.checkForUpdates({ force: true })
|
|
43
|
+
|
|
44
|
+
expect(calls).toBe(2)
|
|
45
|
+
expect(manager.getSnapshot().latestVersion).toBe("0.13.0")
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("surfaces install failures without clearing the running version", async () => {
|
|
49
|
+
let installedVersion: string | null = null
|
|
50
|
+
const manager = new UpdateManager({
|
|
51
|
+
currentVersion: "0.12.0",
|
|
52
|
+
fetchLatestVersion: async () => "0.13.0",
|
|
53
|
+
installVersion: (_packageName, version) => {
|
|
54
|
+
installedVersion = version
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
errorCode: "version_not_live_yet",
|
|
58
|
+
userTitle: "Update not live yet",
|
|
59
|
+
userMessage: "This update is still propagating. Try again in a few minutes.",
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const result = await manager.installUpdate()
|
|
65
|
+
|
|
66
|
+
expect(result).toEqual({
|
|
67
|
+
ok: false,
|
|
68
|
+
action: "restart",
|
|
69
|
+
errorCode: "version_not_live_yet",
|
|
70
|
+
userTitle: "Update not live yet",
|
|
71
|
+
userMessage: "This update is still propagating. Try again in a few minutes.",
|
|
72
|
+
})
|
|
73
|
+
expect(installedVersion === "0.13.0").toBe(true)
|
|
74
|
+
expect(manager.getSnapshot().status).toBe("error")
|
|
75
|
+
expect(manager.getSnapshot().currentVersion).toBe("0.12.0")
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test("always exposes an available reload action in dev mode", async () => {
|
|
79
|
+
const manager = new UpdateManager({
|
|
80
|
+
currentVersion: "0.12.0",
|
|
81
|
+
fetchLatestVersion: async () => "9.9.9",
|
|
82
|
+
installVersion: () => ({
|
|
83
|
+
ok: true,
|
|
84
|
+
errorCode: null,
|
|
85
|
+
userTitle: null,
|
|
86
|
+
userMessage: null,
|
|
87
|
+
}),
|
|
88
|
+
devMode: true,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
expect(manager.getSnapshot()).toMatchObject({
|
|
92
|
+
status: "available",
|
|
93
|
+
updateAvailable: true,
|
|
94
|
+
installAction: "restart",
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const result = await manager.installUpdate()
|
|
98
|
+
expect(result).toEqual({
|
|
99
|
+
ok: true,
|
|
100
|
+
action: "restart",
|
|
101
|
+
errorCode: null,
|
|
102
|
+
userTitle: null,
|
|
103
|
+
userMessage: null,
|
|
104
|
+
})
|
|
105
|
+
expect(manager.getSnapshot().status).toBe("restart_pending")
|
|
106
|
+
})
|
|
107
|
+
})
|