@aiworker/web 1.22.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/cli.js +14 -4
- package/bin/cli.test.js +165 -73
- package/bin/lib/cli-api-target.js +2 -2
- package/bin/lib/cli-args.js +119 -119
- package/bin/lib/cli-lifecycle.js +9 -9
- package/bin/lib/cli-network.js +3 -3
- package/bin/lib/cli-paths.js +3 -3
- package/bin/lib/cli-ports.js +2 -2
- package/bin/lib/cli-process.js +4 -4
- package/bin/lib/cli-startup.js +64 -15
- package/bin/lib/cli-tunnel-profiles.js +1 -1
- package/bin/lib/cli-tunnel-utils.js +2 -2
- package/bin/lib/commands-connect-url.js +19 -19
- package/bin/lib/commands-lifecycle.js +27 -27
- package/bin/lib/commands-logs.js +5 -5
- package/bin/lib/commands-models.js +1 -1
- package/bin/lib/commands-projects.js +1 -1
- package/bin/lib/commands-schedule.js +1 -1
- package/bin/lib/commands-serve.js +29 -29
- package/bin/lib/commands-session.js +1 -1
- package/bin/lib/commands-startup.js +45 -10
- package/bin/lib/commands-status.js +1 -1
- package/bin/lib/commands-tunnel.js +40 -40
- package/bin/lib/commands-update.js +1 -1
- package/bin/lib/commands-update.test.js +6 -6
- package/dist/assets/{CodeMirrorCommentWidgets-B3u7UIcE.js → CodeMirrorCommentWidgets-B1Gizi3a.js} +1 -1
- package/dist/assets/DiffView-DXjVsEOw.js +4 -0
- package/dist/assets/DirtyBranchSwitchDialog-aig6zJjg.js +1 -0
- package/dist/assets/FilesView-CJdG0-wk.js +17 -0
- package/dist/assets/GitView-BXsS3jG6.js +6 -0
- package/dist/assets/{JsonTreeView-B0BdZEHG.js → JsonTreeView-D_hpXBnF.js} +1 -1
- package/dist/assets/LinearIssuesView-BoUiLQG1.js +1 -0
- package/dist/assets/MarkdownImageGallery-B_B_a9KS.js +1 -0
- package/dist/assets/MarkdownRendererImpl-xgyyLiBM.js +12 -0
- package/dist/assets/OnboardingScreen-DIp84OWt.js +1 -0
- package/dist/assets/{PatchDiff-DvfqDql7.js → PatchDiff-8p6nLJaU.js} +1 -1
- package/dist/assets/PierreDiffViewer-DjC8nTk9.js +161 -0
- package/dist/assets/PlanView-CJTmOF6I.js +5 -0
- package/dist/assets/SettingsView-DD-JqIJx.js +28 -0
- package/dist/assets/{SettingsWindow-i45U1Cy1.js → SettingsWindow-Cpb4hM6q.js} +1 -1
- package/dist/assets/SymbolsNerdFontMono-Regular-aK5vsLov.woff2 +0 -0
- package/dist/assets/{ToolOutputDialog-BTwyh5OF.js → ToolOutputDialog-DfaKZXNU.js} +6 -6
- package/dist/assets/ToolPartDiffPreview-DtZLzrSK.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-DD8lDVej.js → VirtualizedFileDiff-C8i9HpTB.js} +1 -1
- package/dist/assets/WalkthroughView-DeKMvZlL.js +4 -0
- package/dist/assets/{computeVirtualFileMetrics-BMIRgMHv.js → computeVirtualFileMetrics-BBI7ZwrC.js} +2 -2
- package/dist/assets/contextPanelEmbeddedChat-BdA6bdky.js +1 -0
- package/dist/assets/de-DCrsQ5zS.js +10 -0
- package/dist/assets/es-Dwv4YfH6.js +16 -0
- package/dist/assets/fr-BCWg4Cnx.js +14 -0
- package/dist/assets/ghostty-vt-DdA0Zryv.wasm +0 -0
- package/dist/assets/index-B68h5S5B.css +1 -0
- package/dist/assets/index-DVwMarHI.js +39 -0
- package/dist/assets/{index-CODwDsU_.js → index-jXSK5RrB.js} +1 -1
- package/dist/assets/{isDiffPlainText-BqayB8d3.js → isDiffPlainText-B_x_rVon.js} +1 -1
- package/dist/assets/{isFilePlainText-QfoiKJNY.js → isFilePlainText-DW_xMer1.js} +1 -1
- package/dist/assets/ja-Bruw3RYC.js +16 -0
- package/dist/assets/ko-idhXGF8j.js +16 -0
- package/dist/assets/main-BkyaQhJX.js +930 -0
- package/dist/assets/{main-C5DVOxc6.js → main-CzdPuL0s.js} +2 -2
- package/dist/assets/markdownCore-Cn4Tyejo.js +5 -0
- package/dist/assets/{miniChat-DUJ4irZc.js → miniChat-CCkqKOLp.js} +2 -2
- package/dist/assets/{mobile-CcaxB0n4.js → mobile-mvxTo055.js} +2 -2
- package/dist/assets/patchFileDiff-Csk4iNUi.js +13 -0
- package/dist/assets/pl-CGZWUqZJ.js +16 -0
- package/dist/assets/pt-BR-B8V7u7tA.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-BxoyujA5.js +2 -0
- package/dist/assets/renderMobileApp-B48CsP4r.js +25 -0
- package/dist/assets/runtimeEndpointReset-Bg5gx18H.js +17 -0
- package/dist/assets/scheduledTasksApi-BW0Etzwz.js +1 -0
- package/dist/assets/{shikiHighlight-DYOWuY65.js → shikiHighlight-CRl0SUC9.js} +1 -1
- package/dist/assets/surface-oMvhsog3.js +1 -0
- package/dist/assets/tr-BK_KiO2V.js +7 -0
- package/dist/assets/uk-CG74xV8N.js +16 -0
- package/dist/assets/useAppFontEffects-CPKeOpUo.js +7284 -0
- package/dist/assets/useGitComparison-DH220WmO.js +1 -0
- package/dist/assets/useInlineCommentController-BiWz4AVb.js +2 -0
- package/dist/assets/useRootScrollLock-BS4-kr7P.js +1 -0
- package/dist/assets/{vendor-base-ui-BeMD8X_w.js → vendor-base-ui-DdV8vhHn.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-data-DFZik9R2.js → vendor-codemirror-language-data-Bg4AEeCr.js} +2 -2
- package/dist/assets/vendor-zod-BPAANqvH.js +39 -0
- package/dist/assets/{web-components-Bhg83tjf.js → web-components-z7hfnS8n.js} +1 -1
- package/dist/assets/zh-CN-T4AXofZI.js +16 -0
- package/dist/assets/zh-TW-Dte40sxp.js +16 -0
- package/dist/index.html +21 -59
- package/dist/mini-chat.html +7 -7
- package/dist/mobile.html +5 -5
- package/package.json +3 -4
- package/server/index.d.ts +15 -0
- package/server/index.js +158 -89
- package/server/lib/agent-memory/actions.js +2 -2
- package/server/lib/agent-memory/feature-flag.js +1 -1
- package/server/lib/agent-memory/feature-flag.test.js +8 -8
- package/server/lib/agent-memory/routes.js +2 -2
- package/server/lib/agent-tool/runtime.js +37 -54
- package/server/lib/agent-tool/runtime.test.js +59 -59
- package/server/lib/browser-control/broker.js +3 -3
- package/server/lib/browser-control/broker.test.js +1 -1
- package/server/lib/changelog/update-notes.js +82 -0
- package/server/lib/changelog/update-notes.test.js +59 -0
- package/server/lib/client-auth/remote-clients.test.js +5 -5
- package/server/lib/context-obligatory/runtime.js +7 -7
- package/server/lib/context-obligatory/runtime.test.js +5 -5
- package/server/lib/data-dir-migration.js +40 -0
- package/server/lib/data-dir-migration.test.js +59 -0
- package/server/lib/dev-servers/parse.js +2 -2
- package/server/lib/dev-servers/parse.test.js +2 -2
- package/server/lib/dev-servers/routes.js +1 -1
- package/server/lib/dev-tunnel/DOCUMENTATION.md +13 -4
- package/server/lib/dev-tunnel/client.js +1 -1
- package/server/lib/dev-tunnel/runtime.js +5 -5
- package/server/lib/dev-tunnel/tunnel.test.js +21 -1
- package/server/lib/dictation/local/model-catalog.js +1 -1
- package/server/lib/dictation/local/worker-process.js +2 -2
- package/server/lib/event-stream/directory-ws-bridge.js +1 -1
- package/server/lib/event-stream/global-ws-bridge.js +1 -1
- package/server/lib/event-stream/protocol.test.js +4 -4
- package/server/lib/event-stream/runtime.test.js +18 -18
- package/server/lib/fs/DOCUMENTATION.md +5 -0
- package/server/lib/fs/routes.js +68 -26
- package/server/lib/fs/routes.test.js +208 -22
- package/server/lib/git/DOCUMENTATION.md +32 -6
- package/server/lib/git/identity-storage.js +1 -1
- package/server/lib/git/issue-2746-longpaths.test.js +4 -4
- package/server/lib/git/routes.js +41 -5
- package/server/lib/git/service.js +300 -224
- package/server/lib/git/service.test.js +596 -28
- package/server/lib/github/auth.js +8 -8
- package/server/lib/github/pr-status.js +1 -1
- package/server/lib/github/routes.js +2 -2
- package/server/lib/inherited-env.js +17 -10
- package/server/lib/inherited-env.test.js +10 -11
- package/server/lib/linear/auth.js +8 -8
- package/server/lib/linear/auth.test.js +24 -24
- package/server/lib/linear/issues.test.js +6 -6
- package/server/lib/linear/mapping.test.js +4 -4
- package/server/lib/linear/oauth.js +2 -2
- package/server/lib/linear/oauth.test.js +14 -14
- package/server/lib/linear/routes.js +5 -5
- package/server/lib/linear/routes.test.js +16 -16
- package/server/lib/linear/status-runtime.test.js +9 -9
- package/server/lib/linear/status.js +1 -1
- package/server/lib/linear/status.test.js +15 -15
- package/server/lib/linear/teams.test.js +4 -4
- package/server/lib/magic-prompts/routes.js +2 -2
- package/server/lib/mcp-reconnect/DOCUMENTATION.md +55 -0
- package/server/lib/mcp-reconnect/runtime.js +122 -0
- package/server/lib/mcp-reconnect/runtime.test.js +140 -0
- package/server/lib/message-queue/DOCUMENTATION.md +161 -0
- package/server/lib/message-queue/runtime.js +870 -0
- package/server/lib/message-queue/runtime.test.js +482 -0
- package/server/lib/network-defaults.js +23 -0
- package/server/lib/network-defaults.test.js +52 -0
- package/server/lib/notifications/apns-runtime.js +17 -17
- package/server/lib/notifications/apns-runtime.test.js +11 -11
- package/server/lib/notifications/emitter-runtime.js +1 -1
- package/server/lib/notifications/emitter-runtime.test.js +1 -1
- package/server/lib/notifications/push-runtime.js +7 -7
- package/server/lib/notifications/routes.js +1 -1
- package/server/lib/notifications/runtime.js +1 -1
- package/server/lib/openchamber-control/action-resolution.test.js +10 -10
- package/server/lib/openchamber-control/actions.js +24 -24
- package/server/lib/openchamber-control/error.js +4 -4
- package/server/lib/openchamber-control/routes.js +2 -2
- package/server/lib/openchamber-control/routes.test.js +5 -5
- package/server/lib/openchamber-control/screenshots.js +1 -1
- package/server/lib/openchamber-control/screenshots.test.js +1 -1
- package/server/lib/openchamber-control/service.js +50 -50
- package/server/lib/openchamber-control/service.test.js +5 -5
- package/server/lib/openchamber-sessions/routes.js +124 -22
- package/server/lib/openchamber-sessions/routes.test.js +99 -11
- package/server/lib/opencode/DOCUMENTATION.md +30 -6
- package/server/lib/opencode/auth.js +1 -1
- package/server/lib/opencode/bootstrap-runtime.js +8 -6
- package/server/lib/opencode/cli-options.js +8 -8
- package/server/lib/opencode/config-paths.test.js +116 -0
- package/server/lib/opencode/core-routes.js +8 -7
- package/server/lib/opencode/core-routes.test.js +11 -11
- package/server/lib/opencode/env-config.js +5 -5
- package/server/lib/opencode/env-config.test.js +7 -7
- package/server/lib/opencode/env-runtime.js +10 -10
- package/server/lib/opencode/env-runtime.test.js +18 -18
- package/server/lib/opencode/feature-routes-runtime.js +19 -12
- package/server/lib/opencode/hmr-state-runtime.js +2 -2
- package/server/lib/opencode/hmr-state-runtime.test.js +1 -1
- package/server/lib/opencode/input-history-scope.js +14 -0
- package/server/lib/opencode/lifecycle.js +8 -8
- package/server/lib/opencode/lifecycle.test.js +4 -4
- package/server/lib/opencode/managed-plugin-config.js +35 -0
- package/server/lib/opencode/managed-process-registry.js +3 -3
- package/server/lib/opencode/mcp-oauth-callback.test.js +2 -2
- package/server/lib/opencode/openchamber-routes.js +79 -19
- package/server/lib/opencode/openchamber-routes.test.js +139 -12
- package/server/lib/opencode/plugins.js +2 -2
- package/server/lib/opencode/plugins.test.js +1 -1
- package/server/lib/opencode/project-icon-routes.js +2 -2
- package/server/lib/opencode/project-icon-routes.test.js +1 -1
- package/server/lib/opencode/providers.js +44 -2
- package/server/lib/opencode/providers.test.js +131 -4
- package/server/lib/opencode/proxy.js +2 -2
- package/server/lib/opencode/pwa-manifest-routes.js +1 -1
- package/server/lib/opencode/restart-session-recovery.test.js +1 -1
- package/server/lib/opencode/routes-upgrade.test.js +4 -4
- package/server/lib/opencode/routes.js +19 -17
- package/server/lib/opencode/server-startup-runtime.js +6 -6
- package/server/lib/opencode/session-runtime.js +3 -3
- package/server/lib/opencode/session-runtime.test.js +6 -6
- package/server/lib/opencode/settings-files.js +214 -0
- package/server/lib/opencode/settings-helpers.js +116 -44
- package/server/lib/opencode/settings-helpers.test.js +354 -12
- package/server/lib/opencode/settings-registry.json +747 -0
- package/server/lib/opencode/settings-runtime.js +141 -20
- package/server/lib/opencode/settings-runtime.test.js +191 -4
- package/server/lib/opencode/shared.js +5 -2
- package/server/lib/opencode/shared.test.js +7 -7
- package/server/lib/opencode/shutdown-runtime.js +2 -0
- package/server/lib/opencode/skill-routes.js +2 -1
- package/server/lib/opencode/snippets.js +1 -2
- package/server/lib/opencode/startup-performance.js +1 -1
- package/server/lib/opencode/startup-performance.test.js +6 -6
- package/server/lib/opencode/static-routes-runtime.js +8 -8
- package/server/lib/opencode/static-routes-runtime.test.js +7 -7
- package/server/lib/opencode/upgrade-capability.js +2 -2
- package/server/lib/opencode/upgrade-capability.test.js +3 -3
- package/server/lib/package-manager.js +17 -42
- package/server/lib/package-manager.test.js +19 -19
- package/server/lib/permission-auto-accept/runtime.js +1 -1
- package/server/lib/project-context/DOCUMENTATION.md +25 -1
- package/server/lib/project-context/routes.http.test.js +18 -0
- package/server/lib/project-context/routes.js +16 -1
- package/server/lib/project-context/runtime.js +248 -23
- package/server/lib/project-context/runtime.test.js +106 -3
- package/server/lib/projects/DOCUMENTATION.md +76 -0
- package/server/lib/projects/project-config.js +144 -11
- package/server/lib/projects/project-id.js +17 -0
- package/server/lib/projects/project-setup.js +409 -0
- package/server/lib/projects/project-setup.test.js +557 -0
- package/server/lib/projects/routes.js +59 -0
- package/server/lib/quota/DOCUMENTATION.md +40 -2
- package/server/lib/quota/credentials/providers.js +4 -0
- package/server/lib/quota/credentials/store.js +4 -4
- package/server/lib/quota/credentials/store.test.js +8 -8
- package/server/lib/quota/index.js +1 -0
- package/server/lib/quota/providers/cline-pass.js +125 -0
- package/server/lib/quota/providers/cline-pass.test.js +103 -0
- package/server/lib/quota/providers/exe-dev.js +75 -0
- package/server/lib/quota/providers/exe-dev.test.js +47 -0
- package/server/lib/quota/providers/hyper.js +118 -0
- package/server/lib/quota/providers/hyper.test.js +126 -0
- package/server/lib/quota/providers/index.js +22 -0
- package/server/lib/quota/providers/neuralwatt.js +3 -6
- package/server/lib/quota/providers/neuralwatt.test.js +7 -7
- package/server/lib/quota/providers/ollama-cloud.js +32 -1
- package/server/lib/quota/providers/ollama-cloud.test.js +33 -1
- package/server/lib/quota/providers/opencode-go.js +3 -2
- package/server/lib/quota/providers/opencode-go.test.js +35 -6
- package/server/lib/quota/providers/openrouter.js +119 -24
- package/server/lib/quota/providers/openrouter.test.js +330 -0
- package/server/lib/quota/providers/xai.js +1 -1
- package/server/lib/quota/routes.js +2 -0
- package/server/lib/quota/utils/auth.js +1 -1
- package/server/lib/relay/DOCUMENTATION.md +56 -1
- package/server/lib/relay/cross-compat.test.js +26 -0
- package/server/lib/relay/downstream-scheduler.js +154 -0
- package/server/lib/relay/downstream-scheduler.test.js +162 -0
- package/server/lib/relay/e2ee.js +10 -8
- package/server/lib/relay/flow-control.test.js +177 -0
- package/server/lib/relay/host-client.js +38 -9
- package/server/lib/relay/host-client.test.js +9 -1
- package/server/lib/relay/host-lock.js +1 -1
- package/server/lib/relay/service.js +5 -5
- package/server/lib/relay/tunnel-codec.js +9 -0
- package/server/lib/relay/tunnel-host.js +69 -18
- package/server/lib/relay/tunnel-host.test.js +9 -1
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +2 -2
- package/server/lib/scheduled-tasks/routes.js +5 -5
- package/server/lib/scheduled-tasks/service.js +25 -25
- package/server/lib/scheduled-tasks/service.test.js +1 -1
- package/server/lib/security/bind-host.js +4 -4
- package/server/lib/security/bind-host.test.js +1 -1
- package/server/lib/security/request-security.js +11 -5
- package/server/lib/security/request-security.test.js +14 -14
- package/server/lib/session-assist/runtime.js +15 -18
- package/server/lib/session-folders/routes.js +2 -2
- package/server/lib/session-folders/routes.test.js +8 -8
- package/server/lib/session-goal/DOCUMENTATION.md +20 -7
- package/server/lib/session-goal/create.js +4 -2
- package/server/lib/session-goal/create.test.js +1 -1
- package/server/lib/session-goal/objectives.js +4 -4
- package/server/lib/session-goal/routes.js +1 -1
- package/server/lib/session-goal/runtime.js +91 -30
- package/server/lib/session-goal/runtime.test.js +428 -2
- package/server/lib/session-knowledge/runtime.js +11 -11
- package/server/lib/session-knowledge/runtime.test.js +5 -5
- package/server/lib/skills-catalog/DOCUMENTATION.md +1 -1
- package/server/lib/skills-catalog/cache.test.js +2 -2
- package/server/lib/skills-catalog/disk-cache.js +5 -5
- package/server/lib/skills-catalog/github-meta.test.js +2 -2
- package/server/lib/skills-catalog/install.js +3 -2
- package/server/lib/small-model/DOCUMENTATION.md +7 -2
- package/server/lib/small-model/call.js +18 -7
- package/server/lib/small-model/call.test.js +58 -6
- package/server/lib/small-model/index.js +12 -16
- package/server/lib/small-model/index.test.js +1 -1
- package/server/lib/small-model/resolve.js +1 -1
- package/server/lib/small-model/resolve.test.js +1 -1
- package/server/lib/small-model/routes.js +2 -1
- package/server/lib/small-model/runtime-providers.js +15 -7
- package/server/lib/small-model/runtime-providers.test.js +7 -1
- package/server/lib/system-prompt/runtime.js +3 -22
- package/server/lib/system-prompt/runtime.test.js +3 -3
- package/server/lib/terminal/DOCUMENTATION.md +14 -10
- package/server/lib/terminal/runtime.js +190 -31
- package/server/lib/terminal/runtime.test.js +572 -38
- package/server/lib/terminal/shells.js +20 -3
- package/server/lib/terminal/shells.test.js +33 -2
- package/server/lib/tts/base-url.js +3 -3
- package/server/lib/tts/routes.test.js +20 -20
- package/server/lib/ui-auth/ui-auth.js +12 -9
- package/server/lib/ui-auth/ui-auth.test.js +19 -3
- package/server/lib/ui-auth/ui-passkeys.js +7 -7
- package/server/lib/walkthrough/DOCUMENTATION.md +33 -14
- package/server/lib/walkthrough/index.js +1 -0
- package/server/lib/walkthrough/jobs.test.js +1 -1
- package/server/lib/walkthrough/language.test.js +1 -1
- package/server/lib/walkthrough/model-settings.js +11 -15
- package/server/lib/walkthrough/model-settings.test.js +1 -1
- package/server/lib/walkthrough/prompt.js +3 -1
- package/server/lib/walkthrough/pull-request.test.js +5 -5
- package/server/lib/walkthrough/reproduce-2607.test.js +2 -2
- package/server/lib/walkthrough/routes.test.js +26 -5
- package/server/lib/walkthrough/sources.js +23 -2
- package/server/lib/walkthrough/store.js +3 -3
- package/server/lib/walkthrough/store.test.js +1 -1
- package/server/opencode-proxy.test.js +2 -2
- package/server/proxy-headers.js +1 -1
- package/server/sse-routes.test.js +4 -4
- package/README.md.bak +0 -250
- package/bin/lib/DOCUMENTATION.md.bak +0 -163
- package/dist/assets/CommitSection-SGBCAW0w.js +0 -1
- package/dist/assets/DiffView-DluiRSla.js +0 -4
- package/dist/assets/FilesView-Bdq8ppva.js +0 -17
- package/dist/assets/GitView-Znn7VJcP.js +0 -6
- package/dist/assets/LinearIssuesView-jP-hXyac.js +0 -1
- package/dist/assets/MarkdownImageGallery-ClOvuZxC.js +0 -1
- package/dist/assets/MarkdownRendererImpl-Dt9mjTRN.js +0 -12
- package/dist/assets/OnboardingScreen-D2DG7K6b.js +0 -1
- package/dist/assets/PierreDiffViewer-B7m3f0d7.js +0 -157
- package/dist/assets/PlanView-B0F3BtPZ.js +0 -5
- package/dist/assets/SettingsView-BBW-S-gh.js +0 -28
- package/dist/assets/ToolPartDiffPreview-CUNe28U1.js +0 -8
- package/dist/assets/WalkthroughView-egQgxel3.js +0 -4
- package/dist/assets/contextPanelEmbeddedChat-C3fjDbdg.js +0 -1
- package/dist/assets/de-C1P6X5Rs.js +0 -9
- package/dist/assets/es-CDgaF1_t.js +0 -15
- package/dist/assets/fr-Bgxqvvz_.js +0 -13
- package/dist/assets/index-C57jT0Sa.css +0 -1
- package/dist/assets/index-DW_E3rnL.js +0 -7
- package/dist/assets/ja-BDRrEcNP.js +0 -15
- package/dist/assets/ko-Ba8sQV79.js +0 -15
- package/dist/assets/main-Bv5Pkngh.js +0 -928
- package/dist/assets/markdownCore-CZwzz5gw.js +0 -3
- package/dist/assets/patchFileDiff-yNfWXN4E.js +0 -10
- package/dist/assets/pl-Dioz29BI.js +0 -15
- package/dist/assets/pt-BR-DIs6ElwV.js +0 -15
- package/dist/assets/renderElectronMiniChatApp-BcKLHXKe.js +0 -2
- package/dist/assets/renderMobileApp-D9yQJy0L.js +0 -25
- package/dist/assets/runtimeEndpointReset-CjhvxLH0.js +0 -16
- package/dist/assets/scheduledTasksApi-DLW0mkQK.js +0 -1
- package/dist/assets/tr-DFnMlPxY.js +0 -6
- package/dist/assets/uk-ZrCNjM7Y.js +0 -15
- package/dist/assets/useAppFontEffects-CjrntdL0.js +0 -7310
- package/dist/assets/useInlineCommentController-CDEuSlPH.js +0 -2
- package/dist/assets/useRootScrollLock-BHYPkrLv.js +0 -1
- package/dist/assets/vendor-ghostty-web-CrxinGGH.js +0 -13
- package/dist/assets/vendor-zod-BLfBSgBh.js +0 -39
- package/dist/assets/zh-CN-iv_AV4EC.js +0 -15
- package/dist/assets/zh-TW-CzEUk38j.js +0 -15
- package/server/index.d.ts.bak +0 -39
- package/server/lib/agent-tool/DOCUMENTATION.md.bak +0 -119
- package/server/lib/browser-control/DOCUMENTATION.md.bak +0 -50
- package/server/lib/context-obligatory/DOCUMENTATION.md.bak +0 -19
- package/server/lib/dev-tunnel/DOCUMENTATION.md.bak +0 -57
- package/server/lib/dictation/DOCUMENTATION.md.bak +0 -100
- package/server/lib/event-stream/DOCUMENTATION.md.bak +0 -62
- package/server/lib/fs/DOCUMENTATION.md.bak +0 -47
- package/server/lib/git/DOCUMENTATION.md.bak +0 -193
- package/server/lib/github/DOCUMENTATION.md.bak +0 -183
- package/server/lib/linear/DOCUMENTATION.md.bak +0 -97
- package/server/lib/linear/OAUTH-BROKER-HANDOFF.md.bak +0 -91
- package/server/lib/markdown-image-grants/DOCUMENTATION.md.bak +0 -40
- package/server/lib/notifications/APNS.md.bak +0 -135
- package/server/lib/notifications/DOCUMENTATION.md.bak +0 -138
- package/server/lib/openchamber-control/DOCUMENTATION.md.bak +0 -64
- package/server/lib/opencode/DOCUMENTATION.md.bak +0 -431
- package/server/lib/opencode/managed-process-registry.d.ts.bak +0 -13
- package/server/lib/opencode/provider-env-aliases.d.ts.bak +0 -3
- package/server/lib/permission-auto-accept/DOCUMENTATION.md.bak +0 -34
- package/server/lib/project-context/DOCUMENTATION.md.bak +0 -160
- package/server/lib/quota/DOCUMENTATION.md.bak +0 -123
- package/server/lib/relay/DOCUMENTATION.md.bak +0 -100
- package/server/lib/scheduled-tasks/DOCUMENTATION.md.bak +0 -182
- package/server/lib/session-assist/DOCUMENTATION.md.bak +0 -66
- package/server/lib/session-goal/DOCUMENTATION.md.bak +0 -213
- package/server/lib/session-knowledge/DOCUMENTATION.md.bak +0 -89
- package/server/lib/skills-catalog/DOCUMENTATION.md.bak +0 -151
- package/server/lib/small-model/DOCUMENTATION.md.bak +0 -212
- package/server/lib/system-prompt/DOCUMENTATION.md.bak +0 -36
- package/server/lib/terminal/DOCUMENTATION.md.bak +0 -50
- package/server/lib/text/DOCUMENTATION.md.bak +0 -35
- package/server/lib/tts/DOCUMENTATION.md.bak +0 -147
- package/server/lib/tunnels/DOCUMENTATION.md.bak +0 -21
- package/server/lib/ui-auth/DOCUMENTATION.md.bak +0 -44
- package/server/lib/walkthrough/DOCUMENTATION.md.bak +0 -411
|
@@ -91,6 +91,10 @@ other runtime API.
|
|
|
91
91
|
a blocker instead of a raw 500 message.
|
|
92
92
|
- `call.js` — wire formats and per-provider auth, replicating OpenCode's
|
|
93
93
|
plugin auth loaders:
|
|
94
|
+
- OpenCode-hosted providers receive `x-opencode-session`. Session-backed
|
|
95
|
+
features reuse the real OpenCode session id, walkthrough retries reuse the
|
|
96
|
+
walkthrough cache key, and standalone one-shot actions receive a fresh
|
|
97
|
+
opaque id for that generation.
|
|
94
98
|
- **GitHub Copilot**: fetches the requested model's authenticated `/models`
|
|
95
99
|
metadata from `https://api.githubcopilot.com` (or
|
|
96
100
|
`copilot-api.<enterprise>`) and honors its advertised endpoint, preferring
|
|
@@ -113,8 +117,9 @@ other runtime API.
|
|
|
113
117
|
- Everything else: OpenAI-compatible `/chat/completions` against the
|
|
114
118
|
provider's base URL, resolved from (1) `provider.<id>.options.baseURL`
|
|
115
119
|
in the OpenCode config, (2) the hardcoded `https://api.openai.com/v1`
|
|
116
|
-
endpoint, (3) the endpoint OpenCode resolved at runtime,
|
|
117
|
-
|
|
120
|
+
endpoint, (3) the selected model's endpoint OpenCode resolved at runtime,
|
|
121
|
+
(4) the provider-level runtime endpoint, or (5) the provider's `api` field
|
|
122
|
+
from the models.dev catalog. The credential follows
|
|
118
123
|
the same shape: config `options.apiKey`, then the runtime credential, then
|
|
119
124
|
the auth.json entry. `provider.<id>.options.headers` is sent with the
|
|
120
125
|
request and overrides the bearer default, so gateways that authenticate on
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
4
5
|
import { readAuthFile, writeAuthFile } from '../opencode/auth.js';
|
|
5
6
|
import { readConfig, readConfigLayers, isPlainObject } from '../opencode/shared.js';
|
|
6
7
|
import { getCatalogProvider } from './catalog.js';
|
|
@@ -17,7 +18,7 @@ const COPILOT_MODELS_TIMEOUT_MS = 5_000;
|
|
|
17
18
|
// Qwen, …) spend part of this budget on reasoning before the actual answer.
|
|
18
19
|
const DEFAULT_MAX_OUTPUT_TOKENS = 4_000;
|
|
19
20
|
|
|
20
|
-
const USER_AGENT = 'opencode/1.0
|
|
21
|
+
const USER_AGENT = 'opencode/1.0 aiworker';
|
|
21
22
|
|
|
22
23
|
const mergeHeadersCaseInsensitive = (base, overrides) => {
|
|
23
24
|
const merged = { ...base };
|
|
@@ -605,6 +606,8 @@ const readProviderConfig = (workingDirectory, providerID) => {
|
|
|
605
606
|
}
|
|
606
607
|
}
|
|
607
608
|
|
|
609
|
+
const getRuntimeModel = (runtimeProvider, modelID) => runtimeProvider?.models?.get(modelID) ?? null;
|
|
610
|
+
|
|
608
611
|
// ---------------------------------------------------------------------------
|
|
609
612
|
// Dispatch
|
|
610
613
|
// ---------------------------------------------------------------------------
|
|
@@ -645,10 +648,11 @@ export async function resolveProviderLogin({ auth, workingDirectory, providerID
|
|
|
645
648
|
|| null;
|
|
646
649
|
}
|
|
647
650
|
|
|
648
|
-
export async function callSmallModel({ auth, catalog, workingDirectory, providerID, modelID, prompt, system, maxOutputTokens, responseSchema, timeoutMs, signal }) {
|
|
651
|
+
export async function callSmallModel({ auth, catalog, workingDirectory, sessionID, providerID, modelID, prompt, system, maxOutputTokens, responseSchema, timeoutMs, signal }) {
|
|
649
652
|
const tokens = Number(maxOutputTokens) > 0 ? Number(maxOutputTokens) : DEFAULT_MAX_OUTPUT_TOKENS;
|
|
650
653
|
const providerConfig = readProviderConfig(workingDirectory, providerID);
|
|
651
654
|
const runtimeProvider = await getRuntimeProvider(providerID);
|
|
655
|
+
const runtimeModel = getRuntimeModel(runtimeProvider, modelID);
|
|
652
656
|
// Match OpenCode's resolveSDK precedence: config `provider.<id>.options`
|
|
653
657
|
// wins, then what OpenCode itself resolved at runtime (the only place a
|
|
654
658
|
// plugin's credential exists), and the auth.json entry last.
|
|
@@ -758,9 +762,10 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
|
|
|
758
762
|
// base URL for that provider (openai itself included). When a custom provider
|
|
759
763
|
// is not in the catalog (e.g. a user-configured OpenAI-compatible proxy),
|
|
760
764
|
// fall back to its baseURL from the OpenCode provider config, then to the
|
|
761
|
-
// endpoint OpenCode resolved at runtime
|
|
762
|
-
//
|
|
763
|
-
//
|
|
765
|
+
// selected model's endpoint OpenCode resolved at runtime, then to the
|
|
766
|
+
// provider-level runtime endpoint. For a plugin provider, the runtime listing
|
|
767
|
+
// is the only place those endpoints exist, and several are local proxies the
|
|
768
|
+
// plugin itself runs. The openai provider also respects
|
|
764
769
|
// provider.openai.options.baseURL — OpenCode itself uses the same config for
|
|
765
770
|
// all providers including openai.
|
|
766
771
|
const provider = getCatalogProvider(catalog, providerID);
|
|
@@ -770,7 +775,8 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
|
|
|
770
775
|
? providerConfigUrl
|
|
771
776
|
: providerID === 'openai'
|
|
772
777
|
? defaultOpenaiUrl
|
|
773
|
-
:
|
|
778
|
+
: runtimeModel?.api?.url
|
|
779
|
+
?? runtimeProvider?.baseURL
|
|
774
780
|
?? (typeof provider?.api === 'string' && provider.api
|
|
775
781
|
? provider.api
|
|
776
782
|
: null);
|
|
@@ -795,7 +801,12 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
|
|
|
795
801
|
baseURL,
|
|
796
802
|
// Configured headers last: a gateway that authenticates on its own header
|
|
797
803
|
// must be able to override the bearer default rather than sit beside it.
|
|
798
|
-
headers: mergeHeadersCaseInsensitive(
|
|
804
|
+
headers: mergeHeadersCaseInsensitive(
|
|
805
|
+
mergeHeadersCaseInsensitive({ Authorization: `Bearer ${apiKey}` }, providerConfig?.headers),
|
|
806
|
+
providerID.startsWith('opencode')
|
|
807
|
+
? { 'x-opencode-session': typeof sessionID === 'string' && sessionID.trim() ? sessionID.trim() : randomUUID() }
|
|
808
|
+
: null,
|
|
809
|
+
),
|
|
799
810
|
modelID,
|
|
800
811
|
prompt,
|
|
801
812
|
system,
|
|
@@ -69,8 +69,8 @@ describe('callSmallModel — custom provider config', () => {
|
|
|
69
69
|
afterEach(() => {
|
|
70
70
|
globalThis.fetch = originalFetch;
|
|
71
71
|
vi.restoreAllMocks();
|
|
72
|
-
delete process.env.
|
|
73
|
-
delete process.env.
|
|
72
|
+
delete process.env.AIWORKER_TEST_PROVIDER_KEY;
|
|
73
|
+
delete process.env.AIWORKER_TEST_GATEWAY_KEY;
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
describe('config-supplied credentials (no auth.json entry)', () => {
|
|
@@ -104,11 +104,11 @@ describe('callSmallModel — custom provider config', () => {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
it('resolves an OpenCode environment variable before sending the API key', async () => {
|
|
107
|
-
process.env.
|
|
107
|
+
process.env.AIWORKER_TEST_PROVIDER_KEY = 'sk-env-key';
|
|
108
108
|
readConfig.mockReturnValue({
|
|
109
109
|
provider: {
|
|
110
110
|
custom: {
|
|
111
|
-
options: { apiKey: '{env:
|
|
111
|
+
options: { apiKey: '{env:AIWORKER_TEST_PROVIDER_KEY}', baseURL: 'https://proxy.example.test/v1' },
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
114
|
});
|
|
@@ -127,7 +127,7 @@ describe('callSmallModel — custom provider config', () => {
|
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
it('sends configured provider headers alongside the bearer token', async () => {
|
|
130
|
-
process.env.
|
|
130
|
+
process.env.AIWORKER_TEST_GATEWAY_KEY = 'sub-key';
|
|
131
131
|
readConfig.mockReturnValue({
|
|
132
132
|
provider: {
|
|
133
133
|
custom: {
|
|
@@ -135,7 +135,7 @@ describe('callSmallModel — custom provider config', () => {
|
|
|
135
135
|
apiKey: 'sk-config',
|
|
136
136
|
baseURL: 'https://proxy.example.test/v1',
|
|
137
137
|
headers: {
|
|
138
|
-
'Ocp-Apim-Subscription-Key': '{env:
|
|
138
|
+
'Ocp-Apim-Subscription-Key': '{env:AIWORKER_TEST_GATEWAY_KEY}',
|
|
139
139
|
'x-tenant': 'team',
|
|
140
140
|
},
|
|
141
141
|
},
|
|
@@ -483,6 +483,29 @@ describe('callSmallModel — custom provider config', () => {
|
|
|
483
483
|
});
|
|
484
484
|
|
|
485
485
|
describe('catalog-based base URL (no config override)', () => {
|
|
486
|
+
it('identifies OpenCode Go requests with the owning conversation', async () => {
|
|
487
|
+
readConfig.mockReturnValue({});
|
|
488
|
+
fetchMock.mockResolvedValue(ok('ok'));
|
|
489
|
+
|
|
490
|
+
await callSmallModel({
|
|
491
|
+
auth: { 'opencode-go': { type: 'api', key: 'go-key' } },
|
|
492
|
+
catalog: {
|
|
493
|
+
'opencode-go': {
|
|
494
|
+
id: 'opencode-go',
|
|
495
|
+
api: 'https://opencode.ai/zen/go/v1',
|
|
496
|
+
models: { utility: { id: 'utility' } },
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
workingDirectory: '/proj',
|
|
500
|
+
sessionID: 'ses_conversation',
|
|
501
|
+
providerID: 'opencode-go',
|
|
502
|
+
modelID: 'utility',
|
|
503
|
+
prompt: 'hi',
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
expect(lastCall(fetchMock).init.headers['x-opencode-session']).toBe('ses_conversation');
|
|
507
|
+
});
|
|
508
|
+
|
|
486
509
|
it('uses the catalog api field when no config baseURL is set', async () => {
|
|
487
510
|
readConfig.mockReturnValue({});
|
|
488
511
|
fetchMock.mockResolvedValue(ok('ok'));
|
|
@@ -542,6 +565,35 @@ describe('callSmallModel — custom provider config', () => {
|
|
|
542
565
|
expect(init.headers.Authorization).toBe('Bearer plugin-key');
|
|
543
566
|
});
|
|
544
567
|
|
|
568
|
+
it('uses the selected runtime model endpoint', async () => {
|
|
569
|
+
readConfig.mockReturnValue({});
|
|
570
|
+
getRuntimeProvider.mockResolvedValue({
|
|
571
|
+
id: 'runtime-provider',
|
|
572
|
+
apiKey: 'plugin-key',
|
|
573
|
+
baseURL: 'https://runtime-provider/v1beta',
|
|
574
|
+
models: new Map([
|
|
575
|
+
['first-model', { api: { url: 'https://runtime-provider/v1beta', npm: '@ai-sdk/google' } }],
|
|
576
|
+
['selected-model', { api: { url: 'https://runtime-provider/v1', npm: '@ai-sdk/openai' } }],
|
|
577
|
+
]),
|
|
578
|
+
anonymousZen: false,
|
|
579
|
+
});
|
|
580
|
+
fetchMock.mockResolvedValue(ok('done'));
|
|
581
|
+
|
|
582
|
+
await callSmallModel({
|
|
583
|
+
auth: {},
|
|
584
|
+
catalog: {},
|
|
585
|
+
workingDirectory: '/proj',
|
|
586
|
+
providerID: 'runtime-provider',
|
|
587
|
+
modelID: 'selected-model',
|
|
588
|
+
prompt: 'hi',
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
const { url, init } = lastCall(fetchMock);
|
|
592
|
+
expect(url).toBe('https://runtime-provider/v1/chat/completions');
|
|
593
|
+
expect(url).not.toContain('/v1beta');
|
|
594
|
+
expect(JSON.parse(init.body).model).toBe('selected-model');
|
|
595
|
+
});
|
|
596
|
+
|
|
545
597
|
it('keeps the ChatGPT-plan login on its own transport instead of the runtime key', async () => {
|
|
546
598
|
readConfig.mockReturnValue({});
|
|
547
599
|
// OpenCode reports an OAuth access token as `options.apiKey` for openai;
|
|
@@ -6,6 +6,7 @@ import { readConfigLayers } from '../opencode/shared.js';
|
|
|
6
6
|
import { getModelCatalog } from './catalog.js';
|
|
7
7
|
import { resolveSmallModel, parseModelRef, isUsableAuthEntry, getAuthEntryForProvider } from './resolve.js';
|
|
8
8
|
import { DEDICATED_WIRE_FORMAT_PROVIDERS, callSmallModel, resolveProviderLogin } from './call.js';
|
|
9
|
+
import { readMergedSettingsSync } from '../opencode/settings-files.js';
|
|
9
10
|
import { getRuntimeProviderSnapshot } from './runtime-providers.js';
|
|
10
11
|
|
|
11
12
|
// Never a small model, whatever the transport looks like. A plugin can publish
|
|
@@ -16,26 +17,20 @@ import { getRuntimeProviderSnapshot } from './runtime-providers.js';
|
|
|
16
17
|
// conditional on an endpoint existing.
|
|
17
18
|
const CLAUDE_CODE_PROVIDER = 'claude-code';
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
-
process.env.
|
|
21
|
-
? path.resolve(process.env.
|
|
22
|
-
: path.join(os.homedir(), '.config', '
|
|
20
|
+
const AIWORKER_SETTINGS_FILE = path.join(
|
|
21
|
+
process.env.AIWORKER_DATA_DIR
|
|
22
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
23
|
+
: path.join(os.homedir(), '.config', 'aiworker'),
|
|
23
24
|
'settings.json',
|
|
24
25
|
);
|
|
25
26
|
|
|
26
|
-
//
|
|
27
|
+
// AiWorker's own settings: when the user unchecks "use default small model"
|
|
27
28
|
// their explicit override outranks every other resolution step.
|
|
28
29
|
const readSmallModelSettingsOverride = () => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (settings.smallModelUseDefault !== false) return null;
|
|
34
|
-
const override = typeof settings.smallModelOverride === 'string' ? settings.smallModelOverride.trim() : '';
|
|
35
|
-
return override || null;
|
|
36
|
-
} catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
30
|
+
const settings = readMergedSettingsSync({ fs, path, settingsFilePath: AIWORKER_SETTINGS_FILE });
|
|
31
|
+
if (settings.smallModelUseDefault !== false) return null;
|
|
32
|
+
const override = typeof settings.smallModelOverride === 'string' ? settings.smallModelOverride.trim() : '';
|
|
33
|
+
return override || null;
|
|
39
34
|
};
|
|
40
35
|
|
|
41
36
|
// Rough safety clamp so a huge input never blows the model's context window.
|
|
@@ -102,7 +97,7 @@ const readConfiguredSmallModel = (workingDirectory) => {
|
|
|
102
97
|
* Generates text with the user's small model, resolved and authenticated
|
|
103
98
|
* entirely server-side from the OpenCode config and auth store.
|
|
104
99
|
*/
|
|
105
|
-
export async function generateSmallModelText({ prompt, system, maxOutputTokens, model, directory, preferredProviderID, preferredModelID, restrictToPreferredProvider = false, responseSchema, timeoutMs, signal, onOverflow = 'truncate' }) {
|
|
100
|
+
export async function generateSmallModelText({ prompt, system, maxOutputTokens, model, directory, sessionID, preferredProviderID, preferredModelID, restrictToPreferredProvider = false, responseSchema, timeoutMs, signal, onOverflow = 'truncate' }) {
|
|
106
101
|
if (typeof prompt !== 'string' || !prompt.trim()) {
|
|
107
102
|
throw Object.assign(new Error('prompt is required'), { statusCode: 400 });
|
|
108
103
|
}
|
|
@@ -169,6 +164,7 @@ export async function generateSmallModelText({ prompt, system, maxOutputTokens,
|
|
|
169
164
|
auth,
|
|
170
165
|
catalog,
|
|
171
166
|
workingDirectory: directory,
|
|
167
|
+
sessionID,
|
|
172
168
|
providerID: resolved.providerID,
|
|
173
169
|
modelID: resolved.modelID,
|
|
174
170
|
prompt: clamped.prompt,
|
|
@@ -7,7 +7,7 @@ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vites
|
|
|
7
7
|
// this the suite would resolve whatever small model the developer running it
|
|
8
8
|
// happens to have configured.
|
|
9
9
|
const TEMP_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'small-model-settings-'));
|
|
10
|
-
process.env.
|
|
10
|
+
process.env.AIWORKER_DATA_DIR = TEMP_DATA_DIR;
|
|
11
11
|
|
|
12
12
|
vi.mock('../opencode/auth.js', () => ({ readAuthFile: vi.fn() }));
|
|
13
13
|
vi.mock('../opencode/shared.js', () => ({
|
|
@@ -76,7 +76,7 @@ const pickWithinProvider = (providerID, auth, catalog, family) => {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export function resolveSmallModel({ auth, catalog, settingsSmallModel, configSmallModel, preferredProviderID, preferredModelID }) {
|
|
79
|
-
//
|
|
79
|
+
// AiWorker's own setting (Settings → Sessions → Small Model override)
|
|
80
80
|
// outranks everything, including the OpenCode config.
|
|
81
81
|
const fromSettings = parseModelRef(settingsSmallModel);
|
|
82
82
|
if (fromSettings) {
|
|
@@ -57,7 +57,7 @@ describe('isUsableAuthEntry', () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
describe('resolveSmallModel', () => {
|
|
60
|
-
it('gives the
|
|
60
|
+
it('gives the AiWorker settings override top priority', () => {
|
|
61
61
|
const result = resolveSmallModel({
|
|
62
62
|
auth: { anthropic: { type: 'api', key: 'sk-x' } },
|
|
63
63
|
catalog,
|
|
@@ -21,13 +21,14 @@ export function registerSmallModelRoutes(app, { getSmallModelService }) {
|
|
|
21
21
|
app.post('/api/small-model/generate', async (req, res) => {
|
|
22
22
|
try {
|
|
23
23
|
const { generateSmallModelText } = await getSmallModelService();
|
|
24
|
-
const { prompt, system, maxOutputTokens, model, directory, preferredProviderID, preferredModelID, restrictToPreferredProvider } = req.body || {};
|
|
24
|
+
const { prompt, system, maxOutputTokens, model, directory, sessionID, preferredProviderID, preferredModelID, restrictToPreferredProvider } = req.body || {};
|
|
25
25
|
const result = await generateSmallModelText({
|
|
26
26
|
prompt,
|
|
27
27
|
system,
|
|
28
28
|
maxOutputTokens,
|
|
29
29
|
model,
|
|
30
30
|
directory,
|
|
31
|
+
sessionID,
|
|
31
32
|
preferredProviderID,
|
|
32
33
|
preferredModelID,
|
|
33
34
|
restrictToPreferredProvider: restrictToPreferredProvider === true,
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
//
|
|
9
9
|
// `GET /provider` is where that state becomes visible. It reports, per
|
|
10
10
|
// provider, the resolved `options.baseURL` and `options.apiKey`, and per model
|
|
11
|
-
// the wire adapter (`api.npm`) and endpoint (`api.url`).
|
|
11
|
+
// the wire adapter (`api.npm`) and endpoint (`api.url`). The provider-level
|
|
12
|
+
// endpoint remains only as a fallback when the selected model has no endpoint.
|
|
12
13
|
//
|
|
13
14
|
// What it does NOT report is `options.fetch`. OpenCode strips functions from
|
|
14
15
|
// the response, and a plugin is free to put its whole protocol in there:
|
|
@@ -61,8 +62,8 @@ export function resetOpenCodeRuntimeProviders() {
|
|
|
61
62
|
/**
|
|
62
63
|
* The boundary. Everything the `/provider` payload claims is checked here, so
|
|
63
64
|
* the rest of this module and its callers work with settled values:
|
|
64
|
-
* a credential we may use,
|
|
65
|
-
* anonymous zen case.
|
|
65
|
+
* a credential we may use, provider and model endpoints, and whether the
|
|
66
|
+
* provider is the anonymous zen case.
|
|
66
67
|
*
|
|
67
68
|
* The credential deliberately prefers `options.apiKey` over the `key` field:
|
|
68
69
|
* for a plugin provider the former is what its auth loader produced and what
|
|
@@ -82,13 +83,22 @@ function parseProviderListing(payload) {
|
|
|
82
83
|
const id = text(record(raw).id);
|
|
83
84
|
if (!id) continue;
|
|
84
85
|
const options = record(record(raw).options);
|
|
85
|
-
const
|
|
86
|
+
const models = new Map();
|
|
87
|
+
for (const [modelID, rawModel] of Object.entries(record(record(raw).models))) {
|
|
88
|
+
const model = record(rawModel);
|
|
89
|
+
const api = record(model.api);
|
|
90
|
+
const modelURL = endpoint(api.url);
|
|
91
|
+
const modelNpm = text(api.npm);
|
|
92
|
+
models.set(modelID, { api: { url: modelURL, npm: modelNpm } });
|
|
93
|
+
}
|
|
94
|
+
const firstModel = models.values().next().value;
|
|
86
95
|
const declaredKey = text(options.apiKey);
|
|
87
96
|
providers.set(id, {
|
|
88
97
|
id,
|
|
89
98
|
source: text(record(raw).source),
|
|
90
99
|
apiKey: declaredKey === ZEN_ANONYMOUS_API_KEY ? null : (declaredKey ?? text(record(raw).key)),
|
|
91
|
-
baseURL: endpoint(options.baseURL) ??
|
|
100
|
+
baseURL: endpoint(options.baseURL) ?? firstModel?.api?.url ?? null,
|
|
101
|
+
models,
|
|
92
102
|
// True only for the zen-without-login case: a provider that is present
|
|
93
103
|
// and usable through OpenCode, but that we must not call ourselves.
|
|
94
104
|
anonymousZen: declaredKey === ZEN_ANONYMOUS_API_KEY,
|
|
@@ -152,5 +162,3 @@ export async function getRuntimeProvider(providerID) {
|
|
|
152
162
|
const current = await getRuntimeProviderSnapshot();
|
|
153
163
|
return current?.providers.get(providerID) ?? null;
|
|
154
164
|
}
|
|
155
|
-
|
|
156
|
-
|
|
@@ -14,7 +14,10 @@ const providerPayload = (overrides = {}) => ({
|
|
|
14
14
|
id: 'llmapi',
|
|
15
15
|
source: 'config',
|
|
16
16
|
options: { apiKey: 'plugin-key', baseURL: 'https://api.llmapi.ai/v1/' },
|
|
17
|
-
models: {
|
|
17
|
+
models: {
|
|
18
|
+
'claude-opus-4-8': { api: { id: 'claude-opus-4-8', url: '', npm: '@ai-sdk/anthropic' } },
|
|
19
|
+
'gpt-5.6-luna': { api: { id: 'gpt-5.6-luna', url: 'https://api.llmapi.ai/v1', npm: '@ai-sdk/openai' } },
|
|
20
|
+
},
|
|
18
21
|
},
|
|
19
22
|
{
|
|
20
23
|
id: 'opencode',
|
|
@@ -59,6 +62,9 @@ describe('OpenCode runtime provider snapshot', () => {
|
|
|
59
62
|
const provider = await getRuntimeProvider('llmapi');
|
|
60
63
|
|
|
61
64
|
expect(provider).toMatchObject({ apiKey: 'plugin-key', baseURL: 'https://api.llmapi.ai/v1' });
|
|
65
|
+
expect(provider.models.get('gpt-5.6-luna')).toEqual({
|
|
66
|
+
api: { url: 'https://api.llmapi.ai/v1', npm: '@ai-sdk/openai' },
|
|
67
|
+
});
|
|
62
68
|
expect(fetchMock.mock.calls[0][0]).toBe('http://127.0.0.1:4096/provider');
|
|
63
69
|
expect(fetchMock.mock.calls[0][1].headers).toMatchObject({ Authorization: 'Basic test' });
|
|
64
70
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { parse as parseJsonc } from 'jsonc-parser';
|
|
2
1
|
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { appendManagedPlugin } from '../opencode/managed-plugin-config.js';
|
|
3
3
|
|
|
4
4
|
const PROVIDER_PROMPT_BOUNDARY = 'You are powered by the model named';
|
|
5
5
|
const MINIMAL_IDENTITY = 'You are OpenCode, a coding agent.';
|
|
@@ -9,7 +9,7 @@ const PROVIDER_PROMPT_BOUNDARY = ${JSON.stringify(PROVIDER_PROMPT_BOUNDARY)}
|
|
|
9
9
|
const MINIMAL_IDENTITY = ${JSON.stringify(MINIMAL_IDENTITY)}
|
|
10
10
|
const optimizedSessions = new Map()
|
|
11
11
|
|
|
12
|
-
export const
|
|
12
|
+
export const AiWorkerSystemPromptPlugin = async () => ({
|
|
13
13
|
"chat.message": async (input, output) => {
|
|
14
14
|
if (!input.sessionID) return
|
|
15
15
|
const agent = output?.message?.agent ?? input.agent
|
|
@@ -33,25 +33,6 @@ export const OpenChamberSystemPromptPlugin = async () => ({
|
|
|
33
33
|
})
|
|
34
34
|
`;
|
|
35
35
|
|
|
36
|
-
const mergePluginConfig = (rawConfig, pluginUrl) => {
|
|
37
|
-
const errors = [];
|
|
38
|
-
const parsed = typeof rawConfig === 'string' && rawConfig.trim()
|
|
39
|
-
? parseJsonc(rawConfig, errors, { allowTrailingComma: true })
|
|
40
|
-
: {};
|
|
41
|
-
if (errors.length > 0 || !parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
42
|
-
throw new Error('OPENCODE_CONFIG_CONTENT must contain a valid JSON object before OpenChamber can inject its system prompt optimizer');
|
|
43
|
-
}
|
|
44
|
-
if (parsed.plugin !== undefined && !Array.isArray(parsed.plugin)) {
|
|
45
|
-
throw new Error('OPENCODE_CONFIG_CONTENT plugin must be an array before OpenChamber can inject its system prompt optimizer');
|
|
46
|
-
}
|
|
47
|
-
const configured = Array.isArray(parsed.plugin) ? parsed.plugin : [];
|
|
48
|
-
parsed.plugin = [
|
|
49
|
-
...configured.filter((value) => value !== pluginUrl && (!Array.isArray(value) || value[0] !== pluginUrl)),
|
|
50
|
-
pluginUrl,
|
|
51
|
-
];
|
|
52
|
-
return JSON.stringify(parsed);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
36
|
export const createSystemPromptRuntime = ({ fsPromises, path, dataDir }) => {
|
|
56
37
|
const pluginDirectory = path.join(dataDir, 'system-prompt');
|
|
57
38
|
const pluginPath = path.join(pluginDirectory, 'openchamber-system-prompt-plugin.js');
|
|
@@ -60,7 +41,7 @@ export const createSystemPromptRuntime = ({ fsPromises, path, dataDir }) => {
|
|
|
60
41
|
await fsPromises.mkdir(pluginDirectory, { recursive: true });
|
|
61
42
|
await fsPromises.writeFile(pluginPath, createPluginSource(), { mode: 0o600 });
|
|
62
43
|
return {
|
|
63
|
-
OPENCODE_CONFIG_CONTENT:
|
|
44
|
+
OPENCODE_CONFIG_CONTENT: appendManagedPlugin(rawConfig, pathToFileURL(pluginPath).href, 'system prompt optimizer'),
|
|
64
45
|
};
|
|
65
46
|
};
|
|
66
47
|
|
|
@@ -25,7 +25,7 @@ describe('managed system prompt runtime', () => {
|
|
|
25
25
|
expect(config.model).toBe('test/model');
|
|
26
26
|
expect(config.plugin).toEqual(['file:///existing.js', pathToFileURL(pluginPath).href]);
|
|
27
27
|
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
|
|
28
|
-
const hooks = await pluginModule.
|
|
28
|
+
const hooks = await pluginModule.AiWorkerSystemPromptPlugin();
|
|
29
29
|
const output = {
|
|
30
30
|
system: ['Behavioral prompt\nYou are powered by the model named GPT.\n<env>kept</env>'],
|
|
31
31
|
};
|
|
@@ -46,7 +46,7 @@ describe('managed system prompt runtime', () => {
|
|
|
46
46
|
await runtime.prepareManagedOpenCodeEnv('{}');
|
|
47
47
|
const pluginPath = path.join(dataDir, 'system-prompt', 'openchamber-system-prompt-plugin.js');
|
|
48
48
|
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
|
|
49
|
-
const hooks = await pluginModule.
|
|
49
|
+
const hooks = await pluginModule.AiWorkerSystemPromptPlugin();
|
|
50
50
|
const output = { system: ['Unrecognized prompt'] };
|
|
51
51
|
await hooks['chat.message']({ sessionID: 'session-1', agent: 'plan' });
|
|
52
52
|
await hooks['experimental.chat.system.transform']({ sessionID: 'session-1' }, output);
|
|
@@ -60,7 +60,7 @@ describe('managed system prompt runtime', () => {
|
|
|
60
60
|
await runtime.prepareManagedOpenCodeEnv('{}');
|
|
61
61
|
const pluginPath = path.join(dataDir, 'system-prompt', 'openchamber-system-prompt-plugin.js');
|
|
62
62
|
const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
|
|
63
|
-
const hooks = await pluginModule.
|
|
63
|
+
const hooks = await pluginModule.AiWorkerSystemPromptPlugin();
|
|
64
64
|
const output = {
|
|
65
65
|
system: ['Custom agent prompt\nYou are powered by the model named GPT.\n<env>kept</env>'],
|
|
66
66
|
};
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Ownership
|
|
4
4
|
|
|
5
|
-
`runtime.js` owns terminal identity, PTY processes,
|
|
5
|
+
`runtime.js` owns terminal identity, PTY processes, launch mode, session purpose, ordered output, bounded scrollback, WebSocket attachments, and lifecycle routes. `shells.js` discovers executable shell families, resolves the persisted shell ID, and builds the per-shell argv for interactive versus command launches. Clients own tab arrangement. Interactive terminals use stable IDs; project actions keep a stable UI tab and allocate a fresh terminal ID for each command execution. Electron uses this same runtime in-process; VS Code returns an explicit unsupported error.
|
|
6
6
|
|
|
7
7
|
## Protocol
|
|
8
8
|
|
|
9
9
|
`/api/terminal/ws` is the only terminal data transport. It uses v3 binary JSON control frames and is opened through `openRuntimeWebSocket`, preserving direct, Electron proxy, URL-token authentication, and private-relay routing.
|
|
10
10
|
|
|
11
11
|
- `attach` registers a connection for one terminal. One socket may attach to many terminals.
|
|
12
|
-
- Every attach and reconnect begins with an authoritative `snapshot` containing bounded history and the
|
|
12
|
+
- Every attach and reconnect begins with an authoritative `snapshot` containing bounded history, the current sequence, and the PTY `cols`/`rows` the history was drawn for. The client replays the history at that size before fitting its viewport; replaying shell output at another width leaves stray fragments that the shell's own SIGWINCH redraw never clears.
|
|
13
13
|
- A current socket that closes or errors before its initial `open` invalidates its URL-scoped auth token before retrying, so retries mint a fresh token instead of backing off against a rejected upgrade. Hidden or offline clients wait 60 seconds and wake promptly on visibility/online recovery.
|
|
14
14
|
- `output`, `exit`, and `restarted` carry monotonically increasing per-terminal sequences. Output carries raw live bytes plus replay-safe bytes with terminal query exchanges removed.
|
|
15
15
|
- Attach registers before capturing the snapshot, buffers concurrent events, drops events represented by the snapshot sequence, then enters live delivery.
|
|
@@ -19,21 +19,25 @@
|
|
|
19
19
|
|
|
20
20
|
HTTP remains the authenticated command plane for create, resize, appearance updates, restart, close, and force-kill. There is no SSE output or HTTP input compatibility path.
|
|
21
21
|
|
|
22
|
-
`GET /api/terminal/sessions` enumerates live sessions (optionally filtered by resolved `cwd`) so clients can adopt terminals their local tab projection does not know about — another device, a new browser tab, or cleared storage. `POST /api/terminal/touch` refreshes `lastActivity` for the listed session ids; open clients call it periodically so background tabs, which hold no WebSocket attachment, are not idle-reaped while a client still shows them.
|
|
22
|
+
`GET /api/terminal/sessions` enumerates live sessions (optionally filtered by resolved `cwd`) so clients can adopt terminals their local tab projection does not know about — another device, a new browser tab, or cleared storage. Listings include the effective launch mode and the normalized purpose, but never the command text. `POST /api/terminal/touch` refreshes `lastActivity` for the listed session ids; open clients call it periodically so background tabs, which hold no WebSocket attachment, are not idle-reaped while a client still shows them.
|
|
23
23
|
|
|
24
24
|
## PTY Lifecycle
|
|
25
25
|
|
|
26
26
|
- IDs are client-provided or generated with `randomUUID()`.
|
|
27
|
-
-
|
|
27
|
+
- Create defaults to interactive mode. Command mode requires a non-empty trimmed command no longer than the terminal input limit and launches the shell so the PTY exits when that command exits. Each session also carries a normalized purpose. Omitted purpose means `{ type: 'terminal' }`. Project actions use `{ type: 'project-action', actionId, executionId }`, and the server validates both IDs as non-empty bounded strings. Create responses and attach snapshots echo the effective mode and purpose.
|
|
28
|
+
- Concurrent creates for one ID are single-flight only when working directory, shell preference, login mode, launch mode, and session purpose match. Command-mode creates must also match the command text, unless the purpose is a project action that is already running for the same resolved `(cwd, actionId)` pair. In that case the runtime returns the existing session and its existing execution identity, even when another client requested a different session ID. Existing IDs cannot be reused for another working directory or another purpose.
|
|
28
29
|
- Dimensions are bounded to 1-1000 columns and 1-500 rows; input is capped at 64 KiB.
|
|
29
30
|
- A client may create before its renderer has mounted. It derives an initial size from the container and font metrics (falling back to 80x24 when unavailable), then sends a resize once Ghostty reports its final dimensions. This allows shell startup and renderer initialization to overlap.
|
|
30
|
-
- PTY children
|
|
31
|
-
- PTY children also strip AppImage `ARGV0` (and other host-private shell vars such as `ELECTRON_RUN_AS_NODE`, `BASH_ENV`, `ENV`, `BASH_XTRACEFD`). An exported `ARGV0` makes zsh rewrite argv[0] for every external command, which breaks Python venv detection and other argv[0]/$0 consumers while leaving `/proc/self/exe` correct. On
|
|
32
|
-
- `GET /api/terminal/shells` reports shell IDs available on the active server using the same augmented PATH provided to spawned PTYs, plus whether each executable has a supported login-mode argument. `auto` preserves environment/platform fallback order; an explicit unavailable shell fails creation instead of silently running a different shell. Login mode is opt-in and uses only built-in arguments for known shells. Preference changes affect new sessions and explicit restarts, not running PTYs.
|
|
33
|
-
- PTY data and exit callbacks enter one FIFO queue.
|
|
31
|
+
- PTY children never inherit `NODE_CHANNEL_FD`; daemon IPC descriptors are host-private and invalid after PTY descriptor cleanup, and an inherited value (even empty) makes Node CLIs such as opencode print `Failed to parse IPC channel number` on exit.
|
|
32
|
+
- PTY children also strip AppImage `ARGV0` (and other host-private shell vars such as `ELECTRON_RUN_AS_NODE`, `BASH_ENV`, `ENV`, `BASH_XTRACEFD`). An exported `ARGV0` makes zsh rewrite argv[0] for every external command, which breaks Python venv detection and other argv[0]/$0 consumers while leaving `/proc/self/exe` correct. On POSIX, PTY spawn is wrapped with `env -u ARGV0 -u NODE_CHANNEL_FD` (`resolvePosixPtyLaunch`) because `bun-pty` merges the native OS environ and would otherwise reintroduce both after a JS-only delete.
|
|
33
|
+
- `GET /api/terminal/shells` reports shell IDs available on the active server using the same augmented PATH provided to spawned PTYs, plus whether each executable has a supported login-mode argument. `auto` preserves environment/platform fallback order; an explicit unavailable shell fails creation instead of silently running a different shell. Login mode is opt-in and uses only built-in arguments for known shells. Interactive shells still launch as before. Command-mode launches reuse the same environment and login support, but switch argv by shell family: POSIX and Fish use interactive `-c`, Nushell uses `-c`, PowerShell uses `-Command`, and cmd uses `/d /s /c`. Preference changes affect new sessions and explicit restarts, not running PTYs.
|
|
34
|
+
- PTY data and exit callbacks enter one FIFO queue. The runtime wires those listeners in the same synchronous turn that receives the PTY object. `node-pty` and `bun-pty` both expose the PTY before dispatching registered callbacks. If a backend emitted exit before listener registration, this layer could not recover it, so the wiring stays adjacent to PTY creation.
|
|
34
35
|
- Scrollback is retained on the server and capped at 512 KiB with UTF-8-safe trimming. Device-status, device-attribute, cursor-position reply, and color-query exchanges are removed from replay history with incomplete control sequences carried across PTY chunks; live output remains byte-for-byte unchanged.
|
|
35
|
-
- Exited sessions remain attachable until explicit close or
|
|
36
|
-
-
|
|
36
|
+
- Exited sessions remain attachable until explicit close, idle cleanup, or a successful replacement of the same project action. Creating a replacement retires only exited records for the same resolved directory and action, after the new PTY starts. Failed creation preserves the old record and output. These replaced records do not exhaust the terminal capacity limit.
|
|
37
|
+
- Deduplicated create responses may describe another client's execution. Cancellation cleanup closes only the terminal ID allocated for the cancelled request; it never closes an adopted peer execution.
|
|
38
|
+
- Create and restart validate the working directory with a real `stat` and answer HTTP 400 `Invalid working directory` when it is not a directory. When the path does not exist at all (`ENOENT`/`ENOTDIR`, a worktree deleted outside AiWorker) the body also carries `code: "TERMINAL_CWD_MISSING"`. The client shows the failure without moving the session. The runtime never substitutes a parent directory on its own.
|
|
39
|
+
- Restarts are serialized per terminal. Each restart spawns and wires the replacement before terminating the old process, retaining the terminal ID. Command-mode sessions reject restart with HTTP 400 instead of silently turning into interactive shells with stale action metadata.
|
|
40
|
+
- A delete that arrives while create is still pending leaves a cancellation tombstone. When the PTY arrives, the runtime terminates it immediately, never inserts the session into the live map, and returns a create error while the delete still succeeds.
|
|
37
41
|
- Close uses SIGTERM with bounded SIGKILL escalation. Force-kill, idle cleanup, and runtime shutdown terminate process groups immediately where supported. Removal explicitly sends a fatal scoped closure and evicts client projections even when a PTY backend fails to emit `onExit`; attached terminals are not considered idle.
|
|
38
42
|
|
|
39
43
|
## Security And Relay
|