@vantran-se/openchamber-web 1.23.0 → 1.24.3
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/bin/cli.test.js +29 -6
- package/bin/lib/cli-http.js +5 -1
- package/bin/lib/commands-status.js +5 -0
- package/dist/assets/{CodeMirrorCommentWidgets-DFg2wnpo.js → CodeMirrorCommentWidgets-BjJHrzmU.js} +1 -1
- package/dist/assets/DiffView-COVQhWGd.js +4 -0
- package/dist/assets/FilesView-B1V5JxFX.js +13 -0
- package/dist/assets/GitView-CDOJy5Ay.js +6 -0
- package/dist/assets/GuestAttachDialog-D3S0Rjic.js +1 -0
- package/dist/assets/{JsonTreeView-BKdsjg5B.js → JsonTreeView-D0C9nU7K.js} +1 -1
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/LinearIssuesView-D7FKg1XJ.js +1 -0
- package/dist/assets/{MarkdownImageGallery-_5gEVr0X.js → MarkdownImageGallery-BrFjAVEf.js} +1 -1
- package/dist/assets/MarkdownRendererImpl-DWepTafh.js +12 -0
- package/dist/assets/{OnboardingScreen-DdP4_nuc.js → OnboardingScreen-Dyxxiqdt.js} +1 -1
- package/dist/assets/{PatchDiff-am3SAss2.js → PatchDiff-DnEhjXcC.js} +1 -1
- package/dist/assets/PierreDiffViewer-DyxZVVVR.js +210 -0
- package/dist/assets/{PlanView-BoJxdRZe.js → PlanView-LW2z_EAP.js} +4 -4
- package/dist/assets/PluginPane-B4tKno2h.js +3 -0
- package/dist/assets/SettingsView-DEaMulN9.js +29 -0
- package/dist/assets/{SettingsWindow-DVTdDSol.js → SettingsWindow-FLDJXOgQ.js} +1 -1
- package/dist/assets/SubmoduleDiffSummary-CvVwD-Vi.js +1 -0
- package/dist/assets/ToolOutputDialog-BiP0x2Rw.js +15 -0
- package/dist/assets/{ToolPartDiffPreview-C7WLk5i9.js → ToolPartDiffPreview-CkR4msKP.js} +2 -2
- package/dist/assets/{VirtualizedFileDiff-DcdgkH-x.js → VirtualizedFileDiff-0pyr_mHY.js} +4 -4
- package/dist/assets/WalkthroughView-yNj4nOP5.js +4 -0
- package/dist/assets/appThemeRegistry-BuUKhbYQ.js +1 -0
- package/dist/assets/commitAndPush-DvJKa3Ks.js +1 -0
- package/dist/assets/{computeVirtualFileMetrics-C8byMnRh.js → computeVirtualFileMetrics-DxTBMuu-.js} +2 -2
- package/dist/assets/de-DgxgFyGG.js +10 -0
- package/dist/assets/es-BFsZj5F_.js +16 -0
- package/dist/assets/fr-jdZ5KKQi.js +14 -0
- package/dist/assets/{index-BB7ryz8y.js → index-CKHjPKbq.js} +1 -1
- package/dist/assets/index-Cbci4i1W.js +40 -0
- package/dist/assets/index-DqaMWNrC.css +1 -0
- package/dist/assets/{isDiffPlainText-45ujMPGb.js → isDiffPlainText-D7FkBMP6.js} +1 -1
- package/dist/assets/{isFilePlainText-Cc4KFSYi.js → isFilePlainText-CL2vsrIZ.js} +1 -1
- package/dist/assets/ja-C5kKrhO4.js +16 -0
- package/dist/assets/ko-DXDTT_0O.js +16 -0
- package/dist/assets/main-CZALtVek.js +896 -0
- package/dist/assets/main-CgXMBNvG.js +2 -0
- package/dist/assets/{markdown-shiki.worker-D5fBU-lw.js → markdown-shiki.worker-CahLvHIq.js} +5 -5
- package/dist/assets/markdownCore-Dh7WEsdM.js +5 -0
- package/dist/assets/miniChat-CwTmDixi.js +2 -0
- package/dist/assets/mobile-CLf2vCtO.js +2 -0
- package/dist/assets/patchFileDiff-Bsba8KFp.js +13 -0
- package/dist/assets/pl-D-7x4Y8Q.js +16 -0
- package/dist/assets/pt-BR-CSBZrHa8.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-C5gAFBk0.js +2 -0
- package/dist/assets/renderMobileApp-V4hYd32H.js +25 -0
- package/dist/assets/runtimeEndpointReset-DhlHCn-2.js +25 -0
- package/dist/assets/scheduledTasksApi-DzjbLd8P.js +1 -0
- package/dist/assets/shikiHighlight-8_3csmr6.js +1 -0
- package/dist/assets/surface-B6n7wJtt.js +1 -0
- package/dist/assets/textMateThemeFromAppTheme-CHo2fCgW.js +1 -0
- package/dist/assets/tr-Dr-oJ22L.js +7 -0
- package/dist/assets/uk-9Nx83UcI.js +16 -0
- package/dist/assets/useAppFontEffects-khZXpFL_.js +7333 -0
- package/dist/assets/useInlineCommentController-DkQ_gqpn.js +2 -0
- package/dist/assets/useRootScrollLock-DdvHf9Pt.js +2 -0
- package/dist/assets/{vendor-base-ui-DdV8vhHn.js → vendor-base-ui-CsXYnR5c.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-data-DzlSIu_W.js → vendor-codemirror-language-data-CdzUmUyZ.js} +2 -2
- package/dist/assets/vendor-framer-motion-Bq3iTZ6r.js +1 -0
- package/dist/assets/vendor-katex-Cp579UoJ.css +1 -0
- package/dist/assets/{vendor-motion-dom-D_o3BTI3.js → vendor-motion-dom-DcCgeG69.js} +1 -1
- package/dist/assets/{vendor-remixicon-react-Dil0zKKT.js → vendor-remixicon-react-BDmGmFCA.js} +1 -1
- package/dist/assets/vendor-strip-json-comments-5dJlOkP1.js +4 -0
- package/dist/assets/{vendor-tanstack-react-virtual-g05Cx3z9.js → vendor-tanstack-react-virtual-ClYN8CzB.js} +1 -1
- package/dist/assets/{vendor-tanstack-virtual-core-Dubg10g5.js → vendor-tanstack-virtual-core-BewVQryB.js} +1 -1
- package/dist/assets/vendor-zod-CgAdXWZO.js +39 -0
- package/dist/assets/{web-components-CdGfR2XJ.js → web-components-B8IcVQAA.js} +2 -2
- package/dist/assets/zh-CN-Bxy_Hj3r.js +16 -0
- package/dist/assets/zh-TW-0BB4gpHM.js +16 -0
- package/dist/index.html +6 -4
- package/dist/mini-chat.html +6 -4
- package/dist/mobile.html +6 -4
- package/package.json +12 -9
- package/server/built-in-extensions/registry.json +4 -0
- package/server/index.d.ts +2 -0
- package/server/index.js +47 -23
- package/server/lib/agent-memory/runtime.js +7 -2
- package/server/lib/agent-memory/runtime.test.js +22 -0
- package/server/lib/agent-tool/DOCUMENTATION.md +17 -4
- package/server/lib/agent-tool/runtime.js +62 -15
- package/server/lib/agent-tool/runtime.test.js +88 -2
- package/server/lib/changelog/update-notes.js +2 -1
- package/server/lib/changelog/update-notes.test.js +19 -0
- package/server/lib/event-stream/DOCUMENTATION.md +26 -0
- package/server/lib/event-stream/delta-coalescer.js +0 -0
- package/server/lib/event-stream/delta-coalescer.test.js +292 -0
- package/server/lib/event-stream/global-hub.js +68 -13
- package/server/lib/event-stream/global-hub.test.js +182 -0
- package/server/lib/event-stream/global-ws-bridge.js +16 -18
- package/server/lib/event-stream/index.js +4 -0
- package/server/lib/event-stream/protocol.js +21 -7
- package/server/lib/event-stream/runtime.js +9 -6
- package/server/lib/event-stream/runtime.test.js +60 -0
- package/server/lib/fs/DOCUMENTATION.md +10 -1
- package/server/lib/fs/git-process.test.js +77 -0
- package/server/lib/fs/routes.js +58 -5
- package/server/lib/fs/routes.test.js +116 -0
- package/server/lib/fs/search.js +2 -1
- package/server/lib/git/DOCUMENTATION.md +30 -6
- package/server/lib/git/routes.js +37 -11
- package/server/lib/git/routes.test.js +78 -0
- package/server/lib/git/serial-refresh.js +93 -0
- package/server/lib/git/serial-refresh.test.js +112 -0
- package/server/lib/git/service.js +588 -74
- package/server/lib/git/service.test.js +492 -0
- package/server/lib/github/DOCUMENTATION.md +5 -0
- package/server/lib/github/pr-status.js +5 -5
- package/server/lib/github/pr-status.test.js +1 -1
- package/server/lib/github/routes.js +6 -6
- package/server/lib/guests/DOCUMENTATION.md +68 -0
- package/server/lib/guests/auth-store.js +184 -0
- package/server/lib/guests/auth-store.test.js +112 -0
- package/server/lib/guests/background.test.js +83 -0
- package/server/lib/guests/builtins.js +34 -0
- package/server/lib/guests/builtins.test.js +174 -0
- package/server/lib/guests/catalog.js +477 -0
- package/server/lib/guests/catalog.test.js +390 -0
- package/server/lib/guests/clone.js +297 -0
- package/server/lib/guests/extract-zip.js +136 -0
- package/server/lib/guests/extract-zip.test.js +109 -0
- package/server/lib/guests/files.js +308 -0
- package/server/lib/guests/files.test.js +262 -0
- package/server/lib/guests/grant-scope.js +81 -0
- package/server/lib/guests/grant-scope.test.js +75 -0
- package/server/lib/guests/host-session.js +73 -0
- package/server/lib/guests/host-session.test.js +30 -0
- package/server/lib/guests/html-styles.js +6 -0
- package/server/lib/guests/html-styles.test.js +45 -0
- package/server/lib/guests/html-tokens.js +73 -0
- package/server/lib/guests/html-tokens.test.js +53 -0
- package/server/lib/guests/install.js +406 -0
- package/server/lib/guests/install.test.js +338 -0
- package/server/lib/guests/oauth.js +501 -0
- package/server/lib/guests/oauth.test.js +481 -0
- package/server/lib/guests/persist.js +336 -0
- package/server/lib/guests/persist.test.js +288 -0
- package/server/lib/guests/request.js +112 -0
- package/server/lib/guests/request.test.js +206 -0
- package/server/lib/guests/routes.js +838 -0
- package/server/lib/guests/service.js +538 -0
- package/server/lib/guests/service.test.js +277 -0
- package/server/lib/guests/sockets.js +129 -0
- package/server/lib/guests/sockets.test.js +110 -0
- package/server/lib/guests/ssh-install.test.js +188 -0
- package/server/lib/guests/storage.js +68 -0
- package/server/lib/guests/storage.test.js +59 -0
- package/server/lib/guests/updates.js +294 -0
- package/server/lib/guests/updates.test.js +334 -0
- package/server/lib/guests/upload.js +50 -0
- package/server/lib/guests/upload.test.js +131 -0
- package/server/lib/linear/DOCUMENTATION.md +1 -1
- package/server/lib/message-queue/DOCUMENTATION.md +17 -1
- package/server/lib/message-queue/runtime.js +34 -1
- package/server/lib/message-queue/runtime.test.js +83 -6
- package/server/lib/notifications/DOCUMENTATION.md +1 -1
- package/server/lib/notifications/emitter-runtime.js +4 -3
- package/server/lib/notifications/routes.js +5 -0
- package/server/lib/openchamber-control/DOCUMENTATION.md +6 -0
- package/server/lib/openchamber-sessions/routes.js +62 -29
- package/server/lib/openchamber-sessions/routes.test.js +96 -0
- package/server/lib/opencode/DOCUMENTATION.md +73 -5
- package/server/lib/opencode/bootstrap-runtime.js +1 -0
- package/server/lib/opencode/core-routes.js +39 -1
- package/server/lib/opencode/env-runtime.js +5 -1
- package/server/lib/opencode/env-runtime.test.js +72 -0
- package/server/lib/opencode/feature-routes-runtime.js +30 -1
- package/server/lib/opencode/lifecycle-process.test.js +84 -0
- package/server/lib/opencode/lifecycle.js +58 -70
- package/server/lib/opencode/lifecycle.test.js +29 -0
- package/server/lib/opencode/managed-plugin-config.js +1 -1
- package/server/lib/opencode/openchamber-routes.js +72 -14
- package/server/lib/opencode/openchamber-routes.test.js +71 -3
- package/server/lib/opencode/proxy.js +33 -0
- package/server/lib/opencode/session-runtime.js +68 -0
- package/server/lib/opencode/session-runtime.test.js +43 -0
- package/server/lib/opencode/settings-helpers.js +11 -0
- package/server/lib/opencode/settings-helpers.test.js +24 -2
- package/server/lib/opencode/settings-normalization-runtime.js +2 -0
- package/server/lib/opencode/settings-normalization-runtime.test.js +13 -0
- package/server/lib/opencode/settings-registry.json +18 -0
- package/server/lib/opencode/settings-runtime.js +36 -9
- package/server/lib/opencode/settings-runtime.test.js +158 -2
- package/server/lib/opencode/shutdown-runtime.js +3 -0
- package/server/lib/opencode/shutdown-runtime.test.js +24 -0
- package/server/lib/opencode/theme-archive.js +53 -0
- package/server/lib/opencode/theme-catalog.js +171 -0
- package/server/lib/opencode/theme-catalog.test.js +125 -0
- package/server/lib/opencode/theme-import.test.js +144 -0
- package/server/lib/opencode/theme-runtime.js +100 -28
- package/server/lib/opencode/theme-runtime.test.js +15 -0
- package/server/lib/permission-auto-accept/DOCUMENTATION.md +8 -0
- package/server/lib/permission-auto-accept/runtime.js +13 -0
- package/server/lib/permission-auto-accept/runtime.test.js +37 -1
- package/server/lib/project-context/DOCUMENTATION.md +13 -3
- package/server/lib/project-context/runtime.js +8 -2
- package/server/lib/project-context/runtime.test.js +54 -0
- package/server/lib/projects/DOCUMENTATION.md +2 -1
- package/server/lib/projects/project-config.js +40 -11
- package/server/lib/projects/project-config.test.js +97 -0
- package/server/lib/projects/project-id.js +26 -0
- package/server/lib/quota/DOCUMENTATION.md +0 -1
- package/server/lib/quota/providers/index.js +0 -7
- package/server/lib/realtime-proxy-terminal.test.js +150 -0
- package/server/lib/realtime-proxy.js +2 -0
- package/server/lib/relay/DOCUMENTATION.md +20 -0
- package/server/lib/relay/host-client.js +7 -0
- package/server/lib/relay/host-client.test.js +14 -0
- package/server/lib/routing/DOCUMENTATION.md +83 -0
- package/server/lib/routing/defaults.js +98 -0
- package/server/lib/routing/feature-flag.js +11 -0
- package/server/lib/routing/history.js +34 -0
- package/server/lib/routing/jev.js +98 -0
- package/server/lib/routing/routes.http.test.js +100 -0
- package/server/lib/routing/routes.js +81 -0
- package/server/lib/routing/runtime.js +217 -0
- package/server/lib/routing/runtime.test.js +188 -0
- package/server/lib/routing/store.js +249 -0
- package/server/lib/routing/store.test.js +96 -0
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +9 -1
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +28 -0
- package/server/lib/scheduled-tasks/runtime.js +14 -4
- package/server/lib/scheduled-tasks/runtime.test.js +67 -0
- package/server/lib/security/request-security.js +9 -5
- package/server/lib/security/request-security.test.js +37 -0
- package/server/lib/session-assist/DOCUMENTATION.md +131 -66
- package/server/lib/session-assist/context.js +127 -0
- package/server/lib/session-assist/context.test.js +132 -0
- package/server/lib/session-assist/prompt.js +54 -0
- package/server/lib/session-assist/runtime.js +111 -257
- package/server/lib/session-assist/runtime.test.js +203 -0
- package/server/lib/small-model/DOCUMENTATION.md +14 -2
- package/server/lib/small-model/call.js +37 -6
- package/server/lib/small-model/call.test.js +182 -0
- package/server/lib/terminal/DOCUMENTATION.md +23 -2
- package/server/lib/terminal/runtime.js +27 -5
- package/server/lib/terminal/runtime.test.js +67 -0
- package/server/lib/terminal/shutdown.js +106 -0
- package/server/lib/terminal/shutdown.test.js +84 -0
- package/server/lib/tts/service.test.js +78 -0
- package/server/lib/ui-auth/DOCUMENTATION.md +8 -0
- package/server/lib/ui-auth/session-cookie.js +51 -0
- package/server/lib/ui-auth/session-cookie.test.js +46 -0
- package/server/lib/ui-auth/ui-auth.js +56 -17
- package/server/lib/ui-auth/ui-auth.test.js +96 -0
- package/server/lib/walkthrough/DOCUMENTATION.md +15 -5
- package/server/lib/walkthrough/pr-source.test.js +21 -0
- package/server/lib/walkthrough/pull-request.js +7 -4
- package/server/lib/walkthrough/pull-request.test.js +16 -0
- package/server/lib/walkthrough/routes.js +19 -0
- package/server/lib/walkthrough/routes.test.js +26 -0
- package/server/lib/walkthrough/sources.js +13 -2
- package/server/opencode-worktree-readiness.test.js +130 -0
- package/dist/assets/DiffView-3Dn4QHbu.js +0 -4
- package/dist/assets/DirtyBranchSwitchDialog-CwOerqVi.js +0 -1
- package/dist/assets/FilesView-BdTOpew9.js +0 -17
- package/dist/assets/GitView-Dqf9vHJh.js +0 -6
- package/dist/assets/LinearIssuesView-CmbeL7I6.js +0 -1
- package/dist/assets/MarkdownRendererImpl-wVZlx4dd.js +0 -12
- package/dist/assets/PierreDiffViewer-DCsydlKs.js +0 -161
- package/dist/assets/SettingsView-CcM8pF5O.js +0 -28
- package/dist/assets/ToolOutputDialog-BgfTbDZ4.js +0 -15
- package/dist/assets/WalkthroughView-BEVUDxuw.js +0 -4
- package/dist/assets/appThemeRegistry-BK0dHKa6.js +0 -1
- package/dist/assets/de-C8jV7J9y.js +0 -10
- package/dist/assets/es-BI6J1M5R.js +0 -16
- package/dist/assets/fr-D6oDFt7E.js +0 -14
- package/dist/assets/index-BqhiCmYS.js +0 -39
- package/dist/assets/index-FqSOBz0J.css +0 -1
- package/dist/assets/ja-C_4i5EQu.js +0 -16
- package/dist/assets/ko-BFbOTpbq.js +0 -16
- package/dist/assets/main-C9ETLKFc.js +0 -2
- package/dist/assets/main-CJmb2dF8.js +0 -930
- package/dist/assets/markdownCore-DVuZpnli.js +0 -5
- package/dist/assets/miniChat-B4Ytoq2n.js +0 -2
- package/dist/assets/mobile-BUT6-lo1.js +0 -2
- package/dist/assets/patchFileDiff-8j1apICU.js +0 -13
- package/dist/assets/pl-COFvT56l.js +0 -16
- package/dist/assets/pt-BR-DtVOO2Lj.js +0 -16
- package/dist/assets/registerCustomTheme-BBXUCRnG.js +0 -1
- package/dist/assets/renderElectronMiniChatApp-B6vUG-vF.js +0 -2
- package/dist/assets/renderMobileApp-BNgHraq6.js +0 -25
- package/dist/assets/runtimeEndpointReset-B8LYpWvr.js +0 -17
- package/dist/assets/scheduledTasksApi-GC4epIYA.js +0 -1
- package/dist/assets/shikiHighlight-BJISeZsQ.js +0 -1
- package/dist/assets/surface-BvRzS58U.js +0 -1
- package/dist/assets/tr-B8Q0Go2l.js +0 -7
- package/dist/assets/uk-Dk4a-Fn-.js +0 -16
- package/dist/assets/useAppFontEffects-DpFNefUA.js +0 -7284
- package/dist/assets/useGitComparison-Dfxaqd0s.js +0 -1
- package/dist/assets/useInlineCommentController-CscsE4t-.js +0 -2
- package/dist/assets/useRootScrollLock-BaCvuRlT.js +0 -1
- package/dist/assets/vendor-framer-motion-livSKyyV.js +0 -1
- package/dist/assets/vendor-zod-BPAANqvH.js +0 -39
- package/dist/assets/zh-CN-B_BXxG1Q.js +0 -16
- package/dist/assets/zh-TW-DLrl1SPZ.js +0 -16
- package/server/lib/mcp-reconnect/DOCUMENTATION.md +0 -55
- package/server/lib/mcp-reconnect/runtime.js +0 -122
- package/server/lib/mcp-reconnect/runtime.test.js +0 -140
- package/server/lib/quota/providers/crof.js +0 -96
- package/server/lib/quota/providers/crof.test.js +0 -98
- /package/dist/assets/{vendor-katex-k3Ppq1Ha.js → vendor-katex-CHD9BdDO.js} +0 -0
|
@@ -58,7 +58,7 @@ const createOpenCode = () => {
|
|
|
58
58
|
return { state, fetchImpl };
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
const createRuntime = ({ dataDir = makeDataDir(), openCode = createOpenCode(), knowledge = null, retryDelayMs } = {}) => {
|
|
61
|
+
const createRuntime = ({ dataDir = makeDataDir(), openCode = createOpenCode(), knowledge = null, retryDelayMs, resolvePromptBody, now } = {}) => {
|
|
62
62
|
let eventHandler = () => {};
|
|
63
63
|
let statusHandler = () => {};
|
|
64
64
|
const broadcasts = [];
|
|
@@ -79,6 +79,8 @@ const createRuntime = ({ dataDir = makeDataDir(), openCode = createOpenCode(), k
|
|
|
79
79
|
abortHoldMs: 50,
|
|
80
80
|
};
|
|
81
81
|
if (retryDelayMs) options.retryDelayMs = retryDelayMs;
|
|
82
|
+
if (resolvePromptBody) options.resolvePromptBody = resolvePromptBody;
|
|
83
|
+
if (now) options.now = now;
|
|
82
84
|
const runtime = createMessageQueueRuntime(options);
|
|
83
85
|
return {
|
|
84
86
|
runtime,
|
|
@@ -95,6 +97,36 @@ const settle = async (ms = 30) => {
|
|
|
95
97
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
96
98
|
};
|
|
97
99
|
|
|
100
|
+
describe('auto routing', () => {
|
|
101
|
+
it('lets the routing hook rewrite the model of a queued prompt and a queued command', async () => {
|
|
102
|
+
const resolvePromptBody = vi.fn(async (body) => {
|
|
103
|
+
if (body.model?.modelID === 'auto') body.model = { providerID: 'openai', modelID: 'gpt-6-astra' };
|
|
104
|
+
if (body.model === 'openchamber/auto') body.model = 'openai/gpt-6-astra';
|
|
105
|
+
return null;
|
|
106
|
+
});
|
|
107
|
+
const { runtime, openCode, emit } = createRuntime({ resolvePromptBody });
|
|
108
|
+
runtime.start();
|
|
109
|
+
openCode.state.statuses = { [SESSION]: { type: 'busy' } };
|
|
110
|
+
openCode.state.commands = [{ name: 'review', template: 'Review $ARGUMENTS' }];
|
|
111
|
+
const auto = { providerID: 'openchamber', modelID: 'auto', agent: 'build' };
|
|
112
|
+
await runtime.enqueue(SESSION, DIRECTORY, item({ content: 'plain', text: 'plain', sendConfig: auto }));
|
|
113
|
+
await runtime.enqueue(SESSION, DIRECTORY, item({ content: '/review src', text: '/review src', sendConfig: auto }));
|
|
114
|
+
|
|
115
|
+
openCode.state.statuses = {};
|
|
116
|
+
emit({ type: 'session.status', properties: { sessionID: SESSION, status: { type: 'idle' } } });
|
|
117
|
+
await settle();
|
|
118
|
+
emit({ type: 'session.status', properties: { sessionID: SESSION, status: { type: 'idle' } } });
|
|
119
|
+
await settle();
|
|
120
|
+
|
|
121
|
+
expect(openCode.state.sent.map((entry) => entry.body.model)).toEqual([
|
|
122
|
+
{ providerID: 'openai', modelID: 'gpt-6-astra' },
|
|
123
|
+
'openai/gpt-6-astra',
|
|
124
|
+
]);
|
|
125
|
+
expect(resolvePromptBody).toHaveBeenCalledTimes(2);
|
|
126
|
+
expect(resolvePromptBody.mock.calls[0][1]).toEqual({ sessionId: SESSION, directory: DIRECTORY });
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
98
130
|
describe('parseQueuedItemInput', () => {
|
|
99
131
|
it('rejects an item the server could not deliver later', () => {
|
|
100
132
|
expect(() => parseQueuedItemInput({ content: 'x' })).toThrow(TypeError);
|
|
@@ -175,9 +207,10 @@ describe('message queue runtime', () => {
|
|
|
175
207
|
});
|
|
176
208
|
|
|
177
209
|
it('does not send into a running turn even when the status event says idle', async () => {
|
|
178
|
-
const { runtime, openCode, emit } = createRuntime();
|
|
210
|
+
const { runtime, openCode, emit } = createRuntime({ now: () => 10_000 });
|
|
179
211
|
runtime.start();
|
|
180
|
-
|
|
212
|
+
// Live unfinished turn: created after this runtime started.
|
|
213
|
+
openCode.state.tail = [{ info: { role: 'assistant', time: { created: 10_001 } } }];
|
|
181
214
|
await runtime.enqueue(SESSION, DIRECTORY, item());
|
|
182
215
|
emit({ type: 'session.status', properties: { sessionID: SESSION, status: { type: 'idle' } } });
|
|
183
216
|
await settle();
|
|
@@ -185,10 +218,24 @@ describe('message queue runtime', () => {
|
|
|
185
218
|
|
|
186
219
|
// The reply completes: that alone drains the queue (a missed idle event
|
|
187
220
|
// must not strand it).
|
|
188
|
-
openCode.state.tail = [{ info: { role: 'assistant', time: { created:
|
|
189
|
-
emit({ type: 'message.updated', properties: { info: { role: 'assistant', sessionID: SESSION, time: { created:
|
|
221
|
+
openCode.state.tail = [{ info: { role: 'assistant', time: { created: 10_001, completed: 10_002 } } }];
|
|
222
|
+
emit({ type: 'message.updated', properties: { info: { role: 'assistant', sessionID: SESSION, time: { created: 10_001, completed: 10_002 } } } });
|
|
223
|
+
await settle();
|
|
224
|
+
expect(openCode.state.sent).toHaveLength(1);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('delivers past an unfinished tail that predates this runtime (dead pre-restart run)', async () => {
|
|
228
|
+
const { runtime, openCode, emit } = createRuntime({ now: () => 10_000 });
|
|
229
|
+
runtime.start();
|
|
230
|
+
// Assistant reply interrupted by a server restart: unfinished, but older
|
|
231
|
+
// than this runtime — no completion event will ever arrive for it, so it
|
|
232
|
+
// must not block a restored queue forever.
|
|
233
|
+
openCode.state.tail = [{ info: { role: 'assistant', time: { created: 1 } } }];
|
|
234
|
+
await runtime.enqueue(SESSION, DIRECTORY, item());
|
|
235
|
+
emit({ type: 'session.status', properties: { sessionID: SESSION, status: { type: 'idle' } } });
|
|
190
236
|
await settle();
|
|
191
237
|
expect(openCode.state.sent).toHaveLength(1);
|
|
238
|
+
expect(openCode.state.sent[0].path).toBe(`/session/${SESSION}/prompt_async`);
|
|
192
239
|
});
|
|
193
240
|
|
|
194
241
|
it('treats an unreachable OpenCode as unknown, not idle', async () => {
|
|
@@ -251,7 +298,7 @@ describe('message queue runtime', () => {
|
|
|
251
298
|
const first = createRuntime({ dataDir });
|
|
252
299
|
first.runtime.start();
|
|
253
300
|
first.openCode.state.statuses = { [SESSION]: { type: 'busy' } };
|
|
254
|
-
await first.runtime.enqueue(SESSION, DIRECTORY, item({ content: 'persisted', text: 'persisted' }));
|
|
301
|
+
await first.runtime.enqueue(SESSION, DIRECTORY, item({ content: 'persisted', text: 'persisted', contextPreview: 'Saved context preview' }));
|
|
255
302
|
await first.runtime.flush();
|
|
256
303
|
first.runtime.stop();
|
|
257
304
|
|
|
@@ -259,6 +306,7 @@ describe('message queue runtime', () => {
|
|
|
259
306
|
second.runtime.start();
|
|
260
307
|
await second.runtime.load();
|
|
261
308
|
expect(second.runtime.sessionSnapshot(SESSION).items.map((entry) => entry.content)).toEqual(['persisted']);
|
|
309
|
+
expect(second.runtime.sessionSnapshot(SESSION).items[0].contextPreview).toBe('Saved context preview');
|
|
262
310
|
second.connect();
|
|
263
311
|
await settle();
|
|
264
312
|
expect(second.openCode.state.sent).toHaveLength(1);
|
|
@@ -315,6 +363,35 @@ describe('message queue runtime', () => {
|
|
|
315
363
|
expect(runtime.snapshot().sessions).toEqual([]);
|
|
316
364
|
});
|
|
317
365
|
|
|
366
|
+
it('retains a bounded context preview in snapshots and broadcasts without exposing the full payload', async () => {
|
|
367
|
+
const { runtime, broadcasts } = createRuntime();
|
|
368
|
+
runtime.start();
|
|
369
|
+
const context = [{ kind: 'context', text: 'Full quoted content', metadata: { openchamberContext: { kind: 'chat-quote', quote: 'Original answer', text: 'Explain this' } } }];
|
|
370
|
+
const { itemId } = await runtime.enqueue(SESSION, DIRECTORY, item({ content: '', text: '', context, contextPreview: 'Explain this' }));
|
|
371
|
+
const projected = runtime.sessionSnapshot(SESSION).items[0];
|
|
372
|
+
expect(projected.contextPreview).toBe('Explain this');
|
|
373
|
+
expect(projected.content).toBe('');
|
|
374
|
+
expect(projected.text).toBe('');
|
|
375
|
+
expect(projected).not.toHaveProperty('context');
|
|
376
|
+
expect(broadcasts.at(-1).properties.session.items[0].contextPreview).toBe('Explain this');
|
|
377
|
+
const taken = await runtime.take(SESSION, itemId);
|
|
378
|
+
expect(taken.item.context).toEqual(context);
|
|
379
|
+
expect(taken.item.content).toBe('');
|
|
380
|
+
|
|
381
|
+
await runtime.enqueue(SESSION, DIRECTORY, item({ content: '', text: '', context, contextPreview: 'a'.repeat(5000) }));
|
|
382
|
+
expect(runtime.sessionSnapshot(SESSION).items[0].contextPreview).toBe('a'.repeat(100) + '...');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('derives a preview for older queued annotations without a saved summary', async () => {
|
|
386
|
+
const { runtime } = createRuntime();
|
|
387
|
+
runtime.start();
|
|
388
|
+
await runtime.enqueue(SESSION, DIRECTORY, item({ content: '', text: '', context: [
|
|
389
|
+
{ kind: 'instruction', text: 'Use the skill' },
|
|
390
|
+
{ kind: 'context', text: 'Model-facing wrapper', metadata: { openchamberContext: { kind: 'browser-annotation', text: 'Fix the button\nMore detail' } } },
|
|
391
|
+
] }));
|
|
392
|
+
expect(runtime.sessionSnapshot(SESSION).items[0].contextPreview).toBe('Fix the button...');
|
|
393
|
+
});
|
|
394
|
+
|
|
318
395
|
it('names the directory in the broadcast that empties a queue', async () => {
|
|
319
396
|
// The UI keys its projection by directory; without it the client cannot
|
|
320
397
|
// tell which queue just delivered its last message and keeps showing it.
|
|
@@ -77,7 +77,7 @@ This module provides notification message preparation utilities for the web serv
|
|
|
77
77
|
### Emitter runtime API (emitter-runtime.js)
|
|
78
78
|
- `createNotificationEmitterRuntime(dependencies)`: creates runtime for unified notification emission channels.
|
|
79
79
|
- Returned API:
|
|
80
|
-
|
|
80
|
+
- `writeSseEvent(res, payload, serializedPayload?)`, where broadcast callers may share one JSON encoding across recipients
|
|
81
81
|
- `emitDesktopNotification(payload)`
|
|
82
82
|
- `broadcastUiNotification(payload)`
|
|
83
83
|
|
|
@@ -22,8 +22,8 @@ export const createNotificationEmitterRuntime = (dependencies) => {
|
|
|
22
22
|
onDesktopNotification = typeof cb === 'function' ? cb : null;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const writeSseEvent = (res, payload) => {
|
|
26
|
-
res.write(`data: ${
|
|
25
|
+
const writeSseEvent = (res, payload, serializedPayload = JSON.stringify(payload)) => {
|
|
26
|
+
res.write(`data: ${serializedPayload}\n\n`);
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const emitDesktopNotification = (payload) => {
|
|
@@ -91,9 +91,10 @@ export const createNotificationEmitterRuntime = (dependencies) => {
|
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
const serializedPayload = JSON.stringify(syntheticPayload);
|
|
94
95
|
for (const res of clients) {
|
|
95
96
|
try {
|
|
96
|
-
writeSseEvent(res, syntheticPayload);
|
|
97
|
+
writeSseEvent(res, syntheticPayload, serializedPayload);
|
|
97
98
|
} catch {
|
|
98
99
|
// ignore
|
|
99
100
|
}
|
|
@@ -44,6 +44,7 @@ export const registerNotificationRoutes = (app, dependencies) => {
|
|
|
44
44
|
writeSseEvent,
|
|
45
45
|
getSessionActivitySnapshot,
|
|
46
46
|
getSessionStateSnapshot,
|
|
47
|
+
getPendingBlockingRequestsSnapshot,
|
|
47
48
|
getSessionAttentionSnapshot,
|
|
48
49
|
getSessionState,
|
|
49
50
|
getSessionAttentionState,
|
|
@@ -295,11 +296,15 @@ export const registerNotificationRoutes = (app, dependencies) => {
|
|
|
295
296
|
});
|
|
296
297
|
});
|
|
297
298
|
|
|
299
|
+
// Cross-project seed for clients that do not initialize every directory:
|
|
300
|
+
// live status per session plus the permission and question requests still
|
|
301
|
+
// waiting for an answer. Both come from the server's single upstream stream.
|
|
298
302
|
app.get('/api/sessions/status', async (_req, res) => {
|
|
299
303
|
await ensureSessionWatcher();
|
|
300
304
|
const snapshot = getSessionStateSnapshot();
|
|
301
305
|
res.json({
|
|
302
306
|
sessions: snapshot,
|
|
307
|
+
pending: typeof getPendingBlockingRequestsSnapshot === 'function' ? getPendingBlockingRequestsSnapshot() : {},
|
|
303
308
|
serverTime: Date.now(),
|
|
304
309
|
});
|
|
305
310
|
});
|
|
@@ -44,6 +44,12 @@ other.
|
|
|
44
44
|
- Send and fork dispatches without an explicit model/agent/variant reuse the
|
|
45
45
|
target session's last user-message selection before falling back to the
|
|
46
46
|
configured defaults; only session creation resolves defaults directly.
|
|
47
|
+
- Default agents resolve from the owning project before global settings and
|
|
48
|
+
OpenCode defaults. Directory-based requests identify the project before
|
|
49
|
+
creating a worktree; existing linked worktrees resolve through Git's primary
|
|
50
|
+
worktree root. Send/fork fallback uses that same owner. Configured model IDs
|
|
51
|
+
and effort preferences survive missing catalog entries rather than silently
|
|
52
|
+
dispatching with a different model.
|
|
47
53
|
- Usage errors name the missing or conflicting input so CLI and agent-tool
|
|
48
54
|
callers can correct an invalid request without an upfront usage manual.
|
|
49
55
|
- Explicit `projectId` or `directory` scope takes precedence over the managed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { createOpencodeClient } from '@opencode-ai/sdk/v2';
|
|
3
|
-
import { createWorktree, getWorktreeBootstrapStatus } from '../git/index.js';
|
|
3
|
+
import { createWorktree, getWorktreeBootstrapStatus, resolvePrimaryWorktreeRoot } from '../git/index.js';
|
|
4
4
|
import { expandSnippets } from '../opencode/snippets.js';
|
|
5
5
|
import { expandCommandGoalObjective, parseScheduledCommandPrompt } from '../scheduled-tasks/runtime.js';
|
|
6
6
|
import { buildGoalIntroText, createSessionGoal } from '../session-goal/create.js';
|
|
@@ -75,6 +75,7 @@ const resolveVariant = (providers, providerID, modelID, variant) => {
|
|
|
75
75
|
if (!normalized) return undefined;
|
|
76
76
|
const provider = providers.find((entry) => entry?.id === providerID);
|
|
77
77
|
const model = providerModels(provider).find((entry) => entry?.id === modelID);
|
|
78
|
+
if (!model) return normalized;
|
|
78
79
|
return model?.variants && Object.prototype.hasOwnProperty.call(model.variants, normalized)
|
|
79
80
|
? normalized
|
|
80
81
|
: undefined;
|
|
@@ -82,6 +83,18 @@ const resolveVariant = (providers, providerID, modelID, variant) => {
|
|
|
82
83
|
|
|
83
84
|
const parseConfigModel = (value) => splitModel(value);
|
|
84
85
|
|
|
86
|
+
const resolveProjectDefaults = (settings, directory, projectId) => {
|
|
87
|
+
const projects = Array.isArray(settings?.projects) ? settings.projects : [];
|
|
88
|
+
const matchedProject = projectId
|
|
89
|
+
? projects.find((entry) => entry?.id === projectId) || null
|
|
90
|
+
: projects.find((entry) => entry?.path === directory) || null;
|
|
91
|
+
return {
|
|
92
|
+
defaultAgent: asNonEmptyString(matchedProject?.defaultAgent),
|
|
93
|
+
defaultModel: asNonEmptyString(matchedProject?.defaultModel),
|
|
94
|
+
defaultVariant: asNonEmptyString(matchedProject?.defaultVariant),
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
85
98
|
const buildDirectoryHeaders = (directory) => ({
|
|
86
99
|
// OpenCode rejects non-ASCII header values; the official SDK sends this
|
|
87
100
|
// header percent-encoded, so match that wire format (non-ASCII checkout
|
|
@@ -121,11 +134,15 @@ const fetchSelectionInputs = async ({ buildOpenCodeUrl, authHeaders, directory,
|
|
|
121
134
|
};
|
|
122
135
|
};
|
|
123
136
|
|
|
124
|
-
const resolveDefaultSelection = ({ agents, providers, settings, opencodeDefaultAgent, opencodeDefaultModel }) => {
|
|
137
|
+
const resolveDefaultSelection = ({ agents, providers, settings, projectDefaults, opencodeDefaultAgent, opencodeDefaultModel }) => {
|
|
125
138
|
const primaryAgents = agents.filter((agent) => isPrimaryAgentMode(agent?.mode) && agent?.hidden !== true);
|
|
126
139
|
let resolvedAgent = null;
|
|
140
|
+
const projectDefaultAgent = asNonEmptyString(projectDefaults?.defaultAgent);
|
|
127
141
|
const settingsDefaultAgent = asNonEmptyString(settings?.defaultAgent);
|
|
128
|
-
if (
|
|
142
|
+
if (projectDefaultAgent) {
|
|
143
|
+
resolvedAgent = agents.find((agent) => agent?.name === projectDefaultAgent) || null;
|
|
144
|
+
}
|
|
145
|
+
if (!resolvedAgent && settingsDefaultAgent) {
|
|
129
146
|
resolvedAgent = agents.find((agent) => agent?.name === settingsDefaultAgent) || null;
|
|
130
147
|
}
|
|
131
148
|
if (!resolvedAgent && opencodeDefaultAgent) {
|
|
@@ -140,20 +157,24 @@ const resolveDefaultSelection = ({ agents, providers, settings, opencodeDefaultA
|
|
|
140
157
|
|
|
141
158
|
let model = null;
|
|
142
159
|
let variant;
|
|
160
|
+
const projectDefaultModel = parseConfigModel(projectDefaults?.defaultModel);
|
|
143
161
|
const settingsDefaultModel = parseConfigModel(settings?.defaultModel);
|
|
144
|
-
if (
|
|
162
|
+
if (projectDefaultModel) {
|
|
163
|
+
model = projectDefaultModel;
|
|
164
|
+
variant = resolveVariant(providers, model.providerID, model.modelID, projectDefaults?.defaultVariant);
|
|
165
|
+
}
|
|
166
|
+
if (!model && settingsDefaultModel) {
|
|
145
167
|
model = settingsDefaultModel;
|
|
146
168
|
variant = resolveVariant(providers, model.providerID, model.modelID, settings?.defaultVariant);
|
|
147
169
|
}
|
|
148
170
|
|
|
149
|
-
if (!model && resolvedAgent?.model?.providerID && resolvedAgent?.model?.modelID
|
|
150
|
-
&& hasProviderModel(providers, resolvedAgent.model.providerID, resolvedAgent.model.modelID)) {
|
|
171
|
+
if (!model && resolvedAgent?.model?.providerID && resolvedAgent?.model?.modelID) {
|
|
151
172
|
model = { providerID: resolvedAgent.model.providerID, modelID: resolvedAgent.model.modelID };
|
|
152
173
|
variant = resolveVariant(providers, model.providerID, model.modelID, resolvedAgent.variant);
|
|
153
174
|
}
|
|
154
175
|
|
|
155
176
|
const opencodeModel = parseConfigModel(opencodeDefaultModel);
|
|
156
|
-
if (!model && opencodeModel
|
|
177
|
+
if (!model && opencodeModel) {
|
|
157
178
|
model = opencodeModel;
|
|
158
179
|
}
|
|
159
180
|
|
|
@@ -304,9 +325,15 @@ const resolveRequestedDirectory = async ({ payload, readSettingsFromDiskMigrated
|
|
|
304
325
|
|
|
305
326
|
const directory = asNonEmptyString(payload?.directory);
|
|
306
327
|
const validated = await validateDirectoryPath(directory);
|
|
307
|
-
return validated.
|
|
308
|
-
|
|
309
|
-
|
|
328
|
+
if (!validated.ok) return { ok: false, status: 400, error: validated.error || 'Invalid directory' };
|
|
329
|
+
const settings = await readSettingsFromDiskMigrated();
|
|
330
|
+
const projects = sanitizeProjects(settings?.projects || []);
|
|
331
|
+
let project = projects.find((entry) => entry.path === validated.directory);
|
|
332
|
+
if (!project && projects.length > 0) {
|
|
333
|
+
const { root } = await resolvePrimaryWorktreeRoot(validated.directory);
|
|
334
|
+
project = projects.find((entry) => entry.path === root);
|
|
335
|
+
}
|
|
336
|
+
return { ok: true, directory: validated.directory, ...(project ? { projectId: project.id } : {}) };
|
|
310
337
|
};
|
|
311
338
|
|
|
312
339
|
const PROMPT_LANDED_TIMEOUT_MS = 5_000;
|
|
@@ -395,6 +422,10 @@ export const createOpenChamberSessionService = (dependencies) => {
|
|
|
395
422
|
emitSessionCreatedEvent,
|
|
396
423
|
createSessionGoal: createSessionGoalOverride,
|
|
397
424
|
sessionKnowledgeRuntime = null,
|
|
425
|
+
// Auto routing. Prompts dispatched here go straight to OpenCode, not
|
|
426
|
+
// through the proxy that rewrites the Auto sentinel, so the same hook runs
|
|
427
|
+
// on the body before it is sent. Null when routing is not wired in.
|
|
428
|
+
resolvePromptBody = null,
|
|
398
429
|
} = dependencies;
|
|
399
430
|
|
|
400
431
|
// Last user message of an existing session, as a selection to reuse. Returns
|
|
@@ -468,6 +499,7 @@ export const createOpenChamberSessionService = (dependencies) => {
|
|
|
468
499
|
authHeaders,
|
|
469
500
|
sessionID,
|
|
470
501
|
directory,
|
|
502
|
+
projectId,
|
|
471
503
|
prompt,
|
|
472
504
|
goalInput,
|
|
473
505
|
requestedModel,
|
|
@@ -495,7 +527,10 @@ export const createOpenChamberSessionService = (dependencies) => {
|
|
|
495
527
|
directory,
|
|
496
528
|
readSettingsFromDiskMigrated,
|
|
497
529
|
});
|
|
498
|
-
const defaults = resolveDefaultSelection(
|
|
530
|
+
const defaults = resolveDefaultSelection({
|
|
531
|
+
...inputs,
|
|
532
|
+
projectDefaults: resolveProjectDefaults(inputs.settings, directory, projectId),
|
|
533
|
+
});
|
|
499
534
|
if (!model) {
|
|
500
535
|
model = defaults.model;
|
|
501
536
|
if (variant == null) variant = defaults.variant;
|
|
@@ -565,25 +600,21 @@ export const createOpenChamberSessionService = (dependencies) => {
|
|
|
565
600
|
? await sessionKnowledgeRuntime.resolvePendingForSession(sessionID, directory)
|
|
566
601
|
.catch(() => ({ text: '', signature: '' }))
|
|
567
602
|
: { text: '', signature: '' };
|
|
603
|
+
const payload = {
|
|
604
|
+
model,
|
|
605
|
+
...(agent ? { agent } : {}),
|
|
606
|
+
...(variant ? { variant } : {}),
|
|
607
|
+
parts: [
|
|
608
|
+
...(knowledge.text ? [{ type: 'text', text: knowledge.text, synthetic: true }] : []),
|
|
609
|
+
{ type: 'text', text: expandedPrompt },
|
|
610
|
+
...(goalInput.enabled
|
|
611
|
+
? [{ type: 'text', text: buildGoalIntroText(goalInput.tokenBudget), synthetic: true }]
|
|
612
|
+
: []),
|
|
613
|
+
],
|
|
614
|
+
};
|
|
615
|
+
await resolvePromptBody?.(payload, { sessionId: sessionID, directory });
|
|
568
616
|
try {
|
|
569
|
-
await runPromptAsync({
|
|
570
|
-
baseUrl,
|
|
571
|
-
authHeaders,
|
|
572
|
-
sessionID,
|
|
573
|
-
directory,
|
|
574
|
-
payload: {
|
|
575
|
-
model,
|
|
576
|
-
...(agent ? { agent } : {}),
|
|
577
|
-
...(variant ? { variant } : {}),
|
|
578
|
-
parts: [
|
|
579
|
-
...(knowledge.text ? [{ type: 'text', text: knowledge.text, synthetic: true }] : []),
|
|
580
|
-
{ type: 'text', text: expandedPrompt },
|
|
581
|
-
...(goalInput.enabled
|
|
582
|
-
? [{ type: 'text', text: buildGoalIntroText(goalInput.tokenBudget), synthetic: true }]
|
|
583
|
-
: []),
|
|
584
|
-
],
|
|
585
|
-
},
|
|
586
|
-
});
|
|
617
|
+
await runPromptAsync({ baseUrl, authHeaders, sessionID, directory, payload });
|
|
587
618
|
} catch (error) {
|
|
588
619
|
throw markGoalPartial(error);
|
|
589
620
|
}
|
|
@@ -735,6 +766,7 @@ export const createOpenChamberSessionService = (dependencies) => {
|
|
|
735
766
|
authHeaders,
|
|
736
767
|
sessionID,
|
|
737
768
|
directory: sessionDirectory,
|
|
769
|
+
projectId: resolvedDirectory.projectId,
|
|
738
770
|
prompt,
|
|
739
771
|
goalInput,
|
|
740
772
|
requestedModel: model,
|
|
@@ -838,6 +870,7 @@ export const createOpenChamberSessionService = (dependencies) => {
|
|
|
838
870
|
authHeaders,
|
|
839
871
|
sessionID: targetSessionID,
|
|
840
872
|
directory,
|
|
873
|
+
projectId: resolvedDirectory.projectId,
|
|
841
874
|
prompt,
|
|
842
875
|
goalInput,
|
|
843
876
|
requestedModel,
|
|
@@ -90,6 +90,7 @@ vi.mock('@opencode-ai/sdk/v2', () => ({
|
|
|
90
90
|
vi.mock('../git/index.js', () => ({
|
|
91
91
|
createWorktree: (...args) => globalThis.__openchamberCreateWorktreeMock(...args),
|
|
92
92
|
getWorktreeBootstrapStatus: (...args) => globalThis.__openchamberGetWorktreeBootstrapStatusMock(...args),
|
|
93
|
+
resolvePrimaryWorktreeRoot: async (directory) => ({ root: directory === '/repo/worktrees/side-task' ? '/repo/app' : directory }),
|
|
93
94
|
}));
|
|
94
95
|
|
|
95
96
|
const createApp = (overrides = {}, options = {}) => {
|
|
@@ -360,6 +361,101 @@ describe('openchamber session routes', () => {
|
|
|
360
361
|
}
|
|
361
362
|
});
|
|
362
363
|
|
|
364
|
+
it('lets the routing hook replace an Auto default before the prompt leaves', async () => {
|
|
365
|
+
const originalFetch = globalThis.fetch;
|
|
366
|
+
const fetchMock = vi.fn(async (url) => {
|
|
367
|
+
const text = String(url);
|
|
368
|
+
if (text.includes('/prompt_async')) {
|
|
369
|
+
return { ok: true, text: async () => '' };
|
|
370
|
+
}
|
|
371
|
+
if (text.includes('/config/providers')) {
|
|
372
|
+
return { ok: true, json: async () => ({ providers: [{ id: 'openai', models: { 'gpt-5.5': { id: 'gpt-5.5' } } }] }) };
|
|
373
|
+
}
|
|
374
|
+
if (text.includes('/agent')) {
|
|
375
|
+
return { ok: true, json: async () => [{ name: 'build', mode: 'primary' }] };
|
|
376
|
+
}
|
|
377
|
+
if (text.includes('/config')) {
|
|
378
|
+
return { ok: true, json: async () => ({}) };
|
|
379
|
+
}
|
|
380
|
+
return { ok: true, json: async () => ({ id: 'ses_123' }) };
|
|
381
|
+
});
|
|
382
|
+
globalThis.fetch = fetchMock;
|
|
383
|
+
const seen = [];
|
|
384
|
+
const resolvePromptBody = vi.fn(async (body, target) => {
|
|
385
|
+
seen.push({ model: body.model, target });
|
|
386
|
+
body.model = { providerID: 'openai', modelID: 'gpt-5.5' };
|
|
387
|
+
});
|
|
388
|
+
const { app } = createApp({
|
|
389
|
+
readSettingsFromDiskMigrated: async () => ({
|
|
390
|
+
defaultModel: 'openchamber/auto',
|
|
391
|
+
defaultAgent: 'build',
|
|
392
|
+
projects: [{ id: 'proj_1', path: '/repo/app' }],
|
|
393
|
+
}),
|
|
394
|
+
resolvePromptBody,
|
|
395
|
+
});
|
|
396
|
+
try {
|
|
397
|
+
await request(app)
|
|
398
|
+
.post('/api/openchamber/sessions')
|
|
399
|
+
.send({ directory: '/repo/app', prompt: 'Run this' })
|
|
400
|
+
.expect(200);
|
|
401
|
+
|
|
402
|
+
expect(seen).toEqual([{
|
|
403
|
+
model: { providerID: 'openchamber', modelID: 'auto' },
|
|
404
|
+
target: { sessionId: 'ses_123', directory: '/repo/app' },
|
|
405
|
+
}]);
|
|
406
|
+
const promptCall = fetchMock.mock.calls.find(([url]) => String(url).includes('/prompt_async'));
|
|
407
|
+
expect(JSON.parse(promptCall?.[1]?.body).model).toEqual({ providerID: 'openai', modelID: 'gpt-5.5' });
|
|
408
|
+
} finally {
|
|
409
|
+
globalThis.fetch = originalFetch;
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it.each([
|
|
414
|
+
['', { projectId: 'proj_1' }],
|
|
415
|
+
['', { directory: '/repo/app', worktree: { name: 'side-task' } }],
|
|
416
|
+
['', { directory: '/repo/worktrees/side-task' }],
|
|
417
|
+
['/ses_existing/send', { directory: '/repo/worktrees/side-task' }],
|
|
418
|
+
['/ses_existing/fork', { directory: '/repo/worktrees/side-task' }],
|
|
419
|
+
])('prefers project defaults for %s with %j', async (endpoint, scope) => {
|
|
420
|
+
const originalFetch = globalThis.fetch;
|
|
421
|
+
const fetchMock = vi.fn(async (url) => {
|
|
422
|
+
const text = String(url);
|
|
423
|
+
if (text.includes('/prompt_async')) {
|
|
424
|
+
return { ok: true, text: async () => '' };
|
|
425
|
+
}
|
|
426
|
+
if (text.includes('/config/providers')) {
|
|
427
|
+
return { ok: true, json: async () => ({ providers: [{ id: 'openai', models: { 'gpt-5.5': { id: 'gpt-5.5' } } }] }) };
|
|
428
|
+
}
|
|
429
|
+
if (text.includes('/agent')) {
|
|
430
|
+
return { ok: true, json: async () => [{ name: 'build', mode: 'primary' }, { name: 'plan', mode: 'primary' }] };
|
|
431
|
+
}
|
|
432
|
+
if (text.includes('/config')) {
|
|
433
|
+
return { ok: true, json: async () => ({}) };
|
|
434
|
+
}
|
|
435
|
+
return { ok: true, json: async () => ({ id: 'ses_123' }) };
|
|
436
|
+
});
|
|
437
|
+
globalThis.fetch = fetchMock;
|
|
438
|
+
const { app } = createApp({
|
|
439
|
+
readSettingsFromDiskMigrated: async () => ({
|
|
440
|
+
defaultModel: 'openai/gpt-5.5',
|
|
441
|
+
defaultAgent: 'build',
|
|
442
|
+
projects: [{ id: 'proj_1', path: '/repo/app', defaultAgent: 'plan' }],
|
|
443
|
+
}),
|
|
444
|
+
});
|
|
445
|
+
try {
|
|
446
|
+
const response = await request(app)
|
|
447
|
+
.post(`/api/openchamber/sessions${endpoint}`)
|
|
448
|
+
.send({ ...scope, prompt: 'Run this' })
|
|
449
|
+
.expect(200);
|
|
450
|
+
|
|
451
|
+
expect(response.body.agent).toBe('plan');
|
|
452
|
+
const promptCall = fetchMock.mock.calls.find(([url]) => String(url).includes('/prompt_async'));
|
|
453
|
+
expect(JSON.parse(promptCall?.[1]?.body)).toMatchObject({ agent: 'plan' });
|
|
454
|
+
} finally {
|
|
455
|
+
globalThis.fetch = originalFetch;
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
363
459
|
it('dispatches an initial prompt when model is provided', async () => {
|
|
364
460
|
const originalFetch = globalThis.fetch;
|
|
365
461
|
const fetchMock = vi.fn(async (url) => {
|
|
@@ -32,10 +32,9 @@ This module provides OpenCode server integration utilities for the web server ru
|
|
|
32
32
|
- `packages/web/server/lib/opencode/tunnel-wiring-runtime.js`: tunnel service/routes composition runtime and active-port wiring for main server startup.
|
|
33
33
|
- `packages/web/server/lib/opencode/startup-pipeline-runtime.js`: server startup tail orchestration runtime for terminal/proxy/static/start-listen flow.
|
|
34
34
|
- `packages/web/server/lib/opencode/startup-performance.js`: opt-in startup phase diagnostics with fixed labels and numeric metadata allowlists.
|
|
35
|
-
- `packages/web/server/lib/agent-tool/runtime.js`: managed OpenCode custom-tool materialization, environment injection,
|
|
35
|
+
- `packages/web/server/lib/agent-tool/runtime.js`: managed OpenCode custom-tool materialization, environment injection, same-machine authentication (loopback, or the bound address for a concrete bind), and fixed CLI action dispatch.
|
|
36
36
|
- `packages/web/server/lib/system-prompt/runtime.js`: opt-in managed OpenCode system-prompt optimizer materialization and plugin injection.
|
|
37
|
-
- `packages/web/server/lib/
|
|
38
|
-
- `packages/web/server/lib/opencode/managed-plugin-config.js`: the one `OPENCODE_CONFIG_CONTENT` merge every managed plugin (agent tools, system prompt optimizer, MCP reconnect) appends itself through.
|
|
37
|
+
- `packages/web/server/lib/opencode/managed-plugin-config.js`: the one `OPENCODE_CONFIG_CONTENT` merge every managed plugin (agent tools, system prompt optimizer) appends itself through.
|
|
39
38
|
- `packages/web/server/lib/opencode/server-utils-runtime.js`: shared server runtime utilities for OpenCode proxy wiring, OpenCode port/readiness helpers, and snapshot fetchers.
|
|
40
39
|
- `packages/web/server/lib/opencode/openchamber-routes.js`: OpenChamber update and models metadata route registration.
|
|
41
40
|
- `packages/web/server/lib/opencode/pwa-manifest-routes.js`: PWA manifest route registration with recent-session shortcut resolution and short-lived caching.
|
|
@@ -45,6 +44,28 @@ This module provides OpenCode server integration utilities for the web server ru
|
|
|
45
44
|
- `packages/web/server/lib/opencode/settings-helpers.js`: Settings payload sanitization/format helpers runtime for response shaping and persisted merge prep.
|
|
46
45
|
- `packages/web/server/lib/opencode/settings-normalization-runtime.js`: path/settings/tunnel normalization and sanitization helpers runtime used by settings/routes/config wiring.
|
|
47
46
|
- `packages/web/server/lib/opencode/theme-runtime.js`: custom theme JSON validation and theme directory loading runtime for settings utility routes.
|
|
47
|
+
|
|
48
|
+
`POST /api/config/themes` saves a converted VS Code palette. The runtime validates
|
|
49
|
+
literal colors and required authored roles, assigns a content-derived filename,
|
|
50
|
+
and publishes through a same-directory hard link so partial files and overwrites
|
|
51
|
+
are impossible. Identical retries reuse the existing file; a manually edited
|
|
52
|
+
collision returns 409. Temporary files are ignored by the loader and removed
|
|
53
|
+
after publication or failure. Non-missing-directory read failures propagate to
|
|
54
|
+
the route instead of returning an authoritative empty library.
|
|
55
|
+
|
|
56
|
+
The common request middleware parses theme POST bodies before these routes;
|
|
57
|
+
integration tests must use that middleware rather than an unrestricted test parser.
|
|
58
|
+
`DELETE /api/config/themes/:id` finds a valid regular JSON file by its metadata ID
|
|
59
|
+
inside the custom themes directory. IDs are never used as filenames. Hand-added
|
|
60
|
+
themes are supported; symlinks and bundled themes are outside deletion ownership.
|
|
61
|
+
Duplicate matching IDs fail explicitly. Missing themes are an idempotent success;
|
|
62
|
+
filesystem failures remain errors.
|
|
63
|
+
`theme-catalog.js` owns POST catalog search/package routes under
|
|
64
|
+
`/api/config/themes/catalog/`. It fetches only Open VSX and its Eclipse CDN over
|
|
65
|
+
HTTPS, validates redirects and checksums, and verifies packaged identity.
|
|
66
|
+
`theme-archive.js` reads selected JSON entries in memory with bounded decompression.
|
|
67
|
+
JSON includes and token references stay inside the package. Each failed variant
|
|
68
|
+
is reported separately so valid siblings remain available. No extension code runs.
|
|
48
69
|
- `packages/web/server/lib/opencode/proxy.js`: OpenCode API/SSE forwarding and readiness-gate route registration.
|
|
49
70
|
- `packages/web/server/lib/opencode/session-runtime.js`: session status/attention/activity runtime for OpenCode SSE events.
|
|
50
71
|
- `packages/web/server/lib/opencode/watcher.js`: global SSE watcher runtime for push/session event fanout.
|
|
@@ -132,8 +153,12 @@ The runtime maintains active-session count incrementally from idempotent activit
|
|
|
132
153
|
- `killProcessOnPort(port)`
|
|
133
154
|
|
|
134
155
|
Managed OpenCode launch also merges the environment returned by the agent-tool
|
|
135
|
-
runtime
|
|
136
|
-
|
|
156
|
+
runtime and the opt-in system prompt optimizer, each appending its `file://`
|
|
157
|
+
entry to the previous one's config. OpenChamber adds no automatic MCP reconnect
|
|
158
|
+
loop; recovery after a failed connection is manual for both local and remote
|
|
159
|
+
servers. Previously generated reconnect plugin files are inert because managed
|
|
160
|
+
launch no longer registers them. User-configured plugins remain user-owned.
|
|
161
|
+
PATH and `OPENCODE_SERVER_PASSWORD` remain lifecycle-owned and cannot
|
|
137
162
|
be replaced by injected values. External OpenCode processes receive no
|
|
138
163
|
OpenChamber tool injection. Managed launch env strips AppImage `ARGV0` before
|
|
139
164
|
spawn so zsh-backed OpenCode tools do not rewrite child argv[0] to the AppImage
|
|
@@ -153,9 +178,27 @@ Transport-triggered health checks share the periodic monitor's failure accountin
|
|
|
153
178
|
|
|
154
179
|
Managed health failures are classified as `timeout`, `connection_refused`, `connection_reset`, `invalid_response`, or `error`. The lifecycle retains the latest counted failure with a bounded detail string and source. Managed process wrappers continue capturing a sanitized, bounded stderr tail after readiness and retain exit code/signal. Before replacing a managed process, lifecycle snapshots the reason, latest health failure, process diagnostics/aliveness, busy-session count, and timestamp into `lastOpenCodeRestartDiagnostics`; successful startup does not clear this snapshot, and `/health` exposes it for post-restart diagnosis without process environment or credentials.
|
|
155
180
|
|
|
181
|
+
Managed process ownership starts at spawn. The registry and runtime process
|
|
182
|
+
handle include children that have not announced readiness yet, so shutdown can
|
|
183
|
+
stop an in-flight startup. Readiness timeout, malformed startup output, and
|
|
184
|
+
health-probe errors close that child before retrying. Shutdown cancels further
|
|
185
|
+
startup attempts. Closing a process is single-flight and unregisters it only
|
|
186
|
+
after it exits.
|
|
187
|
+
|
|
188
|
+
On Windows, managed teardown invokes the existing tree termination command
|
|
189
|
+
before terminating the root. Calling `child.kill()` first loses the ancestry
|
|
190
|
+
needed to find Git, shell, and MCP descendants. On POSIX, the managed child
|
|
191
|
+
starts in its own process group and teardown escalates against that group even
|
|
192
|
+
if the root has already exited. A tool ignoring SIGTERM must not survive just
|
|
193
|
+
because the server closed its own pipes. The
|
|
194
|
+
`lifecycle-process.test.js` regressions launch real parent/child fixtures and
|
|
195
|
+
check PID exit plus registry cleanup. macOS results do not validate Windows
|
|
196
|
+
ConPTY or Console Window Host behavior.
|
|
197
|
+
|
|
156
198
|
## Public exports (env-runtime.js)
|
|
157
199
|
- `createOpenCodeEnvRuntime(dependencies)`: creates runtime that owns OpenCode CLI environment and binary discovery state.
|
|
158
200
|
- OpenCode CLI resolution order is persisted settings, environment overrides, bundled Desktop CLI when available, PATH, known install locations, then platform shell discovery.
|
|
201
|
+
- Automatic bundled resolution under `OPENCHAMBER_RUNTIME=desktop` stays in runtime state and is returned to the managed launch function, including on OpenCode restart. It does not populate `process.env.OPENCODE_BINARY`: AppImage updater relaunch inherits that environment and would mistake the previous bundle path for an explicit override. Explicit settings/env selections and non-desktop or non-bundled resolution retain their existing environment behavior. This prevents future inheritance; it does not reinterpret overrides already inherited from older releases.
|
|
159
202
|
- Returned API:
|
|
160
203
|
- `applyLoginShellEnvSnapshot()`
|
|
161
204
|
- `getLoginShellEnvSnapshot()`
|
|
@@ -328,6 +371,7 @@ Managed health failures are classified as `timeout`, `connection_refused`, `conn
|
|
|
328
371
|
|
|
329
372
|
## Public exports (shutdown-runtime.js)
|
|
330
373
|
- `createGracefulShutdownRuntime(dependencies)`: creates graceful shutdown runtime for managed OpenCode and web server teardown sequencing.
|
|
374
|
+
- After stopping owned runtimes and OpenCode, HTTP shutdown closes active connections as well as the listener. A remaining SSE response must not hold Desktop open until its fallback deadline. Upgraded sockets remain the responsibility of their owning runtime.
|
|
331
375
|
- Returned API:
|
|
332
376
|
- `gracefulShutdown(options?)`
|
|
333
377
|
|
|
@@ -385,6 +429,20 @@ within a ten-minute overall deadline.
|
|
|
385
429
|
- Foreground servers running under a systemd user unit queue installation in
|
|
386
430
|
a separate transient unit and restart the configured service afterwards.
|
|
387
431
|
`OPENCHAMBER_SYSTEMD_UNIT` overrides the default `openchamber.service`.
|
|
432
|
+
- On Windows the install-and-restart script is written to
|
|
433
|
+
`<data dir>/update-install.cmd` before the response and run with
|
|
434
|
+
`cmd.exe /c <file>`. A newline ends a `cmd.exe /c` command line, so the
|
|
435
|
+
same script passed as an argument ran nothing and exited 0; the batch
|
|
436
|
+
file keeps every line. The package-manager line is `call`ed because
|
|
437
|
+
npm, pnpm and yarn are `.cmd` shims that would otherwise end the script,
|
|
438
|
+
the pre-install pause is a loopback `ping` because `timeout` rejects a
|
|
439
|
+
detached child's stdin, and the file deletes itself on its last line
|
|
440
|
+
because the restart command carries the server's flags. If the file
|
|
441
|
+
cannot be written the route answers 500 and the server keeps running.
|
|
442
|
+
The listener is closed before the batch is spawned: on Windows the
|
|
443
|
+
detached child inherits the listening socket and would hold the port
|
|
444
|
+
for the whole batch, so the restart inside it failed with "port already
|
|
445
|
+
in use" and the update ended with no server.
|
|
388
446
|
- `GET /api/openchamber/models-metadata`
|
|
389
447
|
- `GET /api/zen/models`
|
|
390
448
|
|
|
@@ -422,6 +480,16 @@ within a ten-minute overall deadline.
|
|
|
422
480
|
- Generic `/api/*` forwarding with hop-by-hop header filtering
|
|
423
481
|
- Windows `/session` merge fallback path behavior
|
|
424
482
|
- OpenCode readiness gate for proxied `/api` requests
|
|
483
|
+
- Worktree checkout gate before directory-scoped upstream reads and writes
|
|
484
|
+
|
|
485
|
+
Git bootstrap must reach `git-ready` before OpenCode can cache a new worktree's
|
|
486
|
+
project identity or config. Setup scripts may still be running; the optional UI
|
|
487
|
+
setup wait remains separate. Failed or timed-out checkout returns 503 without
|
|
488
|
+
forwarding. The shared draft creator keeps the project directory selected until
|
|
489
|
+
creation returns, because preview paths have no bootstrap state.
|
|
490
|
+
|
|
491
|
+
This server gate covers web, Electron, hosted mobile, and Capacitor connections.
|
|
492
|
+
The VS Code extension owns its separate Git and proxy implementation.
|
|
425
493
|
|
|
426
494
|
## Public exports (watcher.js)
|
|
427
495
|
- `createOpenCodeWatcherRuntime(dependencies)`: creates global event watcher runtime backed by the shared upstream SSE reader.
|
|
@@ -131,6 +131,7 @@ export const createBootstrapRuntime = (dependencies) => {
|
|
|
131
131
|
writeSseEvent,
|
|
132
132
|
getSessionActivitySnapshot: sessionRuntime.getSessionActivitySnapshot,
|
|
133
133
|
getSessionStateSnapshot: sessionRuntime.getSessionStateSnapshot,
|
|
134
|
+
getPendingBlockingRequestsSnapshot: sessionRuntime.getPendingBlockingRequestsSnapshot,
|
|
134
135
|
getSessionAttentionSnapshot: sessionRuntime.getSessionAttentionSnapshot,
|
|
135
136
|
getSessionState: sessionRuntime.getSessionState,
|
|
136
137
|
getSessionAttentionState: sessionRuntime.getSessionAttentionState,
|