@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
|
@@ -15,7 +15,7 @@ export const registerScheduledTaskRoutes = (app, dependencies) => {
|
|
|
15
15
|
sanitizeProjects,
|
|
16
16
|
projectConfigRuntime,
|
|
17
17
|
scheduledTasksRuntime,
|
|
18
|
-
|
|
18
|
+
getAiWorkerEventClients,
|
|
19
19
|
writeSseEvent,
|
|
20
20
|
scheduledTaskService = createScheduledTaskService(dependencies),
|
|
21
21
|
} = dependencies;
|
|
@@ -149,14 +149,14 @@ export const registerScheduledTaskRoutes = (app, dependencies) => {
|
|
|
149
149
|
// not of this server: a desktop shell and a browser tab can be connected to
|
|
150
150
|
// the same server at once. Recording it on the connection keeps the answer
|
|
151
151
|
// current without any enable/disable setting to go stale.
|
|
152
|
-
res.
|
|
152
|
+
res.aiworkerBrowserCapable = req.query?.browser === '1';
|
|
153
153
|
|
|
154
|
-
const clients =
|
|
154
|
+
const clients = getAiWorkerEventClients();
|
|
155
155
|
clients.add(res);
|
|
156
156
|
|
|
157
157
|
try {
|
|
158
158
|
writeSseEvent(res, {
|
|
159
|
-
type: '
|
|
159
|
+
type: 'aiworker:event-stream-ready',
|
|
160
160
|
properties: {
|
|
161
161
|
connectedAt: Date.now(),
|
|
162
162
|
},
|
|
@@ -167,7 +167,7 @@ export const registerScheduledTaskRoutes = (app, dependencies) => {
|
|
|
167
167
|
const heartbeat = setInterval(() => {
|
|
168
168
|
try {
|
|
169
169
|
writeSseEvent(res, {
|
|
170
|
-
type: '
|
|
170
|
+
type: 'aiworker:heartbeat',
|
|
171
171
|
properties: {
|
|
172
172
|
timestamp: Date.now(),
|
|
173
173
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { AiWorkerControlError } from '../openchamber-control/error.js';
|
|
4
4
|
import { setLoopFileEnabled } from './loops.js';
|
|
5
5
|
|
|
6
6
|
const asNonEmptyString = (value) => {
|
|
@@ -24,10 +24,10 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
24
24
|
|
|
25
25
|
const findProjectByID = async (projectID) => {
|
|
26
26
|
const normalized = asNonEmptyString(projectID);
|
|
27
|
-
if (!normalized) throw new
|
|
27
|
+
if (!normalized) throw new AiWorkerControlError('projectId is required', 400);
|
|
28
28
|
const projects = await listProjects();
|
|
29
29
|
const project = projects.find((entry) => entry.id === normalized) || null;
|
|
30
|
-
if (!project) throw new
|
|
30
|
+
if (!project) throw new AiWorkerControlError('Project not found', 404);
|
|
31
31
|
return project;
|
|
32
32
|
};
|
|
33
33
|
|
|
@@ -35,17 +35,17 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
35
35
|
const requestedProjectID = asNonEmptyString(projectId);
|
|
36
36
|
const requestedDirectory = asNonEmptyString(directory);
|
|
37
37
|
if (requestedProjectID && requestedDirectory) {
|
|
38
|
-
throw new
|
|
38
|
+
throw new AiWorkerControlError('Provide only one of projectId or directory', 400);
|
|
39
39
|
}
|
|
40
40
|
if (requestedProjectID) {
|
|
41
41
|
await findProjectByID(requestedProjectID);
|
|
42
42
|
return requestedProjectID;
|
|
43
43
|
}
|
|
44
|
-
if (!requestedDirectory) throw new
|
|
44
|
+
if (!requestedDirectory) throw new AiWorkerControlError('projectId or directory is required', 400);
|
|
45
45
|
const resolvedDirectory = path.resolve(requestedDirectory);
|
|
46
46
|
const projects = await listProjects();
|
|
47
47
|
const project = projects.find((entry) => path.resolve(entry.path) === resolvedDirectory);
|
|
48
|
-
if (!project) throw new
|
|
48
|
+
if (!project) throw new AiWorkerControlError(`Project not found for directory: ${resolvedDirectory}`, 404);
|
|
49
49
|
return project.id;
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -57,28 +57,28 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
57
57
|
const findLoopTask = async (projectID, taskID) => {
|
|
58
58
|
await findProjectByID(projectID);
|
|
59
59
|
const normalizedTaskID = asNonEmptyString(taskID);
|
|
60
|
-
if (!normalizedTaskID) throw new
|
|
60
|
+
if (!normalizedTaskID) throw new AiWorkerControlError('taskId is required', 400);
|
|
61
61
|
const tasks = await scheduledTasksRuntime.syncProject(projectID);
|
|
62
62
|
const task = tasks.find((entry) => entry?.id === normalizedTaskID) || null;
|
|
63
|
-
if (!task) throw new
|
|
64
|
-
if (!task.loopFile) throw new
|
|
65
|
-
if (!fs.existsSync(task.loopFile)) throw new
|
|
63
|
+
if (!task) throw new AiWorkerControlError('Task not found', 404);
|
|
64
|
+
if (!task.loopFile) throw new AiWorkerControlError('Task is not managed by a loop file', 400);
|
|
65
|
+
if (!fs.existsSync(task.loopFile)) throw new AiWorkerControlError('Loop file not found', 404);
|
|
66
66
|
return task;
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
const setLoopEnabled = async (projectID, taskID, enabled) => {
|
|
70
70
|
if (typeof enabled !== 'boolean') {
|
|
71
|
-
throw new
|
|
71
|
+
throw new AiWorkerControlError('enabled must be a boolean', 400);
|
|
72
72
|
}
|
|
73
73
|
const task = await findLoopTask(projectID, taskID);
|
|
74
74
|
try {
|
|
75
75
|
if (!setLoopFileEnabled(task.loopFile, enabled)) {
|
|
76
|
-
throw new
|
|
76
|
+
throw new AiWorkerControlError('Loop file must be valid before changing its enabled state', 400);
|
|
77
77
|
}
|
|
78
78
|
} catch (error) {
|
|
79
|
-
if (error instanceof
|
|
79
|
+
if (error instanceof AiWorkerControlError) throw error;
|
|
80
80
|
const message = error instanceof Error ? error.message : 'Failed to update loop file';
|
|
81
|
-
throw new
|
|
81
|
+
throw new AiWorkerControlError(message, 500);
|
|
82
82
|
}
|
|
83
83
|
const tasks = await scheduledTasksRuntime.syncProject(projectID);
|
|
84
84
|
return tasks.find((entry) => entry.id === taskID) || null;
|
|
@@ -90,7 +90,7 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
90
90
|
fs.unlinkSync(task.loopFile);
|
|
91
91
|
} catch (error) {
|
|
92
92
|
const message = error instanceof Error ? error.message : 'Failed to delete loop file';
|
|
93
|
-
throw new
|
|
93
|
+
throw new AiWorkerControlError(message, 500);
|
|
94
94
|
}
|
|
95
95
|
return scheduledTasksRuntime.syncProject(projectID);
|
|
96
96
|
};
|
|
@@ -98,7 +98,7 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
98
98
|
const upsert = async (projectID, taskInput) => {
|
|
99
99
|
await findProjectByID(projectID);
|
|
100
100
|
if (!taskInput || typeof taskInput !== 'object') {
|
|
101
|
-
throw new
|
|
101
|
+
throw new AiWorkerControlError('task payload is required', 400);
|
|
102
102
|
}
|
|
103
103
|
let upserted;
|
|
104
104
|
try {
|
|
@@ -106,7 +106,7 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
106
106
|
} catch (error) {
|
|
107
107
|
const message = error instanceof Error ? error.message : 'Failed to save scheduled task';
|
|
108
108
|
const invalid = message.toLowerCase().includes('required') || message.toLowerCase().includes('invalid');
|
|
109
|
-
throw new
|
|
109
|
+
throw new AiWorkerControlError(message, invalid ? 400 : 500);
|
|
110
110
|
}
|
|
111
111
|
await scheduledTasksRuntime.syncProject(projectID);
|
|
112
112
|
const tasks = await projectConfigRuntime.listScheduledTasks(projectID);
|
|
@@ -120,7 +120,7 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
120
120
|
const remove = async (projectID, taskID) => {
|
|
121
121
|
await findProjectByID(projectID);
|
|
122
122
|
const normalizedTaskID = asNonEmptyString(taskID);
|
|
123
|
-
if (!normalizedTaskID) throw new
|
|
123
|
+
if (!normalizedTaskID) throw new AiWorkerControlError('taskId is required', 400);
|
|
124
124
|
const current = await projectConfigRuntime.listScheduledTasks(projectID);
|
|
125
125
|
const existing = current.find((task) => task.id === normalizedTaskID) || null;
|
|
126
126
|
if (existing?.loopFile && fs.existsSync(existing.loopFile)) {
|
|
@@ -129,13 +129,13 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
129
129
|
// file exists. The file itself is the removal surface. Once the file is
|
|
130
130
|
// gone (the task is an orphan that the next sync would remove anyway),
|
|
131
131
|
// deleting the row is safe and allowed.
|
|
132
|
-
throw new
|
|
132
|
+
throw new AiWorkerControlError(
|
|
133
133
|
'Loop task is managed by its .agents/loops markdown file; delete the file to remove the task',
|
|
134
134
|
400,
|
|
135
135
|
);
|
|
136
136
|
}
|
|
137
137
|
const result = await projectConfigRuntime.deleteScheduledTask(projectID, normalizedTaskID);
|
|
138
|
-
if (!result.deleted) throw new
|
|
138
|
+
if (!result.deleted) throw new AiWorkerControlError('Task not found', 404);
|
|
139
139
|
await scheduledTasksRuntime.syncProject(projectID);
|
|
140
140
|
return projectConfigRuntime.listScheduledTasks(projectID);
|
|
141
141
|
};
|
|
@@ -143,14 +143,14 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
143
143
|
const run = async (projectID, taskID) => {
|
|
144
144
|
await findProjectByID(projectID);
|
|
145
145
|
const normalizedTaskID = asNonEmptyString(taskID);
|
|
146
|
-
if (!normalizedTaskID) throw new
|
|
146
|
+
if (!normalizedTaskID) throw new AiWorkerControlError('taskId is required', 400);
|
|
147
147
|
const result = await scheduledTasksRuntime.runNow(projectID, normalizedTaskID);
|
|
148
148
|
if (result.running || result.queued) {
|
|
149
|
-
throw new
|
|
149
|
+
throw new AiWorkerControlError(result.error || 'Task already running', 409);
|
|
150
150
|
}
|
|
151
|
-
if (result.skipped) throw new
|
|
151
|
+
if (result.skipped) throw new AiWorkerControlError('Task not found or disabled', 404);
|
|
152
152
|
if (!result.ok) {
|
|
153
|
-
throw new
|
|
153
|
+
throw new AiWorkerControlError(result.error || 'Task run failed', 500, { task: result.task });
|
|
154
154
|
}
|
|
155
155
|
return {
|
|
156
156
|
task: result.task,
|
|
@@ -164,7 +164,7 @@ export const createScheduledTaskService = (dependencies) => {
|
|
|
164
164
|
const setEnabled = async (projectID, taskID, enabled) => {
|
|
165
165
|
const tasks = await list(projectID);
|
|
166
166
|
const task = tasks.find((entry) => entry?.id === taskID);
|
|
167
|
-
if (!task) throw new
|
|
167
|
+
if (!task) throw new AiWorkerControlError('Task not found', 404);
|
|
168
168
|
const result = await upsert(projectID, { ...task, enabled });
|
|
169
169
|
return result.task;
|
|
170
170
|
};
|
|
@@ -166,7 +166,7 @@ describe('scheduled-task loop-file routes', () => {
|
|
|
166
166
|
sanitizeProjects: vi.fn(),
|
|
167
167
|
projectConfigRuntime: {},
|
|
168
168
|
scheduledTasksRuntime: {},
|
|
169
|
-
|
|
169
|
+
getAiWorkerEventClients: () => new Set(),
|
|
170
170
|
writeSseEvent: vi.fn(),
|
|
171
171
|
});
|
|
172
172
|
return handlers;
|
|
@@ -32,9 +32,9 @@ export const isLoopbackBindHost = (host) => {
|
|
|
32
32
|
export const isNetworkExposedBindHost = (host) => !isLoopbackBindHost(host);
|
|
33
33
|
|
|
34
34
|
export const isUnsafeUnauthenticatedLanAllowed = (env = process.env) =>
|
|
35
|
-
env?.
|
|
35
|
+
env?.AIWORKER_ALLOW_UNAUTHENTICATED_LAN === 'true';
|
|
36
36
|
|
|
37
37
|
export const getUnauthenticatedLanErrorMessage = (host) =>
|
|
38
|
-
`
|
|
39
|
-
+ 'Set --ui-password or
|
|
40
|
-
+ 'or set
|
|
38
|
+
`AiWorker refuses to bind to ${host || 'a network-exposed host'} without UI authentication. `
|
|
39
|
+
+ 'Set --ui-password or AIWORKER_UI_PASSWORD before exposing it over LAN, '
|
|
40
|
+
+ 'or set AIWORKER_ALLOW_UNAUTHENTICATED_LAN=true to accept the risk.';
|
|
@@ -103,7 +103,7 @@ export const createRequestSecurityRuntime = (deps) => {
|
|
|
103
103
|
return { origins, hosts };
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
const isRequestOriginAllowed =
|
|
106
|
+
const isRequestOriginAllowed = (req) => {
|
|
107
107
|
const originHeader = typeof req.headers.origin === 'string' ? req.headers.origin.trim() : '';
|
|
108
108
|
if (!originHeader) {
|
|
109
109
|
return false;
|
|
@@ -120,15 +120,21 @@ export const createRequestSecurityRuntime = (deps) => {
|
|
|
120
120
|
return false;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
const
|
|
124
|
-
|
|
123
|
+
const forwardedHostHeader = req.headers['x-forwarded-host'];
|
|
124
|
+
const forwardedHost = (Array.isArray(forwardedHostHeader) ? forwardedHostHeader[0] : forwardedHostHeader || '')
|
|
125
|
+
.split(',')[0].trim().toLowerCase();
|
|
126
|
+
const hostHeader = req.headers.host;
|
|
127
|
+
const host = forwardedHost || (Array.isArray(hostHeader) ? hostHeader[0] : hostHeader || '').trim().toLowerCase();
|
|
128
|
+
if (host && host === origin.host.toLowerCase()) return true;
|
|
125
129
|
|
|
126
|
-
// TLS commonly ends at a cloud edge before an HTTP hop to
|
|
130
|
+
// TLS commonly ends at a cloud edge before an HTTP hop to AiWorker.
|
|
127
131
|
// In that setup the browser's Origin is https while a generic reverse
|
|
128
132
|
// proxy reports the upstream request as http. The external host remains
|
|
129
133
|
// authoritative, so compare it directly instead of requiring the proxy to
|
|
130
134
|
// preserve the browser-facing protocol.
|
|
131
|
-
return
|
|
135
|
+
return getRequestOriginCandidates(req).then((candidates) => (
|
|
136
|
+
candidates.origins.has(origin.origin) || candidates.hosts.has(origin.host.toLowerCase())
|
|
137
|
+
));
|
|
132
138
|
};
|
|
133
139
|
|
|
134
140
|
return {
|
|
@@ -9,55 +9,55 @@ describe('request security runtime', () => {
|
|
|
9
9
|
test('allows packaged client origins for remote client transports', async () => {
|
|
10
10
|
const runtime = createRuntime();
|
|
11
11
|
|
|
12
|
-
await
|
|
12
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
13
13
|
headers: {
|
|
14
14
|
origin: 'openchamber-ui://app',
|
|
15
15
|
host: '192.168.1.130:1202',
|
|
16
16
|
},
|
|
17
17
|
socket: {},
|
|
18
|
-
})).
|
|
18
|
+
})).toBe(true);
|
|
19
19
|
|
|
20
|
-
await
|
|
20
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
21
21
|
headers: {
|
|
22
22
|
origin: 'capacitor://localhost',
|
|
23
23
|
host: '192.168.1.130:1202',
|
|
24
24
|
},
|
|
25
25
|
socket: {},
|
|
26
|
-
})).
|
|
26
|
+
})).toBe(true);
|
|
27
27
|
|
|
28
28
|
// Android Capacitor WebView (androidScheme 'https') reports this origin.
|
|
29
|
-
await
|
|
29
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
30
30
|
headers: {
|
|
31
31
|
origin: 'https://localhost',
|
|
32
32
|
host: '192.168.1.130:1202',
|
|
33
33
|
},
|
|
34
34
|
socket: {},
|
|
35
|
-
})).
|
|
35
|
+
})).toBe(true);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
test('rejects unknown origins', async () => {
|
|
39
39
|
const runtime = createRuntime();
|
|
40
40
|
|
|
41
|
-
await
|
|
41
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
42
42
|
headers: {
|
|
43
43
|
origin: 'https://evil.example.com',
|
|
44
44
|
host: '192.168.1.130:1202',
|
|
45
45
|
},
|
|
46
46
|
socket: {},
|
|
47
|
-
})).
|
|
47
|
+
})).toBe(false);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
test('allows the external host when TLS terminates before an HTTP proxy hop', async () => {
|
|
51
51
|
const runtime = createRuntime();
|
|
52
52
|
|
|
53
|
-
await
|
|
53
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
54
54
|
headers: {
|
|
55
55
|
origin: 'https://devchamber.example.com',
|
|
56
56
|
host: 'devchamber.example.com',
|
|
57
57
|
'x-forwarded-proto': 'http',
|
|
58
58
|
},
|
|
59
59
|
socket: {},
|
|
60
|
-
})).
|
|
60
|
+
})).toBe(true);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
test('uses the forwarded external host without trusting a different origin', async () => {
|
|
@@ -71,13 +71,13 @@ describe('request security runtime', () => {
|
|
|
71
71
|
socket: {},
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
await
|
|
74
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
75
75
|
...request,
|
|
76
76
|
headers: { ...request.headers, origin: 'https://devchamber.example.com' },
|
|
77
|
-
})).
|
|
78
|
-
await
|
|
77
|
+
})).toBe(true);
|
|
78
|
+
expect(await runtime.isRequestOriginAllowed({
|
|
79
79
|
...request,
|
|
80
80
|
headers: { ...request.headers, origin: 'https://evil.example.com' },
|
|
81
|
-
})).
|
|
81
|
+
})).toBe(false);
|
|
82
82
|
});
|
|
83
83
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Session assist: after a session goes idle and stays quiet, generate a short
|
|
2
2
|
// recap of the agent's last reply plus one suggested user follow-up with the
|
|
3
3
|
// small model, and store both on the session's metadata
|
|
4
|
-
// (metadata.
|
|
4
|
+
// (metadata.aiworker.assist). Clients decide visibility from
|
|
5
5
|
// assist.forMessageID — a new message makes the payload stale everywhere
|
|
6
6
|
// without any extra writes.
|
|
7
7
|
//
|
|
@@ -12,10 +12,10 @@ import fs from 'fs';
|
|
|
12
12
|
import os from 'os';
|
|
13
13
|
import path from 'path';
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
process.env.
|
|
17
|
-
? path.resolve(process.env.
|
|
18
|
-
: path.join(os.homedir(), '.config', '
|
|
15
|
+
const AIWORKER_SETTINGS_FILE = path.join(
|
|
16
|
+
process.env.AIWORKER_DATA_DIR
|
|
17
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
18
|
+
: path.join(os.homedir(), '.config', 'aiworker'),
|
|
19
19
|
'settings.json',
|
|
20
20
|
);
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ const OPENCHAMBER_SETTINGS_FILE = path.join(
|
|
|
24
24
|
// payloads stay untouched — clients keep showing them and dismissal still works.
|
|
25
25
|
const getSessionAssistTargets = () => {
|
|
26
26
|
try {
|
|
27
|
-
const raw = fs.readFileSync(
|
|
27
|
+
const raw = fs.readFileSync(AIWORKER_SETTINGS_FILE, 'utf8');
|
|
28
28
|
const settings = JSON.parse(raw);
|
|
29
29
|
return {
|
|
30
30
|
recap: settings?.sessionRecapEnabled !== false,
|
|
@@ -257,6 +257,7 @@ export const createSessionAssistRuntime = ({
|
|
|
257
257
|
prompt: `The latest exchange in the conversation:\n\n${transcript}\n\nWrite ${requestedFields} in the SAME language as this sample from the conversation: "${languageSample}"`,
|
|
258
258
|
system: buildAssistSystemPrompt(targets),
|
|
259
259
|
directory,
|
|
260
|
+
sessionID: sessionId,
|
|
260
261
|
preferredProviderID: typeof lastAssistantInfo.providerID === 'string' ? lastAssistantInfo.providerID : undefined,
|
|
261
262
|
preferredModelID: typeof lastAssistantInfo.modelID === 'string' ? lastAssistantInfo.modelID : undefined,
|
|
262
263
|
});
|
|
@@ -316,8 +317,8 @@ export const createSessionAssistRuntime = ({
|
|
|
316
317
|
const currentMetadata = freshSession?.metadata && typeof freshSession.metadata === 'object'
|
|
317
318
|
? freshSession.metadata
|
|
318
319
|
: (session.metadata && typeof session.metadata === 'object' ? session.metadata : {});
|
|
319
|
-
const currentNamespace = currentMetadata.
|
|
320
|
-
? currentMetadata.
|
|
320
|
+
const currentNamespace = currentMetadata.aiworker && typeof currentMetadata.aiworker === 'object'
|
|
321
|
+
? currentMetadata.aiworker
|
|
321
322
|
: {};
|
|
322
323
|
|
|
323
324
|
console.log(`[session-assist] generated for ${sessionId} via ${generated.providerID}/${generated.modelID}`);
|
|
@@ -327,7 +328,7 @@ export const createSessionAssistRuntime = ({
|
|
|
327
328
|
body: {
|
|
328
329
|
metadata: {
|
|
329
330
|
...currentMetadata,
|
|
330
|
-
|
|
331
|
+
aiworker: {
|
|
331
332
|
...currentNamespace,
|
|
332
333
|
assist: {
|
|
333
334
|
recap,
|
|
@@ -32,10 +32,10 @@ export const registerSessionFoldersRoutes = (app, dependencies) => {
|
|
|
32
32
|
const {
|
|
33
33
|
fsPromises,
|
|
34
34
|
path,
|
|
35
|
-
|
|
35
|
+
aiworkerDataDir,
|
|
36
36
|
} = dependencies;
|
|
37
37
|
|
|
38
|
-
const filePath = path.join(
|
|
38
|
+
const filePath = path.join(aiworkerDataDir, 'sessions-directories.json');
|
|
39
39
|
let saveQueue = Promise.resolve();
|
|
40
40
|
|
|
41
41
|
const ensureDir = async () => {
|
|
@@ -73,7 +73,7 @@ describe('session folders routes', () => {
|
|
|
73
73
|
registerSessionFoldersRoutes(app, {
|
|
74
74
|
fsPromises,
|
|
75
75
|
path,
|
|
76
|
-
|
|
76
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
const handler = getRoute('POST', '/api/session-folders');
|
|
@@ -103,7 +103,7 @@ describe('session folders routes', () => {
|
|
|
103
103
|
registerSessionFoldersRoutes(app, {
|
|
104
104
|
fsPromises,
|
|
105
105
|
path,
|
|
106
|
-
|
|
106
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
const handler = getRoute('POST', '/api/session-folders');
|
|
@@ -121,7 +121,7 @@ describe('session folders routes', () => {
|
|
|
121
121
|
registerSessionFoldersRoutes(app, {
|
|
122
122
|
fsPromises,
|
|
123
123
|
path,
|
|
124
|
-
|
|
124
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
const response = createMockResponse();
|
|
@@ -137,7 +137,7 @@ describe('session folders routes', () => {
|
|
|
137
137
|
registerSessionFoldersRoutes(app, {
|
|
138
138
|
fsPromises,
|
|
139
139
|
path,
|
|
140
|
-
|
|
140
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
const response = createMockResponse();
|
|
@@ -156,7 +156,7 @@ describe('session folders routes', () => {
|
|
|
156
156
|
registerSessionFoldersRoutes(app, {
|
|
157
157
|
fsPromises,
|
|
158
158
|
path,
|
|
159
|
-
|
|
159
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
const response = createMockResponse();
|
|
@@ -181,7 +181,7 @@ describe('session folders routes', () => {
|
|
|
181
181
|
registerSessionFoldersRoutes(app, {
|
|
182
182
|
fsPromises,
|
|
183
183
|
path,
|
|
184
|
-
|
|
184
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
const response = createMockResponse();
|
|
@@ -204,7 +204,7 @@ describe('session folders routes', () => {
|
|
|
204
204
|
registerSessionFoldersRoutes(app, {
|
|
205
205
|
fsPromises,
|
|
206
206
|
path,
|
|
207
|
-
|
|
207
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
208
208
|
});
|
|
209
209
|
|
|
210
210
|
const response = createMockResponse();
|
|
@@ -227,7 +227,7 @@ describe('session folders routes', () => {
|
|
|
227
227
|
registerSessionFoldersRoutes(app, {
|
|
228
228
|
fsPromises,
|
|
229
229
|
path,
|
|
230
|
-
|
|
230
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
231
231
|
});
|
|
232
232
|
|
|
233
233
|
const response = createMockResponse();
|
|
@@ -108,13 +108,26 @@ before touching the filesystem). Rationale: metadata rides every
|
|
|
108
108
|
stop), with a tick-side safety net. Messages sent while paused leave
|
|
109
109
|
the goal alone; Resume re-arms the loop, and resuming over an aborted
|
|
110
110
|
tail skips the audit and goes straight to a continuation nudge;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
- terminal checks, cheapest first: assistant turn error → `blocked`;
|
|
112
|
+
`tokensUsed >= tokenBudget` → `budgetLimited`;
|
|
113
|
+
`turnsUsed >= MAX_AUTO_TURNS` (20) → `blocked`;
|
|
114
|
+
- error classification is independent of `finish`: `MessageAbortedError`
|
|
115
|
+
keeps the pause/resume behavior; only a `finish: "length"` with no
|
|
116
|
+
error, or `MessageOutputLengthError`, is an in-progress truncation that
|
|
117
|
+
skips the audit and continues. Any other non-null error wins over a
|
|
118
|
+
length finish and blocks with its non-empty `error.name`, or
|
|
119
|
+
`assistant turn failed` when unnamed;
|
|
120
|
+
- length recovery is bounded separately from the token budget and
|
|
121
|
+
auto-continuation cap: the first truncation permits one continuation, but
|
|
122
|
+
a second consecutive completed, non-summary assistant turn that is also
|
|
123
|
+
truncated settles the goal as `blocked` (`repeated output truncation`).
|
|
124
|
+
The consecutive state is derived from the loaded message history, not
|
|
125
|
+
persisted, using `info.time.created` chronology rather than message IDs.
|
|
126
|
+
Summary messages are not agent turns; an ordinary completed assistant
|
|
127
|
+
turn naturally breaks the consecutive condition. Explicit Resume grants
|
|
128
|
+
one new recovery attempt over the same transcript; the continuation
|
|
129
|
+
consumes that permission, so another truncation blocks again. Resume
|
|
130
|
+
does not bypass assistant errors or the token budget;
|
|
118
131
|
- otherwise, small-model audit of the objective + the last assistant turn
|
|
119
132
|
only — no conversation history and no continuation prompts
|
|
120
133
|
(`restrictToPreferredProvider`, session's own provider/model preferred):
|
|
@@ -14,7 +14,7 @@ export const buildGoalIntroText = (tokenBudget) => {
|
|
|
14
14
|
+ '\n</system-reminder>';
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const fitObjective = async ({ objective, directory, providerID, modelID, warn }) => {
|
|
17
|
+
const fitObjective = async ({ objective, directory, sessionID, providerID, modelID, warn }) => {
|
|
18
18
|
if (objective.length <= GOAL_OBJECTIVE_CHAR_LIMIT) return objective;
|
|
19
19
|
|
|
20
20
|
let distilled = null;
|
|
@@ -32,6 +32,7 @@ const fitObjective = async ({ objective, directory, providerID, modelID, warn })
|
|
|
32
32
|
'Write in the same language as the task text.',
|
|
33
33
|
].join('\n'),
|
|
34
34
|
directory,
|
|
35
|
+
sessionID,
|
|
35
36
|
preferredProviderID: providerID,
|
|
36
37
|
preferredModelID: modelID,
|
|
37
38
|
});
|
|
@@ -66,6 +67,7 @@ export const createSessionGoal = async ({
|
|
|
66
67
|
const objectiveText = await fitObjective({
|
|
67
68
|
objective: String(objective ?? '').trim(),
|
|
68
69
|
directory,
|
|
70
|
+
sessionID,
|
|
69
71
|
providerID,
|
|
70
72
|
modelID,
|
|
71
73
|
warn,
|
|
@@ -105,7 +107,7 @@ export const createSessionGoal = async ({
|
|
|
105
107
|
'content-type': 'application/json',
|
|
106
108
|
accept: 'application/json',
|
|
107
109
|
},
|
|
108
|
-
body: JSON.stringify({ metadata: {
|
|
110
|
+
body: JSON.stringify({ metadata: { aiworker: { goal } } }),
|
|
109
111
|
});
|
|
110
112
|
if (!response.ok) throw new Error(`goal metadata patch failed (${response.status})`);
|
|
111
113
|
return goal;
|
|
@@ -64,7 +64,7 @@ describe('session goal creation', () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
const payload = JSON.parse(fetchMock.mock.calls[0][1].body);
|
|
67
|
-
expect(payload.metadata.
|
|
67
|
+
expect(payload.metadata.aiworker.goal).toMatchObject({
|
|
68
68
|
objective: 'Finish the migration',
|
|
69
69
|
objectiveFile: false,
|
|
70
70
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// File-backed goal objectives. Session metadata must stay light (it rides
|
|
2
2
|
// every session.updated event), so the objective TEXT lives in a file under
|
|
3
|
-
// the
|
|
3
|
+
// the AiWorker data dir, keyed by the SESSION ID: sessions are globally
|
|
4
4
|
// unique and carry at most one goal at a time, so the mapping is fully
|
|
5
5
|
// deterministic — the metadata only carries an `objectiveFile: true` flag,
|
|
6
6
|
// never a path, and user-writable metadata cannot become a file-read vector.
|
|
@@ -16,9 +16,9 @@ export const GOAL_OBJECTIVE_CHAR_LIMIT = 5_000;
|
|
|
16
16
|
const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{4,128}$/;
|
|
17
17
|
|
|
18
18
|
const goalsDir = () => path.join(
|
|
19
|
-
process.env.
|
|
20
|
-
? path.resolve(process.env.
|
|
21
|
-
: path.join(os.homedir(), '.config', '
|
|
19
|
+
process.env.AIWORKER_DATA_DIR
|
|
20
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
21
|
+
: path.join(os.homedir(), '.config', 'aiworker'),
|
|
22
22
|
'goals',
|
|
23
23
|
);
|
|
24
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { deleteObjective, readObjective, writeObjective } from './objectives.js';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// AiWorker-owned routes for file-backed goal objectives, keyed by session
|
|
4
4
|
// id (one goal per session; a new goal overwrites the old file). The UI
|
|
5
5
|
// writes the objective file before stamping the goal metadata (which only
|
|
6
6
|
// carries an `objectiveFile: true` flag), reads it back for display, and
|