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
|
@@ -11,6 +11,7 @@ import { PROFILE_REQUEST_FIELDS } from "../shared/profile-request.js";
|
|
|
11
11
|
import { parseBotProfilePatch } from "./bot-profile.js";
|
|
12
12
|
import { validateBotCwd } from "./bot-cwd.js";
|
|
13
13
|
import { newId } from "./contracts.js";
|
|
14
|
+
import { UNDO_STALE } from "./direct-apply.js";
|
|
14
15
|
import { profileRevision, profileSnapshot } from "./profile-revision.js";
|
|
15
16
|
import { recordProfileChange } from "./profile-versions.js";
|
|
16
17
|
import { redactSecretsInText } from "./redact.js";
|
|
@@ -205,6 +206,9 @@ export class ProfileRequestService {
|
|
|
205
206
|
const snapshot = profileSnapshot(target);
|
|
206
207
|
const before = {};
|
|
207
208
|
const finalChanges = {};
|
|
209
|
+
// Undo restores `before`, so it is offered only when the card holds the
|
|
210
|
+
// old values exactly (credential-shaped text is scrubbed from cards).
|
|
211
|
+
let exactBefore = true;
|
|
208
212
|
for (const field of PROFILE_REQUEST_FIELDS) {
|
|
209
213
|
if (field === "notifications" || field === "speakReplies") {
|
|
210
214
|
const value = changes[field];
|
|
@@ -218,6 +222,8 @@ export class ProfileRequestService {
|
|
|
218
222
|
if (value === undefined || value === snapshot[field])
|
|
219
223
|
continue;
|
|
220
224
|
before[field] = redactSecretsInText(snapshot[field]);
|
|
225
|
+
if (before[field] !== snapshot[field])
|
|
226
|
+
exactBefore = false;
|
|
221
227
|
finalChanges[field] = value;
|
|
222
228
|
}
|
|
223
229
|
}
|
|
@@ -240,11 +246,14 @@ export class ProfileRequestService {
|
|
|
240
246
|
expectedRevision: profileRevision(target),
|
|
241
247
|
};
|
|
242
248
|
const copy = profileCardCopy({ name: targetName, crossBot }, snapshot, before, finalChanges, reason);
|
|
243
|
-
const
|
|
249
|
+
const grant = submitted ? this.autoApply?.(args.botId, args.threadId, targetBotId) ?? null : null;
|
|
250
|
+
// A new working folder widens what the bot's tools read and write
|
|
251
|
+
// without asking, so below Full access it keeps today's card.
|
|
252
|
+
const automatic = grant === "full-access" || (grant === "self" && finalChanges.cwd === undefined);
|
|
253
|
+
const persistence = this.canPersist?.(args.botId, args.threadId, !automatic);
|
|
244
254
|
if (persistence && !persistence.ok) {
|
|
245
255
|
throw new ProfileRequestError(persistence.error, persistence.status);
|
|
246
256
|
}
|
|
247
|
-
const automatic = submitted && this.autoApply?.(args.botId, args.threadId) === true;
|
|
248
257
|
const messageInput = {
|
|
249
258
|
role: "bot",
|
|
250
259
|
kind: "options",
|
|
@@ -267,10 +276,77 @@ export class ProfileRequestService {
|
|
|
267
276
|
// The hidden receipt is persisted before the profile changes. The same
|
|
268
277
|
// validation and durable commit marker serve both automatic and human decisions.
|
|
269
278
|
const result = this.resolve({ botId: args.botId, threadId: args.threadId, requestId, behavior: "allow" });
|
|
270
|
-
if (result.state === "applied")
|
|
271
|
-
|
|
279
|
+
if (result.state === "applied") {
|
|
280
|
+
if (!result.settlementPending)
|
|
281
|
+
this.recordAutoApplied(args.threadId, message.id, targetBotId, exactBefore);
|
|
282
|
+
return { ...proposal, result, appliedBy: grant };
|
|
283
|
+
}
|
|
272
284
|
throw new ProfileRequestError(result.state === "invalid" ? result.error : "The profile change could not be applied", result.state === "invalid" ? result.status : 409);
|
|
273
285
|
}
|
|
286
|
+
/** Marks a card whose change applied without a person, with the revision
|
|
287
|
+
* its Undo checks. A failure here only loses the Undo. */
|
|
288
|
+
recordAutoApplied(threadId, messageId, targetBotId, exactBefore) {
|
|
289
|
+
try {
|
|
290
|
+
const card = this.store.messagesFor(threadId).find((candidate) => candidate.id === messageId)?.card;
|
|
291
|
+
const target = this.store.bot(targetBotId);
|
|
292
|
+
if (!card?.profileRequest || !target)
|
|
293
|
+
return;
|
|
294
|
+
this.store.patchMessage(threadId, messageId, {
|
|
295
|
+
card: {
|
|
296
|
+
...card,
|
|
297
|
+
autoApplied: true,
|
|
298
|
+
profileRequest: { ...card.profileRequest, ...(exactBefore ? { undo: { appliedRevision: profileRevision(target) } } : {}) },
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// The profile change itself is durable; only its Undo is lost.
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/** Puts back the fields a change that applied without a person changed.
|
|
307
|
+
* Authorized by the caller exactly like answering the card; refuses,
|
|
308
|
+
* applying nothing, once the profile changed since. */
|
|
309
|
+
undo(args) {
|
|
310
|
+
const message = this.store
|
|
311
|
+
.messagesFor(args.threadId)
|
|
312
|
+
.find((candidate) => candidate.card?.requestId === args.requestId && candidate.card.profileRequest);
|
|
313
|
+
const card = message?.card;
|
|
314
|
+
const payload = card?.profileRequest;
|
|
315
|
+
if (!message || !card || !payload)
|
|
316
|
+
return { claimed: false };
|
|
317
|
+
if (card.undone)
|
|
318
|
+
return { claimed: true, state: "already_undone" };
|
|
319
|
+
const cannot = (error, status = 409) => ({ claimed: true, state: "invalid", error, status });
|
|
320
|
+
if (!card.autoApplied || card.answered !== "allow")
|
|
321
|
+
return cannot("Only a change that applied on its own can be undone here.");
|
|
322
|
+
if (payload.botId !== args.botId || payload.threadId !== args.threadId) {
|
|
323
|
+
return cannot("This profile change belongs to another conversation", 403);
|
|
324
|
+
}
|
|
325
|
+
if (!payload.undo)
|
|
326
|
+
return cannot("This change can't be undone here.");
|
|
327
|
+
const target = this.store.bot(payload.targetBotId);
|
|
328
|
+
if (!target || profileRevision(target) !== payload.undo.appliedRevision) {
|
|
329
|
+
return { claimed: true, state: "invalid", error: UNDO_STALE, status: 409, stale: true };
|
|
330
|
+
}
|
|
331
|
+
const { cwd, ...rest } = payload.before;
|
|
332
|
+
const patch = {};
|
|
333
|
+
for (const field of PROFILE_REQUEST_FIELDS) {
|
|
334
|
+
if (field === "cwd" || payload.changes[field] === undefined)
|
|
335
|
+
continue;
|
|
336
|
+
patch[field] = rest[field];
|
|
337
|
+
}
|
|
338
|
+
if (payload.changes.cwd !== undefined) {
|
|
339
|
+
const checked = validateBotCwd(cwd || null);
|
|
340
|
+
if (!checked.ok)
|
|
341
|
+
return cannot(checked.error);
|
|
342
|
+
patch.cwd = checked.cwd ?? undefined;
|
|
343
|
+
}
|
|
344
|
+
if (!this.store.patchBotProfile(target.id, patch))
|
|
345
|
+
return cannot(NO_SUCH_BOT, 404);
|
|
346
|
+
recordProfileChange(target.id, "user", `undo:${message.id}`, payload.changes, payload.before);
|
|
347
|
+
this.store.patchMessage(args.threadId, message.id, { card: { ...card, undone: true } });
|
|
348
|
+
return { claimed: true, state: "undone", targetBotId: target.id };
|
|
349
|
+
}
|
|
274
350
|
/** Claims a profile card even after it was settled, so a duplicate click
|
|
275
351
|
* never re-applies an already-applied change. */
|
|
276
352
|
resolve(args) {
|
|
@@ -14,9 +14,13 @@ export function isLoopback(hostname) {
|
|
|
14
14
|
const host = hostname.replace(/^\[|\]$/g, "");
|
|
15
15
|
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host.startsWith("127.");
|
|
16
16
|
}
|
|
17
|
+
/** A provider's base URL: the configured one, or its default. */
|
|
18
|
+
export function providerBaseUrl(provider, base) {
|
|
19
|
+
return (base?.trim() || DEFAULT_URLS[provider]).replace(/\/+$/, "");
|
|
20
|
+
}
|
|
17
21
|
/** The models endpoint for a provider, from its base URL or the default. */
|
|
18
22
|
export function providerModelsUrl(provider, base) {
|
|
19
|
-
const root = (
|
|
23
|
+
const root = providerBaseUrl(provider, base);
|
|
20
24
|
if (provider === "anthropic")
|
|
21
25
|
return root.endsWith("/v1") ? `${root}/models` : `${root}/v1/models`;
|
|
22
26
|
return `${root}/models`;
|
|
@@ -283,6 +283,7 @@ export const CLIENT_ALLOW = [
|
|
|
283
283
|
// approvals and cards
|
|
284
284
|
{ methods: ["POST"], path: /^\/api\/bots\/[\w-]+\/respond$/ },
|
|
285
285
|
{ methods: ["POST"], path: /^\/api\/threads\/[\w-]+\/respond$/ },
|
|
286
|
+
{ methods: ["POST"], path: /^\/api\/threads\/[\w-]+\/undo$/ }, // whoever may answer the card: see the handler
|
|
286
287
|
{ methods: ["PATCH"], path: /^\/api\/bots\/[\w-]+\/cards\/[\w-]+$/ },
|
|
287
288
|
{ methods: ["POST"], path: /^\/api\/bots\/[\w-]+\/secret-cards\/[\w-]+\/(?:resume|dismiss)$/ },
|
|
288
289
|
{ methods: ["GET"], path: /^\/api\/bots\/[\w-]+\/connector-cards\/[\w-]+\/status$/ },
|
|
@@ -340,7 +341,7 @@ export function clientBotPatchViolation(body) {
|
|
|
340
341
|
return null;
|
|
341
342
|
}
|
|
342
343
|
/** Same for a room: name and reading state, never its folder or who answers. */
|
|
343
|
-
const CLIENT_GROUP_PATCH_FIELDS = new Set(["name", "bulletin", "unread", "pinnedMessageId", "section"]);
|
|
344
|
+
const CLIENT_GROUP_PATCH_FIELDS = new Set(["name", "bulletin", "unread", "pinnedMessageId", "section", "turnTimeoutMinutes"]);
|
|
344
345
|
export function clientGroupPatchViolation(body) {
|
|
345
346
|
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
346
347
|
return "body";
|
|
@@ -1,6 +1,34 @@
|
|
|
1
|
+
import { MAX_ROOM_TURN_TIMEOUT_MINUTES, MIN_ROOM_TURN_TIMEOUT_MINUTES, } from "./config.js";
|
|
1
2
|
export function roomTurnTimeoutMs(minutes) {
|
|
2
3
|
return minutes * 60_000;
|
|
3
4
|
}
|
|
5
|
+
/** A conversation may replace the global group ceiling. Null clears that
|
|
6
|
+
* choice. Anything else is refused rather than silently clamped. */
|
|
7
|
+
export function parseConversationTurnTimeout(value) {
|
|
8
|
+
if (value === null)
|
|
9
|
+
return { ok: true, minutes: null };
|
|
10
|
+
if (typeof value !== "number" ||
|
|
11
|
+
!Number.isInteger(value) ||
|
|
12
|
+
value < MIN_ROOM_TURN_TIMEOUT_MINUTES ||
|
|
13
|
+
value > MAX_ROOM_TURN_TIMEOUT_MINUTES) {
|
|
14
|
+
return {
|
|
15
|
+
ok: false,
|
|
16
|
+
error: "Turn limit must be a whole number from 1 to 1,440, or null to use the group default.",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { ok: true, minutes: value };
|
|
20
|
+
}
|
|
21
|
+
/** The ceiling captured for one turn. An override wins; a missing or
|
|
22
|
+
* unusable one keeps the global group limit. */
|
|
23
|
+
export function effectiveRoomTurnTimeoutMinutes(globalMinutes, override) {
|
|
24
|
+
if (typeof override === "number" &&
|
|
25
|
+
Number.isInteger(override) &&
|
|
26
|
+
override >= MIN_ROOM_TURN_TIMEOUT_MINUTES &&
|
|
27
|
+
override <= MAX_ROOM_TURN_TIMEOUT_MINUTES) {
|
|
28
|
+
return override;
|
|
29
|
+
}
|
|
30
|
+
return globalMinutes;
|
|
31
|
+
}
|
|
4
32
|
/**
|
|
5
33
|
* One room turn's slice of the configured room ceiling.
|
|
6
34
|
*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { sameModelSelection } from "../../shared/thread-model.js";
|
|
2
|
+
import { PASS } from "./table.js";
|
|
3
|
+
const MAX_THREAD_IDS = 10_000;
|
|
4
|
+
export function createThreadModelRoutes(deps) {
|
|
5
|
+
return async ({ req, res, path, method, auth, json, readBody }) => {
|
|
6
|
+
const m = path.match(/^\/api\/bots\/([\w-]+)\/threads\/follow-model$/);
|
|
7
|
+
if (!m || method !== "POST")
|
|
8
|
+
return PASS;
|
|
9
|
+
const bot = deps.bot(m[1]);
|
|
10
|
+
if (!bot)
|
|
11
|
+
return json(res, 404, { error: "no such bot" });
|
|
12
|
+
if (!deps.mayChangeModel(auth))
|
|
13
|
+
return json(res, 403, { error: "On this Cloud only its owner can change a bot's model." });
|
|
14
|
+
const body = await readBody(req);
|
|
15
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || Object.keys(body).some((key) => key !== "threadIds")) {
|
|
16
|
+
return json(res, 400, { error: "send {} or { threadIds }" });
|
|
17
|
+
}
|
|
18
|
+
const raw = body.threadIds;
|
|
19
|
+
if (raw !== undefined && (!Array.isArray(raw) || raw.length > MAX_THREAD_IDS || raw.some((id) => typeof id !== "string"))) {
|
|
20
|
+
return json(res, 400, { error: "threadIds must be a list of this bot's thread ids" });
|
|
21
|
+
}
|
|
22
|
+
const tasks = bot.tasks ?? [];
|
|
23
|
+
const ids = new Set(tasks.map((task) => task.threadId));
|
|
24
|
+
const foreign = raw?.find((id) => !ids.has(id));
|
|
25
|
+
if (foreign !== undefined)
|
|
26
|
+
return json(res, 400, { error: `"${foreign}" is not one of this bot's threads` });
|
|
27
|
+
if (bot.approvalGrant)
|
|
28
|
+
return json(res, 409, { error: "wait for the approval-level change to finish before changing models" });
|
|
29
|
+
const listed = raw === undefined ? null : new Set(raw);
|
|
30
|
+
const own = tasks.filter((task) => (!listed || listed.has(task.threadId)) &&
|
|
31
|
+
task.modelSelection !== undefined && !sameModelSelection(task.modelSelection, bot.modelSelection));
|
|
32
|
+
const busy = own.filter((task) => deps.busy(bot.id, task.threadId));
|
|
33
|
+
const switched = deps.follow(bot.id, own.filter((task) => !busy.includes(task)).map((task) => task.threadId));
|
|
34
|
+
return json(res, 200, { switched: switched.length, busy: busy.length, bot: deps.reply(bot.id) });
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PASS } from "./table.js";
|
|
2
|
+
export function createUndoRoutes(deps) {
|
|
3
|
+
return async ({ req, res, path, method, auth, json, readBody }) => {
|
|
4
|
+
const m = path.match(/^\/api\/threads\/([\w-]+)\/undo$/);
|
|
5
|
+
if (!m || method !== "POST")
|
|
6
|
+
return PASS;
|
|
7
|
+
const threadId = m[1];
|
|
8
|
+
const body = await readBody(req);
|
|
9
|
+
const requestId = typeof body?.requestId === "string" ? body.requestId : "";
|
|
10
|
+
if (!requestId)
|
|
11
|
+
return json(res, 400, { error: "requestId is required" });
|
|
12
|
+
const refusal = deps.refusal(auth, threadId, requestId);
|
|
13
|
+
if (refusal)
|
|
14
|
+
return json(res, 403, { error: refusal });
|
|
15
|
+
deps.undo(auth, res, threadId, requestId);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { cronScheduleLabel } from "../shared/cron-label.js";
|
|
5
5
|
import { normalizeCronSchedule, nextCronRuns } from "../shared/routine-schedule.js";
|
|
6
6
|
import { newId } from "./contracts.js";
|
|
7
|
+
import { SELF_ROUTINE_CAP, UNDO_STALE } from "./direct-apply.js";
|
|
7
8
|
import { redactSecretsInText } from "./redact.js";
|
|
8
9
|
import { parseJson, schemaIssue } from "./schema.js";
|
|
9
10
|
import { intervalHasRestrictions, nextOccurrence, } from "./routines.js";
|
|
@@ -200,6 +201,19 @@ const storedOperationSchema = z.discriminatedUnion("action", [
|
|
|
200
201
|
z.object({ action: z.literal("run_now"), ...storedManageBase }).strict(),
|
|
201
202
|
z.object({ action: z.literal("delete"), ...storedManageBase }).strict(),
|
|
202
203
|
]);
|
|
204
|
+
const storedSnapshotSchema = storedDefinitionSchema.extend({
|
|
205
|
+
enabled: z.boolean(),
|
|
206
|
+
target: z.literal("room-goal").optional(),
|
|
207
|
+
groupId: z.string().min(1).max(128).optional(),
|
|
208
|
+
resultsThreadId: z.string().min(1).max(128).optional(),
|
|
209
|
+
}).strict();
|
|
210
|
+
const storedUndoSchema = z.object({
|
|
211
|
+
name: z.string().max(200),
|
|
212
|
+
schedule: storedScheduleSchema.optional(),
|
|
213
|
+
appliedUpdatedAt: z.number().int().nonnegative().optional(),
|
|
214
|
+
before: storedSnapshotSchema.optional(),
|
|
215
|
+
ownersBefore: z.boolean().optional(),
|
|
216
|
+
}).strict();
|
|
203
217
|
const routineRequestCardDataSchema = z.object({
|
|
204
218
|
version: z.literal(1),
|
|
205
219
|
requestId: z.string().min(1).max(128),
|
|
@@ -209,6 +223,7 @@ const routineRequestCardDataSchema = z.object({
|
|
|
209
223
|
operation: storedOperationSchema,
|
|
210
224
|
appliedAt: z.number().int().nonnegative().optional(),
|
|
211
225
|
resultId: z.string().min(1).max(128).optional(),
|
|
226
|
+
undo: storedUndoSchema.optional(),
|
|
212
227
|
}).strict();
|
|
213
228
|
export class RoutineRequestError extends Error {
|
|
214
229
|
status;
|
|
@@ -896,6 +911,47 @@ function revalidateOperation(operation, manager, botId, now) {
|
|
|
896
911
|
}
|
|
897
912
|
}
|
|
898
913
|
}
|
|
914
|
+
/** The routine as it stands, kept for Undo. None when it carries what a
|
|
915
|
+
* card cannot hold exactly: attachments, or credential-shaped text the
|
|
916
|
+
* transcript scrubs. */
|
|
917
|
+
function routineSnapshot(routine) {
|
|
918
|
+
if (routine.attachments?.length)
|
|
919
|
+
return undefined;
|
|
920
|
+
if (redactSecretsInText(routine.name) !== routine.name || redactSecretsInText(routine.prompt) !== routine.prompt)
|
|
921
|
+
return undefined;
|
|
922
|
+
const snapshot = {
|
|
923
|
+
name: routine.name,
|
|
924
|
+
instructions: routine.prompt,
|
|
925
|
+
schedule: structuredClone(routine.schedule),
|
|
926
|
+
runOn: routine.runOn,
|
|
927
|
+
durationMinutes: routine.durationMinutes,
|
|
928
|
+
...(routine.timeoutMinutes === undefined ? {} : { timeoutMinutes: routine.timeoutMinutes }),
|
|
929
|
+
...(routine.continuity ? { continuity: true } : {}),
|
|
930
|
+
...(routine.overlap === "queue" ? { overlap: "queue" } : {}),
|
|
931
|
+
enabled: routine.enabled,
|
|
932
|
+
...(routine.target === "room-goal" ? { target: "room-goal" } : {}),
|
|
933
|
+
...(routine.groupId ? { groupId: routine.groupId } : {}),
|
|
934
|
+
...(routine.resultsThreadId ? { resultsThreadId: routine.resultsThreadId } : {}),
|
|
935
|
+
};
|
|
936
|
+
return storedSnapshotSchema.safeParse(snapshot).success ? snapshot : undefined;
|
|
937
|
+
}
|
|
938
|
+
/** Puts an updated routine's definition back exactly: an interval's
|
|
939
|
+
* restrictions are cleared explicitly, since an omitted one is kept. */
|
|
940
|
+
function restorePatch(before, now) {
|
|
941
|
+
const schedule = asSchedule(before.schedule, now);
|
|
942
|
+
return {
|
|
943
|
+
name: before.name,
|
|
944
|
+
prompt: before.instructions,
|
|
945
|
+
runOn: before.runOn,
|
|
946
|
+
schedule: schedule.type === "interval"
|
|
947
|
+
? { ...schedule, weekdays: schedule.weekdays ?? null, window: schedule.window ?? null, endsAt: schedule.endsAt ?? null }
|
|
948
|
+
: schedule,
|
|
949
|
+
durationMinutes: before.durationMinutes,
|
|
950
|
+
timeoutMinutes: before.timeoutMinutes ?? null,
|
|
951
|
+
continuity: before.continuity === true,
|
|
952
|
+
overlap: before.overlap ?? "skip",
|
|
953
|
+
};
|
|
954
|
+
}
|
|
899
955
|
export class RoutineRequestService {
|
|
900
956
|
store;
|
|
901
957
|
routines;
|
|
@@ -968,18 +1024,27 @@ export class RoutineRequestService {
|
|
|
968
1024
|
};
|
|
969
1025
|
if (args.from)
|
|
970
1026
|
messageInput.from = args.from;
|
|
1027
|
+
// Resolve the current source-thread grant after the asynchronous probe.
|
|
1028
|
+
const owner = operation.forBot?.botId ?? botId;
|
|
1029
|
+
const grant = submitted ? this.autoApply?.(botId, threadId, owner) ?? null : null;
|
|
1030
|
+
// A bot applying its own routines keeps at most SELF_ROUTINE_CAP of them
|
|
1031
|
+
// on; a create or resume past that shows the card, as before. Full
|
|
1032
|
+
// access has no cap.
|
|
1033
|
+
const automatic = grant === "full-access" || (grant === "self" && !this.overSelfCap(operation, owner));
|
|
971
1034
|
// This check and append are deliberately adjacent and synchronous. JS
|
|
972
1035
|
// cannot interleave another completed proposal between the capacity /
|
|
973
1036
|
// ownership decision and the durable transcript write.
|
|
974
|
-
const persistence = this.canPersist?.(botId, threadId);
|
|
1037
|
+
const persistence = this.canPersist?.(botId, threadId, !automatic);
|
|
975
1038
|
if (persistence && !persistence.ok) {
|
|
976
1039
|
throw new RoutineRequestError(persistence.error, persistence.status);
|
|
977
1040
|
}
|
|
978
1041
|
if (args.canCommit && !args.canCommit()) {
|
|
979
1042
|
throw new RoutineRequestError("The requesting turn ended before this proposal could be saved", 401);
|
|
980
1043
|
}
|
|
981
|
-
//
|
|
982
|
-
const
|
|
1044
|
+
// What Undo puts back, taken before the change.
|
|
1045
|
+
const before = automatic && (operation.action === "update" || operation.action === "delete")
|
|
1046
|
+
? this.snapshotOf(operation.routineId, owner)
|
|
1047
|
+
: undefined;
|
|
983
1048
|
if (automatic) {
|
|
984
1049
|
messageInput.card.options = [];
|
|
985
1050
|
messageInput.card.dismissed = true;
|
|
@@ -1005,8 +1070,10 @@ export class RoutineRequestService {
|
|
|
1005
1070
|
catch (error) {
|
|
1006
1071
|
result = { claimed: true, state: "invalid", error: error instanceof Error ? error.message : String(error), status: error instanceof RoutineRequestError ? error.status : 400 };
|
|
1007
1072
|
}
|
|
1008
|
-
if (result.state === "applied")
|
|
1009
|
-
|
|
1073
|
+
if (result.state === "applied") {
|
|
1074
|
+
this.recordAutoApplied(threadId, message.id, definition, before);
|
|
1075
|
+
return { ...proposal, result, ...(grant ? { appliedBy: grant } : {}) };
|
|
1076
|
+
}
|
|
1010
1077
|
// The scheduler commit can succeed even if settling its transcript
|
|
1011
1078
|
// fails. Report that exact result; a retry only finishes the receipt.
|
|
1012
1079
|
const receipt = this.routines.routineRequestReceipt(requestId);
|
|
@@ -1015,10 +1082,147 @@ export class RoutineRequestService {
|
|
|
1015
1082
|
return { ...proposal, result: {
|
|
1016
1083
|
claimed: true, state: "applied", action: receipt.action, resultId: receipt.resultId,
|
|
1017
1084
|
settlementPending: true, message: "Routine change applied. Recording the operation receipt could not finish; the change will not be applied again.",
|
|
1018
|
-
} };
|
|
1085
|
+
}, ...(grant ? { appliedBy: grant } : {}) };
|
|
1019
1086
|
}
|
|
1020
1087
|
throw new RoutineRequestError(result.state === "invalid" ? result.error : "The routine change could not be applied", result.state === "invalid" ? result.status : 409);
|
|
1021
1088
|
}
|
|
1089
|
+
/** Whether one more enabled routine would take the bot past the cap. */
|
|
1090
|
+
overSelfCap(operation, owner) {
|
|
1091
|
+
if (operation.action !== "create" && operation.action !== "resume")
|
|
1092
|
+
return false;
|
|
1093
|
+
const enabled = this.routines.listRoutines().filter((routine) => routine.botId === owner && routine.enabled).length;
|
|
1094
|
+
return enabled + 1 > SELF_ROUTINE_CAP;
|
|
1095
|
+
}
|
|
1096
|
+
snapshotOf(routineId, owner) {
|
|
1097
|
+
const routine = ownedRoutine(this.routines, routineId, owner);
|
|
1098
|
+
return routine ? routineSnapshot(routine) : undefined;
|
|
1099
|
+
}
|
|
1100
|
+
/** Marks a card whose change applied without a person, with what its
|
|
1101
|
+
* one-line receipt and Undo need. A failure here leaves the change
|
|
1102
|
+
* applied and the card as it was, only without Undo. */
|
|
1103
|
+
recordAutoApplied(threadId, messageId, definition, before) {
|
|
1104
|
+
try {
|
|
1105
|
+
const message = this.store.messagesFor(threadId).find((candidate) => candidate.id === messageId);
|
|
1106
|
+
const card = message?.card;
|
|
1107
|
+
const payload = card?.routineRequest;
|
|
1108
|
+
if (!message || !card || !payload)
|
|
1109
|
+
return;
|
|
1110
|
+
const action = payload.operation.action;
|
|
1111
|
+
const routineId = payload.operation.action === "create" ? payload.resultId : payload.operation.routineId;
|
|
1112
|
+
const after = routineId ? this.routines.listRoutines().find((routine) => routine.id === routineId) : undefined;
|
|
1113
|
+
const undo = {
|
|
1114
|
+
name: redactSecretsInText(after?.name ?? definition?.name ?? before?.name ?? "routine"),
|
|
1115
|
+
...(action === "create" || action === "update" || action === "resume"
|
|
1116
|
+
? { schedule: structuredClone(after?.schedule ?? definition?.schedule) }
|
|
1117
|
+
: {}),
|
|
1118
|
+
...(after && (action === "update" || action === "pause" || action === "resume") ? { appliedUpdatedAt: after.updatedAt } : {}),
|
|
1119
|
+
...(before ? { before } : {}),
|
|
1120
|
+
};
|
|
1121
|
+
if (!storedUndoSchema.safeParse(undo).success)
|
|
1122
|
+
delete undo.schedule;
|
|
1123
|
+
this.store.patchMessage(threadId, messageId, {
|
|
1124
|
+
card: { ...card, autoApplied: true, routineRequest: { ...payload, undo } },
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
catch {
|
|
1128
|
+
// The routine change itself is durable; only its Undo is lost.
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
/** Puts back a change that applied without a person. Authorized by the
|
|
1132
|
+
* caller exactly like answering the card; refuses, applying nothing, once
|
|
1133
|
+
* the routine changed since. A second Undo reports already undone. */
|
|
1134
|
+
undo(args) {
|
|
1135
|
+
const message = this.store
|
|
1136
|
+
.messagesFor(args.threadId)
|
|
1137
|
+
.find((candidate) => candidate.card?.requestId === args.requestId && candidate.card.routineRequest);
|
|
1138
|
+
const card = message?.card;
|
|
1139
|
+
if (!message || !card)
|
|
1140
|
+
return { claimed: false };
|
|
1141
|
+
if (card.undone)
|
|
1142
|
+
return { claimed: true, state: "already_undone" };
|
|
1143
|
+
const cannot = (error, status = 409) => ({ claimed: true, state: "invalid", error, status });
|
|
1144
|
+
if (!card.autoApplied || card.answered !== "allow")
|
|
1145
|
+
return cannot("Only a change that applied on its own can be undone here.");
|
|
1146
|
+
const parsed = routineRequestCardDataSchema.safeParse(card.routineRequest);
|
|
1147
|
+
if (!parsed.success)
|
|
1148
|
+
return cannot("This change can't be undone here.");
|
|
1149
|
+
const payload = parsed.data;
|
|
1150
|
+
if (payload.botId !== args.botId || payload.threadId !== args.threadId) {
|
|
1151
|
+
return cannot("This routine change belongs to another conversation", 403);
|
|
1152
|
+
}
|
|
1153
|
+
const operation = payload.operation;
|
|
1154
|
+
const owner = operation.forBot?.botId ?? payload.botId;
|
|
1155
|
+
const undo = payload.undo;
|
|
1156
|
+
const stale = { claimed: true, state: "invalid", error: UNDO_STALE, status: 409, stale: true };
|
|
1157
|
+
const now = this.now();
|
|
1158
|
+
let routineId;
|
|
1159
|
+
let restoredId;
|
|
1160
|
+
try {
|
|
1161
|
+
switch (operation.action) {
|
|
1162
|
+
case "run_now":
|
|
1163
|
+
return cannot("A routine run can't be undone.");
|
|
1164
|
+
case "create": {
|
|
1165
|
+
routineId = payload.resultId ?? "";
|
|
1166
|
+
if (!routineId || !ownedRoutine(this.routines, routineId, owner))
|
|
1167
|
+
return stale;
|
|
1168
|
+
this.routines.remove(routineId);
|
|
1169
|
+
break;
|
|
1170
|
+
}
|
|
1171
|
+
case "update":
|
|
1172
|
+
case "pause":
|
|
1173
|
+
case "resume": {
|
|
1174
|
+
routineId = operation.routineId;
|
|
1175
|
+
if (undo?.appliedUpdatedAt === undefined || (operation.action === "update" && !undo.before)) {
|
|
1176
|
+
return cannot("This change can't be undone here.");
|
|
1177
|
+
}
|
|
1178
|
+
const current = ownedRoutine(this.routines, routineId, owner);
|
|
1179
|
+
if (!current || current.updatedAt !== undo.appliedUpdatedAt)
|
|
1180
|
+
return stale;
|
|
1181
|
+
const patch = operation.action === "update"
|
|
1182
|
+
? restorePatch(undo.before, now)
|
|
1183
|
+
: { enabled: operation.action === "pause" };
|
|
1184
|
+
if (!this.routines.update(routineId, patch))
|
|
1185
|
+
return stale;
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
case "delete": {
|
|
1189
|
+
routineId = operation.routineId;
|
|
1190
|
+
const before = undo?.before;
|
|
1191
|
+
if (!before)
|
|
1192
|
+
return cannot("This change can't be undone here.");
|
|
1193
|
+
restoredId = this.routines.create({
|
|
1194
|
+
name: before.name,
|
|
1195
|
+
prompt: before.instructions,
|
|
1196
|
+
botId: owner,
|
|
1197
|
+
...(before.target ? { target: before.target, groupId: before.groupId } : {}),
|
|
1198
|
+
runOn: before.runOn,
|
|
1199
|
+
enabled: before.enabled,
|
|
1200
|
+
schedule: asSchedule(before.schedule, now),
|
|
1201
|
+
durationMinutes: before.durationMinutes,
|
|
1202
|
+
...(before.timeoutMinutes === undefined ? {} : { timeoutMinutes: before.timeoutMinutes }),
|
|
1203
|
+
...(before.continuity ? { continuity: true } : {}),
|
|
1204
|
+
...(before.overlap === "queue" ? { overlap: "queue" } : {}),
|
|
1205
|
+
...(before.resultsThreadId ? { resultsThreadId: before.resultsThreadId } : {}),
|
|
1206
|
+
}).id;
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
default:
|
|
1210
|
+
return cannot("This change can't be undone here.");
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
return cannot(redactSecretsInText(error instanceof Error ? error.message : String(error)).slice(0, 500));
|
|
1215
|
+
}
|
|
1216
|
+
this.store.patchMessage(args.threadId, message.id, { card: { ...card, undone: true } });
|
|
1217
|
+
return {
|
|
1218
|
+
claimed: true,
|
|
1219
|
+
state: "undone",
|
|
1220
|
+
action: operation.action,
|
|
1221
|
+
routineId,
|
|
1222
|
+
...(restoredId ? { restoredId } : {}),
|
|
1223
|
+
...(undo?.ownersBefore !== undefined ? { ownersBefore: undo.ownersBefore } : {}),
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1022
1226
|
async requireCloudReadiness(operation, proposerBotId) {
|
|
1023
1227
|
if (!this.cloudReady || operation.action === "pause" || operation.action === "delete")
|
|
1024
1228
|
return;
|
|
@@ -943,7 +943,7 @@ export class RoutineManager {
|
|
|
943
943
|
this.commitMutation(() => {
|
|
944
944
|
run = this.newRun(routine, this.now(), true, allocations, request?.threadId ?? routine.sourceThreadId);
|
|
945
945
|
// Preserve the invoking chat as provenance/fallback for this run.
|
|
946
|
-
//
|
|
946
|
+
// The routine's results destination continues to win.
|
|
947
947
|
if (request)
|
|
948
948
|
run.sourceThreadId = request.threadId;
|
|
949
949
|
if (request)
|
|
@@ -1546,7 +1546,7 @@ export class RoutineManager {
|
|
|
1546
1546
|
if (value === null) {
|
|
1547
1547
|
const destination = this.options.resolveResultsThread?.(routine, true);
|
|
1548
1548
|
if (!destination)
|
|
1549
|
-
throw new Error("Could not
|
|
1549
|
+
throw new Error("Could not find a results thread for this routine");
|
|
1550
1550
|
routine.resultsThreadId = destination;
|
|
1551
1551
|
return () => this.options.discardResultsThread?.(routine.botId, destination);
|
|
1552
1552
|
}
|
|
@@ -45,7 +45,7 @@ function folderClause(cwd) {
|
|
|
45
45
|
function buildSetupPrompt(profileAside, cwd) {
|
|
46
46
|
return ("\n\nThe user explicitly asked you to set yourself up. For this setup request, help configure the bot from what the user tells you." +
|
|
47
47
|
` First ask at most four questions that change what you would build: what the job is, when it should happen (on demand, on a schedule, or when something arrives), which apps or accounts it touches, and ${folderClause(cwd)}.` +
|
|
48
|
-
" Then, before any tool call, tell the user in plain language what you intend: who you will be, what you will do and when, where you will work, what you will need from them, and what you will not do. Ask for missing choices, not an extra yes for already-requested actions
|
|
48
|
+
" Then, before any tool call, tell the user in plain language what you intend: who you will be, what you will do and when, where you will work, what you will need from them, and what you will not do. Ask for missing choices, not an extra yes for already-requested actions: changes to your own profile and routines apply immediately, and the person sees each with an Undo." +
|
|
49
49
|
" First send one message describing the changes you are about to request, then make the tool calls. Follow each result: if applied, continue without another confirmation; if pending, end the turn and wait for its in-app decision. Do not repeat the list or claim success from the permission mode alone." +
|
|
50
50
|
` Use propose_profile for your identity, standing rules ${profileAside}, and the working folder (cwd), propose_routine only for a schedule the user requested, and request_credential for any missing token.` +
|
|
51
51
|
" Full Access does not supply answers, credentials, or broader permissions for another bot. A credential request still needs the user's secure entry. Report failed or cancelled changes honestly." +
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// `/learn` — turn a described workflow, URL, folder, or "what we just did"
|
|
2
2
|
// into a reusable SKILL.md. The live agent authors the skill with skill_manage;
|
|
3
|
-
// the harness applies it
|
|
3
|
+
// the harness applies it (a bot's own skill applies at any level) or stages a pending review.
|
|
4
4
|
//
|
|
5
5
|
// There is no separate distillation engine. This module only builds the
|
|
6
6
|
// prompt and recognises the slash command, so it works on every engine
|
|
@@ -61,7 +61,7 @@ export function buildLearnPrompt(userRequest) {
|
|
|
61
61
|
"1. Inventory every source the user named, using the tools you already have — file tools for local paths, web fetch for URLs, and this conversation if they referred to something you just did. If the request is ambiguous about scope, make a reasonable choice and note it; do not stall.\n" +
|
|
62
62
|
"2. Check existing skills with skills_list. If one already covers this topic, leave it alone unless the user explicitly asked to revise that named learned/editable skill. For an explicit revision, read only the exact SKILL.md path listed for that skill in your system prompt (the native .agents/skills/<exact-name>/SKILL.md link is a fallback), preserve every still-valid step, re-verify what changed, then call skill_manage with action=\"update\" and skill_name set to that exact name. If you cannot read or verify the current skill, stop instead of replacing it from memory. For a genuinely new skill, use action=\"create\".\n" +
|
|
63
63
|
"3. Pass source as the exact URL or folder you used, or \"conversation\" when the conversation is the source.\n" +
|
|
64
|
-
"4. Follow the skill_manage result:
|
|
64
|
+
"4. Follow the skill_manage result: your own skill changes apply immediately and the person sees them with an Undo. After an applied result, continue the requested work without another confirmation. If review is pending, a create stays inactive and an update leaves the current version untouched; end the turn and wait for the in-app decision. Never claim success from the permission mode alone, and report failed or cancelled changes honestly.\n\n" +
|
|
65
65
|
AUTHORING_STANDARDS +
|
|
66
66
|
"\n\nWhen done, tell the user the skill name and a one-line summary of what it captured.");
|
|
67
67
|
}
|
|
@@ -29,7 +29,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
29
29
|
import { closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, symlinkSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
30
30
|
import { dirname, join, relative, resolve } from "node:path";
|
|
31
31
|
import { z } from "zod";
|
|
32
|
-
import { writeFileAtomic } from "./atomic.js";
|
|
32
|
+
import { writeFileAtomic, writeFileAtomicIfChanged } from "./atomic.js";
|
|
33
33
|
import { DATA_DIR } from "./config.js";
|
|
34
34
|
import { redactSecretsInText } from "./redact.js";
|
|
35
35
|
import { LEARN_SOURCE_PREFIX } from "./skill-learn.js";
|
|
@@ -191,9 +191,10 @@ function readManagedLinks(botId) {
|
|
|
191
191
|
return [];
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
|
+
// Rebuilt on every turn and almost always the same list.
|
|
194
195
|
function writeManagedLinks(botId, names) {
|
|
195
196
|
mkdirSync(skillStateDir(botId), { recursive: true, mode: 0o700 });
|
|
196
|
-
|
|
197
|
+
writeFileAtomicIfChanged(managedLinksPath(botId), `${JSON.stringify([...new Set(names)].sort(), null, 2)}\n`, { mode: 0o600 });
|
|
197
198
|
}
|
|
198
199
|
function manifestFromFile(path) {
|
|
199
200
|
try {
|
|
@@ -1188,6 +1189,36 @@ export function applySkillWriteWithReceipt(botId, staged, recordApplied) {
|
|
|
1188
1189
|
message: "Skill change applied. Recording its receipt or cleaning up staging could not finish; do not apply it again." };
|
|
1189
1190
|
}
|
|
1190
1191
|
}
|
|
1192
|
+
/** Undo for a learned-skill write that applied without a person: a create
|
|
1193
|
+
* is removed; an update gets the SKILL.md it replaced back, as a fresh
|
|
1194
|
+
* revision through the same stage and apply path. Only while the skill is
|
|
1195
|
+
* exactly as that write left it; otherwise `stale`, and nothing changes. */
|
|
1196
|
+
export function undoSkillWrite(botId, write) {
|
|
1197
|
+
const entry = readManifest(botId)[write.name];
|
|
1198
|
+
if (!entry || entry.appliedStageId !== write.stagedId || !installedLearnedSkillMatches(botId, write.name, entry)) {
|
|
1199
|
+
return { stale: true };
|
|
1200
|
+
}
|
|
1201
|
+
if (write.action === "create") {
|
|
1202
|
+
const removed = removeSkill(botId, write.name);
|
|
1203
|
+
return "error" in removed ? removed : { undone: true };
|
|
1204
|
+
}
|
|
1205
|
+
if (!write.previous)
|
|
1206
|
+
return { error: "This change can't be undone here." };
|
|
1207
|
+
const staged = stageSkillWrite(botId, {
|
|
1208
|
+
action: "update",
|
|
1209
|
+
targetName: write.name,
|
|
1210
|
+
files: [{ path: "SKILL.md", content: write.previous.skillMd }],
|
|
1211
|
+
source: write.previous.source,
|
|
1212
|
+
});
|
|
1213
|
+
if ("error" in staged)
|
|
1214
|
+
return staged;
|
|
1215
|
+
const applied = applyStagedSkillWrite(botId, staged.id, { expectedSha256: staged.sha256 });
|
|
1216
|
+
if ("error" in applied) {
|
|
1217
|
+
rejectStagedSkillWrite(botId, staged.id);
|
|
1218
|
+
return applied;
|
|
1219
|
+
}
|
|
1220
|
+
return { undone: true };
|
|
1221
|
+
}
|
|
1191
1222
|
/** The skills block appended to a bot's system prompt: enabled skills only,
|
|
1192
1223
|
* index lines only — the same progressive-disclosure shape the spec asks
|
|
1193
1224
|
* agents for. Bodies never ride the prompt; the bot reads the file when a
|