@vantran-se/openchamber-web 1.24.4 → 2.0.2
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 +2 -8
- package/bin/lib/commands-connect-url.js +3 -8
- package/bin/lib/commands-tunnel.js +3 -0
- package/dist/assets/{CodeMirrorCommentWidgets-B4a0JkUz.js → CodeMirrorCommentWidgets-B82Oye3V.js} +1 -1
- package/dist/assets/DiffView-DJLmWn6Z.js +4 -0
- package/dist/assets/FilesView-DjksmZRo.js +17 -0
- package/dist/assets/{GitView-Byu2DY7s.js → GitView-DiUsUIgr.js} +4 -4
- package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-CYwWYLr3.js} +1 -1
- package/dist/assets/GuestSurfacePane-CmctyRI4.js +1 -0
- package/dist/assets/{JsonTreeView-VW_7czo3.js → JsonTreeView-DOHHcbgJ.js} +1 -1
- package/dist/assets/LinearIssuesView-DaqnylcV.js +1 -0
- package/dist/assets/MarkdownImageGallery-HJNvWt7L.js +1 -0
- package/dist/assets/MarkdownRendererImpl-nDZRGg2I.js +12 -0
- package/dist/assets/OnboardingScreen-C2B5Bsof.js +1 -0
- package/dist/assets/{PatchDiff-DCjGhlYV.js → PatchDiff-C4FLBjpt.js} +1 -1
- package/dist/assets/{PierreDiffViewer-CEDZNkU2.js → PierreDiffViewer-C_1S9c9n.js} +1 -1
- package/dist/assets/PlanView-_eZvb-G5.js +5 -0
- package/dist/assets/PluginPane-sM8fcZIV.js +3 -0
- package/dist/assets/SettingsView-BRfr46hb.js +29 -0
- package/dist/assets/SettingsWindow-CcUDwQHg.js +1 -0
- package/dist/assets/SubmoduleDiffSummary-CrN1ZGwl.js +1 -0
- package/dist/assets/ToolOutputDialog-Cij4jI09.js +15 -0
- package/dist/assets/ToolPartDiffPreview-BXzwQ-JY.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-NU0FHFhT.js → VirtualizedFileDiff-DSWTLJNH.js} +4 -4
- package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-CpUUgY8H.js} +2 -2
- package/dist/assets/appThemeRegistry-B5tHTkR8.js +1 -0
- package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-D7D8qV8p.js} +1 -1
- package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.js → computeVirtualFileMetrics-cq9uQ30i.js} +1 -1
- package/dist/assets/de-CUO1-KqF.js +10 -0
- package/dist/assets/es-BlbC6sGJ.js +16 -0
- package/dist/assets/fr-D2nwe_0W.js +14 -0
- package/dist/assets/index-BQOoYIOu.js +1 -0
- package/dist/assets/index-CLcM8HKx.css +1 -0
- package/dist/assets/index-CvLgnuNI.js +41 -0
- package/dist/assets/{isDiffPlainText-CO4e5J_S.js → isDiffPlainText-D0obJ2p8.js} +1 -1
- package/dist/assets/{isFilePlainText-ue6kCou7.js → isFilePlainText-DB3_rjyW.js} +1 -1
- package/dist/assets/ja-CA7vnDsZ.js +16 -0
- package/dist/assets/ko-Chy6a9l-.js +16 -0
- package/dist/assets/main-B9JFK3vF.js +896 -0
- package/dist/assets/main-Bhiqnrhd.js +2 -0
- package/dist/assets/{markdownCore-CK48LG0z.js → markdownCore-tfqOFaBd.js} +4 -4
- package/dist/assets/miniChat-B0UOL5zA.js +2 -0
- package/dist/assets/mobile-BJppFIEo.js +2 -0
- package/dist/assets/{patchFileDiff-D7QVlovD.js → patchFileDiff-Db6KuwCP.js} +3 -3
- package/dist/assets/pl-CTR5A_1P.js +16 -0
- package/dist/assets/pt-BR-BE1O1KEx.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-Yz5sfoFh.js +2 -0
- package/dist/assets/renderMobileApp-QQ-yB8aY.js +25 -0
- package/dist/assets/runtimeEndpointReset-DKDdzRzm.js +27 -0
- package/dist/assets/scheduledTasksApi-C5sCuNqf.js +1 -0
- package/dist/assets/{shikiHighlight-BPu3JiQs.js → shikiHighlight-xURoo5t_.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.js → textMateThemeFromAppTheme-BZLGuYTU.js} +1 -1
- package/dist/assets/tr-Bh5vK6cW.js +7 -0
- package/dist/assets/uk-CLDkZFy_.js +16 -0
- package/dist/assets/useAppFontEffects-BxrtCnfR.js +7337 -0
- package/dist/assets/{useInlineCommentController-CgS0fnw_.js → useInlineCommentController-CeXuR-SR.js} +2 -2
- package/dist/assets/useRootScrollLock-DgWxezc1.js +2 -0
- package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.js → vendor-codemirror-language-data-DOuaQUZ0.js} +2 -2
- package/dist/assets/vendor-cron-parser-CjvYVigk.js +2 -0
- package/dist/assets/vendor-opencode-client-_BAHQo4R.js +12 -0
- package/dist/assets/{vendor-remixicon-react-BDmGmFCA.js → vendor-remixicon-react-D6dZ0UwF.js} +1 -1
- package/dist/assets/{vendor-zod-CgAdXWZO.js → vendor-zod-Gea9uBlY.js} +1 -1
- package/dist/assets/{web-components-DxX8bZ1o.js → web-components-63H1h21j.js} +2 -2
- package/dist/assets/zh-CN-DODgcmYj.js +16 -0
- package/dist/assets/zh-TW-ByNfIwLA.js +16 -0
- package/dist/index.html +13 -7
- package/dist/mini-chat.html +5 -5
- package/dist/mobile.html +5 -5
- package/package.json +6 -5
- package/server/index.d.ts +1 -0
- package/server/index.js +246 -91
- package/server/lib/agent-tool/DOCUMENTATION.md +32 -15
- package/server/lib/agent-tool/runtime.js +135 -54
- package/server/lib/agent-tool/runtime.test.js +155 -87
- package/server/lib/browser-control/DOCUMENTATION.md +37 -2
- package/server/lib/browser-control/provider.js +217 -0
- package/server/lib/browser-control/provider.test.js +251 -0
- package/server/lib/context-obligatory/runtime.js +61 -50
- package/server/lib/context-obligatory/runtime.test.js +62 -162
- package/server/lib/event-stream/DOCUMENTATION.md +12 -1
- package/server/lib/event-stream/delta-coalescer.js +0 -0
- package/server/lib/event-stream/delta-coalescer.test.js +68 -57
- package/server/lib/event-stream/directory-ws-bridge.js +1 -1
- package/server/lib/event-stream/global-hub.js +10 -1
- package/server/lib/event-stream/global-hub.test.js +27 -26
- package/server/lib/event-stream/index.js +6 -0
- package/server/lib/event-stream/protocol.js +26 -19
- package/server/lib/event-stream/protocol.test.js +18 -7
- package/server/lib/event-stream/rebind.test.js +5 -5
- package/server/lib/event-stream/runtime.test.js +17 -15
- package/server/lib/event-stream/translate-v2.js +313 -0
- package/server/lib/event-stream/translate-v2.test.js +209 -0
- package/server/lib/event-stream/upstream-reader.test.js +16 -16
- package/server/lib/fs/DOCUMENTATION.md +4 -2
- package/server/lib/fs/byte-range.js +35 -0
- package/server/lib/fs/byte-range.test.js +31 -0
- package/server/lib/fs/routes.js +47 -42
- package/server/lib/fs/routes.test.js +123 -18
- package/server/lib/git/service.js +12 -0
- package/server/lib/github/DOCUMENTATION.md +1 -1
- package/server/lib/github/pr-status.js +22 -2
- package/server/lib/github/pr-status.test.js +27 -14
- package/server/lib/guests/DOCUMENTATION.md +13 -0
- package/server/lib/guests/catalog.js +12 -0
- package/server/lib/guests/catalog.test.js +33 -0
- package/server/lib/guests/routes.js +10 -1
- package/server/lib/guests/service.js +192 -14
- package/server/lib/guests/service.test.js +201 -1
- package/server/lib/guests/surface.js +630 -0
- package/server/lib/guests/surface.test.js +486 -0
- package/server/lib/markdown-image-grants/routes.js +18 -5
- package/server/lib/markdown-image-grants/routes.test.js +9 -6
- package/server/lib/message-queue/DOCUMENTATION.md +33 -22
- package/server/lib/message-queue/runtime.js +155 -100
- package/server/lib/message-queue/runtime.test.js +168 -82
- package/server/lib/notifications/DOCUMENTATION.md +28 -0
- package/server/lib/notifications/control-stream.http.test.js +68 -0
- package/server/lib/notifications/emitter-runtime.js +15 -0
- package/server/lib/notifications/emitter-runtime.test.js +32 -0
- package/server/lib/notifications/push-runtime.js +5 -1
- package/server/lib/notifications/push-runtime.test.js +2 -2
- package/server/lib/notifications/routes.js +1 -1
- package/server/lib/notifications/runtime.js +115 -26
- package/server/lib/notifications/template-runtime.js +13 -15
- package/server/lib/notifications/template-runtime.test.js +16 -9
- package/server/lib/notifications/trigger-runtime.test.js +187 -0
- package/server/lib/openchamber-control/DOCUMENTATION.md +10 -0
- package/server/lib/openchamber-control/actions.js +3 -2
- package/server/lib/openchamber-control/error.js +1 -1
- package/server/lib/openchamber-control/file-open.js +59 -0
- package/server/lib/openchamber-control/file-open.test.js +57 -0
- package/server/lib/openchamber-control/routes.test.js +12 -0
- package/server/lib/openchamber-control/service.js +102 -49
- package/server/lib/openchamber-control/service.test.js +84 -44
- package/server/lib/openchamber-sessions/archive-store.js +224 -0
- package/server/lib/openchamber-sessions/archive-store.test.js +187 -0
- package/server/lib/openchamber-sessions/opencode-client.js +22 -0
- package/server/lib/openchamber-sessions/routes.js +398 -328
- package/server/lib/openchamber-sessions/routes.test.js +777 -740
- package/server/lib/openchamber-sessions/session-metadata-store.js +285 -0
- package/server/lib/openchamber-sessions/session-metadata-store.test.js +259 -0
- package/server/lib/opencode/DOCUMENTATION.md +449 -29
- package/server/lib/opencode/agents.js +233 -327
- package/server/lib/opencode/auth.js +41 -44
- package/server/lib/opencode/auth.test.js +92 -0
- package/server/lib/opencode/cli-upgrade.d.ts +4 -0
- package/server/lib/opencode/cli-upgrade.js +18 -0
- package/server/lib/opencode/cli-upgrade.test.js +36 -0
- package/server/lib/opencode/commands.js +164 -145
- package/server/lib/opencode/compatibility.d.ts +16 -0
- package/server/lib/opencode/compatibility.js +88 -0
- package/server/lib/opencode/compatibility.test.js +62 -0
- package/server/lib/opencode/config-entity-routes.js +58 -29
- package/server/lib/opencode/config-entity-v2.test.js +290 -0
- package/server/lib/opencode/config-mutation-response.js +17 -7
- package/server/lib/opencode/config-mutation-response.test.js +4 -7
- package/server/lib/opencode/config-paths.test.js +2 -2
- package/server/lib/opencode/config-v2.d.ts +183 -0
- package/server/lib/opencode/config-v2.js +801 -0
- package/server/lib/opencode/config-v2.test.js +466 -0
- package/server/lib/opencode/credential-db.js +144 -0
- package/server/lib/opencode/credential-db.test.js +104 -0
- package/server/lib/opencode/env-runtime.js +48 -9
- package/server/lib/opencode/env-runtime.test.js +45 -0
- package/server/lib/opencode/feature-routes-runtime.js +15 -5
- package/server/lib/opencode/lifecycle-process.test.js +7 -4
- package/server/lib/opencode/lifecycle.js +85 -30
- package/server/lib/opencode/lifecycle.test.js +80 -8
- package/server/lib/opencode/login-shell-env.js +27 -0
- package/server/lib/opencode/managed-config-file.js +119 -0
- package/server/lib/opencode/managed-config-file.test.js +152 -0
- package/server/lib/opencode/managed-plugin-config.js +33 -13
- package/server/lib/opencode/mcp.js +43 -149
- package/server/lib/opencode/network-runtime.js +6 -7
- package/server/lib/opencode/openchamber-routes.test.js +2 -5
- package/server/lib/opencode/plugin-routes.js +3 -3
- package/server/lib/opencode/plugin-routes.test.js +6 -24
- package/server/lib/opencode/plugins.js +91 -40
- package/server/lib/opencode/plugins.test.js +28 -15
- package/server/lib/opencode/providers.js +63 -83
- package/server/lib/opencode/providers.test.js +128 -59
- package/server/lib/opencode/proxy-agent-wiring.test.js +4 -2
- package/server/lib/opencode/proxy.js +179 -129
- package/server/lib/opencode/pwa-manifest-routes.js +4 -2
- package/server/lib/opencode/pwa-manifest-routes.test.js +3 -2
- package/server/lib/opencode/response-envelope.js +14 -0
- package/server/lib/opencode/response-envelope.test.js +23 -0
- package/server/lib/opencode/routes-upgrade.test.js +73 -116
- package/server/lib/opencode/routes.js +121 -428
- package/server/lib/opencode/server-utils-runtime.js +10 -4
- package/server/lib/opencode/session-activity.js +78 -0
- package/server/lib/opencode/session-runtime.js +21 -10
- package/server/lib/opencode/session-runtime.test.js +9 -9
- package/server/lib/opencode/settings-helpers.js +9 -9
- package/server/lib/opencode/settings-helpers.test.js +7 -12
- package/server/lib/opencode/settings-registry.json +6 -8
- package/server/lib/opencode/settings-runtime.js +18 -1
- package/server/lib/opencode/settings-runtime.test.js +31 -2
- package/server/lib/opencode/shared.js +54 -22
- package/server/lib/opencode/shared.test.js +8 -4
- package/server/lib/opencode/shutdown-runtime.js +54 -7
- package/server/lib/opencode/shutdown-runtime.test.js +224 -30
- package/server/lib/opencode/skill-routes.js +23 -24
- package/server/lib/opencode/skills.js +7 -5
- package/server/lib/opencode/tunnel-wiring-runtime.js +2 -1
- package/server/lib/opencode/upgrade-capability.test.js +1 -1
- package/server/lib/opencode/v1-migration-topup.js +288 -0
- package/server/lib/opencode/v1-migration-topup.test.js +266 -0
- package/server/lib/opencode/v2-install.d.ts +8 -0
- package/server/lib/opencode/v2-install.js +158 -0
- package/server/lib/opencode/v2-install.test.js +124 -0
- package/server/lib/opencode/watcher.js +10 -3
- package/server/lib/opencode/watcher.test.js +17 -21
- package/server/lib/opencode/websearch-config.js +66 -0
- package/server/lib/package-manager.js +55 -68
- package/server/lib/package-manager.test.js +241 -75
- package/server/lib/permission-auto-accept/runtime.js +38 -22
- package/server/lib/permission-auto-accept/runtime.test.js +39 -17
- package/server/lib/quota/DOCUMENTATION.md +4 -0
- package/server/lib/quota/providers/xai.js +4 -4
- package/server/lib/relay/tunnel-host.js +4 -1
- package/server/lib/relay/tunnel-host.test.js +7 -0
- package/server/lib/routing/DOCUMENTATION.md +52 -21
- package/server/lib/routing/defaults.js +23 -6
- package/server/lib/routing/jev.js +17 -7
- package/server/lib/routing/routes.http.test.js +68 -33
- package/server/lib/routing/routes.js +65 -22
- package/server/lib/routing/runtime.js +145 -48
- package/server/lib/routing/runtime.test.js +79 -55
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +4 -0
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +51 -46
- package/server/lib/scheduled-tasks/runtime.js +81 -74
- package/server/lib/scheduled-tasks/runtime.test.js +53 -1
- package/server/lib/session-assist/DOCUMENTATION.md +8 -3
- package/server/lib/session-assist/context.js +122 -46
- package/server/lib/session-assist/context.test.js +117 -49
- package/server/lib/session-assist/runtime.js +52 -22
- package/server/lib/session-assist/runtime.test.js +145 -173
- package/server/lib/session-goal/DOCUMENTATION.md +23 -15
- package/server/lib/session-goal/create.js +9 -12
- package/server/lib/session-goal/create.test.js +27 -28
- package/server/lib/session-goal/runtime.js +164 -77
- package/server/lib/session-goal/runtime.test.js +271 -519
- package/server/lib/session-knowledge/DOCUMENTATION.md +5 -0
- package/server/lib/session-knowledge/runtime.js +59 -41
- package/server/lib/session-knowledge/runtime.test.js +90 -20
- package/server/lib/small-model/DOCUMENTATION.md +154 -208
- package/server/lib/small-model/client.js +113 -0
- package/server/lib/small-model/index.js +296 -158
- package/server/lib/small-model/index.test.js +423 -256
- package/server/lib/small-model/routes.js +1 -1
- package/server/lib/spaces/DOCUMENTATION.md +807 -0
- package/server/lib/spaces/code-in-bait.js +185 -0
- package/server/lib/spaces/code-in.js +641 -0
- package/server/lib/spaces/code-in.test.js +1048 -0
- package/server/lib/spaces/errors.js +8 -0
- package/server/lib/spaces/exec-http.js +94 -0
- package/server/lib/spaces/gatekeeper-channel.js +202 -0
- package/server/lib/spaces/gatekeeper-channel.test.js +215 -0
- package/server/lib/spaces/gatekeeper-program.cjs +914 -0
- package/server/lib/spaces/gatekeeper-program.test.js +1391 -0
- package/server/lib/spaces/hardening.js +457 -0
- package/server/lib/spaces/hardening.test.js +368 -0
- package/server/lib/spaces/host-git.js +127 -0
- package/server/lib/spaces/host-git.test.js +137 -0
- package/server/lib/spaces/labels.js +234 -0
- package/server/lib/spaces/labels.test.js +219 -0
- package/server/lib/spaces/layout.js +184 -0
- package/server/lib/spaces/layout.test.js +30 -0
- package/server/lib/spaces/manager.js +82 -0
- package/server/lib/spaces/manager.test.js +114 -0
- package/server/lib/spaces/places/code-in.docker.live.test.js +216 -0
- package/server/lib/spaces/places/contract-suite.js +156 -0
- package/server/lib/spaces/places/contract.docker.live.test.js +7 -0
- package/server/lib/spaces/places/contract.test.js +6 -0
- package/server/lib/spaces/places/docker-engine.js +126 -0
- package/server/lib/spaces/places/docker-live-support.js +202 -0
- package/server/lib/spaces/places/docker-tools.js +186 -0
- package/server/lib/spaces/places/docker.js +587 -0
- package/server/lib/spaces/places/docker.test.js +1610 -0
- package/server/lib/spaces/places/escape-suite.js +1018 -0
- package/server/lib/spaces/places/escape.docker.live.test.js +7 -0
- package/server/lib/spaces/places/fake-docker.js +375 -0
- package/server/lib/spaces/places/memory-place.js +103 -0
- package/server/lib/spaces/places/packed.docker.live.test.js +77 -0
- package/server/lib/spaces/places/registry.js +43 -0
- package/server/lib/spaces/places/registry.test.js +50 -0
- package/server/lib/spaces/places/server.docker.live.test.js +225 -0
- package/server/lib/spaces/run-command.js +261 -0
- package/server/lib/spaces/run-command.test.js +396 -0
- package/server/lib/spaces/space-server.js +190 -0
- package/server/lib/spaces/space-server.test.js +335 -0
- package/server/lib/spaces/tools-filler.js +82 -0
- package/server/lib/spaces/tools-filler.test.js +148 -0
- package/server/lib/spaces/tools-pack.js +49 -0
- package/server/lib/spaces/tools-pack.test.js +57 -0
- package/server/lib/spaces/tools.js +138 -0
- package/server/lib/spaces/tools.test.js +153 -0
- package/server/lib/tts/openai-sdk.js +11 -0
- package/server/lib/tts/service.js +7 -5
- package/server/lib/tts/stt.js +3 -2
- package/server/lib/tunnels/DOCUMENTATION.md +2 -0
- package/server/lib/tunnels/routes.js +7 -0
- package/server/lib/tunnels/routes.test.js +55 -0
- package/server/lib/ui-auth/ui-auth.js +38 -2
- package/server/lib/ui-auth/ui-auth.test.js +42 -0
- package/server/lib/ui-auth/ui-passkeys.js +12 -10
- package/server/lib/walkthrough/DOCUMENTATION.md +10 -1
- package/server/lib/walkthrough/pull-request.js +61 -8
- package/server/lib/walkthrough/pull-request.test.js +54 -1
- package/server/lib/walkthrough/routes.js +20 -0
- package/server/lib/walkthrough/routes.test.js +19 -0
- package/server/opencode-proxy.test.js +190 -166
- package/server/opencode-worktree-readiness.test.js +5 -4
- package/dist/assets/DiffView-B2CD9wC8.js +0 -4
- package/dist/assets/FilesView-C4vMFclc.js +0 -13
- package/dist/assets/LinearIssuesView-EzGNCGrd.js +0 -1
- package/dist/assets/MarkdownImageGallery-B2YUDFaB.js +0 -1
- package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +0 -12
- package/dist/assets/OnboardingScreen-CQHBSTEi.js +0 -1
- package/dist/assets/PlanView-D3x-pTkZ.js +0 -5
- package/dist/assets/PluginPane-DEcgDQ9h.js +0 -3
- package/dist/assets/SettingsView-CSinuH-1.js +0 -29
- package/dist/assets/SettingsWindow-B5CHoJFZ.js +0 -1
- package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +0 -1
- package/dist/assets/ToolOutputDialog-tLNWmfcW.js +0 -15
- package/dist/assets/ToolPartDiffPreview-CflzmK-x.js +0 -8
- package/dist/assets/appThemeRegistry-DLicw4dH.js +0 -1
- package/dist/assets/de-Bro6_zyl.js +0 -10
- package/dist/assets/es-Bb9XQZvd.js +0 -16
- package/dist/assets/fr-Dgfzn-IL.js +0 -14
- package/dist/assets/index-CtQNmqUL.js +0 -1
- package/dist/assets/index-DqaMWNrC.css +0 -1
- package/dist/assets/index-Dqc6USl3.js +0 -40
- package/dist/assets/ja-BYsakGFh.js +0 -16
- package/dist/assets/ko-BSaQz9aN.js +0 -16
- package/dist/assets/main-B62_pjL5.js +0 -2
- package/dist/assets/main-BEdcOI2P.js +0 -896
- package/dist/assets/miniChat-LT8Rqofr.js +0 -2
- package/dist/assets/mobile-CqarEynh.js +0 -2
- package/dist/assets/pl-OIviPdoj.js +0 -16
- package/dist/assets/pt-BR-Cltg-E6s.js +0 -16
- package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +0 -2
- package/dist/assets/renderMobileApp-BC1MHg6g.js +0 -25
- package/dist/assets/runtimeEndpointReset-DdrazOVH.js +0 -25
- package/dist/assets/scheduledTasksApi-B2SWMGEU.js +0 -1
- package/dist/assets/tr-CQI-KPre.js +0 -7
- package/dist/assets/uk-CqW7R4DA.js +0 -16
- package/dist/assets/useAppFontEffects-ne84JBVL.js +0 -7333
- package/dist/assets/useRootScrollLock-Cpf1qoGp.js +0 -2
- package/dist/assets/vendor-cron-parser-CUcP7OfX.js +0 -2
- package/dist/assets/vendor-opencode-sdk-BJ73u-bH.js +0 -7
- package/dist/assets/zh-CN-CVZG_o_J.js +0 -16
- package/dist/assets/zh-TW-D5ZxzCZp.js +0 -16
- package/server/lib/opencode/mcp-oauth-callback.test.js +0 -111
- package/server/lib/routing/feature-flag.js +0 -11
- package/server/lib/small-model/call.js +0 -851
- package/server/lib/small-model/call.test.js +0 -1292
- package/server/lib/small-model/catalog.js +0 -13
- package/server/lib/small-model/resolve.js +0 -131
- package/server/lib/small-model/resolve.test.js +0 -197
- package/server/lib/small-model/runtime-providers.js +0 -164
- package/server/lib/small-model/runtime-providers.test.js +0 -118
- package/server/lib/system-prompt/DOCUMENTATION.md +0 -36
- package/server/lib/system-prompt/runtime.js +0 -49
- package/server/lib/system-prompt/runtime.test.js +0 -76
- package/server/lib/walkthrough/reproduce-2607.test.js +0 -148
|
@@ -2,9 +2,9 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
import { createRoutingRuntime, requestTextOf } from './runtime.js';
|
|
3
3
|
import { resolveEffectiveConfig } from './store.js';
|
|
4
4
|
import { excerptHead, excerptHeadTail, turnsToHistory } from './history.js';
|
|
5
|
-
import { decidePermission, decideRouting } from './jev.js';
|
|
5
|
+
import { createJevClient, decidePermission, decideRouting } from './jev.js';
|
|
6
6
|
|
|
7
|
-
const AUTO = { providerID: 'openchamber',
|
|
7
|
+
const AUTO = { providerID: 'openchamber', id: 'auto' };
|
|
8
8
|
const FALLBACK = { model: { providerID: 'anthropic', modelID: 'claude-sonnet-5' }, variant: 'medium' };
|
|
9
9
|
|
|
10
10
|
const readyConfig = () => {
|
|
@@ -18,8 +18,7 @@ const readyConfig = () => {
|
|
|
18
18
|
return config;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
const makeRuntime = ({ config = readyConfig(), token = 'key', answers, askError
|
|
22
|
-
process.env.OPENCHAMBER_ROUTING_ENABLE = flag;
|
|
21
|
+
const makeRuntime = ({ config = readyConfig(), token = 'key', answers, askError } = {}) => {
|
|
23
22
|
const events = [];
|
|
24
23
|
const store = {
|
|
25
24
|
readConfig: vi.fn(async () => config),
|
|
@@ -41,16 +40,15 @@ const makeRuntime = ({ config = readyConfig(), token = 'key', answers, askError,
|
|
|
41
40
|
};
|
|
42
41
|
|
|
43
42
|
describe('requestTextOf', () => {
|
|
44
|
-
it('
|
|
45
|
-
expect(requestTextOf({
|
|
46
|
-
{ type: 'text', text: 'fix the typo' },
|
|
47
|
-
{ type: 'text', text: 'injected', synthetic: true },
|
|
48
|
-
{ type: 'file', url: 'data:...' },
|
|
49
|
-
{ type: 'text', text: 'in README' },
|
|
50
|
-
] })).toBe('fix the typo\n\nin README');
|
|
43
|
+
it('reads the prompt text a v2 send carries', () => {
|
|
44
|
+
expect(requestTextOf({ text: ' fix the typo in README ', files: [{ uri: 'data:...' }] })).toBe('fix the typo in README');
|
|
51
45
|
});
|
|
52
|
-
it('renders a
|
|
53
|
-
expect(requestTextOf({
|
|
46
|
+
it('renders a v2 command body (`name` plus `text`) as the slash command', () => {
|
|
47
|
+
expect(requestTextOf({ name: 'review', text: ' src ' })).toBe('/review src');
|
|
48
|
+
});
|
|
49
|
+
it('keeps the command name when it has no arguments', () => {
|
|
50
|
+
expect(requestTextOf({ name: 'review', text: '' })).toBe('/review');
|
|
51
|
+
expect(requestTextOf({ name: 'review' })).toBe('/review');
|
|
54
52
|
});
|
|
55
53
|
});
|
|
56
54
|
|
|
@@ -85,63 +83,91 @@ describe('decisions', () => {
|
|
|
85
83
|
});
|
|
86
84
|
});
|
|
87
85
|
|
|
88
|
-
describe('
|
|
86
|
+
describe('resolveAutoSelection', () => {
|
|
87
|
+
const send = (extra = {}) => ({ sessionId: 's1', model: AUTO, requestText: 'find the root cause', ...extra });
|
|
88
|
+
|
|
89
89
|
it('leaves a real model untouched and does not consult Jev', async () => {
|
|
90
90
|
const { runtime, jev } = makeRuntime({ answers: {} });
|
|
91
|
-
|
|
92
|
-
expect(await runtime.resolvePromptBody(body, { sessionId: 's1' })).toBeNull();
|
|
93
|
-
expect(body.model.modelID).toBe('claude-opus-5');
|
|
91
|
+
expect(await runtime.resolveAutoSelection(send({ model: { providerID: 'anthropic', id: 'claude-opus-5' } }))).toBeNull();
|
|
94
92
|
expect(jev.ask).not.toHaveBeenCalled();
|
|
95
93
|
});
|
|
96
94
|
|
|
97
|
-
it('
|
|
95
|
+
it('answers with the routed category model, variant and agent', async () => {
|
|
98
96
|
const { runtime, events } = makeRuntime({ answers: { category: { choice: 'hard', confidence: 0.97 } } });
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
expect(
|
|
102
|
-
expect(decision).toMatchObject({ category: 'hard', reason: 'routed', confidence: 0.97 });
|
|
97
|
+
const resolved = await runtime.resolveAutoSelection(send({ agent: 'build' }));
|
|
98
|
+
expect(resolved.model).toEqual({ providerID: 'openai', id: 'gpt-6-astra', variant: 'high' });
|
|
99
|
+
expect(resolved.agent).toBe('plan');
|
|
100
|
+
expect(resolved.decision).toMatchObject({ category: 'hard', reason: 'routed', confidence: 0.97 });
|
|
103
101
|
expect(events.at(-1)).toMatchObject({ type: 'openchamber:routing.decision', properties: { sessionId: 's1', category: 'hard' } });
|
|
104
102
|
});
|
|
105
103
|
|
|
106
|
-
it('
|
|
107
|
-
const { runtime } = makeRuntime({ answers: { category: { choice: 'hard', confidence: 0.97 } } });
|
|
108
|
-
const body = { model: 'openchamber/auto', command: 'review', arguments: '3650' };
|
|
109
|
-
await runtime.resolvePromptBody(body, { sessionId: 's1' });
|
|
110
|
-
expect(body.model).toBe('openai/gpt-6-astra');
|
|
111
|
-
expect(body.agent).toBe('plan');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('uses the fallback and keeps the composer agent when the category has no model', async () => {
|
|
104
|
+
it('uses the fallback pair and keeps the composer agent when the category has no model', async () => {
|
|
115
105
|
const { runtime } = makeRuntime({ answers: { category: { choice: 'trivial', confidence: 0.99 } } });
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
expect(
|
|
106
|
+
const resolved = await runtime.resolveAutoSelection(send({ agent: 'build', requestText: 'fix typo' }));
|
|
107
|
+
expect(resolved.model).toEqual({ providerID: 'anthropic', id: 'claude-sonnet-5', variant: 'medium' });
|
|
108
|
+
expect(resolved.agent).toBe('build');
|
|
119
109
|
});
|
|
120
110
|
|
|
121
111
|
it('falls back on low confidence and on a Jev failure, and records why', async () => {
|
|
122
112
|
const low = makeRuntime({ answers: { category: { choice: 'hard', confidence: 0.3 } } });
|
|
123
|
-
const
|
|
124
|
-
expect(
|
|
125
|
-
expect(
|
|
113
|
+
const lowResolved = await low.runtime.resolveAutoSelection(send());
|
|
114
|
+
expect(lowResolved.decision.reason).toBe('low-confidence');
|
|
115
|
+
expect(lowResolved.model).toMatchObject({ providerID: 'anthropic', id: 'claude-sonnet-5' });
|
|
126
116
|
|
|
127
117
|
const failing = makeRuntime({ askError: Object.assign(new Error('Jev responded 401'), { status: 401 }) });
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
expect(
|
|
131
|
-
expect(body2.model).toEqual(FALLBACK.model);
|
|
118
|
+
const resolved = await failing.runtime.resolveAutoSelection(send());
|
|
119
|
+
expect(resolved.decision).toMatchObject({ reason: 'error', error: 'Jev responded 401' });
|
|
120
|
+
expect(resolved.model).toMatchObject({ providerID: 'anthropic', id: 'claude-sonnet-5' });
|
|
132
121
|
});
|
|
133
122
|
|
|
134
123
|
it('falls back without asking Jev while Auto is not ready, and refuses without a fallback', async () => {
|
|
135
124
|
const config = readyConfig();
|
|
136
125
|
config.enabled = false;
|
|
137
126
|
const notReady = makeRuntime({ config, answers: {} });
|
|
138
|
-
const
|
|
139
|
-
expect(
|
|
140
|
-
expect(
|
|
127
|
+
const resolved = await notReady.runtime.resolveAutoSelection(send());
|
|
128
|
+
expect(resolved.decision.reason).toBe('not-ready');
|
|
129
|
+
expect(resolved.model).toMatchObject({ providerID: 'anthropic', id: 'claude-sonnet-5' });
|
|
141
130
|
expect(notReady.jev.ask).not.toHaveBeenCalled();
|
|
142
131
|
|
|
143
132
|
const noFallback = makeRuntime({ config: { ...readyConfig(), fallback: null }, answers: {} });
|
|
144
|
-
await expect(noFallback.runtime.
|
|
133
|
+
await expect(noFallback.runtime.resolveAutoSelection(send())).rejects.toMatchObject({ status: 400 });
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('jev endpoint', () => {
|
|
138
|
+
const capture = async (token) => {
|
|
139
|
+
let call = null;
|
|
140
|
+
const fetchImpl = async (url, init) => {
|
|
141
|
+
call = { url, init };
|
|
142
|
+
return { ok: true, status: 200, text: async () => JSON.stringify({ answers: {} }) };
|
|
143
|
+
};
|
|
144
|
+
await createJevClient({ fetchImpl }).ask({ state: 'x', questions: {} }, token);
|
|
145
|
+
return { url: call.url, headers: call.init.headers, body: JSON.parse(call.init.body) };
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
it('sends a saved key to TypeSafe and falls back to the free model zen serves without one', async () => {
|
|
149
|
+
const keyed = await capture('secret');
|
|
150
|
+
expect(keyed.url).toBe('https://api.typesafe.ai/v1/systemone');
|
|
151
|
+
expect(keyed.headers.authorization).toBe('Bearer secret');
|
|
152
|
+
expect(keyed.body.model).toBe('jev-latest');
|
|
153
|
+
|
|
154
|
+
const free = await capture(null);
|
|
155
|
+
expect(free.url).toBe('https://opencode.ai/zen/v1/systemone');
|
|
156
|
+
expect(free.headers.authorization).toBeUndefined();
|
|
157
|
+
// Zen counts our calls by this header, and does not know the `jev-latest` alias.
|
|
158
|
+
expect(free.headers['x-opencode-client']).toBe('openchamber');
|
|
159
|
+
expect(free.body.model).toBe('jev-1.13-free');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('auto sessions', () => {
|
|
164
|
+
it('remembers the sentinel selection and forgets it when a real model is chosen', () => {
|
|
165
|
+
const { runtime } = makeRuntime({ answers: {} });
|
|
166
|
+
expect(runtime.isAutoSession('s1')).toBe(false);
|
|
167
|
+
expect(runtime.noteModelSelection('s1', AUTO, '/repo')).toBe(true);
|
|
168
|
+
expect(runtime.isAutoSession('s1')).toBe(true);
|
|
169
|
+
expect(runtime.noteModelSelection('s1', { providerID: 'anthropic', id: 'claude-opus-5' }, '/repo')).toBe(false);
|
|
170
|
+
expect(runtime.isAutoSession('s1')).toBe(false);
|
|
145
171
|
});
|
|
146
172
|
});
|
|
147
173
|
|
|
@@ -165,24 +191,22 @@ describe('evaluatePermission', () => {
|
|
|
165
191
|
expect(events.at(-1)).toMatchObject({ type: 'openchamber:routing.safety-skipped', properties: { permissionId: 'p1', error: 'Jev timed out after 4000ms' } });
|
|
166
192
|
});
|
|
167
193
|
|
|
168
|
-
it('accepts without asking when the safety net is off
|
|
194
|
+
it('accepts without asking when the safety net is off', async () => {
|
|
169
195
|
const off = readyConfig();
|
|
170
196
|
off.safetyNet.enabled = false;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
expect(jev.ask).not.toHaveBeenCalled();
|
|
175
|
-
}
|
|
197
|
+
const { runtime, jev } = makeRuntime({ config: off, answers: {} });
|
|
198
|
+
expect(await runtime.evaluatePermission(permission, '/repo')).toEqual({ action: 'accept' });
|
|
199
|
+
expect(jev.ask).not.toHaveBeenCalled();
|
|
176
200
|
});
|
|
177
201
|
});
|
|
178
202
|
|
|
179
203
|
describe('describe', () => {
|
|
180
|
-
it('reports Auto ready
|
|
181
|
-
expect((await makeRuntime({ answers: {} }).runtime.describe())
|
|
182
|
-
|
|
204
|
+
it('reports Auto ready with an enabled config, a fallback and two categories, key or no key', async () => {
|
|
205
|
+
expect((await makeRuntime({ answers: {} }).runtime.describe())).toMatchObject({ autoReady: true, tokenPresent: true, jevSource: 'typesafe' });
|
|
206
|
+
// Without a key the free Jev model on zen answers, so Auto stays available.
|
|
207
|
+
expect((await makeRuntime({ token: null, answers: {} }).runtime.describe())).toMatchObject({ autoReady: true, tokenPresent: false, jevSource: 'zen-free' });
|
|
183
208
|
const one = readyConfig();
|
|
184
209
|
one.categories = one.categories.map((c, i) => ({ ...c, enabled: i === 0 }));
|
|
185
210
|
expect((await makeRuntime({ config: one, answers: {} }).runtime.describe()).autoReady).toBe(false);
|
|
186
|
-
expect(await makeRuntime({ flag: '', answers: {} }).runtime.describe()).toEqual({ available: false, autoReady: false, tokenPresent: false, config: null, builtins: [] });
|
|
187
211
|
});
|
|
188
212
|
});
|
|
@@ -188,3 +188,7 @@ project write lock on every `syncProject` when the project path is known:
|
|
|
188
188
|
- `POST /api/projects/:projectId/scheduled-tasks/:taskId/run`
|
|
189
189
|
- `GET /api/openchamber/scheduled-tasks/status`
|
|
190
190
|
- `GET /api/openchamber/events`
|
|
191
|
+
|
|
192
|
+
The shared `/api/openchamber/events` stream also carries web notifications.
|
|
193
|
+
Its connection ownership and browser capability flag stay unchanged. Delivery
|
|
194
|
+
and duplicate handling are documented in `../notifications/DOCUMENTATION.md`.
|
|
@@ -14,25 +14,33 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
|
14
14
|
* before creating a session, so the second instance skips.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const sdk = vi.hoisted(() => ({
|
|
18
|
-
sessionCreates: [],
|
|
19
|
-
createOpencodeClient: () => ({
|
|
20
|
-
session: {
|
|
21
|
-
create: async () => {
|
|
22
|
-
sdk.sessionCreates.push(Date.now());
|
|
23
|
-
return { data: { id: `sess-${sdk.sessionCreates.length}` } };
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
command: { list: async () => ({ data: [] }) },
|
|
27
|
-
}),
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
vi.mock('@opencode-ai/sdk/v2', () => ({
|
|
31
|
-
createOpencodeClient: sdk.createOpencodeClient,
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
17
|
import { createScheduledTasksRuntime } from './runtime.js';
|
|
35
18
|
|
|
19
|
+
const opencode = { sessionCreates: [] };
|
|
20
|
+
|
|
21
|
+
const jsonResponse = (body) => new Response(JSON.stringify(body), {
|
|
22
|
+
status: 200,
|
|
23
|
+
headers: { 'content-type': 'application/json' },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Serves the few v2 routes a scheduled run touches so the test drives the real
|
|
28
|
+
* @opencode/client over its own transport instead of replacing the module.
|
|
29
|
+
* `gate` holds the prompt open the way a long-running dispatch would.
|
|
30
|
+
*/
|
|
31
|
+
const installOpenCodeFetch = (gate = null) => {
|
|
32
|
+
globalThis.fetch = vi.fn(async (input) => {
|
|
33
|
+
const { pathname } = new URL(input);
|
|
34
|
+
if (pathname === '/api/session') {
|
|
35
|
+
opencode.sessionCreates.push(Date.now());
|
|
36
|
+
return jsonResponse({ data: { id: `sess-${opencode.sessionCreates.length}` } });
|
|
37
|
+
}
|
|
38
|
+
if (pathname === '/api/command') return jsonResponse({ data: [] });
|
|
39
|
+
if (gate) await gate;
|
|
40
|
+
return jsonResponse({ data: {} });
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
36
44
|
const UTC = (y, mo, d, h, mi, s = 0) => Date.UTC(y, mo, d, h, mi, s);
|
|
37
45
|
const MINUTE = 60_000;
|
|
38
46
|
const HOUR = 3_600_000;
|
|
@@ -118,8 +126,8 @@ const startInstances = async (count, task) => {
|
|
|
118
126
|
describe('issue 2710: daily scheduled task double execution at the configured time', () => {
|
|
119
127
|
beforeEach(() => {
|
|
120
128
|
vi.useFakeTimers();
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
opencode.sessionCreates.length = 0;
|
|
130
|
+
installOpenCodeFetch();
|
|
123
131
|
});
|
|
124
132
|
|
|
125
133
|
afterEach(() => {
|
|
@@ -132,8 +140,8 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
132
140
|
const { runtimes } = await startInstances(1, makeTask({ kind: 'daily', times: ['15:00'] }));
|
|
133
141
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
134
142
|
|
|
135
|
-
expect(
|
|
136
|
-
const firedAt = new Date(
|
|
143
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
144
|
+
const firedAt = new Date(opencode.sessionCreates[0]);
|
|
137
145
|
expect(firedAt.getUTCHours()).toBe(15);
|
|
138
146
|
|
|
139
147
|
runtimes.forEach((runtime) => runtime.stop());
|
|
@@ -144,9 +152,9 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
144
152
|
const { runtimes } = await startInstances(1, makeTask({ kind: 'daily', times: ['15:00'] }));
|
|
145
153
|
await vi.advanceTimersByTimeAsync((4 * 24 * HOUR) + 3_000);
|
|
146
154
|
|
|
147
|
-
expect(
|
|
155
|
+
expect(opencode.sessionCreates.length).toBe(4);
|
|
148
156
|
const byHourBucket = new Map();
|
|
149
|
-
for (const timestamp of
|
|
157
|
+
for (const timestamp of opencode.sessionCreates) {
|
|
150
158
|
const date = new Date(timestamp);
|
|
151
159
|
expect(date.getUTCHours()).toBe(15);
|
|
152
160
|
expect(date.getUTCMinutes()).toBe(0);
|
|
@@ -168,8 +176,8 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
168
176
|
);
|
|
169
177
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
170
178
|
|
|
171
|
-
expect(
|
|
172
|
-
const firedAt = new Date(
|
|
179
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
180
|
+
const firedAt = new Date(opencode.sessionCreates[0]);
|
|
173
181
|
expect(firedAt.getUTCHours()).toBe(15);
|
|
174
182
|
expect(firedAt.getUTCMinutes()).toBe(0);
|
|
175
183
|
expect(projectConfigRuntime.updateScheduledTaskStateIf).toHaveBeenCalled();
|
|
@@ -187,7 +195,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
187
195
|
}));
|
|
188
196
|
await vi.advanceTimersByTimeAsync((25 * HOUR) + 3_000);
|
|
189
197
|
|
|
190
|
-
expect(
|
|
198
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
191
199
|
|
|
192
200
|
runtimes.forEach((runtime) => runtime.stop());
|
|
193
201
|
});
|
|
@@ -197,7 +205,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
197
205
|
const { runtimes } = await startInstances(2, makeTask({ kind: 'cron', cron: '*/5 * * * *' }));
|
|
198
206
|
await vi.advanceTimersByTimeAsync(3 * MINUTE);
|
|
199
207
|
|
|
200
|
-
expect(
|
|
208
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
201
209
|
|
|
202
210
|
runtimes.forEach((runtime) => runtime.stop());
|
|
203
211
|
});
|
|
@@ -211,7 +219,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
211
219
|
}));
|
|
212
220
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
213
221
|
|
|
214
|
-
expect(
|
|
222
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
215
223
|
|
|
216
224
|
runtimes.forEach((runtime) => runtime.stop());
|
|
217
225
|
});
|
|
@@ -229,14 +237,14 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
229
237
|
|
|
230
238
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
231
239
|
|
|
232
|
-
expect(
|
|
240
|
+
expect(opencode.sessionCreates.length).toBe(0);
|
|
233
241
|
expect(runtime.getStatus().runningScheduledTasksCount).toBe(0);
|
|
234
242
|
expect(runtime.getStatus().hasRunningScheduledTasks).toBe(false);
|
|
235
243
|
|
|
236
244
|
// Manual runNow must not be stuck behind a permanently "running" claim failure.
|
|
237
245
|
const manual = await runtime.runNow('p1', 'task-1');
|
|
238
246
|
expect(manual.ok).toBe(true);
|
|
239
|
-
expect(
|
|
247
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
240
248
|
|
|
241
249
|
runtime.stop();
|
|
242
250
|
});
|
|
@@ -263,7 +271,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
263
271
|
|
|
264
272
|
// Initial completion write + best-effort retry.
|
|
265
273
|
expect(completionWrites).toBeGreaterThanOrEqual(2);
|
|
266
|
-
expect(
|
|
274
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
267
275
|
expect(runtime.getStatus().runningScheduledTasksCount).toBe(0);
|
|
268
276
|
|
|
269
277
|
const manual = await runtime.runNow('p1', 'task-1');
|
|
@@ -316,7 +324,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
316
324
|
const manual = await runtime.runNow('p1', 'task-1');
|
|
317
325
|
expect(manual.ok).toBe(false);
|
|
318
326
|
expect(manual.reason).toBe('start-state-failed');
|
|
319
|
-
expect(
|
|
327
|
+
expect(opencode.sessionCreates.length).toBe(0);
|
|
320
328
|
expect(runtime.getStatus().runningScheduledTasksCount).toBe(0);
|
|
321
329
|
|
|
322
330
|
runtime.stop();
|
|
@@ -340,7 +348,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
340
348
|
|
|
341
349
|
await vi.advanceTimersByTimeAsync(5_000);
|
|
342
350
|
|
|
343
|
-
expect(
|
|
351
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
344
352
|
|
|
345
353
|
runtimeA.stop();
|
|
346
354
|
runtimeB.stop();
|
|
@@ -356,7 +364,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
356
364
|
const runtimeA = createScheduledTasksRuntime(createRuntimeDeps(projectConfigRuntime));
|
|
357
365
|
await runtimeA.start();
|
|
358
366
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
359
|
-
expect(
|
|
367
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
360
368
|
|
|
361
369
|
// Advance to day-2 afternoon, still outside slack, so A re-arms for 15:00.
|
|
362
370
|
await vi.advanceTimersByTimeAsync((23 * HOUR) - 3_000);
|
|
@@ -367,7 +375,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
367
375
|
|
|
368
376
|
await vi.advanceTimersByTimeAsync(5_000);
|
|
369
377
|
|
|
370
|
-
expect(
|
|
378
|
+
expect(opencode.sessionCreates.length).toBe(2);
|
|
371
379
|
|
|
372
380
|
runtimeA.stop();
|
|
373
381
|
runtimeB.stop();
|
|
@@ -380,10 +388,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
380
388
|
const fetchGate = new Promise((resolve) => {
|
|
381
389
|
releaseFetch = resolve;
|
|
382
390
|
});
|
|
383
|
-
|
|
384
|
-
await fetchGate;
|
|
385
|
-
return { ok: true, text: async () => '' };
|
|
386
|
-
});
|
|
391
|
+
installOpenCodeFetch(fetchGate);
|
|
387
392
|
|
|
388
393
|
const { runtimes, projectConfigRuntime } = await startInstances(2, makeTask({
|
|
389
394
|
kind: 'once',
|
|
@@ -393,21 +398,21 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
393
398
|
|
|
394
399
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
395
400
|
|
|
396
|
-
// Winner claimed and is blocked in
|
|
397
|
-
expect(
|
|
401
|
+
// Winner claimed and is blocked in the prompt dispatch; exactly one session.
|
|
402
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
398
403
|
const claimsAfterFire = projectConfigRuntime.updateScheduledTaskStateIf.mock.calls.length;
|
|
399
404
|
expect(claimsAfterFire).toBeGreaterThanOrEqual(1);
|
|
400
405
|
|
|
401
406
|
// Advance through many jitter windows. Loser must not keep re-entering claim.
|
|
402
407
|
await vi.advanceTimersByTimeAsync(60_000);
|
|
403
408
|
expect(projectConfigRuntime.updateScheduledTaskStateIf.mock.calls.length).toBe(claimsAfterFire);
|
|
404
|
-
expect(
|
|
409
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
405
410
|
|
|
406
411
|
releaseFetch();
|
|
407
412
|
await Promise.resolve();
|
|
408
413
|
await vi.advanceTimersByTimeAsync(5_000);
|
|
409
414
|
|
|
410
|
-
expect(
|
|
415
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
411
416
|
expect(runtimes.every((runtime) => runtime.getStatus().runningScheduledTasksCount === 0)).toBe(true);
|
|
412
417
|
|
|
413
418
|
runtimes.forEach((runtime) => runtime.stop());
|
|
@@ -467,7 +472,7 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
467
472
|
await runtime.start();
|
|
468
473
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
469
474
|
|
|
470
|
-
expect(
|
|
475
|
+
expect(opencode.sessionCreates.length).toBe(0);
|
|
471
476
|
const tasks = await projectConfigRuntime.listScheduledTasks('p1');
|
|
472
477
|
expect(tasks[0].state.lastStatus).toBe('error');
|
|
473
478
|
expect(tasks[0].state.lastError).toMatch(/Scheduled claim failed/);
|
|
@@ -496,12 +501,12 @@ describe('issue 2710: daily scheduled task double execution at the configured ti
|
|
|
496
501
|
|
|
497
502
|
// The scheduler must not fire a disabled task on its own.
|
|
498
503
|
await vi.advanceTimersByTimeAsync(HOUR + 3_000);
|
|
499
|
-
expect(
|
|
504
|
+
expect(opencode.sessionCreates.length).toBe(0);
|
|
500
505
|
|
|
501
506
|
const manual = await runtime.runNow('p1', 'task-1');
|
|
502
507
|
expect(manual.ok).toBe(true);
|
|
503
508
|
expect(manual.sessionID).toBeTruthy();
|
|
504
|
-
expect(
|
|
509
|
+
expect(opencode.sessionCreates.length).toBe(1);
|
|
505
510
|
expect(runtime.getStatus().runningScheduledTasksCount).toBe(0);
|
|
506
511
|
|
|
507
512
|
// Completion records state but leaves the task paused with no next run.
|