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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { chmodSync, existsSync, readFileSync, mkdirSync, rmSync, statSync, unlinkSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
-
import { writeFileAtomic } from "./atomic.js";
|
|
8
|
+
import { writeFileAtomic, writeFileAtomicIfChanged } from "./atomic.js";
|
|
9
9
|
import { parseToolScope, toolScopeWidens } from "../shared/tool-scope.js";
|
|
10
10
|
import { ensureSections, readSections, changeEmptySection } from "./section-context.js";
|
|
11
11
|
import { removeBotFolder, soulFile, soulHash, writeSoulMirror } from "./bot-folder.js";
|
|
@@ -22,6 +22,7 @@ import { approvalModeFor, isApprovalMode, modelSwitchNeedsAsk } from "../shared/
|
|
|
22
22
|
import { REMOVED_COMPUTER_DRIVER } from "./computer-engine-removal.js";
|
|
23
23
|
import { isMentionBoundary, isMentionNameContinuation } from "../shared/mention-boundary.js";
|
|
24
24
|
import { CONNECTOR_SLUG_PATTERN, CONNECTOR_TOOL_NAME_PATTERN } from "../shared/wire.js";
|
|
25
|
+
import { sameModelSelection } from "../shared/thread-model.js";
|
|
25
26
|
export const groupWireProjectionIsExact = true;
|
|
26
27
|
// Unicode's complete emoji sequences include flags, skin tones and ZWJ
|
|
27
28
|
// combinations. Also allow unqualified single symbols (e.g. ♥), but not
|
|
@@ -32,13 +33,19 @@ export function isProjectEmoji(value) {
|
|
|
32
33
|
}
|
|
33
34
|
export const taskWireProjectionIsExact = true;
|
|
34
35
|
/** The typed wire projection for one task. Pairs with the assertion above:
|
|
35
|
-
* returning WireTask means an undeclared server field cannot ride silently.
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
* returning WireTask means an undeclared server field cannot ride silently.
|
|
37
|
+
* A thread with no model of its own follows its bot: the wire carries the
|
|
38
|
+
* model it runs on either way, so every client reads one effective model. */
|
|
39
|
+
export function toWireTask(task, botModel) {
|
|
40
|
+
const { resumeCursors: _resumeCursors, lastInstanceId: _lastInstanceId, handedMessages: _handedMessages, appliedCompactionId: _appliedCompactionId, contextFloor: _contextFloor, lastContextModel: _lastContextModel, surfaceSource, ...stored } = task;
|
|
41
|
+
const wire = { ...stored, modelSelection: structuredClone(task.modelSelection ?? botModel), followsBotModel: task.modelSelection === undefined };
|
|
38
42
|
// Who pinned stays private. A client learns only whether the pin is the
|
|
39
43
|
// machine's own record, so it never presents one as the person's choice.
|
|
40
44
|
return surfaceSource === "auto" && wire.surface !== undefined ? { ...wire, surfaceAuto: true } : wire;
|
|
41
45
|
}
|
|
46
|
+
/** The level a thread drops to when its engine changes under a level the new
|
|
47
|
+
* engine would have to confirm (modelSwitchNeedsAsk), as on every switch. */
|
|
48
|
+
const ASK_NOW = { approvalMode: "ask", autoApprove: false, alwaysAllow: [] };
|
|
42
49
|
const TASK_PATCH_FIELDS = [
|
|
43
50
|
"title", "projectId", "modelSelection", "approvalMode", "autoApprove", "alwaysAllow",
|
|
44
51
|
"unread", "rewound", "archivedAt", "pinned", "pinnedMessageId", "resumeCursors", "lastInstanceId", "cwd",
|
|
@@ -326,6 +333,9 @@ export function parseConnectorTools(value) {
|
|
|
326
333
|
}
|
|
327
334
|
const BOTS_FILE = join(DATA_DIR, "bots.json");
|
|
328
335
|
const GROUPS_FILE = join(DATA_DIR, "groups.json");
|
|
336
|
+
/** Written once the copies older builds made of a bot's model on its threads
|
|
337
|
+
* are cleared (Store load), so that cleanup runs once per data directory. */
|
|
338
|
+
const THREAD_MODEL_COPIES_CLEARED = join(DATA_DIR, "thread-model-copies-cleared");
|
|
329
339
|
/** The registries carry souls, project paths and per-bot settings, so they are
|
|
330
340
|
* owner-only like the other data-dir stores. A file written by an older
|
|
331
341
|
* release (or loosened by hand) is tightened on load. Best effort: failing to
|
|
@@ -456,6 +466,7 @@ export class Store {
|
|
|
456
466
|
threads = new Map();
|
|
457
467
|
defaultSelection;
|
|
458
468
|
completeNewBotSelection;
|
|
469
|
+
driverKindOf;
|
|
459
470
|
listeners = new Set();
|
|
460
471
|
/** A broken team registry must not prevent loading independent chat data. */
|
|
461
472
|
registeringInitialSections = true;
|
|
@@ -465,9 +476,14 @@ export class Store {
|
|
|
465
476
|
constructor(defaultSelection,
|
|
466
477
|
/** Workspace-wide new-bot defaults (config newBots), applied to every
|
|
467
478
|
* new bot's selection whichever path created it. */
|
|
468
|
-
completeNewBotSelection = (selection) => selection
|
|
479
|
+
completeNewBotSelection = (selection) => selection,
|
|
480
|
+
/** The driver an engine instance runs, for the level a thread keeps when
|
|
481
|
+
* its engine changes (modelSwitchNeedsAsk). Unknown counts as another
|
|
482
|
+
* driver, so Full and Custom fall back to Ask. */
|
|
483
|
+
driverKindOf = () => undefined) {
|
|
469
484
|
this.defaultSelection = defaultSelection;
|
|
470
485
|
this.completeNewBotSelection = completeNewBotSelection;
|
|
486
|
+
this.driverKindOf = driverKindOf;
|
|
471
487
|
mkdirSync(DATA_DIR, { recursive: true });
|
|
472
488
|
for (const file of [BOTS_FILE, GROUPS_FILE])
|
|
473
489
|
tightenRegistryFile(file);
|
|
@@ -696,11 +712,17 @@ export class Store {
|
|
|
696
712
|
}
|
|
697
713
|
if (groupsMigrated)
|
|
698
714
|
this.saveGroups();
|
|
715
|
+
// Older builds copied a bot's model onto every thread it made, so a
|
|
716
|
+
// thread on exactly its bot's model is a copy, not a person's pick: once
|
|
717
|
+
// per data directory it is cleared and the thread follows its bot. A
|
|
718
|
+
// different model can't be told apart from a pick and stays. Only over a
|
|
719
|
+
// bot list this load could read, so an unreadable file is not skipped.
|
|
720
|
+
const clearModelCopies = !existsSync(THREAD_MODEL_COPIES_CLEARED) && (botsLoaded || !existsSync(BOTS_FILE));
|
|
699
721
|
// bots saved before tasks existed have one endless thread; adopt it as
|
|
700
722
|
// their first task so nothing is lost and nothing special-cases it
|
|
701
723
|
for (const b of this.bots) {
|
|
702
|
-
// Folders are organizational only. Preserve
|
|
703
|
-
//
|
|
724
|
+
// Folders are organizational only. Preserve threads' own models while
|
|
725
|
+
// discarding the unshipped folder-default setting.
|
|
704
726
|
if (b.projects?.some((project) => "modelSelection" in project)) {
|
|
705
727
|
b.projects = b.projects.map(({ id, name, emoji }) => ({ id, name, ...(isProjectEmoji(emoji) ? { emoji } : {}) }));
|
|
706
728
|
botsMigrated = true;
|
|
@@ -730,8 +752,8 @@ export class Store {
|
|
|
730
752
|
botsMigrated = true;
|
|
731
753
|
}
|
|
732
754
|
for (const task of b.tasks) {
|
|
733
|
-
if (task.modelSelection
|
|
734
|
-
task.modelSelection
|
|
755
|
+
if (clearModelCopies && task.modelSelection !== undefined && sameModelSelection(task.modelSelection, b.modelSelection)) {
|
|
756
|
+
delete task.modelSelection;
|
|
735
757
|
botsMigrated = true;
|
|
736
758
|
}
|
|
737
759
|
if (!task.resumeCursors) {
|
|
@@ -774,6 +796,8 @@ export class Store {
|
|
|
774
796
|
}
|
|
775
797
|
if (botsMigrated)
|
|
776
798
|
this.saveBots();
|
|
799
|
+
if (clearModelCopies)
|
|
800
|
+
writeFileAtomic(THREAD_MODEL_COPIES_CLEARED, "Threads follow their bot's model; the copies older builds made are cleared.\n", { mode: 0o600 });
|
|
777
801
|
// Search reads SQLite directly, so migrate every known legacy transcript
|
|
778
802
|
// at startup rather than waiting until the user happens to open it. Only
|
|
779
803
|
// pending JSON files are touched; already-migrated threads stay lazy.
|
|
@@ -896,10 +920,13 @@ export class Store {
|
|
|
896
920
|
if (botsDirty)
|
|
897
921
|
this.saveBots();
|
|
898
922
|
}
|
|
923
|
+
// Many saves repeat what is already on disk (the same resume cursor, an
|
|
924
|
+
// unchanged task field), so both registries skip a byte-identical write.
|
|
925
|
+
// Events are still emitted by the callers either way.
|
|
899
926
|
saveBots(bots = this.bots, registerSections = true) {
|
|
900
927
|
if (registerSections)
|
|
901
928
|
this.rememberSections([...this.bots, ...bots].map((bot) => bot.section));
|
|
902
|
-
|
|
929
|
+
writeFileAtomicIfChanged(BOTS_FILE, JSON.stringify(bots.map(({ busy: _busy, activity: _activity, ...bot }) => ({
|
|
903
930
|
...bot,
|
|
904
931
|
tasks: bot.tasks?.map(({ busy: _taskBusy, activity: _taskActivity, turnStartedAt: _taskTurnStarted, ...task }) => persistedPin(task)),
|
|
905
932
|
})), null, 2), { mode: 0o600 });
|
|
@@ -907,7 +934,7 @@ export class Store {
|
|
|
907
934
|
saveGroups(groups = this.groups, registerSections = true) {
|
|
908
935
|
if (registerSections)
|
|
909
936
|
this.rememberSections(groups.map((group) => group.section));
|
|
910
|
-
|
|
937
|
+
writeFileAtomicIfChanged(GROUPS_FILE, JSON.stringify(groups.map(({ busyBotId: _busyBotId, turnStartedAt: _turnStartedAt, ...g }) => ({
|
|
911
938
|
...g,
|
|
912
939
|
...(g.tasks ? { tasks: g.tasks.map((task) => persistedPin(task)) } : {}),
|
|
913
940
|
})), null, 2), { mode: 0o600 });
|
|
@@ -1120,6 +1147,9 @@ export class Store {
|
|
|
1120
1147
|
else if (!patch.busyBotId)
|
|
1121
1148
|
delete group.turnStartedAt;
|
|
1122
1149
|
}
|
|
1150
|
+
if (Object.prototype.hasOwnProperty.call(patch, "turnTimeoutMinutes") && patch.turnTimeoutMinutes == null) {
|
|
1151
|
+
delete group.turnTimeoutMinutes;
|
|
1152
|
+
}
|
|
1123
1153
|
if (!group.dm && Object.prototype.hasOwnProperty.call(patch, "pinnedMessageId")) {
|
|
1124
1154
|
const active = this.activeGroupTask(group.id);
|
|
1125
1155
|
if (active)
|
|
@@ -1260,6 +1290,20 @@ export class Store {
|
|
|
1260
1290
|
this.emit({ type: "group", groupId });
|
|
1261
1291
|
return task;
|
|
1262
1292
|
}
|
|
1293
|
+
/** Null removes the conversation's own ceiling so the next turn uses the
|
|
1294
|
+
* global group limit. A sibling conversation is left alone. */
|
|
1295
|
+
setGroupTaskTurnTimeout(groupId, threadId, minutes) {
|
|
1296
|
+
const task = this.groupTaskByThread(groupId, threadId);
|
|
1297
|
+
if (!task)
|
|
1298
|
+
return null;
|
|
1299
|
+
if (minutes == null)
|
|
1300
|
+
delete task.turnTimeoutMinutes;
|
|
1301
|
+
else
|
|
1302
|
+
task.turnTimeoutMinutes = minutes;
|
|
1303
|
+
this.saveGroups();
|
|
1304
|
+
this.emit({ type: "group", groupId });
|
|
1305
|
+
return task;
|
|
1306
|
+
}
|
|
1263
1307
|
renameGroupTask(groupId, threadId, title) {
|
|
1264
1308
|
const task = this.groupTaskByThread(groupId, threadId);
|
|
1265
1309
|
if (!task)
|
|
@@ -1626,7 +1670,6 @@ export class Store {
|
|
|
1626
1670
|
createdAt: bot.createdAt,
|
|
1627
1671
|
updatedAt: bot.createdAt,
|
|
1628
1672
|
resumeCursors: {},
|
|
1629
|
-
modelSelection: structuredClone(bot.modelSelection),
|
|
1630
1673
|
unread: false,
|
|
1631
1674
|
activity: "idle",
|
|
1632
1675
|
busy: false,
|
|
@@ -1658,7 +1701,8 @@ export class Store {
|
|
|
1658
1701
|
return bot;
|
|
1659
1702
|
}
|
|
1660
1703
|
/** All setup fields and the Chief's receipt commit before publishing any
|
|
1661
|
-
* mutation.
|
|
1704
|
+
* mutation. A new model moves the threads that follow the bot, never a
|
|
1705
|
+
* thread's own model. */
|
|
1662
1706
|
applyTeamSetup(request) {
|
|
1663
1707
|
const chief = this.bot(request.botId);
|
|
1664
1708
|
if (!chief)
|
|
@@ -1687,7 +1731,7 @@ export class Store {
|
|
|
1687
1731
|
// a restricted Chief never creates a bot everyone sees.
|
|
1688
1732
|
...(chief.visibility && chief.visibility !== "everyone" ? { visibility: structuredClone(chief.visibility) } : {}),
|
|
1689
1733
|
tasks: [{ threadId: operation.threadId, title: UNTITLED_THREAD, createdAt, updatedAt: createdAt, resumeCursors: {},
|
|
1690
|
-
|
|
1734
|
+
approvalMode: "ask", autoApprove: false,
|
|
1691
1735
|
unread: false, activity: "idle", busy: false }],
|
|
1692
1736
|
};
|
|
1693
1737
|
// "" is the private-workspace spelling on proposal; the record
|
|
@@ -1702,13 +1746,7 @@ export class Store {
|
|
|
1702
1746
|
const previous = nextBots[at];
|
|
1703
1747
|
next = { ...previous, ...operation.fields };
|
|
1704
1748
|
if (operation.fields.modelSelection)
|
|
1705
|
-
next.tasks = previous.
|
|
1706
|
-
...task,
|
|
1707
|
-
modelSelection: structuredClone(task.modelSelection ?? previous.modelSelection),
|
|
1708
|
-
approvalMode: approvalModeFor(this.projectBotForTask(previous.id, task.threadId)),
|
|
1709
|
-
autoApprove: task.autoApprove ?? previous.autoApprove,
|
|
1710
|
-
alwaysAllow: structuredClone(task.alwaysAllow ?? previous.alwaysAllow ?? []),
|
|
1711
|
-
}));
|
|
1749
|
+
next.tasks = this.withDefaultModel(previous, operation.fields.modelSelection);
|
|
1712
1750
|
nextBots[at] = next;
|
|
1713
1751
|
}
|
|
1714
1752
|
if (operation.fields.chiefOfStaff === false)
|
|
@@ -1746,23 +1784,38 @@ export class Store {
|
|
|
1746
1784
|
this.emit({ type: "bot", botId: chief.id });
|
|
1747
1785
|
return result;
|
|
1748
1786
|
}
|
|
1749
|
-
/**
|
|
1750
|
-
*
|
|
1751
|
-
*
|
|
1752
|
-
|
|
1753
|
-
|
|
1787
|
+
/** Moving this thread from one model to another takes its level back to
|
|
1788
|
+
* Ask where the new engine would have to confirm it (modelSwitchNeedsAsk),
|
|
1789
|
+
* as on every engine switch. Read before anything changes. */
|
|
1790
|
+
switchNeedsAsk(botId, threadId, from, to) {
|
|
1791
|
+
if (from.instanceId === to.instanceId)
|
|
1792
|
+
return false;
|
|
1793
|
+
const thread = this.projectBotForTask(botId, threadId);
|
|
1794
|
+
return Boolean(thread) && modelSwitchNeedsAsk(approvalModeFor(thread), this.driverKindOf(from.instanceId), this.driverKindOf(to.instanceId));
|
|
1795
|
+
}
|
|
1796
|
+
/** A bot's threads as a change of its model to `next` leaves them. Each
|
|
1797
|
+
* keeps the level it runs at now (older threads may still inherit the
|
|
1798
|
+
* bot's, which may change in the same write); one that follows the bot
|
|
1799
|
+
* moves with it, back to Ask where the new engine would have to confirm
|
|
1800
|
+
* its level; a thread's own model stays. */
|
|
1801
|
+
withDefaultModel(bot, next) {
|
|
1802
|
+
return bot.tasks?.map((task) => ({
|
|
1803
|
+
...task,
|
|
1804
|
+
approvalMode: approvalModeFor(this.projectBotForTask(bot.id, task.threadId)),
|
|
1805
|
+
autoApprove: task.autoApprove ?? bot.autoApprove,
|
|
1806
|
+
alwaysAllow: structuredClone(task.alwaysAllow ?? bot.alwaysAllow ?? []),
|
|
1807
|
+
...(task.modelSelection === undefined && this.switchNeedsAsk(bot.id, task.threadId, bot.modelSelection, next) ? structuredClone(ASK_NOW) : {}),
|
|
1808
|
+
}));
|
|
1809
|
+
}
|
|
1810
|
+
/** One reviewed default-model change (propose_model), with
|
|
1811
|
+
* applyTeamSetup's task stamping (withDefaultModel): every thread that
|
|
1812
|
+
* follows the bot moves to the new model, and a thread's own model, which a
|
|
1813
|
+
* person picked there, stays. */
|
|
1754
1814
|
applyModelDefault(id, modelSelection) {
|
|
1755
1815
|
const previous = this.bot(id);
|
|
1756
1816
|
if (!previous)
|
|
1757
1817
|
return null;
|
|
1758
|
-
const next = { ...previous, modelSelection: structuredClone(modelSelection) };
|
|
1759
|
-
next.tasks = previous.tasks?.map((task) => ({
|
|
1760
|
-
...task,
|
|
1761
|
-
modelSelection: structuredClone(task.modelSelection ?? previous.modelSelection),
|
|
1762
|
-
approvalMode: approvalModeFor(this.projectBotForTask(previous.id, task.threadId)),
|
|
1763
|
-
autoApprove: task.autoApprove ?? previous.autoApprove,
|
|
1764
|
-
alwaysAllow: structuredClone(task.alwaysAllow ?? previous.alwaysAllow ?? []),
|
|
1765
|
-
}));
|
|
1818
|
+
const next = { ...previous, modelSelection: structuredClone(modelSelection), tasks: this.withDefaultModel(previous, modelSelection) };
|
|
1766
1819
|
this.saveBots(this.bots.map((candidate) => candidate.id === id ? next : candidate));
|
|
1767
1820
|
this.bots = this.bots.map((candidate) => candidate.id === id ? next : candidate);
|
|
1768
1821
|
this.emit({ type: "bot", botId: id });
|
|
@@ -1860,9 +1913,16 @@ export class Store {
|
|
|
1860
1913
|
patch = { ...patch };
|
|
1861
1914
|
delete patch.toolScope;
|
|
1862
1915
|
}
|
|
1916
|
+
// A new model moves the threads that follow the bot; one whose level the
|
|
1917
|
+
// new engine would have to confirm goes back to Ask. That is a
|
|
1918
|
+
// revocation, so it holds in memory like the others below.
|
|
1919
|
+
const toAsk = patch.modelSelection ? (bot.tasks ?? []).filter((task) => task.modelSelection === undefined &&
|
|
1920
|
+
this.switchNeedsAsk(id, task.threadId, bot.modelSelection, patch.modelSelection)) : [];
|
|
1863
1921
|
// Runtime revocations must become effective in memory even when disk is
|
|
1864
1922
|
// unavailable. Profile edits use the separate atomic path below.
|
|
1865
1923
|
Object.assign(bot, patch);
|
|
1924
|
+
for (const task of toAsk)
|
|
1925
|
+
Object.assign(task, structuredClone(ASK_NOW));
|
|
1866
1926
|
const task = this.activeTask(id);
|
|
1867
1927
|
if (task) {
|
|
1868
1928
|
for (const key of ["resumeCursors", "rewound", "pinnedMessageId", "unread"]) {
|
|
@@ -2586,37 +2646,61 @@ export class Store {
|
|
|
2586
2646
|
this.emit({ type: "bot", botId });
|
|
2587
2647
|
return cleared;
|
|
2588
2648
|
}
|
|
2589
|
-
/**
|
|
2590
|
-
*
|
|
2591
|
-
*
|
|
2649
|
+
/** A person picks a model in one thread, and with updateBotDefault makes it
|
|
2650
|
+
* the bot's model too. A thread on its bot's model follows the bot: it
|
|
2651
|
+
* keeps no model of its own. Model/provider changes are one configuration
|
|
2652
|
+
* transaction: never publish a new provider before its confirmed approval
|
|
2653
|
+
* downgrade, or change the default while leaving the selected thread
|
|
2654
|
+
* behind after a write failure. */
|
|
2592
2655
|
switchTaskModel(botId, threadId, selection, updateBotDefault, resetApprovalToAsk, taskPatch = {}) {
|
|
2593
2656
|
const bot = this.bot(botId);
|
|
2594
2657
|
const task = this.taskByThread(botId, threadId);
|
|
2595
2658
|
if (!bot || !task)
|
|
2596
2659
|
return null;
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2599
|
-
const nextTask = persistedPin({ ...task, ...taskPatch, ...
|
|
2660
|
+
const approval = resetApprovalToAsk ? structuredClone(ASK_NOW) : {};
|
|
2661
|
+
const own = sameModelSelection(selection, updateBotDefault ? selection : bot.modelSelection) ? undefined : structuredClone(selection);
|
|
2662
|
+
const nextTask = persistedPin({ ...task, ...taskPatch, modelSelection: own, ...approval,
|
|
2600
2663
|
...(typeof taskPatch.title === "string" ? { title: taskPatch.title.trim().slice(0, 80) || UNTITLED_THREAD } : {}) });
|
|
2601
|
-
//
|
|
2602
|
-
//
|
|
2603
|
-
|
|
2604
|
-
const nextTasks = bot.tasks.map((candidate) => candidate === task ? nextTask :
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
approvalMode: approvalModeFor(this.projectBotForTask(botId, candidate.threadId)),
|
|
2608
|
-
autoApprove: candidate.autoApprove ?? bot.autoApprove,
|
|
2609
|
-
alwaysAllow: structuredClone(candidate.alwaysAllow ?? bot.alwaysAllow ?? []),
|
|
2610
|
-
});
|
|
2611
|
-
const next = { ...bot, ...(updateBotDefault ? patch : {}),
|
|
2612
|
-
tasks: nextTasks };
|
|
2613
|
-
this.saveBots(this.bots.map((candidate) => candidate === bot ? next : candidate));
|
|
2664
|
+
// A new bot model moves the threads that follow it (withDefaultModel);
|
|
2665
|
+
// every other thread keeps its model and the level it runs at now.
|
|
2666
|
+
const others = updateBotDefault ? this.withDefaultModel(bot, selection) : bot.tasks;
|
|
2667
|
+
const nextTasks = bot.tasks.map((candidate, index) => candidate === task ? nextTask : others[index]);
|
|
2668
|
+
const botPatch = updateBotDefault ? { modelSelection: structuredClone(selection), ...approval } : {};
|
|
2669
|
+
this.saveBots(this.bots.map((candidate) => candidate === bot ? { ...bot, ...botPatch, tasks: nextTasks } : candidate));
|
|
2614
2670
|
bot.tasks.forEach((candidate, index) => Object.assign(candidate, nextTasks[index]));
|
|
2615
|
-
|
|
2616
|
-
Object.assign(bot, patch);
|
|
2671
|
+
Object.assign(bot, botPatch);
|
|
2617
2672
|
this.emit({ type: "bot", botId });
|
|
2618
2673
|
return task;
|
|
2619
2674
|
}
|
|
2675
|
+
/** These threads follow their bot's model again: each one's own model is
|
|
2676
|
+
* cleared ("Switch them too", "Use the bot's model", or a model that can't
|
|
2677
|
+
* run here). A level the bot's engine would have to confirm goes back to
|
|
2678
|
+
* Ask, as on every engine switch, and `reset` adds what a changed model
|
|
2679
|
+
* needs (a hosted workspace's fresh session). One write. Returns the
|
|
2680
|
+
* threads that changed; a listed thread that already follows stays as it is. */
|
|
2681
|
+
followBotModel(botId, threadIds, reset = () => ({})) {
|
|
2682
|
+
const bot = this.bot(botId);
|
|
2683
|
+
if (!bot?.tasks)
|
|
2684
|
+
return [];
|
|
2685
|
+
const listed = new Set(threadIds);
|
|
2686
|
+
const patches = bot.tasks.map((task) => !listed.has(task.threadId) || task.modelSelection === undefined ? null : {
|
|
2687
|
+
...reset(task.modelSelection, bot.modelSelection),
|
|
2688
|
+
modelSelection: undefined,
|
|
2689
|
+
...(this.switchNeedsAsk(botId, task.threadId, task.modelSelection, bot.modelSelection) ? structuredClone(ASK_NOW) : {}),
|
|
2690
|
+
});
|
|
2691
|
+
const moved = bot.tasks.filter((_, index) => patches[index]).map((task) => task.threadId);
|
|
2692
|
+
if (!moved.length)
|
|
2693
|
+
return [];
|
|
2694
|
+
const nextTasks = bot.tasks.map((task, index) => patches[index] ? { ...task, ...patches[index] } : task);
|
|
2695
|
+
this.saveBots(this.bots.map((candidate) => candidate === bot ? { ...bot, tasks: nextTasks } : candidate));
|
|
2696
|
+
bot.tasks.forEach((task, index) => { if (patches[index])
|
|
2697
|
+
Object.assign(task, patches[index]); });
|
|
2698
|
+
const active = bot.tasks.find((task) => task.threadId === bot.threadId);
|
|
2699
|
+
if (active)
|
|
2700
|
+
this.mirrorActiveTask(bot, active);
|
|
2701
|
+
this.emit({ type: "bot", botId });
|
|
2702
|
+
return moved;
|
|
2703
|
+
}
|
|
2620
2704
|
/** One durable write: never leave only part of a bot's threads updated. */
|
|
2621
2705
|
setAllThreadApprovalMode(botId, mode) {
|
|
2622
2706
|
const bot = this.bot(botId);
|
|
@@ -2656,9 +2740,9 @@ export class Store {
|
|
|
2656
2740
|
bot.pinnedMessageId = task.pinnedMessageId;
|
|
2657
2741
|
}
|
|
2658
2742
|
/** A fresh context on the same bot: new thread, new session, same
|
|
2659
|
-
* persona/tools/computer. Becomes the active task. It
|
|
2660
|
-
*
|
|
2661
|
-
* another of this bot's threads keeps
|
|
2743
|
+
* persona/tools/computer. Becomes the active task. It follows the bot's
|
|
2744
|
+
* model unless the caller hands it another one: a thread opened from
|
|
2745
|
+
* another of this bot's threads keeps the model a person picked there. */
|
|
2662
2746
|
createTask(botId, title, activate = true, projectId, openedBy, approvalMode, modelSelection) {
|
|
2663
2747
|
const bot = this.bot(botId);
|
|
2664
2748
|
if (!bot)
|
|
@@ -2674,7 +2758,7 @@ export class Store {
|
|
|
2674
2758
|
...(projectId ? { projectId } : {}),
|
|
2675
2759
|
...(openedBy ? { openedBy: structuredClone(openedBy) } : {}),
|
|
2676
2760
|
resumeCursors: {},
|
|
2677
|
-
modelSelection
|
|
2761
|
+
...(modelSelection && !sameModelSelection(modelSelection, bot.modelSelection) ? { modelSelection: structuredClone(modelSelection) } : {}),
|
|
2678
2762
|
approvalMode: approvalMode ?? approvalModeFor(bot),
|
|
2679
2763
|
autoApprove: approvalMode ? false : Boolean(bot.autoApprove),
|
|
2680
2764
|
alwaysAllow: approvalMode ? [] : [...(bot.alwaysAllow ?? [])],
|
|
@@ -2780,10 +2864,14 @@ export class Store {
|
|
|
2780
2864
|
this.emit({ type: "bot", botId });
|
|
2781
2865
|
return bot;
|
|
2782
2866
|
}
|
|
2867
|
+
/** A first run: no bot yet, so seedIfEmpty makes one. */
|
|
2868
|
+
needsSeed() {
|
|
2869
|
+
return this.bots.length === 0;
|
|
2870
|
+
}
|
|
2783
2871
|
/** First-run seed: one bot so the app never opens empty — it gets a
|
|
2784
2872
|
* random friendly name like every other bot. */
|
|
2785
2873
|
seedIfEmpty() {
|
|
2786
|
-
if (this.
|
|
2874
|
+
if (!this.needsSeed())
|
|
2787
2875
|
return;
|
|
2788
2876
|
this.createBot();
|
|
2789
2877
|
}
|
|
@@ -123,7 +123,7 @@ export function customMcpPrompt(names) {
|
|
|
123
123
|
}
|
|
124
124
|
export const CREDENTIAL_PROMPT = " If a supported API key is missing for the service actually needed, use request_credential to create a secure credential request. Before requesting a computer-provider key, inspect the configured targets with select_computer; an existing self-hosted VPS does not need Boat credentials. Do not request a different provider's key merely because a task mentions cloud. A freshly QR-paired mobile app or the desktop app can show the secure entry card. Never claim it opened unless the request succeeded, and never ask the user to paste credentials into chat.";
|
|
125
125
|
export const THREADS_PROMPT = " A thread is one conversation with its own history and its own run; a bot can have several running at once, and the person sees them as rows under that bot. Use start_thread to open one on yourself for separate work, or on a teammate to hand them a job that should run on its own. Use list_threads to see how the ones you opened are going. When you mention a thread to the person, write its title as #Title so it links. Do not use a ticket comment, a note, or a room post as a stand-in for a thread.";
|
|
126
|
-
const PROPOSAL_RESULT_PROMPT = " Follow the tool result:
|
|
126
|
+
const PROPOSAL_RESULT_PROMPT = " Follow the tool result: a change to your own routines, skills, profile or model applies immediately and the person sees it with an Undo; then continue the requested work without asking for another confirmation. A change for another bot may wait for the person. If it reports a pending review, end the turn and wait for the in-app decision. Never claim success before an applied result, and report failures honestly. Full Access does not grant another bot broader permissions.";
|
|
127
127
|
export const ROUTINE_PROMPT = " If the user explicitly asks to list or review, schedule, run, or change routines, use list_routines and propose_routine or propose_routine_action. Keep run_on omitted or maus to use the bot's current model and configured computer, including its VPS. A routine's box (legacy cloud) destination runs on the bot's cloud computer, not the configured VPS; choose it only when the user explicitly wants the cloud computer. Convert calendar requests such as the first or last day of each month or the second Monday to a five-field cron expression with an explicit IANA timezone; use interval for elapsed every-N-minutes work. Never replace a calendar rule with daily AI date checking or an approximate weekly schedule; clarify ambiguous or unsupported requests." + PROPOSAL_RESULT_PROMPT;
|
|
128
128
|
export const ROUTINE_EXECUTION_PROMPT = " Execute this routine now: use available peer tools for required handoffs rather than merely announcing that you will wait; after an accepted delegation, end this turn for automatic resumption, and report a concrete blocker if no handoff is possible.";
|
|
129
129
|
export const LEARN_PROMPT = " If the user sends /learn or asks you to save a reusable procedure from this work, use skills_list and skill_manage. Create new skills; update an existing learned skill only when the user explicitly asks to revise that exact name. Include source provenance." + PROPOSAL_RESULT_PROMPT;
|
|
@@ -78,6 +78,9 @@ export function createTeamBackup(store, routines, name) {
|
|
|
78
78
|
? { botId: task.closedBy.botId, name: task.closedBy.name, at: task.closedBy.at }
|
|
79
79
|
: undefined,
|
|
80
80
|
pinned: task.pinned === true ? true : undefined,
|
|
81
|
+
turnTimeoutMinutes: "turnTimeoutMinutes" in task && typeof task.turnTimeoutMinutes === "number"
|
|
82
|
+
? task.turnTimeoutMinutes
|
|
83
|
+
: undefined,
|
|
81
84
|
activeLeafId: store.activeLeaf(task.threadId),
|
|
82
85
|
messages: store.messagesFor(task.threadId).map((message) => ({
|
|
83
86
|
id: message.id, role: message.role, text: messageText(message), at: message.at,
|
|
@@ -97,7 +100,10 @@ export function createTeamBackup(store, routines, name) {
|
|
|
97
100
|
? { kind: "mentions" }
|
|
98
101
|
: group.defaultResponder.kind === "auto" && group.defaultResponder.fallbackBotId && !memberIds.includes(group.defaultResponder.fallbackBotId)
|
|
99
102
|
? { kind: "auto" } : group.defaultResponder,
|
|
100
|
-
activeTask: group.threadId,
|
|
103
|
+
activeTask: group.threadId,
|
|
104
|
+
tasks: history(group).map((task, index) => group.dm && index === 0 && typeof group.turnTimeoutMinutes === "number"
|
|
105
|
+
? { ...task, turnTimeoutMinutes: group.turnTimeoutMinutes }
|
|
106
|
+
: task),
|
|
101
107
|
};
|
|
102
108
|
});
|
|
103
109
|
const validRoutines = routines.filter((routine) => {
|
|
@@ -197,8 +203,9 @@ export function importTeamBackup(store, routines, input, selection, options = {}
|
|
|
197
203
|
const bot = store.bot(botIds.get(source.key));
|
|
198
204
|
const tasks = source.tasks.map((task, i) => {
|
|
199
205
|
const record = {
|
|
206
|
+
// Each imported thread follows the imported bot's model.
|
|
200
207
|
threadId: i === 0 ? bot.threadId : newId(), title: task.title, createdAt: task.createdAt, resumeCursors: {},
|
|
201
|
-
|
|
208
|
+
activity: "idle", busy: false, unread: false,
|
|
202
209
|
...(task.titleFromFirstMessage ? { titleFromFirstMessage: true } : {}),
|
|
203
210
|
};
|
|
204
211
|
// Same rule as a message's `from`: the opener is remapped to its
|
|
@@ -229,9 +236,16 @@ export function importTeamBackup(store, routines, input, selection, options = {}
|
|
|
229
236
|
groups.push(group);
|
|
230
237
|
groupIds.set(source.key, group.id);
|
|
231
238
|
const responder = source.defaultResponder;
|
|
232
|
-
store.patchGroup(group.id, {
|
|
239
|
+
store.patchGroup(group.id, {
|
|
240
|
+
bulletin: source.bulletin,
|
|
241
|
+
setupCompletedAt: Date.now(),
|
|
242
|
+
...(source.dm && source.tasks[0]?.turnTimeoutMinutes != null
|
|
243
|
+
? { turnTimeoutMinutes: source.tasks[0].turnTimeoutMinutes }
|
|
244
|
+
: {}),
|
|
245
|
+
defaultResponder: responder.kind === "member" ? { kind: "member", botId: botIds.get(responder.botId) }
|
|
233
246
|
: responder.kind === "auto" ? { kind: "auto", ...(responder.fallbackBotId ? { fallbackBotId: botIds.get(responder.fallbackBotId) } : {}) }
|
|
234
|
-
: responder
|
|
247
|
+
: responder
|
|
248
|
+
});
|
|
235
249
|
// Use the existing task APIs for rooms; direct-message rooms have one.
|
|
236
250
|
const threads = source.tasks.map((task, i) => {
|
|
237
251
|
if (i === 0) {
|
|
@@ -248,6 +262,7 @@ export function importTeamBackup(store, routines, input, selection, options = {}
|
|
|
248
262
|
updatedAt: task.messages.reduce((max, message) => Math.max(max, message.at), task.createdAt),
|
|
249
263
|
...(task.pinned === true ? { pinned: true } : {}),
|
|
250
264
|
...(task.titleFromFirstMessage ? { titleFromFirstMessage: true } : {}),
|
|
265
|
+
...(task.turnTimeoutMinutes != null ? { turnTimeoutMinutes: task.turnTimeoutMinutes } : {}),
|
|
251
266
|
}));
|
|
252
267
|
store.switchGroupTask(group.id, threads[source.tasks.findIndex((task) => task.key === source.activeTask)]);
|
|
253
268
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readyToRun, signedOut } from "./default-model-selection.js";
|
|
2
|
+
import { sameModelSelection } from "../shared/thread-model.js";
|
|
3
|
+
function canRunOn(engine, selection, context) {
|
|
4
|
+
return Boolean(engine?.enabled) && engine.snapshot.state === "available" && context.refusal?.(engine) === undefined &&
|
|
5
|
+
context.allows?.(selection) !== false;
|
|
6
|
+
}
|
|
7
|
+
/** Why this thread's own model gives way to its bot's, or null when the turn
|
|
8
|
+
* runs on it as picked:
|
|
9
|
+
* - "unavailable": its engine is gone, disabled, unavailable or not allowed
|
|
10
|
+
* (by the organisation, or a hosted workspace's assigned models);
|
|
11
|
+
* - "signed-out": its engine is signed out and the bot's model is on another
|
|
12
|
+
* engine that can run. On the same engine the turn runs as picked, so the
|
|
13
|
+
* engine's own sign-in prompt shows; a fallback never hides it. */
|
|
14
|
+
export function threadModelFallback(own, botModel, engine, context = {}) {
|
|
15
|
+
if (sameModelSelection(own, botModel))
|
|
16
|
+
return null;
|
|
17
|
+
const mine = engine(own.instanceId);
|
|
18
|
+
if (!canRunOn(mine, own, context))
|
|
19
|
+
return "unavailable";
|
|
20
|
+
if (!signedOut(mine))
|
|
21
|
+
return null;
|
|
22
|
+
// On the same engine the bot's model is signed out too, so it never stands in.
|
|
23
|
+
const theirs = engine(botModel.instanceId);
|
|
24
|
+
return canRunOn(theirs, botModel, context) && readyToRun(theirs, context) ? "signed-out" : null;
|
|
25
|
+
}
|
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
/**
|
|
5
5
|
* Automatic continuity: when a turn ends without a final answer and the work
|
|
6
|
-
* is resumable (budget cap
|
|
6
|
+
* is resumable (a budget cap), persist a structured handoff and
|
|
7
7
|
* let the harness start a `Continue:` thread on the same bot — transparent to
|
|
8
8
|
* the bot, which just sees a normal new thread seeded with the handoff.
|
|
9
9
|
*
|
|
@@ -82,23 +82,21 @@ export function writeTurnHandoff(input) {
|
|
|
82
82
|
return null;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
|
|
85
|
+
/** The harness's own budget stops, in its own words: the chat driver's
|
|
86
|
+
* "Stopped after 64 steps without a final answer…" and the older model-call
|
|
87
|
+
* cap. Matched from the start of the failure, so a provider's error text can
|
|
88
|
+
* never read as one: "upstream HTTP 429: Rate limit reached" is not a spent
|
|
89
|
+
* budget. */
|
|
90
|
+
const CAP_STOP = /^(?:stopped after \d+ [a-z ]+ without a final answer|model-call limit reached)/i;
|
|
89
91
|
/** Classify a terminal turn as resumable. Returns the reason for the
|
|
90
92
|
* continuation (`cap` = budget exhausted), or null when the turn must not be
|
|
91
|
-
* auto-continued.
|
|
92
|
-
*
|
|
93
|
-
*
|
|
93
|
+
* auto-continued. Only a cap is continued. A provider error (a rate limit, a
|
|
94
|
+
* quota, a rejected key, a model that cannot take the request) fails the same
|
|
95
|
+
* way in a new thread: continuing would go straight back to the provider that
|
|
96
|
+
* just refused. A tool error includes a person's or the approval policy's
|
|
97
|
+
* denial, and a new unattended thread would retry what was refused. */
|
|
94
98
|
export function classifyContinuable(_stopReason, failure) {
|
|
95
|
-
|
|
96
|
-
return null;
|
|
97
|
-
if (NOT_RESUMABLE.test(failure))
|
|
98
|
-
return null;
|
|
99
|
-
if (/limit reached|stopped after \d+ steps|without a final answer/i.test(failure))
|
|
100
|
-
return "cap";
|
|
101
|
-
return null;
|
|
99
|
+
return failure && CAP_STOP.test(failure) ? "cap" : null;
|
|
102
100
|
}
|
|
103
101
|
const CONTINUE_TITLE = /^Continue: /;
|
|
104
102
|
const MAX_PER_BOT_PER_HOUR = 5;
|
|
@@ -131,7 +129,10 @@ export function makeCapContinuationSubscriber(deps) {
|
|
|
131
129
|
if (CONTINUE_TITLE.test(deadTask.title ?? ""))
|
|
132
130
|
return;
|
|
133
131
|
const approvalMode = deadTask.approvalMode === "ask" || deadTask.approvalMode === "full" ? deadTask.approvalMode : undefined;
|
|
134
|
-
const contTask = deps.store.createTask(
|
|
132
|
+
const contTask = deps.store.createTask(
|
|
133
|
+
// Not activated: the person's open thread stays theirs, and their next
|
|
134
|
+
// message goes where they were, not into the continuation.
|
|
135
|
+
bot.id, `Continue: ${(deadTask.title ?? "task").slice(0, 90)}`, false, deadTask.projectId, undefined, approvalMode, deadTask.modelSelection);
|
|
135
136
|
if (!contTask)
|
|
136
137
|
return;
|
|
137
138
|
rec.count += 1;
|
|
@@ -84,11 +84,16 @@ export function createWebhookIngressHandler(manager, claimRequest) {
|
|
|
84
84
|
let release;
|
|
85
85
|
try {
|
|
86
86
|
release = claimRequest?.();
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
let secret = "";
|
|
88
|
+
try {
|
|
89
|
+
secret = (match[2] ? decodeURIComponent(match[2]) : "") || bearerSecret(req);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// A malformed escape is not a secret this server issued.
|
|
93
|
+
}
|
|
89
94
|
// Reject bad capability URLs before buffering or parsing attacker input.
|
|
90
95
|
if (!manager.authorize(match[1], secret)) {
|
|
91
|
-
manager.
|
|
96
|
+
manager.recordUnauthorized(match[1], {
|
|
92
97
|
contentType: header(req, "content-type"),
|
|
93
98
|
eventName: eventName(req),
|
|
94
99
|
deliveryId: deliveryId(req),
|