openmausbot 0.1.55
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/LICENSE +202 -0
- package/README.md +14 -0
- package/cli.js +2 -0
- package/dist/app-icon.svg +29 -0
- package/dist/assets/abap-BdImnpbu.js +1 -0
- package/dist/assets/actionscript-3-B3316cI-.js +1 -0
- package/dist/assets/ada-bCR0ucgS.js +1 -0
- package/dist/assets/ahk-CsyLZFj1.js +1 -0
- package/dist/assets/ahk2-8Zs4aa1G.js +1 -0
- package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/assets/angular-html-DA-rfuFy.js +1 -0
- package/dist/assets/angular-ts-BrjP3tb8.js +1 -0
- package/dist/assets/apache-Pmp26Uib.js +1 -0
- package/dist/assets/apex-DhZFqWV2.js +1 -0
- package/dist/assets/apl-CORt7UWP.js +1 -0
- package/dist/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/assets/ara-BRHolxvo.js +1 -0
- package/dist/assets/asciidoc-CSVQ5wI8.js +1 -0
- package/dist/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/assets/astro-HNnZUWAn.js +1 -0
- package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/assets/awk-DMzUqQB5.js +1 -0
- package/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/dist/assets/ayu-light-BA47KaF1.js +1 -0
- package/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/dist/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/assets/bat-CickPsom.js +1 -0
- package/dist/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/assets/berry-uYugtg8r.js +1 -0
- package/dist/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/assets/bird2-Bx8U0n9b.js +1 -0
- package/dist/assets/blade-2xfisSek.js +1 -0
- package/dist/assets/bsl-DlhNcFeZ.js +1 -0
- package/dist/assets/c-BIGW1oBm.js +1 -0
- package/dist/assets/c3-Dp5svz6Z.js +1 -0
- package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
- package/dist/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
- package/dist/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
- package/dist/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
- package/dist/assets/chapel-DTp_pixX.js +1 -0
- package/dist/assets/clarity-Dn5IMItf.js +1 -0
- package/dist/assets/clojure-P80f7IUj.js +1 -0
- package/dist/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/assets/cobol-nBiQ_Alo.js +1 -0
- package/dist/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/assets/coq-C7JzOVbR.js +1 -0
- package/dist/assets/cpp-BMRokrvK.js +1 -0
- package/dist/assets/crystal-DGywbUpC.js +1 -0
- package/dist/assets/csharp-DSvCPggb.js +1 -0
- package/dist/assets/css-CLj8gQPS.js +1 -0
- package/dist/assets/csv-fuZLfV_i.js +1 -0
- package/dist/assets/cue-D82EKSYY.js +1 -0
- package/dist/assets/cypher-COkxafJQ.js +1 -0
- package/dist/assets/d-85-TOEBH.js +1 -0
- package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/assets/dart-bE4Kk8sk.js +1 -0
- package/dist/assets/dax-CEL-wOlO.js +1 -0
- package/dist/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/assets/diff-D97Zzqfu.js +1 -0
- package/dist/assets/docker-BcOcwvcX.js +1 -0
- package/dist/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/assets/edge-FbVlp4U3.js +1 -0
- package/dist/assets/elixir-CkH2-t6x.js +1 -0
- package/dist/assets/elm-DbKCFpqz.js +1 -0
- package/dist/assets/emacs-lisp-C_m_b--Z.js +1 -0
- package/dist/assets/erb-DXfck5VN.js +1 -0
- package/dist/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/assets/fennel-BYunw83y.js +1 -0
- package/dist/assets/fish-BvzEVeQv.js +1 -0
- package/dist/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/dist/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/assets/gdresource-TyuKm33G.js +1 -0
- package/dist/assets/gdscript-DqcFQ5yU.js +1 -0
- package/dist/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/assets/genie-D0YGMca9.js +1 -0
- package/dist/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/assets/gleam-BspZqrRM.js +1 -0
- package/dist/assets/glimmer-js-ByusRIyA.js +1 -0
- package/dist/assets/glimmer-ts-BfAWNZQY.js +1 -0
- package/dist/assets/glsl-DplSGwfg.js +1 -0
- package/dist/assets/gn-n2N0HUVH.js +1 -0
- package/dist/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/assets/go-C27-OAKa.js +1 -0
- package/dist/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/assets/hack-BWmVpMyf.js +1 -0
- package/dist/assets/haml-D5jkg6IW.js +1 -0
- package/dist/assets/handlebars-BpdQsYii.js +1 -0
- package/dist/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/assets/haxe-CfZj7gIn.js +1 -0
- package/dist/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/assets/hjson-D5-asLiD.js +1 -0
- package/dist/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/assets/horizon-BUw7H-hv.js +1 -0
- package/dist/assets/horizon-bright-CUuTKBJd.js +1 -0
- package/dist/assets/houston-DnULxvSX.js +1 -0
- package/dist/assets/html-derivative-DlHx6ybY.js +1 -0
- package/dist/assets/html-pp8916En.js +1 -0
- package/dist/assets/http-jrhK8wxY.js +1 -0
- package/dist/assets/hurl-irOxFIW8.js +1 -0
- package/dist/assets/hxml-2-FPmUDs.js +1 -0
- package/dist/assets/hy-DFXneXwc.js +1 -0
- package/dist/assets/imba-DGztddWO.js +1 -0
- package/dist/assets/index-B9rLIY81.js +263 -0
- package/dist/assets/index-CJBrtDbC.css +1 -0
- package/dist/assets/index-D-u0LYsV.js +153 -0
- package/dist/assets/ini-BEwlwnbL.js +1 -0
- package/dist/assets/java-CylS5w8V.js +1 -0
- package/dist/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/assets/jinja-f2NsQr07.js +1 -0
- package/dist/assets/jison-wvAkD_A8.js +1 -0
- package/dist/assets/json-Cp-IABpG.js +1 -0
- package/dist/assets/json5-C9tS-k6U.js +1 -0
- package/dist/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/assets/julia-5Bft2YPA.js +1 -0
- package/dist/assets/just-Cwhn7H3k.js +1 -0
- package/dist/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
- package/dist/assets/kanagawa-lotus-C3DzagqV.js +1 -0
- package/dist/assets/kanagawa-wave-DFGoQZhC.js +1 -0
- package/dist/assets/kdl-DV7GczEv.js +1 -0
- package/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/assets/kusto-wEQ09or8.js +1 -0
- package/dist/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/assets/latex-D5pSuvFb.js +1 -0
- package/dist/assets/lean-BZvkOJ9d.js +1 -0
- package/dist/assets/less-B1dDrJ26.js +1 -0
- package/dist/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/assets/liquid-C0sCDyMI.js +1 -0
- package/dist/assets/llvm-BZoOZj88.js +1 -0
- package/dist/assets/log-2UxHyX5q.js +1 -0
- package/dist/assets/logo-BtOb2qkB.js +1 -0
- package/dist/assets/lua-BaeVxFsk.js +1 -0
- package/dist/assets/luau-BnpPk5vE.js +1 -0
- package/dist/assets/make-CHLpvVh8.js +1 -0
- package/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/assets/marko-DjSrsDqO.js +1 -0
- package/dist/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/assets/matlab-D7o27uSR.js +1 -0
- package/dist/assets/mdc-D1_yUvq7.js +1 -0
- package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/assets/mermaid-CQcHuHx7.js +1 -0
- package/dist/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/assets/min-light-CTRr51gU.js +1 -0
- package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/assets/mojo-DJz3ZmWd.js +1 -0
- package/dist/assets/monokai-BRVnQi9A.js +1 -0
- package/dist/assets/moonbit-CHtswR0a.js +1 -0
- package/dist/assets/move-el3G9tDJ.js +1 -0
- package/dist/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/assets/nextflow-C-mBbutL.js +1 -0
- package/dist/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- package/dist/assets/nginx-BpAMiNFr.js +1 -0
- package/dist/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/dist/assets/nim-BIad80T-.js +1 -0
- package/dist/assets/nix-CwoSXNpI.js +1 -0
- package/dist/assets/nord-Ddv68eIx.js +1 -0
- package/dist/assets/nsis-BlV79W_Q.js +1 -0
- package/dist/assets/nushell-D3jzshHO.js +1 -0
- package/dist/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/assets/odin-BBf5iR-q.js +1 -0
- package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/assets/one-light-C3Wv6jpd.js +1 -0
- package/dist/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/assets/org-DM6o9KBp.js +1 -0
- package/dist/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/assets/perl-B9cMNwum.js +1 -0
- package/dist/assets/php-Csjmro_R.js +1 -0
- package/dist/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/assets/po-BTJTHyun.js +1 -0
- package/dist/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/assets/polar-C0HS_06l.js +1 -0
- package/dist/assets/postcss-CXtECtnM.js +1 -0
- package/dist/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/assets/powershell-BmBUJMz7.js +1 -0
- package/dist/assets/prisma-Vru482bI.js +1 -0
- package/dist/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/assets/pug-DKIMFp6K.js +1 -0
- package/dist/assets/puppet-BMWR74SV.js +1 -0
- package/dist/assets/purescript-CklMAg4u.js +1 -0
- package/dist/assets/python-B6aJPvgy.js +1 -0
- package/dist/assets/qml-3beO22l8.js +1 -0
- package/dist/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/assets/qss-IeuSbFQv.js +1 -0
- package/dist/assets/r-Cf5RLm7j.js +1 -0
- package/dist/assets/racket-BqYA7rlc.js +1 -0
- package/dist/assets/raku-DXvB9xmW.js +1 -0
- package/dist/assets/razor-BjBPvh-w.js +1 -0
- package/dist/assets/rbs-CpoqiR4B.js +1 -0
- package/dist/assets/red-hvxz__6c.js +1 -0
- package/dist/assets/reg-C-SQnVFl.js +1 -0
- package/dist/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/assets/rel-C3B-1QV4.js +1 -0
- package/dist/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/assets/ron-D8l8udqQ.js +1 -0
- package/dist/assets/rose-pine-5qJOZa0Y.js +1 -0
- package/dist/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
- package/dist/assets/rose-pine-moon-C9pEdX9L.js +1 -0
- package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/assets/rst-bs7f0vWN.js +1 -0
- package/dist/assets/ruby-C0TQ7zu5.js +1 -0
- package/dist/assets/rust-B1yitclQ.js +1 -0
- package/dist/assets/sas-DEy46yEz.js +1 -0
- package/dist/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/assets/scala-CqE71os6.js +1 -0
- package/dist/assets/scheme-C98Dy4si.js +1 -0
- package/dist/assets/scss-D5BDwBP9.js +1 -0
- package/dist/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/assets/smalltalk-BOQMe2GC.js +1 -0
- package/dist/assets/smithy-cds9vsN8.js +1 -0
- package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/assets/solarized-dark-CpvCGNkr.js +1 -0
- package/dist/assets/solarized-light-Dlz6yCKv.js +1 -0
- package/dist/assets/solidity-DijEV5ha.js +1 -0
- package/dist/assets/soy-8wufbnw4.js +1 -0
- package/dist/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/assets/stata-DI20mbqo.js +1 -0
- package/dist/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/assets/surrealql-Cjom0U5J.js +1 -0
- package/dist/assets/svelte-Cy7k_4gC.js +1 -0
- package/dist/assets/swift-C2oV4EkX.js +1 -0
- package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/assets/system-verilog-0hqHdDBg.js +1 -0
- package/dist/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/assets/templ-DhtptRzy.js +1 -0
- package/dist/assets/terraform-BETggiCN.js +1 -0
- package/dist/assets/tex-D96PA37w.js +1 -0
- package/dist/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/assets/toml-vGWfd6FD.js +1 -0
- package/dist/assets/ts-tags-D351s5mN.js +1 -0
- package/dist/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/assets/tsx-COt5Ahok.js +1 -0
- package/dist/assets/turtle-BsS91CYL.js +1 -0
- package/dist/assets/twig-27uCiNez.js +1 -0
- package/dist/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/assets/typespec-CAFt9gP4.js +1 -0
- package/dist/assets/typst-BUadGCkm.js +1 -0
- package/dist/assets/v-BGw2Nkan.js +1 -0
- package/dist/assets/vala-CsfeWuGM.js +1 -0
- package/dist/assets/vb-Cu-pLBUe.js +1 -0
- package/dist/assets/verilog-nZwndyjY.js +1 -0
- package/dist/assets/vesper-DRje8inN.js +1 -0
- package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/assets/viml-CJc9bBzg.js +1 -0
- package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/assets/vue-BqiEGhQt.js +1 -0
- package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/assets/vue-vine-BoDAl6tE.js +1 -0
- package/dist/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/assets/wit-5i3qLPDT.js +1 -0
- package/dist/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/assets/yaml-Buea-lGh.js +1 -0
- package/dist/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/assets/zig-VOosw3JB.js +1 -0
- package/dist/index.html +14 -0
- package/dist-server/companion/src/listener.js +187 -0
- package/dist-server/computer-proxy.js +1185 -0
- package/dist-server/connector-proxy.js +186 -0
- package/dist-server/container-mcp.js +15186 -0
- package/dist-server/drivers/agents-proxy.js +887 -0
- package/dist-server/drivers/browser-proxy.js +15037 -0
- package/dist-server/drivers/dweb-proxy.js +175 -0
- package/dist-server/drivers/phone-proxy.js +339 -0
- package/dist-server/drivers/pi-mcp-extension.ts +638 -0
- package/dist-server/index.js +64869 -0
- package/dist-server/local-computer.js +198 -0
- package/dist-server/mcp-server.js +1233 -0
- package/dist-server/openmausbot.js +3671 -0
- package/dist-server/pair-cli.js +3671 -0
- package/dist-server/permission-proxy.js +126 -0
- package/dist-server/prepare-cloudflared.js +253 -0
- package/dist-server/proxy-paths.js +29 -0
- package/dist-server/server/atomic.js +40 -0
- package/dist-server/server/attachments.js +471 -0
- package/dist-server/server/auto-approve.js +162 -0
- package/dist-server/server/auto-review.js +79 -0
- package/dist-server/server/avatar-image.js +144 -0
- package/dist-server/server/bot-cwd.js +29 -0
- package/dist-server/server/bot-directory.js +127 -0
- package/dist-server/server/bot-package.js +268 -0
- package/dist-server/server/bot-profile.js +76 -0
- package/dist-server/server/box-create-idempotency.js +463 -0
- package/dist-server/server/box.js +944 -0
- package/dist-server/server/brand.js +65 -0
- package/dist-server/server/browser-connection.js +187 -0
- package/dist-server/server/browser-lifecycle-cleanup.js +359 -0
- package/dist-server/server/calendar-calls.js +314 -0
- package/dist-server/server/channel-queue.js +70 -0
- package/dist-server/server/checkpoints.js +405 -0
- package/dist-server/server/chief-of-staff.js +49 -0
- package/dist-server/server/claude-update.js +70 -0
- package/dist-server/server/cli.js +524 -0
- package/dist-server/server/cloud-backend.js +30 -0
- package/dist-server/server/comms-visibility.js +109 -0
- package/dist-server/server/composio.js +945 -0
- package/dist-server/server/computer-control.js +149 -0
- package/dist-server/server/computer-observation.js +133 -0
- package/dist-server/server/computer-proxy.js +1061 -0
- package/dist-server/server/config.js +760 -0
- package/dist-server/server/connector-proxy.js +228 -0
- package/dist-server/server/container-computer.js +840 -0
- package/dist-server/server/container-mcp.js +27 -0
- package/dist-server/server/contracts.js +24 -0
- package/dist-server/server/control-client.js +100 -0
- package/dist-server/server/data-dir-lease.js +3 -0
- package/dist-server/server/decision-log.js +106 -0
- package/dist-server/server/delegations.js +628 -0
- package/dist-server/server/drivers/acp/core.js +904 -0
- package/dist-server/server/drivers/acp/cursor.js +365 -0
- package/dist-server/server/drivers/acp/custom.js +51 -0
- package/dist-server/server/drivers/acp/droid.js +225 -0
- package/dist-server/server/drivers/acp/gemini.js +95 -0
- package/dist-server/server/drivers/acp/grok.js +249 -0
- package/dist-server/server/drivers/acp/hermes.js +429 -0
- package/dist-server/server/drivers/acp/kimi.js +559 -0
- package/dist-server/server/drivers/acp/opencode-go.js +370 -0
- package/dist-server/server/drivers/acp/qwen.js +98 -0
- package/dist-server/server/drivers/agents-proxy.js +941 -0
- package/dist-server/server/drivers/antigravity-acp.js +507 -0
- package/dist-server/server/drivers/antigravity-release.js +65 -0
- package/dist-server/server/drivers/antigravity-runtime.js +395 -0
- package/dist-server/server/drivers/antigravity.js +144 -0
- package/dist-server/server/drivers/boxagent.js +245 -0
- package/dist-server/server/drivers/browser-proxy.js +520 -0
- package/dist-server/server/drivers/builtIn.js +36 -0
- package/dist-server/server/drivers/claude.js +1337 -0
- package/dist-server/server/drivers/codex-catalog.js +409 -0
- package/dist-server/server/drivers/codex.js +1116 -0
- package/dist-server/server/drivers/dweb-proxy.js +193 -0
- package/dist-server/server/drivers/grok.js +48 -0
- package/dist-server/server/drivers/grokagent.js +4 -0
- package/dist-server/server/drivers/local-inject.js +388 -0
- package/dist-server/server/drivers/minimax.js +108 -0
- package/dist-server/server/drivers/native.js +21 -0
- package/dist-server/server/drivers/openai-chat.js +240 -0
- package/dist-server/server/drivers/openai-compat.js +143 -0
- package/dist-server/server/drivers/phone-proxy.js +372 -0
- package/dist-server/server/drivers/pi.js +855 -0
- package/dist-server/server/drivers/retry.js +96 -0
- package/dist-server/server/enterprise.js +93 -0
- package/dist-server/server/env-path.js +329 -0
- package/dist-server/server/environment.js +105 -0
- package/dist-server/server/generated-image.js +39 -0
- package/dist-server/server/graceful-shutdown.js +24 -0
- package/dist-server/server/group-goal-run.js +124 -0
- package/dist-server/server/harness/bus.js +86 -0
- package/dist-server/server/harness/registry.js +217 -0
- package/dist-server/server/index.js +11634 -0
- package/dist-server/server/installed-playbooks.js +34 -0
- package/dist-server/server/local-computer.js +278 -0
- package/dist-server/server/local-routing.js +9 -0
- package/dist-server/server/local-vm-idle.js +44 -0
- package/dist-server/server/local-vm-inventory.js +34 -0
- package/dist-server/server/local-vm-lease.js +58 -0
- package/dist-server/server/mcp-bridge.js +251 -0
- package/dist-server/server/mcp-probe.js +153 -0
- package/dist-server/server/mcp-registry.js +130 -0
- package/dist-server/server/member-turn.js +6 -0
- package/dist-server/server/message-db.js +358 -0
- package/dist-server/server/message-file.js +439 -0
- package/dist-server/server/names.js +26 -0
- package/dist-server/server/notify.js +69 -0
- package/dist-server/server/openmaus-status-capsule.js +329 -0
- package/dist-server/server/openmausbot.js +6 -0
- package/dist-server/server/package-export.js +157 -0
- package/dist-server/server/pair-cli.js +7 -0
- package/dist-server/server/peer-approval-key.js +5 -0
- package/dist-server/server/peer-approval.js +182 -0
- package/dist-server/server/peer-provenance.js +44 -0
- package/dist-server/server/peer-roster.js +139 -0
- package/dist-server/server/permission-proxy.js +157 -0
- package/dist-server/server/phone-secret.js +358 -0
- package/dist-server/server/private-screen-capture.js +17 -0
- package/dist-server/server/procs.js +123 -0
- package/dist-server/server/project-scout.js +304 -0
- package/dist-server/server/proxy-paths.js +47 -0
- package/dist-server/server/redact.js +102 -0
- package/dist-server/server/remote-computer.js +170 -0
- package/dist-server/server/repeat-detector.js +48 -0
- package/dist-server/server/replies.js +38 -0
- package/dist-server/server/request-auth.js +327 -0
- package/dist-server/server/room-cwd.js +15 -0
- package/dist-server/server/room-post-budget.js +112 -0
- package/dist-server/server/room-turn-timeout.js +115 -0
- package/dist-server/server/routine-requests.js +871 -0
- package/dist-server/server/routines.js +1111 -0
- package/dist-server/server/schema.js +11 -0
- package/dist-server/server/screen-frame-gate.js +80 -0
- package/dist-server/server/section-context.js +85 -0
- package/dist-server/server/send-idempotency.js +53 -0
- package/dist-server/server/sessions.js +319 -0
- package/dist-server/server/skill-fetch.js +148 -0
- package/dist-server/server/skill-learn.js +61 -0
- package/dist-server/server/skill-library.js +116 -0
- package/dist-server/server/skills.js +1223 -0
- package/dist-server/server/steer-queue.js +114 -0
- package/dist-server/server/store.js +1217 -0
- package/dist-server/server/surface.js +107 -0
- package/dist-server/server/tailscale.js +103 -0
- package/dist-server/server/team-backup.js +183 -0
- package/dist-server/server/team-library.js +205 -0
- package/dist-server/server/team-manifest.js +224 -0
- package/dist-server/server/thread-events.js +226 -0
- package/dist-server/server/tts/elevenlabs.js +92 -0
- package/dist-server/server/tts/index.js +79 -0
- package/dist-server/server/tts/speech-text.js +195 -0
- package/dist-server/server/tts/system-voices.js +74 -0
- package/dist-server/server/tunnel.js +202 -0
- package/dist-server/server/turn-context.js +40 -0
- package/dist-server/server/turn-dispatch-guard.js +152 -0
- package/dist-server/server/turn-images.js +138 -0
- package/dist-server/server/turn-watchdog.js +65 -0
- package/dist-server/server/vps-computer.js +1099 -0
- package/dist-server/server/vps-container-mcp.js +31 -0
- package/dist-server/server/webhook-ingress.js +173 -0
- package/dist-server/server/webhooks.js +491 -0
- package/dist-server/server/workspace.js +178 -0
- package/dist-server/shared/approval-mode.js +39 -0
- package/dist-server/shared/bot-avatar.js +33 -0
- package/dist-server/shared/bot-profile.js +7 -0
- package/dist-server/shared/connector-availability.js +12 -0
- package/dist-server/shared/credential-request.js +82 -0
- package/dist-server/shared/group-goal-run.js +1 -0
- package/dist-server/shared/import-name.js +11 -0
- package/dist-server/shared/mascot-bodies.js +107 -0
- package/dist-server/shared/routine-request.js +10 -0
- package/dist-server/shared/routine-run.js +1 -0
- package/dist-server/shared/skill-request.js +16 -0
- package/dist-server/shared/team-backup.js +148 -0
- package/dist-server/src/lib/composer-attachments.js +676 -0
- package/dist-server/tunnel-guardian.js +390 -0
- package/dist-server/vps-container-mcp.js +15209 -0
- package/package.json +32 -0
- package/skills/create-verification-skill/SKILL.md +65 -0
- package/skills/create-verification-skill/manifest.json +13 -0
- package/skills/phone-harness/SKILL.md +26 -0
- package/skills/phone-harness/agents/openai.yaml +13 -0
- package/skills/phone-harness/manifest.json +17 -0
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
// Generic ACP (Agent Client Protocol) driver core — one JSON-RPC-2.0-over-
|
|
2
|
+
// stdio session runtime that every ACP CLI harness (Grok Build, Gemini CLI,
|
|
3
|
+
// …) rides. Modeled on t3code's AcpSessionRuntime + per-agent AcpSupport
|
|
4
|
+
// split: the protocol mechanics live here, the per-harness quirks (spawn
|
|
5
|
+
// argv, auth method, model catalog, sign-in check) live in a small support
|
|
6
|
+
// object. Adding a harness = write server/drivers/acp/<name>.ts.
|
|
7
|
+
//
|
|
8
|
+
// ACP has no `turn/completed` notification: the `session/prompt` RPC *result*
|
|
9
|
+
// is the completion signal (it carries stopReason + usage). Permission
|
|
10
|
+
// requests arrive as server→client `session/request_permission` and surface
|
|
11
|
+
// as canonical request.opened events, answered fail-closed (nothing approved
|
|
12
|
+
// unless the agent explicitly offered an `allow`-kind option — option ORDER
|
|
13
|
+
// is never a security contract). session/load REPLAYS history as ordinary
|
|
14
|
+
// session/update notifications, so updates are double-gated: nothing emits
|
|
15
|
+
// before the prompt is sent, and `_meta.isReplay` updates are dropped.
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { lstat, mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
18
|
+
import { basename, dirname, isAbsolute, relative, resolve } from "node:path";
|
|
19
|
+
import { PROVIDER_CREDENTIAL_ENV, WORKSPACE_CREDENTIAL_ENV } from "../../config.js";
|
|
20
|
+
import { decodeInjectId } from "../local-inject.js";
|
|
21
|
+
import { describeSpawnFailure, execCli, killCliTree, spawnCli } from "../../procs.js";
|
|
22
|
+
/**
|
|
23
|
+
* A `host::model` pick talks to a loopback server with its own key.
|
|
24
|
+
* Subscription ACP login (grok.com cached_token) must not fail that turn.
|
|
25
|
+
*/
|
|
26
|
+
export function skipSubscriptionAuthForLocalInject(model) {
|
|
27
|
+
return Boolean(decodeInjectId(model));
|
|
28
|
+
}
|
|
29
|
+
import { newEventId, newId } from "../../contracts.js";
|
|
30
|
+
import { computerProxyEnv } from "../../container-computer.js";
|
|
31
|
+
import { augmentedPath } from "../../env-path.js";
|
|
32
|
+
import { supportsApprovalMode } from "../../../shared/approval-mode.js";
|
|
33
|
+
// Resolved from the server root, never relative to this file: bundling inlines
|
|
34
|
+
// this module two directories up, so the `".."` pair here would climb past the
|
|
35
|
+
// packaged server dir entirely. See server/proxy-paths.ts.
|
|
36
|
+
const COMPUTER_PROXY_PATH = SPAWNED_PROXIES.computer;
|
|
37
|
+
import { appendNative } from "../native.js";
|
|
38
|
+
import { SPAWNED_PROXIES } from "../../proxy-paths.js";
|
|
39
|
+
const envOr = (key, fallback) => Number(process.env[key] ?? fallback);
|
|
40
|
+
const INIT_TIMEOUT = envOr("OPENMAUS_ACP_INIT_TIMEOUT_MS", 300_000);
|
|
41
|
+
const SESSION_CONFIG_TIMEOUT = envOr("OPENMAUS_ACP_SESSION_CONFIG_TIMEOUT_MS", 300_000); // configureSession's per-request default
|
|
42
|
+
const NEW_SESSION_TIMEOUT = envOr("OPENMAUS_ACP_NEW_SESSION_TIMEOUT_MS", 300_000);
|
|
43
|
+
const LOAD_SESSION_TIMEOUT = envOr("OPENMAUS_ACP_LOAD_SESSION_TIMEOUT_MS", 120_000); // history replay on a long thread is slow
|
|
44
|
+
const CLIENT_FILE_MAX_BYTES = 8 * 1024 * 1024;
|
|
45
|
+
const TOOL_LOG_TEXT_LIMIT = 64_000;
|
|
46
|
+
async function readAcpImageBlocks(images) {
|
|
47
|
+
return Promise.all(images.map(async (image) => ({
|
|
48
|
+
type: "image",
|
|
49
|
+
data: (await readFile(image.path)).toString("base64"),
|
|
50
|
+
mimeType: image.mime,
|
|
51
|
+
})));
|
|
52
|
+
}
|
|
53
|
+
function sanitizeToolLogValue(value, budget, depth = 0) {
|
|
54
|
+
if (depth > 12 || budget.nodes-- <= 0)
|
|
55
|
+
return undefined;
|
|
56
|
+
if (typeof value === "string") {
|
|
57
|
+
if (/^data:image\//iu.test(value) || budget.text <= 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
const limit = Math.min(TOOL_LOG_TEXT_LIMIT, budget.text);
|
|
60
|
+
const text = value.length <= limit ? value : `[Earlier output truncated]\n\n${value.slice(-limit)}`;
|
|
61
|
+
budget.text -= text.length;
|
|
62
|
+
return text;
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(value)) {
|
|
65
|
+
return value.flatMap((entry) => {
|
|
66
|
+
const sanitized = sanitizeToolLogValue(entry, budget, depth + 1);
|
|
67
|
+
return sanitized === undefined ? [] : [sanitized];
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (!value || typeof value !== "object")
|
|
71
|
+
return value;
|
|
72
|
+
const record = value;
|
|
73
|
+
return Object.fromEntries(Object.entries(record).flatMap(([key, entry]) => {
|
|
74
|
+
if ((record.type === "image" && (key === "data" || key === "blob")) ||
|
|
75
|
+
(key === "blob" && typeof record.mimeType === "string" && record.mimeType.startsWith("image/")))
|
|
76
|
+
return [];
|
|
77
|
+
const sanitized = sanitizeToolLogValue(entry, budget, depth + 1);
|
|
78
|
+
return sanitized === undefined ? [] : [[key, sanitized]];
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
function sanitizeAcpToolMessage(message) {
|
|
82
|
+
const isToolUpdate = message?.method === "session/update"
|
|
83
|
+
&& ["tool_call", "tool_call_update"].includes(message?.params?.update?.sessionUpdate);
|
|
84
|
+
const isPermission = message?.method === "session/request_permission";
|
|
85
|
+
if (!isToolUpdate && !isPermission)
|
|
86
|
+
return message;
|
|
87
|
+
return sanitizeToolLogValue(message, { nodes: 512, text: TOOL_LOG_TEXT_LIMIT });
|
|
88
|
+
}
|
|
89
|
+
function decodeAcpConfig(defaultCli) {
|
|
90
|
+
return (raw) => {
|
|
91
|
+
const o = (raw ?? {});
|
|
92
|
+
return {
|
|
93
|
+
cli: typeof o.cli === "string" ? o.cli : defaultCli,
|
|
94
|
+
fullAuto: o.fullAuto === true,
|
|
95
|
+
workspace: typeof o.workspace === "string" ? o.workspace : undefined,
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* ACP JSON-RPC-over-stdio driver. Harness differences (argv, auth, catalog)
|
|
101
|
+
* live in `support`; this is the shared handshake and turn runtime.
|
|
102
|
+
*/
|
|
103
|
+
export function createAcpDriver(support) {
|
|
104
|
+
const DRIVER_KIND = support.driverKind;
|
|
105
|
+
const SOURCE = support.nativeSource;
|
|
106
|
+
const decodeConfig = decodeAcpConfig(support.defaultCli);
|
|
107
|
+
const DENY_TIMEOUT_NOTE = "OpenMausBot: nobody answered this permission request in time. Skip this action and finish what you can without it.";
|
|
108
|
+
return {
|
|
109
|
+
driverKind: DRIVER_KIND,
|
|
110
|
+
metadata: {
|
|
111
|
+
displayName: support.displayName,
|
|
112
|
+
supportsMultipleInstances: true,
|
|
113
|
+
access: support.access ?? "subscription",
|
|
114
|
+
},
|
|
115
|
+
install: support.install,
|
|
116
|
+
models: support.models,
|
|
117
|
+
decodeConfig,
|
|
118
|
+
defaultConfig: () => decodeConfig({}),
|
|
119
|
+
async create(input) {
|
|
120
|
+
const { instanceId, config } = input;
|
|
121
|
+
const childEnv = (activeConfig = config) => {
|
|
122
|
+
const env = {
|
|
123
|
+
...process.env,
|
|
124
|
+
...input.environment,
|
|
125
|
+
PATH: augmentedPath(),
|
|
126
|
+
};
|
|
127
|
+
const allowedCredentials = new Set(support.credentialEnv ?? []);
|
|
128
|
+
// two lists, one rule: foreign PROVIDER keys must not flip a CLI's
|
|
129
|
+
// billing off its own login, and WORKSPACE credentials (box token,
|
|
130
|
+
// voice key, …) are the harness's secrets — riding along in
|
|
131
|
+
// `...process.env` is not a grant. A driver keeps only what its
|
|
132
|
+
// credentialEnv allowlist names.
|
|
133
|
+
for (const key of [...PROVIDER_CREDENTIAL_ENV, ...WORKSPACE_CREDENTIAL_ENV]) {
|
|
134
|
+
if (!allowedCredentials.has(key))
|
|
135
|
+
delete env[key];
|
|
136
|
+
}
|
|
137
|
+
support.transformEnv?.(env, activeConfig, instanceId);
|
|
138
|
+
return env;
|
|
139
|
+
};
|
|
140
|
+
let models = support.models;
|
|
141
|
+
const refreshModels = async () => {
|
|
142
|
+
if (!support.resolveModels)
|
|
143
|
+
return;
|
|
144
|
+
try {
|
|
145
|
+
const resolved = await support.resolveModels(childEnv(), config, instanceId);
|
|
146
|
+
if (resolved.options.length)
|
|
147
|
+
models = resolved;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Keep the last usable catalog when an optional discovery source is down.
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
if (support.resolveModelsOnCreate !== false)
|
|
154
|
+
await refreshModels();
|
|
155
|
+
const listeners = new Set();
|
|
156
|
+
const active = new Map();
|
|
157
|
+
const emit = (event) => {
|
|
158
|
+
for (const listener of listeners)
|
|
159
|
+
listener(event);
|
|
160
|
+
};
|
|
161
|
+
// ACP content blocks may carry a complete raster image inline. Keep the
|
|
162
|
+
// bytes on the wire, but never duplicate megabytes of base64 into the
|
|
163
|
+
// provider-native diagnostic log in either direction.
|
|
164
|
+
const nativeLogMessage = (msg) => {
|
|
165
|
+
let redacted = msg;
|
|
166
|
+
const prompt = msg?.method === "session/prompt" ? msg?.params?.prompt : null;
|
|
167
|
+
if (Array.isArray(prompt)) {
|
|
168
|
+
redacted = {
|
|
169
|
+
...msg,
|
|
170
|
+
params: {
|
|
171
|
+
...msg.params,
|
|
172
|
+
prompt: prompt.map((content) => content?.type === "image" && typeof content.data === "string"
|
|
173
|
+
? { ...content, data: `[image data: ${content.data.length} base64 chars]` }
|
|
174
|
+
: content),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const content = redacted?.params?.update?.content;
|
|
179
|
+
if (redacted?.method !== "session/update" ||
|
|
180
|
+
redacted?.params?.update?.sessionUpdate !== "agent_message_chunk" ||
|
|
181
|
+
content?.type !== "image" ||
|
|
182
|
+
typeof content.data !== "string")
|
|
183
|
+
return support.sanitizeToolPayload ? sanitizeAcpToolMessage(redacted) : redacted;
|
|
184
|
+
redacted = {
|
|
185
|
+
...redacted,
|
|
186
|
+
params: {
|
|
187
|
+
...redacted.params,
|
|
188
|
+
update: {
|
|
189
|
+
...redacted.params.update,
|
|
190
|
+
content: { ...content, data: `[image data: ${content.data.length} base64 chars]` },
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
return support.sanitizeToolPayload ? sanitizeAcpToolMessage(redacted) : redacted;
|
|
195
|
+
};
|
|
196
|
+
const base = (threadId, turnId) => ({
|
|
197
|
+
eventId: newEventId(),
|
|
198
|
+
provider: DRIVER_KIND,
|
|
199
|
+
threadId,
|
|
200
|
+
turnId,
|
|
201
|
+
createdAt: new Date().toISOString(),
|
|
202
|
+
});
|
|
203
|
+
// ACP session mcpServers: stdio is the baseline every ACP agent
|
|
204
|
+
// supports (mcpCapabilities.http/.sse only add EXTRA transports), so
|
|
205
|
+
// an injected stdio proxy — e.g. the peer-agent comms tool — attaches
|
|
206
|
+
// fine here. env is the ACP {name,value}[] shape.
|
|
207
|
+
const acpMcpServers = (turn) => {
|
|
208
|
+
const servers = [];
|
|
209
|
+
const acpEnv = (env) => Object.entries(env).map(([name, value]) => ({ name, value: String(value) }));
|
|
210
|
+
const agents = turn.integrations?.agents;
|
|
211
|
+
if (agents) {
|
|
212
|
+
servers.push({ name: "agents", command: agents.command, args: agents.args, env: acpEnv(agents.env) });
|
|
213
|
+
}
|
|
214
|
+
const composio = turn.integrations?.composio;
|
|
215
|
+
if (composio) {
|
|
216
|
+
servers.push({
|
|
217
|
+
name: "composio",
|
|
218
|
+
command: composio.command,
|
|
219
|
+
args: composio.args,
|
|
220
|
+
env: acpEnv(composio.env),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const browser = turn.integrations?.browser;
|
|
224
|
+
if (browser) {
|
|
225
|
+
servers.push({ name: "browser", command: browser.command, args: browser.args, env: acpEnv(browser.env) });
|
|
226
|
+
}
|
|
227
|
+
// The bot's computer, mounted exactly like the Claude driver does.
|
|
228
|
+
// Cloud boxes use the REST adapter; host and sandbox Cua connections
|
|
229
|
+
// expose Cua Driver's official MCP server directly.
|
|
230
|
+
const computer = turn.integrations?.computer;
|
|
231
|
+
if (computer) {
|
|
232
|
+
servers.push({
|
|
233
|
+
name: "computer",
|
|
234
|
+
command: process.execPath,
|
|
235
|
+
args: [COMPUTER_PROXY_PATH],
|
|
236
|
+
env: acpEnv({ ELECTRON_RUN_AS_NODE: "1", ...computerProxyEnv(computer) }),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else if (turn.integrations?.localComputer) {
|
|
240
|
+
const local = turn.integrations.localComputer;
|
|
241
|
+
servers.push({
|
|
242
|
+
name: "computer",
|
|
243
|
+
command: local.command,
|
|
244
|
+
args: local.args,
|
|
245
|
+
env: acpEnv(local.env ?? {}),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// user-configured servers, after the built-ins: a residual name
|
|
249
|
+
// collision keeps the built-in (reserved names are filtered at the
|
|
250
|
+
// config boundary; this is defense in depth).
|
|
251
|
+
for (const [name, server] of Object.entries(turn.integrations?.custom ?? {})) {
|
|
252
|
+
if (servers.some((existing) => existing.name === name))
|
|
253
|
+
continue;
|
|
254
|
+
servers.push({ name, command: server.command, args: server.args, env: acpEnv(server.env) });
|
|
255
|
+
}
|
|
256
|
+
return servers;
|
|
257
|
+
};
|
|
258
|
+
const sendTurn = async (turn) => {
|
|
259
|
+
const { threadId } = turn;
|
|
260
|
+
if (active.has(threadId))
|
|
261
|
+
throw new Error("a turn is already running on this thread");
|
|
262
|
+
// Provider-instance `fullAuto` predates per-bot approval levels. Every
|
|
263
|
+
// harness turn now carries the bot's mode, so Ask/Auto must explicitly
|
|
264
|
+
// put the native agent back into its interactive mode. Otherwise a
|
|
265
|
+
// legacy Grok bypassPermissions / Cursor --force / Droid auto-high /
|
|
266
|
+
// Antigravity yolo setting would silently outrank the selector. Calls
|
|
267
|
+
// that omit approvalMode retain the old adapter-level behavior for
|
|
268
|
+
// embedders and tests outside the harness.
|
|
269
|
+
const turnConfig = turn.approvalMode === undefined
|
|
270
|
+
? config
|
|
271
|
+
: { ...config, fullAuto: turn.approvalMode === "full" && supportsApprovalMode(DRIVER_KIND, "full") };
|
|
272
|
+
const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
|
|
273
|
+
if (controlsHost && turnConfig.fullAuto && turn.approvalMode !== "full") {
|
|
274
|
+
throw new Error("local computer control requires interactive provider approvals");
|
|
275
|
+
}
|
|
276
|
+
const turnId = newId();
|
|
277
|
+
const cwd = turn.cwd ?? turnConfig.workspace ?? homedir();
|
|
278
|
+
const env = childEnv(turnConfig);
|
|
279
|
+
if (support.requireAuthenticationBeforeSpawn
|
|
280
|
+
&& !skipSubscriptionAuthForLocalInject(turn.model)
|
|
281
|
+
&& !(await support.isAuthenticated(env, turnConfig, instanceId))) {
|
|
282
|
+
emit({ ...base(threadId, turnId), type: "turn.started" });
|
|
283
|
+
emit({ ...base(threadId, turnId), type: "runtime.error", message: support.loginNote, setup: true });
|
|
284
|
+
emit({ ...base(threadId, turnId), type: "turn.completed", ok: false, stopReason: "auth_required", cost: null });
|
|
285
|
+
return { turnId };
|
|
286
|
+
}
|
|
287
|
+
const resolvedModel = support.resolveTurnModel?.(turn.model, env);
|
|
288
|
+
support.applyTurnEnv?.(env, { model: resolvedModel, requestedModel: turn.model });
|
|
289
|
+
const cliTurn = resolvedModel !== undefined && resolvedModel !== turn.model
|
|
290
|
+
? { ...turn, model: resolvedModel }
|
|
291
|
+
: turn;
|
|
292
|
+
const mcpServers = acpMcpServers(turn);
|
|
293
|
+
let launch;
|
|
294
|
+
try {
|
|
295
|
+
launch = support.resolveCommand
|
|
296
|
+
? await support.resolveCommand(env, turnConfig, instanceId)
|
|
297
|
+
: { command: turnConfig.cli };
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
emit({ ...base(threadId, turnId), type: "turn.started" });
|
|
301
|
+
emit({
|
|
302
|
+
...base(threadId, turnId),
|
|
303
|
+
type: "runtime.error",
|
|
304
|
+
message: error instanceof Error ? error.message : String(error),
|
|
305
|
+
setup: true,
|
|
306
|
+
});
|
|
307
|
+
emit({ ...base(threadId, turnId), type: "turn.completed", ok: false, stopReason: "setup_required", cost: null });
|
|
308
|
+
return { turnId };
|
|
309
|
+
}
|
|
310
|
+
const child = spawnCli(launch.command, [...(launch.args ?? []), ...support.spawnArgs(turnConfig, cliTurn)], {
|
|
311
|
+
cwd,
|
|
312
|
+
env: launch.env ?? env,
|
|
313
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
314
|
+
});
|
|
315
|
+
const state = { settled: false, promptSent: false, text: "" };
|
|
316
|
+
const asks = new Map();
|
|
317
|
+
let nextId = 1;
|
|
318
|
+
let sessionId = null;
|
|
319
|
+
let interruptTimer = null;
|
|
320
|
+
const rpcPending = new Map();
|
|
321
|
+
const send = (obj) => {
|
|
322
|
+
try {
|
|
323
|
+
child.stdin.write(JSON.stringify(obj) + "\n");
|
|
324
|
+
}
|
|
325
|
+
catch { }
|
|
326
|
+
appendNative(threadId, { dir: "out", source: SOURCE, msg: nativeLogMessage(obj) });
|
|
327
|
+
};
|
|
328
|
+
const request = (method, params, timeoutMs) => new Promise((resolve, reject) => {
|
|
329
|
+
const id = nextId++;
|
|
330
|
+
let timer = null;
|
|
331
|
+
if (timeoutMs) {
|
|
332
|
+
timer = setTimeout(() => {
|
|
333
|
+
rpcPending.delete(id);
|
|
334
|
+
reject(new Error(`${method} timed out`));
|
|
335
|
+
}, timeoutMs);
|
|
336
|
+
timer.unref?.();
|
|
337
|
+
}
|
|
338
|
+
rpcPending.set(id, { resolve, reject, timer });
|
|
339
|
+
send({ jsonrpc: "2.0", id, method, params });
|
|
340
|
+
});
|
|
341
|
+
const stop = () => killCliTree(child);
|
|
342
|
+
const resolveClientPath = async (requestPath) => {
|
|
343
|
+
if (typeof requestPath !== "string" || !isAbsolute(requestPath)) {
|
|
344
|
+
throw new Error("ACP file paths must be absolute.");
|
|
345
|
+
}
|
|
346
|
+
const workspace = await realpath(cwd).catch(() => resolve(cwd));
|
|
347
|
+
const requested = resolve(requestPath);
|
|
348
|
+
const lexical = relative(resolve(cwd), requested);
|
|
349
|
+
if (lexical === ".." || lexical.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(lexical)) {
|
|
350
|
+
throw new Error("ACP file path is outside the session workspace.");
|
|
351
|
+
}
|
|
352
|
+
const suffix = [basename(requested)];
|
|
353
|
+
let ancestor = dirname(requested);
|
|
354
|
+
while (!(await lstat(ancestor).catch(() => null))) {
|
|
355
|
+
const parent = dirname(ancestor);
|
|
356
|
+
if (parent === ancestor)
|
|
357
|
+
throw new Error("ACP file path has no accessible parent.");
|
|
358
|
+
suffix.unshift(basename(ancestor));
|
|
359
|
+
ancestor = parent;
|
|
360
|
+
}
|
|
361
|
+
const candidate = resolve(await realpath(ancestor), ...suffix);
|
|
362
|
+
const rel = relative(workspace, candidate);
|
|
363
|
+
if (rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(rel)) {
|
|
364
|
+
throw new Error("ACP file path is outside the session workspace.");
|
|
365
|
+
}
|
|
366
|
+
const existing = await lstat(candidate).catch(() => null);
|
|
367
|
+
if (existing?.isSymbolicLink())
|
|
368
|
+
throw new Error("ACP file path cannot be a symbolic link.");
|
|
369
|
+
return candidate;
|
|
370
|
+
};
|
|
371
|
+
const handleClientFileRequest = async (msg) => {
|
|
372
|
+
const fail = (error) => send({
|
|
373
|
+
jsonrpc: "2.0",
|
|
374
|
+
id: msg.id,
|
|
375
|
+
error: { code: -32602, message: error instanceof Error ? error.message : String(error) },
|
|
376
|
+
});
|
|
377
|
+
try {
|
|
378
|
+
if (!support.clientFileSystem)
|
|
379
|
+
throw new Error("Client file access is disabled.");
|
|
380
|
+
const params = msg.params ?? {};
|
|
381
|
+
const path = await resolveClientPath(params.path);
|
|
382
|
+
if (msg.method === "fs/read_text_file") {
|
|
383
|
+
const info = await stat(path);
|
|
384
|
+
if (!info.isFile() || info.size > CLIENT_FILE_MAX_BYTES) {
|
|
385
|
+
throw new Error(`ACP can only read text files under ${CLIENT_FILE_MAX_BYTES} bytes.`);
|
|
386
|
+
}
|
|
387
|
+
const content = await readFile(path, "utf8");
|
|
388
|
+
if (params.line == null && params.limit == null) {
|
|
389
|
+
send({ jsonrpc: "2.0", id: msg.id, result: { content } });
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const line = Number.isInteger(params.line) && params.line > 0 ? params.line : 1;
|
|
393
|
+
const limit = Number.isInteger(params.limit) && params.limit >= 0 ? params.limit : undefined;
|
|
394
|
+
const lines = content.split("\n");
|
|
395
|
+
const start = line - 1;
|
|
396
|
+
send({
|
|
397
|
+
jsonrpc: "2.0",
|
|
398
|
+
id: msg.id,
|
|
399
|
+
result: { content: lines.slice(start, limit === undefined ? undefined : start + limit).join("\n") },
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (typeof params.content !== "string" || Buffer.byteLength(params.content) > CLIENT_FILE_MAX_BYTES) {
|
|
404
|
+
throw new Error(`ACP can only write text files under ${CLIENT_FILE_MAX_BYTES} bytes.`);
|
|
405
|
+
}
|
|
406
|
+
await mkdir(dirname(path), { recursive: true });
|
|
407
|
+
await writeFile(path, params.content, "utf8");
|
|
408
|
+
send({ jsonrpc: "2.0", id: msg.id, result: {} });
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
fail(error);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
/** Emit buffered assistant text as its own item, then clear it. */
|
|
415
|
+
const flushAssistantText = () => {
|
|
416
|
+
const text = state.text;
|
|
417
|
+
state.text = "";
|
|
418
|
+
if (!text.trim())
|
|
419
|
+
return;
|
|
420
|
+
emit({ ...base(threadId, turnId), type: "item.completed", itemType: "assistant_text", text });
|
|
421
|
+
};
|
|
422
|
+
const settle = (ok, stopReason) => {
|
|
423
|
+
if (state.settled)
|
|
424
|
+
return;
|
|
425
|
+
state.settled = true;
|
|
426
|
+
if (interruptTimer)
|
|
427
|
+
clearTimeout(interruptTimer);
|
|
428
|
+
for (const finish of asks.values())
|
|
429
|
+
finish("cancel", "system");
|
|
430
|
+
for (const p of rpcPending.values()) {
|
|
431
|
+
if (p.timer)
|
|
432
|
+
clearTimeout(p.timer);
|
|
433
|
+
p.reject(new Error("turn settled"));
|
|
434
|
+
}
|
|
435
|
+
rpcPending.clear();
|
|
436
|
+
active.delete(threadId);
|
|
437
|
+
flushAssistantText();
|
|
438
|
+
emit({ ...base(threadId, turnId), type: "turn.completed", ok, stopReason, cost: null });
|
|
439
|
+
stop(); // the agent process does not exit on its own
|
|
440
|
+
};
|
|
441
|
+
// server→client permission request → canonical request.opened
|
|
442
|
+
const handleServerRequest = (msg) => {
|
|
443
|
+
if (msg.method === "fs/read_text_file" || msg.method === "fs/write_text_file") {
|
|
444
|
+
void handleClientFileRequest(msg);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (msg.method !== "session/request_permission") {
|
|
448
|
+
// never leave an unknown server request hanging — the agent blocks
|
|
449
|
+
return send({ jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: "method not found" } });
|
|
450
|
+
}
|
|
451
|
+
const params = msg.params ?? {};
|
|
452
|
+
flushAssistantText();
|
|
453
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
454
|
+
const optionFor = (want) => options.find((o) => o.kind === `${want}_once` && typeof o.optionId === "string")?.optionId
|
|
455
|
+
?? options.find((o) => String(o.kind ?? "").startsWith(want) && typeof o.optionId === "string")?.optionId
|
|
456
|
+
?? null;
|
|
457
|
+
const cancelled = { outcome: { outcome: "cancelled" } };
|
|
458
|
+
const missing = (want) => emit({
|
|
459
|
+
...base(threadId, turnId),
|
|
460
|
+
type: "runtime.error",
|
|
461
|
+
message: `${DRIVER_KIND} offered no "${want}" permission option — cancelling the request instead of guessing`,
|
|
462
|
+
});
|
|
463
|
+
const toolCall = params.toolCall ?? {};
|
|
464
|
+
const isQuestion = String(toolCall.toolCallId ?? "").startsWith("interaction_");
|
|
465
|
+
if (turnConfig.fullAuto && turn.approvalMode === undefined && !isQuestion) {
|
|
466
|
+
const allow = optionFor("allow");
|
|
467
|
+
if (!allow)
|
|
468
|
+
missing("allow");
|
|
469
|
+
return send({
|
|
470
|
+
jsonrpc: "2.0",
|
|
471
|
+
id: msg.id,
|
|
472
|
+
result: allow ? { outcome: { outcome: "selected", optionId: allow } } : cancelled,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
const kind = String(toolCall.kind ?? "");
|
|
476
|
+
const tool = kind === "execute" ? "shell" : kind === "edit" ? "edit" : kind || "tool";
|
|
477
|
+
const summary = String(toolCall.rawInput?.command ?? toolCall.title ?? tool).slice(0, 200);
|
|
478
|
+
const requestId = newId();
|
|
479
|
+
const finish = (behavior, source = "user", message) => {
|
|
480
|
+
if (!asks.delete(requestId))
|
|
481
|
+
return;
|
|
482
|
+
clearTimeout(timer);
|
|
483
|
+
const want = behavior === "allow" ? "allow" : "reject";
|
|
484
|
+
const named = isQuestion && behavior === "answer"
|
|
485
|
+
? options.filter((option) => option.optionId === message || option.name?.trim() === message)
|
|
486
|
+
: [];
|
|
487
|
+
const optionId = behavior === "cancel"
|
|
488
|
+
? null
|
|
489
|
+
: isQuestion
|
|
490
|
+
? named.length === 1 && typeof named[0].optionId === "string" ? named[0].optionId : null
|
|
491
|
+
: optionFor(want);
|
|
492
|
+
if (behavior !== "cancel" && !optionId)
|
|
493
|
+
missing(isQuestion ? "matching answer" : want);
|
|
494
|
+
send({
|
|
495
|
+
jsonrpc: "2.0",
|
|
496
|
+
id: msg.id,
|
|
497
|
+
result: optionId ? { outcome: { outcome: "selected", optionId } } : cancelled,
|
|
498
|
+
});
|
|
499
|
+
emit({
|
|
500
|
+
...base(threadId, turnId),
|
|
501
|
+
type: "request.resolved",
|
|
502
|
+
requestId,
|
|
503
|
+
behavior: optionId && isQuestion ? "answer" : optionId && behavior === "allow" ? "allow" : "deny",
|
|
504
|
+
source: optionId ? source : "system",
|
|
505
|
+
approvalScope: controlsHost ? "local-computer" : undefined,
|
|
506
|
+
});
|
|
507
|
+
};
|
|
508
|
+
const timer = setTimeout(() => {
|
|
509
|
+
emit({ ...base(threadId, turnId), type: "runtime.error", message: DENY_TIMEOUT_NOTE });
|
|
510
|
+
finish("deny", "timeout");
|
|
511
|
+
}, 15 * 60_000);
|
|
512
|
+
timer.unref?.();
|
|
513
|
+
asks.set(requestId, finish);
|
|
514
|
+
emit({
|
|
515
|
+
...base(threadId, turnId),
|
|
516
|
+
type: "request.opened",
|
|
517
|
+
requestId,
|
|
518
|
+
requestType: isQuestion ? "question" : "permission",
|
|
519
|
+
tool,
|
|
520
|
+
summary,
|
|
521
|
+
choices: isQuestion
|
|
522
|
+
? options.flatMap((option) => typeof option.name === "string" && option.name.trim() ? [option.name.trim()] : [])
|
|
523
|
+
: undefined,
|
|
524
|
+
approvalScope: controlsHost ? "local-computer" : undefined,
|
|
525
|
+
});
|
|
526
|
+
};
|
|
527
|
+
const handleNotification = (msg) => {
|
|
528
|
+
// Vendor side-channels (e.g. grok's `_x.ai/*`) are teed to the
|
|
529
|
+
// native log but never normalized: the prompt result is the settle.
|
|
530
|
+
if (msg.method !== "session/update")
|
|
531
|
+
return;
|
|
532
|
+
const p = msg.params ?? {};
|
|
533
|
+
if (!state.promptSent || p._meta?.isReplay === true)
|
|
534
|
+
return;
|
|
535
|
+
const u = p.update ?? {};
|
|
536
|
+
switch (u.sessionUpdate) {
|
|
537
|
+
case "agent_message_chunk": {
|
|
538
|
+
const content = u.content;
|
|
539
|
+
const delta = content?.text;
|
|
540
|
+
if (content?.type === "image" && typeof content.data === "string" && content.data) {
|
|
541
|
+
flushAssistantText();
|
|
542
|
+
emit({
|
|
543
|
+
...base(threadId, turnId),
|
|
544
|
+
type: "item.completed",
|
|
545
|
+
itemType: "assistant_image",
|
|
546
|
+
data: content.data,
|
|
547
|
+
alt: "Generated image",
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
else if (typeof delta === "string" && delta) {
|
|
551
|
+
state.text += delta;
|
|
552
|
+
emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
|
|
553
|
+
}
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
case "agent_thought_chunk": {
|
|
557
|
+
const delta = u.content?.text;
|
|
558
|
+
if (typeof delta === "string" && delta) {
|
|
559
|
+
emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta });
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
case "tool_call": {
|
|
564
|
+
flushAssistantText();
|
|
565
|
+
emit({
|
|
566
|
+
...base(threadId, turnId),
|
|
567
|
+
type: "item.started",
|
|
568
|
+
itemType: "tool",
|
|
569
|
+
itemId: u.toolCallId,
|
|
570
|
+
title: String(u.rawInput?.command ?? u.title ?? "tool").slice(0, 80),
|
|
571
|
+
});
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case "tool_call_update": {
|
|
575
|
+
if (u.status === "completed" || u.status === "failed") {
|
|
576
|
+
emit({
|
|
577
|
+
...base(threadId, turnId),
|
|
578
|
+
type: "item.completed",
|
|
579
|
+
itemType: "tool",
|
|
580
|
+
itemId: u.toolCallId,
|
|
581
|
+
ok: u.status !== "failed",
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
let buf = "";
|
|
589
|
+
// decode as UTF-8 across chunk boundaries — a raw `buf += chunk` splits
|
|
590
|
+
// multibyte characters that straddle two reads and corrupts the text
|
|
591
|
+
child.stdout.setEncoding("utf8");
|
|
592
|
+
child.stdout.on("data", (chunk) => {
|
|
593
|
+
buf += chunk;
|
|
594
|
+
let nl;
|
|
595
|
+
while ((nl = buf.indexOf("\n")) !== -1) {
|
|
596
|
+
const line = buf.slice(0, nl);
|
|
597
|
+
buf = buf.slice(nl + 1);
|
|
598
|
+
if (!line.trim())
|
|
599
|
+
continue;
|
|
600
|
+
let msg;
|
|
601
|
+
try {
|
|
602
|
+
msg = JSON.parse(line);
|
|
603
|
+
}
|
|
604
|
+
catch {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
appendNative(threadId, { dir: "in", source: SOURCE, msg: nativeLogMessage(msg) });
|
|
608
|
+
if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {
|
|
609
|
+
const pend = rpcPending.get(msg.id);
|
|
610
|
+
if (pend) {
|
|
611
|
+
rpcPending.delete(msg.id);
|
|
612
|
+
if (pend.timer)
|
|
613
|
+
clearTimeout(pend.timer);
|
|
614
|
+
if (msg.error) {
|
|
615
|
+
const error = new Error(msg.error.message ?? JSON.stringify(msg.error));
|
|
616
|
+
Object.assign(error, { code: msg.error.code, data: msg.error.data });
|
|
617
|
+
pend.reject(error);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
pend.resolve(msg.result);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
else if (msg.id !== undefined && msg.method) {
|
|
625
|
+
handleServerRequest(msg);
|
|
626
|
+
}
|
|
627
|
+
else if (msg.method) {
|
|
628
|
+
handleNotification(msg);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
let stderr = "";
|
|
633
|
+
child.stderr.on("data", (c) => {
|
|
634
|
+
if (!support.redactStderr)
|
|
635
|
+
stderr += c;
|
|
636
|
+
if (stderr.length > 8192)
|
|
637
|
+
stderr = stderr.slice(-8192);
|
|
638
|
+
});
|
|
639
|
+
child.on("error", (e) => {
|
|
640
|
+
emit({ ...base(threadId, turnId), type: "runtime.error", ...describeSpawnFailure(e, launch.command) });
|
|
641
|
+
settle(false, "spawn_error");
|
|
642
|
+
});
|
|
643
|
+
child.on("close", (code) => {
|
|
644
|
+
if (!state.settled) {
|
|
645
|
+
emit({
|
|
646
|
+
...base(threadId, turnId),
|
|
647
|
+
type: "runtime.error",
|
|
648
|
+
message: `${DRIVER_KIND} exited ${code} before the prompt result${stderr ? `: ${stderr.trim().slice(-300)}` : ""}`,
|
|
649
|
+
});
|
|
650
|
+
settle(false, "exit_before_result");
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
const interrupt = () => {
|
|
654
|
+
if (sessionId)
|
|
655
|
+
send({ jsonrpc: "2.0", method: "session/cancel", params: { sessionId } });
|
|
656
|
+
else
|
|
657
|
+
stop();
|
|
658
|
+
if (interruptTimer)
|
|
659
|
+
clearTimeout(interruptTimer);
|
|
660
|
+
interruptTimer = setTimeout(() => settle(true, "cancelled"), 5_000);
|
|
661
|
+
interruptTimer.unref?.();
|
|
662
|
+
};
|
|
663
|
+
active.set(threadId, { stop, interrupt, turnId, asks });
|
|
664
|
+
emit({ ...base(threadId, turnId), type: "turn.started" });
|
|
665
|
+
(async () => {
|
|
666
|
+
try {
|
|
667
|
+
const init = await request("initialize", {
|
|
668
|
+
protocolVersion: 1,
|
|
669
|
+
clientInfo: { name: "openmausbot", version: "0.0.0" },
|
|
670
|
+
clientCapabilities: {
|
|
671
|
+
fs: {
|
|
672
|
+
readTextFile: support.clientFileSystem === true,
|
|
673
|
+
writeTextFile: support.clientFileSystem === true,
|
|
674
|
+
},
|
|
675
|
+
terminal: false,
|
|
676
|
+
},
|
|
677
|
+
}, INIT_TIMEOUT);
|
|
678
|
+
const methods = Array.isArray(init?.authMethods) ? init.authMethods : [];
|
|
679
|
+
const methodId = support.pickAuthMethod(methods);
|
|
680
|
+
if (!skipSubscriptionAuthForLocalInject(turn.model)) {
|
|
681
|
+
if (methodId) {
|
|
682
|
+
try {
|
|
683
|
+
await request("authenticate", { methodId }, INIT_TIMEOUT);
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
if (support.authFailure === "fail")
|
|
687
|
+
throw new Error(support.loginNote);
|
|
688
|
+
// else: proceed on an ambient login
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
else if (support.authFailure === "fail") {
|
|
692
|
+
throw new Error(support.loginNote);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
const images = turn.images ?? [];
|
|
696
|
+
const runtimeAcceptsImages = init?.agentCapabilities?.promptCapabilities?.image === true;
|
|
697
|
+
if (images.length && support.images === true && !runtimeAcceptsImages) {
|
|
698
|
+
throw new Error(`${support.displayName} is configured for image attachments, but this installed runtime does not advertise ACP image input. Update the ${support.displayName} CLI or send the message without an image.`);
|
|
699
|
+
}
|
|
700
|
+
const cursor = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
|
|
701
|
+
let sessionResult = null;
|
|
702
|
+
if (cursor) {
|
|
703
|
+
try {
|
|
704
|
+
sessionResult = await request(support.resumeMethod === "resume" ? "session/resume" : "session/load", { sessionId: cursor, cwd, mcpServers }, LOAD_SESSION_TIMEOUT);
|
|
705
|
+
if (sessionResult)
|
|
706
|
+
sessionId = cursor;
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
/* session gone, load unsupported, or too slow — start fresh */
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (!sessionId) {
|
|
713
|
+
sessionResult = await request("session/new", { cwd, mcpServers }, NEW_SESSION_TIMEOUT);
|
|
714
|
+
sessionId = typeof sessionResult?.sessionId === "string" ? sessionResult.sessionId : null;
|
|
715
|
+
if (!sessionId)
|
|
716
|
+
throw new Error("session/new returned no sessionId");
|
|
717
|
+
}
|
|
718
|
+
let selectedModel = null;
|
|
719
|
+
let sessionStarted = false;
|
|
720
|
+
const emitSessionStarted = () => {
|
|
721
|
+
if (sessionStarted)
|
|
722
|
+
return;
|
|
723
|
+
sessionStarted = true;
|
|
724
|
+
emit({
|
|
725
|
+
...base(threadId, turnId),
|
|
726
|
+
type: "session.started",
|
|
727
|
+
sessionId,
|
|
728
|
+
model: selectedModel ?? init?._meta?.modelState?.currentModelId ?? cliTurn.model ?? null,
|
|
729
|
+
});
|
|
730
|
+
};
|
|
731
|
+
try {
|
|
732
|
+
if (support.selectModel) {
|
|
733
|
+
const { configId } = support.selectModel;
|
|
734
|
+
const currentOf = (r) => (Array.isArray(r?.configOptions) ? r.configOptions : []).find((o) => o?.id === configId)
|
|
735
|
+
?.currentValue ?? null;
|
|
736
|
+
selectedModel = currentOf(sessionResult);
|
|
737
|
+
if (cliTurn.model && cliTurn.model !== selectedModel) {
|
|
738
|
+
selectedModel = currentOf(await request("session/set_config_option", { sessionId, configId, value: cliTurn.model }, INIT_TIMEOUT));
|
|
739
|
+
// an agent that answers OK but keeps its old model is worse than
|
|
740
|
+
// one that errors: it burns a paid turn on the wrong thing
|
|
741
|
+
if (selectedModel !== cliTurn.model) {
|
|
742
|
+
throw new Error(`${DRIVER_KIND} did not switch to ${cliTurn.model} (still ${selectedModel ?? "unknown"})`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (support.configureSession) {
|
|
747
|
+
await support.configureSession({
|
|
748
|
+
request: (method, params, timeoutMs) => request(method, params, timeoutMs ?? SESSION_CONFIG_TIMEOUT),
|
|
749
|
+
sessionId,
|
|
750
|
+
config: turnConfig,
|
|
751
|
+
turn: cliTurn,
|
|
752
|
+
sessionModels: Array.isArray(sessionResult?.models?.availableModels)
|
|
753
|
+
? sessionResult.models.availableModels
|
|
754
|
+
: [],
|
|
755
|
+
});
|
|
756
|
+
// initialize's currentModelId is the CLI default (grok-4.6),
|
|
757
|
+
// not the model this turn asked for. After a successful pin,
|
|
758
|
+
// report the slug we set so the UI does not claim otherwise.
|
|
759
|
+
if (!selectedModel && cliTurn.model)
|
|
760
|
+
selectedModel = cliTurn.model;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
// session.started is the only place the resume cursor is recorded,
|
|
765
|
+
// so a rejected setting must not orphan a session we just created.
|
|
766
|
+
emitSessionStarted();
|
|
767
|
+
throw error;
|
|
768
|
+
}
|
|
769
|
+
emitSessionStarted();
|
|
770
|
+
state.promptSent = true;
|
|
771
|
+
const text = support.buildPromptText
|
|
772
|
+
? support.buildPromptText(turn)
|
|
773
|
+
: turn.system
|
|
774
|
+
? `${turn.system}\n\n${turn.text}`
|
|
775
|
+
: turn.text;
|
|
776
|
+
const imageBlocks = support.images === true && runtimeAcceptsImages
|
|
777
|
+
? await readAcpImageBlocks(images)
|
|
778
|
+
: [];
|
|
779
|
+
const result = await request("session/prompt", {
|
|
780
|
+
sessionId,
|
|
781
|
+
prompt: [{ type: "text", text }, ...imageBlocks],
|
|
782
|
+
});
|
|
783
|
+
// opencode 1.18.18 reports usage at the result root; grok and
|
|
784
|
+
// gemini put it under _meta. Read both rather than lose the count.
|
|
785
|
+
const usage = result?.usage ?? result?._meta ?? {};
|
|
786
|
+
if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") {
|
|
787
|
+
emit({
|
|
788
|
+
...base(threadId, turnId),
|
|
789
|
+
type: "thread.token-usage.updated",
|
|
790
|
+
input: usage.inputTokens ?? 0,
|
|
791
|
+
output: usage.outputTokens ?? 0,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
const reason = result?.stopReason;
|
|
795
|
+
if (reason === "end_turn")
|
|
796
|
+
settle(true, null);
|
|
797
|
+
else if (reason === "cancelled")
|
|
798
|
+
settle(true, "cancelled");
|
|
799
|
+
else {
|
|
800
|
+
const errorMessage = typeof result?.error === "string" && result.error
|
|
801
|
+
? result.error
|
|
802
|
+
: typeof result?.message === "string" && result.message
|
|
803
|
+
? result.message
|
|
804
|
+
: `Model turn failed: ${reason ?? "unknown error"}`;
|
|
805
|
+
emit({
|
|
806
|
+
...base(threadId, turnId),
|
|
807
|
+
type: "runtime.error",
|
|
808
|
+
message: errorMessage,
|
|
809
|
+
});
|
|
810
|
+
settle(false, reason ?? "failed");
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
catch (e) {
|
|
814
|
+
if (!state.settled) {
|
|
815
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
816
|
+
const code = support.classifyError?.(e);
|
|
817
|
+
// Authentication setup is a user action, not a retry. The
|
|
818
|
+
// classifier is preferred; loginNote remains a compatibility
|
|
819
|
+
// fallback for existing ACP supports.
|
|
820
|
+
const needsAuth = code === "invalid_credentials" || code === "inactive_subscription"
|
|
821
|
+
|| message === support.loginNote;
|
|
822
|
+
emit({
|
|
823
|
+
...base(threadId, turnId),
|
|
824
|
+
type: "runtime.error",
|
|
825
|
+
message,
|
|
826
|
+
...(needsAuth ? { setup: true } : {}),
|
|
827
|
+
});
|
|
828
|
+
settle(false, needsAuth ? "auth_required" : "rpc_error");
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
})();
|
|
832
|
+
return { turnId };
|
|
833
|
+
};
|
|
834
|
+
const snapshot = async () => {
|
|
835
|
+
const env = childEnv();
|
|
836
|
+
if (support.snapshot)
|
|
837
|
+
return support.snapshot(env, config, instanceId);
|
|
838
|
+
const version = await new Promise((resolve) => {
|
|
839
|
+
execCli(config.cli, ["--version"], { timeout: 8000, env }, (err, stdout) => resolve(err ? null : stdout.trim()));
|
|
840
|
+
});
|
|
841
|
+
if (!version)
|
|
842
|
+
return { state: "unavailable", reason: `\`${config.cli}\` CLI not found` };
|
|
843
|
+
return { state: "available", version, authenticated: await support.isAuthenticated(env, config, instanceId) };
|
|
844
|
+
};
|
|
845
|
+
return {
|
|
846
|
+
instanceId,
|
|
847
|
+
driverKind: DRIVER_KIND,
|
|
848
|
+
displayName: input.displayName,
|
|
849
|
+
enabled: input.enabled,
|
|
850
|
+
get models() {
|
|
851
|
+
return models;
|
|
852
|
+
},
|
|
853
|
+
refreshModels: support.resolveModels ? refreshModels : undefined,
|
|
854
|
+
snapshot,
|
|
855
|
+
adapter: {
|
|
856
|
+
provider: DRIVER_KIND,
|
|
857
|
+
capabilities: {
|
|
858
|
+
sessionModelSwitch: "unsupported",
|
|
859
|
+
agentsMcp: true,
|
|
860
|
+
customMcp: true,
|
|
861
|
+
computerMcp: true,
|
|
862
|
+
composioMcp: true,
|
|
863
|
+
browserMcp: true,
|
|
864
|
+
images: support.images !== false,
|
|
865
|
+
nativeImageInput: support.images === true,
|
|
866
|
+
effortLevels: support.effortLevels,
|
|
867
|
+
// OpenMausBot supplies a per-bot approvalMode on every harness
|
|
868
|
+
// turn, which safely overrides a legacy instance fullAuto value.
|
|
869
|
+
// Direct adapter calls that omit it still fail closed in sendTurn.
|
|
870
|
+
localComputerMcp: true,
|
|
871
|
+
},
|
|
872
|
+
sendTurn,
|
|
873
|
+
interruptTurn: async (threadId) => active.get(threadId)?.interrupt(),
|
|
874
|
+
respondToRequest: async (threadId, requestId, decision) => {
|
|
875
|
+
const turn = active.get(threadId);
|
|
876
|
+
const finish = turn?.asks.get(requestId);
|
|
877
|
+
if (!finish)
|
|
878
|
+
return "unavailable"; // settled, timed out, or turn gone
|
|
879
|
+
finish(decision.behavior, "user", decision.message);
|
|
880
|
+
return decision.behavior === "allow"
|
|
881
|
+
? "allowed-once"
|
|
882
|
+
: decision.behavior === "answer"
|
|
883
|
+
? "answered"
|
|
884
|
+
: "rejected";
|
|
885
|
+
},
|
|
886
|
+
hasSession: (threadId) => active.has(threadId),
|
|
887
|
+
stopAll: async () => {
|
|
888
|
+
for (const { stop } of active.values())
|
|
889
|
+
stop();
|
|
890
|
+
},
|
|
891
|
+
onEvent: (listener) => {
|
|
892
|
+
listeners.add(listener);
|
|
893
|
+
return () => listeners.delete(listener);
|
|
894
|
+
},
|
|
895
|
+
},
|
|
896
|
+
dispose: async () => {
|
|
897
|
+
for (const { stop } of active.values())
|
|
898
|
+
stop();
|
|
899
|
+
listeners.clear();
|
|
900
|
+
},
|
|
901
|
+
};
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
}
|