@openchambery/web 1.19.22-beta.1 → 2.0.0-beta.10
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/README.md +13 -7
- package/bin/cli-opencode.test.js +208 -0
- package/bin/cli.js +62 -9
- package/bin/lib/cli-startup.js +105 -2
- package/bin/lib/cli-startup.test.js +179 -0
- package/dist/assets/{AgentAvatar-CRaBJXKs.js → AgentAvatar-CzTtVMnI.js} +1 -1
- package/dist/assets/AssistantView-Cg1qQvVe.js +3 -0
- package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-hOi72Wdx.js} +1 -1
- package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-DCqrQkkz.js} +1 -1
- package/dist/assets/Icon-B8fSTLdP.js +1 -0
- package/dist/assets/MarkdownRenderer-Cw74oVxV.js +2 -0
- package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-YM7iRmzK.js} +3 -3
- package/dist/assets/MarkstreamRendererImpl-BDy_oGJQ.js +3 -0
- package/dist/assets/MobileDetailNavigation-iX_hb_Xt.js +1 -0
- package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-CAMUupwR.js} +2 -2
- package/dist/assets/MobileSurface-CHbdPcLH.js +2 -0
- package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-Ci1ODFFX.js} +1 -1
- package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-O4cGdz5p.js} +1 -1
- package/dist/assets/OverlayScrollbar-CRfGtcmG.js +2 -0
- package/dist/assets/SettingsView-Dg8BtTV1.js +25 -0
- package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-B8d8qIQ7.js} +1 -1
- package/dist/assets/TerminalView-CMiw3s_c.js +8 -0
- package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-ByaQYRTp.js} +2 -2
- package/dist/assets/appThemeRegistry-BGGHio8T.js +1 -0
- package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
- package/dist/assets/{button-CI7Q-Zw_.js → button-DIIrsMjW.js} +1 -1
- package/dist/assets/{collapsible-D5Phaykh.js → collapsible-CnvUeYst.js} +1 -1
- package/dist/assets/{context-menu-CubSDW2g.js → context-menu-BGBUpZEQ.js} +1 -1
- package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-JGFqhlaM.js} +1 -1
- package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-CHEwrI93.js} +1 -1
- package/dist/assets/{diagram-DdLO8HZ9.js → diagram-rvn7_qBM.js} +1 -1
- package/dist/assets/{dialog-B6pnIYmp.js → dialog-CQ63sc9E.js} +1 -1
- package/dist/assets/es-Dsmc0A_3.js +15 -0
- package/dist/assets/fileMentionTouchSelection-BzzcWJCi.js +3 -0
- package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-DN6gbLhM.js} +1 -1
- package/dist/assets/fr-C_ZlZDI2.js +13 -0
- package/dist/assets/gitApi-BF8vuoZc.js +10 -0
- package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-BMztRu0c.js} +1 -1
- package/dist/assets/{input-D-__fwRi.js → input-BVu-iykl.js} +1 -1
- package/dist/assets/insertionBoundaries-EcAfGT2D.js +1 -0
- package/dist/assets/ja-BqqTaWTi.js +15 -0
- package/dist/assets/ko-DP2XPM37.js +15 -0
- package/dist/assets/main-B7t9lavz.js +2 -0
- package/dist/assets/main-D2BzpnUt.css +1 -0
- package/dist/assets/main-DWP-prNB.js +169 -0
- package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
- package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-D9-B1wdY.js} +1 -1
- package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
- package/dist/assets/mcpQueries-CfXrAkuU.js +1 -0
- package/dist/assets/miniChat-DgOrEQbN.js +2 -0
- package/dist/assets/mobile-Co_ZOHkZ.js +2 -0
- package/dist/assets/multirun-DelyUO6k.js +4 -0
- package/dist/assets/persistence-D0y7226-.js +3 -0
- package/dist/assets/pl-BCf5uLlK.js +15 -0
- package/dist/assets/platform-CurqO28D.js +6 -0
- package/dist/assets/{projectMeta-S1hJ7oBp.js → projectMeta-CPElzJ0n.js} +2 -2
- package/dist/assets/pt-BR-W09Q2pvn.js +15 -0
- package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
- package/dist/assets/renderElectronMiniChatApp-pOE93w86.js +2 -0
- package/dist/assets/renderMobileApp-F7ZSLcw8.js +28 -0
- package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
- package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-DSS0KxG3.js} +1 -1
- package/dist/assets/runtimeEndpointReset-x2Wgx57R.js +149 -0
- package/dist/assets/sessionChangeSummary-BUGSYcHp.js +2 -0
- package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-Bss8M20i.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-CE7W7lJ9.js} +1 -1
- package/dist/assets/src-lS1keDVV.css +2 -0
- package/dist/assets/store-jaP79mqI.js +15 -0
- package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-mDR2jXTo.js} +1 -1
- package/dist/assets/textarea-XD2oX_Ee.js +1 -0
- package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-DYeQ_Hvy.js} +1 -1
- package/dist/assets/uk-BgFzz8m2.js +15 -0
- package/dist/assets/{url-_z7evzdK.js → url-CGbJbiJq.js} +1 -1
- package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-BAWHZ-t0.js} +64 -45
- package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-Bz4tRflF.js} +1 -1
- package/dist/assets/useDesktopWindowControlsLayout-hN_nLb7T.js +1 -0
- package/dist/assets/useEffectiveDirectory-Do9DQ453.js +1 -0
- package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-Bjx8zkGM.js} +1 -1
- package/dist/assets/useMobileNavigationStore-BsY_gi_K.js +414 -0
- package/dist/assets/useSessionAutoCleanup-C4g5KmDf.js +2 -0
- package/dist/assets/useUIStore-syVmbuDA.js +1 -0
- package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-B5QXxBok.js} +2 -2
- package/dist/assets/vendor-opencode-client-C12gfjag.js +12 -0
- package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-Dq5ocTF8.js} +1 -1
- package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
- package/dist/assets/zh-CN-BhqSLgUY.js +15 -0
- package/dist/assets/zh-TW-BsaBgY-p.js +15 -0
- package/dist/index.html +12 -12
- package/dist/mini-chat.html +12 -12
- package/dist/mobile.html +12 -12
- package/package.json +3 -2
- package/server/index.js +423 -20
- package/server/lib/assistants/DOCUMENTATION.md +26 -21
- package/server/lib/assistants/assign.js +2 -1
- package/server/lib/assistants/contact-store.js +37 -19
- package/server/lib/assistants/contracts.js +11 -1
- package/server/lib/assistants/projection-history.test.js +454 -0
- package/server/lib/assistants/routes.js +11 -5
- package/server/lib/assistants/service.js +850 -294
- package/server/lib/assistants/service.test.js +588 -274
- package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
- package/server/lib/browser-provider/contract.js +312 -0
- package/server/lib/browser-provider/host.js +421 -0
- package/server/lib/browser-provider/host.test.js +304 -0
- package/server/lib/browser-provider/routes.js +122 -0
- package/server/lib/browser-provider/routes.test.js +120 -0
- package/server/lib/browser-provider/selection-store.js +43 -0
- package/server/lib/browser-provider/selection-store.test.js +35 -0
- package/server/lib/conversations/DOCUMENTATION.md +5 -5
- package/server/lib/conversations/service.js +87 -57
- package/server/lib/conversations/service.test.js +132 -100
- package/server/lib/conversations/validation.js +11 -1
- package/server/lib/event-stream/DOCUMENTATION.md +3 -1
- package/server/lib/event-stream/directory-ws-bridge.js +13 -2
- package/server/lib/event-stream/global-hub.js +51 -5
- package/server/lib/event-stream/reasoning-projection.js +16 -1
- package/server/lib/event-stream/reasoning-projection.test.js +15 -0
- package/server/lib/event-stream/runtime.js +57 -20
- package/server/lib/event-stream/runtime.test.js +112 -0
- package/server/lib/llm/DOCUMENTATION.md +95 -84
- package/server/lib/llm/catalog.js +111 -45
- package/server/lib/llm/catalog.test.js +121 -59
- package/server/lib/llm/completions.js +41 -12
- package/server/lib/llm/completions.test.js +45 -4
- package/server/lib/llm/generate.js +516 -453
- package/server/lib/llm/generate.test.js +705 -484
- package/server/lib/llm/routes.js +8 -4
- package/server/lib/message-queue/DOCUMENTATION.md +5 -5
- package/server/lib/message-queue/opencode-adapter.js +191 -104
- package/server/lib/message-queue/opencode-adapter.test.js +328 -58
- package/server/lib/message-queue/runtime.js +15 -2
- package/server/lib/message-queue/runtime.test.js +6 -6
- package/server/lib/message-queue/worker.js +7 -1
- package/server/lib/notifications/DOCUMENTATION.md +3 -1
- package/server/lib/notifications/runtime.js +133 -31
- package/server/lib/notifications/runtime.test.js +103 -0
- package/server/lib/notifications/template-runtime.js +130 -38
- package/server/lib/notifications/template-runtime.test.js +96 -0
- package/server/lib/opencode/DOCUMENTATION.md +111 -16
- package/server/lib/opencode/agent-document.d.ts +28 -0
- package/server/lib/opencode/agent-document.js +280 -0
- package/server/lib/opencode/agent-document.test.js +137 -0
- package/server/lib/opencode/agents.js +96 -0
- package/server/lib/opencode/auth-state-runtime.js +5 -0
- package/server/lib/opencode/bootstrap-runtime.js +2 -0
- package/server/lib/opencode/config-entity-routes.js +117 -103
- package/server/lib/opencode/config-entity-routes.test.js +148 -10
- package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
- package/server/lib/opencode/ensure-cli.d.ts +8 -0
- package/server/lib/opencode/ensure-cli.js +238 -0
- package/server/lib/opencode/ensure-cli.test.js +119 -0
- package/server/lib/opencode/env-runtime.js +208 -111
- package/server/lib/opencode/env-runtime.test.js +211 -14
- package/server/lib/opencode/feature-routes-runtime.js +90 -5
- package/server/lib/opencode/feature-routes-runtime.test.js +25 -0
- package/server/lib/opencode/http-server-connections.js +98 -0
- package/server/lib/opencode/http-server-connections.test.js +321 -0
- package/server/lib/opencode/lifecycle.js +539 -95
- package/server/lib/opencode/lifecycle.test.js +698 -6
- package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
- package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
- package/server/lib/opencode/network-runtime.js +28 -16
- package/server/lib/opencode/network-runtime.test.js +68 -4
- package/server/lib/opencode/openchamber-routes.js +7 -1
- package/server/lib/opencode/opencode2-pin.js +139 -0
- package/server/lib/opencode/opencode2-pin.test.js +88 -0
- package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
- package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
- package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
- package/server/lib/opencode/plugin-compatibility.js +15 -0
- package/server/lib/opencode/plugin-compatibility.test.js +21 -0
- package/server/lib/opencode/plugin-routes.js +3 -18
- package/server/lib/opencode/plugin-routes.test.js +9 -9
- package/server/lib/opencode/plugins.js +4 -3
- package/server/lib/opencode/plugins.test.js +12 -0
- package/server/lib/opencode/provider-catalog.js +58 -12
- package/server/lib/opencode/provider-catalog.test.js +48 -0
- package/server/lib/opencode/proxy.js +373 -28
- package/server/lib/opencode/proxy.test.js +353 -0
- package/server/lib/opencode/routes.js +333 -76
- package/server/lib/opencode/routes.test.js +315 -50
- package/server/lib/opencode/runtime-contract.js +401 -0
- package/server/lib/opencode/runtime-contract.test.js +214 -0
- package/server/lib/opencode/server-opencode-fetch.js +242 -0
- package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
- package/server/lib/opencode/server-utils-runtime.js +35 -0
- package/server/lib/opencode/shared-service.integration.test.js +66 -0
- package/server/lib/opencode/shared-service.js +147 -0
- package/server/lib/opencode/shared-service.test.js +93 -0
- package/server/lib/opencode/shared.js +3 -3
- package/server/lib/opencode/shutdown-runtime.js +15 -6
- package/server/lib/opencode/shutdown-runtime.test.js +49 -1
- package/server/lib/opencode/skill-routes.js +26 -40
- package/server/lib/opencode/skill-routes.test.js +71 -7
- package/server/lib/opencode/skills.test.js +15 -0
- package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
- package/server/lib/opencode/static-routes-runtime.js +3 -1
- package/server/lib/opencode/static-routes-runtime.test.js +33 -0
- package/server/lib/opencode/upgrade-screen.d.ts +30 -0
- package/server/lib/opencode/upgrade-screen.js +299 -0
- package/server/lib/opencode/upgrade-screen.test.js +196 -0
- package/server/lib/opencode/v1-migration-gate.js +157 -0
- package/server/lib/opencode/v1-migration-gate.test.js +111 -0
- package/server/lib/opencode/v2-client.js +60 -0
- package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
- package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
- package/server/lib/opencode/watcher.js +2 -1
- package/server/lib/opencode/watcher.test.js +1 -1
- package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
- package/server/lib/permission-auto-accept/runtime.js +14 -7
- package/server/lib/permission-auto-accept/runtime.test.js +27 -12
- package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
- package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
- package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
- package/server/lib/scheduled-tasks/routes.js +27 -0
- package/server/lib/scheduled-tasks/routes.test.js +94 -0
- package/server/lib/scheduled-tasks/runtime.js +381 -310
- package/server/lib/scheduled-tasks/runtime.test.js +600 -309
- package/server/lib/session-goal/DOCUMENTATION.md +173 -22
- package/server/lib/session-goal/capability.js +57 -0
- package/server/lib/session-goal/capability.test.js +33 -0
- package/server/lib/session-goal/routes.js +240 -2
- package/server/lib/session-goal/routes.test.js +211 -0
- package/server/lib/session-goal/runtime.js +1617 -113
- package/server/lib/session-goal/runtime.test.js +1500 -70
- package/server/lib/session-index/DOCUMENTATION.md +18 -15
- package/server/lib/session-index/event-ingest.js +89 -16
- package/server/lib/session-index/event-ingest.test.js +73 -0
- package/server/lib/session-index/host-authority-snapshot.js +92 -0
- package/server/lib/session-index/routes.host-archive.test.js +201 -0
- package/server/lib/session-index/routes.js +63 -10
- package/server/lib/session-index/routes.test.js +8 -8
- package/server/lib/session-index/service.js +7 -1
- package/server/lib/session-index/sync-runtime.js +171 -41
- package/server/lib/session-index/sync-runtime.test.js +164 -9
- package/server/lib/session-metadata/DOCUMENTATION.md +96 -0
- package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
- package/server/lib/session-metadata/opencode-session-record.js +316 -0
- package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
- package/server/lib/session-metadata/routes.js +197 -0
- package/server/lib/session-metadata/routes.test.js +156 -0
- package/server/lib/session-metadata/session-archive.d.ts +73 -0
- package/server/lib/session-metadata/session-archive.js +654 -0
- package/server/lib/session-metadata/session-archive.test.js +359 -0
- package/server/lib/session-metadata/session-metadata-store.d.ts +38 -0
- package/server/lib/session-metadata/session-metadata-store.js +422 -0
- package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
- package/server/lib/session-metadata/session-projection.d.ts +41 -0
- package/server/lib/session-metadata/session-projection.js +279 -0
- package/server/lib/session-metadata/session-projection.test.js +64 -0
- package/server/lib/session-metadata/system-session.js +55 -0
- package/server/lib/session-metadata/system-session.test.js +55 -0
- package/server/lib/session-title/DOCUMENTATION.md +40 -7
- package/server/lib/session-title/native-runtime.test.js +167 -0
- package/server/lib/session-title/runtime.js +69 -44
- package/server/lib/session-title/session-access.js +46 -0
- package/server/lib/session-title/session-access.test.js +91 -0
- package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
- package/server/lib/session-turn-pages/reconcile.service.js +1 -1
- package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
- package/server/lib/session-turn-pages/routes.js +123 -75
- package/server/lib/session-turn-pages/routes.test.js +566 -0
- package/server/lib/session-turn-pages/service.js +6 -2
- package/server/lib/session-turn-pages/service.test.js +34 -0
- package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
- package/server/lib/session-turn-pages/session-message-projection.js +281 -0
- package/server/lib/small-model/DOCUMENTATION.md +101 -146
- package/server/lib/small-model/catalog.js +84 -54
- package/server/lib/small-model/catalog.test.js +111 -9
- package/server/lib/small-model/custom-api.js +68 -0
- package/server/lib/small-model/custom-api.test.js +64 -0
- package/server/lib/small-model/index.js +125 -241
- package/server/lib/small-model/resolve.js +35 -120
- package/server/lib/small-model/resolve.test.js +20 -110
- package/server/lib/small-model/routes.js +1 -3
- package/server/lib/small-model/summary-settings.test.js +157 -253
- package/server/opencode-proxy.test.js +63 -12
- package/dist/assets/AssistantView-BUveAgc4.js +0 -3
- package/dist/assets/Icon-BgdfPhNs.js +0 -1
- package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
- package/dist/assets/MarkstreamRendererImpl-D2dsW5Lg.js +0 -3
- package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
- package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
- package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
- package/dist/assets/SettingsView-PkXIavr7.js +0 -25
- package/dist/assets/TerminalView-CDdzF4yi.js +0 -8
- package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
- package/dist/assets/es-DA3MqOo0.js +0 -15
- package/dist/assets/fileMentionTouchSelection-DNeiqns-.js +0 -4
- package/dist/assets/fr-Dbz0CdIA.js +0 -13
- package/dist/assets/gitApi-DXyC4MiV.js +0 -7
- package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
- package/dist/assets/ja-DumanVQA.js +0 -15
- package/dist/assets/ko-C0IkiUjQ.js +0 -15
- package/dist/assets/main-BPS5ylib.js +0 -2
- package/dist/assets/main-Cpd9VWYs.js +0 -169
- package/dist/assets/main-qSeeJRW1.css +0 -1
- package/dist/assets/markdown-worker-CptoICmq.js +0 -2
- package/dist/assets/markdownTheme-DOfGg-A7.js +0 -1
- package/dist/assets/miniChat-B8_KLWiY.js +0 -2
- package/dist/assets/mobile-CtktKrXn.js +0 -2
- package/dist/assets/multirun-CYfP9Cfm.js +0 -4
- package/dist/assets/persistence-Ce3AnpEr.js +0 -3
- package/dist/assets/pl-B9No6MQp.js +0 -15
- package/dist/assets/platform-0w_Q9_ji.js +0 -2
- package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
- package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
- package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
- package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
- package/dist/assets/runtimeEndpointReset-ChOxt-J7.js +0 -147
- package/dist/assets/sessionChangeSummary-NnxcqCTI.js +0 -2
- package/dist/assets/src-rS3zCcsC.css +0 -2
- package/dist/assets/store-5B_LDnLM.js +0 -15
- package/dist/assets/textarea-bHYxZtfH.js +0 -1
- package/dist/assets/uk-C_1KkqyP.js +0 -15
- package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
- package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
- package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
- package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
- package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
- package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
- package/dist/assets/zh-CN-uWILgBdt.js +0 -15
- package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
- package/server/lib/small-model/call.js +0 -427
- package/server/lib/small-model/call.test.js +0 -382
- package/server/lib/small-model/opencode-session.js +0 -339
- package/server/lib/small-model/opencode-session.test.js +0 -177
|
@@ -33,7 +33,7 @@ The following APIs are exported by their owning modules. `event-stream/index.js`
|
|
|
33
33
|
### Reasoning projection helpers (`reasoning-projection.js`)
|
|
34
34
|
Import from the module file (not the event-stream barrel). Public surface:
|
|
35
35
|
- `shouldIncludeReasoning` / `readIncludeReasoningQuery` / `readIncludeReasoningFromUrl` / `stripIncludeReasoningParam`
|
|
36
|
-
- `projectMessagesPayloadForReasoning` / `projectMessagesPayloadWithoutReasoning` — array / `{ records }` / `{ record }` / `{ parts }`
|
|
36
|
+
- `projectMessagesPayloadForReasoning` / `projectMessagesPayloadWithoutReasoning` — array / `{ records }` / `{ record }` / `{ parts }`, plus native v2 `{ data }` list/context envelopes and assistant `content[]`. Filtering preserves message identities, cursor, text/tool content and token totals.
|
|
37
37
|
- `createReasoningOutboundFilter` — per-connection stream filter (version-suffix-aware type match; preserves original `type` on kept events)
|
|
38
38
|
- `filterSseBlock` / `createSseBlockSplitter` — SSE disabled path (CR-safe chunk boundaries)
|
|
39
39
|
|
|
@@ -49,6 +49,8 @@ Import from the module file (not the event-stream barrel). Public surface:
|
|
|
49
49
|
- `createUpstreamSseReader(...)`: creates a start/stop reader for OpenCode SSE streams. The reader parses SSE blocks, tracks the latest `Last-Event-ID`, reconnects after closed or stalled upstream streams, and reports events through callbacks.
|
|
50
50
|
|
|
51
51
|
## Runtime behavior
|
|
52
|
+
|
|
53
|
+
Native v2 `session.reasoning.*` events are filtered at the same outbound boundary as legacy `session.next.reasoning.*`. Both `/api/session/:id/message` and `/api/session/:id/context` use the snapshot projection before relay encryption.
|
|
52
54
|
- Browser clients connect to the WS endpoints above.
|
|
53
55
|
- OpenChamber still fetches OpenCode upstream event streams over SSE.
|
|
54
56
|
- The web server creates one shared global message-stream hub. OpenCode watcher side effects and global WS clients subscribe to that hub, so there is one upstream `/global/event` SSE reader for both server-side processing and browser fan-out.
|
|
@@ -21,6 +21,8 @@ export function acceptDirectoryMessageStreamWsConnection({
|
|
|
21
21
|
buildOpenCodeUrl,
|
|
22
22
|
getOpenCodeAuthHeaders,
|
|
23
23
|
processForwardedEventPayload,
|
|
24
|
+
/** Optional Host archive/metadata projection for session lifecycle events. */
|
|
25
|
+
projectOutboundSessionPayload = null,
|
|
24
26
|
wsClients,
|
|
25
27
|
triggerHealthCheck,
|
|
26
28
|
heartbeatIntervalMs,
|
|
@@ -78,14 +80,23 @@ export function acceptDirectoryMessageStreamWsConnection({
|
|
|
78
80
|
const run = async () => {
|
|
79
81
|
const forwardEvent = ({ envelope, payload }) => {
|
|
80
82
|
const directory = requestedDirectory || envelope?.directory || 'global';
|
|
83
|
+
let outbound = payload;
|
|
84
|
+
if (typeof projectOutboundSessionPayload === 'function') {
|
|
85
|
+
try {
|
|
86
|
+
const projected = projectOutboundSessionPayload(payload);
|
|
87
|
+
if (projected != null) outbound = projected;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn('[event-stream] directory session projection failed:', error?.message ?? error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
81
92
|
|
|
82
|
-
sendMessageStreamWsEvent(socket,
|
|
93
|
+
sendMessageStreamWsEvent(socket, outbound, {
|
|
83
94
|
directory,
|
|
84
95
|
eventId: typeof envelope?.eventId === 'string' && envelope.eventId.length > 0 ? envelope.eventId : undefined,
|
|
85
96
|
reasoningFilter,
|
|
86
97
|
});
|
|
87
98
|
|
|
88
|
-
processForwardedEventPayload(
|
|
99
|
+
processForwardedEventPayload(outbound, (syntheticPayload) => {
|
|
89
100
|
sendMessageStreamWsEvent(socket, syntheticPayload, {
|
|
90
101
|
directory: 'global',
|
|
91
102
|
reasoningFilter,
|
|
@@ -64,6 +64,13 @@ export function createGlobalMessageStreamHub({
|
|
|
64
64
|
upstreamReconnectDelayMs,
|
|
65
65
|
replayLimit = MESSAGE_STREAM_GLOBAL_REPLAY_LIMIT,
|
|
66
66
|
getRuntimeIdentity = null,
|
|
67
|
+
/**
|
|
68
|
+
* Optional Host authority projection for session.created / session.updated
|
|
69
|
+
* (and GlobalEvent wrap). Applied before fan-out and replay so browser and
|
|
70
|
+
* server subscribers share the same archive view. Must be sync and cheap.
|
|
71
|
+
* @type {((payload: unknown) => unknown) | null}
|
|
72
|
+
*/
|
|
73
|
+
projectOutboundSessionPayload = null,
|
|
67
74
|
}) {
|
|
68
75
|
const eventSubscribers = new Set();
|
|
69
76
|
const statusSubscribers = new Set();
|
|
@@ -97,12 +104,19 @@ export function createGlobalMessageStreamHub({
|
|
|
97
104
|
}
|
|
98
105
|
};
|
|
99
106
|
|
|
107
|
+
let sessionPayloadProjector = typeof projectOutboundSessionPayload === 'function'
|
|
108
|
+
? projectOutboundSessionPayload
|
|
109
|
+
: null;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Diff-summarize only. Host archive projection is applied at fan-out and
|
|
113
|
+
* replayAfter time against the *current* committed store — never baked into
|
|
114
|
+
* the replay buffer (stale archive stamps must not stick after Host writes).
|
|
115
|
+
*/
|
|
100
116
|
const normalizeEvent = ({ envelope, payload, connectionContext }) => {
|
|
101
117
|
const directory =
|
|
102
118
|
typeof envelope?.directory === 'string' && envelope.directory.length > 0 ? envelope.directory : 'global';
|
|
103
119
|
const eventId = typeof envelope?.eventId === 'string' && envelope.eventId.length > 0 ? envelope.eventId : undefined;
|
|
104
|
-
// Summarize FileDiff bodies before fan-out and replay buffering so reconnect
|
|
105
|
-
// replay cannot re-amplify full patch frames into >64KB WS payloads.
|
|
106
120
|
const outboundPayload = summarizeOutboundEventPayload(payload);
|
|
107
121
|
return {
|
|
108
122
|
envelope,
|
|
@@ -113,6 +127,19 @@ export function createGlobalMessageStreamHub({
|
|
|
113
127
|
};
|
|
114
128
|
};
|
|
115
129
|
|
|
130
|
+
const projectForOutbound = (payload) => {
|
|
131
|
+
if (typeof sessionPayloadProjector !== 'function') return payload;
|
|
132
|
+
try {
|
|
133
|
+
const projected = sessionPayloadProjector(payload);
|
|
134
|
+
// null = Host not ready; suppress this lifecycle frame.
|
|
135
|
+
if (projected == null) return null;
|
|
136
|
+
return projected;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.warn('[event-stream] session payload projection failed:', error?.message ?? error);
|
|
139
|
+
return payload;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
116
143
|
const start = () => {
|
|
117
144
|
if (reader) {
|
|
118
145
|
return;
|
|
@@ -127,7 +154,8 @@ export function createGlobalMessageStreamHub({
|
|
|
127
154
|
buildUrl: () => {
|
|
128
155
|
buildUrlFailed = false;
|
|
129
156
|
try {
|
|
130
|
-
|
|
157
|
+
// v2 serves the event stream at `/api/event`; `/api/global/event` is gone.
|
|
158
|
+
return new URL(buildOpenCodeUrl('/event', ''));
|
|
131
159
|
} catch {
|
|
132
160
|
buildUrlFailed = true;
|
|
133
161
|
throw new Error('OpenCode service unavailable');
|
|
@@ -160,6 +188,7 @@ export function createGlobalMessageStreamHub({
|
|
|
160
188
|
...event,
|
|
161
189
|
connectionContext: event?.connectionContext ?? activeConnectionRuntimeIdentity,
|
|
162
190
|
});
|
|
191
|
+
// Buffer the unprojected payload so reconnect replay uses current Host authority.
|
|
163
192
|
if (normalized.eventId) {
|
|
164
193
|
replay.push(normalized);
|
|
165
194
|
if (replay.length > replayLimit) {
|
|
@@ -167,8 +196,14 @@ export function createGlobalMessageStreamHub({
|
|
|
167
196
|
}
|
|
168
197
|
}
|
|
169
198
|
|
|
199
|
+
const projectedPayload = projectForOutbound(normalized.payload);
|
|
200
|
+
if (projectedPayload == null) return;
|
|
201
|
+
const outbound = projectedPayload === normalized.payload
|
|
202
|
+
? normalized
|
|
203
|
+
: { ...normalized, payload: projectedPayload };
|
|
204
|
+
|
|
170
205
|
for (const subscriber of Array.from(eventSubscribers)) {
|
|
171
|
-
notifySubscriber('event', subscriber,
|
|
206
|
+
notifySubscriber('event', subscriber, outbound);
|
|
172
207
|
}
|
|
173
208
|
},
|
|
174
209
|
onError(error) {
|
|
@@ -226,10 +261,21 @@ export function createGlobalMessageStreamHub({
|
|
|
226
261
|
}
|
|
227
262
|
|
|
228
263
|
const index = replay.findIndex((entry) => entry.eventId === eventId);
|
|
229
|
-
|
|
264
|
+
if (index === -1) return [];
|
|
265
|
+
// Re-project with current committed Host authority at replay time.
|
|
266
|
+
const out = [];
|
|
267
|
+
for (const entry of replay.slice(index + 1)) {
|
|
268
|
+
const projectedPayload = projectForOutbound(entry.payload);
|
|
269
|
+
if (projectedPayload == null) continue;
|
|
270
|
+
out.push(projectedPayload === entry.payload ? entry : { ...entry, payload: projectedPayload });
|
|
271
|
+
}
|
|
272
|
+
return out;
|
|
230
273
|
},
|
|
231
274
|
setRuntimeIdentityProvider(provider) {
|
|
232
275
|
runtimeIdentityProvider = typeof provider === 'function' ? provider : null;
|
|
233
276
|
},
|
|
277
|
+
setSessionPayloadProjector(projector) {
|
|
278
|
+
sessionPayloadProjector = typeof projector === 'function' ? projector : null;
|
|
279
|
+
},
|
|
234
280
|
};
|
|
235
281
|
}
|
|
@@ -96,6 +96,10 @@ function projectParts(parts) {
|
|
|
96
96
|
* @param {unknown} record
|
|
97
97
|
*/
|
|
98
98
|
function projectRecord(record) {
|
|
99
|
+
if (isPlainObject(record) && record.type === 'assistant' && Array.isArray(record.content)) {
|
|
100
|
+
const content = projectParts(record.content);
|
|
101
|
+
return content === record.content ? record : { ...record, content };
|
|
102
|
+
}
|
|
99
103
|
if (!isPlainObject(record) || !Array.isArray(record.parts)) return record;
|
|
100
104
|
const parts = projectParts(record.parts);
|
|
101
105
|
return parts === record.parts ? record : { ...record, parts };
|
|
@@ -126,6 +130,17 @@ export function projectMessagesPayloadWithoutReasoning(payload) {
|
|
|
126
130
|
let next = payload;
|
|
127
131
|
let changed = false;
|
|
128
132
|
|
|
133
|
+
if (Array.isArray(payload.data)) {
|
|
134
|
+
const data = projectMessagesPayloadWithoutReasoning(payload.data);
|
|
135
|
+
if (data !== payload.data) {
|
|
136
|
+
next = { ...next, data };
|
|
137
|
+
changed = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (payload.type === 'assistant' && Array.isArray(payload.content)) {
|
|
141
|
+
return projectRecord(payload);
|
|
142
|
+
}
|
|
143
|
+
|
|
129
144
|
if (Array.isArray(payload.records)) {
|
|
130
145
|
const records = projectMessagesPayloadWithoutReasoning(payload.records);
|
|
131
146
|
if (records !== payload.records) {
|
|
@@ -222,7 +237,7 @@ export function createReasoningOutboundFilter() {
|
|
|
222
237
|
const type = baseEventType(rawType);
|
|
223
238
|
|
|
224
239
|
// session.next.reasoning.* (incl. versioned …reasoning.delta.1)
|
|
225
|
-
if (type.startsWith('session.next.reasoning.')) return null;
|
|
240
|
+
if (type.startsWith('session.next.reasoning.') || type.startsWith('session.reasoning.')) return null;
|
|
226
241
|
|
|
227
242
|
if (type === 'message.part.updated') {
|
|
228
243
|
const body = eventBodyOf(payload);
|
|
@@ -13,6 +13,21 @@ import {
|
|
|
13
13
|
} from './reasoning-projection.js';
|
|
14
14
|
|
|
15
15
|
describe('shouldIncludeReasoning / query helpers', () => {
|
|
16
|
+
it('projects v2 list/context and exact snapshots without dropping rows or cursor', () => {
|
|
17
|
+
const row = { id: 'm', type: 'assistant', tokens: { reasoning: 12 }, content: [
|
|
18
|
+
{ type: 'reasoning', text: 'hidden' }, { type: 'text', text: 'visible' },
|
|
19
|
+
] };
|
|
20
|
+
const payload = { data: [row], cursor: { next: 'older' } };
|
|
21
|
+
const projected = projectMessagesPayloadForReasoning(payload, false);
|
|
22
|
+
expect(projected).toEqual({ data: [{ ...row, content: [row.content[1]] }], cursor: payload.cursor });
|
|
23
|
+
expect(projectMessagesPayloadForReasoning(row, false)).toEqual(projected.data[0]);
|
|
24
|
+
expect(projectMessagesPayloadForReasoning(payload, true)).toBe(payload);
|
|
25
|
+
expect(row.content).toHaveLength(2);
|
|
26
|
+
const filter = createReasoningOutboundFilter();
|
|
27
|
+
expect(filter.projectEvent({ type: 'session.reasoning.delta.1', data: { delta: 'hidden' } })).toBeNull();
|
|
28
|
+
const text = { type: 'session.text.delta', data: { delta: 'visible' } };
|
|
29
|
+
expect(filter.projectEvent(text)).toBe(text);
|
|
30
|
+
});
|
|
16
31
|
it('only treats the strict string false as disabled', () => {
|
|
17
32
|
expect(shouldIncludeReasoning(undefined)).toBe(true);
|
|
18
33
|
expect(shouldIncludeReasoning(null)).toBe(true);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
MESSAGE_STREAM_GLOBAL_WS_PATH,
|
|
7
7
|
MESSAGE_STREAM_WS_HEARTBEAT_INTERVAL_MS,
|
|
8
8
|
sendMessageStreamWsEvent,
|
|
9
|
+
sendMessageStreamWsFrame,
|
|
9
10
|
} from './protocol.js';
|
|
10
11
|
import { createGlobalMessageStreamHub } from './global-hub.js';
|
|
11
12
|
import { createGlobalMessageStreamWsBridge } from './global-ws-bridge.js';
|
|
@@ -62,6 +63,9 @@ export function createMessageStreamWsRuntime({
|
|
|
62
63
|
buildOpenCodeUrl,
|
|
63
64
|
getOpenCodeAuthHeaders,
|
|
64
65
|
processForwardedEventPayload,
|
|
66
|
+
projectOutboundSessionPayload = null,
|
|
67
|
+
/** Await Host metadata ready before directory WS attaches upstream. */
|
|
68
|
+
ensureSessionMetadataReady = null,
|
|
65
69
|
wsClients,
|
|
66
70
|
triggerHealthCheck,
|
|
67
71
|
heartbeatIntervalMs = MESSAGE_STREAM_WS_HEARTBEAT_INTERVAL_MS,
|
|
@@ -104,29 +108,62 @@ export function createMessageStreamWsRuntime({
|
|
|
104
108
|
const includeReasoning = shouldIncludeReasoning(requestUrl.searchParams.get('includeReasoning'));
|
|
105
109
|
const reasoningFilter = includeReasoning ? null : createReasoningOutboundFilter();
|
|
106
110
|
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
// Host metadata readiness gates *both* global and directory streams before
|
|
112
|
+
// accept/hub.start — otherwise globalBridge.accept → hub.start bypasses the
|
|
113
|
+
// gate and lifecycle frames can fan out while the store is still unknown.
|
|
114
|
+
void (async () => {
|
|
115
|
+
if (typeof ensureSessionMetadataReady === 'function') {
|
|
116
|
+
try {
|
|
117
|
+
const ready = await ensureSessionMetadataReady();
|
|
118
|
+
if (!ready) {
|
|
119
|
+
sendMessageStreamWsFrame(socket, {
|
|
120
|
+
type: 'error',
|
|
121
|
+
message: 'session metadata is unavailable',
|
|
122
|
+
code: 'session_metadata_unavailable',
|
|
123
|
+
retryable: true,
|
|
124
|
+
});
|
|
125
|
+
socket.close(1013, 'session metadata unavailable');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
sendMessageStreamWsFrame(socket, {
|
|
130
|
+
type: 'error',
|
|
131
|
+
message: 'session metadata is unavailable',
|
|
132
|
+
code: 'session_metadata_unavailable',
|
|
133
|
+
retryable: true,
|
|
134
|
+
});
|
|
135
|
+
socket.close(1013, 'session metadata unavailable');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (socket.readyState !== 1) return;
|
|
141
|
+
|
|
142
|
+
if (isGlobalStream) {
|
|
143
|
+
globalBridge.accept(socket, {
|
|
144
|
+
requestedLastEventId,
|
|
145
|
+
reasoningFilter,
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
acceptDirectoryMessageStreamWsConnection({
|
|
151
|
+
socket,
|
|
109
152
|
requestedLastEventId,
|
|
153
|
+
requestedDirectory,
|
|
110
154
|
reasoningFilter,
|
|
155
|
+
buildOpenCodeUrl,
|
|
156
|
+
getOpenCodeAuthHeaders,
|
|
157
|
+
processForwardedEventPayload,
|
|
158
|
+
projectOutboundSessionPayload,
|
|
159
|
+
wsClients,
|
|
160
|
+
triggerHealthCheck,
|
|
161
|
+
heartbeatIntervalMs,
|
|
162
|
+
upstreamStallTimeoutMs,
|
|
163
|
+
upstreamReconnectDelayMs,
|
|
164
|
+
fetchImpl,
|
|
111
165
|
});
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
acceptDirectoryMessageStreamWsConnection({
|
|
116
|
-
socket,
|
|
117
|
-
requestedLastEventId,
|
|
118
|
-
requestedDirectory,
|
|
119
|
-
reasoningFilter,
|
|
120
|
-
buildOpenCodeUrl,
|
|
121
|
-
getOpenCodeAuthHeaders,
|
|
122
|
-
processForwardedEventPayload,
|
|
123
|
-
wsClients,
|
|
124
|
-
triggerHealthCheck,
|
|
125
|
-
heartbeatIntervalMs,
|
|
126
|
-
upstreamStallTimeoutMs,
|
|
127
|
-
upstreamReconnectDelayMs,
|
|
128
|
-
fetchImpl,
|
|
129
|
-
});
|
|
166
|
+
})();
|
|
130
167
|
});
|
|
131
168
|
|
|
132
169
|
const upgradeHandler = (req, socket, head) => {
|
|
@@ -126,6 +126,118 @@ describe('event stream broadcaster', () => {
|
|
|
126
126
|
});
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
+
describe('message stream websocket runtime Host metadata readiness', () => {
|
|
130
|
+
it('does not accept/start global hub while metadata load is pending', async () => {
|
|
131
|
+
const server = new EventEmitter();
|
|
132
|
+
const wsClients = new Set();
|
|
133
|
+
let fetchCalls = 0;
|
|
134
|
+
let releaseReady;
|
|
135
|
+
const readyGate = new Promise((resolve) => { releaseReady = resolve; });
|
|
136
|
+
|
|
137
|
+
const runtime = createMessageStreamWsRuntime({
|
|
138
|
+
server,
|
|
139
|
+
uiAuthController: null,
|
|
140
|
+
isRequestOriginAllowed: async () => true,
|
|
141
|
+
rejectWebSocketUpgrade() {},
|
|
142
|
+
buildOpenCodeUrl: (path) => `http://127.0.0.1:4096${path}`,
|
|
143
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
144
|
+
processForwardedEventPayload() {},
|
|
145
|
+
ensureSessionMetadataReady: async () => {
|
|
146
|
+
await readyGate;
|
|
147
|
+
return true;
|
|
148
|
+
},
|
|
149
|
+
wsClients,
|
|
150
|
+
upstreamReconnectDelayMs: 0,
|
|
151
|
+
fetchImpl: async (_url, options) => {
|
|
152
|
+
fetchCalls += 1;
|
|
153
|
+
return createSseResponse({
|
|
154
|
+
signal: options.signal,
|
|
155
|
+
holdOpen: true,
|
|
156
|
+
blocks: [
|
|
157
|
+
'id: evt-1\ndata: {"type":"session.updated","properties":{"info":{"id":"ses_1","time":{}}}}\n\n',
|
|
158
|
+
],
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const socket = new FakeSocket();
|
|
164
|
+
runtime.wsServer.emit('connection', socket, { url: '/api/global/event/ws' });
|
|
165
|
+
await new Promise((resolve) => setTimeout(resolve, 15));
|
|
166
|
+
|
|
167
|
+
// Still pending → no hub.start → no upstream fetch, no accept frames yet.
|
|
168
|
+
expect(fetchCalls).toBe(0);
|
|
169
|
+
expect(socket.closeCalls).toEqual([]);
|
|
170
|
+
expect(socket.sent.some((f) => f.type === 'event')).toBe(false);
|
|
171
|
+
|
|
172
|
+
releaseReady(true);
|
|
173
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
174
|
+
|
|
175
|
+
expect(fetchCalls).toBe(1);
|
|
176
|
+
expect(socket.sent.some((f) => f.type === 'event'
|
|
177
|
+
&& f.payload?.type === 'session.updated')).toBe(true);
|
|
178
|
+
|
|
179
|
+
socket.close();
|
|
180
|
+
await runtime.close();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('rejects global connection when metadata ready fails', async () => {
|
|
184
|
+
const server = new EventEmitter();
|
|
185
|
+
const runtime = createMessageStreamWsRuntime({
|
|
186
|
+
server,
|
|
187
|
+
uiAuthController: null,
|
|
188
|
+
isRequestOriginAllowed: async () => true,
|
|
189
|
+
rejectWebSocketUpgrade() {},
|
|
190
|
+
buildOpenCodeUrl: (path) => `http://127.0.0.1:4096${path}`,
|
|
191
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
192
|
+
processForwardedEventPayload() {},
|
|
193
|
+
ensureSessionMetadataReady: async () => false,
|
|
194
|
+
wsClients: new Set(),
|
|
195
|
+
upstreamReconnectDelayMs: 0,
|
|
196
|
+
fetchImpl: async () => {
|
|
197
|
+
throw new Error('upstream must not start');
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const socket = new FakeSocket();
|
|
202
|
+
runtime.wsServer.emit('connection', socket, { url: '/api/global/event/ws' });
|
|
203
|
+
await new Promise((resolve) => setTimeout(resolve, 15));
|
|
204
|
+
|
|
205
|
+
expect(socket.sent.some((f) => f.type === 'error'
|
|
206
|
+
&& f.code === 'session_metadata_unavailable'
|
|
207
|
+
&& f.retryable === true)).toBe(true);
|
|
208
|
+
expect(socket.closeCalls.some((c) => c.code === 1013)).toBe(true);
|
|
209
|
+
|
|
210
|
+
await runtime.close();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('gates directory stream the same way as global', async () => {
|
|
214
|
+
const server = new EventEmitter();
|
|
215
|
+
const runtime = createMessageStreamWsRuntime({
|
|
216
|
+
server,
|
|
217
|
+
uiAuthController: null,
|
|
218
|
+
isRequestOriginAllowed: async () => true,
|
|
219
|
+
rejectWebSocketUpgrade() {},
|
|
220
|
+
buildOpenCodeUrl: (path) => `http://127.0.0.1:4096${path}`,
|
|
221
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
222
|
+
processForwardedEventPayload() {},
|
|
223
|
+
ensureSessionMetadataReady: async () => false,
|
|
224
|
+
wsClients: new Set(),
|
|
225
|
+
upstreamReconnectDelayMs: 0,
|
|
226
|
+
fetchImpl: async () => {
|
|
227
|
+
throw new Error('upstream must not start');
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const socket = new FakeSocket();
|
|
232
|
+
runtime.wsServer.emit('connection', socket, { url: '/api/event/ws?directory=/repo' });
|
|
233
|
+
await new Promise((resolve) => setTimeout(resolve, 15));
|
|
234
|
+
|
|
235
|
+
expect(socket.sent.some((f) => f.code === 'session_metadata_unavailable')).toBe(true);
|
|
236
|
+
expect(socket.closeCalls.some((c) => c.code === 1013)).toBe(true);
|
|
237
|
+
await runtime.close();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
129
241
|
describe('message stream websocket runtime', () => {
|
|
130
242
|
it('filters reasoning events per connection when includeReasoning=false', async () => {
|
|
131
243
|
const server = new EventEmitter();
|