openmausbot 0.1.97 → 0.1.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/ActivityPanel-CgUgtumZ.js +1 -0
- package/dist/assets/BotSettingsDialog-lJj6oiir.js +7 -0
- package/dist/assets/CloudBackendPicker-C5-_arUz.js +1 -0
- package/dist/assets/ComputerPanel-euJ2CSlH.js +1 -0
- package/dist/assets/DesktopViewer-XCln1Pn_.js +5 -0
- package/dist/assets/InspectorPanel-DKYyQZlo.js +5 -0
- package/dist/assets/KeyboardShortcutsModal-DkkZWZRI.js +1 -0
- package/dist/assets/LocalVmWorkspace-Cwt3_XSx.js +1 -0
- package/dist/assets/MacLocalControl-B28RbdCS.js +1 -0
- package/dist/assets/NewBotDialog-dMWDs5xm.js +2 -0
- package/dist/assets/RemoteAgentSettingsPanel-B5KWXnqH.js +1 -0
- package/dist/assets/RoutinesPage-DOosRYDK.js +1 -0
- package/dist/assets/RoutinesSection-uOfOTfkB.js +1 -0
- package/dist/assets/SettingsModal-BTGkFbj1.js +11 -0
- package/dist/assets/TeamMapPage-DqNdJn96.js +8 -0
- package/dist/assets/TriggersPanel-c6xkG9mi.js +1 -0
- package/dist/assets/VisibilitySection-DD4ffIR5.js +12 -0
- package/dist/assets/WebhooksPanel-Cz17K-_O.js +3 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-k1UCNXb7.js → abnfDiagram-O67JEVCF-CX6tDpkg.js} +1 -1
- package/dist/assets/activity-BAVWO0Zj.js +1 -0
- package/dist/assets/{arc-BUWGa9UU.js → arc-h45-9NFk.js} +1 -1
- package/dist/assets/architectureDiagram-NJMV4G6O-CgLFXbg6.js +36 -0
- package/dist/assets/{blockDiagram-BEXU5L5S-Ag9nLW4H.js → blockDiagram-BEXU5L5S-DGDFFc9M.js} +1 -1
- package/dist/assets/brain-CeCdoTb2.js +1 -0
- package/dist/assets/{c4Diagram-YGBWAQC7-Bh0sjgUa.js → c4Diagram-YGBWAQC7-B9aklAUj.js} +1 -1
- package/dist/assets/channel-TsOXCiPg.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BFBcIIqu.js → chunk-2Q5K7J3B-BC0KmWsM.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-Dvfpex-f.js → chunk-3FUC2YCW-BbDFHsJ1.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-CMjP2YZW.js → chunk-5VM5RSS4-DtGbsjOM.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-BryoeTc7.js → chunk-GTNCS2PH-VMghMcEl.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-BhfvcchO.js → chunk-GWA4HPMP-BP_JyGWQ.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-bcCVp8rp.js → chunk-JWPE2WC7-CYFyzgAU.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-yKIjIlkQ.js → chunk-NETBCI7D-DBNa_kqm.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-D3Zqbmup.js → chunk-WEXAMYUT-BKEeM7CJ.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CjVoIMG6.js → chunk-XXDRQBXY-C__m3ZXl.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-mUCWdVyH.js → classDiagram-v2-NBCMYWYE-CBUrTSAX.js} +1 -1
- package/dist/assets/computer-control-NLov352p.js +1 -0
- package/dist/assets/core-6lvx-ypA.js +12 -0
- package/dist/assets/cose-bilkent-JH36ORCC-GRfL-1Vb.js +1 -0
- package/dist/assets/cpp-DddhVWhK.js +1 -0
- package/dist/assets/{cynefin-OW5HDTMX-BpRrxoO4.js → cynefin-OW5HDTMX-LopwGUjm.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-D3VBWCPK.js → cynefinDiagram-VND7K2PF-DSB8ZRNS.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DxEIACOO.js → dagre-6A5THRUB-DL3Kgdfk.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-BANgZEOS.js → diagram-22UHCM2B-DIxN34BM.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-Cnq7nJir.js → diagram-3UASUU5V-CJnSvXaI.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-4TBDIWnz.js → diagram-ATOU4E4O-C-XF-lO8.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-CD_B39D4.js → diagram-CDSNMT55-Df2yZPi_.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-CzvnFhEC.js → diagram-MLGK6HIB-BBs9oZFs.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-C4w2SNml.js → diagram-MPIPVDR6-CEbK11mM.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-gkUEBm2s.js → ebnfDiagram-ZINNZB2B-BNq6Yv3C.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-B_56X6mw.js → elk-276RUBZZ-CFbQ2bGm.js} +1 -1
- package/dist/assets/engine-javascript-D5Xfc425.js +141 -0
- package/dist/assets/{erDiagram-OPXOYQCR-C_oByoDS.js → erDiagram-OPXOYQCR-Bz8vmjN4.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-CR9xXJkC.js → flowDiagram-KWPJA3E3-cQUfEYrx.js} +1 -1
- package/dist/assets/ganttDiagram-FUAMR5RP-BY8n9tbI.js +292 -0
- package/dist/assets/{gitGraphDiagram-X574FWY7-VJ6-Tp_p.js → gitGraphDiagram-X574FWY7-KduSOMDN.js} +1 -1
- package/dist/assets/image-plus-DNIWU160.js +1 -0
- package/dist/assets/index-C8xb62XS.js +585 -0
- package/dist/assets/index-DbN_Hfjs.css +1 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-2Lu9te4D.js → infoDiagram-VRGFBTTK-xBKgUkpL.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-DR49XY-e.js → ishikawaDiagram-OU5B5YK6-_ei09tAJ.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-ZoR0M305.js → journeyDiagram-ZHPQQLJL-Fp49MkSp.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DLKHxrO-.js → kanban-definition-PNTS6WVX-CY9lLm7V.js} +1 -1
- package/dist/assets/{linear-BASsfWly.js → linear-D2ai4eM2.js} +1 -1
- package/dist/assets/{mermaid.core-BVidiPkk.js → mermaid.core-DP6qPqu1.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-g_Qx0U5C.js → mindmap-definition-NLK3R4M7-g1ynEw_d.js} +1 -1
- package/dist/assets/module-DCUsy2BQ.js +3 -0
- package/dist/assets/page-visible-BZn8304W.js +1 -0
- package/dist/assets/{pegDiagram-GJSIUBJH-DI9Cjs8w.js → pegDiagram-GJSIUBJH-Bt4ObsZj.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-sRI0Uhr5.js → pieDiagram-5QR66LMP-CeLld8bY.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-CamKTby3.js → quadrantDiagram-O4NWA36T-D4WWZw3x.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-DOLlkbf3.js → railroadDiagram-XR7U4H2S-D95P5NqU.js} +1 -1
- package/dist/assets/remote-desktop-panel-DOIMmT4T.js +1 -0
- package/dist/assets/{requirementDiagram-PLB6GJNP-DrRQ5r97.js → requirementDiagram-PLB6GJNP-D12p3RhR.js} +1 -1
- package/dist/assets/ruby-Ct1eTYOr.js +1 -0
- package/dist/assets/{sankeyDiagram-IPEJSGJF-DGS_lXcX.js → sankeyDiagram-IPEJSGJF-Co26Dwcs.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-CkjWqwtf.js → sequenceDiagram-PO4LG4MO-y5YxaDKt.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-CepRgful.js → sizeCapture-INFHLROL-D49hZ8U5.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-hykr-9RS.js → stateDiagram-v2-GCMORJYK-C0sylr-r.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-CKOHEZbI.js → swimlanes-2SLR337P-DmRWsDIs.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-DzhbBHtd.js +8 -0
- package/dist/assets/table-file.worker-KkMx-SNx.js +10 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-CB8pJ6AC.js → timeline-definition-EJHVYXUP-DVmHYs3w.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-DrclJHdl.js → usecaseDiagram-POWQR4AR-BQ1q0VV6.js} +1 -1
- package/dist/assets/user-B9lohN4n.js +1 -0
- package/dist/assets/{vennDiagram-UO4OBE2U-zS23qjv8.js → vennDiagram-UO4OBE2U-C_cnfST0.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-52jgeKxz.js → wardleyDiagram-VNRHLVJA-Bb3BSu12.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-DSdl9rQ8.js → xychartDiagram-PMCCYNJV-CcwEuuEi.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +19 -8
- package/dist-server/container-mcp.js +8 -0
- package/dist-server/desktop-entry.mjs +54 -0
- package/dist-server/drivers/agents-proxy.js +30 -12
- package/dist-server/index.js +2745 -1487
- package/dist-server/openmausbot.js +1004 -642
- package/dist-server/pair-cli.js +1004 -642
- package/dist-server/permission-proxy.js +2 -0
- package/dist-server/server/activity.js +2 -0
- package/dist-server/server/atomic.js +70 -2
- package/dist-server/server/checkpoints.js +55 -39
- package/dist-server/server/cloud-home-start.js +3 -3
- package/dist-server/server/config.js +22 -1
- package/dist-server/server/connector-card-text.js +15 -0
- package/dist-server/server/default-model-selection.js +7 -4
- package/dist-server/server/direct-apply.js +20 -0
- package/dist-server/server/drivers/acp/core.js +10 -0
- package/dist-server/server/drivers/acp/grok.js +47 -5
- package/dist-server/server/drivers/agents-call.js +1 -1
- package/dist-server/server/drivers/agents-catalog.js +34 -11
- package/dist-server/server/drivers/agents-proxy.js +1 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +5 -3
- package/dist-server/server/drivers/claude-login-auth.js +1 -1
- package/dist-server/server/drivers/claude.js +35 -11
- package/dist-server/server/drivers/codex-device-auth.js +27 -319
- package/dist-server/server/drivers/codex.js +147 -24
- package/dist-server/server/drivers/device-auth.js +343 -0
- package/dist-server/server/drivers/openai-chat.js +17 -8
- package/dist-server/server/fleet-agent.js +18 -5
- package/dist-server/server/harness/http.js +5 -2
- package/dist-server/server/harness/registry.js +82 -45
- package/dist-server/server/harness-capabilities.js +7 -4
- package/dist-server/server/hosted-models.js +3 -1
- package/dist-server/server/index.js +471 -77
- package/dist-server/server/key-rejections.js +57 -0
- package/dist-server/server/mcp-oauth.js +125 -47
- package/dist-server/server/message-file.js +102 -7
- package/dist-server/server/model-requests.js +55 -4
- package/dist-server/server/permission-proxy.js +4 -0
- package/dist-server/server/profile-requests.js +80 -4
- package/dist-server/server/provider-key-check.js +5 -1
- package/dist-server/server/request-auth.js +2 -1
- package/dist-server/server/room-turn-timeout.js +28 -0
- package/dist-server/server/routes/thread-models.js +36 -0
- package/dist-server/server/routes/undo.js +17 -0
- package/dist-server/server/routine-requests.js +210 -6
- package/dist-server/server/routines.js +2 -2
- package/dist-server/server/setup-mode.js +1 -1
- package/dist-server/server/skill-learn.js +2 -2
- package/dist-server/server/skills.js +33 -2
- package/dist-server/server/store.js +148 -60
- package/dist-server/server/system-prompt.js +1 -1
- package/dist-server/server/team-backup.js +19 -4
- package/dist-server/server/thread-model.js +25 -0
- package/dist-server/server/turn-continuation.js +17 -16
- package/dist-server/server/webhook-ingress.js +8 -3
- package/dist-server/server/webhooks.js +40 -5
- package/dist-server/server/workspace-backup.js +6 -1
- package/dist-server/shared/device-sign-in.js +57 -0
- package/dist-server/shared/team-backup.js +2 -0
- package/dist-server/shared/thread-model.js +13 -0
- package/dist-server/vps-container-mcp.js +8 -0
- package/dist-server/workspace-backup.worker.js +42 -23
- package/package.json +1 -1
- package/skills/create-verification-skill/SKILL.md +9 -8
- package/skills/create-verification-skill/manifest.json +1 -1
- package/dist/assets/DesktopViewer-CPidMCtt.js +0 -5
- package/dist/assets/abap-BdImnpbu.js +0 -1
- package/dist/assets/actionscript-3-B3316cI-.js +0 -1
- package/dist/assets/ada-bCR0ucgS.js +0 -1
- package/dist/assets/ahk-CsyLZFj1.js +0 -1
- package/dist/assets/ahk2-8Zs4aa1G.js +0 -1
- package/dist/assets/andromeeda-C4gqWexZ.js +0 -1
- package/dist/assets/angular-html-DA-rfuFy.js +0 -1
- package/dist/assets/angular-ts-BrjP3tb8.js +0 -1
- package/dist/assets/apache-Pmp26Uib.js +0 -1
- package/dist/assets/apex-DhZFqWV2.js +0 -1
- package/dist/assets/apl-CORt7UWP.js +0 -1
- package/dist/assets/applescript-Co6uUVPk.js +0 -1
- package/dist/assets/ara-BRHolxvo.js +0 -1
- package/dist/assets/architectureDiagram-NJMV4G6O-DiPor3UT.js +0 -36
- package/dist/assets/asciidoc-CSVQ5wI8.js +0 -1
- package/dist/assets/asm-D_Q5rh1f.js +0 -1
- package/dist/assets/astro-HNnZUWAn.js +0 -1
- package/dist/assets/aurora-x-D-2ljcwZ.js +0 -1
- package/dist/assets/awk-DMzUqQB5.js +0 -1
- package/dist/assets/ayu-dark-DYE7WIF3.js +0 -1
- package/dist/assets/ayu-light-BA47KaF1.js +0 -1
- package/dist/assets/ayu-mirage-32ctXXKs.js +0 -1
- package/dist/assets/ballerina-BFfxhgS-.js +0 -1
- package/dist/assets/bat-CickPsom.js +0 -1
- package/dist/assets/beancount-k_qm7-4y.js +0 -1
- package/dist/assets/berry-uYugtg8r.js +0 -1
- package/dist/assets/bibtex-CHM0blh-.js +0 -1
- package/dist/assets/bicep-Bmn6On1c.js +0 -1
- package/dist/assets/bird2-Bx8U0n9b.js +0 -1
- package/dist/assets/blade-2xfisSek.js +0 -1
- package/dist/assets/bsl-DlhNcFeZ.js +0 -1
- package/dist/assets/c3-Dp5svz6Z.js +0 -1
- package/dist/assets/cadence-Bv_4Rxtq.js +0 -1
- package/dist/assets/cairo-KRGpt6FW.js +0 -1
- package/dist/assets/catppuccin-frappe-CZL1YF0i.js +0 -1
- package/dist/assets/catppuccin-latte-DH-8KZSZ.js +0 -1
- package/dist/assets/catppuccin-macchiato-B7yYVSCf.js +0 -1
- package/dist/assets/catppuccin-mocha-Ct7hS0mc.js +0 -1
- package/dist/assets/channel-DR8eD6QW.js +0 -1
- package/dist/assets/chapel-DTp_pixX.js +0 -1
- package/dist/assets/clarity-Dn5IMItf.js +0 -1
- package/dist/assets/clojure-P80f7IUj.js +0 -1
- package/dist/assets/cmake-D1j8_8rp.js +0 -1
- package/dist/assets/cobol-nBiQ_Alo.js +0 -1
- package/dist/assets/codeowners-Bp6g37R7.js +0 -1
- package/dist/assets/codeql-DsOJ9woJ.js +0 -1
- package/dist/assets/coffee-Ch7k5sss.js +0 -1
- package/dist/assets/common-lisp-Cg-RD9OK.js +0 -1
- package/dist/assets/coq-C7JzOVbR.js +0 -1
- package/dist/assets/cose-bilkent-JH36ORCC-BBmR1bbX.js +0 -1
- package/dist/assets/cpp-BMRokrvK.js +0 -1
- package/dist/assets/crystal-DGywbUpC.js +0 -1
- package/dist/assets/csv-fuZLfV_i.js +0 -1
- package/dist/assets/cue-D82EKSYY.js +0 -1
- package/dist/assets/cypher-COkxafJQ.js +0 -1
- package/dist/assets/d-85-TOEBH.js +0 -1
- package/dist/assets/dark-plus-C3mMm8J8.js +0 -1
- package/dist/assets/dax-CEL-wOlO.js +0 -1
- package/dist/assets/desktop-BmXAJ9_W.js +0 -1
- package/dist/assets/dotenv-Da5cRb03.js +0 -1
- package/dist/assets/dracula-BzJJZx-M.js +0 -1
- package/dist/assets/dracula-soft-BXkSAIEj.js +0 -1
- package/dist/assets/dream-maker-BtqSS_iP.js +0 -1
- package/dist/assets/edge-FbVlp4U3.js +0 -1
- package/dist/assets/elixir-CkH2-t6x.js +0 -1
- package/dist/assets/elm-DbKCFpqz.js +0 -1
- package/dist/assets/emacs-lisp-C_m_b--Z.js +0 -1
- package/dist/assets/erb-DXfck5VN.js +0 -1
- package/dist/assets/erlang-DsQrWhSR.js +0 -1
- package/dist/assets/everforest-dark-BgDCqdQA.js +0 -1
- package/dist/assets/everforest-light-C8M2exoo.js +0 -1
- package/dist/assets/fennel-BYunw83y.js +0 -1
- package/dist/assets/fluent-C4IJs8-o.js +0 -1
- package/dist/assets/fortran-fixed-form-CkoXwp7k.js +0 -1
- package/dist/assets/fortran-free-form-BxgE0vQu.js +0 -1
- package/dist/assets/fsharp-CXgrBDvD.js +0 -1
- package/dist/assets/ganttDiagram-FUAMR5RP-gtYBTu0B.js +0 -292
- package/dist/assets/gdresource-TyuKm33G.js +0 -1
- package/dist/assets/gdscript-DqcFQ5yU.js +0 -1
- package/dist/assets/gdshader-DkwncUOv.js +0 -1
- package/dist/assets/genie-D0YGMca9.js +0 -1
- package/dist/assets/gherkin-DyxjwDmM.js +0 -1
- package/dist/assets/git-commit-F4YmCXRG.js +0 -1
- package/dist/assets/git-rebase-r7XF79zn.js +0 -1
- package/dist/assets/github-dark-DHJKELXO.js +0 -1
- package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +0 -1
- package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -1
- package/dist/assets/github-light-DAi9KRSo.js +0 -1
- package/dist/assets/github-light-high-contrast-BfjtVDDH.js +0 -1
- package/dist/assets/gleam-BspZqrRM.js +0 -1
- package/dist/assets/glimmer-js-ByusRIyA.js +0 -1
- package/dist/assets/glimmer-ts-BfAWNZQY.js +0 -1
- package/dist/assets/glsl-DplSGwfg.js +0 -1
- package/dist/assets/gn-n2N0HUVH.js +0 -1
- package/dist/assets/gnuplot-DdkO51Og.js +0 -1
- package/dist/assets/groovy-gcz8RCvz.js +0 -1
- package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -1
- package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +0 -1
- package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +0 -1
- package/dist/assets/gruvbox-light-hard-CH1njM8p.js +0 -1
- package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +0 -1
- package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +0 -1
- package/dist/assets/hack-BWmVpMyf.js +0 -1
- package/dist/assets/haml-D5jkg6IW.js +0 -1
- package/dist/assets/handlebars-BpdQsYii.js +0 -1
- package/dist/assets/haskell-Df6bDoY_.js +0 -1
- package/dist/assets/haxe-CfZj7gIn.js +0 -1
- package/dist/assets/hcl-BWvSN4gD.js +0 -1
- package/dist/assets/hjson-D5-asLiD.js +0 -1
- package/dist/assets/hlsl-D3lLCCz7.js +0 -1
- package/dist/assets/horizon-BUw7H-hv.js +0 -1
- package/dist/assets/horizon-bright-CUuTKBJd.js +0 -1
- package/dist/assets/houston-DnULxvSX.js +0 -1
- package/dist/assets/html-derivative-DlHx6ybY.js +0 -1
- package/dist/assets/http-jrhK8wxY.js +0 -1
- package/dist/assets/hurl-irOxFIW8.js +0 -1
- package/dist/assets/hxml-2-FPmUDs.js +0 -1
- package/dist/assets/hy-DFXneXwc.js +0 -1
- package/dist/assets/imba-DGztddWO.js +0 -1
- package/dist/assets/index-Cp4S4NOL.js +0 -153
- package/dist/assets/index-CpaIXRLp.js +0 -614
- package/dist/assets/index-Z81SMsgR.css +0 -1
- package/dist/assets/ini-BEwlwnbL.js +0 -1
- package/dist/assets/jinja-f2NsQr07.js +0 -1
- package/dist/assets/jison-wvAkD_A8.js +0 -1
- package/dist/assets/jsonl-DcaNXYhu.js +0 -1
- package/dist/assets/jsonnet-DFQXde-d.js +0 -1
- package/dist/assets/jssm-C2t-YnRu.js +0 -1
- package/dist/assets/julia-5Bft2YPA.js +0 -1
- package/dist/assets/just-Cwhn7H3k.js +0 -1
- package/dist/assets/kanagawa-dragon-BuwD2xS4.js +0 -1
- package/dist/assets/kanagawa-lotus-C3DzagqV.js +0 -1
- package/dist/assets/kanagawa-wave-DFGoQZhC.js +0 -1
- package/dist/assets/kdl-DV7GczEv.js +0 -1
- package/dist/assets/kusto-wEQ09or8.js +0 -1
- package/dist/assets/laserwave-DUszq2jm.js +0 -1
- package/dist/assets/latex-D5pSuvFb.js +0 -1
- package/dist/assets/lean-BZvkOJ9d.js +0 -1
- package/dist/assets/light-plus-B7mTdjB0.js +0 -1
- package/dist/assets/liquid-C0sCDyMI.js +0 -1
- package/dist/assets/llvm-BZoOZj88.js +0 -1
- package/dist/assets/log-2UxHyX5q.js +0 -1
- package/dist/assets/logo-BtOb2qkB.js +0 -1
- package/dist/assets/luau-BnpPk5vE.js +0 -1
- package/dist/assets/marko-DjSrsDqO.js +0 -1
- package/dist/assets/material-theme-D5KoaKCx.js +0 -1
- package/dist/assets/material-theme-darker-BfHTSMKl.js +0 -1
- package/dist/assets/material-theme-lighter-B0m2ddpp.js +0 -1
- package/dist/assets/material-theme-ocean-CyktbL80.js +0 -1
- package/dist/assets/material-theme-palenight-Csfq5Kiy.js +0 -1
- package/dist/assets/matlab-D7o27uSR.js +0 -1
- package/dist/assets/mdc-D1_yUvq7.js +0 -1
- package/dist/assets/mermaid-CQcHuHx7.js +0 -1
- package/dist/assets/min-dark-CafNBF8u.js +0 -1
- package/dist/assets/min-light-CTRr51gU.js +0 -1
- package/dist/assets/mipsasm-CKIfxQSi.js +0 -1
- package/dist/assets/mojo-DJz3ZmWd.js +0 -1
- package/dist/assets/monokai-BRVnQi9A.js +0 -1
- package/dist/assets/moonbit-CHtswR0a.js +0 -1
- package/dist/assets/move-el3G9tDJ.js +0 -1
- package/dist/assets/narrat-DRg8JJMk.js +0 -1
- package/dist/assets/nextflow-C-mBbutL.js +0 -1
- package/dist/assets/nextflow-groovy-vE_lwT2v.js +0 -1
- package/dist/assets/nginx-BpAMiNFr.js +0 -1
- package/dist/assets/night-owl-C39BiMTA.js +0 -1
- package/dist/assets/night-owl-light-CMTm3GFP.js +0 -1
- package/dist/assets/nim-BIad80T-.js +0 -1
- package/dist/assets/nix-CwoSXNpI.js +0 -1
- package/dist/assets/nord-Ddv68eIx.js +0 -1
- package/dist/assets/nsis-BlV79W_Q.js +0 -1
- package/dist/assets/nushell-D3jzshHO.js +0 -1
- package/dist/assets/objective-c-DXmwc3jG.js +0 -1
- package/dist/assets/objective-cpp-CLxacb5B.js +0 -1
- package/dist/assets/ocaml-C0hk2d4L.js +0 -1
- package/dist/assets/odin-BBf5iR-q.js +0 -1
- package/dist/assets/one-dark-pro-DVMEJ2y_.js +0 -1
- package/dist/assets/one-light-C3Wv6jpd.js +0 -1
- package/dist/assets/openscad-C4EeE6gA.js +0 -1
- package/dist/assets/org-DM6o9KBp.js +0 -1
- package/dist/assets/pascal-D93ZcfNL.js +0 -1
- package/dist/assets/perl-B9cMNwum.js +0 -1
- package/dist/assets/pkl-u5AG7uiY.js +0 -1
- package/dist/assets/plastic-3e1v2bzS.js +0 -1
- package/dist/assets/plsql-ChMvpjG-.js +0 -1
- package/dist/assets/po-BTJTHyun.js +0 -1
- package/dist/assets/poimandres-CS3Unz2-.js +0 -1
- package/dist/assets/polar-C0HS_06l.js +0 -1
- package/dist/assets/postcss-CXtECtnM.js +0 -1
- package/dist/assets/powerquery-CEu0bR-o.js +0 -1
- package/dist/assets/prisma-Vru482bI.js +0 -1
- package/dist/assets/prolog-CbFg5uaA.js +0 -1
- package/dist/assets/pug-DKIMFp6K.js +0 -1
- package/dist/assets/puppet-BMWR74SV.js +0 -1
- package/dist/assets/purescript-CklMAg4u.js +0 -1
- package/dist/assets/qml-3beO22l8.js +0 -1
- package/dist/assets/qmldir-C8lEn-DE.js +0 -1
- package/dist/assets/qss-IeuSbFQv.js +0 -1
- package/dist/assets/racket-BqYA7rlc.js +0 -1
- package/dist/assets/raku-DXvB9xmW.js +0 -1
- package/dist/assets/razor-BjBPvh-w.js +0 -1
- package/dist/assets/rbs-CpoqiR4B.js +0 -1
- package/dist/assets/red-hvxz__6c.js +0 -1
- package/dist/assets/reg-C-SQnVFl.js +0 -1
- package/dist/assets/regexp-CDVJQ6XC.js +0 -1
- package/dist/assets/rel-C3B-1QV4.js +0 -1
- package/dist/assets/riscv-BM1_JUlF.js +0 -1
- package/dist/assets/ron-D8l8udqQ.js +0 -1
- package/dist/assets/rose-pine-5qJOZa0Y.js +0 -1
- package/dist/assets/rose-pine-dawn-zx0QlTCp.js +0 -1
- package/dist/assets/rose-pine-moon-C9pEdX9L.js +0 -1
- package/dist/assets/rosmsg-BJDFO7_C.js +0 -1
- package/dist/assets/rst-bs7f0vWN.js +0 -1
- package/dist/assets/ruby-C0TQ7zu5.js +0 -1
- package/dist/assets/sas-DEy46yEz.js +0 -1
- package/dist/assets/scala-CqE71os6.js +0 -1
- package/dist/assets/scheme-C98Dy4si.js +0 -1
- package/dist/assets/sdbl-DVxCFoDh.js +0 -1
- package/dist/assets/shaderlab-Dg9Lc6iA.js +0 -1
- package/dist/assets/shellsession-BADoaaVG.js +0 -1
- package/dist/assets/slack-dark-BthQWCQV.js +0 -1
- package/dist/assets/slack-ochin-DqwNpetd.js +0 -1
- package/dist/assets/smalltalk-BOQMe2GC.js +0 -1
- package/dist/assets/smithy-cds9vsN8.js +0 -1
- package/dist/assets/snazzy-light-Bw305WKR.js +0 -1
- package/dist/assets/solarized-dark-CpvCGNkr.js +0 -1
- package/dist/assets/solarized-light-Dlz6yCKv.js +0 -1
- package/dist/assets/solidity-DijEV5ha.js +0 -1
- package/dist/assets/soy-8wufbnw4.js +0 -1
- package/dist/assets/sparql-rVzFXLq3.js +0 -1
- package/dist/assets/splunk-BtCnVYZw.js +0 -1
- package/dist/assets/ssh-config-_ykCGR6B.js +0 -1
- package/dist/assets/stata-DI20mbqo.js +0 -1
- package/dist/assets/stylus-BEDo0Tqx.js +0 -1
- package/dist/assets/surrealql-Cjom0U5J.js +0 -1
- package/dist/assets/svelte-Cy7k_4gC.js +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D-8vqZ4-.js +0 -8
- package/dist/assets/synthwave-84-CbfX1IO0.js +0 -1
- package/dist/assets/system-verilog-0hqHdDBg.js +0 -1
- package/dist/assets/systemd-4A_iFExJ.js +0 -1
- package/dist/assets/talonscript-CkByrt1z.js +0 -1
- package/dist/assets/tasl-QIJgUcNo.js +0 -1
- package/dist/assets/tcl-dwOrl1Do.js +0 -1
- package/dist/assets/templ-DhtptRzy.js +0 -1
- package/dist/assets/terraform-BETggiCN.js +0 -1
- package/dist/assets/tex-D96PA37w.js +0 -1
- package/dist/assets/tokyo-night-hegEt444.js +0 -1
- package/dist/assets/ts-tags-D351s5mN.js +0 -1
- package/dist/assets/tsv-B_m7g4N7.js +0 -1
- package/dist/assets/turtle-BsS91CYL.js +0 -1
- package/dist/assets/twig-27uCiNez.js +0 -1
- package/dist/assets/typespec-CAFt9gP4.js +0 -1
- package/dist/assets/typst-BUadGCkm.js +0 -1
- package/dist/assets/v-BGw2Nkan.js +0 -1
- package/dist/assets/vala-CsfeWuGM.js +0 -1
- package/dist/assets/vb-Cu-pLBUe.js +0 -1
- package/dist/assets/verilog-nZwndyjY.js +0 -1
- package/dist/assets/vesper-DRje8inN.js +0 -1
- package/dist/assets/vhdl-CeAyd5Ju.js +0 -1
- package/dist/assets/viml-CJc9bBzg.js +0 -1
- package/dist/assets/vitesse-black-Bkuqu6BP.js +0 -1
- package/dist/assets/vitesse-dark-D0r3Knsf.js +0 -1
- package/dist/assets/vitesse-light-CVO1_9PV.js +0 -1
- package/dist/assets/vue-BqiEGhQt.js +0 -1
- package/dist/assets/vue-html-AaS7Mt5G.js +0 -1
- package/dist/assets/vue-vine-BoDAl6tE.js +0 -1
- package/dist/assets/vyper-CDx5xZoG.js +0 -1
- package/dist/assets/wasm-CG6Dc4jp.js +0 -1
- package/dist/assets/wenyan-BV7otONQ.js +0 -1
- package/dist/assets/wgsl-Dx-B1_4e.js +0 -1
- package/dist/assets/wikitext-BhOHFoWU.js +0 -1
- package/dist/assets/wit-5i3qLPDT.js +0 -1
- package/dist/assets/wolfram-lXgVvXCa.js +0 -1
- package/dist/assets/xsl-CtQFsRM5.js +0 -1
- package/dist/assets/zenscript-DVFEvuxE.js +0 -1
- package/dist/assets/zig-VOosw3JB.js +0 -1
- /package/dist/assets/{shellscript-Yzrsuije.js → bash-Yzrsuije.js} +0 -0
- /package/dist/assets/{docker-BcOcwvcX.js → dockerfile-BcOcwvcX.js} +0 -0
- /package/dist/assets/{make-CHLpvVh8.js → makefile-CHLpvVh8.js} +0 -0
|
@@ -48,11 +48,11 @@ import { buildTurnDigest, coverageForDriver, digestPromptLine, renderDigest, too
|
|
|
48
48
|
import { appendDecision, bindDecisionRetention, boundRetentionDays, decisionRetentionDays, decisionsCsv, flushDecisionLog, pruneDecisions, readDecisionRange, readDecisions, withDecisionActor } from "./decision-log.js";
|
|
49
49
|
import { validateBotCwd } from "./bot-cwd.js";
|
|
50
50
|
import { ATTACHMENTS_DIR, attachmentExists, cleanupStaleAttachmentPartials, deleteAttachment, extensionForMime, FILE_MAX_BYTES, IMAGE_MAX_BYTES, parseAudioRange, readAttachment, saveAudio, saveFile, saveImage, saveImageUpload, validateAttachmentUploadId, } from "./attachments.js";
|
|
51
|
-
import { messageFileDisposition, messageFileDownloadName, messageAttachmentName, messageFileRoots, messageImageTargetAt, messageReferencesFile, openMessageFile, } from "./message-file.js";
|
|
51
|
+
import { messageFileDisposition, messageFileDownloadName, messageAttachmentName, messageFileRoots, messageImageTargetAt, messageReferencesFile, listMessageFolder, messageFolderEntryName, openMessageFile, openMessageFolderEntry, } from "./message-file.js";
|
|
52
52
|
import { avatarGenerationRequestSchema, avatarGenerationStateMatches, generateAvatarImage, avatarImageStatus, snapshotAvatarGenerationState, } from "./avatar-image.js";
|
|
53
53
|
import { fitsOnOneLine, parseBotProfilePatch } from "./bot-profile.js";
|
|
54
54
|
import { groupTurnCwd } from "./room-cwd.js";
|
|
55
|
-
import { RoomTurnDeadline, RoomTurnStallRegistry, roomTurnTimeoutMessage } from "./room-turn-timeout.js";
|
|
55
|
+
import { RoomTurnDeadline, RoomTurnStallRegistry, effectiveRoomTurnTimeoutMinutes, parseConversationTurnTimeout, roomTurnTimeoutMessage } from "./room-turn-timeout.js";
|
|
56
56
|
import { roomTurnEnd } from "./room-turn-end.js";
|
|
57
57
|
import { pickComputer } from "./computer-selection.js";
|
|
58
58
|
import * as boat from "./boat.js";
|
|
@@ -67,6 +67,7 @@ import { liveCardKind, liveDecisionRefusal } from "../shared/live-approval.js";
|
|
|
67
67
|
import { boatAccountResourceChangeError, cloudBackendChangeError, vpsAliasResourceChangeError, } from "./cloud-backend.js";
|
|
68
68
|
import * as composio from "./composio.js";
|
|
69
69
|
import { connectorCallFromFrame, connectorRefusalText, connectorUnrecognizedText, evaluateConnectorTools } from "./connector-verdict.js";
|
|
70
|
+
import { connectorCardText } from "./connector-card-text.js";
|
|
70
71
|
import { chiefOfStaffSystemPrompt } from "./chief-of-staff.js";
|
|
71
72
|
import { buildRecall } from "./recall.js";
|
|
72
73
|
import { createMemoryUpkeep, upkeepEnabled } from "./memory-upkeep.js";
|
|
@@ -74,7 +75,7 @@ import { appendAboutMe, commitLearned, planLearned } from "./profile-learned.js"
|
|
|
74
75
|
import { canAccessTeam, canReachPeer, coordinatorSupervises, livePeerRoster, livePeerRosterBlock, peerAllowed, peerName, peerRosterSystemPrompt, peerStatus, peerStatusWords, reachablePeers, resolveTeammate, roomPeerRosterSystemPrompt, roomRosterLine, PEER_ACCESS_HELP } from "./peer-roster.js";
|
|
75
76
|
import { containerComputerAction, containerComputerExists, containerComputerFrame, containerComputerMcp, containerComputerScreenshot, containerComputerStatus, containerExec, containerRuntimeStatus, localVmWakeAction, localVmWorkspaceExists, perBotLocalVmTarget, poolLocalVmTarget, SHARED_LOCAL_VM_TARGET, setupCommands, VM_WORKSPACE_GUEST, } from "./container-computer.js";
|
|
76
77
|
import { attachForTurn, saveBotAttachment } from "./bot-attachment.js";
|
|
77
|
-
import { cacheUntilConfigChanges, ensureDirs, instanceConfigs, loadConfig, providerReloadKeys, localVmIdleTimeoutMinutes, localVmMaxInstances, localVmMode, parseConfigPatch, roomTurnTimeoutMinutes, threadEventLogMaxBytes, maxConcurrentBotThreads, threadEventLogRetentionDays, saveConfig, showToolCallsEnabled, routinesInConversationEnabled, claudeUserMcpEnabled, skillAuthoringEnabled, autoRecallEnabled, captureQuietMs, tidyHour, sharedComputersEnabled, builtInBrowserEnabled, llmThreadTitlesEnabled, browserProfileReplacementConflict, browserProfilePartitionTarget, syncCredentialEnv, stripWorkspaceCredentialEnv, PROVIDER_CREDENTIAL_ENV, withInstanceCli, persistableInstanceConfigs, vpsSshAlias, browserEngineAttachCdpUrl, DATA_DIR, skillsLibraryEnabled, EVENTS_DIR, NATIVE_DIR, customMcpServers, roomHandoffLimits, onConfigSaved, CLAUDE_API_INSTANCE, liveSettingsFor, mergeOpenCodeProviderKeys, openCodeProviderKeys, } from "./config.js";
|
|
78
|
+
import { cacheUntilConfigChanges, ensureDirs, instanceConfigs, loadConfig, providerReloadKeys, localVmIdleTimeoutMinutes, localVmMaxInstances, localVmMode, parseConfigPatch, roomTurnTimeoutMinutes, mcpCallTimeoutMinutes, threadEventLogMaxBytes, maxConcurrentBotThreads, threadEventLogRetentionDays, saveConfig, showToolCallsEnabled, routinesInConversationEnabled, claudeUserMcpEnabled, skillAuthoringEnabled, autoRecallEnabled, captureQuietMs, tidyHour, sharedComputersEnabled, builtInBrowserEnabled, llmThreadTitlesEnabled, browserProfileReplacementConflict, browserProfilePartitionTarget, syncCredentialEnv, stripWorkspaceCredentialEnv, PROVIDER_CREDENTIAL_ENV, withInstanceCli, persistableInstanceConfigs, vpsSshAlias, browserEngineAttachCdpUrl, DATA_DIR, skillsLibraryEnabled, EVENTS_DIR, NATIVE_DIR, customMcpServers, roomHandoffLimits, onConfigSaved, CLAUDE_API_INSTANCE, liveSettingsFor, mergeOpenCodeProviderKeys, openCodeProviderKeys, } from "./config.js";
|
|
78
79
|
import { sweepThreadEventLogs } from "./thread-retention.js";
|
|
79
80
|
import { ComputerControl } from "./computer-control.js";
|
|
80
81
|
import { augmentedPath, findCliCandidates, resetPathCache } from "./env-path.js";
|
|
@@ -83,7 +84,8 @@ import { appendUsage, parseUsageRange, readUsage, summarizeUsage, usageCsv, USAG
|
|
|
83
84
|
import { loadPlanUsage, planAccountsFromInstances } from "./plan-usage.js";
|
|
84
85
|
import { GroupUsageReader } from "./group-thread-usage.js";
|
|
85
86
|
import { ledgerCost } from "./model-prices.js";
|
|
86
|
-
import { checkProviderKey, PROVIDER_KEY_KINDS } from "./provider-key-check.js";
|
|
87
|
+
import { checkProviderKey, PROVIDER_KEY_KINDS, providerBaseUrl } from "./provider-key-check.js";
|
|
88
|
+
import { forgetKey, noteKeyAccepted, noteKeyRejected, onKeyRejectionChange } from "./key-rejections.js";
|
|
87
89
|
import { assertWithinBudget, noteSpend, spendAlertText, spendState, takeSpendAlert } from "./spend.js";
|
|
88
90
|
import { fleetAvailable, fleetRequest, fleetSocketPath } from "./fleet-client.js";
|
|
89
91
|
import { entitled } from "./enterprise.js";
|
|
@@ -95,7 +97,7 @@ import { RETRY_MAX_ATTEMPTS } from "./drivers/retry.js";
|
|
|
95
97
|
import { recoveryCapabilityError } from "./automatic-recovery.js";
|
|
96
98
|
import { decodeGeneratedImage } from "./generated-image.js";
|
|
97
99
|
import { addDisabledMcpServer, MAX_MCP_SERVERS, isRemoteMcpServer, listMcpServers, mcpServerNameError, parseMcpServerMutation, parseMcpServersImport, parseStoredMcpServer, } from "./mcp-registry.js";
|
|
98
|
-
import { McpOAuthError, McpSignInError, McpOAuthManager, mcpOAuthRedirectUri, withMcpSignIn, withoutPendingSignIn } from "./mcp-oauth.js";
|
|
100
|
+
import { MCP_OAUTH_CALLBACK_PATH, McpOAuthError, McpSignInError, McpOAuthManager, mcpCallbackOrigin, mcpOAuthRedirectUri, withMcpSignIn, withoutPendingSignIn } from "./mcp-oauth.js";
|
|
99
101
|
import { probeMcpServer } from "./mcp-probe.js";
|
|
100
102
|
import { GROUP_GOAL_MAX_TURNS, groupGoalAssignmentKey, groupGoalCompletionTurnId, groupGoalCoordinatorInstructions, groupGoalWorkerInstructions, parseGroupGoalDecision, resolveGroupGoalMember, selectGroupGoalCoordinator, } from "./group-goal-run.js";
|
|
101
103
|
import { BUILT_IN_DRIVERS } from "./drivers/builtIn.js";
|
|
@@ -126,12 +128,13 @@ import { createCloudMoveRoutes, workspaceShared } from "./cloud-move-http.js";
|
|
|
126
128
|
import { RESTART_EXIT_CODE } from "./restart.js";
|
|
127
129
|
import { holdIncludedServices } from "./included-services.js";
|
|
128
130
|
import { selectDefaultModelSelection, withNewBotEffort } from "./default-model-selection.js";
|
|
131
|
+
import { threadModelFallback } from "./thread-model.js";
|
|
129
132
|
import { computerEngineMoveText, removedComputerInstanceIds, writeComputerEngineMoveLines } from "./computer-engine-removal.js";
|
|
130
133
|
import { cancelPeerApprovalsFor, cancelPeerApprovalsForThread, dismissStalePeerCards, peerApprovalFailure, requestPeerApproval, resolvePeerComms } from "./peer-approval.js";
|
|
131
134
|
import { peerDeliveryReceipt } from "./peer-delivery.js";
|
|
132
135
|
import { peerProvenanceNote, withPeerProvenance } from "./peer-provenance.js";
|
|
133
136
|
import { decideRoomPost, emptyRoomPostBudget } from "./room-post-budget.js";
|
|
134
|
-
import { isProjectEmoji, mentionedBots, parseConnectorTools, roomResponders, sectionKey, Store, titleFromLlm, toWireTask, } from "./store.js";
|
|
137
|
+
import { isProjectEmoji, mentionedBots, parseConnectorTools, roomResponders, sectionKey, Store, threadTitleFrom, titleFromLlm, toWireTask, } from "./store.js";
|
|
135
138
|
import * as tts from "./tts/index.js";
|
|
136
139
|
import { createDecider, deciderIncludedHere, deciderReady, deciderSavePatch, describeDecider } from "./decider/index.js";
|
|
137
140
|
import { decideRoomResponder } from "./decider/room-routing.js";
|
|
@@ -150,7 +153,7 @@ import { ensureWorkspace, ensureTaskWorkspace, workspaceLocationsPrompt, support
|
|
|
150
153
|
import { listMemoryTopics, memoryDate, readMemoryFile, readMemoryTopic, writeMemoryFile, writeMemoryTopic } from "./workspace.js";
|
|
151
154
|
import { beginMemoryTurn, endMemoryTurn, flushAllMemoryJournals, journalMemoryWrite, } from "./memory-journal.js";
|
|
152
155
|
import { readSectionContext, readSections, sectionContextKey, sectionContextLabel, sectionContextSystemPrompt, writeSectionContext, SECTION_CONTEXT_MAX_BYTES, } from "./section-context.js";
|
|
153
|
-
import { applyStagedSkillWrite, applySkillWriteWithReceipt, getStagedSkillWrite, installOrgSkill, installSkill, listBotSkillsWithOrigin, resolveBotSkills, parseSkillMd, scanSkillText, listSkills, listStagedSkillWrites, isSkillName, readSkillFile, rejectStagedSkillWrite, removeSkill, setSkillEnabled, skillPackageStamps, skillsSystemPrompt, stageSkillWrite, } from "./skills.js";
|
|
156
|
+
import { applyStagedSkillWrite, applySkillWriteWithReceipt, getStagedSkillWrite, installOrgSkill, installSkill, listBotSkillsWithOrigin, resolveBotSkills, parseSkillMd, scanSkillText, listSkills, listStagedSkillWrites, isSkillName, readSkillFile, rejectStagedSkillWrite, removeSkill, setSkillEnabled, skillPackageStamps, skillsSystemPrompt, stageSkillWrite, undoSkillWrite, } from "./skills.js";
|
|
154
157
|
import { fetchSkillFromSource } from "./skill-fetch.js";
|
|
155
158
|
import { pickBotName } from "./names.js";
|
|
156
159
|
import { botSkillTemplateSchema, newBotDefaultsSchema, resolveBotCreationDefaults } from "./new-bot-defaults.js";
|
|
@@ -176,6 +179,7 @@ import { agentBrowserFrame, agentBrowserIntegration, browserEngineEncryptionKey,
|
|
|
176
179
|
import { createScreenFrameSource } from "./screen-frame-source.js";
|
|
177
180
|
import { screenFrameHash, screenSurfaceForTool, screenTouchingTool, settledFrameIsNews } from "./screen-frame-gate.js";
|
|
178
181
|
import { asSchedule, RoutineRequestService } from "./routine-requests.js";
|
|
182
|
+
import { directApply, UNDO_STALE } from "./direct-apply.js";
|
|
179
183
|
import { createOptionsCard } from "./options-card.js";
|
|
180
184
|
import { buildBotOverview, connectedAppsFacts } from "./bot-overview.js";
|
|
181
185
|
import { ProfileRequestService } from "./profile-requests.js";
|
|
@@ -228,7 +232,7 @@ import { applyPendingWorkspaceRestore, readLastWorkspaceRestore } from "./worksp
|
|
|
228
232
|
import { createCustomDomainVerifier, customDomainIpv4, normalizeCustomDomain } from "./custom-domain.js";
|
|
229
233
|
import { allowedScopes, createEmailSignIn, parseAllowList } from "./account-signin.js";
|
|
230
234
|
import { ProviderAuthSessions } from "./provider-auth-sessions.js";
|
|
231
|
-
import { clearSessionCookie, clientBotPatchViolation, clientGroupPatchViolation, isLoopbackHost, isProxied, labelFromUserAgent, requestOrigin, requestSource, requiredScope, resolveLoopbackTrust, resolveRequestAuth, parseCookies, serializeSessionCookie, sessionCookieName, } from "./request-auth.js";
|
|
235
|
+
import { clearSessionCookie, clientBotPatchViolation, clientGroupPatchViolation, isLoopbackHost, isProxied, labelFromUserAgent, provesSameOrigin, requestOrigin, requestSource, requiredScope, resolveLoopbackTrust, resolveRequestAuth, parseCookies, serializeSessionCookie, sessionCookieName, } from "./request-auth.js";
|
|
232
236
|
import { cookieMaxAgeSeconds, formatPairingCode, SessionRegistry } from "./sessions.js";
|
|
233
237
|
import { ThreadStarters } from "./thread-starters.js";
|
|
234
238
|
import { activityCsv, activityEntries, appendAdminAction, botAuditSnapshot, botChangeRows, configChangeRows, parseActivityWhat, flushAdminActivity, pruneAdminActivity, readAdminActivityRange, sharedSignIn, signInListsOf, } from "./admin-activity.js";
|
|
@@ -244,6 +248,8 @@ import { createHostedSlackRoutes } from "./routes/hosted-slack.js";
|
|
|
244
248
|
import { createBotPresetRoutes } from "./routes/bot-presets.js";
|
|
245
249
|
import { createBotMemoryRoutes } from "./routes/bot-memory.js";
|
|
246
250
|
import { createDeciderRoutes } from "./routes/decider.js";
|
|
251
|
+
import { createThreadModelRoutes } from "./routes/thread-models.js";
|
|
252
|
+
import { createUndoRoutes } from "./routes/undo.js";
|
|
247
253
|
import { createDesktopViewer, desktopViewerUrl } from "./routes/desktop-viewer.js";
|
|
248
254
|
import { localDesktopTarget, localVmViewerStatus, viewerTargetId } from "./desktop-viewer-targets.js";
|
|
249
255
|
import { createAntigravityLeftoverRoutes } from "./routes/antigravity-leftovers.js";
|
|
@@ -835,12 +841,13 @@ function openerFrom(fromThreadId) {
|
|
|
835
841
|
return person;
|
|
836
842
|
return person && !cloudOwnerPerson(person) ? person : CLOUD_NOBODY_KEY;
|
|
837
843
|
}
|
|
838
|
-
/** The model a thread a bot opens from one of its own threads starts on
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
+
/** The model a thread a bot opens from one of its own threads starts on
|
|
845
|
+
* (#1950): the model a person picked in that thread, kept as the new thread's
|
|
846
|
+
* own. A thread that follows the bot opens one that follows it too. Only the
|
|
847
|
+
* bot's own thread counts — a room or a teammate's thread is not one of its
|
|
848
|
+
* tasks, and another bot's model belongs to another engine. Approval stays
|
|
849
|
+
* the bot's, except that a level the inherited engine would have to confirm
|
|
850
|
+
* on a model switch starts the new thread in Ask. */
|
|
844
851
|
function parentThreadModel(botId, parentThreadId) {
|
|
845
852
|
// The stored profile, never a per-thread projection: the comparison is
|
|
846
853
|
// against the defaults the new thread would otherwise get.
|
|
@@ -1668,6 +1675,9 @@ const managedPolicy = new ManagedDesktopPolicy({ onChange: () => {
|
|
|
1668
1675
|
broadcast({ kind: "config", ...configStatus() });
|
|
1669
1676
|
resyncOpenCodeProviderKeys();
|
|
1670
1677
|
} });
|
|
1678
|
+
// A key the provider refused, or accepted again, changes what Model
|
|
1679
|
+
// providers and the picker show; the config event refreshes /api/instances.
|
|
1680
|
+
onKeyRejectionChange(() => broadcast({ kind: "config", ...configStatus() }));
|
|
1671
1681
|
/** A computer kind this server will not use, refused before anything is
|
|
1672
1682
|
* prepared: a Cloud home never offers this computer or a Local VM
|
|
1673
1683
|
* (cloud-home.ts), and an enrolled organisation may disallow any kind. */
|
|
@@ -2055,6 +2065,9 @@ function agentsIntegration(botId, threadId, depth, skillAuthoring, generation, r
|
|
|
2055
2065
|
const speaking = botForThread(botId, threadId) ?? store.bot(botId);
|
|
2056
2066
|
return tts.voiceReady(cfg, speaking?.voice) && speaking?.voiceNotes !== false ? "1" : "0";
|
|
2057
2067
|
})(),
|
|
2068
|
+
// And for a role: team setup, bot creation and deletion, rooms and
|
|
2069
|
+
// retries are shown only to a Chief of Staff, whom their routes require.
|
|
2070
|
+
OMB_CHIEF_OF_STAFF: store.bot(botId)?.chiefOfStaff === true ? "1" : "0",
|
|
2058
2071
|
},
|
|
2059
2072
|
};
|
|
2060
2073
|
}
|
|
@@ -3005,6 +3018,62 @@ function checkedTaskModelSwitch(current, raw, updateBotDefault, resetApprovalToA
|
|
|
3005
3018
|
}
|
|
3006
3019
|
return checked;
|
|
3007
3020
|
}
|
|
3021
|
+
/** An engine as a turn's start sees it, without waiting on its CLI: the live
|
|
3022
|
+
* instance and what its last read said (registry.lastSnapshot). Before the
|
|
3023
|
+
* first read it counts as available, so only what is known stops a pick. */
|
|
3024
|
+
function threadEngine(instanceId) {
|
|
3025
|
+
const live = registry.get(instanceId);
|
|
3026
|
+
if (!live)
|
|
3027
|
+
return undefined;
|
|
3028
|
+
return {
|
|
3029
|
+
instanceId, driverKind: live.driverKind, enabled: live.enabled, models: live.models,
|
|
3030
|
+
capabilities: { modelVariants: live.adapter.capabilities.modelVariants === true },
|
|
3031
|
+
snapshot: registry.lastSnapshot(instanceId) ?? { state: "available" },
|
|
3032
|
+
access: providerConfigs()[instanceId]?.access ?? registry.access(instanceId),
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
/** This thread's own model, when it gives way to its bot's now
|
|
3036
|
+
* (threadModelFallback); null when the thread follows its bot or its own
|
|
3037
|
+
* model runs as picked. */
|
|
3038
|
+
function threadModelGivingWay(botId, threadId) {
|
|
3039
|
+
const bot = store.bot(botId);
|
|
3040
|
+
const own = store.taskByThread(botId, threadId)?.modelSelection;
|
|
3041
|
+
if (!bot || !own)
|
|
3042
|
+
return null;
|
|
3043
|
+
return threadModelFallback(own, bot.modelSelection, threadEngine, {
|
|
3044
|
+
refusal: policyModelRefusal,
|
|
3045
|
+
...(hostedModels ? { allows: (selection) => hostedModels.allows(selection) } : {}),
|
|
3046
|
+
}) ? own : null;
|
|
3047
|
+
}
|
|
3048
|
+
/** The model a turn in this thread would run on now (healThreadModel). */
|
|
3049
|
+
function turnSelection(botId, threadId) {
|
|
3050
|
+
return threadModelGivingWay(botId, threadId) ? store.bot(botId)?.modelSelection : store.projectBotForTask(botId, threadId)?.modelSelection;
|
|
3051
|
+
}
|
|
3052
|
+
/** A model as a person reads it: its catalog label, after its engine's name
|
|
3053
|
+
* while that engine is here. */
|
|
3054
|
+
function modelName(selection) {
|
|
3055
|
+
const instance = registry.get(selection.instanceId);
|
|
3056
|
+
const label = instance?.models.options.find((option) => option.id === selection.model)?.label ?? selection.model;
|
|
3057
|
+
return instance ? `${instance.displayName || instance.driverKind} · ${label}` : label;
|
|
3058
|
+
}
|
|
3059
|
+
/** A thread's own model that can't run here gives way to its bot's: the turn
|
|
3060
|
+
* about to start runs on the bot's model, the thread follows the bot from
|
|
3061
|
+
* then on, and one plain line in it says so. Every turn starts through
|
|
3062
|
+
* startTurn (a person's message, a routine, a delegation, a webhook, a
|
|
3063
|
+
* queued follow-up), so this is the one place it happens; a room turn
|
|
3064
|
+
* already runs on each member's own model. */
|
|
3065
|
+
function healThreadModel(botId, threadId) {
|
|
3066
|
+
const bot = store.bot(botId);
|
|
3067
|
+
const own = threadModelGivingWay(botId, threadId);
|
|
3068
|
+
if (!bot || !own || bot.approvalGrant || threadBusy(botId, threadId))
|
|
3069
|
+
return;
|
|
3070
|
+
if (!store.followBotModel(botId, [threadId], (from, to) => hostedModels?.resetTask(from, to) ?? {}).length)
|
|
3071
|
+
return;
|
|
3072
|
+
store.appendMessage(threadId, { role: "bot", kind: "activity", tool: {
|
|
3073
|
+
name: `notice: This thread's model (${modelName(own)}) isn't available, so it now uses ${bot.name}'s model (${modelName(bot.modelSelection)}).`,
|
|
3074
|
+
ok: true,
|
|
3075
|
+
} });
|
|
3076
|
+
}
|
|
3008
3077
|
function checkedExportSkillNames(value, bots) {
|
|
3009
3078
|
// Sharing instructions is explicit: ordinary package exports include none.
|
|
3010
3079
|
if (value === undefined)
|
|
@@ -3201,15 +3270,27 @@ function checkedMemberIds(value) {
|
|
|
3201
3270
|
return { ok: true, memberIds };
|
|
3202
3271
|
}
|
|
3203
3272
|
let bootSelection = { instanceId: "", model: "" };
|
|
3273
|
+
/** The engines read at start, while it runs (below). It is where the server
|
|
3274
|
+
* learns what each engine supports (a Claude CLI's version and flags), so a
|
|
3275
|
+
* turn waits for it, as every turn did when the server listened only after
|
|
3276
|
+
* that read. */
|
|
3277
|
+
let enginesRead = null;
|
|
3204
3278
|
/** Preset bots for New bot, from shared files and the organization (presets.ts). */
|
|
3205
3279
|
const presetStore = createPresetStore();
|
|
3206
|
-
const store = new Store(() => bootSelection, (selection) => withNewBotEffort(selection, cfg.newBots?.effort, registry.get(selection.instanceId)?.adapter.capabilities.effortLevels));
|
|
3280
|
+
const store = new Store(() => bootSelection, (selection) => withNewBotEffort(selection, cfg.newBots?.effort, registry.get(selection.instanceId)?.adapter.capabilities.effortLevels), (instanceId) => registry.cliTarget(instanceId)?.driverKind);
|
|
3207
3281
|
const teamComputers = new TeamComputers(join(DATA_DIR, "team-computers.json"), ENVIRONMENT_ID);
|
|
3208
3282
|
let followupsReady = false;
|
|
3209
3283
|
const sendSequencer = new SendSequencer();
|
|
3210
3284
|
// Before the new-bot default is read: a saved default may name the engine.
|
|
3211
3285
|
await retryComputerEngineMove();
|
|
3212
|
-
|
|
3286
|
+
// Reading the new-bot default probes every engine CLI (each up to its
|
|
3287
|
+
// timeout), and at start only the first-run seed needs the answer. A later
|
|
3288
|
+
// start listens without waiting: the same read runs behind it, and turns
|
|
3289
|
+
// wait for it (enginesRead).
|
|
3290
|
+
if (store.needsSeed())
|
|
3291
|
+
bootSelection = await defaultSelection();
|
|
3292
|
+
else
|
|
3293
|
+
enginesRead = defaultSelection().then((selection) => { bootSelection = selection; }, (error) => console.warn(`[engines] reading the engines at start failed: ${error instanceof Error ? error.message : String(error)}`)).finally(() => { enginesRead = null; });
|
|
3213
3294
|
store.seedIfEmpty();
|
|
3214
3295
|
hostedModels?.reconcile(store);
|
|
3215
3296
|
// Skills library boot sweep (features.skillsLibrary): migrate per-bot
|
|
@@ -3263,11 +3344,13 @@ if (browserCleanupReferencesReconciled)
|
|
|
3263
3344
|
* than the desktop window did. Stripped here rather than at each call site
|
|
3264
3345
|
* so a new broadcast cannot forget. */
|
|
3265
3346
|
let activeCoordinationForThread = (_threadId) => false;
|
|
3266
|
-
|
|
3347
|
+
/** One thread as a client sees it. `botModel` is its bot's model: a thread
|
|
3348
|
+
* without a model of its own runs on it (toWireTask). */
|
|
3349
|
+
const wireTask = (task, botModel) => {
|
|
3267
3350
|
// Time-based snoozes heal on read against the server clock — no client
|
|
3268
3351
|
// timer, no device skew. The 0 sentinel ("until new activity") is not a
|
|
3269
3352
|
// time and survives reads; only a wake event in the store clears it.
|
|
3270
|
-
const { snoozedUntil, ...base } = toWireTask(task);
|
|
3353
|
+
const { snoozedUntil, ...base } = toWireTask(task, botModel);
|
|
3271
3354
|
const coordinated = { ...base, waitingForTeammates: activeCoordinationForThread(task.threadId) && !task.busy };
|
|
3272
3355
|
const asleep = snoozedUntil === 0 || (snoozedUntil !== undefined && snoozedUntil > Date.now());
|
|
3273
3356
|
return asleep ? { ...coordinated, snoozedUntil } : coordinated;
|
|
@@ -3281,7 +3364,7 @@ const wireBot = (bot) => {
|
|
|
3281
3364
|
? { ...rest, approvalMode: "ask", autoApprove: false }
|
|
3282
3365
|
: rest;
|
|
3283
3366
|
return { ...visible, waitingForTeammates: activeCoordinationForThread(bot.threadId) && !threadBusy(bot.id, bot.threadId),
|
|
3284
|
-
avatarUrl: visible.avatarUrl ?? null, ...(tasks ? { tasks: tasks.map(wireTask) } : {}) };
|
|
3367
|
+
avatarUrl: visible.avatarUrl ?? null, ...(tasks ? { tasks: tasks.map((task) => wireTask(task, bot.modelSelection)) } : {}) };
|
|
3285
3368
|
};
|
|
3286
3369
|
function toolScopeForTurn(botId) {
|
|
3287
3370
|
const bot = store.bot(botId);
|
|
@@ -3296,7 +3379,7 @@ function toolScopeForTurn(botId) {
|
|
|
3296
3379
|
* can validate it before sending the confirmation that makes it effective. */
|
|
3297
3380
|
const wireTrustedApprovalBot = (bot) => {
|
|
3298
3381
|
const { resumeCursors: _resumeCursors, tasks, approvalGrant: _approvalGrant, lastProfileRequestId: _lastProfileRequestId, lastTeamSetupReceipt: _lastTeamSetupReceipt, packageBase: _packageBase, assignedSkills: _assignedSkills, ...rest } = bot;
|
|
3299
|
-
return { ...rest, approvalMode: approvalModeFor(rest), avatarUrl: rest.avatarUrl ?? null, ...(tasks ? { tasks: tasks.map(wireTask) } : {}) };
|
|
3382
|
+
return { ...rest, approvalMode: approvalModeFor(rest), avatarUrl: rest.avatarUrl ?? null, ...(tasks ? { tasks: tasks.map((task) => wireTask(task, bot.modelSelection)) } : {}) };
|
|
3300
3383
|
};
|
|
3301
3384
|
/** A settings-based preview, not a receipt of a dispatched turn. No
|
|
3302
3385
|
* provisioning or credentials are needed to inspect it. The selected engine
|
|
@@ -3481,6 +3564,24 @@ function sourceApprovalMode(botId, threadId) {
|
|
|
3481
3564
|
function fullAccessForSource(botId, threadId) {
|
|
3482
3565
|
return sourceApprovalMode(botId, threadId) === "full";
|
|
3483
3566
|
}
|
|
3567
|
+
/** On a Cloud home a turn a guest drives keeps a card for every change,
|
|
3568
|
+
* even one to the bot itself. */
|
|
3569
|
+
function selfApplyBlocked(threadId) {
|
|
3570
|
+
return cloudGuestDriven(threadId);
|
|
3571
|
+
}
|
|
3572
|
+
/** Whether a bot's routine, skill, profile or model change applies without
|
|
3573
|
+
* a person, and why: Full access applies everything as it always has, and
|
|
3574
|
+
* at any level a change to the bot itself applies, shown as a one-line
|
|
3575
|
+
* receipt with Undo (server/direct-apply.ts). Team setup and peer approvals
|
|
3576
|
+
* stay Full access only (fullAccessForSource). */
|
|
3577
|
+
function appliesDirectly(botId, threadId, targetBotId) {
|
|
3578
|
+
return directApply({
|
|
3579
|
+
fullAccess: fullAccessForSource(botId, threadId),
|
|
3580
|
+
botId,
|
|
3581
|
+
targetBotId,
|
|
3582
|
+
blocked: selfApplyBlocked(threadId) || !connectorThread(botId, threadId),
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3484
3585
|
function peerReviewRequired(bot, threadId) {
|
|
3485
3586
|
return Boolean(bot.approvePeerComms && !fullAccessForSource(bot.id, threadId));
|
|
3486
3587
|
}
|
|
@@ -3950,7 +4051,7 @@ const storedAvatarExists = (avatarUrl) => attachmentExists(avatarUrl.slice("/api
|
|
|
3950
4051
|
const publicBot = (bot) => ({
|
|
3951
4052
|
...wireBot(bot),
|
|
3952
4053
|
...messagePage(bot.threadId, DEFAULT_PAGE),
|
|
3953
|
-
tasks: store.tasks(bot.id).map(wireTask),
|
|
4054
|
+
tasks: store.tasks(bot.id).map((task) => wireTask(task, bot.modelSelection)),
|
|
3954
4055
|
});
|
|
3955
4056
|
const publicBotQueuedMessages = () => queuedSteerSnapshot((botId, threadId) => Boolean(store.taskByThread(botId, threadId)));
|
|
3956
4057
|
// busyBotId names only the speaker that currently owns the provider process.
|
|
@@ -4142,6 +4243,15 @@ function updateChannel(groupId, value) {
|
|
|
4142
4243
|
throw Object.assign(new Error("invalid default responder"), { status: 400 });
|
|
4143
4244
|
patch.defaultResponder = responder;
|
|
4144
4245
|
}
|
|
4246
|
+
if (Object.prototype.hasOwnProperty.call(body, "turnTimeoutMinutes")) {
|
|
4247
|
+
if (!existing.dm) {
|
|
4248
|
+
throw Object.assign(new Error("set the turn limit on that conversation"), { status: 400 });
|
|
4249
|
+
}
|
|
4250
|
+
const parsed = parseConversationTurnTimeout(body.turnTimeoutMinutes);
|
|
4251
|
+
if (!parsed.ok)
|
|
4252
|
+
throw Object.assign(new Error(parsed.error), { status: 400 });
|
|
4253
|
+
patch.turnTimeoutMinutes = parsed.minutes;
|
|
4254
|
+
}
|
|
4145
4255
|
if (body.cwd !== undefined) {
|
|
4146
4256
|
if (existing.dm)
|
|
4147
4257
|
throw Object.assign(new Error("direct-message channels cannot have a working folder"), { status: 400 });
|
|
@@ -4462,7 +4572,14 @@ function publicGroupState(record) {
|
|
|
4462
4572
|
usage = groupUsageReader.forThread(group.threadId);
|
|
4463
4573
|
}
|
|
4464
4574
|
catch { /* accounting must not block chat */ }
|
|
4465
|
-
return {
|
|
4575
|
+
return {
|
|
4576
|
+
...group,
|
|
4577
|
+
// Always present so a client can tell "back to the group default"
|
|
4578
|
+
// apart from a partial patch that did not mention the field.
|
|
4579
|
+
turnTimeoutMinutes: record.turnTimeoutMinutes ?? null,
|
|
4580
|
+
usage: usage ?? null,
|
|
4581
|
+
working: groupIsWorking(group) || [...roomHandoffs.nodes.values()].some(n => n.groupId === group.id && !["completed", "failed", "cancelled"].includes(n.status)),
|
|
4582
|
+
};
|
|
4466
4583
|
}
|
|
4467
4584
|
function beginGroupTurnOperation(groupId, threadId, botIds = []) {
|
|
4468
4585
|
const operation = {
|
|
@@ -8847,6 +8964,8 @@ async function startTurn(botId, text, opts) {
|
|
|
8847
8964
|
},
|
|
8848
8965
|
};
|
|
8849
8966
|
}
|
|
8967
|
+
// A thread whose own model can't run runs on its bot's from now on.
|
|
8968
|
+
healThreadModel(botId, threadId);
|
|
8850
8969
|
const bot = store.projectBotForTask(botId, threadId);
|
|
8851
8970
|
if (!bot)
|
|
8852
8971
|
throw Object.assign(new Error("no such task"), { status: 404 });
|
|
@@ -9088,6 +9207,11 @@ async function startTurn(botId, text, opts) {
|
|
|
9088
9207
|
turnContext.delete(threadId);
|
|
9089
9208
|
void (async () => {
|
|
9090
9209
|
try {
|
|
9210
|
+
if (enginesRead) {
|
|
9211
|
+
await enginesRead;
|
|
9212
|
+
if (!directTurnClaimIsCurrent(bot.id, dispatchClaimId, threadId))
|
|
9213
|
+
throw new DirectTurnSetupCancelled("turn stopped while the engines were read");
|
|
9214
|
+
}
|
|
9091
9215
|
// Readiness can wait on the network. Admit the task first so its busy
|
|
9092
9216
|
// state, Stop action and watchdog own that wait just like VM setup.
|
|
9093
9217
|
if (opts?.runOn === "cloud") {
|
|
@@ -10042,6 +10166,7 @@ async function startTurn(botId, text, opts) {
|
|
|
10042
10166
|
mentionTurn: tagged.length > 0,
|
|
10043
10167
|
integrations,
|
|
10044
10168
|
mcpFromUserConfig: claudeUserMcpEnabled(cfg) && !managedPolicy.restrictsMcp(),
|
|
10169
|
+
mcpCallTimeoutMs: mcpCallTimeoutMinutes(cfg) * 60_000,
|
|
10045
10170
|
cwd,
|
|
10046
10171
|
}), () => !directTurnClaimExists(bot.id, dispatchClaimId, threadId), async () => {
|
|
10047
10172
|
await instance.adapter.interruptTurn(threadId).catch(() => { });
|
|
@@ -10279,6 +10404,9 @@ function routineSourceOwner(run) {
|
|
|
10279
10404
|
? { bot, group: undefined, threadId: task.threadId }
|
|
10280
10405
|
: null;
|
|
10281
10406
|
}
|
|
10407
|
+
// Without a results snapshot (a run from before every routine reported to
|
|
10408
|
+
// its bot's main thread, or a bot with no visible thread) the chat that
|
|
10409
|
+
// made the routine is the destination.
|
|
10282
10410
|
const threadId = run.sourceThreadId?.trim();
|
|
10283
10411
|
if (!threadId)
|
|
10284
10412
|
return null;
|
|
@@ -10299,6 +10427,21 @@ function routineSourceOwner(run) {
|
|
|
10299
10427
|
const group = store.groupByThread(threadId);
|
|
10300
10428
|
return group?.memberIds.includes(bot.id) ? { bot, group, threadId } : null;
|
|
10301
10429
|
}
|
|
10430
|
+
/** There is no durable "main thread" (bot.threadId is only the UI
|
|
10431
|
+
* selection), so a routine reports into the bot's oldest open conversation:
|
|
10432
|
+
* the one it was created with until that is deleted or archived. Deleting
|
|
10433
|
+
* the last one already gives the bot a fresh conversation (Store.deleteTask). */
|
|
10434
|
+
function botMainThread(botId) {
|
|
10435
|
+
const visible = store.tasks(botId).filter((task) => !task.routineRunId);
|
|
10436
|
+
const open = visible.filter((task) => task.archivedAt === undefined);
|
|
10437
|
+
return (open.length ? open : visible).reduce((oldest, task) => (!oldest || task.createdAt < oldest.createdAt ? task : oldest), undefined)?.threadId;
|
|
10438
|
+
}
|
|
10439
|
+
/** Routines without a chosen thread used to get an automatic "<name> ·
|
|
10440
|
+
* Results" thread. One nobody renamed is that automatic default, not a
|
|
10441
|
+
* choice: new runs move to the main thread, and the old thread stays. */
|
|
10442
|
+
function legacyResultsThread(routineName, task) {
|
|
10443
|
+
return task.title.endsWith(" · Results") || task.title === threadTitleFrom(`${routineName} · Results`);
|
|
10444
|
+
}
|
|
10302
10445
|
function routineSourceThread(run) {
|
|
10303
10446
|
return routineSourceOwner(run)?.threadId ?? null;
|
|
10304
10447
|
}
|
|
@@ -10532,21 +10675,32 @@ routines = new RoutineManager({
|
|
|
10532
10675
|
return Boolean(bot && !bot.hidden && task && !task.routineRunId);
|
|
10533
10676
|
},
|
|
10534
10677
|
resolveResultsThread: (routine, forceNew) => {
|
|
10535
|
-
//
|
|
10536
|
-
//
|
|
10537
|
-
//
|
|
10538
|
-
|
|
10539
|
-
|
|
10678
|
+
// Every run reports into the routine's bot's main thread, wherever the
|
|
10679
|
+
// routine was made (chat, editor, API, or a teammate's request). A run
|
|
10680
|
+
// still executes in its own hidden task, which Run logs and Open run use.
|
|
10681
|
+
// A thread the person chose in "Post results to" keeps winning.
|
|
10682
|
+
const bot = store.bot(routine.botId);
|
|
10683
|
+
const chosen = !forceNew && routine.resultsThreadId && bot && !bot.hidden
|
|
10684
|
+
? store.taskByThread(bot.id, routine.resultsThreadId) : undefined;
|
|
10685
|
+
// On a Cloud home a routine reports only where its writer may write: a
|
|
10686
|
+
// guest's (or nobody's) keeps a results conversation opened as theirs.
|
|
10687
|
+
const opener = CLOUD_HOME ? routineOpener(routine.id) : undefined;
|
|
10688
|
+
const main = bot && !bot.hidden ? botMainThread(bot.id) : undefined;
|
|
10689
|
+
const mainAllowed = main && (!CLOUD_HOME || (opener === CLOUD_OWNER_KEY && !cloudGuestOpened(main)));
|
|
10690
|
+
const keepChosen = chosen && !chosen.routineRunId && !(mainAllowed && legacyResultsThread(routine.name, chosen));
|
|
10691
|
+
if (keepChosen)
|
|
10692
|
+
return chosen.threadId;
|
|
10693
|
+
if (mainAllowed || !CLOUD_HOME)
|
|
10694
|
+
return main;
|
|
10540
10695
|
const threadId = store.createTask(routine.botId, `${routine.name} · Results`, false)?.threadId;
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
if (threadId && CLOUD_HOME)
|
|
10544
|
-
threadStarters.set(threadId, routineOpener(routine.id));
|
|
10696
|
+
if (threadId)
|
|
10697
|
+
threadStarters.set(threadId, opener);
|
|
10545
10698
|
return threadId;
|
|
10546
10699
|
},
|
|
10547
10700
|
discardResultsThread: (botId, threadId) => {
|
|
10548
10701
|
const task = store.taskByThread(botId, threadId);
|
|
10549
|
-
|
|
10702
|
+
// The main thread is reused, never allocated, so a failed write keeps it.
|
|
10703
|
+
if (task && threadId !== botMainThread(botId) && !task.busy && !task.routineRunId && store.messagesFor(threadId).length === 0) {
|
|
10550
10704
|
store.deleteTask(botId, threadId);
|
|
10551
10705
|
handoffs.forget(threadId);
|
|
10552
10706
|
}
|
|
@@ -10718,7 +10872,11 @@ if (recoveryOwners.length > 0) {
|
|
|
10718
10872
|
// after the user confirms a durable card. Keeping this beside the scheduler
|
|
10719
10873
|
// makes the card resolvable after an app restart without involving the model.
|
|
10720
10874
|
async function cloudRoutineReadiness(botId, threadId) {
|
|
10721
|
-
const
|
|
10875
|
+
const projected = threadId ? store.projectBotForTask(botId, threadId) : store.bot(botId);
|
|
10876
|
+
// The model its turn will run on: a thread's own model that can't run
|
|
10877
|
+
// gives way to the bot's when the turn starts (healThreadModel).
|
|
10878
|
+
const selection = threadId ? turnSelection(botId, threadId) : projected?.modelSelection;
|
|
10879
|
+
const bot = projected && selection ? { ...projected, modelSelection: selection } : projected;
|
|
10722
10880
|
if (!bot || bot.hidden)
|
|
10723
10881
|
return { ready: false, reason: "The routine's target bot no longer exists." };
|
|
10724
10882
|
if (!boat.boatConfigured(cfg)) {
|
|
@@ -10758,7 +10916,7 @@ async function cloudRoutineReadiness(botId, threadId) {
|
|
|
10758
10916
|
const routineRequests = new RoutineRequestService({
|
|
10759
10917
|
store,
|
|
10760
10918
|
routines,
|
|
10761
|
-
autoApply:
|
|
10919
|
+
autoApply: appliesDirectly,
|
|
10762
10920
|
cloudReady: cloudRoutineReadiness,
|
|
10763
10921
|
canPersist: proposalPersistence,
|
|
10764
10922
|
// Cross-bot routines: the confirmation card can sit open indefinitely, so
|
|
@@ -11091,14 +11249,14 @@ function resolveAndSendTrust(res, threadId, requestId, behavior) {
|
|
|
11091
11249
|
}
|
|
11092
11250
|
const profileRequests = new ProfileRequestService({
|
|
11093
11251
|
store,
|
|
11094
|
-
autoApply:
|
|
11252
|
+
autoApply: appliesDirectly,
|
|
11095
11253
|
canPersist: proposalPersistence,
|
|
11096
11254
|
// A Chief may change a section peer; anyone else only itself. Re-checked at confirm.
|
|
11097
11255
|
validateTarget: chiefPeerTargetRule("profile"),
|
|
11098
11256
|
});
|
|
11099
11257
|
const modelRequests = new ModelRequestService({
|
|
11100
11258
|
store,
|
|
11101
|
-
autoApply:
|
|
11259
|
+
autoApply: appliesDirectly,
|
|
11102
11260
|
canPersist: proposalPersistence,
|
|
11103
11261
|
// Same authority rule as profile proposals: a Chief may name one section peer.
|
|
11104
11262
|
validateTarget: chiefPeerTargetRule("default model"),
|
|
@@ -11416,6 +11574,104 @@ function resolveAndSendModel(res, args) {
|
|
|
11416
11574
|
json(res, 200, { ok: true, outcome: "rejected" });
|
|
11417
11575
|
return true;
|
|
11418
11576
|
}
|
|
11577
|
+
/** Undo for a learned skill a bot wrote without a person (undoSkillWrite). */
|
|
11578
|
+
function undoSkillCard(botId, threadId, messageId) {
|
|
11579
|
+
const message = store.messagesFor(threadId).find((candidate) => candidate.id === messageId);
|
|
11580
|
+
const card = message?.card;
|
|
11581
|
+
const request = card?.skillRequest;
|
|
11582
|
+
if (!message || !card || !request)
|
|
11583
|
+
return { claimed: false };
|
|
11584
|
+
if (card.undone)
|
|
11585
|
+
return { claimed: true, state: "already_undone" };
|
|
11586
|
+
if (!card.autoApplied || card.answered !== "allow") {
|
|
11587
|
+
return { claimed: true, state: "invalid", error: "Only a change that applied on its own can be undone here.", status: 409 };
|
|
11588
|
+
}
|
|
11589
|
+
if (request.botId !== botId || request.threadId !== threadId) {
|
|
11590
|
+
return { claimed: true, state: "invalid", error: "this skill request belongs to a different bot", status: 403 };
|
|
11591
|
+
}
|
|
11592
|
+
const undone = undoSkillWrite(botId, {
|
|
11593
|
+
stagedId: request.stagedId,
|
|
11594
|
+
name: request.name,
|
|
11595
|
+
action: request.action,
|
|
11596
|
+
...(request.previous ? { previous: { skillMd: request.previous.preview, source: request.previous.source } } : {}),
|
|
11597
|
+
});
|
|
11598
|
+
if ("stale" in undone)
|
|
11599
|
+
return { claimed: true, state: "invalid", error: UNDO_STALE, status: 409, stale: true };
|
|
11600
|
+
if ("error" in undone)
|
|
11601
|
+
return { claimed: true, state: "invalid", error: undone.error, status: 409 };
|
|
11602
|
+
store.patchMessage(threadId, message.id, { card: { ...card, undone: true } });
|
|
11603
|
+
return { claimed: true, state: "undone" };
|
|
11604
|
+
}
|
|
11605
|
+
/** Undo on the one-line receipt of a change that applied without a person
|
|
11606
|
+
* (a bot's change to itself, or Full access). The caller authorizes it
|
|
11607
|
+
* exactly like answering that card; the card remembers it was undone. */
|
|
11608
|
+
function undoAppliedChange(res, threadId, requestId) {
|
|
11609
|
+
const message = store.messagesFor(threadId).find((candidate) => candidate.card?.requestId === requestId);
|
|
11610
|
+
const card = message?.card;
|
|
11611
|
+
if (!message || !card)
|
|
11612
|
+
return json(res, 404, { error: "this change is no longer in the conversation" });
|
|
11613
|
+
// The proposing bot comes from the conversation (a room card's trusted
|
|
11614
|
+
// sender, else the thread's bot), never from the card payload.
|
|
11615
|
+
const botId = message.from?.botId ?? store.botByThread(threadId)?.id;
|
|
11616
|
+
if (!botId)
|
|
11617
|
+
return json(res, 400, { error: "this change has no valid owner" });
|
|
11618
|
+
let result = { claimed: false };
|
|
11619
|
+
if (card.routineRequest) {
|
|
11620
|
+
const undone = routineRequests.undo({ botId, threadId, requestId });
|
|
11621
|
+
// On a Cloud home a routine Undo removed is forgotten, and one it put
|
|
11622
|
+
// back is the owner's only if it was theirs before the change.
|
|
11623
|
+
if (undone.claimed && undone.state === "undone" && cloudRoutineAuthors) {
|
|
11624
|
+
if (undone.action === "create")
|
|
11625
|
+
cloudRoutineAuthors.remove(undone.routineId);
|
|
11626
|
+
else if (undone.action === "update") {
|
|
11627
|
+
if (undone.ownersBefore)
|
|
11628
|
+
cloudOwnersRoutine(undone.routineId);
|
|
11629
|
+
else
|
|
11630
|
+
cloudRoutineAuthors.forget(undone.routineId);
|
|
11631
|
+
}
|
|
11632
|
+
else if (undone.action === "delete" && undone.restoredId) {
|
|
11633
|
+
if (undone.ownersBefore)
|
|
11634
|
+
cloudOwnersRoutine(undone.restoredId);
|
|
11635
|
+
else
|
|
11636
|
+
cloudRoutineAuthors.wrote(undone.restoredId, CLOUD_NOBODY_KEY);
|
|
11637
|
+
}
|
|
11638
|
+
}
|
|
11639
|
+
result = undone;
|
|
11640
|
+
}
|
|
11641
|
+
else if (card.profileRequest) {
|
|
11642
|
+
const undone = profileRequests.undo({ botId, threadId, requestId });
|
|
11643
|
+
if (undone.claimed && undone.state === "undone") {
|
|
11644
|
+
const target = store.bot(undone.targetBotId);
|
|
11645
|
+
if (target)
|
|
11646
|
+
broadcast({ kind: "bot", bot: wireBot(target) });
|
|
11647
|
+
}
|
|
11648
|
+
result = undone;
|
|
11649
|
+
}
|
|
11650
|
+
else if (card.modelRequest) {
|
|
11651
|
+
const undone = modelRequests.undo({ botId, threadId, requestId });
|
|
11652
|
+
if (undone.claimed && undone.state === "undone") {
|
|
11653
|
+
const target = store.bot(undone.targetBotId);
|
|
11654
|
+
if (target)
|
|
11655
|
+
broadcast({ kind: "bot", bot: wireBot(target) });
|
|
11656
|
+
}
|
|
11657
|
+
result = undone;
|
|
11658
|
+
}
|
|
11659
|
+
else if (card.skillRequest) {
|
|
11660
|
+
result = undoSkillCard(botId, threadId, message.id);
|
|
11661
|
+
}
|
|
11662
|
+
if (!result.claimed)
|
|
11663
|
+
return json(res, 409, { error: "Only a change that applied on its own can be undone here." });
|
|
11664
|
+
if (result.state === "invalid") {
|
|
11665
|
+
return json(res, result.status, { error: result.error, ...(result.stale ? { code: "changed-since" } : {}) });
|
|
11666
|
+
}
|
|
11667
|
+
if (result.state === "undone") {
|
|
11668
|
+
appendDecision(DATA_DIR, {
|
|
11669
|
+
threadId, requestId, botId, botName: store.bot(botId)?.name,
|
|
11670
|
+
tool: card.tool, summary: card.subtitle, decision: "user-undone", source: "user",
|
|
11671
|
+
});
|
|
11672
|
+
}
|
|
11673
|
+
json(res, 200, { ok: true, undone: true, ...(result.state === "already_undone" ? { alreadyUndone: true } : {}) });
|
|
11674
|
+
}
|
|
11419
11675
|
// Webhook definitions are independent from calendar schedules, but every
|
|
11420
11676
|
// delivery joins the same RoutineManager queue. That keeps unattended work
|
|
11421
11677
|
// ordered behind a busy MAUS and gives webhook runs the same durable receipts.
|
|
@@ -11437,10 +11693,9 @@ const webhooks = new WebhookManager({
|
|
|
11437
11693
|
return;
|
|
11438
11694
|
store.appendMessage(threadId, { role: "bot", kind: "text", text, webhookPost: true });
|
|
11439
11695
|
},
|
|
11440
|
-
//
|
|
11441
|
-
//
|
|
11442
|
-
//
|
|
11443
|
-
// does), reused forever after via trigger.resultsThreadId.
|
|
11696
|
+
// Create-on-first-use, never activated (so it never steals the bot's
|
|
11697
|
+
// live selection the way POST /api/bots/:id/tasks does), reused forever
|
|
11698
|
+
// after via trigger.resultsThreadId.
|
|
11444
11699
|
resolvePostThread: (trigger, forceNew) => {
|
|
11445
11700
|
if (!forceNew && trigger.resultsThreadId)
|
|
11446
11701
|
return trigger.resultsThreadId;
|
|
@@ -12207,6 +12462,10 @@ setupRetry = 0) {
|
|
|
12207
12462
|
{ id: "skill-instructions", label: "Skill instructions", text: renderSkillInstructions(selectedSkills, { includeRoot: Boolean(workspace) }) },
|
|
12208
12463
|
{ id: "playbooks", label: "Playbooks", text: installedPlaybookInstructions(text, bot.playbooks) },
|
|
12209
12464
|
]);
|
|
12465
|
+
// The engines read at start is part of setup (it learns what this engine
|
|
12466
|
+
// supports): wait for it before the claim below.
|
|
12467
|
+
if (enginesRead)
|
|
12468
|
+
await enginesRead;
|
|
12210
12469
|
// run the turn and wait for it to settle, folding the reply text so a
|
|
12211
12470
|
// chained @mention can be routed afterwards
|
|
12212
12471
|
// Claim only after setup succeeded. An unavailable, busy, unsupported, or
|
|
@@ -12246,7 +12505,10 @@ setupRetry = 0) {
|
|
|
12246
12505
|
else
|
|
12247
12506
|
markCancelledProviderHandshake(threadId, retirementOwner);
|
|
12248
12507
|
};
|
|
12249
|
-
const
|
|
12508
|
+
const liveGroup = store.group(groupId) ?? group;
|
|
12509
|
+
const timeoutMinutes = effectiveRoomTurnTimeoutMinutes(roomTurnTimeoutMinutes(cfg), liveGroup.dm
|
|
12510
|
+
? liveGroup.turnTimeoutMinutes
|
|
12511
|
+
: store.groupTaskByThread(liveGroup.id, threadId)?.turnTimeoutMinutes);
|
|
12250
12512
|
const outcome = await new Promise((resolve) => {
|
|
12251
12513
|
let done = false;
|
|
12252
12514
|
let unsub = () => { };
|
|
@@ -12334,6 +12596,7 @@ setupRetry = 0) {
|
|
|
12334
12596
|
cwd,
|
|
12335
12597
|
integrations,
|
|
12336
12598
|
mcpFromUserConfig: claudeUserMcpEnabled(cfg) && !managedPolicy.restrictsMcp(),
|
|
12599
|
+
mcpCallTimeoutMs: mcpCallTimeoutMinutes(cfg) * 60_000,
|
|
12337
12600
|
...(instance.instanceId === readyBot.modelSelection.instanceId
|
|
12338
12601
|
? memberTurnSelection(readyBot.modelSelection)
|
|
12339
12602
|
: { model: instance.models.default }),
|
|
@@ -13367,14 +13630,16 @@ function roomPostEligibility(bot, group) {
|
|
|
13367
13630
|
}
|
|
13368
13631
|
return { ok: true };
|
|
13369
13632
|
}
|
|
13370
|
-
|
|
13633
|
+
/** `opensCard` false for a change that applies directly: it adds no open
|
|
13634
|
+
* card, so the open-card budget does not hold it back. */
|
|
13635
|
+
function proposalPersistence(botId, threadId, opensCard = true) {
|
|
13371
13636
|
if (!store.bot(botId)) {
|
|
13372
13637
|
return { ok: false, status: 403, error: "unknown sender" };
|
|
13373
13638
|
}
|
|
13374
13639
|
if (!connectorThread(botId, threadId)) {
|
|
13375
13640
|
return { ok: false, status: 403, error: "source conversation does not belong to sender" };
|
|
13376
13641
|
}
|
|
13377
|
-
if (fullAccessForSource(botId, threadId))
|
|
13642
|
+
if (!opensCard || fullAccessForSource(botId, threadId))
|
|
13378
13643
|
return { ok: true };
|
|
13379
13644
|
// Only cards on the visible branch can be acted on from the composer.
|
|
13380
13645
|
// Abandoned branches must not permanently consume the proposal quota.
|
|
@@ -13387,14 +13652,14 @@ function proposalPersistence(botId, threadId) {
|
|
|
13387
13652
|
? { ok: false, status: 429, error: "confirm or cancel an existing proposal first" }
|
|
13388
13653
|
: { ok: true };
|
|
13389
13654
|
}
|
|
13390
|
-
function skillProposalPersistence(botId, threadId) {
|
|
13655
|
+
function skillProposalPersistence(botId, threadId, opensCard = true) {
|
|
13391
13656
|
if (!store.bot(botId)) {
|
|
13392
13657
|
return { ok: false, status: 403, error: "unknown sender" };
|
|
13393
13658
|
}
|
|
13394
13659
|
if (!connectorThread(botId, threadId)) {
|
|
13395
13660
|
return { ok: false, status: 403, error: "source conversation does not belong to sender" };
|
|
13396
13661
|
}
|
|
13397
|
-
if (fullAccessForSource(botId, threadId))
|
|
13662
|
+
if (!opensCard || fullAccessForSource(botId, threadId))
|
|
13398
13663
|
return { ok: true };
|
|
13399
13664
|
const openRequests = store.activePath(threadId).filter((message) => message.card?.skillRequest?.botId === botId &&
|
|
13400
13665
|
!message.card.answered &&
|
|
@@ -13461,6 +13726,7 @@ function appendSkillRequestCard(args) {
|
|
|
13461
13726
|
sha256: args.staged.sha256,
|
|
13462
13727
|
warnings: args.staged.warnings,
|
|
13463
13728
|
createdAt: Date.now(),
|
|
13729
|
+
...(args.applied && args.previous ? { previous: args.previous } : {}),
|
|
13464
13730
|
};
|
|
13465
13731
|
const from = store.bot(args.botId);
|
|
13466
13732
|
store.appendMessage(args.threadId, {
|
|
@@ -13471,7 +13737,7 @@ function appendSkillRequestCard(args) {
|
|
|
13471
13737
|
title: copy.title,
|
|
13472
13738
|
subtitle: copy.subtitle,
|
|
13473
13739
|
options: args.applied ? [] : [args.staged.action === "create" ? "Enable" : "Update", "Deny"],
|
|
13474
|
-
...(args.applied ? { answered: "allow", title: `Skill "${args.staged.name}" ${args.staged.action === "create" ? "enabled" : "updated"}` } : {}),
|
|
13740
|
+
...(args.applied ? { answered: "allow", autoApplied: true, title: `Skill "${args.staged.name}" ${args.staged.action === "create" ? "enabled" : "updated"}` } : {}),
|
|
13475
13741
|
requestId,
|
|
13476
13742
|
tool: copy.tool,
|
|
13477
13743
|
skillRequest: payload,
|
|
@@ -14262,6 +14528,7 @@ function configStatus() {
|
|
|
14262
14528
|
// not a secret — the settings picker shows it; "" = follow the system
|
|
14263
14529
|
language: cfg.language ?? "",
|
|
14264
14530
|
rooms: { turnTimeoutMinutes: roomTurnTimeoutMinutes(cfg) },
|
|
14531
|
+
mcp: { callTimeoutMinutes: mcpCallTimeoutMinutes(cfg) },
|
|
14265
14532
|
automaticRecovery: cfg.automaticRecovery ?? { enabled: false },
|
|
14266
14533
|
// absent effort = no level is sent, so clients can tell it from any level
|
|
14267
14534
|
newBots: cfg.newBots?.effort ? { effort: cfg.newBots.effort } : {},
|
|
@@ -14617,15 +14884,22 @@ function serveStatic(res, path) {
|
|
|
14617
14884
|
try {
|
|
14618
14885
|
const data = readFileSync(file);
|
|
14619
14886
|
const type = MIME[extname(file)] ?? "application/octet-stream";
|
|
14620
|
-
|
|
14887
|
+
// Vite puts a content hash in the name of every file it writes under
|
|
14888
|
+
// /assets/, so a URL there never changes: the browser keeps it instead of
|
|
14889
|
+
// downloading the bundle on every load. That only holds while public/ has
|
|
14890
|
+
// no assets/ dir (index.test.ts checks). Pages are revalidated, so a new
|
|
14891
|
+
// build shows up on the next load.
|
|
14892
|
+
const cache = type === "text/html" ? "no-cache" : safe.startsWith("/assets/") ? "public, max-age=31536000, immutable" : null;
|
|
14893
|
+
res.writeHead(200, { "content-type": type, ...(type === "text/html" ? PAGE_HEADERS : {}), ...(cache ? { "cache-control": cache } : {}) });
|
|
14621
14894
|
res.end(data);
|
|
14622
14895
|
return true;
|
|
14623
14896
|
}
|
|
14624
14897
|
catch {
|
|
14625
|
-
// SPA fallback
|
|
14898
|
+
// SPA fallback. Never immutable: a missing chunk answered with the page
|
|
14899
|
+
// must not stay cached under the chunk's URL.
|
|
14626
14900
|
try {
|
|
14627
14901
|
const data = readFileSync(join(STATIC_DIR, "index.html"));
|
|
14628
|
-
res.writeHead(200, { "content-type": "text/html", ...PAGE_HEADERS });
|
|
14902
|
+
res.writeHead(200, { "content-type": "text/html", "cache-control": "no-cache", ...PAGE_HEADERS });
|
|
14629
14903
|
res.end(data);
|
|
14630
14904
|
return true;
|
|
14631
14905
|
}
|
|
@@ -14740,6 +15014,20 @@ ROUTES.push(createBotMemoryRoutes({
|
|
|
14740
15014
|
},
|
|
14741
15015
|
}));
|
|
14742
15016
|
ROUTES.push(createDeciderRoutes({ decider }));
|
|
15017
|
+
// "Switch them too": a bot's threads on a model of their own follow its model again.
|
|
15018
|
+
ROUTES.push(createThreadModelRoutes({
|
|
15019
|
+
bot: (id) => store.bot(id),
|
|
15020
|
+
busy: (botId, threadId) => threadBusy(botId, threadId),
|
|
15021
|
+
follow: (botId, threadIds) => store.followBotModel(botId, threadIds, (from, to) => hostedModels?.resetTask(from, to) ?? {}),
|
|
15022
|
+
// Like updateBotDefault on a thread: on a Cloud home, the owner's alone.
|
|
15023
|
+
mayChangeModel: (auth) => !CLOUD_HOME || cloudOwnerSession(auth),
|
|
15024
|
+
reply: (botId) => publicBot(store.bot(botId)),
|
|
15025
|
+
}));
|
|
15026
|
+
// Undo on the one-line receipt of a change that applied without a person.
|
|
15027
|
+
ROUTES.push(createUndoRoutes({
|
|
15028
|
+
refusal: (auth, threadId, requestId) => cardAnswerRefusal(auth, threadId, requestId, "allow"),
|
|
15029
|
+
undo: (auth, res, threadId, requestId) => withDecisionActor(decisionActorFor(auth), () => undoAppliedChange(res, threadId, requestId)),
|
|
15030
|
+
}));
|
|
14743
15031
|
ROUTES.push(createAntigravityLeftoverRoutes({
|
|
14744
15032
|
hosted: Boolean(hostedModels),
|
|
14745
15033
|
isAntigravity: (instanceId) => registry.get(instanceId)?.driverKind === "antigravityAgent",
|
|
@@ -14892,6 +15180,17 @@ const handleRequest = async (req, res) => {
|
|
|
14892
15180
|
if (refusal)
|
|
14893
15181
|
return json(res, 403, { error: refusal, code: "managed_policy" });
|
|
14894
15182
|
}
|
|
15183
|
+
// An MCP server's sign-in, started in a browser on another computer,
|
|
15184
|
+
// comes back here (server/mcp-oauth.ts). Public like the loopback
|
|
15185
|
+
// listener it stands in for: the state is the credential, checked once.
|
|
15186
|
+
if (path === MCP_OAUTH_CALLBACK_PATH) {
|
|
15187
|
+
const answer = method === "GET"
|
|
15188
|
+
? await mcpOAuth.publicCallback(req.headers.host, url.search)
|
|
15189
|
+
: { status: 400, text: "Invalid sign-in callback. Return to OpenMausBot and try again." };
|
|
15190
|
+
res.writeHead(answer.status, { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store", "referrer-policy": "no-referrer" });
|
|
15191
|
+
res.end(answer.text);
|
|
15192
|
+
return;
|
|
15193
|
+
}
|
|
14895
15194
|
// ── who is asking (server/request-auth.ts) ──────────────────────────
|
|
14896
15195
|
// Two public routes come first: what this server is, and turning a pairing
|
|
14897
15196
|
// code into a session. Everything else needs the loopback owner or a
|
|
@@ -15957,7 +16256,9 @@ const handleRequest = async (req, res) => {
|
|
|
15957
16256
|
forBot = { botId: target.id, name: target.name };
|
|
15958
16257
|
}
|
|
15959
16258
|
}
|
|
15960
|
-
|
|
16259
|
+
// Sender and conversation only: the service checks the open-card
|
|
16260
|
+
// budget once it knows whether this change opens a card at all.
|
|
16261
|
+
const persistence = proposalPersistence(from.id, fromThreadId, false);
|
|
15961
16262
|
if (!persistence.ok) {
|
|
15962
16263
|
return json(res, persistence.status, { error: persistence.error });
|
|
15963
16264
|
}
|
|
@@ -15991,6 +16292,12 @@ const handleRequest = async (req, res) => {
|
|
|
15991
16292
|
cloudRoutineAuthors?.forget(applied.resultId);
|
|
15992
16293
|
cloudRoutineAuthors?.wrote(applied.resultId, CLOUD_NOBODY_KEY);
|
|
15993
16294
|
}
|
|
16295
|
+
// Undo puts an updated or deleted routine back as the owner's only
|
|
16296
|
+
// if it was theirs before this change.
|
|
16297
|
+
if (applied && wasOwners && proposedCard?.autoApplied && proposedCard.routineRequest?.undo) {
|
|
16298
|
+
store.patchMessage(fromThreadId, proposed.messageId, { card: { ...proposedCard,
|
|
16299
|
+
routineRequest: { ...proposedCard.routineRequest, undo: { ...proposedCard.routineRequest.undo, ownersBefore: true } } } });
|
|
16300
|
+
}
|
|
15994
16301
|
appendDecision(DATA_DIR, {
|
|
15995
16302
|
threadId: fromThreadId,
|
|
15996
16303
|
requestId: proposed.requestId,
|
|
@@ -16001,7 +16308,7 @@ const handleRequest = async (req, res) => {
|
|
|
16001
16308
|
// response returned to the model.
|
|
16002
16309
|
summary: proposedCard?.subtitle ?? proposed.summary,
|
|
16003
16310
|
decision: proposed.state === "applied" ? "auto-approved" : "card-shown",
|
|
16004
|
-
source: proposed.state === "applied" ? "full-access" : "routine",
|
|
16311
|
+
source: proposed.state === "applied" ? proposed.appliedBy ?? "full-access" : "routine",
|
|
16005
16312
|
});
|
|
16006
16313
|
return json(res, 201, proposed);
|
|
16007
16314
|
}
|
|
@@ -16086,7 +16393,7 @@ const handleRequest = async (req, res) => {
|
|
|
16086
16393
|
appendDecision(DATA_DIR, {
|
|
16087
16394
|
threadId: body.fromThreadId, requestId: proposed.requestId, botId: from.id, botName: from.name,
|
|
16088
16395
|
tool: "update_profile", summary: proposed.detail, decision: proposed.state === "applied" ? "auto-approved" : "card-shown",
|
|
16089
|
-
source: proposed.state === "applied" ? "full-access" : "profile",
|
|
16396
|
+
source: proposed.state === "applied" ? proposed.appliedBy ?? "full-access" : "profile",
|
|
16090
16397
|
});
|
|
16091
16398
|
return json(res, 201, proposed);
|
|
16092
16399
|
}
|
|
@@ -16118,7 +16425,7 @@ const handleRequest = async (req, res) => {
|
|
|
16118
16425
|
appendDecision(DATA_DIR, {
|
|
16119
16426
|
threadId: body.fromThreadId, requestId: proposed.requestId, botId: from.id, botName: from.name,
|
|
16120
16427
|
tool: "update_model", summary: proposed.detail, decision: proposed.state === "applied" ? "auto-approved" : "card-shown",
|
|
16121
|
-
source: proposed.state === "applied" ? "full-access" : "model",
|
|
16428
|
+
source: proposed.state === "applied" ? proposed.appliedBy ?? "full-access" : "model",
|
|
16122
16429
|
});
|
|
16123
16430
|
return json(res, 201, proposed);
|
|
16124
16431
|
}
|
|
@@ -16330,7 +16637,10 @@ const handleRequest = async (req, res) => {
|
|
|
16330
16637
|
if (!connectorThread(from.id, fromThreadId)) {
|
|
16331
16638
|
return json(res, 403, { error: "source conversation does not belong to sender" });
|
|
16332
16639
|
}
|
|
16333
|
-
|
|
16640
|
+
// Skills written here are always the sender's own, so they apply at
|
|
16641
|
+
// any level, with a one-line receipt and Undo (appliesDirectly).
|
|
16642
|
+
const direct = appliesDirectly(from.id, fromThreadId, from.id);
|
|
16643
|
+
const persistence = skillProposalPersistence(from.id, fromThreadId, !direct);
|
|
16334
16644
|
if (!persistence.ok)
|
|
16335
16645
|
return json(res, persistence.status, { error: persistence.error });
|
|
16336
16646
|
const action = body.action === "create" || body.action === "update" ? body.action : "";
|
|
@@ -16356,11 +16666,15 @@ const handleRequest = async (req, res) => {
|
|
|
16356
16666
|
});
|
|
16357
16667
|
if ("error" in staged)
|
|
16358
16668
|
return json(res, 422, { error: staged.error });
|
|
16359
|
-
if (
|
|
16669
|
+
if (direct) {
|
|
16670
|
+
// What an update replaces, so Undo can put it back.
|
|
16671
|
+
const previousSkillMd = staged.action === "update" ? readSkillFile(from.id, staged.name) : null;
|
|
16672
|
+
const previousSource = listSkills(from.id).find((skill) => skill.name === staged.name)?.source;
|
|
16673
|
+
const previous = previousSkillMd !== null && previousSource ? { preview: previousSkillMd, source: previousSource } : undefined;
|
|
16360
16674
|
const applied = applySkillWriteWithReceipt(from.id, staged, () => {
|
|
16361
|
-
const receipt = appendSkillRequestCard({ botId: from.id, threadId: fromThreadId, staged, applied: true });
|
|
16675
|
+
const receipt = appendSkillRequestCard({ botId: from.id, threadId: fromThreadId, staged, applied: true, previous });
|
|
16362
16676
|
appendDecision(DATA_DIR, { threadId: fromThreadId, requestId: receipt.requestId, botId: from.id, botName: from.name,
|
|
16363
|
-
tool: "stage_skill", summary: receipt.summary, decision: "auto-approved", source:
|
|
16677
|
+
tool: "stage_skill", summary: receipt.summary, decision: "auto-approved", source: direct });
|
|
16364
16678
|
});
|
|
16365
16679
|
if ("error" in applied) {
|
|
16366
16680
|
rejectStagedSkillWrite(from.id, staged.id);
|
|
@@ -18048,6 +18362,9 @@ const handleRequest = async (req, res) => {
|
|
|
18048
18362
|
const message = store.appendMessage(threadId, {
|
|
18049
18363
|
role: "bot",
|
|
18050
18364
|
kind: "connector",
|
|
18365
|
+
// Read only by clients that predate the card (older phones draw
|
|
18366
|
+
// an unknown kind by its text); see connector-card-text.ts.
|
|
18367
|
+
text: connectorCardText(toolkit.label, item.alias),
|
|
18051
18368
|
...(owner.group ? { from: { botId: owner.bot.id, name: owner.bot.name, color: owner.bot.color } } : {}),
|
|
18052
18369
|
connector: {
|
|
18053
18370
|
slug: item.slug,
|
|
@@ -18460,7 +18777,7 @@ const handleRequest = async (req, res) => {
|
|
|
18460
18777
|
return json(res, 200, {
|
|
18461
18778
|
bots: shownBots.map((bot) => memberBot({
|
|
18462
18779
|
...wireBot(bot),
|
|
18463
|
-
tasks: store.tasks(bot.id).map(wireTask),
|
|
18780
|
+
tasks: store.tasks(bot.id).map((task) => wireTask(task, bot.modelSelection)),
|
|
18464
18781
|
...messagePage(bot.threadId, limit),
|
|
18465
18782
|
}, visible)),
|
|
18466
18783
|
botQueuedMessages: visible.everything ? queued : Object.fromEntries(Object.entries(queued).filter(([threadId]) => visible.thread(threadId))),
|
|
@@ -18593,7 +18910,28 @@ const handleRequest = async (req, res) => {
|
|
|
18593
18910
|
}
|
|
18594
18911
|
roots = messageFileRootsForThread(senderId, threadId);
|
|
18595
18912
|
}
|
|
18596
|
-
|
|
18913
|
+
// A link to a folder answers with its files; each one is then fetched
|
|
18914
|
+
// by plain name under the same message grant and file checks.
|
|
18915
|
+
const folderEntry = method === "POST" && body.entry !== undefined
|
|
18916
|
+
? messageFolderEntryName(body.entry)
|
|
18917
|
+
: undefined;
|
|
18918
|
+
if (folderEntry === null)
|
|
18919
|
+
return json(res, 400, { error: "entry must be one file name" });
|
|
18920
|
+
let file;
|
|
18921
|
+
try {
|
|
18922
|
+
file = folderEntry
|
|
18923
|
+
? await openMessageFolderEntry(href, folderEntry, roots)
|
|
18924
|
+
: await openMessageFile(href, roots);
|
|
18925
|
+
}
|
|
18926
|
+
catch (error) {
|
|
18927
|
+
// Only a client that asks for listings gets one; older clients keep the 400.
|
|
18928
|
+
if (body?.listFolder !== true || folderEntry || error?.code !== "directory")
|
|
18929
|
+
throw error;
|
|
18930
|
+
const listing = await listMessageFolder(href, roots);
|
|
18931
|
+
res.setHeader("x-openmausbot-folder", "1");
|
|
18932
|
+
res.setHeader("cache-control", "private, no-store");
|
|
18933
|
+
return json(res, 200, listing);
|
|
18934
|
+
}
|
|
18597
18935
|
if ((streamsMessageImage || botAttachment?.kind === "image") && !file.mime.startsWith("image/")) {
|
|
18598
18936
|
await file.handle.close();
|
|
18599
18937
|
return json(res, 415, { error: "only images can be previewed here" });
|
|
@@ -19424,7 +19762,7 @@ const handleRequest = async (req, res) => {
|
|
|
19424
19762
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
19425
19763
|
return json(res, 400, { error: "body must be a JSON object" });
|
|
19426
19764
|
}
|
|
19427
|
-
const allowed = new Set(["title", "pinned"]);
|
|
19765
|
+
const allowed = new Set(["title", "pinned", "turnTimeoutMinutes"]);
|
|
19428
19766
|
if (Object.keys(body).some((key) => !allowed.has(key)))
|
|
19429
19767
|
return json(res, 400, { error: "unsupported channel thread setting" });
|
|
19430
19768
|
const existing = store.groupTaskByThread(group.id, m[2]);
|
|
@@ -19432,12 +19770,16 @@ const handleRequest = async (req, res) => {
|
|
|
19432
19770
|
return json(res, 404, { error: "no such channel task" });
|
|
19433
19771
|
const pinning = body.pinned !== undefined;
|
|
19434
19772
|
const renaming = body.title !== undefined;
|
|
19435
|
-
|
|
19773
|
+
const limiting = Object.prototype.hasOwnProperty.call(body, "turnTimeoutMinutes");
|
|
19774
|
+
if (!pinning && !renaming && !limiting)
|
|
19436
19775
|
return json(res, 400, { error: "unsupported channel thread setting" });
|
|
19437
19776
|
if (pinning && typeof body.pinned !== "boolean")
|
|
19438
19777
|
return json(res, 400, { error: "pinned must be a boolean" });
|
|
19439
19778
|
if (renaming && typeof body.title !== "string")
|
|
19440
19779
|
return json(res, 400, { error: "title must be a string" });
|
|
19780
|
+
const parsedLimit = limiting ? parseConversationTurnTimeout(body.turnTimeoutMinutes) : null;
|
|
19781
|
+
if (parsedLimit && !parsedLimit.ok)
|
|
19782
|
+
return json(res, 400, { error: parsedLimit.error });
|
|
19441
19783
|
// Echoing the current title lets a newer client pin on an older server
|
|
19442
19784
|
// without the old handler turning a missing title into "Untitled".
|
|
19443
19785
|
// That echo is not a rename. A real rename stays blocked while working.
|
|
@@ -19445,10 +19787,19 @@ const handleRequest = async (req, res) => {
|
|
|
19445
19787
|
const notYours = titleChange ? cloudThreadRefusal(auth, m[2]) : null;
|
|
19446
19788
|
if (notYours)
|
|
19447
19789
|
return json(res, 403, { error: notYours });
|
|
19448
|
-
|
|
19790
|
+
// A longer limit applies to the next turn, so it can be saved while
|
|
19791
|
+
// this one is still running. A rename still waits.
|
|
19792
|
+
const settingsOnly = !titleChange && (pinning || limiting);
|
|
19793
|
+
if (channelTaskBlocked(group) && !settingsOnly) {
|
|
19449
19794
|
return json(res, 409, { error: "this channel is working or waiting on you — finish that turn first" });
|
|
19450
19795
|
}
|
|
19451
19796
|
let task = existing;
|
|
19797
|
+
if (limiting && parsedLimit?.ok) {
|
|
19798
|
+
const limited = store.setGroupTaskTurnTimeout(group.id, m[2], parsedLimit.minutes);
|
|
19799
|
+
if (!limited)
|
|
19800
|
+
return json(res, 404, { error: "no such channel task" });
|
|
19801
|
+
task = limited;
|
|
19802
|
+
}
|
|
19452
19803
|
if (pinning) {
|
|
19453
19804
|
const pinned = store.setGroupTaskPinned(group.id, m[2], body.pinned === true);
|
|
19454
19805
|
if (!pinned)
|
|
@@ -20282,10 +20633,12 @@ const handleRequest = async (req, res) => {
|
|
|
20282
20633
|
}
|
|
20283
20634
|
// patchBot persists first and emits the canonical bot change, which the
|
|
20284
20635
|
// store listener above turns into the slim wire-format SSE broadcast.
|
|
20636
|
+
// Every thread that follows the bot moves with it; the selected thread
|
|
20637
|
+
// follows it from now on too, as if picked there.
|
|
20285
20638
|
const bot = store.patchBot(existing.id, { modelSelection: checked.selection });
|
|
20286
20639
|
if (!bot)
|
|
20287
20640
|
return json(res, 404, { error: "no such bot" });
|
|
20288
|
-
store.patchTask(bot.id, selected.threadId, { modelSelection:
|
|
20641
|
+
store.patchTask(bot.id, selected.threadId, { modelSelection: undefined,
|
|
20289
20642
|
...hostedModels?.resetTask(selected.modelSelection, checked.selection) });
|
|
20290
20643
|
return json(res, 200, { bot: wireBot(bot) });
|
|
20291
20644
|
}
|
|
@@ -20971,8 +21324,10 @@ const handleRequest = async (req, res) => {
|
|
|
20971
21324
|
// and pins that already match stay; returning to Auto sweeps nothing.
|
|
20972
21325
|
if (computerSpecified && requestedComputer !== undefined)
|
|
20973
21326
|
store.clearAutoSurfacePins(m[1], requestedComputer);
|
|
21327
|
+
// A new bot model moves every thread that follows the bot; the selected
|
|
21328
|
+
// thread follows it from now on too.
|
|
20974
21329
|
if (normalizedSelection && selectedTask)
|
|
20975
|
-
store.patchTask(bot.id, selectedTask.threadId, { modelSelection:
|
|
21330
|
+
store.patchTask(bot.id, selectedTask.threadId, { modelSelection: undefined,
|
|
20976
21331
|
...hostedModels?.resetTask(selectedTask.modelSelection, normalizedSelection) });
|
|
20977
21332
|
if (existingBot && (bot.browserProfile !== beforeBrowserProfile || bot.browser !== beforeBrowserEnabled)) {
|
|
20978
21333
|
browserLive.closeForBot(bot.id);
|
|
@@ -21769,12 +22124,15 @@ const handleRequest = async (req, res) => {
|
|
|
21769
22124
|
if (phoneSecretSubmissions.hasThread(bot.threadId)) {
|
|
21770
22125
|
return json(res, 409, { error: "this task is securely saving a credential — try again when it finishes" });
|
|
21771
22126
|
}
|
|
21772
|
-
|
|
22127
|
+
// A thread's own model that can't run gives way to the bot's when the
|
|
22128
|
+
// rerun starts (healThreadModel), so check the model it will run on.
|
|
22129
|
+
const rerunSelection = turnSelection(bot.id, bot.threadId) ?? bot.modelSelection;
|
|
22130
|
+
if (!registry.get(rerunSelection.instanceId)) {
|
|
21773
22131
|
return json(res, 409, {
|
|
21774
|
-
error: `provider instance "${
|
|
22132
|
+
error: `provider instance "${rerunSelection.instanceId}" is unavailable — pick another model in settings`,
|
|
21775
22133
|
});
|
|
21776
22134
|
}
|
|
21777
|
-
const rerunInstance = registry.get(
|
|
22135
|
+
const rerunInstance = registry.get(rerunSelection.instanceId);
|
|
21778
22136
|
const rerunRefusal = policyModelRefusal(rerunInstance);
|
|
21779
22137
|
if (rerunRefusal)
|
|
21780
22138
|
return json(res, 409, { error: rerunRefusal });
|
|
@@ -22205,7 +22563,7 @@ const handleRequest = async (req, res) => {
|
|
|
22205
22563
|
// Who opened it decides who may answer its cards on a shared workspace.
|
|
22206
22564
|
if (auth.kind === "session")
|
|
22207
22565
|
threadStarters.set(task.threadId, actorKey(auth));
|
|
22208
|
-
return json(res, 201, { bot: publicBot(store.bot(bot.id)), task: wireTask(task) });
|
|
22566
|
+
return json(res, 201, { bot: publicBot(store.bot(bot.id)), task: wireTask(task, store.bot(bot.id).modelSelection) });
|
|
22209
22567
|
}
|
|
22210
22568
|
m = path.match(/^\/api\/bots\/([\w-]+)\/tasks\/([\w-]+)$/);
|
|
22211
22569
|
if (m && method === "POST") {
|
|
@@ -22223,7 +22581,7 @@ const handleRequest = async (req, res) => {
|
|
|
22223
22581
|
const switched = store.switchTask(bot.id, m[2]);
|
|
22224
22582
|
if (!switched)
|
|
22225
22583
|
return json(res, 404, { error: "no such task" });
|
|
22226
|
-
const switchedSettings = { ...wireBot(switched), tasks: store.tasks(switched.id).map(wireTask) };
|
|
22584
|
+
const switchedSettings = { ...wireBot(switched), tasks: store.tasks(switched.id).map((task) => wireTask(task, switched.modelSelection)) };
|
|
22227
22585
|
// "0" is settings only, a positive page is a bounded transcript, and no
|
|
22228
22586
|
// parameter is the whole thread — the one branch that materialises it.
|
|
22229
22587
|
// Search results land through that branch (src/lib/focus-message.ts):
|
|
@@ -22305,7 +22663,7 @@ const handleRequest = async (req, res) => {
|
|
|
22305
22663
|
const task = store.refreshTaskPermissions(profile.id, m[2]);
|
|
22306
22664
|
if (!task)
|
|
22307
22665
|
return json(res, 404, { error: "no such task" });
|
|
22308
|
-
return json(res, 200, { task: wireTask(task), bot: publicBot(store.bot(profile.id)) });
|
|
22666
|
+
return json(res, 200, { task: wireTask(task, store.bot(profile.id).modelSelection), bot: publicBot(store.bot(profile.id)) });
|
|
22309
22667
|
}
|
|
22310
22668
|
const patch = {};
|
|
22311
22669
|
if (body.projectId !== undefined) {
|
|
@@ -22411,7 +22769,7 @@ const handleRequest = async (req, res) => {
|
|
|
22411
22769
|
const task = patch.modelSelection
|
|
22412
22770
|
? store.switchTaskModel(m[1], m[2], patch.modelSelection, body.updateBotDefault === true, body.resetApprovalToAsk === true, { ...patch, ...hostedModels?.resetTask(current.modelSelection, patch.modelSelection) })
|
|
22413
22771
|
: store.patchTask(m[1], m[2], patch);
|
|
22414
|
-
return json(res, 200, { task: wireTask(task), bot: publicBot(store.bot(m[1])) });
|
|
22772
|
+
return json(res, 200, { task: wireTask(task, store.bot(m[1]).modelSelection), bot: publicBot(store.bot(m[1])) });
|
|
22415
22773
|
}
|
|
22416
22774
|
if (m && method === "DELETE") {
|
|
22417
22775
|
const bot = store.bot(m[1]);
|
|
@@ -22483,7 +22841,7 @@ const handleRequest = async (req, res) => {
|
|
|
22483
22841
|
const task = store.retitleTask(m[1], m[2], startedFrom, title) ?? store.taskByThread(m[1], m[2]);
|
|
22484
22842
|
if (!task)
|
|
22485
22843
|
return json(res, 404, { error: "no such task" });
|
|
22486
|
-
return json(res, 200, { task: wireTask(task) });
|
|
22844
|
+
return json(res, 200, { task: wireTask(task, store.bot(m[1]).modelSelection) });
|
|
22487
22845
|
}
|
|
22488
22846
|
finally {
|
|
22489
22847
|
titleRegenerations.delete(m[2]);
|
|
@@ -23061,7 +23419,17 @@ const handleRequest = async (req, res) => {
|
|
|
23061
23419
|
return json(res, 400, { error: "That does not look like an API key." });
|
|
23062
23420
|
const url = typeof body?.url === "string" && body.url.trim() ? body.url.trim() : (saved && "url" in saved ? saved.url : undefined);
|
|
23063
23421
|
res.setHeader("cache-control", "no-store");
|
|
23064
|
-
|
|
23422
|
+
const verdict = await checkProviderKey({ provider: kind, key, url });
|
|
23423
|
+
// A Test is a real use the person chose: the engines on this key agree
|
|
23424
|
+
// with its verdict. Claude runs its key through its CLI, not here.
|
|
23425
|
+
if (kind !== "anthropic") {
|
|
23426
|
+
const base = providerBaseUrl(kind, url);
|
|
23427
|
+
if (verdict.ok)
|
|
23428
|
+
noteKeyAccepted(base, key);
|
|
23429
|
+
else if (verdict.reason === "rejected")
|
|
23430
|
+
noteKeyRejected(base, key);
|
|
23431
|
+
}
|
|
23432
|
+
return json(res, 200, verdict);
|
|
23065
23433
|
}
|
|
23066
23434
|
if (method === "GET" && path === "/api/decisions") {
|
|
23067
23435
|
const rawLimit = url.searchParams.get("limit");
|
|
@@ -23514,7 +23882,7 @@ const handleRequest = async (req, res) => {
|
|
|
23514
23882
|
mcpProbesInFlight -= 1;
|
|
23515
23883
|
}
|
|
23516
23884
|
}
|
|
23517
|
-
// The loopback listener and authenticated paste-back complete the same flow.
|
|
23885
|
+
// The loopback listener, the public callback and authenticated paste-back complete the same flow.
|
|
23518
23886
|
const mcpSignIn = /^\/api\/mcp\/servers\/([a-z][a-z0-9_-]{0,31})\/(sign-in|sign-out)(?:\/([0-9a-f-]{36}))?$/.exec(path);
|
|
23519
23887
|
if (mcpSignIn) {
|
|
23520
23888
|
const [, name, action, flowId] = mcpSignIn;
|
|
@@ -23550,7 +23918,14 @@ const handleRequest = async (req, res) => {
|
|
|
23550
23918
|
return json(res, 200, { auth: await mcpOAuth.completeCallback(name, flowId, body.callbackUrl, owner) });
|
|
23551
23919
|
}
|
|
23552
23920
|
if (action === "sign-in" && method === "POST" && !flowId) {
|
|
23553
|
-
|
|
23921
|
+
// A browser on another computer cannot reach this machine's
|
|
23922
|
+
// loopback: it comes back to the https address it is using now,
|
|
23923
|
+
// when that is one this server vouches for (mcpCallbackOrigin).
|
|
23924
|
+
const remote = auth.kind === "session" && (isProxied(req) || !isLoopbackHost(req.headers.host));
|
|
23925
|
+
const origin = remote
|
|
23926
|
+
? mcpCallbackOrigin(provesSameOrigin(req) ? requestOrigin(req) : null, [CLOUD_HOME?.publicOrigin, hostedWorkspaceConfiguration()?.tenant.origin, savedCustomDomain(), FALLBACK_PUBLIC_URL])
|
|
23927
|
+
: null;
|
|
23928
|
+
const started = await mcpOAuth.start(name, url, undefined, owner, { remote, origin });
|
|
23554
23929
|
if (auth.kind === "session" && !sessions.isLive(auth.session.id)) {
|
|
23555
23930
|
mcpOAuth.revokeOwner(owner);
|
|
23556
23931
|
return json(res, 401, { error: "Your session ended. Start a new sign-in." });
|
|
@@ -24091,6 +24466,12 @@ const handleRequest = async (req, res) => {
|
|
|
24091
24466
|
browserCleanup.abort(request);
|
|
24092
24467
|
throw error;
|
|
24093
24468
|
}
|
|
24469
|
+
// A saved or cleared key starts over: neither it nor the key it
|
|
24470
|
+
// replaced stays marked as rejected.
|
|
24471
|
+
const resetKeys = PROVIDER_KEY_KINDS.flatMap((kind) => {
|
|
24472
|
+
const next = patch[kind]?.key;
|
|
24473
|
+
return next === undefined ? [] : [cfg[kind]?.key, next];
|
|
24474
|
+
});
|
|
24094
24475
|
let configWriteCommitted = false;
|
|
24095
24476
|
const externalSecretStorage = url.searchParams.get("secretStorage") === "external";
|
|
24096
24477
|
try {
|
|
@@ -24156,6 +24537,9 @@ const handleRequest = async (req, res) => {
|
|
|
24156
24537
|
}
|
|
24157
24538
|
throw error;
|
|
24158
24539
|
}
|
|
24540
|
+
for (const key of resetKeys)
|
|
24541
|
+
if (key)
|
|
24542
|
+
forgetKey(key);
|
|
24159
24543
|
// Desktops already counting down move to the new window at once, in
|
|
24160
24544
|
// every isolation mode; busy work still defers its expiry.
|
|
24161
24545
|
if (patch.localVm?.idleTimeoutMinutes !== undefined) {
|
|
@@ -24772,7 +25156,7 @@ const handleRequest = async (req, res) => {
|
|
|
24772
25156
|
catch (e) {
|
|
24773
25157
|
const status = e?.status ?? 500;
|
|
24774
25158
|
const candidateCode = e?.code;
|
|
24775
|
-
const code = typeof candidateCode === "string" && ["guarded_busy", "guarded_branch", "guarded_permissions", "guarded_request_changed", "guarded_request_untracked"].includes(candidateCode)
|
|
25159
|
+
const code = typeof candidateCode === "string" && ["guarded_busy", "guarded_branch", "guarded_permissions", "guarded_request_changed", "guarded_request_untracked", "outside_workspace"].includes(candidateCode)
|
|
24776
25160
|
? candidateCode : undefined;
|
|
24777
25161
|
return json(res, status, { error: e instanceof Error ? e.message : String(e), ...(code ? { code } : {}) });
|
|
24778
25162
|
}
|
|
@@ -24922,12 +25306,22 @@ restoreChannelMessages();
|
|
|
24922
25306
|
if (movedAutoPins)
|
|
24923
25307
|
console.log(`Works on: moved ${movedAutoPins} auto-pinned thread(s) to their bot's current Works on`);
|
|
24924
25308
|
}
|
|
24925
|
-
server.listen(PORT, "127.0.0.1", () => {
|
|
25309
|
+
server.listen(PORT, "127.0.0.1", async () => {
|
|
24926
25310
|
companyRuntimeReady();
|
|
24927
25311
|
console.log(`openmausbot server on http://127.0.0.1:${PORT}`);
|
|
25312
|
+
// Words queued before the restart start first, ahead of anything sent from
|
|
25313
|
+
// now on. Their turns wait for the engines read at start like every turn.
|
|
24928
25314
|
followupsReady = true;
|
|
24929
25315
|
drainQueuedSends();
|
|
24930
25316
|
drainQueuedChannelSends();
|
|
25317
|
+
// The work below wakes delegators, and a woken delegator keeps its session
|
|
25318
|
+
// only on an engine that records what it was handed, which that read finds
|
|
25319
|
+
// out: it starts once the read is done.
|
|
25320
|
+
if (enginesRead) {
|
|
25321
|
+
await enginesRead;
|
|
25322
|
+
if (companyShutdown)
|
|
25323
|
+
return;
|
|
25324
|
+
}
|
|
24931
25325
|
// Startup work uses the same turn dispatcher and local tool endpoint as
|
|
24932
25326
|
// ordinary chat. Start only once every registry is initialized and the
|
|
24933
25327
|
// endpoint is listening; earlier dispatch can hit uninitialized bindings.
|