@vantran-se/openchamber-web 1.24.4 → 2.0.1
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-ntXIbhV2.js} +1 -1
- package/dist/assets/DiffView-CjxUovbM.js +4 -0
- package/dist/assets/FilesView-DFWD0RQR.js +17 -0
- package/dist/assets/{GitView-Byu2DY7s.js → GitView-BWa7e5vw.js} +4 -4
- package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-DdOuyZzp.js} +1 -1
- package/dist/assets/GuestSurfacePane-DDfzid3U.js +1 -0
- package/dist/assets/{JsonTreeView-VW_7czo3.js → JsonTreeView-3sGwqexh.js} +1 -1
- package/dist/assets/LinearIssuesView-C6dpIdjw.js +1 -0
- package/dist/assets/MarkdownImageGallery-Q7YR_tl4.js +1 -0
- package/dist/assets/MarkdownRendererImpl-DxDV56bL.js +12 -0
- package/dist/assets/OnboardingScreen-rK4Stg__.js +1 -0
- package/dist/assets/{PatchDiff-DCjGhlYV.js → PatchDiff-DS-IMU1j.js} +1 -1
- package/dist/assets/{PierreDiffViewer-CEDZNkU2.js → PierreDiffViewer-DW23KWKZ.js} +1 -1
- package/dist/assets/PlanView-BCk_VobP.js +5 -0
- package/dist/assets/PluginPane-BPP1dbck.js +3 -0
- package/dist/assets/SettingsView-kMmrpn43.js +29 -0
- package/dist/assets/SettingsWindow-BpvD6IYQ.js +1 -0
- package/dist/assets/SubmoduleDiffSummary-BLttSS1X.js +1 -0
- package/dist/assets/ToolOutputDialog-CtqDqbyr.js +15 -0
- package/dist/assets/ToolPartDiffPreview-5FcKdyRy.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-NU0FHFhT.js → VirtualizedFileDiff-C763hNCR.js} +4 -4
- package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-lap5btCF.js} +2 -2
- package/dist/assets/appThemeRegistry-u4zw1l0B.js +1 -0
- package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-CwGJfJG-.js} +1 -1
- package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.js → computeVirtualFileMetrics-DW5jjX4n.js} +1 -1
- package/dist/assets/de-CndEg67T.js +10 -0
- package/dist/assets/es-DglvPlaC.js +16 -0
- package/dist/assets/fr-DejPCpbS.js +14 -0
- package/dist/assets/index-C2r64QfD.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-D47a6hMP.js} +1 -1
- package/dist/assets/{isFilePlainText-ue6kCou7.js → isFilePlainText-B-mGgeKl.js} +1 -1
- package/dist/assets/ja-CVpQawCd.js +16 -0
- package/dist/assets/ko-Bw446h9j.js +16 -0
- package/dist/assets/main-Bz4WWXbr.js +2 -0
- package/dist/assets/main-oreqDsOV.js +896 -0
- package/dist/assets/{markdownCore-CK48LG0z.js → markdownCore-D2e5sp9y.js} +4 -4
- package/dist/assets/miniChat-D1v2gsBI.js +2 -0
- package/dist/assets/mobile-DBj3lCf2.js +2 -0
- package/dist/assets/{patchFileDiff-D7QVlovD.js → patchFileDiff-BpeUOV91.js} +3 -3
- package/dist/assets/pl-BYNlPdhm.js +16 -0
- package/dist/assets/pt-BR-CL-Yfv9H.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-DVtfEB2z.js +2 -0
- package/dist/assets/renderMobileApp-DFp9-xbG.js +25 -0
- package/dist/assets/runtimeEndpointReset-CsIpUep1.js +27 -0
- package/dist/assets/scheduledTasksApi-B5OHVpPM.js +1 -0
- package/dist/assets/{shikiHighlight-BPu3JiQs.js → shikiHighlight-DNm2Z_Jc.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.js → textMateThemeFromAppTheme-DGF2kG7g.js} +1 -1
- package/dist/assets/tr-DzmbxLy-.js +7 -0
- package/dist/assets/uk-C7vQnwro.js +16 -0
- package/dist/assets/useAppFontEffects-Ci3FEaPf.js +7337 -0
- package/dist/assets/{useInlineCommentController-CgS0fnw_.js → useInlineCommentController-CuUuakrt.js} +2 -2
- package/dist/assets/useRootScrollLock-CQIsyERq.js +2 -0
- package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.js → vendor-codemirror-language-data-CQa7_1cr.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-B6tKQyJN.js} +2 -2
- package/dist/assets/zh-CN-Cl0srS-z.js +16 -0
- package/dist/assets/zh-TW-DtII5bDb.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
|
@@ -20,7 +20,6 @@ const createRuntime = async (overrides = {}) => {
|
|
|
20
20
|
const dataDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openchamber-agent-tool-'));
|
|
21
21
|
temporaryDirectories.push(dataDir);
|
|
22
22
|
const executeAction = vi.fn(async () => ({ projects: [] }));
|
|
23
|
-
const env = {};
|
|
24
23
|
const runtime = createAgentToolRuntime({
|
|
25
24
|
crypto,
|
|
26
25
|
fsPromises: fs,
|
|
@@ -28,10 +27,32 @@ const createRuntime = async (overrides = {}) => {
|
|
|
28
27
|
dataDir,
|
|
29
28
|
getActivePort: () => 3901,
|
|
30
29
|
executeAction,
|
|
31
|
-
env,
|
|
32
30
|
...overrides,
|
|
33
31
|
});
|
|
34
|
-
return { runtime, dataDir, executeAction
|
|
32
|
+
return { runtime, dataDir, executeAction };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const pluginDirectoryFor = (dataDir) => path.join(dataDir, 'agent-tool', 'openchamber-agent-tool');
|
|
36
|
+
|
|
37
|
+
/** What a managed OpenCode child start does: write the plugin, mint a token. */
|
|
38
|
+
const prepareManagedEnv = async (runtime, options) => {
|
|
39
|
+
const pluginDirectory = await runtime.materializePlugin(options);
|
|
40
|
+
return { pluginDirectory, ...runtime.createChildEnv() };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Loads the generated plugin and returns the tools it registers, by name. */
|
|
44
|
+
const loadTools = async (dataDir, tag) => {
|
|
45
|
+
const entrypoint = path.join(pluginDirectoryFor(dataDir), 'index.js');
|
|
46
|
+
const pluginModule = await import(`${pathToFileURL(entrypoint).href}?${tag}=${Date.now()}`);
|
|
47
|
+
const registered = {};
|
|
48
|
+
await pluginModule.default.setup({
|
|
49
|
+
tool: {
|
|
50
|
+
transform: (edit) => {
|
|
51
|
+
edit({ add: (tool) => { registered[tool.name] = tool; } });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
return registered;
|
|
35
56
|
};
|
|
36
57
|
|
|
37
58
|
describe('agent tool action allowlist', () => {
|
|
@@ -53,6 +74,7 @@ describe('agent tool action allowlist', () => {
|
|
|
53
74
|
'schedule.run',
|
|
54
75
|
'schedule.delete',
|
|
55
76
|
'schedule.toggle',
|
|
77
|
+
'file.open',
|
|
56
78
|
])('delegates %s to the shared control service', async (action) => {
|
|
57
79
|
const { runtime, executeAction } = await createRuntime();
|
|
58
80
|
const input = { action, projectId: 'project-1' };
|
|
@@ -75,21 +97,16 @@ describe('agent tool action allowlist', () => {
|
|
|
75
97
|
});
|
|
76
98
|
|
|
77
99
|
describe('managed agent tool runtime', () => {
|
|
78
|
-
it('materializes the plugin and
|
|
79
|
-
const { runtime, dataDir
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(
|
|
88
|
-
expect(config.plugin).toEqual([
|
|
89
|
-
'file:///existing.js',
|
|
90
|
-
['example-plugin', { flag: true }],
|
|
91
|
-
expect.stringContaining('/agent-tool/openchamber-plugin.js'),
|
|
92
|
-
]);
|
|
100
|
+
it('materializes the plugin and mints a per-child callback token', async () => {
|
|
101
|
+
const { runtime, dataDir } = await createRuntime();
|
|
102
|
+
|
|
103
|
+
const preparedEnv = await prepareManagedEnv(runtime);
|
|
104
|
+
const source = await fs.readFile(path.join(pluginDirectoryFor(dataDir), 'index.js'), 'utf8');
|
|
105
|
+
const manifest = JSON.parse(await fs.readFile(path.join(pluginDirectoryFor(dataDir), 'package.json'), 'utf8'));
|
|
106
|
+
|
|
107
|
+
// The managed config layer names this directory; the runtime only writes it.
|
|
108
|
+
expect(preparedEnv.pluginDirectory).toBe(pluginDirectoryFor(dataDir));
|
|
109
|
+
expect(manifest.exports).toEqual({ '.': './index.js' });
|
|
93
110
|
expect(preparedEnv.OPENCHAMBER_AGENT_TOOL_URL).toBe('http://127.0.0.1:3901/api/openchamber/agent-tool');
|
|
94
111
|
expect(preparedEnv.OPENCHAMBER_AGENT_TOOL_TOKEN).toMatch(/^[A-Za-z0-9_-]+$/);
|
|
95
112
|
expect(source).toContain('openchamber: {');
|
|
@@ -97,40 +114,40 @@ describe('managed agent tool runtime', () => {
|
|
|
97
114
|
expect(source).toContain(JSON.stringify({ const: action, description }));
|
|
98
115
|
}
|
|
99
116
|
expect(source).not.toContain('"schedule.status"');
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
expect(
|
|
103
|
-
expect(
|
|
104
|
-
expect(hooks.tool.openchamber.args.action.oneOf).toContainEqual({
|
|
117
|
+
const tool = await loadTools(dataDir, 'schema');
|
|
118
|
+
expect(tool.openchamber.description).toContain('Session dispatches return immediately by default');
|
|
119
|
+
expect(tool.openchamber.description).toContain('Set wait only when the user asks or the next step requires the completed result');
|
|
120
|
+
expect(tool.openchamber.input.properties.action.oneOf).toContainEqual({
|
|
105
121
|
const: 'session.messages',
|
|
106
122
|
description: 'Read text-only messages and current sessionStatus for sessionId; directory and limit 10 are defaults',
|
|
107
123
|
});
|
|
108
|
-
expect(
|
|
124
|
+
expect(tool.openchamber.input.properties.parameters.properties.wait.description).toBe(
|
|
109
125
|
'Wait for current session activity to become idle. Omit by default; use only when the user asks or the next step requires the completed result',
|
|
110
126
|
);
|
|
111
|
-
expect(
|
|
127
|
+
expect(tool.openchamber.input.properties.parameters.properties.sessionId).toEqual({ type: 'string' });
|
|
112
128
|
expect(source).not.toContain('title: "OpenChamber"');
|
|
113
|
-
|
|
129
|
+
// Nothing resolves from the generated directory, so the file must not import.
|
|
130
|
+
expect(source).not.toMatch(/\bimport\b/);
|
|
114
131
|
expect(source).not.toContain(preparedEnv.OPENCHAMBER_AGENT_TOOL_TOKEN);
|
|
115
132
|
});
|
|
116
133
|
|
|
117
134
|
it('emits both tools, each carrying only its own actions and inputs', async () => {
|
|
118
135
|
const { runtime, dataDir } = await createRuntime();
|
|
119
|
-
await runtime
|
|
120
|
-
const
|
|
121
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?both=${Date.now()}`);
|
|
122
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
136
|
+
await prepareManagedEnv(runtime);
|
|
137
|
+
const tool = await loadTools(dataDir, 'both');
|
|
123
138
|
|
|
124
|
-
const controlActions = tool.openchamber.
|
|
125
|
-
const webActions = tool.openchamber_web.
|
|
139
|
+
const controlActions = tool.openchamber.input.properties.action.oneOf.map((entry) => entry.const);
|
|
140
|
+
const webActions = tool.openchamber_web.input.properties.action.oneOf.map((entry) => entry.const);
|
|
126
141
|
expect(webActions).toContain('browser.open');
|
|
127
142
|
expect(controlActions).not.toContain('browser.open');
|
|
128
143
|
expect(webActions).not.toContain('session.create');
|
|
129
144
|
|
|
130
145
|
// Turning one tool off has to remove its inputs too, not just its actions.
|
|
131
|
-
expect(Object.keys(tool.openchamber_web.
|
|
132
|
-
expect(Object.keys(tool.openchamber.
|
|
133
|
-
expect(Object.keys(tool.openchamber.
|
|
146
|
+
expect(Object.keys(tool.openchamber_web.input.properties.parameters.properties)).toContain('url');
|
|
147
|
+
expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).not.toContain('url');
|
|
148
|
+
expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).toContain('sessionId');
|
|
149
|
+
expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).toContain('path');
|
|
150
|
+
expect(Object.keys(tool.openchamber_web.input.properties.parameters.properties)).not.toContain('path');
|
|
134
151
|
});
|
|
135
152
|
|
|
136
153
|
it('keeps the action schema to one validator keyword', async () => {
|
|
@@ -139,23 +156,19 @@ describe('managed agent tool runtime', () => {
|
|
|
139
156
|
// instead of an error. `oneOf` is the keyword that stayed. Its branches
|
|
140
157
|
// carry the per-action descriptions the model reads.
|
|
141
158
|
const { runtime, dataDir } = await createRuntime();
|
|
142
|
-
await runtime
|
|
143
|
-
const
|
|
144
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?validator=${Date.now()}`);
|
|
145
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
159
|
+
await prepareManagedEnv(runtime);
|
|
160
|
+
const tool = await loadTools(dataDir, 'validator');
|
|
146
161
|
|
|
147
162
|
for (const entry of Object.values(tool)) {
|
|
148
|
-
expect(entry.
|
|
149
|
-
expect(entry.
|
|
163
|
+
expect(entry.input.properties.action.oneOf).toBeInstanceOf(Array);
|
|
164
|
+
expect(entry.input.properties.action).not.toHaveProperty('enum');
|
|
150
165
|
}
|
|
151
166
|
});
|
|
152
167
|
|
|
153
168
|
it('accepts inputs passed beside the action, not only inside parameters', async () => {
|
|
154
169
|
const { runtime, dataDir } = await createRuntime();
|
|
155
|
-
const prepared = await runtime
|
|
156
|
-
const
|
|
157
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?flat=${Date.now()}`);
|
|
158
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
170
|
+
const prepared = await prepareManagedEnv(runtime);
|
|
171
|
+
const tool = await loadTools(dataDir, 'flat');
|
|
159
172
|
|
|
160
173
|
const sent = [];
|
|
161
174
|
const originalFetch = globalThis.fetch;
|
|
@@ -167,7 +180,7 @@ describe('managed agent tool runtime', () => {
|
|
|
167
180
|
sent.push(JSON.parse(init.body));
|
|
168
181
|
return new Response(JSON.stringify({ schemaVersion: 1, ok: true, action: 'browser.open', data: {} }));
|
|
169
182
|
};
|
|
170
|
-
const context = {
|
|
183
|
+
const context = { sessionID: 'ses_1', agent: 'build', messageID: 'msg_1', id: 'call_1', progress: async () => {} };
|
|
171
184
|
|
|
172
185
|
try {
|
|
173
186
|
// The shape a model actually produced: url and viewport next to action.
|
|
@@ -194,49 +207,44 @@ describe('managed agent tool runtime', () => {
|
|
|
194
207
|
expect(sent[0].input).toEqual({ action: 'browser.open', url: 'https://example.test', viewport: 'mobile' });
|
|
195
208
|
expect(sent[1].input.url).toBe('https://example.test/nested');
|
|
196
209
|
expect(sent[2].input).toEqual({ action: 'session.messages', sessionId: 'ses_1', limit: 3 });
|
|
210
|
+
// v2 tools get no directory; the session id is what OpenChamber resolves from.
|
|
211
|
+
expect(sent[2].sessionID).toBe('ses_1');
|
|
212
|
+
expect(sent[2].contextDirectory).toBeUndefined();
|
|
197
213
|
});
|
|
198
214
|
|
|
199
215
|
it('omits a tool the user turned off', async () => {
|
|
200
216
|
const { runtime, dataDir } = await createRuntime();
|
|
201
|
-
await runtime
|
|
202
|
-
const
|
|
203
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?web=${Date.now()}`);
|
|
204
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
217
|
+
await prepareManagedEnv(runtime, { includeControl: false, includeWeb: true, includeMemory: false });
|
|
218
|
+
const tool = await loadTools(dataDir, 'web');
|
|
205
219
|
|
|
206
220
|
expect(Object.keys(tool)).toEqual(['openchamber_web']);
|
|
207
221
|
});
|
|
208
222
|
|
|
209
223
|
it('exposes memory as its own tool carrying only its own inputs', async () => {
|
|
210
224
|
const { runtime, dataDir } = await createRuntime();
|
|
211
|
-
await runtime
|
|
212
|
-
const
|
|
213
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?memory=${Date.now()}`);
|
|
214
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
225
|
+
await prepareManagedEnv(runtime, { includeControl: true, includeWeb: false, includeMemory: true });
|
|
226
|
+
const tool = await loadTools(dataDir, 'memory');
|
|
215
227
|
|
|
216
228
|
expect(Object.keys(tool)).toEqual(['openchamber', 'openchamber_memory']);
|
|
217
|
-
expect(Object.keys(tool.openchamber_memory.
|
|
229
|
+
expect(Object.keys(tool.openchamber_memory.input.properties.parameters.properties).sort())
|
|
218
230
|
.toEqual(['body', 'memoryId', 'scope', 'title', 'type']);
|
|
219
231
|
// Memory inputs must not leak into the control tool's schema, which the
|
|
220
232
|
// model pays for on every unrelated call.
|
|
221
|
-
expect(Object.keys(tool.openchamber.
|
|
233
|
+
expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).not.toContain('memoryId');
|
|
222
234
|
});
|
|
223
235
|
|
|
224
236
|
it('omits memory entirely when the user turns it off', async () => {
|
|
225
237
|
const { runtime, dataDir } = await createRuntime();
|
|
226
|
-
await runtime
|
|
227
|
-
const
|
|
228
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?nomemory=${Date.now()}`);
|
|
229
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
238
|
+
await prepareManagedEnv(runtime, { includeControl: true, includeWeb: false, includeMemory: false });
|
|
239
|
+
const tool = await loadTools(dataDir, 'nomemory');
|
|
230
240
|
|
|
231
241
|
expect(Object.keys(tool)).toEqual(['openchamber']);
|
|
232
242
|
});
|
|
233
243
|
|
|
234
244
|
it('injects the plugin when memory is the only tool left on', async () => {
|
|
235
245
|
const { runtime, dataDir } = await createRuntime();
|
|
236
|
-
await runtime
|
|
237
|
-
const
|
|
238
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?onlymemory=${Date.now()}`);
|
|
239
|
-
const { tool } = await pluginModule.OpenChamberPlugin();
|
|
246
|
+
await prepareManagedEnv(runtime, { includeControl: false, includeWeb: false, includeMemory: true });
|
|
247
|
+
const tool = await loadTools(dataDir, 'onlymemory');
|
|
240
248
|
|
|
241
249
|
expect(Object.keys(tool)).toEqual(['openchamber_memory']);
|
|
242
250
|
});
|
|
@@ -245,7 +253,7 @@ describe('managed agent tool runtime', () => {
|
|
|
245
253
|
const { runtime } = await createRuntime();
|
|
246
254
|
let failed = false;
|
|
247
255
|
try {
|
|
248
|
-
await runtime
|
|
256
|
+
await prepareManagedEnv(runtime, { includeControl: false, includeWeb: false, includeMemory: false });
|
|
249
257
|
} catch {
|
|
250
258
|
failed = true;
|
|
251
259
|
}
|
|
@@ -261,6 +269,7 @@ describe('managed agent tool runtime', () => {
|
|
|
261
269
|
const result = await runtime.execute({
|
|
262
270
|
input: { action: 'read', title: 'Uses bun' },
|
|
263
271
|
contextDirectory: '/work/project',
|
|
272
|
+
contextSessionId: 'ses_1',
|
|
264
273
|
tool: 'openchamber_memory',
|
|
265
274
|
});
|
|
266
275
|
|
|
@@ -270,7 +279,7 @@ describe('managed agent tool runtime', () => {
|
|
|
270
279
|
'memory.read',
|
|
271
280
|
{ action: 'memory.read', title: 'Uses bun' },
|
|
272
281
|
'/work/project',
|
|
273
|
-
{},
|
|
282
|
+
{ contextSessionId: 'ses_1' },
|
|
274
283
|
);
|
|
275
284
|
});
|
|
276
285
|
|
|
@@ -317,6 +326,35 @@ describe('managed agent tool runtime', () => {
|
|
|
317
326
|
expect(executeAction).toHaveBeenCalledWith('projects.list', { action: 'projects.list' }, '/work/project', {});
|
|
318
327
|
});
|
|
319
328
|
|
|
329
|
+
it('resolves the directory from the session id the plugin sends', async () => {
|
|
330
|
+
const executeAction = vi.fn(async () => ({}));
|
|
331
|
+
const resolveSessionDirectory = vi.fn(async () => '/work/other-worktree');
|
|
332
|
+
const { runtime } = await createRuntime({ executeAction, resolveSessionDirectory });
|
|
333
|
+
|
|
334
|
+
await runtime.execute({ input: { action: 'session.messages', sessionId: 'ses_1' }, sessionID: 'ses_1' });
|
|
335
|
+
|
|
336
|
+
expect(resolveSessionDirectory).toHaveBeenCalledWith('ses_1');
|
|
337
|
+
// The calling session also scopes the action (browser pages are per session).
|
|
338
|
+
expect(executeAction).toHaveBeenCalledWith(
|
|
339
|
+
'session.messages',
|
|
340
|
+
{ action: 'session.messages', sessionId: 'ses_1' },
|
|
341
|
+
'/work/other-worktree',
|
|
342
|
+
{ contextSessionId: 'ses_1' },
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('runs without a directory when the session cannot be resolved', async () => {
|
|
347
|
+
const executeAction = vi.fn(async () => ({}));
|
|
348
|
+
const { runtime } = await createRuntime({
|
|
349
|
+
executeAction,
|
|
350
|
+
resolveSessionDirectory: vi.fn(async () => { throw new Error('unavailable'); }),
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await runtime.execute({ input: { action: 'projects.list' }, sessionID: 'ses_1' });
|
|
354
|
+
|
|
355
|
+
expect(executeAction).toHaveBeenCalledWith('projects.list', { action: 'projects.list' }, undefined, { contextSessionId: 'ses_1' });
|
|
356
|
+
});
|
|
357
|
+
|
|
320
358
|
it('keeps service failures as structured tool results', async () => {
|
|
321
359
|
const error = Object.assign(new Error('Task not found'), { statusCode: 404 });
|
|
322
360
|
const { runtime } = await createRuntime({ executeAction: vi.fn(async () => { throw error; }) });
|
|
@@ -352,9 +390,45 @@ describe('managed agent tool runtime', () => {
|
|
|
352
390
|
expect(executeAction).toHaveBeenCalledWith('projects.list', { action: 'projects.list' }, undefined, { signal: controller.signal });
|
|
353
391
|
});
|
|
354
392
|
|
|
393
|
+
it('aborts the actions a session still has running when its turn is cancelled', async () => {
|
|
394
|
+
// Safety net beside the plugin's forwarded abort signal: the server also
|
|
395
|
+
// ends the action itself when the event stream reports the cancel.
|
|
396
|
+
const executeAction = vi.fn(async (_action, _input, _directory, options) => {
|
|
397
|
+
await new Promise((resolve, reject) => {
|
|
398
|
+
options.signal.addEventListener('abort', () => reject(Object.assign(new Error('OpenChamber action was cancelled'), { statusCode: 499 })), { once: true });
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
const { runtime } = await createRuntime({ executeAction });
|
|
402
|
+
const env = await prepareManagedEnv(runtime);
|
|
403
|
+
const app = express();
|
|
404
|
+
runtime.registerRoutes(app, express);
|
|
405
|
+
|
|
406
|
+
// supertest only sends when awaited; `.then` starts both requests now.
|
|
407
|
+
const pending = request(app)
|
|
408
|
+
.post('/api/openchamber/agent-tool')
|
|
409
|
+
.set('authorization', `Bearer ${env.OPENCHAMBER_AGENT_TOOL_TOKEN}`)
|
|
410
|
+
.send({ input: { action: 'projects.list' }, sessionID: 'ses_cancel' })
|
|
411
|
+
.then((response) => response);
|
|
412
|
+
const other = request(app)
|
|
413
|
+
.post('/api/openchamber/agent-tool')
|
|
414
|
+
.set('authorization', `Bearer ${env.OPENCHAMBER_AGENT_TOOL_TOKEN}`)
|
|
415
|
+
.send({ input: { action: 'projects.list' }, sessionID: 'ses_other' })
|
|
416
|
+
.then((response) => response);
|
|
417
|
+
// Let both requests reach the service before cancelling one session.
|
|
418
|
+
await vi.waitFor(() => expect(executeAction).toHaveBeenCalledTimes(2));
|
|
419
|
+
|
|
420
|
+
expect(runtime.abortSession('ses_cancel')).toBe(1);
|
|
421
|
+
const response = await pending;
|
|
422
|
+
expect(response.body).toEqual(expect.objectContaining({ ok: false, error: expect.objectContaining({ message: 'OpenChamber action was cancelled' }) }));
|
|
423
|
+
|
|
424
|
+
// The other session's action is untouched until it is told otherwise.
|
|
425
|
+
expect(runtime.abortSession('ses_other')).toBe(1);
|
|
426
|
+
await other;
|
|
427
|
+
});
|
|
428
|
+
|
|
355
429
|
it('requires the per-child token on the loopback route', async () => {
|
|
356
430
|
const { runtime } = await createRuntime();
|
|
357
|
-
const env = await runtime
|
|
431
|
+
const env = await prepareManagedEnv(runtime);
|
|
358
432
|
const app = express();
|
|
359
433
|
runtime.registerRoutes(app, express);
|
|
360
434
|
|
|
@@ -380,7 +454,7 @@ describe('managed agent tool runtime', () => {
|
|
|
380
454
|
['fd7a:115c::3', 'http://[fd7a:115c::3]:3901/api/openchamber/agent-tool'],
|
|
381
455
|
])('points the callback at where a listener bound to %s answers', async (boundAddress, expectedUrl) => {
|
|
382
456
|
const { runtime } = await createRuntime({ getActiveHost: () => boundAddress });
|
|
383
|
-
const env = await runtime
|
|
457
|
+
const env = await prepareManagedEnv(runtime);
|
|
384
458
|
expect(env.OPENCHAMBER_AGENT_TOOL_URL).toBe(expectedUrl);
|
|
385
459
|
});
|
|
386
460
|
|
|
@@ -395,7 +469,7 @@ describe('managed agent tool runtime', () => {
|
|
|
395
469
|
[null, '192.168.1.20', 401],
|
|
396
470
|
])('bound to %s, answers a token-bearing caller from %s with %i', async (boundAddress, remoteAddress, status) => {
|
|
397
471
|
const { runtime } = await createRuntime({ getActiveHost: () => boundAddress });
|
|
398
|
-
const env = await runtime
|
|
472
|
+
const env = await prepareManagedEnv(runtime);
|
|
399
473
|
const app = express();
|
|
400
474
|
app.use((req, _res, next) => {
|
|
401
475
|
Object.defineProperty(req.socket, 'remoteAddress', { value: remoteAddress, configurable: true });
|
|
@@ -417,7 +491,7 @@ describe('managed agent tool runtime', () => {
|
|
|
417
491
|
['not a url', 'localhost', 'localhost'],
|
|
418
492
|
])('keeps the callback %s away from an environment proxy', async (callbackUrl, existing, expected) => {
|
|
419
493
|
const { runtime, dataDir } = await createRuntime();
|
|
420
|
-
await runtime
|
|
494
|
+
await prepareManagedEnv(runtime);
|
|
421
495
|
const keys = ['OPENCHAMBER_AGENT_TOOL_URL', 'NO_PROXY', 'no_proxy'];
|
|
422
496
|
const previous = keys.map((key) => process.env[key]);
|
|
423
497
|
try {
|
|
@@ -426,9 +500,7 @@ describe('managed agent tool runtime', () => {
|
|
|
426
500
|
if (existing === undefined) delete process.env[key];
|
|
427
501
|
else process.env[key] = existing;
|
|
428
502
|
}
|
|
429
|
-
|
|
430
|
-
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
|
|
431
|
-
await pluginModule.OpenChamberPlugin();
|
|
503
|
+
await loadTools(dataDir, 'proxy');
|
|
432
504
|
|
|
433
505
|
expect(process.env.NO_PROXY).toBe(expected);
|
|
434
506
|
expect(process.env.no_proxy).toBe(expected);
|
|
@@ -453,32 +525,28 @@ describe('managed agent tool runtime', () => {
|
|
|
453
525
|
const previousUrl = process.env.OPENCHAMBER_AGENT_TOOL_URL;
|
|
454
526
|
const previousToken = process.env.OPENCHAMBER_AGENT_TOOL_TOKEN;
|
|
455
527
|
try {
|
|
456
|
-
const env = await runtime
|
|
528
|
+
const env = await prepareManagedEnv(runtime);
|
|
457
529
|
process.env.OPENCHAMBER_AGENT_TOOL_URL = env.OPENCHAMBER_AGENT_TOOL_URL;
|
|
458
530
|
process.env.OPENCHAMBER_AGENT_TOOL_TOKEN = env.OPENCHAMBER_AGENT_TOOL_TOKEN;
|
|
459
|
-
const
|
|
460
|
-
const
|
|
461
|
-
const hooks = await pluginModule.OpenChamberPlugin();
|
|
462
|
-
const metadata = vi.fn();
|
|
531
|
+
const tool = await loadTools(dataDir, 'callback');
|
|
532
|
+
const progress = vi.fn(async () => {});
|
|
463
533
|
|
|
464
|
-
const result = await
|
|
534
|
+
const result = await tool.openchamber.execute(
|
|
465
535
|
{ action: 'projects.list', parameters: {} },
|
|
466
|
-
{
|
|
536
|
+
{ sessionID: 'ses_1', agent: 'build', messageID: 'msg_1', id: 'call_1', progress },
|
|
467
537
|
);
|
|
468
538
|
|
|
469
|
-
|
|
539
|
+
// No output schema is declared, so the envelope has to travel as content.
|
|
540
|
+
expect(result.output).toBeUndefined();
|
|
541
|
+
expect(JSON.parse(result.content)).toEqual({
|
|
470
542
|
schemaVersion: 1,
|
|
471
543
|
ok: true,
|
|
472
544
|
action: 'projects.list',
|
|
473
545
|
data: { projects: [] },
|
|
474
546
|
});
|
|
475
|
-
expect(result.title).toBe('List configured projects');
|
|
476
547
|
expect(result.metadata.openchamber.description).toBe('List configured projects');
|
|
477
|
-
expect(
|
|
478
|
-
|
|
479
|
-
metadata: expect.objectContaining({
|
|
480
|
-
openchamber: expect.objectContaining({ description: 'List configured projects' }),
|
|
481
|
-
}),
|
|
548
|
+
expect(progress).toHaveBeenCalledWith(expect.objectContaining({
|
|
549
|
+
openchamber: expect.objectContaining({ description: 'List configured projects' }),
|
|
482
550
|
}));
|
|
483
551
|
} finally {
|
|
484
552
|
if (previousUrl === undefined) delete process.env.OPENCHAMBER_AGENT_TOOL_URL;
|
|
@@ -16,9 +16,36 @@ itself; it can only ask and wait.
|
|
|
16
16
|
validates the envelope and hands the outcome to the broker.
|
|
17
17
|
- `../../index.js` supplies `emitRequest`, which writes the request to the
|
|
18
18
|
OpenChamber SSE clients and returns how many were reached.
|
|
19
|
+
- `provider.js` sits between the control service and the broker. It reads
|
|
20
|
+
the `browserProvider` setting on every action: `builtin` goes to the broker;
|
|
21
|
+
an extension id goes to that extension's service (`contributes.service.provides`
|
|
22
|
+
includes `browser`) as `POST /browser-control` on its loopback through
|
|
23
|
+
`../guests/service.js`, with the open/action timeouts, the response cap, and
|
|
24
|
+
the idle stop from `@openchamber/sdk`. The body also carries `context`
|
|
25
|
+
(`directory`, `sessionId`, each `null` when unknown): the project and chat the
|
|
26
|
+
tool call came from, threaded from the plugin (`contextDirectory`,
|
|
27
|
+
`contextSessionId`) through the control service; the model never types it.
|
|
28
|
+
The answer is parsed with
|
|
29
|
+
`browserProviderResultSchema`; `ok: false` becomes the agent's error, any
|
|
30
|
+
other status or shape is reported as unknown page state. A selected extension
|
|
31
|
+
that cannot serve (`isBrowserProviderGuest`: enabled, fully approved, has the
|
|
32
|
+
role) resets the setting to `builtin`, emits
|
|
33
|
+
`openchamber:browser-provider-reset`, and runs the action in-app. A settings
|
|
34
|
+
or catalog read that fails is not that: the action fails with 503, nothing
|
|
35
|
+
runs anywhere, and the choice stays, because a read error says nothing
|
|
36
|
+
about the extension and an action must never land in a browser the user
|
|
37
|
+
did not pick. `REQUEST_FAILED` (sent, no answer) is reported to the agent
|
|
38
|
+
as "may or may not have run, read the page first", never as unchanged.
|
|
39
|
+
`handleGuestDeactivated` does the same reset when the guest routes pause,
|
|
40
|
+
remove, or withdraw approval from the selected extension.
|
|
41
|
+
When the extension also shows a shared surface (`../guests/surface.js`),
|
|
42
|
+
an action is refused with 409 while the user holds the surface, and every
|
|
43
|
+
action that runs is reported as agent activity so the panel says "Agent is
|
|
44
|
+
working".
|
|
19
45
|
- `../openchamber-control/service.js` is the only caller. It maps the
|
|
20
|
-
`browser.*` actions of the `openchamber_web` tool onto
|
|
21
|
-
owns their parameter
|
|
46
|
+
`browser.*` actions of the `openchamber_web` tool onto the router's
|
|
47
|
+
`request()` (same signature as the broker) and owns their parameter
|
|
48
|
+
validation.
|
|
22
49
|
- The client half is `packages/ui/src/lib/browser/controlClient.ts`, which
|
|
23
50
|
registers the mounted browser pane as the one responder.
|
|
24
51
|
|
|
@@ -48,3 +75,11 @@ itself; it can only ask and wait.
|
|
|
48
75
|
and a missing one silently turns every answer into an agent-visible timeout.
|
|
49
76
|
- Request payload limits are sized for a page snapshot (visible text plus every
|
|
50
77
|
interactive element), not for a control message.
|
|
78
|
+
- The provider path never trusts the setting alone: the extension is
|
|
79
|
+
re-checked against the catalog on every action, and the grant list handed to
|
|
80
|
+
the service proxy is the catalog's effective list, so a version that widened
|
|
81
|
+
its permissions is refused until re-approved.
|
|
82
|
+
- The provider's answer shapes are the desktop panel's own
|
|
83
|
+
(`packages/sdk/src/service-providers.ts`); `browser.capture` still returns
|
|
84
|
+
`base64`/`mime` and the control service writes the file, so the agent sees
|
|
85
|
+
the same result whoever took the picture.
|