@vantran-se/openchamber-web 2.0.4 → 2.0.5
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/dist/assets/{CodeMirrorCommentWidgets-DZF8ntaq.js → CodeMirrorCommentWidgets-DCAQTLMM.js} +1 -1
- package/dist/assets/DiffView-uzNpHBlV.js +4 -0
- package/dist/assets/FilesView-Bkq0sjNx.js +17 -0
- package/dist/assets/{GitView-DwQwqDvu.js → GitView-BmibZC7n.js} +4 -4
- package/dist/assets/{GuestAttachDialog-CoXM0gh_.js → GuestAttachDialog-D7TJG1rt.js} +1 -1
- package/dist/assets/GuestSurfacePane-PAcYaFzP.js +1 -0
- package/dist/assets/{JsonTreeView-Cq_27v0y.js → JsonTreeView-BNT2yUVu.js} +1 -1
- package/dist/assets/{LinearIssuesView-CsSSRMhn.js → LinearIssuesView-D3Azksju.js} +1 -1
- package/dist/assets/MarkdownImageGallery-BGY6tuiK.js +1 -0
- package/dist/assets/MarkdownRendererImpl-DJmhQ37f.js +12 -0
- package/dist/assets/{OnboardingScreen-ClatMQAX.js → OnboardingScreen-D_41H2qC.js} +1 -1
- package/dist/assets/{PatchDiff-DOwlrIK9.js → PatchDiff-C0RSqa9W.js} +1 -1
- package/dist/assets/{PierreDiffViewer-CrcGqqD_.js → PierreDiffViewer-BBE7tTY2.js} +3 -3
- package/dist/assets/{PlanView-BGLsMgQk.js → PlanView-Bqc-xuDe.js} +4 -4
- package/dist/assets/PluginPane-DQ0k41Q-.js +3 -0
- package/dist/assets/SettingsView-BAiJU5hJ.js +30 -0
- package/dist/assets/{SettingsWindow-B0BAy-f9.js → SettingsWindow-DuC4ecLw.js} +1 -1
- package/dist/assets/{SubmoduleDiffSummary-fcdDzCSW.js → SubmoduleDiffSummary-0HgmcMXz.js} +1 -1
- package/dist/assets/{ToolOutputDialog-B7xaUz2H.js → ToolOutputDialog-DdHkQJEE.js} +6 -6
- package/dist/assets/{ToolPartDiffPreview-CkWCaUmZ.js → ToolPartDiffPreview-DWdN0r5K.js} +2 -2
- package/dist/assets/{VirtualizedFileDiff-DONC4eI2.js → VirtualizedFileDiff-ClDezf2d.js} +1 -1
- package/dist/assets/{WalkthroughView-CXDImOXW.js → WalkthroughView-BJicp0RC.js} +2 -2
- package/dist/assets/{appThemeRegistry-Br2TLSlb.js → appThemeRegistry-CiuyzfSW.js} +1 -1
- package/dist/assets/changesTree-CePHbFcj.js +1 -0
- package/dist/assets/commitAndPush-DTKb6IwT.js +1 -0
- package/dist/assets/{computeVirtualFileMetrics-_-20umCs.js → computeVirtualFileMetrics-kM_zJNMt.js} +2 -2
- package/dist/assets/de-BMnfXm-G.js +10 -0
- package/dist/assets/es-DKEvCCL6.js +16 -0
- package/dist/assets/fr-D9fgE2xE.js +14 -0
- package/dist/assets/index-DUke1nme.css +1 -0
- package/dist/assets/{index-OKDYjcMF.js → index-DWB8bR_g.js} +1 -1
- package/dist/assets/index-Di6zUe5h.js +41 -0
- package/dist/assets/{isDiffPlainText-D77M6q1f.js → isDiffPlainText-DOoIsGqs.js} +1 -1
- package/dist/assets/{isFilePlainText-C54B4Mns.js → isFilePlainText-bt5VHvcd.js} +1 -1
- package/dist/assets/ja-BEXad-KW.js +16 -0
- package/dist/assets/ko-DGrQ0lEe.js +16 -0
- package/dist/assets/{main-CGEvLMd2.js → main-BpLQbZC2.js} +2 -2
- package/dist/assets/main-CI9DtHJv.js +896 -0
- package/dist/assets/{markdown-shiki.worker-CahLvHIq.js → markdown-shiki.worker-Bg8SoAJJ.js} +8 -5
- package/dist/assets/markdownCore-DK2KK5RW.js +7 -0
- package/dist/assets/{miniChat-BSxkRQLj.js → miniChat-CIWaR8wG.js} +2 -2
- package/dist/assets/{mobile-C5KRTNOB.js → mobile-CnWKEYIP.js} +2 -2
- package/dist/assets/patchFileDiff-Cqn0C0az.js +13 -0
- package/dist/assets/pl-BlhkmR5b.js +16 -0
- package/dist/assets/pt-BR-Bs96Ew9I.js +16 -0
- package/dist/assets/{renderElectronMiniChatApp-CnkAQV6o.js → renderElectronMiniChatApp-D9t0-9nV.js} +2 -2
- package/dist/assets/renderMobileApp-CW7fVs_s.js +25 -0
- package/dist/assets/runtimeEndpointReset-D0PcCb3K.js +27 -0
- package/dist/assets/scheduledTasksApi-Dahv3ws0.js +1 -0
- package/dist/assets/{shikiHighlight-4O0ZM14D.js → shikiHighlight-l-MbaNS4.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-yNq7b0DZ.js → textMateThemeFromAppTheme-C-kGcOpD.js} +1 -1
- package/dist/assets/tr-CehUK1M9.js +7 -0
- package/dist/assets/uk-Bno5WGNz.js +16 -0
- package/dist/assets/{useAppFontEffects-Bzyszv2N.js → useAppFontEffects-DDjuwVqi.js} +160 -156
- package/dist/assets/{useInlineCommentController-zOf6ORAq.js → useInlineCommentController-Clg8EO3A.js} +2 -2
- package/dist/assets/{useRootScrollLock-DCaZ4BWi.js → useRootScrollLock-nDcRrgJ1.js} +2 -2
- package/dist/assets/{vendor-codemirror-language-data-6cEFPNUR.js → vendor-codemirror-language-data-oKF0MAwd.js} +2 -2
- package/dist/assets/vendor-opencode-client-DSZl7jSi.js +12 -0
- package/dist/assets/vendor-zod-BSoePkpP.js +39 -0
- package/dist/assets/{web-components-BEo4-ISu.js → web-components-Bc1WPd4i.js} +1 -1
- package/dist/assets/zh-CN-DnAweIWo.js +16 -0
- package/dist/assets/zh-TW-BbBBK25p.js +16 -0
- package/dist/index.html +5 -5
- package/dist/mini-chat.html +5 -5
- package/dist/mobile.html +5 -5
- package/package.json +3 -3
- package/server/index.js +82 -0
- package/server/lib/agent-tool/DOCUMENTATION.md +132 -47
- package/server/lib/agent-tool/runtime.js +34 -15
- package/server/lib/agent-tool/runtime.test.js +15 -0
- package/server/lib/browser-control/DOCUMENTATION.md +14 -2
- package/server/lib/event-stream/global-hub.js +23 -1
- package/server/lib/event-stream/global-hub.test.js +35 -0
- package/server/lib/event-stream/global-ws-bridge.js +3 -1
- package/server/lib/event-stream/upstream-reader.js +3 -2
- package/server/lib/git/DOCUMENTATION.md +1 -1
- package/server/lib/git/service.js +22 -0
- package/server/lib/git/service.test.js +8 -0
- package/server/lib/guests/DOCUMENTATION.md +4 -3
- package/server/lib/guests/catalog.js +19 -1
- package/server/lib/guests/catalog.test.js +22 -0
- package/server/lib/guests/html-styles.js +5 -3
- package/server/lib/guests/html-styles.test.js +3 -1
- package/server/lib/guests/routes.js +11 -3
- package/server/lib/guests/service.js +5 -0
- package/server/lib/guests/surface.js +55 -9
- package/server/lib/guests/surface.test.js +56 -6
- package/server/lib/notifications/DOCUMENTATION.md +10 -0
- package/server/lib/notifications/emit-route.js +118 -0
- package/server/lib/notifications/emit-route.test.js +112 -0
- package/server/lib/openchamber-control/actions.js +16 -2
- package/server/lib/openchamber-control/service.js +23 -0
- package/server/lib/openchamber-control/service.test.js +31 -0
- package/server/lib/openchamber-sessions/fork-inheritance.js +103 -0
- package/server/lib/openchamber-sessions/fork-inheritance.test.js +69 -0
- package/server/lib/openchamber-sessions/routes.js +11 -0
- package/server/lib/openchamber-sessions/routes.test.js +18 -0
- package/server/lib/opencode/bootstrap-runtime.js +14 -1
- package/server/lib/opencode/compatibility.js +6 -3
- package/server/lib/opencode/compatibility.test.js +8 -0
- package/server/lib/opencode/config-entity-v2.test.js +27 -0
- package/server/lib/opencode/config-paths.test.js +12 -0
- package/server/lib/opencode/config-v2.d.ts +2 -0
- package/server/lib/opencode/config-v2.js +42 -0
- package/server/lib/opencode/config-v2.test.js +17 -0
- package/server/lib/opencode/core-routes.js +20 -4
- package/server/lib/opencode/core-routes.test.js +41 -0
- package/server/lib/opencode/env-runtime.js +1 -8
- package/server/lib/opencode/feature-routes-runtime.js +2 -1
- package/server/lib/opencode/lifecycle.js +30 -2
- package/server/lib/opencode/lifecycle.test.js +27 -0
- package/server/lib/opencode/managed-config-file.js +18 -6
- package/server/lib/opencode/managed-config-file.test.js +23 -7
- package/server/lib/opencode/mcp.js +7 -2
- package/server/lib/opencode/npm-registry-config.d.ts +1 -0
- package/server/lib/opencode/npm-registry-config.js +123 -0
- package/server/lib/opencode/npm-registry-config.test.js +167 -0
- package/server/lib/opencode/npm-registry.js +5 -6
- package/server/lib/opencode/npm-registry.test.js +30 -0
- package/server/lib/opencode/openchamber-routes.js +29 -11
- package/server/lib/opencode/openchamber-routes.test.js +18 -0
- package/server/lib/opencode/project-directory-runtime.js +8 -0
- package/server/lib/opencode/project-directory-runtime.test.js +13 -0
- package/server/lib/opencode/provider-source-route.test.js +59 -0
- package/server/lib/opencode/providers.js +3 -0
- package/server/lib/opencode/providers.test.js +62 -0
- package/server/lib/opencode/proxy.js +38 -2
- package/server/lib/opencode/routes-upgrade.test.js +1 -1
- package/server/lib/opencode/routes.js +36 -1
- package/server/lib/opencode/server-utils-runtime.js +5 -0
- package/server/lib/opencode/settings-helpers.js +10 -0
- package/server/lib/opencode/settings-helpers.test.js +2 -2
- package/server/lib/opencode/settings-registry.json +17 -0
- package/server/lib/opencode/settings-runtime.js +1 -0
- package/server/lib/opencode/shared.js +49 -4
- package/server/lib/opencode/shared.test.js +78 -1
- package/server/lib/opencode/shutdown-runtime.js +3 -0
- package/server/lib/opencode/skill-routes.js +28 -12
- package/server/lib/opencode/skill-routes.test.js +121 -1
- package/server/lib/opencode/upgrade-capability.js +0 -14
- package/server/lib/opencode/v1-migration-topup.js +21 -15
- package/server/lib/opencode/v1-migration-topup.test.js +14 -6
- package/server/lib/opencode/v2-install.d.ts +1 -0
- package/server/lib/opencode/v2-install.js +8 -7
- package/server/lib/opencode/v2-install.test.js +13 -4
- package/server/lib/opencode/watcher.js +3 -1
- package/server/lib/opencode/websearch-config.js +13 -1
- package/server/lib/quota/providers/kimi.js +1 -1
- package/server/lib/realtime-proxy.js +5 -1
- package/server/lib/realtime-proxy.test.js +29 -0
- package/server/lib/relay/tunnel-host.js +4 -1
- package/server/lib/relay/tunnel-host.test.js +11 -0
- package/server/lib/session-assist/DOCUMENTATION.md +17 -5
- package/server/lib/session-assist/runtime.js +19 -2
- package/server/lib/session-assist/runtime.test.js +8 -0
- package/server/lib/session-goal/DOCUMENTATION.md +11 -0
- package/server/lib/small-model/DOCUMENTATION.md +11 -10
- package/server/lib/small-model/index.js +27 -32
- package/server/lib/small-model/index.test.js +28 -16
- package/server/lib/spaces/DOCUMENTATION.md +404 -32
- package/server/lib/spaces/code-in-bait.js +145 -6
- package/server/lib/spaces/code-in.js +10 -150
- package/server/lib/spaces/code-in.test.js +7 -83
- package/server/lib/spaces/code-out.js +1819 -0
- package/server/lib/spaces/code-out.test.js +3945 -0
- package/server/lib/spaces/code-transfer.js +173 -0
- package/server/lib/spaces/command-stream.test.js +181 -0
- package/server/lib/spaces/dispatcher.js +670 -0
- package/server/lib/spaces/dispatcher.test.js +582 -0
- package/server/lib/spaces/gatekeeper-channel.js +61 -7
- package/server/lib/spaces/gatekeeper-channel.test.js +43 -7
- package/server/lib/spaces/gatekeeper-program.cjs +68 -14
- package/server/lib/spaces/gatekeeper-program.test.js +118 -3
- package/server/lib/spaces/hardening.js +20 -4
- package/server/lib/spaces/hardening.test.js +18 -3
- package/server/lib/spaces/host-git.js +7 -4
- package/server/lib/spaces/host-git.test.js +6 -2
- package/server/lib/spaces/host.js +310 -0
- package/server/lib/spaces/host.test.js +273 -0
- package/server/lib/spaces/journey.js +552 -0
- package/server/lib/spaces/journey.test.js +589 -0
- package/server/lib/spaces/layout.js +42 -8
- package/server/lib/spaces/manager.js +4 -3
- package/server/lib/spaces/manager.test.js +6 -0
- package/server/lib/spaces/places/code-out.docker.live.test.js +412 -0
- package/server/lib/spaces/places/contract-suite.js +36 -0
- package/server/lib/spaces/places/dispatcher.docker.live.test.js +296 -0
- package/server/lib/spaces/places/docker-live-support.js +27 -4
- package/server/lib/spaces/places/docker.js +33 -5
- package/server/lib/spaces/places/docker.test.js +42 -3
- package/server/lib/spaces/places/escape-suite.js +118 -14
- package/server/lib/spaces/places/fake-docker.js +3 -1
- package/server/lib/spaces/places/grants.docker.live.test.js +197 -0
- package/server/lib/spaces/places/memory-place.js +24 -1
- package/server/lib/spaces/places/registry.js +1 -1
- package/server/lib/spaces/places/server.docker.live.test.js +69 -33
- package/server/lib/spaces/places/sessions.docker.live.test.js +222 -0
- package/server/lib/spaces/routes.js +216 -0
- package/server/lib/spaces/routes.test.js +213 -0
- package/server/lib/spaces/run-command.js +261 -8
- package/server/lib/spaces/run-command.test.js +193 -3
- package/server/lib/spaces/space-events.js +153 -0
- package/server/lib/spaces/space-events.test.js +160 -0
- package/server/lib/spaces/space-opencode.js +52 -0
- package/server/lib/spaces/space-opencode.test.js +41 -0
- package/server/lib/spaces/space-records.js +150 -0
- package/server/lib/spaces/space-records.test.js +113 -0
- package/server/lib/spaces/space-sessions.js +210 -0
- package/server/lib/spaces/space-sessions.test.js +161 -0
- package/server/lib/spaces/websocket.js +329 -0
- package/server/lib/spaces/websocket.test.js +345 -0
- package/server/lib/ui-auth/ui-auth.js +8 -2
- package/server/lib/ui-auth/ui-auth.test.js +29 -0
- package/server/opencode-proxy.test.js +113 -0
- package/dist/assets/DiffView-D2p4Ar0H.js +0 -4
- package/dist/assets/FilesView-BeTtbsip.js +0 -17
- package/dist/assets/GuestSurfacePane-DIJvkkM-.js +0 -1
- package/dist/assets/MarkdownImageGallery-CY0dWTaE.js +0 -1
- package/dist/assets/MarkdownRendererImpl-ILdXzLrt.js +0 -12
- package/dist/assets/PluginPane-B39JEUL8.js +0 -3
- package/dist/assets/SettingsView-13B2_G-A.js +0 -29
- package/dist/assets/commitAndPush-CcI4ol7c.js +0 -1
- package/dist/assets/de-BtYnUgII.js +0 -10
- package/dist/assets/es-crP5qPye.js +0 -16
- package/dist/assets/fr-CLSRuALk.js +0 -14
- package/dist/assets/index-CLcM8HKx.css +0 -1
- package/dist/assets/index-CvLgnuNI.js +0 -41
- package/dist/assets/ja-SZ1-ucLV.js +0 -16
- package/dist/assets/ko-HthbT_Ve.js +0 -16
- package/dist/assets/main-BJklC7Cl.js +0 -896
- package/dist/assets/markdownCore-BzQuETm0.js +0 -5
- package/dist/assets/patchFileDiff-DGw2KryM.js +0 -13
- package/dist/assets/pl-C8eYfjL1.js +0 -16
- package/dist/assets/pt-BR-cYOrZdxS.js +0 -16
- package/dist/assets/renderMobileApp-CMxjwisj.js +0 -25
- package/dist/assets/runtimeEndpointReset-C45jrFq4.js +0 -27
- package/dist/assets/scheduledTasksApi-CkK1F9s2.js +0 -1
- package/dist/assets/tr-B4IQ5t-T.js +0 -7
- package/dist/assets/uk-DCoXG6ve.js +0 -16
- package/dist/assets/vendor-opencode-client-_BAHQo4R.js +0 -12
- package/dist/assets/vendor-zod-Gea9uBlY.js +0 -39
- package/dist/assets/zh-CN-BXBoxsFk.js +0 -16
- package/dist/assets/zh-TW-C6oelpnL.js +0 -16
package/dist/index.html
CHANGED
|
@@ -515,17 +515,17 @@
|
|
|
515
515
|
pointer-events: none;
|
|
516
516
|
}
|
|
517
517
|
</style>
|
|
518
|
-
<script type="module" crossorigin src="/assets/main-
|
|
518
|
+
<script type="module" crossorigin src="/assets/main-BpLQbZC2.js"></script>
|
|
519
519
|
<link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
|
|
520
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-zod-
|
|
520
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-zod-BSoePkpP.js">
|
|
521
521
|
<link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
|
|
522
522
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
|
|
523
523
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
|
|
524
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-
|
|
525
|
-
<link rel="modulepreload" crossorigin href="/assets/index-
|
|
524
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-DSZl7jSi.js">
|
|
525
|
+
<link rel="modulepreload" crossorigin href="/assets/index-Di6zUe5h.js">
|
|
526
526
|
<link rel="modulepreload" crossorigin href="/assets/contextPanelEmbeddedChat-CirXEPJq.js">
|
|
527
527
|
<link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
|
|
528
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
528
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DUke1nme.css">
|
|
529
529
|
<link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
|
|
530
530
|
</head>
|
|
531
531
|
<body class="h-full bg-background text-foreground">
|
package/dist/mini-chat.html
CHANGED
|
@@ -74,16 +74,16 @@
|
|
|
74
74
|
}
|
|
75
75
|
</style>
|
|
76
76
|
|
|
77
|
-
<script type="module" crossorigin src="/assets/miniChat-
|
|
77
|
+
<script type="module" crossorigin src="/assets/miniChat-CIWaR8wG.js"></script>
|
|
78
78
|
<link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
|
|
79
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-zod-
|
|
79
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-zod-BSoePkpP.js">
|
|
80
80
|
<link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
|
|
81
81
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
|
|
82
82
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
|
|
83
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-
|
|
84
|
-
<link rel="modulepreload" crossorigin href="/assets/index-
|
|
83
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-DSZl7jSi.js">
|
|
84
|
+
<link rel="modulepreload" crossorigin href="/assets/index-Di6zUe5h.js">
|
|
85
85
|
<link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
|
|
86
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
86
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DUke1nme.css">
|
|
87
87
|
<link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
|
|
88
88
|
</head>
|
|
89
89
|
<body class="h-full bg-background text-foreground">
|
package/dist/mobile.html
CHANGED
|
@@ -63,16 +63,16 @@
|
|
|
63
63
|
})();
|
|
64
64
|
</script>
|
|
65
65
|
|
|
66
|
-
<script type="module" crossorigin src="/assets/mobile-
|
|
66
|
+
<script type="module" crossorigin src="/assets/mobile-CnWKEYIP.js"></script>
|
|
67
67
|
<link rel="modulepreload" crossorigin href="/assets/vendor-vite-runtime-DD1OZmLC.js">
|
|
68
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-zod-
|
|
68
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-zod-BSoePkpP.js">
|
|
69
69
|
<link rel="modulepreload" crossorigin href="/assets/vendor-scheduler-7OC5HNn7.js">
|
|
70
70
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BUCujlKS.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-BAGL70zf.js">
|
|
72
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-
|
|
73
|
-
<link rel="modulepreload" crossorigin href="/assets/index-
|
|
72
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-DSZl7jSi.js">
|
|
73
|
+
<link rel="modulepreload" crossorigin href="/assets/index-Di6zUe5h.js">
|
|
74
74
|
<link rel="modulepreload" crossorigin href="/assets/vendor-katex-CHD9BdDO.js">
|
|
75
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
75
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DUke1nme.css">
|
|
76
76
|
<link rel="stylesheet" crossorigin href="/assets/vendor-katex-Cp579UoJ.css">
|
|
77
77
|
</head>
|
|
78
78
|
<body class="h-full bg-background text-foreground">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vantran-se/openchamber-web",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./server/index.js",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"@clack/prompts": "^1.1.0",
|
|
28
28
|
"@octokit/rest": "^22.0.1",
|
|
29
29
|
"@openchamber/sdk": "2.0.0",
|
|
30
|
-
"@opencode/client": "2.0.
|
|
31
|
-
"@opencode/schema": "2.0.
|
|
30
|
+
"@opencode/client": "2.0.18",
|
|
31
|
+
"@opencode/schema": "2.0.18",
|
|
32
32
|
"@simplewebauthn/server": "13.3.3",
|
|
33
33
|
"bun-pty": "^0.4.5",
|
|
34
34
|
"compression": "^1.8.1",
|
package/server/index.js
CHANGED
|
@@ -118,6 +118,8 @@ import { createAgentMemoryRuntime } from './lib/agent-memory/runtime.js';
|
|
|
118
118
|
import { createAgentMemoryActions } from './lib/agent-memory/actions.js';
|
|
119
119
|
import { createMemoryProjectResolver } from './lib/agent-memory/project-resolution.js';
|
|
120
120
|
import { isAgentMemoryFeatureAvailable } from './lib/agent-memory/feature-flag.js';
|
|
121
|
+
import { createSpacesHost } from './lib/spaces/host.js';
|
|
122
|
+
import { createSwitchController, registerSpaceRoutes } from './lib/spaces/routes.js';
|
|
121
123
|
import { resolvePrimaryWorktreeRoot } from './lib/git/service.js';
|
|
122
124
|
import { createRemoteClientAuthRuntime } from './lib/client-auth/remote-clients.js';
|
|
123
125
|
import { createClientPairingRuntime } from './lib/client-auth/pairing.js';
|
|
@@ -135,6 +137,7 @@ import { createOpenChamberSessionService } from './lib/openchamber-sessions/rout
|
|
|
135
137
|
import { createSessionMetadataStore, createOpenCodeSessionMetadata } from './lib/openchamber-sessions/session-metadata-store.js';
|
|
136
138
|
import { createScheduledTaskService } from './lib/scheduled-tasks/service.js';
|
|
137
139
|
import { createOpenChamberControlService } from './lib/openchamber-control/service.js';
|
|
140
|
+
import { createPluginNotificationEmitter } from './lib/notifications/emit-route.js';
|
|
138
141
|
import { OpenChamberControlError } from './lib/openchamber-control/error.js';
|
|
139
142
|
import { createFileOpenRequester } from './lib/openchamber-control/file-open.js';
|
|
140
143
|
import { applyConnectAttemptTimeout } from './lib/network-defaults.js';
|
|
@@ -392,6 +395,8 @@ const projectDirectoryRuntime = createProjectDirectoryRuntime({
|
|
|
392
395
|
normalizeDirectoryPath,
|
|
393
396
|
getReadSettingsFromDiskMigrated: () => readSettingsFromDiskMigrated,
|
|
394
397
|
sanitizeProjects,
|
|
398
|
+
// A space's directory never runs on the host, whatever route carries it.
|
|
399
|
+
refuseDirectory: (candidate) => spacesHost?.refuseDirectory(candidate) ?? null,
|
|
395
400
|
});
|
|
396
401
|
|
|
397
402
|
const resolveDirectoryCandidate = (...args) => projectDirectoryRuntime.resolveDirectoryCandidate(...args);
|
|
@@ -635,6 +640,9 @@ let openCodeNotReadySince = 0;
|
|
|
635
640
|
let isExternalOpenCode = false;
|
|
636
641
|
let exitOnShutdown = true;
|
|
637
642
|
let uiAuthController = null;
|
|
643
|
+
// The isolated-spaces host: the place, the manager and the dispatcher. Null while the feature's
|
|
644
|
+
// switch is off, and then nothing of the feature runs, see docs/isolated-spaces/DESIGN.md.
|
|
645
|
+
let spacesHost = null;
|
|
638
646
|
let activeTunnelController = null;
|
|
639
647
|
let globalWatcherStartPromise = null;
|
|
640
648
|
const tunnelProviderRegistry = createTunnelProviderRegistry([
|
|
@@ -1131,6 +1139,10 @@ const serverUtilsRuntime = createServerUtilsRuntime({
|
|
|
1131
1139
|
// down, while the proxy is registered later still.
|
|
1132
1140
|
getArchivedSessions: () => openChamberSessionService.archiveStore.getAll(),
|
|
1133
1141
|
getStoredSessionMetadata: () => sessionMetadataStore.listUnmigrated(),
|
|
1142
|
+
// Isolated spaces: with the switch on, the session list carries every space's sessions and
|
|
1143
|
+
// the global SSE stream their events. Called, not captured: the host is made in `main`.
|
|
1144
|
+
getMergeSpaceSessionList: () => (spacesHost ? (payload) => spacesHost.mergeSessionList(payload) : null),
|
|
1145
|
+
getSpaceEventHub: () => (spacesHost ? globalMessageStreamHub : null),
|
|
1134
1146
|
fs,
|
|
1135
1147
|
os,
|
|
1136
1148
|
path,
|
|
@@ -1592,6 +1604,12 @@ const fileOpenRequester = createFileOpenRequester({
|
|
|
1592
1604
|
},
|
|
1593
1605
|
});
|
|
1594
1606
|
|
|
1607
|
+
const pluginNotificationEmitter = createPluginNotificationEmitter({
|
|
1608
|
+
readSettingsFromDiskMigrated,
|
|
1609
|
+
emitDesktopNotification,
|
|
1610
|
+
broadcastUiNotification,
|
|
1611
|
+
});
|
|
1612
|
+
|
|
1595
1613
|
const openChamberControlService = createOpenChamberControlService({
|
|
1596
1614
|
readSettingsFromDiskMigrated,
|
|
1597
1615
|
sanitizeProjects,
|
|
@@ -1602,6 +1620,15 @@ const openChamberControlService = createOpenChamberControlService({
|
|
|
1602
1620
|
scheduledTaskService,
|
|
1603
1621
|
browserControl: browserControlRouter,
|
|
1604
1622
|
fileOpen: fileOpenRequester,
|
|
1623
|
+
// The tool is off by default; a plugin generated before it was switched off
|
|
1624
|
+
// must not keep paging the user.
|
|
1625
|
+
notifyUser: async (input) => {
|
|
1626
|
+
const settings = await readSettingsFromDiskMigrated().catch(() => null);
|
|
1627
|
+
if (settings?.agentNotifyToolEnabled !== true) {
|
|
1628
|
+
return { status: 403, body: { error: 'The notify tool is turned off in OpenChamber settings' } };
|
|
1629
|
+
}
|
|
1630
|
+
return pluginNotificationEmitter.emit(input);
|
|
1631
|
+
},
|
|
1605
1632
|
agentMemoryActions: createAgentMemoryActions({
|
|
1606
1633
|
agentMemoryRuntime,
|
|
1607
1634
|
createError: (message, status) => new OpenChamberControlError(message, status),
|
|
@@ -1711,6 +1738,7 @@ const gracefulShutdownRuntime = createGracefulShutdownRuntime({
|
|
|
1711
1738
|
getDictationRuntime: () => dictationRuntime,
|
|
1712
1739
|
getRelayService: () => relayServiceInstance,
|
|
1713
1740
|
getRelayReconcileTimer: () => relayReconcileTimer,
|
|
1741
|
+
getSpacesHost: () => spacesHost,
|
|
1714
1742
|
});
|
|
1715
1743
|
|
|
1716
1744
|
const gracefulShutdown = (...args) => gracefulShutdownRuntime.gracefulShutdown(...args);
|
|
@@ -1907,6 +1935,31 @@ async function main(options = {}) {
|
|
|
1907
1935
|
// but do not hold server listen or managed OpenCode startup on `say -v "?"`.
|
|
1908
1936
|
const sayTTSCapability = detectSayTtsCapability(process);
|
|
1909
1937
|
|
|
1938
|
+
// The isolated-spaces switch, read here at start and changed live through its route below.
|
|
1939
|
+
// While it is off the feature has no place, no manager, no route and runs no `docker`.
|
|
1940
|
+
const buildSpacesHost = () => createSpacesHost({
|
|
1941
|
+
dataDir: OPENCHAMBER_DATA_DIR,
|
|
1942
|
+
dockerPath: searchPathFor('docker', buildAugmentedPath()) ?? 'docker',
|
|
1943
|
+
gitPath: searchPathFor('git', buildAugmentedPath()) ?? 'git',
|
|
1944
|
+
// git starts `docker exec` itself when code moves in or out, so its PATH must find docker.
|
|
1945
|
+
hostEnvironment: { ...process.env, PATH: buildAugmentedPath() },
|
|
1946
|
+
// So the session list can say which registered project each space was made for.
|
|
1947
|
+
listProjectDirectories: async () => {
|
|
1948
|
+
const settings = await readSettingsFromDiskMigrated();
|
|
1949
|
+
return sanitizeProjects(settings?.projects || []).map((project) => project.path);
|
|
1950
|
+
},
|
|
1951
|
+
});
|
|
1952
|
+
const startupSettings = await readSettingsFromDiskMigrated().catch(() => null);
|
|
1953
|
+
if (startupSettings?.isolatedSpacesEnabled === true) {
|
|
1954
|
+
try {
|
|
1955
|
+
spacesHost = buildSpacesHost();
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
// The feature is absent then, and the rest of the server starts as with the switch off.
|
|
1958
|
+
console.error(`[spaces] isolated spaces are unavailable this start: ${error?.code ?? ''} ${error?.message ?? error}`.trim());
|
|
1959
|
+
spacesHost = null;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1910
1963
|
const app = express();
|
|
1911
1964
|
const serverStartedAt = new Date().toISOString();
|
|
1912
1965
|
const packagedClientOrigins = new Set([
|
|
@@ -2058,6 +2111,7 @@ async function main(options = {}) {
|
|
|
2058
2111
|
isUiVisible,
|
|
2059
2112
|
getUiNotificationClients: () => uiNotificationClients,
|
|
2060
2113
|
writeSseEvent,
|
|
2114
|
+
pluginNotificationEmitter,
|
|
2061
2115
|
sessionRuntime,
|
|
2062
2116
|
setPushInitialized,
|
|
2063
2117
|
fs,
|
|
@@ -2073,8 +2127,35 @@ async function main(options = {}) {
|
|
|
2073
2127
|
setAutoAcceptSession,
|
|
2074
2128
|
agentToolRuntime,
|
|
2075
2129
|
desktopUpdater,
|
|
2130
|
+
skipBodyParsing: (req) => spacesHost?.skipsBodyParsing(req) === true,
|
|
2076
2131
|
});
|
|
2077
2132
|
uiAuthController = bootstrapResult.uiAuthController;
|
|
2133
|
+
// After the API auth gate, before every route that reads a directory, before the OpenCode proxy.
|
|
2134
|
+
// The slot is mounted once and reads the host at call time, so the switch can turn the feature
|
|
2135
|
+
// on and off live: with no host it passes every request on and no upgrade is taken.
|
|
2136
|
+
app.use((req, res, next) => (spacesHost ? spacesHost.middleware(req, res, next) : next()));
|
|
2137
|
+
server.on('upgrade', (...args) => { spacesHost?.upgradeHandler(...args); });
|
|
2138
|
+
const startSpacesHost = (host) => {
|
|
2139
|
+
host.prepareUpgrades({ uiAuthController, isRequestOriginAllowed });
|
|
2140
|
+
// Every space's events join the host's hub, and the host asks each space for its live status.
|
|
2141
|
+
void host.startEvents(globalMessageStreamHub).catch((error) => {
|
|
2142
|
+
console.warn(`[spaces] could not follow the spaces: ${error?.message ?? error}`);
|
|
2143
|
+
});
|
|
2144
|
+
};
|
|
2145
|
+
if (spacesHost) startSpacesHost(spacesHost);
|
|
2146
|
+
const spacesSwitch = createSwitchController({
|
|
2147
|
+
getHost: () => spacesHost,
|
|
2148
|
+
setHost: (host) => { spacesHost = host; },
|
|
2149
|
+
buildHost: buildSpacesHost,
|
|
2150
|
+
startHost: startSpacesHost,
|
|
2151
|
+
persist: (enabled) => persistSettings({ isolatedSpacesEnabled: enabled }),
|
|
2152
|
+
});
|
|
2153
|
+
registerSpaceRoutes(app, {
|
|
2154
|
+
getJourney: () => spacesHost?.journey ?? null,
|
|
2155
|
+
getPlaces: () => spacesHost?.places() ?? [],
|
|
2156
|
+
readSwitch: spacesSwitch.readSwitch,
|
|
2157
|
+
setSwitch: spacesSwitch.setSwitch,
|
|
2158
|
+
});
|
|
2078
2159
|
realtimeProxyRuntime = attachRealtimeProxy({
|
|
2079
2160
|
app,
|
|
2080
2161
|
server,
|
|
@@ -2168,6 +2249,7 @@ async function main(options = {}) {
|
|
|
2168
2249
|
guestSurfaceRuntime?.endForGuest(event.guestId);
|
|
2169
2250
|
return browserControlRouter.handleGuestDeactivated(event);
|
|
2170
2251
|
},
|
|
2252
|
+
surfaceViewerHeaders: (guestId, viewerId) => guestSurfaceRuntime?.viewerHeaders(guestId, viewerId) ?? null,
|
|
2171
2253
|
builtInExtensionsDir: options.builtInExtensionsDir,
|
|
2172
2254
|
openchamberUserConfigRoot: OPENCHAMBER_USER_CONFIG_ROOT,
|
|
2173
2255
|
managedChatsRoot: OPENCHAMBER_CHATS_DIR,
|
|
@@ -1,55 +1,121 @@
|
|
|
1
|
-
# Managed OpenChamber
|
|
1
|
+
# Managed OpenChamber Agent Tool
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
This module exposes OpenChamber
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- `openchamber`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
5
|
+
This module exposes OpenChamber to agents as typed OpenCode custom tools. There
|
|
6
|
+
are two, because controlling sessions and driving a page are separate intents
|
|
7
|
+
the user can want independently:
|
|
8
|
+
|
|
9
|
+
- `openchamber` — projects, sessions, worktrees, scheduled tasks, and showing
|
|
10
|
+
a file to the user (`file.open`). Enabled while the persisted
|
|
11
|
+
`agentControlToolEnabled` setting is not `false`.
|
|
12
|
+
- `openchamber_web` — looking at and interacting with the page in OpenChamber's
|
|
13
|
+
browser panel. Enabled while `agentWebToolEnabled` is not `false`.
|
|
14
|
+
- `openchamber_notify` — `notify.send`, a notification to the user through
|
|
15
|
+
`lib/notifications/emit-route.js` (same limits and rate window as
|
|
16
|
+
`POST /api/notifications/emit`). Off by default: enabled only while
|
|
17
|
+
`agentNotifyToolEnabled` is `true`, and the control service refuses the
|
|
18
|
+
action when the setting is off, so a stale plugin cannot keep paging.
|
|
19
|
+
|
|
20
|
+
Both default to on, are toggled in Settings → General → OpenCode CLI, and take
|
|
21
|
+
effect in the running OpenCode within a couple of seconds — OpenChamber rewrites
|
|
22
|
+
the managed config file OpenCode watches (see
|
|
23
|
+
`lib/opencode/managed-config-file.js`). Installs where the user's own
|
|
24
|
+
environment sets `OPENCODE_CONFIG` fall back to `OPENCODE_CONFIG_CONTENT` and
|
|
25
|
+
still need a restart for a toggle. Each tool carries only its own actions and
|
|
26
|
+
only the parameters those actions use, so turning one off removes its inputs
|
|
27
|
+
from the schema rather than leaving them visible. The plugin is injected only
|
|
28
|
+
when OpenChamber launches and owns the OpenCode process, and not at all when
|
|
29
|
+
both settings are `false`.
|
|
30
|
+
|
|
31
|
+
- The plugin accepts the action's inputs either inside `parameters` or beside
|
|
32
|
+
`action`, because models produce both shapes; an explicit `parameters` object
|
|
33
|
+
wins on a conflict. Rejecting the flattened shape turned a call that plainly
|
|
34
|
+
carried a `url` into "url is required", which reads as a broken tool rather
|
|
35
|
+
than a malformed call.
|
|
36
|
+
|
|
37
|
+
## Runtime flow
|
|
38
|
+
|
|
39
|
+
1. The OpenChamber HTTP listener binds and publishes its authoritative port.
|
|
40
|
+
2. `materializePlugin()` writes the plugin under
|
|
41
|
+
`<openchamber-data-dir>/agent-tool/` and returns its directory; the managed
|
|
42
|
+
config layer lists that directory in `<data-dir>/opencode.managed.json` and
|
|
43
|
+
rewrites the file whenever a tool setting changes.
|
|
44
|
+
3. `createChildEnv()` adds a random per-child token and the callback URL to the
|
|
45
|
+
managed OpenCode child environment. They are present even while every tool
|
|
46
|
+
is off, so a tool switched on later can call back without a restart. The URL
|
|
47
|
+
points at loopback, except when the listener is bound to one concrete
|
|
48
|
+
address (`--host <ip>`): that socket does not answer on loopback, so the URL
|
|
49
|
+
uses the bound address instead.
|
|
50
|
+
4. The plugin calls `POST /api/openchamber/agent-tool` with its typed input and
|
|
51
|
+
the session id OpenCode gives the tool; OpenChamber resolves the session's
|
|
52
|
+
directory on its own side.
|
|
53
|
+
5. The route delegates the fixed action allowlist directly to the shared
|
|
54
|
+
OpenChamber control service. The CLI uses the same service through its
|
|
55
|
+
authenticated HTTP adapter, so Goal Mode ordering, wait behavior,
|
|
56
|
+
partial-failure reporting, and scheduled-task contracts have one owner.
|
|
57
|
+
6. Each action definition owns a short presentation title and a separate
|
|
58
|
+
agent-facing description. The generated schema uses the description to state
|
|
59
|
+
required inputs or one non-obvious behavior, while completed calls use the
|
|
60
|
+
short title in native tool metadata.
|
|
61
|
+
|
|
62
|
+
## Agent context budget
|
|
63
|
+
|
|
64
|
+
- The tool exposes one shared parameter object rather than repeating parameters
|
|
65
|
+
in a large per-action union. Action descriptions carry only required inputs,
|
|
66
|
+
defaults, or one non-obvious semantic detail.
|
|
67
|
+
- The action schema carries `oneOf` and no `enum`. A node combining `enum` and
|
|
68
|
+
`oneOf` is valid JSON Schema, but some OpenAI-compatible gateways reject it
|
|
69
|
+
and answer with an empty completion instead of an error.
|
|
70
|
+
- Obvious fields rely on their names and JSON types. Parameter descriptions are
|
|
71
|
+
reserved for formats, dependencies, scope, and behavior that cannot be safely
|
|
72
|
+
inferred from the field name.
|
|
73
|
+
- Session dispatches do not wait by default. Agents are told to set `wait` only
|
|
74
|
+
when the user asks or the next step requires the completed result.
|
|
75
|
+
- The tool exposes only agent-relevant actions
|
|
76
|
+
(`OPENCHAMBER_AGENT_TOOL_ACTIONS`): `schedule.status` stays CLI-only because
|
|
77
|
+
`schedule.list` already returns scheduler status, and enable/disable are one
|
|
78
|
+
`schedule.toggle` action driven by the `disabled` boolean.
|
|
79
|
+
- The tool description frames intent: created sessions and scheduled tasks are
|
|
80
|
+
user-facing work the user follows up with. The agent must not decide on its
|
|
81
|
+
own to delegate parts of its current task, but an explicit user request to
|
|
82
|
+
create, send, or schedule always wins, even when it relates to the current
|
|
83
|
+
task (strict models otherwise read the old unconditional "never delegate" as
|
|
84
|
+
a hard ban and refused user-requested sends).
|
|
85
|
+
- Optional behavior switches (`worktree`, `goal`, `agent`, `variant`, `wait`)
|
|
86
|
+
state their default and an explicit "only when the user asks" rule so agents
|
|
87
|
+
do not invent worktrees, goal mode, or waits the user never requested.
|
|
88
|
+
- Detailed combination rules are enforced by the shared control service and
|
|
89
|
+
returned as actionable usage errors only after an invalid call. Per-action
|
|
90
|
+
examples and a repeated per-action parameter schema are intentionally omitted.
|
|
39
91
|
|
|
40
92
|
## Security invariants
|
|
41
93
|
|
|
42
|
-
- The callback accepts same-machine requests only and requires the current
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
94
|
+
- The callback accepts same-machine requests only and requires the current
|
|
95
|
+
per-child bearer token using a timing-safe comparison. Same-machine means a
|
|
96
|
+
loopback source, or, for a listener bound to one concrete address, a source
|
|
97
|
+
equal to that address: the OS sources a local connection to `<ip>` from
|
|
98
|
+
`<ip>`. A wildcard bind keeps the loopback-only rule, and another machine on
|
|
99
|
+
the network always arrives with its own address.
|
|
100
|
+
- The token is never persisted, logged, returned to the UI, or written into
|
|
101
|
+
the materialized plugin.
|
|
102
|
+
- The plugin adds the callback host to `NO_PROXY`/`no_proxy` inside the managed
|
|
103
|
+
child when it loads. Without that, an `HTTP_PROXY` in the child's environment
|
|
104
|
+
would receive a non-loopback callback, token included, because `fetch` has no
|
|
105
|
+
per-request way to skip the environment proxy.
|
|
106
|
+
- Inputs map to a fixed action and parameter allowlist. There is no arbitrary
|
|
107
|
+
CLI, shell, route, or URL forwarding.
|
|
108
|
+
- Session/worktree deletion and project-path registration are not exposed.
|
|
109
|
+
- A cancelled turn aborts the session's in-flight actions. OpenCode 2 gives a
|
|
110
|
+
plugin tool no abort signal and the plugin's request stays open, so the
|
|
111
|
+
server tracks in-flight actions per session and `abortSession(sessionID)` is
|
|
112
|
+
called from the event dispatch in `server/index.js` when the stream reports
|
|
113
|
+
`session.idle` with `aborted: true`; the abort signal then reaches the
|
|
114
|
+
shared service as before. A dropped HTTP request still aborts as well.
|
|
49
115
|
|
|
50
116
|
## Result contract
|
|
51
117
|
|
|
52
|
-
|
|
118
|
+
Every completed call returns JSON:
|
|
53
119
|
|
|
54
120
|
```json
|
|
55
121
|
{
|
|
@@ -60,10 +126,29 @@ Completed calls return JSON:
|
|
|
60
126
|
}
|
|
61
127
|
```
|
|
62
128
|
|
|
63
|
-
Command and operational failures use the same envelope with `ok: false` and
|
|
129
|
+
Command and operational failures use the same envelope with `ok: false` and
|
|
130
|
+
an `error` object. OpenCode-level cancellation can still produce a native tool
|
|
131
|
+
error state.
|
|
132
|
+
|
|
133
|
+
## Runtime parity
|
|
134
|
+
|
|
135
|
+
- Web and Desktop managed OpenCode: injected automatically.
|
|
136
|
+
- External OpenCode selected with `OPENCODE_HOST` or skip-start: not injected,
|
|
137
|
+
because OpenChamber does not control that process environment.
|
|
138
|
+
- VS Code: not injected; the extension owns a separate OpenCode lifecycle.
|
|
139
|
+
- Hosted and Capacitor mobile clients use the server's managed OpenCode tool
|
|
140
|
+
when connected to such a server; no tool runs in the client runtime.
|
|
64
141
|
|
|
65
|
-
##
|
|
142
|
+
## The calling tool is part of the request
|
|
66
143
|
|
|
67
|
-
Each generated tool sends its own name with every callback.
|
|
144
|
+
Each generated tool sends its own name with every callback. Models routinely
|
|
145
|
+
drop the namespace their tool's name appears to supply — `openchamber_memory`
|
|
146
|
+
asked for `memory.read` gets called as `read` — and resolving the bare name
|
|
147
|
+
inside the calling tool's action set makes that unambiguous even where it is not
|
|
148
|
+
globally (`delete` belongs to both schedule and memory).
|
|
68
149
|
|
|
69
|
-
|
|
150
|
+
Resolution never reaches outside the tool that asked: `open` from the memory
|
|
151
|
+
tool fails rather than driving the browser. An unresolvable action answers with
|
|
152
|
+
the actions that tool actually has, because an error that only says
|
|
153
|
+
"unsupported" leaves the model to guess a second wrong name — which is exactly
|
|
154
|
+
what happened before this existed.
|
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
OPENCHAMBER_AGENT_TOOL_ACTIONS,
|
|
4
4
|
OPENCHAMBER_MEMORY_ACTION_DEFINITIONS,
|
|
5
5
|
OPENCHAMBER_MEMORY_ACTIONS,
|
|
6
|
+
OPENCHAMBER_NOTIFY_ACTION_DEFINITIONS,
|
|
7
|
+
OPENCHAMBER_NOTIFY_ACTIONS,
|
|
6
8
|
resolveAgentToolAction,
|
|
7
9
|
OPENCHAMBER_WEB_ACTION_DEFINITIONS,
|
|
8
10
|
OPENCHAMBER_WEB_ACTIONS,
|
|
@@ -12,12 +14,18 @@ const TOOL_SCHEMA_VERSION = 1;
|
|
|
12
14
|
const PLUGIN_ID = 'openchamber-agent-tool';
|
|
13
15
|
// Everything either managed tool may ask for; the agent allowlist stays
|
|
14
16
|
// narrower than the full control surface.
|
|
15
|
-
const ACTIONS = new Set([
|
|
17
|
+
const ACTIONS = new Set([
|
|
18
|
+
...OPENCHAMBER_AGENT_TOOL_ACTIONS,
|
|
19
|
+
...OPENCHAMBER_WEB_ACTIONS,
|
|
20
|
+
...OPENCHAMBER_MEMORY_ACTIONS,
|
|
21
|
+
...OPENCHAMBER_NOTIFY_ACTIONS,
|
|
22
|
+
]);
|
|
16
23
|
const AGENT_TOOL_ACTION_TITLES = Object.fromEntries(
|
|
17
24
|
[
|
|
18
25
|
...OPENCHAMBER_AGENT_TOOL_ACTION_DEFINITIONS,
|
|
19
26
|
...OPENCHAMBER_WEB_ACTION_DEFINITIONS,
|
|
20
27
|
...OPENCHAMBER_MEMORY_ACTION_DEFINITIONS,
|
|
28
|
+
...OPENCHAMBER_NOTIFY_ACTION_DEFINITIONS,
|
|
21
29
|
].map(({ action, title }) => [action, title]),
|
|
22
30
|
);
|
|
23
31
|
|
|
@@ -28,7 +36,7 @@ const AGENT_TOOL_ACTION_TITLES = Object.fromEntries(
|
|
|
28
36
|
* in the other tool's schema, which is both misleading and paid for in context
|
|
29
37
|
* on every call.
|
|
30
38
|
*/
|
|
31
|
-
const WEB_PARAMETER_NAMES = ['url', 'selector', 'text', 'value', 'submit', 'direction', 'viewport', 'label'];
|
|
39
|
+
const WEB_PARAMETER_NAMES = ['url', 'selector', 'text', 'value', 'submit', 'direction', 'viewport', 'label', 'tabId'];
|
|
32
40
|
// `title` is shared with the control tool, so it is not listed here — only the
|
|
33
41
|
// names memory alone introduces are kept out of the other schemas.
|
|
34
42
|
const MEMORY_ONLY_PARAMETER_NAMES = ['body', 'scope', 'memoryId', 'type'];
|
|
@@ -87,6 +95,7 @@ const ALL_PARAMETER_PROPERTIES = {
|
|
|
87
95
|
direction: { type: 'string', enum: ['up', 'down', 'top', 'bottom'], description: 'Scroll direction for browser.scroll' },
|
|
88
96
|
viewport: { type: 'string', enum: ['mobile', 'tablet', 'desktop', 'fill'], description: 'Page layout size; snapshots report which one is in effect' },
|
|
89
97
|
label: { type: 'string', description: 'Short name for a browser.capture image, such as before-fix' },
|
|
98
|
+
tabId: { type: 'string', description: 'Browser tab to act on, an id from the tabs a browser.snapshot lists. Omit to use the tab the user is looking at' },
|
|
90
99
|
body: { type: 'string', description: 'Full text of the memory; state it so it still makes sense in a session that has none of this conversation' },
|
|
91
100
|
scope: { type: 'string', enum: ['global', 'project', 'both'], description: 'global is about the user and applies everywhere; project is about this codebase. both is only valid for memory.list' },
|
|
92
101
|
memoryId: { type: 'string', description: 'Memory ID from a memory.list or memory.read result' },
|
|
@@ -108,12 +117,22 @@ const MEMORY_PARAMETER_PROPERTIES = {
|
|
|
108
117
|
...MEMORY_PARAMETER_OVERRIDES,
|
|
109
118
|
};
|
|
110
119
|
|
|
120
|
+
// Its own names, not the shared map: `title` and `body` mean something else in
|
|
121
|
+
// the control and memory tools.
|
|
122
|
+
const NOTIFY_PARAMETER_PROPERTIES = {
|
|
123
|
+
title: { type: 'string', description: 'Short headline the user reads first, up to 120 characters' },
|
|
124
|
+
body: { type: 'string', description: 'One or two sentences of detail, up to 500 characters' },
|
|
125
|
+
showWhenFocused: { type: 'boolean', description: 'Show it even while the user is looking at OpenChamber. Only for something that cannot wait' },
|
|
126
|
+
};
|
|
127
|
+
|
|
111
128
|
const CONTROL_TOOL_DESCRIPTION = "Control OpenChamber projects, sessions, and scheduled tasks on the user's behalf. Sessions and scheduled tasks you create are for the user to follow and interact with. Do not decide on your own to hand parts of your current task to another session; when the user asks you to create a session, send a prompt to one, or schedule a task, do it, including when the work relates to your current task. Use one action per call. Scope with projectId or directory; omit both to use the current session directory. Session dispatches return immediately by default and you receive no notification when a dispatched session finishes, so never promise to report back on it; the user follows it in OpenChamber; a dispatched session needs no follow-up from you. If the user later asks how it went, use session.messages (add wait to block until it is idle, lastAssistant for just the final answer) — session.send always sends a NEW prompt and never just waits. Set wait only when the user asks or the next step requires the completed result. Session and worktree deletion are unavailable.";
|
|
112
129
|
|
|
113
130
|
const WEB_TOOL_DESCRIPTION = "Look at and interact with a web page in OpenChamber's browser panel, so you can check your own work rather than describing what you expect. Use one action per call. Open a page, snapshot it to read its text and its interactive elements, then click, type or scroll using the selectors the snapshot returned; snapshots also report any errors the page logged. Pass a selector to browser.snapshot to read one part of a long page. browser.inspect returns computed styles when the question is how something renders. Set viewport to check a layout at mobile, tablet or desktop size. The page runs with the user's real logins, so treat what you see as their live session.";
|
|
114
131
|
|
|
115
132
|
const MEMORY_TOOL_DESCRIPTION = "Keep what you learn across sessions, so the user does not have to explain the same thing twice. Use one action per call. The session already lists the titles of what is stored. A title is an abbreviation, not the memory: read the entry with memory.read once before acting on it (it then stays in your context; do not re-read it on later turns), because titles leave out the conditions and exceptions that decide how the memory applies, and the ones that look self-explanatory hide them most often. Save something only when it will still be true in a later session — a stable preference, a project convention, a decision and its reason, or a hard-won pointer. Do not save one-off task state, anything you can read from the code, secrets or credentials, or anything the user asked you not to keep; when the user explicitly asks you to remember something, save it, unless it is a secret or credential. Choose the scope deliberately: global is about the user and reaches every project, so put a project's conventions in project scope. Save in the moment, without asking first, when the user corrects how you work or states a preference, confirms that a non-obvious approach worked, or when you learn a project fact that took real effort to find. One fact per entry. The user can review and remove what you save, so save when it fits and mention it briefly.";
|
|
116
133
|
|
|
134
|
+
const NOTIFY_TOOL_DESCRIPTION = "Send the user a notification through OpenChamber, so they learn about something without watching the session. Use it when you finish work that took long enough for the user to step away, when you are blocked on something only the user can resolve, or when the user asked to be told about something. Do not use it for routine progress, for every finished step, or to repeat what your reply already says to a user who is present. Keep the title short and put detail in the body.";
|
|
135
|
+
|
|
117
136
|
const asNonEmptyString = (value) => {
|
|
118
137
|
if (typeof value !== 'string') return null;
|
|
119
138
|
const trimmed = value.trim();
|
|
@@ -245,12 +264,7 @@ const createToolEntry = ({ name, description, definitions, parameters }) => Stri
|
|
|
245
264
|
})
|
|
246
265
|
`;
|
|
247
266
|
|
|
248
|
-
|
|
249
|
-
export const createPluginSource = ({
|
|
250
|
-
includeControl,
|
|
251
|
-
includeWeb,
|
|
252
|
-
includeMemory,
|
|
253
|
-
}) => {
|
|
267
|
+
export const createPluginSource = ({ includeControl, includeWeb, includeMemory, includeNotify }) => {
|
|
254
268
|
const entries = [];
|
|
255
269
|
if (includeControl) {
|
|
256
270
|
entries.push(createToolEntry({
|
|
@@ -276,6 +290,14 @@ export const createPluginSource = ({
|
|
|
276
290
|
parameters: MEMORY_PARAMETER_PROPERTIES,
|
|
277
291
|
}));
|
|
278
292
|
}
|
|
293
|
+
if (includeNotify) {
|
|
294
|
+
entries.push(createToolEntry({
|
|
295
|
+
name: 'openchamber_notify',
|
|
296
|
+
description: NOTIFY_TOOL_DESCRIPTION,
|
|
297
|
+
definitions: OPENCHAMBER_NOTIFY_ACTION_DEFINITIONS,
|
|
298
|
+
parameters: NOTIFY_PARAMETER_PROPERTIES,
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
279
301
|
|
|
280
302
|
// The callback carries a bearer token over plain HTTP. With a proxy in the
|
|
281
303
|
// OpenCode environment, fetch could hand a non-loopback callback and token to
|
|
@@ -337,17 +359,13 @@ export const createAgentToolRuntime = (dependencies) => {
|
|
|
337
359
|
* change while it runs, so the source on disk always matches the settings —
|
|
338
360
|
* the running OpenCode reloads the directory it already has configured.
|
|
339
361
|
*/
|
|
340
|
-
const materializePlugin = async ({ includeControl = true, includeWeb = true, includeMemory = true } = {}) => {
|
|
341
|
-
if (!includeControl && !includeWeb && !includeMemory) {
|
|
362
|
+
const materializePlugin = async ({ includeControl = true, includeWeb = true, includeMemory = true, includeNotify = false } = {}) => {
|
|
363
|
+
if (!includeControl && !includeWeb && !includeMemory && !includeNotify) {
|
|
342
364
|
throw new Error('At least one OpenChamber managed tool must be enabled to inject the plugin');
|
|
343
365
|
}
|
|
344
366
|
await fsPromises.mkdir(pluginDirectory, { recursive: true });
|
|
345
367
|
await fsPromises.writeFile(pluginManifestPath, AGENT_TOOL_PLUGIN_PACKAGE_JSON, { mode: 0o600 });
|
|
346
|
-
await fsPromises.writeFile(pluginPath, createPluginSource({
|
|
347
|
-
includeControl,
|
|
348
|
-
includeWeb,
|
|
349
|
-
includeMemory,
|
|
350
|
-
}), { mode: 0o600 });
|
|
368
|
+
await fsPromises.writeFile(pluginPath, createPluginSource({ includeControl, includeWeb, includeMemory, includeNotify }), { mode: 0o600 });
|
|
351
369
|
return pluginDirectory;
|
|
352
370
|
};
|
|
353
371
|
|
|
@@ -503,6 +521,7 @@ export const createAgentToolRuntime = (dependencies) => {
|
|
|
503
521
|
pluginDirectory,
|
|
504
522
|
materializePlugin,
|
|
505
523
|
createChildEnv,
|
|
524
|
+
authorizeRequest: authorizeManagedChild,
|
|
506
525
|
registerRoutes,
|
|
507
526
|
execute,
|
|
508
527
|
abortSession,
|
|
@@ -249,6 +249,21 @@ describe('managed agent tool runtime', () => {
|
|
|
249
249
|
expect(Object.keys(tool)).toEqual(['openchamber_memory']);
|
|
250
250
|
});
|
|
251
251
|
|
|
252
|
+
it('exposes notify as its own tool only when switched on', async () => {
|
|
253
|
+
const { runtime, dataDir } = await createRuntime();
|
|
254
|
+
await prepareManagedEnv(runtime, { includeControl: true, includeWeb: false, includeMemory: false, includeNotify: true });
|
|
255
|
+
const tool = await loadTools(dataDir, 'notify');
|
|
256
|
+
|
|
257
|
+
expect(Object.keys(tool)).toEqual(['openchamber', 'openchamber_notify']);
|
|
258
|
+
expect(Object.keys(tool.openchamber_notify.input.properties.parameters.properties).sort())
|
|
259
|
+
.toEqual(['body', 'showWhenFocused', 'title']);
|
|
260
|
+
expect(Object.keys(tool.openchamber.input.properties.parameters.properties)).not.toContain('showWhenFocused');
|
|
261
|
+
|
|
262
|
+
const { runtime: plain, dataDir: plainDir } = await createRuntime();
|
|
263
|
+
await prepareManagedEnv(plain, { includeControl: true, includeWeb: false, includeMemory: false });
|
|
264
|
+
expect(Object.keys(await loadTools(plainDir, 'nonotify'))).toEqual(['openchamber']);
|
|
265
|
+
});
|
|
266
|
+
|
|
252
267
|
it('refuses to inject a plugin with no tools in it', async () => {
|
|
253
268
|
const { runtime } = await createRuntime();
|
|
254
269
|
let failed = false;
|
|
@@ -46,8 +46,20 @@ itself; it can only ask and wait.
|
|
|
46
46
|
`browser.*` actions of the `openchamber_web` tool onto the router's
|
|
47
47
|
`request()` (same signature as the broker) and owns their parameter
|
|
48
48
|
validation.
|
|
49
|
-
- The client half is `packages/ui/src/lib/browser/controlClient.ts
|
|
50
|
-
|
|
49
|
+
- The client half is `packages/ui/src/lib/browser/controlClient.ts`. Every
|
|
50
|
+
mounted browser tab registers its pane under its context-panel tab id. An
|
|
51
|
+
action with `tabId` runs in that tab; without one it runs in the browser tab
|
|
52
|
+
the user last had in front of them (`setShownBrowserTab`, set by
|
|
53
|
+
`ContextPanel`), never in whichever pane registered last, and never switches
|
|
54
|
+
the user to the tab it acts in. `browser.open` without `tabId` never
|
|
55
|
+
navigates an existing tab: the registered opener (`ContextPanel`,
|
|
56
|
+
`useUIStore.openAgentBrowserTab`) makes a new background tab and the answer
|
|
57
|
+
carries its `tabId`. `browser.snapshot` answers carry `tabs`
|
|
58
|
+
(`id`, `title`, `url`, `active`). A client without the named tab waits
|
|
59
|
+
briefly, so the client that has it claims first, then claims and answers
|
|
60
|
+
"no such tab". `tabId` is validated and passed through by
|
|
61
|
+
`../openchamber-control/service.js` for every action, so an extension
|
|
62
|
+
provider receives it untouched (`BrowserTabTarget` in `@openchamber/sdk`).
|
|
51
63
|
|
|
52
64
|
## Invariants
|
|
53
65
|
|