@aiworker/web 1.22.0 → 1.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/cli.js +7 -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-CSICpWQD.js} +1 -1
- package/dist/assets/{DiffView-DluiRSla.js → DiffView-hDBKesA_.js} +3 -3
- package/dist/assets/DirtyBranchSwitchDialog-C22HdWj4.js +1 -0
- package/dist/assets/FilesView-CdWzeOp9.js +17 -0
- package/dist/assets/GitView-VQQkYuUw.js +6 -0
- package/dist/assets/{JsonTreeView-B0BdZEHG.js → JsonTreeView-6lrhwD3q.js} +1 -1
- package/dist/assets/LinearIssuesView-Ba6Q9569.js +1 -0
- package/dist/assets/{MarkdownImageGallery-ClOvuZxC.js → MarkdownImageGallery-Czv_mhvS.js} +1 -1
- package/dist/assets/MarkdownRendererImpl-BrMcswiy.js +12 -0
- package/dist/assets/{OnboardingScreen-D2DG7K6b.js → OnboardingScreen-ChxQoAkx.js} +1 -1
- package/dist/assets/{PatchDiff-DvfqDql7.js → PatchDiff-BzcZPlOq.js} +1 -1
- package/dist/assets/{PierreDiffViewer-B7m3f0d7.js → PierreDiffViewer-BchBtZlp.js} +2 -2
- package/dist/assets/PlanView-DyDt_nZ_.js +5 -0
- package/dist/assets/SettingsView-BKf5xBsP.js +28 -0
- package/dist/assets/{SettingsWindow-i45U1Cy1.js → SettingsWindow-DDQYaKWQ.js} +1 -1
- package/dist/assets/{ToolOutputDialog-BTwyh5OF.js → ToolOutputDialog-iYF8Y2bz.js} +6 -6
- package/dist/assets/ToolPartDiffPreview-CdjbOVMM.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-DD8lDVej.js → VirtualizedFileDiff-fFFm2QgH.js} +1 -1
- package/dist/assets/{WalkthroughView-egQgxel3.js → WalkthroughView-DOeCsMju.js} +2 -2
- package/dist/assets/{computeVirtualFileMetrics-BMIRgMHv.js → computeVirtualFileMetrics-CzgLum_K.js} +2 -2
- package/dist/assets/{contextPanelEmbeddedChat-C3fjDbdg.js → contextPanelEmbeddedChat-C0ZLAepL.js} +1 -1
- package/dist/assets/{de-C1P6X5Rs.js → de-B5JaA3SA.js} +6 -5
- package/dist/assets/{es-CDgaF1_t.js → es-EPIH88ii.js} +6 -5
- package/dist/assets/{fr-Bgxqvvz_.js → fr-CAMfxUdZ.js} +6 -5
- package/dist/assets/{index-CODwDsU_.js → index-Bl3LSbjn.js} +1 -1
- package/dist/assets/index-DF5kMP-i.js +39 -0
- package/dist/assets/index-Dl3xbAB_.css +1 -0
- package/dist/assets/{isDiffPlainText-BqayB8d3.js → isDiffPlainText-Dx-dB_8l.js} +1 -1
- package/dist/assets/{isFilePlainText-QfoiKJNY.js → isFilePlainText-CWFPwmnU.js} +1 -1
- package/dist/assets/{ja-BDRrEcNP.js → ja-CifOE1VN.js} +7 -6
- package/dist/assets/{ko-Ba8sQV79.js → ko-BBDyAyYQ.js} +7 -6
- package/dist/assets/{main-C5DVOxc6.js → main-DnBAoktx.js} +2 -2
- package/dist/assets/main-MhSaOZoB.js +930 -0
- package/dist/assets/{markdownCore-CZwzz5gw.js → markdownCore-BrBwOOMn.js} +2 -2
- package/dist/assets/{miniChat-DUJ4irZc.js → miniChat-BKyvLseN.js} +2 -2
- package/dist/assets/{mobile-CcaxB0n4.js → mobile-DKjeFgMN.js} +2 -2
- package/dist/assets/{patchFileDiff-yNfWXN4E.js → patchFileDiff-o8ms1me8.js} +1 -1
- package/dist/assets/pl-DgEHYfFM.js +16 -0
- package/dist/assets/{pt-BR-DIs6ElwV.js → pt-BR-DuWHojUw.js} +6 -5
- package/dist/assets/{renderElectronMiniChatApp-BcKLHXKe.js → renderElectronMiniChatApp-CDgBwwCS.js} +2 -2
- package/dist/assets/renderMobileApp-DJIKIK1r.js +25 -0
- package/dist/assets/runtimeEndpointReset-CYJ9O_xX.js +17 -0
- package/dist/assets/{scheduledTasksApi-DLW0mkQK.js → scheduledTasksApi-D8yamh1b.js} +1 -1
- package/dist/assets/{shikiHighlight-DYOWuY65.js → shikiHighlight-TEQCmzW8.js} +1 -1
- package/dist/assets/tr-D5m2yIWL.js +7 -0
- package/dist/assets/{uk-ZrCNjM7Y.js → uk-D_q60oog.js} +6 -5
- package/dist/assets/{useAppFontEffects-CjrntdL0.js → useAppFontEffects-D7sNpuL2.js} +161 -190
- package/dist/assets/useInlineCommentController-Brz_cLcQ.js +2 -0
- package/dist/assets/{useRootScrollLock-BHYPkrLv.js → useRootScrollLock-lXEnWlaU.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-data-DFZik9R2.js → vendor-codemirror-language-data-Dp3KMRQG.js} +2 -2
- package/dist/assets/vendor-zod-CK21ihK-.js +39 -0
- package/dist/assets/{web-components-Bhg83tjf.js → web-components-_mNEMjFY.js} +1 -1
- package/dist/assets/{zh-CN-iv_AV4EC.js → zh-CN-D8QvsVeg.js} +6 -5
- package/dist/assets/{zh-TW-CzEUk38j.js → zh-TW-B9t7rQQO.js} +6 -5
- package/dist/index.html +22 -22
- package/dist/mini-chat.html +7 -7
- package/dist/mobile.html +5 -5
- package/package.json +3 -3
- package/server/index.js +118 -85
- 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/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 +6 -6
- package/server/lib/fs/DOCUMENTATION.md +2 -0
- package/server/lib/fs/routes.js +37 -26
- package/server/lib/fs/routes.test.js +98 -21
- package/server/lib/git/DOCUMENTATION.md +3 -1
- 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 +17 -1
- package/server/lib/git/service.js +116 -26
- package/server/lib/git/service.test.js +174 -3
- 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 +1 -1
- package/server/lib/inherited-env.test.js +2 -2
- 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 +158 -0
- package/server/lib/message-queue/runtime.js +870 -0
- package/server/lib/message-queue/runtime.test.js +482 -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 +13 -5
- package/server/lib/opencode/auth.js +1 -1
- package/server/lib/opencode/bootstrap-runtime.js +6 -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 +17 -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 +15 -15
- package/server/lib/opencode/openchamber-routes.test.js +11 -11
- 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 +14 -14
- 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-helpers.js +31 -3
- package/server/lib/opencode/settings-helpers.test.js +189 -12
- package/server/lib/opencode/settings-runtime.js +2 -2
- package/server/lib/opencode/settings-runtime.test.js +29 -0
- 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/routes.js +1 -1
- package/server/lib/projects/project-config.js +2 -2
- package/server/lib/quota/DOCUMENTATION.md +9 -1
- 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/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 +15 -0
- package/server/lib/quota/providers/ollama-cloud.js +1 -1
- package/server/lib/quota/providers/opencode-go.js +2 -1
- package/server/lib/quota/providers/opencode-go.test.js +9 -5
- 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 +1 -1
- package/server/lib/relay/host-client.js +1 -1
- 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-host.js +3 -1
- 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 +10 -9
- 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 +88 -22
- 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 +4 -0
- package/server/lib/small-model/call.js +9 -3
- package/server/lib/small-model/call.test.js +29 -6
- package/server/lib/small-model/index.js +8 -7
- 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/system-prompt/runtime.js +3 -22
- package/server/lib/system-prompt/runtime.test.js +3 -3
- package/server/lib/terminal/DOCUMENTATION.md +11 -7
- package/server/lib/terminal/runtime.js +180 -26
- package/server/lib/terminal/runtime.test.js +554 -27
- 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/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 +3 -3
- package/server/lib/walkthrough/model-settings.test.js +1 -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/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/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/MarkdownRendererImpl-Dt9mjTRN.js +0 -12
- 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/index-C57jT0Sa.css +0 -1
- package/dist/assets/index-DW_E3rnL.js +0 -7
- package/dist/assets/main-Bv5Pkngh.js +0 -928
- package/dist/assets/pl-Dioz29BI.js +0 -15
- package/dist/assets/renderMobileApp-D9yQJy0L.js +0 -25
- package/dist/assets/runtimeEndpointReset-CjhvxLH0.js +0 -16
- package/dist/assets/tr-DFnMlPxY.js +0 -6
- package/dist/assets/useInlineCommentController-CDEuSlPH.js +0 -2
- package/dist/assets/vendor-zod-BLfBSgBh.js +0 -39
- 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
|
@@ -60,7 +60,7 @@ const buildMemoryBlock = ({ global, project }) => {
|
|
|
60
60
|
return [
|
|
61
61
|
'You have stored memory from earlier sessions. Only the titles are listed below.',
|
|
62
62
|
'A title is an abbreviation, not the memory. Read the entry with the'
|
|
63
|
-
+ '
|
|
63
|
+
+ ' aiworker_memory tool before you act on it: titles routinely leave out'
|
|
64
64
|
+ ' the conditions, exceptions and reasons that decide how the memory'
|
|
65
65
|
+ ' applies, and a title that looks self-explanatory is the most likely to'
|
|
66
66
|
+ ' be hiding them. Read every title that could bear on the task at hand;'
|
|
@@ -122,8 +122,8 @@ export const createSessionKnowledgeRuntime = (dependencies) => {
|
|
|
122
122
|
*/
|
|
123
123
|
const readPins = (session) => {
|
|
124
124
|
const metadata = isRecord(session?.metadata) ? session.metadata : {};
|
|
125
|
-
const
|
|
126
|
-
const pins = isRecord(
|
|
125
|
+
const aiworker = isRecord(metadata.aiworker) ? metadata.aiworker : {};
|
|
126
|
+
const pins = isRecord(aiworker[PINS_METADATA_KEY]) ? aiworker[PINS_METADATA_KEY] : {};
|
|
127
127
|
const strings = (value) => Array.isArray(value)
|
|
128
128
|
? [...new Set(value.filter((entry) => typeof entry === 'string' && entry.trim()).map((entry) => entry.trim()))]
|
|
129
129
|
: [];
|
|
@@ -229,8 +229,8 @@ export const createSessionKnowledgeRuntime = (dependencies) => {
|
|
|
229
229
|
|
|
230
230
|
const readDeliveredSignature = (session) => {
|
|
231
231
|
const metadata = isRecord(session?.metadata) ? session.metadata : {};
|
|
232
|
-
const
|
|
233
|
-
const delivered =
|
|
232
|
+
const aiworker = isRecord(metadata.aiworker) ? metadata.aiworker : {};
|
|
233
|
+
const delivered = aiworker[KNOWLEDGE_METADATA_KEY];
|
|
234
234
|
return typeof delivered === 'string' ? delivered : '';
|
|
235
235
|
};
|
|
236
236
|
|
|
@@ -267,7 +267,7 @@ export const createSessionKnowledgeRuntime = (dependencies) => {
|
|
|
267
267
|
const setPin = async (sessionId, directory, kind, id, pinned) => {
|
|
268
268
|
const fresh = await readSession(sessionId, directory);
|
|
269
269
|
const metadata = isRecord(fresh?.metadata) ? fresh.metadata : {};
|
|
270
|
-
const
|
|
270
|
+
const aiworker = isRecord(metadata.aiworker) ? metadata.aiworker : {};
|
|
271
271
|
const pins = readPins(fresh);
|
|
272
272
|
const key = kind === 'note' ? 'notes' : 'plans';
|
|
273
273
|
const next = new Set(pins[key]);
|
|
@@ -279,8 +279,8 @@ export const createSessionKnowledgeRuntime = (dependencies) => {
|
|
|
279
279
|
body: {
|
|
280
280
|
metadata: {
|
|
281
281
|
...metadata,
|
|
282
|
-
|
|
283
|
-
...
|
|
282
|
+
aiworker: {
|
|
283
|
+
...aiworker,
|
|
284
284
|
[PINS_METADATA_KEY]: { ...pins, [key]: [...next] },
|
|
285
285
|
[KNOWLEDGE_METADATA_KEY]: '',
|
|
286
286
|
},
|
|
@@ -296,20 +296,20 @@ export const createSessionKnowledgeRuntime = (dependencies) => {
|
|
|
296
296
|
* agent had context it never received.
|
|
297
297
|
*
|
|
298
298
|
* Merged onto a fresh read, because the session's metadata holds other
|
|
299
|
-
*
|
|
299
|
+
* AiWorker state — pinned messages among it — and a blind write would
|
|
300
300
|
* drop whatever changed in between.
|
|
301
301
|
*/
|
|
302
302
|
const recordDelivered = async (sessionId, directory, signature) => {
|
|
303
303
|
const fresh = await readSession(sessionId, directory);
|
|
304
304
|
const metadata = isRecord(fresh?.metadata) ? fresh.metadata : {};
|
|
305
|
-
const
|
|
305
|
+
const aiworker = isRecord(metadata.aiworker) ? metadata.aiworker : {};
|
|
306
306
|
await openCodeFetch(`/session/${encodeURIComponent(sessionId)}`, {
|
|
307
307
|
directory,
|
|
308
308
|
method: 'PATCH',
|
|
309
309
|
body: {
|
|
310
310
|
metadata: {
|
|
311
311
|
...metadata,
|
|
312
|
-
|
|
312
|
+
aiworker: { ...aiworker, [KNOWLEDGE_METADATA_KEY]: signature },
|
|
313
313
|
},
|
|
314
314
|
},
|
|
315
315
|
});
|
|
@@ -193,10 +193,10 @@ describe('reading what a session was told', () => {
|
|
|
193
193
|
const runtime = createRuntime();
|
|
194
194
|
|
|
195
195
|
expect(runtime.readPins({
|
|
196
|
-
metadata: {
|
|
196
|
+
metadata: { aiworker: { project_context_pins: { notes: ['n1'], plans: [] } } },
|
|
197
197
|
})).toEqual({ notes: ['n1'], plans: [] });
|
|
198
198
|
expect(runtime.readPins({
|
|
199
|
-
metadata: {
|
|
199
|
+
metadata: { aiworker: { project_context_pins: { notes: [], plans: ['p1'] } } },
|
|
200
200
|
})).toEqual({ notes: [], plans: ['p1'] });
|
|
201
201
|
expect(runtime.readPins({})).toEqual({ notes: [], plans: [] });
|
|
202
202
|
});
|
|
@@ -208,7 +208,7 @@ describe('reading what a session was told', () => {
|
|
|
208
208
|
requests.push({ path, options });
|
|
209
209
|
if (options.method === 'PATCH') return {};
|
|
210
210
|
return {
|
|
211
|
-
metadata: {
|
|
211
|
+
metadata: { aiworker: { project_context_pins: { notes: [], plans: [] }, knowledge_context_delivered: 'old' } },
|
|
212
212
|
};
|
|
213
213
|
},
|
|
214
214
|
});
|
|
@@ -216,7 +216,7 @@ describe('reading what a session was told', () => {
|
|
|
216
216
|
await runtime.setPin('ses_a', DIRECTORY, 'note', 'n1', true);
|
|
217
217
|
|
|
218
218
|
expect(requests.map((request) => request.path)).toEqual(['/session/ses_a', '/session/ses_a']);
|
|
219
|
-
expect(requests[1].options.body.metadata.
|
|
219
|
+
expect(requests[1].options.body.metadata.aiworker).toEqual({
|
|
220
220
|
project_context_pins: { notes: ['n1'], plans: [] },
|
|
221
221
|
knowledge_context_delivered: '',
|
|
222
222
|
});
|
|
@@ -226,7 +226,7 @@ describe('reading what a session was told', () => {
|
|
|
226
226
|
const runtime = createRuntime();
|
|
227
227
|
|
|
228
228
|
expect(runtime.readDeliveredSignature({
|
|
229
|
-
metadata: {
|
|
229
|
+
metadata: { aiworker: { knowledge_context_delivered: 'sig' } },
|
|
230
230
|
})).toBe('sig');
|
|
231
231
|
});
|
|
232
232
|
|
|
@@ -58,7 +58,7 @@ The following functions are internal helpers used by exported functions:
|
|
|
58
58
|
- `safeRm(dir)`: Safely remove directory recursively (ignores errors).
|
|
59
59
|
- `ensureDir(dirPath)`: Ensure directory exists with recursive creation.
|
|
60
60
|
- `copyDirectoryNoSymlinks(srcDir, dstDir)`: Copy directory contents without symlinks, with path traversal protection.
|
|
61
|
-
- `normalizeUserSkillDir(userSkillDir)`: Normalize user skill directory path (handles legacy `~/.config/opencode/skill`
|
|
61
|
+
- `normalizeUserSkillDir(userSkillDir)`: Normalize the user skill directory path (handles the legacy `skill` directory in the XDG config location, or `~/.config/opencode/skill` when XDG is unset, by selecting the plural `skills` directory when appropriate).
|
|
62
62
|
|
|
63
63
|
### Git Clone Helpers (`install.js`, `scan.js`)
|
|
64
64
|
- `cloneRepo({ cloneUrl, identity, tempDir })`: Clone git repository with preferred partial clone (`--filter=blob:none`) and fallback. Uses non-interactive mode.
|
|
@@ -8,11 +8,11 @@ let tempDataDir;
|
|
|
8
8
|
|
|
9
9
|
beforeEach(() => {
|
|
10
10
|
tempDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skills-cache-test-'));
|
|
11
|
-
process.env.
|
|
11
|
+
process.env.AIWORKER_DATA_DIR = tempDataDir;
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
afterEach(() => {
|
|
15
|
-
delete process.env.
|
|
15
|
+
delete process.env.AIWORKER_DATA_DIR;
|
|
16
16
|
clearCache();
|
|
17
17
|
vi.restoreAllMocks();
|
|
18
18
|
fs.rmSync(tempDataDir, { recursive: true, force: true });
|
|
@@ -2,9 +2,9 @@ import fs from 'fs';
|
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
5
|
-
const resolveDataDir = () => (process.env.
|
|
6
|
-
? path.resolve(process.env.
|
|
7
|
-
: path.join(os.homedir(), '.config', '
|
|
5
|
+
const resolveDataDir = () => (process.env.AIWORKER_DATA_DIR
|
|
6
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
7
|
+
: path.join(os.homedir(), '.config', 'aiworker'));
|
|
8
8
|
|
|
9
9
|
const readJsonFile = (filePath) => {
|
|
10
10
|
try {
|
|
@@ -17,7 +17,7 @@ const readJsonFile = (filePath) => {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Read a persisted cache object from the
|
|
20
|
+
* Read a persisted cache object from the AiWorker data directory.
|
|
21
21
|
* Returns null when the file is missing, unreadable, or malformed.
|
|
22
22
|
*/
|
|
23
23
|
export const readDiskCache = (fileName) => {
|
|
@@ -29,7 +29,7 @@ export const readDiskCache = (fileName) => {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Persist a cache object to the
|
|
32
|
+
* Persist a cache object to the AiWorker data directory with an atomic
|
|
33
33
|
* temp-file rename. Failures are ignored: the in-memory cache stays
|
|
34
34
|
* authoritative and the next successful write retries persistence.
|
|
35
35
|
*/
|
|
@@ -10,11 +10,11 @@ let tempDataDir;
|
|
|
10
10
|
|
|
11
11
|
beforeEach(() => {
|
|
12
12
|
tempDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'github-meta-test-'));
|
|
13
|
-
process.env.
|
|
13
|
+
process.env.AIWORKER_DATA_DIR = tempDataDir;
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
afterEach(() => {
|
|
17
|
-
delete process.env.
|
|
17
|
+
delete process.env.AIWORKER_DATA_DIR;
|
|
18
18
|
globalThis.fetch = originalFetch;
|
|
19
19
|
clearGitHubMetaCache();
|
|
20
20
|
vi.restoreAllMocks();
|
|
@@ -4,13 +4,14 @@ import path from 'path';
|
|
|
4
4
|
|
|
5
5
|
import { assertGitAvailable, looksLikeAuthError, runGit } from './git.js';
|
|
6
6
|
import { parseSkillRepoSource } from './source.js';
|
|
7
|
+
import { OPENCODE_CONFIG_DIR } from '../opencode/shared.js';
|
|
7
8
|
|
|
8
9
|
const SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
9
10
|
|
|
10
11
|
function normalizeUserSkillDir(userSkillDir) {
|
|
11
12
|
if (!userSkillDir) return null;
|
|
12
|
-
const legacySkillDir = path.join(
|
|
13
|
-
const pluralSkillDir = path.join(
|
|
13
|
+
const legacySkillDir = path.join(OPENCODE_CONFIG_DIR, 'skill');
|
|
14
|
+
const pluralSkillDir = path.join(OPENCODE_CONFIG_DIR, 'skills');
|
|
14
15
|
if (userSkillDir === legacySkillDir) {
|
|
15
16
|
if (fs.existsSync(legacySkillDir) && !fs.existsSync(pluralSkillDir)) return legacySkillDir;
|
|
16
17
|
return pluralSkillDir;
|
|
@@ -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
|
|
@@ -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 };
|
|
@@ -645,7 +646,7 @@ export async function resolveProviderLogin({ auth, workingDirectory, providerID
|
|
|
645
646
|
|| null;
|
|
646
647
|
}
|
|
647
648
|
|
|
648
|
-
export async function callSmallModel({ auth, catalog, workingDirectory, providerID, modelID, prompt, system, maxOutputTokens, responseSchema, timeoutMs, signal }) {
|
|
649
|
+
export async function callSmallModel({ auth, catalog, workingDirectory, sessionID, providerID, modelID, prompt, system, maxOutputTokens, responseSchema, timeoutMs, signal }) {
|
|
649
650
|
const tokens = Number(maxOutputTokens) > 0 ? Number(maxOutputTokens) : DEFAULT_MAX_OUTPUT_TOKENS;
|
|
650
651
|
const providerConfig = readProviderConfig(workingDirectory, providerID);
|
|
651
652
|
const runtimeProvider = await getRuntimeProvider(providerID);
|
|
@@ -795,7 +796,12 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
|
|
|
795
796
|
baseURL,
|
|
796
797
|
// Configured headers last: a gateway that authenticates on its own header
|
|
797
798
|
// must be able to override the bearer default rather than sit beside it.
|
|
798
|
-
headers: mergeHeadersCaseInsensitive(
|
|
799
|
+
headers: mergeHeadersCaseInsensitive(
|
|
800
|
+
mergeHeadersCaseInsensitive({ Authorization: `Bearer ${apiKey}` }, providerConfig?.headers),
|
|
801
|
+
providerID.startsWith('opencode')
|
|
802
|
+
? { 'x-opencode-session': typeof sessionID === 'string' && sessionID.trim() ? sessionID.trim() : randomUUID() }
|
|
803
|
+
: null,
|
|
804
|
+
),
|
|
799
805
|
modelID,
|
|
800
806
|
prompt,
|
|
801
807
|
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'));
|
|
@@ -16,18 +16,18 @@ import { getRuntimeProviderSnapshot } from './runtime-providers.js';
|
|
|
16
16
|
// conditional on an endpoint existing.
|
|
17
17
|
const CLAUDE_CODE_PROVIDER = 'claude-code';
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
process.env.
|
|
21
|
-
? path.resolve(process.env.
|
|
22
|
-
: path.join(os.homedir(), '.config', '
|
|
19
|
+
const AIWORKER_SETTINGS_FILE = path.join(
|
|
20
|
+
process.env.AIWORKER_DATA_DIR
|
|
21
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
22
|
+
: path.join(os.homedir(), '.config', 'aiworker'),
|
|
23
23
|
'settings.json',
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
//
|
|
26
|
+
// AiWorker's own settings: when the user unchecks "use default small model"
|
|
27
27
|
// their explicit override outranks every other resolution step.
|
|
28
28
|
const readSmallModelSettingsOverride = () => {
|
|
29
29
|
try {
|
|
30
|
-
const raw = fs.readFileSync(
|
|
30
|
+
const raw = fs.readFileSync(AIWORKER_SETTINGS_FILE, 'utf8');
|
|
31
31
|
const settings = JSON.parse(raw);
|
|
32
32
|
if (!settings || typeof settings !== 'object') return null;
|
|
33
33
|
if (settings.smallModelUseDefault !== false) return null;
|
|
@@ -102,7 +102,7 @@ const readConfiguredSmallModel = (workingDirectory) => {
|
|
|
102
102
|
* Generates text with the user's small model, resolved and authenticated
|
|
103
103
|
* entirely server-side from the OpenCode config and auth store.
|
|
104
104
|
*/
|
|
105
|
-
export async function generateSmallModelText({ prompt, system, maxOutputTokens, model, directory, preferredProviderID, preferredModelID, restrictToPreferredProvider = false, responseSchema, timeoutMs, signal, onOverflow = 'truncate' }) {
|
|
105
|
+
export async function generateSmallModelText({ prompt, system, maxOutputTokens, model, directory, sessionID, preferredProviderID, preferredModelID, restrictToPreferredProvider = false, responseSchema, timeoutMs, signal, onOverflow = 'truncate' }) {
|
|
106
106
|
if (typeof prompt !== 'string' || !prompt.trim()) {
|
|
107
107
|
throw Object.assign(new Error('prompt is required'), { statusCode: 400 });
|
|
108
108
|
}
|
|
@@ -169,6 +169,7 @@ export async function generateSmallModelText({ prompt, system, maxOutputTokens,
|
|
|
169
169
|
auth,
|
|
170
170
|
catalog,
|
|
171
171
|
workingDirectory: directory,
|
|
172
|
+
sessionID,
|
|
172
173
|
providerID: resolved.providerID,
|
|
173
174
|
modelID: resolved.modelID,
|
|
174
175
|
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,
|
|
@@ -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,7 +2,7 @@
|
|
|
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
|
|
|
@@ -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
31
|
- PTY children explicitly clear `NODE_CHANNEL_FD`; daemon IPC descriptors are host-private and invalid after PTY descriptor cleanup.
|
|
31
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 Linux, PTY spawn is wrapped with `env -u ARGV0` because `bun-pty` merges the native OS environ and would otherwise reintroduce `ARGV0` after a JS-only delete.
|
|
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.
|
|
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"`. That is the one rejection the client can recover from: the session, not the terminal, is stranded, and the shared UI moves it to its project directory and starts a terminal there. Every other rejection stays generic; 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
|