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.
Files changed (499) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +14 -0
  3. package/cli.js +2 -0
  4. package/dist/app-icon.svg +29 -0
  5. package/dist/assets/abap-BdImnpbu.js +1 -0
  6. package/dist/assets/actionscript-3-B3316cI-.js +1 -0
  7. package/dist/assets/ada-bCR0ucgS.js +1 -0
  8. package/dist/assets/ahk-CsyLZFj1.js +1 -0
  9. package/dist/assets/ahk2-8Zs4aa1G.js +1 -0
  10. package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
  11. package/dist/assets/angular-html-DA-rfuFy.js +1 -0
  12. package/dist/assets/angular-ts-BrjP3tb8.js +1 -0
  13. package/dist/assets/apache-Pmp26Uib.js +1 -0
  14. package/dist/assets/apex-DhZFqWV2.js +1 -0
  15. package/dist/assets/apl-CORt7UWP.js +1 -0
  16. package/dist/assets/applescript-Co6uUVPk.js +1 -0
  17. package/dist/assets/ara-BRHolxvo.js +1 -0
  18. package/dist/assets/asciidoc-CSVQ5wI8.js +1 -0
  19. package/dist/assets/asm-D_Q5rh1f.js +1 -0
  20. package/dist/assets/astro-HNnZUWAn.js +1 -0
  21. package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  22. package/dist/assets/awk-DMzUqQB5.js +1 -0
  23. package/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
  24. package/dist/assets/ayu-light-BA47KaF1.js +1 -0
  25. package/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
  26. package/dist/assets/ballerina-BFfxhgS-.js +1 -0
  27. package/dist/assets/bat-CickPsom.js +1 -0
  28. package/dist/assets/beancount-k_qm7-4y.js +1 -0
  29. package/dist/assets/berry-uYugtg8r.js +1 -0
  30. package/dist/assets/bibtex-CHM0blh-.js +1 -0
  31. package/dist/assets/bicep-Bmn6On1c.js +1 -0
  32. package/dist/assets/bird2-Bx8U0n9b.js +1 -0
  33. package/dist/assets/blade-2xfisSek.js +1 -0
  34. package/dist/assets/bsl-DlhNcFeZ.js +1 -0
  35. package/dist/assets/c-BIGW1oBm.js +1 -0
  36. package/dist/assets/c3-Dp5svz6Z.js +1 -0
  37. package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  38. package/dist/assets/cairo-KRGpt6FW.js +1 -0
  39. package/dist/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
  40. package/dist/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
  41. package/dist/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
  42. package/dist/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
  43. package/dist/assets/chapel-DTp_pixX.js +1 -0
  44. package/dist/assets/clarity-Dn5IMItf.js +1 -0
  45. package/dist/assets/clojure-P80f7IUj.js +1 -0
  46. package/dist/assets/cmake-D1j8_8rp.js +1 -0
  47. package/dist/assets/cobol-nBiQ_Alo.js +1 -0
  48. package/dist/assets/codeowners-Bp6g37R7.js +1 -0
  49. package/dist/assets/codeql-DsOJ9woJ.js +1 -0
  50. package/dist/assets/coffee-Ch7k5sss.js +1 -0
  51. package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  52. package/dist/assets/coq-C7JzOVbR.js +1 -0
  53. package/dist/assets/cpp-BMRokrvK.js +1 -0
  54. package/dist/assets/crystal-DGywbUpC.js +1 -0
  55. package/dist/assets/csharp-DSvCPggb.js +1 -0
  56. package/dist/assets/css-CLj8gQPS.js +1 -0
  57. package/dist/assets/csv-fuZLfV_i.js +1 -0
  58. package/dist/assets/cue-D82EKSYY.js +1 -0
  59. package/dist/assets/cypher-COkxafJQ.js +1 -0
  60. package/dist/assets/d-85-TOEBH.js +1 -0
  61. package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  62. package/dist/assets/dart-bE4Kk8sk.js +1 -0
  63. package/dist/assets/dax-CEL-wOlO.js +1 -0
  64. package/dist/assets/desktop-BmXAJ9_W.js +1 -0
  65. package/dist/assets/diff-D97Zzqfu.js +1 -0
  66. package/dist/assets/docker-BcOcwvcX.js +1 -0
  67. package/dist/assets/dotenv-Da5cRb03.js +1 -0
  68. package/dist/assets/dracula-BzJJZx-M.js +1 -0
  69. package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  70. package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  71. package/dist/assets/edge-FbVlp4U3.js +1 -0
  72. package/dist/assets/elixir-CkH2-t6x.js +1 -0
  73. package/dist/assets/elm-DbKCFpqz.js +1 -0
  74. package/dist/assets/emacs-lisp-C_m_b--Z.js +1 -0
  75. package/dist/assets/erb-DXfck5VN.js +1 -0
  76. package/dist/assets/erlang-DsQrWhSR.js +1 -0
  77. package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  78. package/dist/assets/everforest-light-C8M2exoo.js +1 -0
  79. package/dist/assets/fennel-BYunw83y.js +1 -0
  80. package/dist/assets/fish-BvzEVeQv.js +1 -0
  81. package/dist/assets/fluent-C4IJs8-o.js +1 -0
  82. package/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  83. package/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
  84. package/dist/assets/fsharp-CXgrBDvD.js +1 -0
  85. package/dist/assets/gdresource-TyuKm33G.js +1 -0
  86. package/dist/assets/gdscript-DqcFQ5yU.js +1 -0
  87. package/dist/assets/gdshader-DkwncUOv.js +1 -0
  88. package/dist/assets/genie-D0YGMca9.js +1 -0
  89. package/dist/assets/gherkin-DyxjwDmM.js +1 -0
  90. package/dist/assets/git-commit-F4YmCXRG.js +1 -0
  91. package/dist/assets/git-rebase-r7XF79zn.js +1 -0
  92. package/dist/assets/github-dark-DHJKELXO.js +1 -0
  93. package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  94. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  95. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  96. package/dist/assets/github-light-DAi9KRSo.js +1 -0
  97. package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  98. package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  99. package/dist/assets/gleam-BspZqrRM.js +1 -0
  100. package/dist/assets/glimmer-js-ByusRIyA.js +1 -0
  101. package/dist/assets/glimmer-ts-BfAWNZQY.js +1 -0
  102. package/dist/assets/glsl-DplSGwfg.js +1 -0
  103. package/dist/assets/gn-n2N0HUVH.js +1 -0
  104. package/dist/assets/gnuplot-DdkO51Og.js +1 -0
  105. package/dist/assets/go-C27-OAKa.js +1 -0
  106. package/dist/assets/graphql-ChdNCCLP.js +1 -0
  107. package/dist/assets/groovy-gcz8RCvz.js +1 -0
  108. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  109. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  110. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  111. package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  112. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  113. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  114. package/dist/assets/hack-BWmVpMyf.js +1 -0
  115. package/dist/assets/haml-D5jkg6IW.js +1 -0
  116. package/dist/assets/handlebars-BpdQsYii.js +1 -0
  117. package/dist/assets/haskell-Df6bDoY_.js +1 -0
  118. package/dist/assets/haxe-CfZj7gIn.js +1 -0
  119. package/dist/assets/hcl-BWvSN4gD.js +1 -0
  120. package/dist/assets/hjson-D5-asLiD.js +1 -0
  121. package/dist/assets/hlsl-D3lLCCz7.js +1 -0
  122. package/dist/assets/horizon-BUw7H-hv.js +1 -0
  123. package/dist/assets/horizon-bright-CUuTKBJd.js +1 -0
  124. package/dist/assets/houston-DnULxvSX.js +1 -0
  125. package/dist/assets/html-derivative-DlHx6ybY.js +1 -0
  126. package/dist/assets/html-pp8916En.js +1 -0
  127. package/dist/assets/http-jrhK8wxY.js +1 -0
  128. package/dist/assets/hurl-irOxFIW8.js +1 -0
  129. package/dist/assets/hxml-2-FPmUDs.js +1 -0
  130. package/dist/assets/hy-DFXneXwc.js +1 -0
  131. package/dist/assets/imba-DGztddWO.js +1 -0
  132. package/dist/assets/index-B9rLIY81.js +263 -0
  133. package/dist/assets/index-CJBrtDbC.css +1 -0
  134. package/dist/assets/index-D-u0LYsV.js +153 -0
  135. package/dist/assets/ini-BEwlwnbL.js +1 -0
  136. package/dist/assets/java-CylS5w8V.js +1 -0
  137. package/dist/assets/javascript-wDzz0qaB.js +1 -0
  138. package/dist/assets/jinja-f2NsQr07.js +1 -0
  139. package/dist/assets/jison-wvAkD_A8.js +1 -0
  140. package/dist/assets/json-Cp-IABpG.js +1 -0
  141. package/dist/assets/json5-C9tS-k6U.js +1 -0
  142. package/dist/assets/jsonc-Des-eS-w.js +1 -0
  143. package/dist/assets/jsonl-DcaNXYhu.js +1 -0
  144. package/dist/assets/jsonnet-DFQXde-d.js +1 -0
  145. package/dist/assets/jssm-C2t-YnRu.js +1 -0
  146. package/dist/assets/jsx-g9-lgVsj.js +1 -0
  147. package/dist/assets/julia-5Bft2YPA.js +1 -0
  148. package/dist/assets/just-Cwhn7H3k.js +1 -0
  149. package/dist/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
  150. package/dist/assets/kanagawa-lotus-C3DzagqV.js +1 -0
  151. package/dist/assets/kanagawa-wave-DFGoQZhC.js +1 -0
  152. package/dist/assets/kdl-DV7GczEv.js +1 -0
  153. package/dist/assets/kotlin-BdnUsdx6.js +1 -0
  154. package/dist/assets/kusto-wEQ09or8.js +1 -0
  155. package/dist/assets/laserwave-DUszq2jm.js +1 -0
  156. package/dist/assets/latex-D5pSuvFb.js +1 -0
  157. package/dist/assets/lean-BZvkOJ9d.js +1 -0
  158. package/dist/assets/less-B1dDrJ26.js +1 -0
  159. package/dist/assets/light-plus-B7mTdjB0.js +1 -0
  160. package/dist/assets/liquid-C0sCDyMI.js +1 -0
  161. package/dist/assets/llvm-BZoOZj88.js +1 -0
  162. package/dist/assets/log-2UxHyX5q.js +1 -0
  163. package/dist/assets/logo-BtOb2qkB.js +1 -0
  164. package/dist/assets/lua-BaeVxFsk.js +1 -0
  165. package/dist/assets/luau-BnpPk5vE.js +1 -0
  166. package/dist/assets/make-CHLpvVh8.js +1 -0
  167. package/dist/assets/markdown-Cvjx9yec.js +1 -0
  168. package/dist/assets/marko-DjSrsDqO.js +1 -0
  169. package/dist/assets/material-theme-D5KoaKCx.js +1 -0
  170. package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  171. package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  172. package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  173. package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  174. package/dist/assets/matlab-D7o27uSR.js +1 -0
  175. package/dist/assets/mdc-D1_yUvq7.js +1 -0
  176. package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  177. package/dist/assets/mermaid-CQcHuHx7.js +1 -0
  178. package/dist/assets/min-dark-CafNBF8u.js +1 -0
  179. package/dist/assets/min-light-CTRr51gU.js +1 -0
  180. package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  181. package/dist/assets/mojo-DJz3ZmWd.js +1 -0
  182. package/dist/assets/monokai-BRVnQi9A.js +1 -0
  183. package/dist/assets/moonbit-CHtswR0a.js +1 -0
  184. package/dist/assets/move-el3G9tDJ.js +1 -0
  185. package/dist/assets/narrat-DRg8JJMk.js +1 -0
  186. package/dist/assets/nextflow-C-mBbutL.js +1 -0
  187. package/dist/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  188. package/dist/assets/nginx-BpAMiNFr.js +1 -0
  189. package/dist/assets/night-owl-C39BiMTA.js +1 -0
  190. package/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
  191. package/dist/assets/nim-BIad80T-.js +1 -0
  192. package/dist/assets/nix-CwoSXNpI.js +1 -0
  193. package/dist/assets/nord-Ddv68eIx.js +1 -0
  194. package/dist/assets/nsis-BlV79W_Q.js +1 -0
  195. package/dist/assets/nushell-D3jzshHO.js +1 -0
  196. package/dist/assets/objective-c-DXmwc3jG.js +1 -0
  197. package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  198. package/dist/assets/ocaml-C0hk2d4L.js +1 -0
  199. package/dist/assets/odin-BBf5iR-q.js +1 -0
  200. package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  201. package/dist/assets/one-light-C3Wv6jpd.js +1 -0
  202. package/dist/assets/openscad-C4EeE6gA.js +1 -0
  203. package/dist/assets/org-DM6o9KBp.js +1 -0
  204. package/dist/assets/pascal-D93ZcfNL.js +1 -0
  205. package/dist/assets/perl-B9cMNwum.js +1 -0
  206. package/dist/assets/php-Csjmro_R.js +1 -0
  207. package/dist/assets/pkl-u5AG7uiY.js +1 -0
  208. package/dist/assets/plastic-3e1v2bzS.js +1 -0
  209. package/dist/assets/plsql-ChMvpjG-.js +1 -0
  210. package/dist/assets/po-BTJTHyun.js +1 -0
  211. package/dist/assets/poimandres-CS3Unz2-.js +1 -0
  212. package/dist/assets/polar-C0HS_06l.js +1 -0
  213. package/dist/assets/postcss-CXtECtnM.js +1 -0
  214. package/dist/assets/powerquery-CEu0bR-o.js +1 -0
  215. package/dist/assets/powershell-BmBUJMz7.js +1 -0
  216. package/dist/assets/prisma-Vru482bI.js +1 -0
  217. package/dist/assets/prolog-CbFg5uaA.js +1 -0
  218. package/dist/assets/proto-C7zT0LnQ.js +1 -0
  219. package/dist/assets/pug-DKIMFp6K.js +1 -0
  220. package/dist/assets/puppet-BMWR74SV.js +1 -0
  221. package/dist/assets/purescript-CklMAg4u.js +1 -0
  222. package/dist/assets/python-B6aJPvgy.js +1 -0
  223. package/dist/assets/qml-3beO22l8.js +1 -0
  224. package/dist/assets/qmldir-C8lEn-DE.js +1 -0
  225. package/dist/assets/qss-IeuSbFQv.js +1 -0
  226. package/dist/assets/r-Cf5RLm7j.js +1 -0
  227. package/dist/assets/racket-BqYA7rlc.js +1 -0
  228. package/dist/assets/raku-DXvB9xmW.js +1 -0
  229. package/dist/assets/razor-BjBPvh-w.js +1 -0
  230. package/dist/assets/rbs-CpoqiR4B.js +1 -0
  231. package/dist/assets/red-hvxz__6c.js +1 -0
  232. package/dist/assets/reg-C-SQnVFl.js +1 -0
  233. package/dist/assets/regexp-CDVJQ6XC.js +1 -0
  234. package/dist/assets/rel-C3B-1QV4.js +1 -0
  235. package/dist/assets/riscv-BM1_JUlF.js +1 -0
  236. package/dist/assets/ron-D8l8udqQ.js +1 -0
  237. package/dist/assets/rose-pine-5qJOZa0Y.js +1 -0
  238. package/dist/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
  239. package/dist/assets/rose-pine-moon-C9pEdX9L.js +1 -0
  240. package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  241. package/dist/assets/rst-bs7f0vWN.js +1 -0
  242. package/dist/assets/ruby-C0TQ7zu5.js +1 -0
  243. package/dist/assets/rust-B1yitclQ.js +1 -0
  244. package/dist/assets/sas-DEy46yEz.js +1 -0
  245. package/dist/assets/sass-Cj5Yp3dK.js +1 -0
  246. package/dist/assets/scala-CqE71os6.js +1 -0
  247. package/dist/assets/scheme-C98Dy4si.js +1 -0
  248. package/dist/assets/scss-D5BDwBP9.js +1 -0
  249. package/dist/assets/sdbl-DVxCFoDh.js +1 -0
  250. package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  251. package/dist/assets/shellscript-Yzrsuije.js +1 -0
  252. package/dist/assets/shellsession-BADoaaVG.js +1 -0
  253. package/dist/assets/slack-dark-BthQWCQV.js +1 -0
  254. package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  255. package/dist/assets/smalltalk-BOQMe2GC.js +1 -0
  256. package/dist/assets/smithy-cds9vsN8.js +1 -0
  257. package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  258. package/dist/assets/solarized-dark-CpvCGNkr.js +1 -0
  259. package/dist/assets/solarized-light-Dlz6yCKv.js +1 -0
  260. package/dist/assets/solidity-DijEV5ha.js +1 -0
  261. package/dist/assets/soy-8wufbnw4.js +1 -0
  262. package/dist/assets/sparql-rVzFXLq3.js +1 -0
  263. package/dist/assets/splunk-BtCnVYZw.js +1 -0
  264. package/dist/assets/sql-CRqJ_cUM.js +1 -0
  265. package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  266. package/dist/assets/stata-DI20mbqo.js +1 -0
  267. package/dist/assets/stylus-BEDo0Tqx.js +1 -0
  268. package/dist/assets/surrealql-Cjom0U5J.js +1 -0
  269. package/dist/assets/svelte-Cy7k_4gC.js +1 -0
  270. package/dist/assets/swift-C2oV4EkX.js +1 -0
  271. package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  272. package/dist/assets/system-verilog-0hqHdDBg.js +1 -0
  273. package/dist/assets/systemd-4A_iFExJ.js +1 -0
  274. package/dist/assets/talonscript-CkByrt1z.js +1 -0
  275. package/dist/assets/tasl-QIJgUcNo.js +1 -0
  276. package/dist/assets/tcl-dwOrl1Do.js +1 -0
  277. package/dist/assets/templ-DhtptRzy.js +1 -0
  278. package/dist/assets/terraform-BETggiCN.js +1 -0
  279. package/dist/assets/tex-D96PA37w.js +1 -0
  280. package/dist/assets/tokyo-night-hegEt444.js +1 -0
  281. package/dist/assets/toml-vGWfd6FD.js +1 -0
  282. package/dist/assets/ts-tags-D351s5mN.js +1 -0
  283. package/dist/assets/tsv-B_m7g4N7.js +1 -0
  284. package/dist/assets/tsx-COt5Ahok.js +1 -0
  285. package/dist/assets/turtle-BsS91CYL.js +1 -0
  286. package/dist/assets/twig-27uCiNez.js +1 -0
  287. package/dist/assets/typescript-BPQ3VLAy.js +1 -0
  288. package/dist/assets/typespec-CAFt9gP4.js +1 -0
  289. package/dist/assets/typst-BUadGCkm.js +1 -0
  290. package/dist/assets/v-BGw2Nkan.js +1 -0
  291. package/dist/assets/vala-CsfeWuGM.js +1 -0
  292. package/dist/assets/vb-Cu-pLBUe.js +1 -0
  293. package/dist/assets/verilog-nZwndyjY.js +1 -0
  294. package/dist/assets/vesper-DRje8inN.js +1 -0
  295. package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  296. package/dist/assets/viml-CJc9bBzg.js +1 -0
  297. package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  298. package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  299. package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  300. package/dist/assets/vue-BqiEGhQt.js +1 -0
  301. package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
  302. package/dist/assets/vue-vine-BoDAl6tE.js +1 -0
  303. package/dist/assets/vyper-CDx5xZoG.js +1 -0
  304. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  305. package/dist/assets/wasm-MzD3tlZU.js +1 -0
  306. package/dist/assets/wenyan-BV7otONQ.js +1 -0
  307. package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  308. package/dist/assets/wikitext-BhOHFoWU.js +1 -0
  309. package/dist/assets/wit-5i3qLPDT.js +1 -0
  310. package/dist/assets/wolfram-lXgVvXCa.js +1 -0
  311. package/dist/assets/xml-sdJ4AIDG.js +1 -0
  312. package/dist/assets/xsl-CtQFsRM5.js +1 -0
  313. package/dist/assets/yaml-Buea-lGh.js +1 -0
  314. package/dist/assets/zenscript-DVFEvuxE.js +1 -0
  315. package/dist/assets/zig-VOosw3JB.js +1 -0
  316. package/dist/index.html +14 -0
  317. package/dist-server/companion/src/listener.js +187 -0
  318. package/dist-server/computer-proxy.js +1185 -0
  319. package/dist-server/connector-proxy.js +186 -0
  320. package/dist-server/container-mcp.js +15186 -0
  321. package/dist-server/drivers/agents-proxy.js +887 -0
  322. package/dist-server/drivers/browser-proxy.js +15037 -0
  323. package/dist-server/drivers/dweb-proxy.js +175 -0
  324. package/dist-server/drivers/phone-proxy.js +339 -0
  325. package/dist-server/drivers/pi-mcp-extension.ts +638 -0
  326. package/dist-server/index.js +64869 -0
  327. package/dist-server/local-computer.js +198 -0
  328. package/dist-server/mcp-server.js +1233 -0
  329. package/dist-server/openmausbot.js +3671 -0
  330. package/dist-server/pair-cli.js +3671 -0
  331. package/dist-server/permission-proxy.js +126 -0
  332. package/dist-server/prepare-cloudflared.js +253 -0
  333. package/dist-server/proxy-paths.js +29 -0
  334. package/dist-server/server/atomic.js +40 -0
  335. package/dist-server/server/attachments.js +471 -0
  336. package/dist-server/server/auto-approve.js +162 -0
  337. package/dist-server/server/auto-review.js +79 -0
  338. package/dist-server/server/avatar-image.js +144 -0
  339. package/dist-server/server/bot-cwd.js +29 -0
  340. package/dist-server/server/bot-directory.js +127 -0
  341. package/dist-server/server/bot-package.js +268 -0
  342. package/dist-server/server/bot-profile.js +76 -0
  343. package/dist-server/server/box-create-idempotency.js +463 -0
  344. package/dist-server/server/box.js +944 -0
  345. package/dist-server/server/brand.js +65 -0
  346. package/dist-server/server/browser-connection.js +187 -0
  347. package/dist-server/server/browser-lifecycle-cleanup.js +359 -0
  348. package/dist-server/server/calendar-calls.js +314 -0
  349. package/dist-server/server/channel-queue.js +70 -0
  350. package/dist-server/server/checkpoints.js +405 -0
  351. package/dist-server/server/chief-of-staff.js +49 -0
  352. package/dist-server/server/claude-update.js +70 -0
  353. package/dist-server/server/cli.js +524 -0
  354. package/dist-server/server/cloud-backend.js +30 -0
  355. package/dist-server/server/comms-visibility.js +109 -0
  356. package/dist-server/server/composio.js +945 -0
  357. package/dist-server/server/computer-control.js +149 -0
  358. package/dist-server/server/computer-observation.js +133 -0
  359. package/dist-server/server/computer-proxy.js +1061 -0
  360. package/dist-server/server/config.js +760 -0
  361. package/dist-server/server/connector-proxy.js +228 -0
  362. package/dist-server/server/container-computer.js +840 -0
  363. package/dist-server/server/container-mcp.js +27 -0
  364. package/dist-server/server/contracts.js +24 -0
  365. package/dist-server/server/control-client.js +100 -0
  366. package/dist-server/server/data-dir-lease.js +3 -0
  367. package/dist-server/server/decision-log.js +106 -0
  368. package/dist-server/server/delegations.js +628 -0
  369. package/dist-server/server/drivers/acp/core.js +904 -0
  370. package/dist-server/server/drivers/acp/cursor.js +365 -0
  371. package/dist-server/server/drivers/acp/custom.js +51 -0
  372. package/dist-server/server/drivers/acp/droid.js +225 -0
  373. package/dist-server/server/drivers/acp/gemini.js +95 -0
  374. package/dist-server/server/drivers/acp/grok.js +249 -0
  375. package/dist-server/server/drivers/acp/hermes.js +429 -0
  376. package/dist-server/server/drivers/acp/kimi.js +559 -0
  377. package/dist-server/server/drivers/acp/opencode-go.js +370 -0
  378. package/dist-server/server/drivers/acp/qwen.js +98 -0
  379. package/dist-server/server/drivers/agents-proxy.js +941 -0
  380. package/dist-server/server/drivers/antigravity-acp.js +507 -0
  381. package/dist-server/server/drivers/antigravity-release.js +65 -0
  382. package/dist-server/server/drivers/antigravity-runtime.js +395 -0
  383. package/dist-server/server/drivers/antigravity.js +144 -0
  384. package/dist-server/server/drivers/boxagent.js +245 -0
  385. package/dist-server/server/drivers/browser-proxy.js +520 -0
  386. package/dist-server/server/drivers/builtIn.js +36 -0
  387. package/dist-server/server/drivers/claude.js +1337 -0
  388. package/dist-server/server/drivers/codex-catalog.js +409 -0
  389. package/dist-server/server/drivers/codex.js +1116 -0
  390. package/dist-server/server/drivers/dweb-proxy.js +193 -0
  391. package/dist-server/server/drivers/grok.js +48 -0
  392. package/dist-server/server/drivers/grokagent.js +4 -0
  393. package/dist-server/server/drivers/local-inject.js +388 -0
  394. package/dist-server/server/drivers/minimax.js +108 -0
  395. package/dist-server/server/drivers/native.js +21 -0
  396. package/dist-server/server/drivers/openai-chat.js +240 -0
  397. package/dist-server/server/drivers/openai-compat.js +143 -0
  398. package/dist-server/server/drivers/phone-proxy.js +372 -0
  399. package/dist-server/server/drivers/pi.js +855 -0
  400. package/dist-server/server/drivers/retry.js +96 -0
  401. package/dist-server/server/enterprise.js +93 -0
  402. package/dist-server/server/env-path.js +329 -0
  403. package/dist-server/server/environment.js +105 -0
  404. package/dist-server/server/generated-image.js +39 -0
  405. package/dist-server/server/graceful-shutdown.js +24 -0
  406. package/dist-server/server/group-goal-run.js +124 -0
  407. package/dist-server/server/harness/bus.js +86 -0
  408. package/dist-server/server/harness/registry.js +217 -0
  409. package/dist-server/server/index.js +11634 -0
  410. package/dist-server/server/installed-playbooks.js +34 -0
  411. package/dist-server/server/local-computer.js +278 -0
  412. package/dist-server/server/local-routing.js +9 -0
  413. package/dist-server/server/local-vm-idle.js +44 -0
  414. package/dist-server/server/local-vm-inventory.js +34 -0
  415. package/dist-server/server/local-vm-lease.js +58 -0
  416. package/dist-server/server/mcp-bridge.js +251 -0
  417. package/dist-server/server/mcp-probe.js +153 -0
  418. package/dist-server/server/mcp-registry.js +130 -0
  419. package/dist-server/server/member-turn.js +6 -0
  420. package/dist-server/server/message-db.js +358 -0
  421. package/dist-server/server/message-file.js +439 -0
  422. package/dist-server/server/names.js +26 -0
  423. package/dist-server/server/notify.js +69 -0
  424. package/dist-server/server/openmaus-status-capsule.js +329 -0
  425. package/dist-server/server/openmausbot.js +6 -0
  426. package/dist-server/server/package-export.js +157 -0
  427. package/dist-server/server/pair-cli.js +7 -0
  428. package/dist-server/server/peer-approval-key.js +5 -0
  429. package/dist-server/server/peer-approval.js +182 -0
  430. package/dist-server/server/peer-provenance.js +44 -0
  431. package/dist-server/server/peer-roster.js +139 -0
  432. package/dist-server/server/permission-proxy.js +157 -0
  433. package/dist-server/server/phone-secret.js +358 -0
  434. package/dist-server/server/private-screen-capture.js +17 -0
  435. package/dist-server/server/procs.js +123 -0
  436. package/dist-server/server/project-scout.js +304 -0
  437. package/dist-server/server/proxy-paths.js +47 -0
  438. package/dist-server/server/redact.js +102 -0
  439. package/dist-server/server/remote-computer.js +170 -0
  440. package/dist-server/server/repeat-detector.js +48 -0
  441. package/dist-server/server/replies.js +38 -0
  442. package/dist-server/server/request-auth.js +327 -0
  443. package/dist-server/server/room-cwd.js +15 -0
  444. package/dist-server/server/room-post-budget.js +112 -0
  445. package/dist-server/server/room-turn-timeout.js +115 -0
  446. package/dist-server/server/routine-requests.js +871 -0
  447. package/dist-server/server/routines.js +1111 -0
  448. package/dist-server/server/schema.js +11 -0
  449. package/dist-server/server/screen-frame-gate.js +80 -0
  450. package/dist-server/server/section-context.js +85 -0
  451. package/dist-server/server/send-idempotency.js +53 -0
  452. package/dist-server/server/sessions.js +319 -0
  453. package/dist-server/server/skill-fetch.js +148 -0
  454. package/dist-server/server/skill-learn.js +61 -0
  455. package/dist-server/server/skill-library.js +116 -0
  456. package/dist-server/server/skills.js +1223 -0
  457. package/dist-server/server/steer-queue.js +114 -0
  458. package/dist-server/server/store.js +1217 -0
  459. package/dist-server/server/surface.js +107 -0
  460. package/dist-server/server/tailscale.js +103 -0
  461. package/dist-server/server/team-backup.js +183 -0
  462. package/dist-server/server/team-library.js +205 -0
  463. package/dist-server/server/team-manifest.js +224 -0
  464. package/dist-server/server/thread-events.js +226 -0
  465. package/dist-server/server/tts/elevenlabs.js +92 -0
  466. package/dist-server/server/tts/index.js +79 -0
  467. package/dist-server/server/tts/speech-text.js +195 -0
  468. package/dist-server/server/tts/system-voices.js +74 -0
  469. package/dist-server/server/tunnel.js +202 -0
  470. package/dist-server/server/turn-context.js +40 -0
  471. package/dist-server/server/turn-dispatch-guard.js +152 -0
  472. package/dist-server/server/turn-images.js +138 -0
  473. package/dist-server/server/turn-watchdog.js +65 -0
  474. package/dist-server/server/vps-computer.js +1099 -0
  475. package/dist-server/server/vps-container-mcp.js +31 -0
  476. package/dist-server/server/webhook-ingress.js +173 -0
  477. package/dist-server/server/webhooks.js +491 -0
  478. package/dist-server/server/workspace.js +178 -0
  479. package/dist-server/shared/approval-mode.js +39 -0
  480. package/dist-server/shared/bot-avatar.js +33 -0
  481. package/dist-server/shared/bot-profile.js +7 -0
  482. package/dist-server/shared/connector-availability.js +12 -0
  483. package/dist-server/shared/credential-request.js +82 -0
  484. package/dist-server/shared/group-goal-run.js +1 -0
  485. package/dist-server/shared/import-name.js +11 -0
  486. package/dist-server/shared/mascot-bodies.js +107 -0
  487. package/dist-server/shared/routine-request.js +10 -0
  488. package/dist-server/shared/routine-run.js +1 -0
  489. package/dist-server/shared/skill-request.js +16 -0
  490. package/dist-server/shared/team-backup.js +148 -0
  491. package/dist-server/src/lib/composer-attachments.js +676 -0
  492. package/dist-server/tunnel-guardian.js +390 -0
  493. package/dist-server/vps-container-mcp.js +15209 -0
  494. package/package.json +32 -0
  495. package/skills/create-verification-skill/SKILL.md +65 -0
  496. package/skills/create-verification-skill/manifest.json +13 -0
  497. package/skills/phone-harness/SKILL.md +26 -0
  498. package/skills/phone-harness/agents/openai.yaml +13 -0
  499. package/skills/phone-harness/manifest.json +17 -0
@@ -0,0 +1,855 @@
1
+ // pi — the pi coding agent (@earendil-works/pi-coding-agent) as a native engine.
2
+ //
3
+ // pi exposes a JSON-RPC mode over stdio (`pi --mode rpc --no-session`) rather
4
+ // than ACP, so — like the Claude Code and Codex CLIs — it gets a native driver
5
+ // that speaks its own protocol and emits canonical RuntimeEvents. pi is a
6
+ // BYOK agent: credentials live in ~/.pi/agent/auth.json and are injected by
7
+ // the pi binary itself, so this driver holds no API key and needs no sign-in.
8
+ //
9
+ // Conversation continuity: the first turn sends `new_session` and remembers
10
+ // the returned `sessionFile`; later turns send `switch_session` with that
11
+ // path (the way Claude Code resumes by session id). `sessionFile` is the
12
+ // resumeCursor the harness persists per thread.
13
+ //
14
+ // Model ids in the picker are `provider/modelId` composites (e.g.
15
+ // `ollama-cloud/glm-5.2`); `set_model` splits that into pi's separate
16
+ // `{provider, modelId}` fields. Live local hosts (oMLX / Ollama / EXO /
17
+ // LM Studio / Unsloth) land as `host::model` inject ids the same way the
18
+ // other engines do: mergeLocalInject lists them in Custom, and a pick
19
+ // upserts ~/.pi/agent/models.json so pi can reach the host. The live
20
+ // catalog is probed from `get_available_models` and every entry is flagged
21
+ // `custom` because pi is a custom-only (BYOK) engine — the model picker's
22
+ // Local pane only lists `custom` options for custom-only engines.
23
+ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
24
+ import { homedir, tmpdir } from "node:os";
25
+ import { join } from "node:path";
26
+ import { PROVIDER_CREDENTIAL_ENV, stripWorkspaceCredentialEnv } from "../config.js";
27
+ import { computerProxyEnv } from "../container-computer.js";
28
+ import { augmentedPath } from "../env-path.js";
29
+ import { describeSpawnFailure, killCliTree, spawnCli } from "../procs.js";
30
+ import { SPAWNED_PROXIES } from "../proxy-paths.js";
31
+ import { EFFORT_LEVELS, newEventId, newId } from "../contracts.js";
32
+ import { decodeInjectId, encodeInjectId, hostApiKey, localHost, mergeLocalInject, } from "./local-inject.js";
33
+ import { appendNative } from "./native.js";
34
+ const DRIVER_KIND = "piAgent";
35
+ const PI_ARGS = ["--mode", "rpc", "--no-session"];
36
+ const NODE_ENV_FLAG = { ELECTRON_RUN_AS_NODE: "1" };
37
+ function readPiPromptImages(turn) {
38
+ return (turn.images ?? []).map((image) => ({
39
+ type: "image",
40
+ data: readFileSync(image.path).toString("base64"),
41
+ mimeType: image.mime,
42
+ }));
43
+ }
44
+ /** Provider-native logs are designed for bug reports. Preserve the RPC
45
+ * shape and encoded size, but never persist a user's image bytes in them. */
46
+ function piNativeLogMessage(message) {
47
+ if (!Array.isArray(message.images))
48
+ return message;
49
+ return {
50
+ ...message,
51
+ images: message.images.map((image) => {
52
+ if (!image || typeof image !== "object")
53
+ return image;
54
+ const value = image;
55
+ return typeof value.data === "string"
56
+ ? { ...value, data: `[image data: ${value.data.length} base64 chars]` }
57
+ : value;
58
+ }),
59
+ };
60
+ }
61
+ /** Harness effort → pi thinking level (`set_thinking_level`). The sets match
62
+ * one-for-one except for the name of the lowest rung: the harness calls it
63
+ * "none", pi calls it "off". Exported for the test. */
64
+ export function piThinkingLevel(effort) {
65
+ return effort === "none" ? "off" : effort;
66
+ }
67
+ /** Mirror of the Claude driver's integration → stdio MCP mount: every entry is
68
+ * a JSON-RPC 2.0 stdio server the pi-mcp-extension consumes. Returns null when
69
+ * there is nothing to mount (the common case). */
70
+ export function buildMcpServers(turn) {
71
+ const servers = {};
72
+ if (turn.integrations?.composio)
73
+ servers.composio = { ...turn.integrations.composio };
74
+ if (turn.integrations?.computer) {
75
+ servers.computer = {
76
+ command: process.execPath,
77
+ args: [SPAWNED_PROXIES.computer],
78
+ env: { ...NODE_ENV_FLAG, ...computerProxyEnv(turn.integrations.computer) },
79
+ };
80
+ }
81
+ else if (turn.integrations?.localComputer) {
82
+ const local = turn.integrations.localComputer;
83
+ servers.computer = {
84
+ command: local.command,
85
+ args: local.args,
86
+ env: local.env,
87
+ // Host control carries scope so the extension gates every call behind
88
+ // a permission card; isolated computers deliberately omit it.
89
+ ...(local.scope ? { scope: local.scope } : {}),
90
+ };
91
+ }
92
+ if (turn.integrations?.agents)
93
+ servers.agents = { ...turn.integrations.agents };
94
+ if (turn.integrations?.phone)
95
+ servers.phone = { ...turn.integrations.phone };
96
+ if (turn.integrations?.dweb) {
97
+ servers.dweb = {
98
+ command: process.execPath,
99
+ args: [SPAWNED_PROXIES.dweb],
100
+ env: { ...NODE_ENV_FLAG, DWEB_URL: turn.integrations.dweb.url },
101
+ };
102
+ }
103
+ return Object.keys(servers).length ? servers : null;
104
+ }
105
+ /** Pure parser: turn a `get_available_models` stdout blob into a catalog.
106
+ * Every option is `custom` (pi is BYOK) and id is the `provider/modelId`
107
+ * composite the picker and `set_model` both use. Exported for the test. */
108
+ export function parsePiCatalog(stdout, fallbackDefault = "") {
109
+ const options = [];
110
+ let def = fallbackDefault;
111
+ for (const line of stdout.split("\n")) {
112
+ if (!line.trim())
113
+ continue;
114
+ let msg;
115
+ try {
116
+ msg = JSON.parse(line);
117
+ }
118
+ catch {
119
+ continue;
120
+ }
121
+ const res = msg;
122
+ if (res?.type !== "response" || res.command !== "get_available_models" || !res.success)
123
+ continue;
124
+ for (const m of res.data?.models ?? []) {
125
+ if (!m?.provider || !m?.id)
126
+ continue;
127
+ const id = `${m.provider}/${m.id}`;
128
+ options.push({ id, label: m.name ?? m.id, custom: true, provider: m.provider });
129
+ }
130
+ break;
131
+ }
132
+ if (!def && options.length)
133
+ def = options[0].id;
134
+ return { default: def, options };
135
+ }
136
+ /** Split a picker id into pi's `{provider, modelId}`. Accepts both the
137
+ * native `provider/modelId` composite and a live-host `host::model`
138
+ * inject id. */
139
+ export function splitPiModel(id) {
140
+ const inject = decodeInjectId(id);
141
+ if (inject)
142
+ return { provider: inject.host, modelId: inject.model };
143
+ if (!id.includes("/"))
144
+ return null;
145
+ const [provider, ...rest] = id.split("/");
146
+ if (!provider || !rest.length)
147
+ return null;
148
+ return { provider, modelId: rest.join("/") };
149
+ }
150
+ /** Prefer live `host::model` inject rows over the same model already
151
+ * listed as `host/model` from ~/.pi/agent/models.json, so Custom does
152
+ * not show duplicates. */
153
+ export function preferPiInjectRows(catalog) {
154
+ const injectIds = new Set(catalog.options.filter((option) => decodeInjectId(option.id)).map((option) => option.id));
155
+ if (!injectIds.size)
156
+ return catalog;
157
+ const options = catalog.options.filter((option) => {
158
+ if (decodeInjectId(option.id))
159
+ return true;
160
+ const slash = option.id.indexOf("/");
161
+ if (slash <= 0)
162
+ return true;
163
+ return !injectIds.has(encodeInjectId(option.id.slice(0, slash), option.id.slice(slash + 1)));
164
+ });
165
+ let def = catalog.default;
166
+ if (def && !options.some((option) => option.id === def)) {
167
+ const slash = def.indexOf("/");
168
+ const mapped = slash > 0 ? encodeInjectId(def.slice(0, slash), def.slice(slash + 1)) : "";
169
+ def = injectIds.has(mapped) ? mapped : (options[0]?.id ?? "");
170
+ }
171
+ return { default: def, options };
172
+ }
173
+ export async function applyPiLocalCatalog(catalog, env = process.env, fetchImpl = fetch) {
174
+ return preferPiInjectRows(await mergeLocalInject(catalog, env, fetchImpl));
175
+ }
176
+ function piAgentDir(env) {
177
+ return join(env.HOME || env.USERPROFILE || homedir(), ".pi", "agent");
178
+ }
179
+ /** Upsert a live local host into ~/.pi/agent/models.json so `set_model`
180
+ * can reach it. Existing providers and models are kept. Returns the
181
+ * `{provider, modelId}` pair pi's RPC expects, or null when the picker
182
+ * id is not a model at all. */
183
+ export function ensurePiInjectModel(modelId, env = process.env) {
184
+ const split = splitPiModel(modelId);
185
+ if (!split)
186
+ return null;
187
+ const inject = decodeInjectId(modelId);
188
+ if (!inject)
189
+ return split;
190
+ const host = localHost(inject.host);
191
+ if (!host)
192
+ return split;
193
+ const dir = piAgentDir(env);
194
+ mkdirSync(dir, { recursive: true });
195
+ const path = join(dir, "models.json");
196
+ let root = { providers: {} };
197
+ if (existsSync(path)) {
198
+ try {
199
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
200
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
201
+ root = parsed;
202
+ }
203
+ else {
204
+ // Malformed — do not destroy the file; still return the split so
205
+ // set_model can try.
206
+ return split;
207
+ }
208
+ }
209
+ catch {
210
+ return split;
211
+ }
212
+ }
213
+ const providers = root.providers && typeof root.providers === "object" && !Array.isArray(root.providers)
214
+ ? { ...root.providers }
215
+ : {};
216
+ const previous = providers[inject.host];
217
+ const existing = previous && typeof previous === "object" && !Array.isArray(previous)
218
+ ? { ...previous }
219
+ : {
220
+ baseUrl: host.baseUrl,
221
+ api: "openai-completions",
222
+ apiKey: hostApiKey(host, env),
223
+ compat: { supportsDeveloperRole: false, supportsReasoningEffort: true },
224
+ models: [],
225
+ };
226
+ existing.baseUrl = host.baseUrl;
227
+ existing.api = typeof existing.api === "string" && existing.api ? existing.api : "openai-completions";
228
+ existing.apiKey = hostApiKey(host, env);
229
+ if (!existing.compat) {
230
+ existing.compat = { supportsDeveloperRole: false, supportsReasoningEffort: true };
231
+ }
232
+ const models = Array.isArray(existing.models)
233
+ ? existing.models.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row))
234
+ : [];
235
+ if (!models.some((row) => row.id === inject.model)) {
236
+ models.push({
237
+ id: inject.model,
238
+ name: inject.model,
239
+ reasoning: true,
240
+ input: ["text"],
241
+ contextWindow: 131072,
242
+ maxTokens: 16384,
243
+ });
244
+ }
245
+ existing.models = models;
246
+ providers[inject.host] = existing;
247
+ root.providers = providers;
248
+ writeFileSync(path, `${JSON.stringify(root, null, 2)}\n`, { mode: 0o600 });
249
+ try {
250
+ chmodSync(path, 0o600);
251
+ }
252
+ catch {
253
+ // Windows ignores POSIX modes; keep the inject even if chmod is unsupported.
254
+ }
255
+ return split;
256
+ }
257
+ /** The pi-side default model, read from ~/.pi/agent/settings.json so the
258
+ * catalog's `default` matches what `pi` would actually run. Missing file →
259
+ * empty string, and the first option is used instead. */
260
+ function readPiDefaultModel(env) {
261
+ const home = env.HOME || env.USERPROFILE || homedir();
262
+ try {
263
+ const s = JSON.parse(readFileSync(`${home}/.pi/agent/settings.json`, "utf8"));
264
+ return s.defaultProvider && s.defaultModel ? `${s.defaultProvider}/${s.defaultModel}` : "";
265
+ }
266
+ catch {
267
+ return "";
268
+ }
269
+ }
270
+ /** Probe the live catalog by spawning `pi --mode rpc --no-session`, sending
271
+ * `get_available_models`, and parsing the response. A failed probe resolves
272
+ * with an empty catalog — the instance reports unavailable via snapshot. */
273
+ export async function fetchPiModels(cli, env) {
274
+ const child = spawnCli(cli, PI_ARGS, { stdio: ["pipe", "pipe", "pipe"], env });
275
+ return new Promise((resolve) => {
276
+ let buf = "";
277
+ let done = false;
278
+ const fallbackDefault = readPiDefaultModel(env);
279
+ const finish = (catalog) => {
280
+ if (done)
281
+ return;
282
+ done = true;
283
+ try {
284
+ killCliTree(child);
285
+ }
286
+ catch {
287
+ /* already gone */
288
+ }
289
+ resolve(catalog);
290
+ };
291
+ const timer = setTimeout(() => finish({ default: "", options: [] }), 15_000);
292
+ timer.unref?.();
293
+ child.stdout.setEncoding("utf8");
294
+ child.stdout.on("data", (chunk) => {
295
+ buf += chunk;
296
+ let nl;
297
+ while ((nl = buf.indexOf("\n")) !== -1) {
298
+ const line = buf.slice(0, nl);
299
+ buf = buf.slice(nl + 1);
300
+ if (!line.trim())
301
+ continue;
302
+ const parsed = parsePiCatalog(line + "\n", fallbackDefault);
303
+ if (parsed.options.length || line.includes('"get_available_models"')) {
304
+ clearTimeout(timer);
305
+ finish(parsed);
306
+ return;
307
+ }
308
+ }
309
+ });
310
+ child.on("error", () => finish({ default: "", options: [] }));
311
+ child.on("close", () => finish({ default: "", options: [] }));
312
+ try {
313
+ child.stdin.write(JSON.stringify({ id: "catalog", type: "get_available_models" }) + "\n");
314
+ }
315
+ catch {
316
+ finish({ default: "", options: [] });
317
+ }
318
+ });
319
+ }
320
+ function decodeConfig(raw) {
321
+ if (raw === null || raw === undefined)
322
+ return { cli: "pi", fullAuto: false };
323
+ if (typeof raw !== "object")
324
+ throw new Error("pi config must be an object");
325
+ const obj = raw;
326
+ if (obj.cli !== undefined && typeof obj.cli !== "string")
327
+ throw new Error("pi config `cli` must be a string");
328
+ if (obj.fullAuto !== undefined && typeof obj.fullAuto !== "boolean")
329
+ throw new Error("pi config `fullAuto` must be a boolean");
330
+ return {
331
+ cli: obj.cli && obj.cli.trim() ? obj.cli.trim() : "pi",
332
+ fullAuto: obj.fullAuto === true,
333
+ };
334
+ }
335
+ const EMPTY = { default: "", options: [] };
336
+ function piEnvironment(source) {
337
+ const env = { ...source, PATH: augmentedPath() };
338
+ // pi is BYOK and reads provider keys straight from its environment: an
339
+ // inherited key would silently flip billing onto one the user never granted
340
+ // pi, and workspace credentials are the harness's secrets, not pi's. The
341
+ // keys pi may use live in its own settings file, so the child inherits
342
+ // neither list.
343
+ stripWorkspaceCredentialEnv(env);
344
+ for (const key of PROVIDER_CREDENTIAL_ENV)
345
+ delete env[key];
346
+ return env;
347
+ }
348
+ export const PiDriver = {
349
+ driverKind: DRIVER_KIND,
350
+ metadata: { displayName: "pi", supportsMultipleInstances: true, access: "custom" },
351
+ install: {
352
+ command: {
353
+ darwin: "npm install -g @earendil-works/pi-coding-agent",
354
+ linux: "npm install -g @earendil-works/pi-coding-agent",
355
+ win32: "npm install -g @earendil-works/pi-coding-agent",
356
+ },
357
+ needsNode: true,
358
+ docsUrl: "https://pi.dev",
359
+ },
360
+ models: EMPTY,
361
+ decodeConfig,
362
+ defaultConfig: () => decodeConfig({}),
363
+ async create(input) {
364
+ const { instanceId, config } = input;
365
+ const catalogEnv = piEnvironment({ ...process.env, ...input.environment });
366
+ let models = EMPTY;
367
+ const refreshModels = async () => {
368
+ let base = models;
369
+ try {
370
+ const resolved = await fetchPiModels(config.cli, catalogEnv);
371
+ if (resolved.options.length)
372
+ base = resolved;
373
+ }
374
+ catch {
375
+ // Keep the last usable catalog when the probe fails.
376
+ }
377
+ try {
378
+ const next = await applyPiLocalCatalog(base, catalogEnv);
379
+ if (next.options.length)
380
+ models = next;
381
+ }
382
+ catch {
383
+ if (base.options.length)
384
+ models = base;
385
+ }
386
+ };
387
+ await refreshModels();
388
+ const listeners = new Set();
389
+ // one active turn per thread
390
+ const active = new Map();
391
+ const emit = (event) => {
392
+ for (const l of [...listeners])
393
+ l(event);
394
+ };
395
+ const base = (threadId, turnId) => ({
396
+ eventId: newEventId(),
397
+ provider: DRIVER_KIND,
398
+ providerInstanceId: instanceId,
399
+ threadId,
400
+ turnId,
401
+ createdAt: new Date().toISOString(),
402
+ });
403
+ const sendTurn = async (turn) => {
404
+ const { threadId } = turn;
405
+ if (active.has(threadId))
406
+ throw new Error("a turn is already running on this thread");
407
+ // Per-bot Ask/Auto is authoritative for harness turns. Preserve the
408
+ // legacy instance flag only for direct adapter callers that omit it.
409
+ const fullAuto = turn.approvalMode === undefined ? config.fullAuto : false;
410
+ // Host control always routes through the permission card; full-auto must
411
+ // never get unapproved hands on the user's machine (same guard as the
412
+ // Claude and ACP drivers).
413
+ const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
414
+ if (controlsHost && fullAuto) {
415
+ throw new Error("local computer control requires the interactive approval broker");
416
+ }
417
+ const turnId = newId();
418
+ const pending = new Map();
419
+ let settled = false;
420
+ // pi's RPC surface accepts image content directly. Read before spawning
421
+ // so an attachment that disappeared produces one clear dispatch error
422
+ // instead of starting a child that can never receive its prompt.
423
+ const images = readPiPromptImages(turn);
424
+ // Write ~/.pi/agent/models.json before creating any credential-bearing
425
+ // MCP temp files. If model setup fails, there is nothing sensitive to
426
+ // clean up yet.
427
+ if (typeof turn.model === "string" && turn.model) {
428
+ ensurePiInjectModel(turn.model, { ...process.env, ...input.environment });
429
+ }
430
+ // integrations → stdio MCP servers for the pi-mcp-extension. The config
431
+ // carries credentials (box token, composio key, comms token), so it goes
432
+ // into a 0600 temp file removed when the turn settles — never on argv.
433
+ const mcpServers = buildMcpServers(turn);
434
+ let mcpTempDir = null;
435
+ if (mcpServers) {
436
+ mcpTempDir = mkdtempSync(join(tmpdir(), "omb-pi-mcp-"));
437
+ try {
438
+ writeFileSync(join(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 0o600 });
439
+ }
440
+ catch (err) {
441
+ // A failed write must not leave the temp dir behind — a partial file
442
+ // could still hold the box token / composio key / comms token.
443
+ try {
444
+ rmSync(mcpTempDir, { recursive: true, force: true });
445
+ }
446
+ catch {
447
+ /* best effort */
448
+ }
449
+ throw err;
450
+ }
451
+ }
452
+ const childArgs = mcpServers ? [...PI_ARGS, "-e", SPAWNED_PROXIES.piMcpExtension] : PI_ARGS;
453
+ // spawnCli can throw synchronously (unresolvable CLI); if it does, the
454
+ // 0600 temp file with the box token / composio key / comms token must
455
+ // not be left on disk — settle() never runs because no child existed.
456
+ const child = (() => {
457
+ try {
458
+ return spawnCli(config.cli, childArgs, {
459
+ stdio: ["pipe", "pipe", "pipe"],
460
+ cwd: turn.cwd,
461
+ env: piEnvironment({
462
+ ...process.env,
463
+ ...input.environment,
464
+ ...(mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join(mcpTempDir, "mcp.json") } : {}),
465
+ }),
466
+ });
467
+ }
468
+ catch (err) {
469
+ if (mcpTempDir) {
470
+ try {
471
+ rmSync(mcpTempDir, { recursive: true, force: true });
472
+ }
473
+ catch {
474
+ /* best effort */
475
+ }
476
+ }
477
+ throw err;
478
+ }
479
+ })();
480
+ let buf = "";
481
+ let assistantText = "";
482
+ // resolve one-shot RPC responses (new_session / switch_session / set_model)
483
+ const responseWaiters = new Map();
484
+ const rejectWaiters = (err) => {
485
+ for (const waiter of responseWaiters.values()) {
486
+ clearTimeout(waiter.timer);
487
+ waiter.reject(err);
488
+ }
489
+ responseWaiters.clear();
490
+ };
491
+ const awaitResponse = (command, timeoutMs = 20_000) => new Promise((resolve, reject) => {
492
+ const timer = setTimeout(() => {
493
+ responseWaiters.delete(command);
494
+ reject(new Error(`pi ${command} timed out`));
495
+ }, timeoutMs);
496
+ timer.unref?.();
497
+ responseWaiters.set(command, { resolve, reject, timer });
498
+ });
499
+ child.stdin.on("error", () => rejectWaiters(new Error("pi stdin closed")));
500
+ const send = (obj) => {
501
+ appendNative(threadId, { dir: "out", source: "pi.rpc", msg: piNativeLogMessage(obj) });
502
+ child.stdin.write(JSON.stringify(obj) + "\n");
503
+ };
504
+ /** Emit buffered assistant text as its own item, then clear it. */
505
+ const flushAssistantText = () => {
506
+ const text = assistantText;
507
+ assistantText = "";
508
+ if (!text.trim())
509
+ return;
510
+ emit({ ...base(threadId, turnId), type: "item.completed", itemType: "assistant_text", text });
511
+ };
512
+ const settle = (ok, stopReason, usage) => {
513
+ if (settled)
514
+ return;
515
+ settled = true;
516
+ flushAssistantText();
517
+ emit({
518
+ ...base(threadId, turnId),
519
+ type: "turn.completed",
520
+ ok,
521
+ stopReason: stopReason ?? (ok ? "end_turn" : "failed"),
522
+ ...(usage ? { usage: { input: usage.input ?? 0, output: usage.output ?? 0 } } : {}),
523
+ });
524
+ try {
525
+ child.stdin.end();
526
+ }
527
+ catch {
528
+ /* already closed */
529
+ }
530
+ try {
531
+ killCliTree(child);
532
+ }
533
+ catch {
534
+ /* already gone */
535
+ }
536
+ if (mcpTempDir) {
537
+ try {
538
+ rmSync(mcpTempDir, { recursive: true, force: true });
539
+ }
540
+ catch {
541
+ /* best effort */
542
+ }
543
+ }
544
+ active.delete(threadId);
545
+ };
546
+ const stop = () => {
547
+ try {
548
+ send({ type: "abort" });
549
+ }
550
+ catch {
551
+ /* ignore */
552
+ }
553
+ try {
554
+ killCliTree(child);
555
+ }
556
+ catch {
557
+ /* ignore */
558
+ }
559
+ settle(true, "cancelled");
560
+ };
561
+ active.set(threadId, { stop, turnId, pending, child });
562
+ const onEvent = (evt) => {
563
+ appendNative(threadId, { dir: "in", source: "pi.rpc", msg: evt });
564
+ switch (evt.type) {
565
+ case "response": {
566
+ if (evt.command && responseWaiters.has(evt.command)) {
567
+ const waiter = responseWaiters.get(evt.command);
568
+ responseWaiters.delete(evt.command);
569
+ clearTimeout(waiter.timer);
570
+ if (evt.success)
571
+ waiter.resolve(evt.data);
572
+ else
573
+ waiter.reject(new Error(`pi ${evt.command} failed`));
574
+ }
575
+ return;
576
+ }
577
+ case "message_update": {
578
+ const e = evt.assistantMessageEvent;
579
+ if (!e)
580
+ return;
581
+ if (e.type === "text_delta" && typeof e.delta === "string") {
582
+ assistantText += e.delta;
583
+ emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta: e.delta });
584
+ }
585
+ else if (e.type === "thinking_delta" && typeof e.delta === "string") {
586
+ emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta: e.delta });
587
+ }
588
+ return;
589
+ }
590
+ case "tool_execution_start": {
591
+ flushAssistantText();
592
+ emit({
593
+ ...base(threadId, turnId),
594
+ type: "item.started",
595
+ itemType: "tool",
596
+ itemId: evt.toolCallId,
597
+ title: String(evt.toolName ?? "tool").slice(0, 80),
598
+ });
599
+ return;
600
+ }
601
+ case "tool_execution_end": {
602
+ emit({
603
+ ...base(threadId, turnId),
604
+ type: "item.completed",
605
+ itemType: "tool",
606
+ itemId: evt.toolCallId,
607
+ ok: !evt.isError,
608
+ });
609
+ return;
610
+ }
611
+ case "extension_ui_request": {
612
+ // pi floods setWidget/setStatus for TUI bookkeeping; only
613
+ // select/confirm/input are questions that wait for an answer.
614
+ if (evt.method === "select" || evt.method === "confirm" || evt.method === "input") {
615
+ flushAssistantText();
616
+ const reqId = evt.id ?? newId();
617
+ const isQuestion = evt.method === "input";
618
+ // Register BEFORE emitting: the harness may auto-approve from
619
+ // inside its synchronous request.opened listener. Emitting first
620
+ // made respondToRequest see no pending ask, return unavailable,
621
+ // then fall back to a human card on every "Always allow" call.
622
+ pending.set(reqId, (decision) => {
623
+ if (decision.behavior === "deny")
624
+ send({ type: "extension_ui_response", id: reqId, cancelled: true });
625
+ else if (isQuestion)
626
+ send({ type: "extension_ui_response", id: reqId, value: decision.message ?? "" });
627
+ else
628
+ send({ type: "extension_ui_response", id: reqId, confirmed: true });
629
+ });
630
+ emit({
631
+ ...base(threadId, turnId),
632
+ requestId: reqId,
633
+ type: "request.opened",
634
+ requestType: isQuestion ? "question" : "permission",
635
+ tool: String(evt.title ?? "pi"),
636
+ summary: String(evt.title ?? "pi wants confirmation"),
637
+ });
638
+ }
639
+ return;
640
+ }
641
+ case "turn_end":
642
+ case "agent_end": {
643
+ const sr = evt.message?.stopReason;
644
+ // toolUse means pi ran a tool and auto-continues next turn to
645
+ // answer — settling now would drop the final reply.
646
+ if (sr === "toolUse" || sr === "tool_use" || sr === "tool_calls")
647
+ return;
648
+ const usage = evt.usage ?? evt.message?.usage;
649
+ if (sr === "error" || sr === "failed") {
650
+ emit({
651
+ ...base(threadId, turnId),
652
+ type: "runtime.error",
653
+ message: String(evt.message?.errorMessage ?? "pi turn failed").slice(0, 2_000),
654
+ });
655
+ settle(false, "failed", usage);
656
+ return;
657
+ }
658
+ settle(true, sr === "cancelled" || sr === "aborted" ? "cancelled" : "end_turn", usage);
659
+ return;
660
+ }
661
+ default:
662
+ return;
663
+ }
664
+ };
665
+ child.stdout.setEncoding("utf8");
666
+ child.stdout.on("data", (chunk) => {
667
+ buf += chunk;
668
+ let nl;
669
+ while ((nl = buf.indexOf("\n")) !== -1) {
670
+ const line = buf.slice(0, nl);
671
+ buf = buf.slice(nl + 1);
672
+ if (!line.trim())
673
+ continue;
674
+ try {
675
+ onEvent(JSON.parse(line));
676
+ }
677
+ catch {
678
+ /* skip non-JSON line */
679
+ }
680
+ }
681
+ });
682
+ child.on("error", (err) => {
683
+ const fail = describeSpawnFailure(err, config.cli);
684
+ rejectWaiters(new Error(fail.message));
685
+ emit({ ...base(threadId, turnId), type: "runtime.error", message: fail.message, setup: fail.setup });
686
+ settle(false);
687
+ });
688
+ child.on("close", () => {
689
+ // a clean close without a terminal event is a failed turn, never a hang
690
+ rejectWaiters(new Error("pi process exited before replying"));
691
+ settle(false);
692
+ });
693
+ emit({ ...base(threadId, turnId), type: "turn.started" });
694
+ // handshake: resume the remembered session or start a fresh one. The
695
+ // harness persists session.started.sessionId as the resumeCursor and
696
+ // hands it back next turn, so that id IS the resume handle — pi's
697
+ // sessionFile, which switch_session expects as `sessionPath`.
698
+ const sessionPath = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
699
+ let sessionFile = sessionPath;
700
+ try {
701
+ const command = sessionPath ? "switch_session" : "new_session";
702
+ const hsPromise = awaitResponse(command);
703
+ send(sessionPath ? { type: "switch_session", sessionPath } : { type: "new_session" });
704
+ const hs = (await hsPromise);
705
+ if (hs?.sessionFile)
706
+ sessionFile = hs.sessionFile;
707
+ emit({
708
+ ...base(threadId, turnId),
709
+ type: "session.started",
710
+ sessionId: sessionFile ?? hs?.sessionId ?? null,
711
+ model: turn.model ?? null,
712
+ });
713
+ }
714
+ catch {
715
+ // without a session we can still try a bare prompt; pi --no-session
716
+ // accepts a prompt without an explicit session.
717
+ }
718
+ // pin the chosen model (composite id or host::model inject → provider + modelId)
719
+ const chosen = typeof turn.model === "string" ? splitPiModel(turn.model) : null;
720
+ if (chosen) {
721
+ try {
722
+ const modelPromise = awaitResponse("set_model");
723
+ send({ type: "set_model", provider: chosen.provider, modelId: chosen.modelId });
724
+ await modelPromise;
725
+ }
726
+ catch {
727
+ /* keep going on the default model */
728
+ }
729
+ }
730
+ // pin reasoning effort after the model (the supported level set is
731
+ // model-dependent); a rejection keeps the engine default
732
+ if (turn.effort) {
733
+ try {
734
+ const levelPromise = awaitResponse("set_thinking_level");
735
+ send({ type: "set_thinking_level", level: piThinkingLevel(turn.effort) });
736
+ await levelPromise;
737
+ }
738
+ catch {
739
+ /* keep going on the engine default */
740
+ }
741
+ }
742
+ const message = turn.system ? `${turn.system}\n\n${turn.text}` : turn.text;
743
+ try {
744
+ send({ type: "prompt", message, ...(images.length ? { images } : {}) });
745
+ }
746
+ catch {
747
+ settle(false);
748
+ }
749
+ return { turnId };
750
+ };
751
+ const snapshot = async () => {
752
+ const version = await new Promise((resolve) => {
753
+ const child = spawnCli(config.cli, ["--version"], {
754
+ stdio: ["ignore", "pipe", "pipe"],
755
+ env: piEnvironment({ ...process.env, ...input.environment }),
756
+ });
757
+ let out = "";
758
+ child.stdout?.setEncoding("utf8");
759
+ child.stdout?.on("data", (c) => (out += c));
760
+ const timer = setTimeout(() => {
761
+ try {
762
+ killCliTree(child);
763
+ }
764
+ catch {
765
+ /* ignore */
766
+ }
767
+ resolve(null);
768
+ }, 8000);
769
+ timer.unref?.();
770
+ child.on("error", () => {
771
+ clearTimeout(timer);
772
+ resolve(null);
773
+ });
774
+ child.on("close", () => {
775
+ clearTimeout(timer);
776
+ resolve(out.trim() || null);
777
+ });
778
+ });
779
+ if (!version)
780
+ return { state: "unavailable", reason: `\`${config.cli}\` CLI not found` };
781
+ // pi manages its own credentials (~/.pi/agent/auth.json); there is no
782
+ // separate sign-in step the harness can probe, so treat it as authed.
783
+ return { state: "available", version, authenticated: true };
784
+ };
785
+ return {
786
+ instanceId,
787
+ driverKind: DRIVER_KIND,
788
+ displayName: input.displayName,
789
+ enabled: input.enabled,
790
+ get models() {
791
+ return models;
792
+ },
793
+ refreshModels,
794
+ snapshot,
795
+ adapter: {
796
+ provider: DRIVER_KIND,
797
+ capabilities: {
798
+ // model is set per turn via set_model before prompt
799
+ sessionModelSwitch: "in-session",
800
+ // Integrations arrive as stdio MCP servers mounted by the
801
+ // pi-mcp-extension (pi core has no MCP client of its own).
802
+ agentsMcp: true,
803
+ computerMcp: true,
804
+ composioMcp: true,
805
+ phoneMcp: true,
806
+ // Host control (the user's real Mac) rides the pi-native permission
807
+ // card (`ctx.ui.confirm` → extension_ui_request) gated in the
808
+ // extension, so it is offered exactly when the other engines offer
809
+ // it: enabled unless the bot is in full-auto.
810
+ localComputerMcp: true,
811
+ // Images ride pi's native RPC prompt as base64 content blocks, so a
812
+ // vision model can inspect them without a separate file-read tool.
813
+ images: true,
814
+ nativeImageInput: true,
815
+ // Reasoning effort pins pi's thinking level per turn (none → off).
816
+ // xhigh/max only land on models that expose them; pi rejects an
817
+ // unsupported level and the turn keeps the engine default.
818
+ effortLevels: EFFORT_LEVELS,
819
+ },
820
+ sendTurn,
821
+ interruptTurn: async (threadId) => active.get(threadId)?.stop(),
822
+ respondToRequest: async (threadId, requestId, decision) => {
823
+ const entry = active.get(threadId);
824
+ const answer = entry?.pending.get(requestId);
825
+ if (!entry || !answer)
826
+ return "unavailable";
827
+ entry.pending.delete(requestId);
828
+ answer({ behavior: decision.behavior, message: decision.message });
829
+ emit({
830
+ ...base(threadId, entry.turnId),
831
+ requestId,
832
+ type: "request.resolved",
833
+ behavior: decision.behavior,
834
+ source: "user",
835
+ });
836
+ return decision.behavior === "allow" ? "allowed-once" : decision.behavior === "answer" ? "answered" : "rejected";
837
+ },
838
+ hasSession: (threadId) => active.has(threadId),
839
+ stopAll: async () => {
840
+ for (const { stop } of active.values())
841
+ stop();
842
+ },
843
+ onEvent: (listener) => {
844
+ listeners.add(listener);
845
+ return () => listeners.delete(listener);
846
+ },
847
+ },
848
+ dispose: async () => {
849
+ for (const { stop } of active.values())
850
+ stop();
851
+ listeners.clear();
852
+ },
853
+ };
854
+ },
855
+ };