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,1116 @@
1
+ // Codex driver — upstream CodexDriver skeleton over agentcal's
2
+ // drivers/codex.js runtime: the official `codex` CLI headless over its
3
+ // app-server JSON-RPC protocol (newline-delimited JSON on stdio).
4
+ // Completion is a real `turn/completed` notification; approval requests
5
+ // arrive as in-process server→client JSON-RPC requests and surface as
6
+ // canonical request.opened events (answered via respondToRequest — no MCP
7
+ // proxy or unix socket needed, unlike claude). Verified against
8
+ // codex-cli 0.144.4 by agentcal.
9
+ //
10
+ // resumeCursor is the codex thread id; a later turn tries thread/resume
11
+ // and falls back to a fresh thread/start.
12
+ import { existsSync } from "node:fs";
13
+ import { homedir } from "node:os";
14
+ import { stripWorkspaceCredentialEnv } from "../config.js";
15
+ import { computerProxyEnv } from "../container-computer.js";
16
+ import { describeSpawnFailure, execCli, killCliTree, spawnCli } from "../procs.js";
17
+ import { SPAWNED_PROXIES } from "../proxy-paths.js";
18
+ import { isHarnessOwnedMcpEnvName } from "../mcp-registry.js";
19
+ import { newEventId, newId } from "../contracts.js";
20
+ import { decodeCodexSelection, readCodexModelCatalog, STATIC_CODEX_MODELS } from "./codex-catalog.js";
21
+ import { codexLocalProviderArgs } from "./local-inject.js";
22
+ import { augmentedPath, splitCliString } from "../env-path.js";
23
+ import { classifyError, computeBackoff, RETRY_MAX_ATTEMPTS } from "./retry.js";
24
+ import { appendNative } from "./native.js";
25
+ export { decodeCodexSelection, readCodexModelCatalog, STATIC_CODEX_MODELS } from "./codex-catalog.js";
26
+ const DRIVER_KIND = "codex";
27
+ const ASTRA_MODEL_ID = "gpt-6-astra";
28
+ const ASTRA_MIN_CODEX_VERSION = [0, 153, 1];
29
+ /** Whether an installed Codex predates the release that exposes GPT-6 Astra
30
+ * through app-server. Unknown version formats stay quiet: a bad guess should
31
+ * never nag someone whose custom build may already support the model. */
32
+ export function codexPredatesAstra(version) {
33
+ const value = version.trim();
34
+ const match = /\bcodex-cli\s+v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?(?![\d.])\b/i.exec(value)
35
+ ?? /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?$/i.exec(value);
36
+ if (!match)
37
+ return false;
38
+ const installed = match.slice(1, 4).map(Number);
39
+ for (let i = 0; i < ASTRA_MIN_CODEX_VERSION.length; i += 1) {
40
+ if (installed[i] !== ASTRA_MIN_CODEX_VERSION[i]) {
41
+ return installed[i] < ASTRA_MIN_CODEX_VERSION[i];
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+ /** Ask the configured executable to update itself. This matters when the user
47
+ * selected a non-PATH Codex: installing a second global copy would leave
48
+ * OpenMausBot pointing at the old binary. */
49
+ export function codexUpdateCommand(cli, platform = process.platform) {
50
+ if (cli === "codex")
51
+ return "codex update";
52
+ const trimmed = cli.trim();
53
+ // Match resolveCliSpawn's one tokenizer pass, including its exception for
54
+ // real unquoted paths containing spaces. A wrapper's fixed arguments must
55
+ // precede `update`, just as they precede `app-server` and `--version`.
56
+ const tokens = trimmed.includes(" ") && existsSync(trimmed)
57
+ ? [trimmed]
58
+ : splitCliString(trimmed);
59
+ const quote = platform === "win32"
60
+ ? (token) => `'${token.replaceAll("'", "''")}'`
61
+ : (token) => `'${token.replaceAll("'", `'\\''`)}'`;
62
+ const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote).join(" ");
63
+ return platform === "win32" ? `& ${command} update` : `${command} update`;
64
+ }
65
+ function codexAstraUpdate(version, models, cli) {
66
+ if (models.options.some((model) => model.id === ASTRA_MODEL_ID) || !codexPredatesAstra(version)) {
67
+ return undefined;
68
+ }
69
+ return {
70
+ title: "Update Codex for GPT-6 Astra",
71
+ message: "This Codex version predates Astra support. Update it, then refresh models. Astra must also be available to your signed-in ChatGPT account.",
72
+ command: codexUpdateCommand(cli),
73
+ };
74
+ }
75
+ function decodeConfig(raw) {
76
+ const o = (raw ?? {});
77
+ return {
78
+ cli: typeof o.cli === "string" ? o.cli : "codex",
79
+ fullAuto: o.fullAuto === true,
80
+ };
81
+ }
82
+ const QUESTION_TIMEOUT_NOTE = "No answer was given — use your best judgment.";
83
+ const DENY_TIMEOUT_NOTE = "OpenMausBot: nobody answered this permission request in time. Skip this action and finish what you can without it.";
84
+ /** RequestPermissionProfile uses null for permission families that were not
85
+ * requested; GrantedPermissionProfile requires those keys to be absent. */
86
+ function grantedPermissions(raw) {
87
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
88
+ return {};
89
+ return Object.fromEntries(Object.entries(raw).filter(([, value]) => value !== null && value !== undefined));
90
+ }
91
+ function additionalPermissionSummary(permissions, reason) {
92
+ const requested = grantedPermissions(permissions);
93
+ const exact = JSON.stringify(requested);
94
+ const prefix = typeof reason === "string" && reason.trim() ? `${reason.trim()} — ` : "";
95
+ return `${prefix}Requested permissions: ${exact}`;
96
+ }
97
+ const plainRecord = (value) => value && typeof value === "object" && !Array.isArray(value)
98
+ ? value
99
+ : null;
100
+ const containsControlCharacter = (value) => {
101
+ for (const character of value) {
102
+ const code = character.charCodeAt(0);
103
+ if (code <= 0x1f || code === 0x7f)
104
+ return true;
105
+ }
106
+ return false;
107
+ };
108
+ function boundedLabel(value) {
109
+ if (typeof value !== "string")
110
+ return null;
111
+ const label = value.trim();
112
+ return label && label.length <= 160 && !containsControlCharacter(label) ? label : null;
113
+ }
114
+ function ordinaryApprovalValue(value) {
115
+ const normalized = value.trim().toLowerCase();
116
+ if (/session|always|permanent|forever|persistent/.test(normalized))
117
+ return false;
118
+ return normalized === "once" || /^(?:accept|approve|allow)(?:ed|[-_]?once)?$/.test(normalized);
119
+ }
120
+ /** Recognize only schema-backed app-access approvals. Arbitrary MCP forms
121
+ * (credentials, free text, URLs, or required fields without a one-time enum)
122
+ * remain user input and are declined; Full access never fabricates them. */
123
+ function mcpAppApprovalForm(params) {
124
+ const request = plainRecord(params);
125
+ if (!request || request.mode !== "form")
126
+ return null;
127
+ const metadata = plainRecord(request._meta);
128
+ const target = plainRecord(metadata?.target);
129
+ const toolParams = plainRecord(metadata?.tool_params);
130
+ const message = boundedLabel(request.message) ?? "App access requested";
131
+ const appName = [
132
+ metadata?.app_name,
133
+ metadata?.appName,
134
+ metadata?.app,
135
+ target?.app,
136
+ target?.name,
137
+ toolParams?.app_name,
138
+ toolParams?.app,
139
+ metadata?.connector_name,
140
+ metadata?.connectorName,
141
+ ].map(boundedLabel).find(Boolean) ?? message.match(/^Allow ChatGPT to use (.+?)\?$/i)?.[1]?.trim();
142
+ // The application identity is the second half of the discriminator. A
143
+ // required approval-looking enum by itself must not turn an arbitrary form
144
+ // into a permission prompt.
145
+ if (!appName)
146
+ return null;
147
+ const schema = plainRecord(request.requestedSchema);
148
+ const properties = plainRecord(schema?.properties);
149
+ const required = schema?.required;
150
+ if (!properties ||
151
+ !Array.isArray(required) ||
152
+ required.length === 0 ||
153
+ required.length > 8 ||
154
+ !required.every((key) => typeof key === "string" && key.length > 0 && key.length <= 100))
155
+ return null;
156
+ const content = {};
157
+ for (const key of required) {
158
+ const field = plainRecord(properties[key]);
159
+ if (!field)
160
+ return null;
161
+ const enumValues = Array.isArray(field.enum)
162
+ ? field.enum.filter((value) => typeof value === "string")
163
+ : [];
164
+ const oneOfValues = Array.isArray(field.oneOf)
165
+ ? field.oneOf
166
+ .map((option) => boundedLabel(plainRecord(option)?.const))
167
+ .filter((value) => Boolean(value))
168
+ : [];
169
+ const chosen = [...oneOfValues, ...enumValues].find(ordinaryApprovalValue);
170
+ if (!chosen)
171
+ return null;
172
+ content[key] = chosen;
173
+ }
174
+ const tool = boundedLabel(appName) ?? boundedLabel(request.serverName) ?? "app_access";
175
+ return { tool, summary: message, allowResult: { action: "accept", content } };
176
+ }
177
+ /** Codex persists these values on its native thread. Keep them explicit on
178
+ * start, resume, and every turn so switching modes cannot leave a more
179
+ * permissive sandbox/reviewer stuck to the next request. */
180
+ function namedApprovalParams(mode) {
181
+ if (mode === "full") {
182
+ return {
183
+ thread: {
184
+ approvalPolicy: "never",
185
+ approvalsReviewer: "user",
186
+ sandbox: "danger-full-access",
187
+ },
188
+ turn: {
189
+ approvalPolicy: "never",
190
+ approvalsReviewer: "user",
191
+ sandboxPolicy: { type: "dangerFullAccess" },
192
+ },
193
+ };
194
+ }
195
+ return {
196
+ thread: {
197
+ approvalPolicy: "on-request",
198
+ approvalsReviewer: mode === "auto" ? "auto_review" : "user",
199
+ sandbox: "workspace-write",
200
+ },
201
+ turn: {
202
+ approvalPolicy: "on-request",
203
+ approvalsReviewer: mode === "auto" ? "auto_review" : "user",
204
+ sandboxPolicy: { type: "workspaceWrite" },
205
+ },
206
+ };
207
+ }
208
+ function effectiveApprovalPolicy(value) {
209
+ if (value === "untrusted" || value === "on-request" || value === "never")
210
+ return value;
211
+ const granular = plainRecord(plainRecord(value)?.granular);
212
+ if (granular &&
213
+ typeof granular.mcp_elicitations === "boolean" &&
214
+ typeof granular.rules === "boolean" &&
215
+ typeof granular.sandbox_approval === "boolean" &&
216
+ (granular.request_permissions === undefined || typeof granular.request_permissions === "boolean") &&
217
+ (granular.skill_approval === undefined || typeof granular.skill_approval === "boolean")) {
218
+ return {
219
+ granular: {
220
+ mcp_elicitations: granular.mcp_elicitations,
221
+ rules: granular.rules,
222
+ sandbox_approval: granular.sandbox_approval,
223
+ ...(typeof granular.request_permissions === "boolean"
224
+ ? { request_permissions: granular.request_permissions }
225
+ : {}),
226
+ ...(typeof granular.skill_approval === "boolean"
227
+ ? { skill_approval: granular.skill_approval }
228
+ : {}),
229
+ },
230
+ };
231
+ }
232
+ return "on-request";
233
+ }
234
+ function effectiveApprovalsReviewer(value) {
235
+ return value === "auto_review" || value === "guardian_subagent" ? value : "user";
236
+ }
237
+ function legacyCustomApprovalParams(config) {
238
+ const approvalPolicy = effectiveApprovalPolicy(config.approval_policy);
239
+ const approvalsReviewer = effectiveApprovalsReviewer(config.approvals_reviewer);
240
+ const sandbox = config.sandbox_mode === "workspace-write" ||
241
+ config.sandbox_mode === "danger-full-access" ||
242
+ config.sandbox_mode === "read-only"
243
+ ? config.sandbox_mode
244
+ : "read-only";
245
+ let sandboxPolicy;
246
+ if (sandbox === "danger-full-access")
247
+ sandboxPolicy = { type: "dangerFullAccess" };
248
+ else if (sandbox === "read-only")
249
+ sandboxPolicy = { type: "readOnly" };
250
+ else {
251
+ const workspace = config.sandbox_workspace_write && typeof config.sandbox_workspace_write === "object"
252
+ ? config.sandbox_workspace_write
253
+ : {};
254
+ sandboxPolicy = {
255
+ type: "workspaceWrite",
256
+ ...(Array.isArray(workspace.writable_roots) ? { writableRoots: workspace.writable_roots } : {}),
257
+ ...(typeof workspace.network_access === "boolean" ? { networkAccess: workspace.network_access } : {}),
258
+ ...(typeof workspace.exclude_slash_tmp === "boolean" ? { excludeSlashTmp: workspace.exclude_slash_tmp } : {}),
259
+ ...(typeof workspace.exclude_tmpdir_env_var === "boolean"
260
+ ? { excludeTmpdirEnvVar: workspace.exclude_tmpdir_env_var }
261
+ : {}),
262
+ };
263
+ }
264
+ return {
265
+ thread: { approvalPolicy, approvalsReviewer, sandbox },
266
+ turn: { approvalPolicy, approvalsReviewer, sandboxPolicy },
267
+ };
268
+ }
269
+ /** config/read is the app-server's parsed, effective config boundary. Keep the
270
+ * remaining wire validation deliberately small so quoted user profile ids are
271
+ * not accidentally reinterpreted or logged as arbitrary config. */
272
+ function configuredPermissionProfile(config) {
273
+ if (typeof config.default_permissions !== "string")
274
+ return null;
275
+ const profile = config.default_permissions.trim();
276
+ if (!profile || profile.length > 240 || containsControlCharacter(profile))
277
+ return null;
278
+ return profile;
279
+ }
280
+ function customApprovalParams(raw) {
281
+ const config = raw && typeof raw === "object" && !Array.isArray(raw)
282
+ ? raw
283
+ : {};
284
+ const fallback = legacyCustomApprovalParams(config);
285
+ const permissions = configuredPermissionProfile(config);
286
+ const approvalPolicy = effectiveApprovalPolicy(config.approval_policy);
287
+ const approvalsReviewer = effectiveApprovalsReviewer(config.approvals_reviewer);
288
+ // Codex 0.151 profiles define the sandbox, but approval policy remains an
289
+ // independent setting. Reassert both approval fields so a resumed Full
290
+ // thread cannot keep `never`; omit only the mutually-exclusive sandboxes.
291
+ return permissions
292
+ ? {
293
+ thread: { permissions, approvalPolicy, approvalsReviewer },
294
+ turn: { permissions, approvalPolicy, approvalsReviewer },
295
+ fallback,
296
+ }
297
+ : fallback;
298
+ }
299
+ function permissionProfileUnsupported(error) {
300
+ const message = error instanceof Error ? error.message : String(error);
301
+ return /(?:experimental api|invalid params|unknown field|unknown.*permissions|permissions.*(?:unsupported|sandbox)|cannot.*permissions)/i.test(message);
302
+ }
303
+ /** Keep private host paths out of diagnostics while preserving enough of the
304
+ * app-server input shape to debug image delivery. The unmodified request is
305
+ * still written to the provider immediately after this log copy is made. */
306
+ function codexNativeLogMessage(message) {
307
+ if (!message || typeof message !== "object" || Array.isArray(message))
308
+ return message;
309
+ const record = message;
310
+ if (record.method !== "turn/start")
311
+ return message;
312
+ const params = record.params;
313
+ if (!params || typeof params !== "object" || Array.isArray(params))
314
+ return message;
315
+ const input = params.input;
316
+ if (!Array.isArray(input))
317
+ return message;
318
+ return {
319
+ ...record,
320
+ params: {
321
+ ...params,
322
+ input: input.map((item) => {
323
+ if (!item || typeof item !== "object" || Array.isArray(item))
324
+ return item;
325
+ const entry = item;
326
+ return entry.type === "localImage"
327
+ ? { ...entry, path: "[private attachment path omitted]" }
328
+ : item;
329
+ }),
330
+ },
331
+ };
332
+ }
333
+ /** Sanitize provider responses before the native diagnostic tee. Sensitive
334
+ * request ids outlive the request promise, so a response arriving after its
335
+ * timeout is still omitted rather than becoming a secret-bearing orphan. */
336
+ export function codexNativeIncomingLogMessage(message, sensitiveResponseIds) {
337
+ if (message?.id !== undefined && sensitiveResponseIds.has(message.id)) {
338
+ return {
339
+ jsonrpc: message.jsonrpc,
340
+ id: message.id,
341
+ ...(message.error !== undefined
342
+ ? { error: "[config/read error omitted]" }
343
+ : { result: "[effective config omitted]" }),
344
+ };
345
+ }
346
+ if (message?.method === "item/completed" && message.params?.item?.type === "imageGeneration") {
347
+ return {
348
+ ...message,
349
+ params: {
350
+ ...message.params,
351
+ item: {
352
+ ...message.params.item,
353
+ result: `[generated image omitted · ${String(message.params.item.result ?? "").length} base64 chars]`,
354
+ savedPath: undefined,
355
+ },
356
+ },
357
+ };
358
+ }
359
+ return message;
360
+ }
361
+ function mountMcpServer(appServerArgs, env, name, server, preApproved = true) {
362
+ Object.assign(env, server.env);
363
+ const prefix = `mcp_servers.${name}`;
364
+ appServerArgs.push("-c", `${prefix}.command=${JSON.stringify(server.command)}`, "-c", `${prefix}.args=${JSON.stringify(server.args)}`,
365
+ // Values stay in the child environment; argv contains names only so
366
+ // credentials never appear in process listings or diagnostics.
367
+ "-c", `${prefix}.env_vars=${JSON.stringify(Object.keys(server.env))}`);
368
+ // Harness-owned servers are pre-quieted; a user-configured server keeps
369
+ // codex's on-request policy so its tool calls become approval cards.
370
+ if (preApproved) {
371
+ appServerArgs.push("-c", `${prefix}.default_tools_approval_mode="auto"`);
372
+ }
373
+ }
374
+ export const CodexDriver = {
375
+ driverKind: DRIVER_KIND,
376
+ metadata: { displayName: "Codex", supportsMultipleInstances: true },
377
+ install: {
378
+ command: {
379
+ darwin: "npm install -g @openai/codex",
380
+ linux: "npm install -g @openai/codex",
381
+ win32: "npm install -g @openai/codex",
382
+ },
383
+ needsNode: true,
384
+ docsUrl: "https://github.com/openai/codex",
385
+ signInCommand: "codex login",
386
+ },
387
+ models: STATIC_CODEX_MODELS,
388
+ decodeConfig,
389
+ defaultConfig: () => decodeConfig({}),
390
+ async create(input) {
391
+ const { instanceId, config } = input;
392
+ const childEnv = () => {
393
+ const env = {
394
+ ...process.env,
395
+ ...input.environment,
396
+ PATH: augmentedPath(),
397
+ NPM_CONFIG_LOGLEVEL: "error",
398
+ };
399
+ // The CLI owns its own ChatGPT login; a leaked API key silently flips
400
+ // billing to pay-as-you-go (agentcal).
401
+ delete env.OPENAI_API_KEY;
402
+ // The harness process may hold workspace credentials (xai/box/voice
403
+ // keys, env-injected at boot); none of them are this CLI's to see.
404
+ stripWorkspaceCredentialEnv(env);
405
+ return env;
406
+ };
407
+ const catalogEnv = childEnv();
408
+ let models = STATIC_CODEX_MODELS;
409
+ const refreshModels = async () => {
410
+ try {
411
+ const resolved = await readCodexModelCatalog(catalogEnv, fetch, config.cli);
412
+ if (resolved.options.length)
413
+ models = resolved;
414
+ }
415
+ catch {
416
+ // Keep the last usable catalog when a local provider is down.
417
+ }
418
+ };
419
+ await refreshModels();
420
+ const listeners = new Set();
421
+ const active = new Map();
422
+ const emit = (event) => {
423
+ for (const l of [...listeners])
424
+ l(event);
425
+ };
426
+ const base = (threadId, turnId) => ({
427
+ eventId: newEventId(),
428
+ provider: DRIVER_KIND,
429
+ threadId,
430
+ turnId,
431
+ createdAt: new Date().toISOString(),
432
+ });
433
+ const sendTurn = async (turn) => {
434
+ // One driver instance serves many threads. Interrupt state belongs to
435
+ // this turn so activity elsewhere cannot cancel or revive its retry.
436
+ let stopRequested = false;
437
+ const { threadId } = turn;
438
+ // Direct adapter callers predating the per-bot selector retain the
439
+ // instance's legacy fullAuto setting. Harness turns always send an
440
+ // explicit mode, which takes precedence.
441
+ const approvalMode = turn.approvalMode ?? (config.fullAuto ? "full" : "ask");
442
+ for (const [name, server] of Object.entries(turn.integrations?.custom ?? {})) {
443
+ const reserved = Object.keys(server.env).find(isHarnessOwnedMcpEnvName);
444
+ if (reserved) {
445
+ throw new Error(`Custom MCP server “${name}” cannot set reserved environment variable “${reserved}”`);
446
+ }
447
+ }
448
+ let autoAcceptPermissions = approvalMode === "full";
449
+ if (active.has(threadId))
450
+ throw new Error("a turn is already running on this thread");
451
+ const turnId = newId();
452
+ // a retry relaunches the whole app-server; the backoff is scaled down in
453
+ // tests so a fake's transient failures don't stall real seconds
454
+ const retryScale = Number(process.env.FAKE_CODEX_RETRY_SCALE ?? "1");
455
+ const launchAttempt = async (attempt) => {
456
+ const env = childEnv();
457
+ const appServerArgs = ["app-server", ...codexLocalProviderArgs(env, turn.model)];
458
+ if (turn.integrations?.composio) {
459
+ mountMcpServer(appServerArgs, env, "openmausbot_connectors", turn.integrations.composio);
460
+ }
461
+ if (turn.integrations?.agents) {
462
+ mountMcpServer(appServerArgs, env, "agents", turn.integrations.agents);
463
+ }
464
+ if (turn.integrations?.computer) {
465
+ const proxyEnv = computerProxyEnv(turn.integrations.computer);
466
+ mountMcpServer(appServerArgs, env, "computer", {
467
+ command: process.execPath,
468
+ args: [SPAWNED_PROXIES.computer],
469
+ env: {
470
+ ELECTRON_RUN_AS_NODE: "1",
471
+ OGB_BOX_ID: proxyEnv.OGB_BOX_ID ?? "",
472
+ OGB_BOX_TOKEN: proxyEnv.OGB_BOX_TOKEN ?? "",
473
+ // who-is-driving endpoint, so a person taking the wheel in the
474
+ // panel pauses this bot's hands mid-turn
475
+ OMB_CONTROL_URL: proxyEnv.OMB_CONTROL_URL ?? "",
476
+ OMB_CONTROL_TOKEN: proxyEnv.OMB_CONTROL_TOKEN ?? "",
477
+ },
478
+ });
479
+ }
480
+ else if (turn.integrations?.localComputer) {
481
+ // The host daemon and isolated Local VM both arrive as a direct Cua
482
+ // Driver stdio MCP server. Codex sees the same computer tool surface.
483
+ mountMcpServer(appServerArgs, env, "computer", turn.integrations.localComputer);
484
+ }
485
+ if (turn.integrations?.browser) {
486
+ mountMcpServer(appServerArgs, env, "browser", turn.integrations.browser);
487
+ }
488
+ for (const [name, server] of Object.entries(turn.integrations?.custom ?? {})) {
489
+ mountMcpServer(appServerArgs, env, name, server, false);
490
+ }
491
+ if (turn.integrations?.phone) {
492
+ const bridge = turn.integrations.phone;
493
+ Object.assign(env, bridge.env);
494
+ const prefix = "mcp_servers.openmausbot_phone";
495
+ appServerArgs.push("-c", `${prefix}.command=${JSON.stringify(bridge.command)}`, "-c", `${prefix}.args=${JSON.stringify(bridge.args)}`, "-c", `${prefix}.env_vars=${JSON.stringify(Object.keys(bridge.env))}`, "-c", `${prefix}.default_tools_approval_mode="auto"`);
496
+ }
497
+ const child = spawnCli(config.cli, appServerArgs, {
498
+ cwd: turn.cwd ?? homedir(),
499
+ env,
500
+ stdio: ["pipe", "pipe", "pipe"],
501
+ });
502
+ let abandoned = false;
503
+ const state = {
504
+ settled: false,
505
+ lastText: "",
506
+ sawStreamDelta: false,
507
+ // codex reports token usage as a running THREAD total; the harness
508
+ // wants this turn's figure, so the last report is banked on settle
509
+ usage: undefined,
510
+ };
511
+ const asks = new Map();
512
+ let nextId = 1;
513
+ const sensitiveResponseIds = new Set();
514
+ const rpcPending = new Map();
515
+ const send = (obj) => {
516
+ try {
517
+ child.stdin.write(JSON.stringify(obj) + "\n");
518
+ }
519
+ catch { }
520
+ appendNative(threadId, {
521
+ dir: "out",
522
+ source: "codex.app-server",
523
+ msg: codexNativeLogMessage(obj),
524
+ });
525
+ };
526
+ const request = (method, params, timeoutMs = 60_000) => new Promise((resolve, reject) => {
527
+ const id = nextId++;
528
+ if (method === "config/read")
529
+ sensitiveResponseIds.add(id);
530
+ // a wedged app-server can accept stdin and never reply; without this
531
+ // the handshake await hangs forever and the bot stays busy for good
532
+ const timer = setTimeout(() => {
533
+ if (rpcPending.delete(id))
534
+ reject(new Error(`codex ${method} timed out after ${timeoutMs}ms`));
535
+ }, timeoutMs);
536
+ if (typeof timer.unref === "function")
537
+ timer.unref();
538
+ rpcPending.set(id, {
539
+ resolve: (v) => {
540
+ clearTimeout(timer);
541
+ resolve(v);
542
+ },
543
+ reject: (e) => {
544
+ clearTimeout(timer);
545
+ reject(e);
546
+ },
547
+ });
548
+ send({ jsonrpc: "2.0", id, method, params });
549
+ });
550
+ const stop = () => {
551
+ stopRequested = true;
552
+ killCliTree(child);
553
+ };
554
+ const settle = (ok, stopReason) => {
555
+ if (state.settled)
556
+ return;
557
+ state.settled = true;
558
+ for (const finish of [...asks.values()])
559
+ finish("deny", "OpenMausBot: the turn ended", "system");
560
+ for (const p of rpcPending.values())
561
+ p.reject(new Error("turn settled"));
562
+ rpcPending.clear();
563
+ active.delete(threadId);
564
+ emit({ ...base(threadId, turnId), type: "turn.completed", ok, stopReason, cost: null, ...(state.usage ? { usage: state.usage } : {}) });
565
+ stop(); // the app-server never exits on its own
566
+ };
567
+ // server→client approval request → canonical request.opened
568
+ // Host-scope tagging mirrors claude.ts: when this turn mounts the real
569
+ // Mac (not a VM), every card carries approvalScope so the harness's
570
+ // local-computer-block backstop applies to remembered always-allows.
571
+ const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
572
+ const handleServerRequest = (msg) => {
573
+ const method = msg.method;
574
+ const params = msg.params ?? {};
575
+ const legacy = method === "execCommandApproval" || method === "applyPatchApproval";
576
+ const isMcpElicitation = method === "mcpServer/elicitation/request";
577
+ const isLegacyMcpPermission = method === "mcpServer/elicitation/request" &&
578
+ params?._meta?.codex_approval_kind === "mcp_tool_call";
579
+ const mcpAppApproval = isMcpElicitation ? mcpAppApprovalForm(params) : null;
580
+ const isMcpPermission = isLegacyMcpPermission || mcpAppApproval !== null;
581
+ const isQuestion = method === "item/tool/requestUserInput";
582
+ const isAdditionalPermission = method === "item/permissions/requestApproval";
583
+ const isPermission = legacy || isMcpPermission || isAdditionalPermission ||
584
+ method === "item/commandExecution/requestApproval" ||
585
+ method === "item/fileChange/requestApproval";
586
+ // A normal MCP elicitation is a form or URL asking for real user input,
587
+ // not a permission. We cannot safely synthesize its structured answer.
588
+ // Unknown future server requests also fail closed instead of being
589
+ // mistaken for commands and accepted by Full Access.
590
+ if (!isQuestion && !isPermission) {
591
+ if (isMcpElicitation) {
592
+ send({ jsonrpc: "2.0", id: msg.id, result: { action: "decline" } });
593
+ }
594
+ else {
595
+ send({
596
+ jsonrpc: "2.0",
597
+ id: msg.id,
598
+ error: { code: -32601, message: `Unsupported server request: ${method}` },
599
+ });
600
+ }
601
+ return;
602
+ }
603
+ const mcpTool = isLegacyMcpPermission
604
+ ? String(params.message ?? "").match(/tool "([^"]+)"/)?.[1]
605
+ : undefined;
606
+ const tool = mcpAppApproval
607
+ ? mcpAppApproval.tool
608
+ : isLegacyMcpPermission
609
+ ? (mcpTool ?? "mcp")
610
+ : isAdditionalPermission
611
+ ? "permissions"
612
+ : method === "item/fileChange/requestApproval" || method === "applyPatchApproval"
613
+ ? "edit"
614
+ : isQuestion
615
+ ? "ask_user"
616
+ : "shell";
617
+ const permissionResult = (allow) => isMcpPermission
618
+ ? allow
619
+ ? (mcpAppApproval?.allowResult ?? { action: "accept", content: {} })
620
+ : { action: "decline" }
621
+ : isAdditionalPermission
622
+ ? { permissions: allow ? grantedPermissions(params.permissions) : {}, scope: "turn" }
623
+ : { decision: allow ? (legacy ? "approved" : "accept") : legacy ? "denied" : "decline" };
624
+ if (autoAcceptPermissions && isPermission) {
625
+ return send({
626
+ jsonrpc: "2.0",
627
+ id: msg.id,
628
+ result: permissionResult(true),
629
+ });
630
+ }
631
+ const requestId = newId();
632
+ const summary = isAdditionalPermission
633
+ ? additionalPermissionSummary(params.permissions, params.reason)
634
+ : isMcpPermission
635
+ ? (mcpAppApproval?.summary ?? (typeof params.message === "string" ? params.message : "MCP access requested"))
636
+ : typeof params.command === "string"
637
+ ? params.command
638
+ : Array.isArray(params.questions)
639
+ ? params.questions.map((q) => q.question ?? q.header).filter(Boolean).join(" · ")
640
+ : typeof params.reason === "string"
641
+ ? params.reason
642
+ : tool;
643
+ const choices = isQuestion
644
+ ? (params.questions?.[0]?.options ?? []).map((o) => o.label).slice(0, 5)
645
+ : undefined;
646
+ const finish = (behavior, message, source = "user") => {
647
+ if (!asks.delete(requestId))
648
+ return;
649
+ clearTimeout(timer);
650
+ if (isQuestion) {
651
+ const answers = {};
652
+ for (const q of Array.isArray(params.questions) ? params.questions : []) {
653
+ answers[q.id] = { answers: [message || QUESTION_TIMEOUT_NOTE] };
654
+ }
655
+ send({ jsonrpc: "2.0", id: msg.id, result: { answers } });
656
+ }
657
+ else {
658
+ send({
659
+ jsonrpc: "2.0",
660
+ id: msg.id,
661
+ result: permissionResult(behavior === "allow"),
662
+ });
663
+ }
664
+ emit({ ...base(threadId, turnId), type: "request.resolved", requestId, behavior, source });
665
+ };
666
+ const timer = setTimeout(() => (isQuestion ? finish("answer", QUESTION_TIMEOUT_NOTE, "timeout") : finish("deny", DENY_TIMEOUT_NOTE, "timeout")), 15 * 60_000);
667
+ timer.unref?.();
668
+ asks.set(requestId, finish);
669
+ emit({
670
+ ...base(threadId, turnId),
671
+ type: "request.opened",
672
+ requestId,
673
+ requestType: isQuestion ? "question" : "permission",
674
+ tool,
675
+ summary,
676
+ choices,
677
+ approvalScope: controlsHost ? "local-computer" : undefined,
678
+ requiresExplicitApproval: isAdditionalPermission || undefined,
679
+ });
680
+ };
681
+ const handleNotification = (msg) => {
682
+ const p = msg.params ?? {};
683
+ switch (msg.method) {
684
+ // token-level chat text; the item/completed frame follows with the
685
+ // whole message, so its delta is only a fallback when none streamed
686
+ case "item/agentMessage/delta": {
687
+ const delta = typeof p.delta === "string" ? p.delta : "";
688
+ if (delta) {
689
+ state.sawStreamDelta = true;
690
+ emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
691
+ }
692
+ break;
693
+ }
694
+ case "item/reasoning/textDelta":
695
+ case "item/reasoning/summaryTextDelta": {
696
+ const delta = typeof p.delta === "string" ? p.delta : "";
697
+ if (delta)
698
+ emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "reasoning_text", delta });
699
+ break;
700
+ }
701
+ case "item/started": {
702
+ const item = p.item ?? {};
703
+ const title = item.type === "commandExecution"
704
+ ? String(item.command ?? "shell")
705
+ : item.type === "fileChange"
706
+ ? "edit"
707
+ : item.type === "mcpToolCall"
708
+ ? (item.tool ?? item.name ?? "mcp")
709
+ : item.type === "webSearch"
710
+ ? "web_search"
711
+ : null;
712
+ if (title)
713
+ emit({ ...base(threadId, turnId), type: "item.started", itemType: "tool", itemId: item.id, title });
714
+ break;
715
+ }
716
+ case "item/completed": {
717
+ const item = p.item ?? {};
718
+ if (item.type === "agentMessage") {
719
+ if (item.text?.trim()) {
720
+ state.lastText = item.text;
721
+ if (!state.sawStreamDelta) {
722
+ emit({ ...base(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta: item.text });
723
+ }
724
+ state.sawStreamDelta = false;
725
+ emit({ ...base(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: item.text });
726
+ }
727
+ }
728
+ else if (item.type === "imageGeneration" && item.status !== "failed") {
729
+ // Current Codex app-server (the same schema consumed by T3
730
+ // Code) returns the generated raster as base64 `result` and
731
+ // may also expose a local `savedPath`. Use bytes, never the
732
+ // provider-owned path: the harness will validate and copy
733
+ // them into its private attachment store.
734
+ if (typeof item.result === "string" && item.result.trim()) {
735
+ emit({
736
+ ...base(threadId, turnId),
737
+ type: "item.completed",
738
+ itemType: "assistant_image",
739
+ itemId: item.id,
740
+ data: item.result,
741
+ alt: typeof item.revisedPrompt === "string" ? item.revisedPrompt : undefined,
742
+ });
743
+ }
744
+ }
745
+ else if (["commandExecution", "fileChange", "mcpToolCall", "webSearch"].includes(item.type)) {
746
+ emit({
747
+ ...base(threadId, turnId),
748
+ type: "item.completed",
749
+ itemType: "tool",
750
+ itemId: item.id,
751
+ ok: item.status !== "failed" && item.status !== "declined",
752
+ });
753
+ }
754
+ else if (item.type === "reasoning") {
755
+ emit({ ...base(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
756
+ }
757
+ break;
758
+ }
759
+ case "thread/tokenUsage/updated": {
760
+ // `last` is the most recent turn when the server sends it;
761
+ // `total` is the thread so far — a fresh app-server per turn
762
+ // makes that this turn's figure too
763
+ const turnUsage = p.tokenUsage?.last ?? p.tokenUsage?.total;
764
+ // codex's inputTokens already includes cachedInputTokens; the
765
+ // cached share is carried alongside so the UI can say how much
766
+ // of a turn was context re-read rather than new text
767
+ if (turnUsage) {
768
+ state.usage = {
769
+ input: turnUsage.inputTokens ?? 0,
770
+ output: turnUsage.outputTokens ?? 0,
771
+ ...(typeof turnUsage.cachedInputTokens === "number"
772
+ ? { cachedInput: turnUsage.cachedInputTokens }
773
+ : {}),
774
+ };
775
+ }
776
+ const t = p.tokenUsage?.total;
777
+ if (t) {
778
+ emit({
779
+ ...base(threadId, turnId),
780
+ type: "thread.token-usage.updated",
781
+ input: t.inputTokens ?? 0,
782
+ output: t.outputTokens ?? 0,
783
+ ...(typeof t.cachedInputTokens === "number"
784
+ ? { cachedInput: t.cachedInputTokens }
785
+ : {}),
786
+ });
787
+ }
788
+ break;
789
+ }
790
+ case "turn/completed": {
791
+ const t = p.turn ?? {};
792
+ settle(t.status === "completed", t.status === "completed" ? null : (t.error?.message ?? t.status ?? "failed"));
793
+ break;
794
+ }
795
+ case "error":
796
+ // shape drift: 0.144 sends {message}, 0.139 nests it under
797
+ // {error:{message}} — surface either (agentcal armor)
798
+ {
799
+ const message = p.message ?? p.error?.message;
800
+ if (message)
801
+ emit({ ...base(threadId, turnId), type: "runtime.error", message: String(message).slice(0, 400) });
802
+ }
803
+ break;
804
+ }
805
+ };
806
+ let buf = "";
807
+ // decode as UTF-8 across chunk boundaries — a raw `buf += chunk` splits
808
+ // multibyte characters that straddle two reads and corrupts the text
809
+ child.stdout.setEncoding("utf8");
810
+ child.stdout.on("data", (chunk) => {
811
+ buf += chunk;
812
+ let nl;
813
+ while ((nl = buf.indexOf("\n")) !== -1) {
814
+ const line = buf.slice(0, nl);
815
+ buf = buf.slice(nl + 1);
816
+ if (!line.trim())
817
+ continue;
818
+ let msg;
819
+ try {
820
+ msg = JSON.parse(line);
821
+ }
822
+ catch {
823
+ continue;
824
+ }
825
+ const loggedMessage = codexNativeIncomingLogMessage(msg, sensitiveResponseIds);
826
+ appendNative(threadId, { dir: "in", source: "codex.app-server", msg: loggedMessage });
827
+ if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) {
828
+ const pend = rpcPending.get(msg.id);
829
+ if (pend) {
830
+ rpcPending.delete(msg.id);
831
+ if (msg.error)
832
+ pend.reject(new Error(msg.error.message ?? JSON.stringify(msg.error)));
833
+ else
834
+ pend.resolve(msg.result);
835
+ }
836
+ }
837
+ else if (msg.id !== undefined && msg.method) {
838
+ handleServerRequest(msg);
839
+ }
840
+ else if (msg.method) {
841
+ handleNotification(msg);
842
+ }
843
+ }
844
+ });
845
+ let stderr = "";
846
+ child.stderr.on("data", (c) => {
847
+ stderr += c;
848
+ if (stderr.length > 8192)
849
+ stderr = stderr.slice(-8192);
850
+ });
851
+ child.on("error", (e) => {
852
+ if (abandoned)
853
+ return;
854
+ emit({ ...base(threadId, turnId), type: "runtime.error", ...describeSpawnFailure(e, config.cli) });
855
+ settle(false, "spawn_error");
856
+ });
857
+ child.on("close", (code) => {
858
+ if (abandoned)
859
+ return;
860
+ if (!state.settled) {
861
+ emit({
862
+ ...base(threadId, turnId),
863
+ type: "runtime.error",
864
+ message: `codex exited ${code} before turn/completed${stderr ? `: ${stderr.trim().slice(-300)}` : ""}`,
865
+ });
866
+ settle(false, "exit_before_result");
867
+ }
868
+ });
869
+ active.set(threadId, { stop, turnId, asks });
870
+ // Relaunching the app-server is still the same logical turn. Keep the
871
+ // active process current on every attempt, but announce the turn once.
872
+ if (attempt === 0)
873
+ emit({ ...base(threadId, turnId), type: "turn.started" });
874
+ // handshake + kickoff; a transient failure (5xx/overloaded/reset) gets
875
+ // one relaunch of the whole app-server after backoff — but only when
876
+ // nothing streamed yet, and never for auth/shape errors or interrupts
877
+ try {
878
+ await request("initialize", {
879
+ clientInfo: { name: "openmausbot", version: "1" },
880
+ // Named permission profiles are an experimental app-server field in
881
+ // Codex 0.151. Negotiate them explicitly; older servers ignore this
882
+ // capability and remain on the legacy Custom fallback below.
883
+ capabilities: { experimentalApi: true },
884
+ });
885
+ send({ jsonrpc: "2.0", method: "initialized", params: {} });
886
+ let approvalParams;
887
+ if (approvalMode === "custom") {
888
+ // config/read returns the effective global + project config for this
889
+ // cwd. Reasserting those values is essential: simply omitting them
890
+ // on a resumed thread would keep the previous named mode sticky.
891
+ let effectiveConfig;
892
+ try {
893
+ const configured = await request("config/read", {
894
+ cwd: turn.cwd ?? homedir(),
895
+ includeLayers: false,
896
+ });
897
+ effectiveConfig = configured?.config;
898
+ }
899
+ catch {
900
+ // Older app-servers and transient failures cannot prove the
901
+ // user's configured sandbox. Fall back to interactive read-only
902
+ // instead of inheriting stale Full or silently broadening a
903
+ // possibly read-only config to workspace write.
904
+ effectiveConfig = {};
905
+ }
906
+ approvalParams = customApprovalParams(effectiveConfig);
907
+ }
908
+ else {
909
+ approvalParams = namedApprovalParams(approvalMode);
910
+ }
911
+ // Codex's `never` means "do not ask to escalate", not "grant every
912
+ // requested permission". Only the user's explicit OpenMausBot Full
913
+ // mode may synthesize approvals; Custom must preserve the sandbox
914
+ // boundary from config.toml (for example never + read-only).
915
+ autoAcceptPermissions = approvalMode === "full";
916
+ const cursor = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
917
+ let codexThreadId = null;
918
+ let startedModel = null;
919
+ if (cursor) {
920
+ try {
921
+ const resumed = await request("thread/resume", {
922
+ threadId: cursor,
923
+ ...approvalParams.thread,
924
+ });
925
+ codexThreadId = resumed?.thread?.id ?? cursor;
926
+ }
927
+ catch (error) {
928
+ if (approvalParams.fallback && permissionProfileUnsupported(error)) {
929
+ // A server can understand config/read before it understands the
930
+ // profile selector. Retry the same resume safely rather than
931
+ // losing the native thread or inheriting its previous mode.
932
+ approvalParams = approvalParams.fallback;
933
+ try {
934
+ const resumed = await request("thread/resume", {
935
+ threadId: cursor,
936
+ ...approvalParams.thread,
937
+ });
938
+ codexThreadId = resumed?.thread?.id ?? cursor;
939
+ }
940
+ catch {
941
+ /* thread gone or resume unsupported — start fresh below */
942
+ }
943
+ }
944
+ /* thread gone or resume unsupported — start fresh below */
945
+ }
946
+ }
947
+ if (!codexThreadId) {
948
+ const selection = decodeCodexSelection(turn.model);
949
+ const startThread = () => request("thread/start", {
950
+ cwd: turn.cwd ?? homedir(),
951
+ model: selection.model,
952
+ ...(selection.modelProvider ? { modelProvider: selection.modelProvider } : {}),
953
+ ...approvalParams.thread,
954
+ ephemeral: false,
955
+ });
956
+ let started;
957
+ try {
958
+ started = await startThread();
959
+ }
960
+ catch (error) {
961
+ if (!approvalParams.fallback || !permissionProfileUnsupported(error))
962
+ throw error;
963
+ approvalParams = approvalParams.fallback;
964
+ started = await startThread();
965
+ }
966
+ codexThreadId = started?.thread?.id ?? null;
967
+ startedModel = started?.model ?? null;
968
+ }
969
+ emit({ ...base(threadId, turnId), type: "session.started", sessionId: codexThreadId, model: startedModel ?? turn.model ?? null });
970
+ const promptText = turn.system ? `${turn.system}\n\n${turn.text}` : turn.text;
971
+ const turnInput = [
972
+ ...(promptText ? [{ type: "text", text: promptText }] : []),
973
+ ...(turn.images ?? []).map((image) => ({ type: "localImage", path: image.path })),
974
+ ];
975
+ const startTurn = () => request("turn/start", {
976
+ threadId: codexThreadId,
977
+ input: turnInput,
978
+ ...approvalParams.turn,
979
+ // Spread, not `effort: turn.effort ?? null`. Probed against
980
+ // codex-cli 0.146.0: null is indistinguishable from an absent key
981
+ // — both leave the thread's current effort alone, emitting no
982
+ // thread/settings/updated, and thread/resume reads the old value
983
+ // back. The app-server offers no way to clear a level either:
984
+ // "" is rejected outright and thread/start takes no effort at
985
+ // all. So a thread keeps the last level it was sent until it is
986
+ // sent another, and choosing Default lands on the bot's next new
987
+ // thread rather than the current one.
988
+ ...(turn.effort ? { effort: turn.effort } : {}),
989
+ });
990
+ try {
991
+ await startTurn();
992
+ }
993
+ catch (error) {
994
+ if (!approvalParams.fallback || !permissionProfileUnsupported(error))
995
+ throw error;
996
+ approvalParams = approvalParams.fallback;
997
+ await startTurn();
998
+ }
999
+ }
1000
+ catch (e) {
1001
+ const failure = e instanceof Error ? e : { text: String(e) };
1002
+ const message = e instanceof Error ? e.message : String(e);
1003
+ const needsAuth = /(?:\b401\b|unauthorized|missing bearer|authentication required)/i.test(message);
1004
+ const verdict = classifyError(failure);
1005
+ if (!state.settled && !needsAuth && verdict.transient && attempt < RETRY_MAX_ATTEMPTS - 1 && state.sawStreamDelta === false) {
1006
+ const delayMs = computeBackoff(attempt);
1007
+ attempt++;
1008
+ emit({
1009
+ ...base(threadId, turnId),
1010
+ type: "turn.retrying",
1011
+ attempt,
1012
+ delayMs,
1013
+ reason: verdict.reason,
1014
+ });
1015
+ // This app-server never exits by itself. Retire the failed attempt
1016
+ // and silence its late handlers before the replacement launches.
1017
+ abandoned = true;
1018
+ killCliTree(child);
1019
+ await new Promise((resolve) => {
1020
+ const timer = setTimeout(resolve, Math.max(1, Math.round(delayMs * retryScale)));
1021
+ timer.unref?.();
1022
+ });
1023
+ if (!stopRequested) {
1024
+ void launchAttempt(attempt).catch(() => { });
1025
+ }
1026
+ else {
1027
+ settle(false, "interrupted");
1028
+ }
1029
+ return;
1030
+ }
1031
+ if (!state.settled) {
1032
+ emit({
1033
+ ...base(threadId, turnId),
1034
+ type: "runtime.error",
1035
+ message,
1036
+ ...(needsAuth ? { setup: true } : {}),
1037
+ });
1038
+ settle(false, needsAuth ? "auth_required" : "rpc_error");
1039
+ }
1040
+ }
1041
+ };
1042
+ void launchAttempt(0).catch(() => { });
1043
+ return { turnId };
1044
+ };
1045
+ const snapshot = async () => {
1046
+ const env = childEnv();
1047
+ const version = await new Promise((resolve) => {
1048
+ execCli(config.cli, ["--version"], { timeout: 8000, env }, (err, stdout) => resolve(err ? null : stdout.trim()));
1049
+ });
1050
+ if (!version)
1051
+ return { state: "unavailable", reason: `\`${config.cli}\` CLI not found` };
1052
+ const authenticated = await new Promise((resolve) => {
1053
+ execCli(config.cli, ["login", "status"], { timeout: 8000, env }, (err, stdout, stderr) => resolve(!err && /^logged in\b/im.test(`${stdout}\n${stderr ?? ""}`)));
1054
+ });
1055
+ // childEnv drops OPENAI_API_KEY on purpose — turns run on the ChatGPT login
1056
+ return {
1057
+ state: "available",
1058
+ version,
1059
+ authenticated,
1060
+ update: codexAstraUpdate(version, models, config.cli),
1061
+ billing: "subscription",
1062
+ };
1063
+ };
1064
+ return {
1065
+ instanceId,
1066
+ driverKind: DRIVER_KIND,
1067
+ displayName: input.displayName,
1068
+ enabled: input.enabled,
1069
+ get models() {
1070
+ return models;
1071
+ },
1072
+ refreshModels,
1073
+ snapshot,
1074
+ adapter: {
1075
+ provider: DRIVER_KIND,
1076
+ capabilities: {
1077
+ sessionModelSwitch: "unsupported",
1078
+ computerMcp: true,
1079
+ localComputerMcp: true,
1080
+ composioMcp: true,
1081
+ agentsMcp: true,
1082
+ customMcp: true,
1083
+ phoneMcp: true,
1084
+ browserMcp: true,
1085
+ images: true,
1086
+ nativeImageInput: true,
1087
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
1088
+ },
1089
+ sendTurn,
1090
+ interruptTurn: async (threadId) => active.get(threadId)?.stop(),
1091
+ respondToRequest: async (threadId, requestId, decision) => {
1092
+ const turn = active.get(threadId);
1093
+ const finish = turn?.asks.get(requestId);
1094
+ if (!finish)
1095
+ return "unavailable"; // settled, timed out, or turn gone
1096
+ finish(decision.behavior, decision.message, "user");
1097
+ return decision.behavior === "allow" ? "allowed-once" : decision.behavior === "answer" ? "answered" : "rejected";
1098
+ },
1099
+ hasSession: (threadId) => active.has(threadId),
1100
+ stopAll: async () => {
1101
+ for (const { stop } of active.values())
1102
+ stop();
1103
+ },
1104
+ onEvent: (listener) => {
1105
+ listeners.add(listener);
1106
+ return () => listeners.delete(listener);
1107
+ },
1108
+ },
1109
+ dispose: async () => {
1110
+ for (const { stop } of active.values())
1111
+ stop();
1112
+ listeners.clear();
1113
+ },
1114
+ };
1115
+ },
1116
+ };