openmausbot 0.1.85 → 0.1.87

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 (262) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  3. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  4. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  5. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  6. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  7. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  8. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  11. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  12. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  13. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  14. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  17. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  18. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  19. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  20. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  21. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  22. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  23. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  24. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  25. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  26. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  27. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  28. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  29. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  30. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  31. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  32. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  33. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  34. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  35. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  36. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  37. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  38. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  44. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  45. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  46. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  47. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  48. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  49. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  50. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  51. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  52. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  53. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  54. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  55. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  56. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  57. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  58. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  59. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  60. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  61. package/dist/assets/abnfDiagram-O67JEVCF-BTiuL79F.js +1 -0
  62. package/dist/assets/arc-C88Blc2v.js +1 -0
  63. package/dist/assets/architectureDiagram-NJMV4G6O-BZsJIZrb.js +36 -0
  64. package/dist/assets/blockDiagram-BEXU5L5S-CVeMe9Ah.js +140 -0
  65. package/dist/assets/c4Diagram-YGBWAQC7-Cedk3Bhx.js +38 -0
  66. package/dist/assets/channel-taCNRlUU.js +1 -0
  67. package/dist/assets/chunk-2Q5K7J3B-D-VRqzTB.js +1 -0
  68. package/dist/assets/chunk-3FUC2YCW-BChov01Y.js +2 -0
  69. package/dist/assets/chunk-5VM5RSS4-C-TfwvHy.js +15 -0
  70. package/dist/assets/chunk-GTNCS2PH-Db7CGj5Y.js +88 -0
  71. package/dist/assets/chunk-GWA4HPMP-CNvIfqKT.js +1 -0
  72. package/dist/assets/chunk-JWPE2WC7-C_bEp5tq.js +1 -0
  73. package/dist/assets/chunk-NETBCI7D-Coi4BOls.js +1 -0
  74. package/dist/assets/chunk-WEXAMYUT-CzylEuL3.js +1 -0
  75. package/dist/assets/chunk-XXDRQBXY-m7p7ztaD.js +1 -0
  76. package/dist/assets/classDiagram-v2-NBCMYWYE-BMy6o9x2.js +217 -0
  77. package/dist/assets/cose-bilkent-JH36ORCC-BDQ__0_J.js +1 -0
  78. package/dist/assets/cynefin-OW5HDTMX-Djv4ghUb.js +166 -0
  79. package/dist/assets/cynefinDiagram-VND7K2PF-BULrlGYH.js +62 -0
  80. package/dist/assets/cytoscape.esm-BB4DxJjf.js +321 -0
  81. package/dist/assets/dagre-6A5THRUB-D-jrT_hU.js +4 -0
  82. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  83. package/dist/assets/diagram-22UHCM2B-p2UhQmfL.js +200 -0
  84. package/dist/assets/diagram-3UASUU5V-DHvGWZ7q.js +24 -0
  85. package/dist/assets/diagram-ATOU4E4O-ChhKDot-.js +3 -0
  86. package/dist/assets/diagram-CDSNMT55-B-T7A43R.js +30 -0
  87. package/dist/assets/diagram-MLGK6HIB-CJm8ENNY.js +24 -0
  88. package/dist/assets/diagram-MPIPVDR6-510tlza8.js +41 -0
  89. package/dist/assets/ebnfDiagram-ZINNZB2B-PkvRPHel.js +1 -0
  90. package/dist/assets/elk-276RUBZZ-BVBVPoX3.js +27 -0
  91. package/dist/assets/erDiagram-OPXOYQCR-BgYMVm7L.js +99 -0
  92. package/dist/assets/flowDiagram-KWPJA3E3-BGqsnc_3.js +213 -0
  93. package/dist/assets/ganttDiagram-FUAMR5RP-D_-UeReH.js +292 -0
  94. package/dist/assets/gitGraphDiagram-X574FWY7-D0uooONx.js +106 -0
  95. package/dist/assets/graph-D8UmR1vK.js +1 -0
  96. package/dist/assets/index-BwdMAQ6p.css +1 -0
  97. package/dist/assets/index-ByJennr7.js +581 -0
  98. package/dist/assets/{index-CTNNoqSJ.js → index-zRsJERGJ.js} +1 -1
  99. package/dist/assets/infoDiagram-VRGFBTTK-B9e1z77f.js +2 -0
  100. package/dist/assets/init-Gi6I4Gst.js +1 -0
  101. package/dist/assets/ishikawaDiagram-OU5B5YK6-BB4vNwvD.js +70 -0
  102. package/dist/assets/journeyDiagram-ZHPQQLJL-DpkosOTB.js +139 -0
  103. package/dist/assets/kanban-definition-PNTS6WVX-DbDP4kUu.js +89 -0
  104. package/dist/assets/linear-DBJGPmjT.js +1 -0
  105. package/dist/assets/mermaid.core-DWPRi-ue.js +311 -0
  106. package/dist/assets/mindmap-definition-NLK3R4M7-nszBbJmx.js +96 -0
  107. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  108. package/dist/assets/pegDiagram-GJSIUBJH-CWykJWu_.js +1 -0
  109. package/dist/assets/pieDiagram-5QR66LMP-g3Da-Vnv.js +39 -0
  110. package/dist/assets/quadrantDiagram-O4NWA36T-Wzrlpzgh.js +7 -0
  111. package/dist/assets/railroadDiagram-XR7U4H2S-moHTFMC0.js +1 -0
  112. package/dist/assets/requirementDiagram-PLB6GJNP-CwfXdFH8.js +84 -0
  113. package/dist/assets/sankeyDiagram-IPEJSGJF-3ZROYvnq.js +40 -0
  114. package/dist/assets/sequenceDiagram-PO4LG4MO-E1TBebP8.js +169 -0
  115. package/dist/assets/sizeCapture-INFHLROL-DHKEU5pu.js +1 -0
  116. package/dist/assets/stateDiagram-v2-GCMORJYK-BguEj09D.js +291 -0
  117. package/dist/assets/swimlanes-2SLR337P-C7IDYnhd.js +1 -0
  118. package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +8 -0
  119. package/dist/assets/timeline-definition-EJHVYXUP-DCW-F5PW.js +120 -0
  120. package/dist/assets/usecaseDiagram-POWQR4AR-xBMFtWYV.js +370 -0
  121. package/dist/assets/vennDiagram-UO4OBE2U-B5nIkuH8.js +34 -0
  122. package/dist/assets/wardleyDiagram-VNRHLVJA-DYFAhdbr.js +78 -0
  123. package/dist/assets/xychartDiagram-PMCCYNJV-DeXwX6Cz.js +7 -0
  124. package/dist/index.html +2 -2
  125. package/dist-server/browser-proxy.js +2 -1
  126. package/dist-server/container-mcp.js +1047 -173
  127. package/dist-server/drivers/agents-proxy.js +16121 -1504
  128. package/dist-server/drivers/phone-proxy.js +33 -2
  129. package/dist-server/enterprise/LICENSE +32 -0
  130. package/dist-server/enterprise/server/index.js +5 -5
  131. package/dist-server/index.js +16134 -7446
  132. package/dist-server/local-computer-proxy.js +3 -3
  133. package/dist-server/local-computer.js +53 -17
  134. package/dist-server/mcp-server.js +3 -1
  135. package/dist-server/openmausbot.js +9353 -6124
  136. package/dist-server/pair-cli.js +9353 -6124
  137. package/dist-server/permission-proxy.js +29 -9
  138. package/dist-server/server/admin-activity.js +410 -0
  139. package/dist-server/server/attachments.js +55 -0
  140. package/dist-server/server/auto-approve.js +4 -3
  141. package/dist-server/server/auto-vm-claims.js +1 -0
  142. package/dist-server/server/bot-package.js +18 -292
  143. package/dist-server/server/bot-profile.js +5 -17
  144. package/dist-server/server/bot-visibility.js +462 -0
  145. package/dist-server/server/box-create-idempotency.js +3 -3
  146. package/dist-server/server/box.js +59 -56
  147. package/dist-server/server/browser-proxy.js +3 -2
  148. package/dist-server/server/browser-runtime.js +38 -2
  149. package/dist-server/server/channel-queue.js +2 -0
  150. package/dist-server/server/cli-prompts.js +24 -2
  151. package/dist-server/server/cli.js +81 -12
  152. package/dist-server/server/command-allowlist.js +154 -0
  153. package/dist-server/server/composio.js +49 -11
  154. package/dist-server/server/computer-wait.js +22 -9
  155. package/dist-server/server/config.js +133 -17
  156. package/dist-server/server/connector-verdict.js +141 -0
  157. package/dist-server/server/container-computer.js +1 -1
  158. package/dist-server/server/decision-log.js +209 -48
  159. package/dist-server/server/default-model-selection.js +29 -3
  160. package/dist-server/server/delegations.js +97 -44
  161. package/dist-server/server/drivers/acp/core.js +163 -33
  162. package/dist-server/server/drivers/acp/grok.js +4 -3
  163. package/dist-server/server/drivers/agents-call.js +1162 -0
  164. package/dist-server/server/drivers/agents-catalog.js +689 -0
  165. package/dist-server/server/drivers/agents-client.js +28 -0
  166. package/dist-server/server/drivers/agents-proxy.js +18 -1722
  167. package/dist-server/server/drivers/boxagent.js +174 -36
  168. package/dist-server/server/drivers/builtIn.js +2 -0
  169. package/dist-server/server/drivers/chat-box-tools.js +90 -0
  170. package/dist-server/server/drivers/chat-images.js +54 -0
  171. package/dist-server/server/drivers/chat-mcp-tools.js +55 -10
  172. package/dist-server/server/drivers/chat-tool-approval.js +70 -18
  173. package/dist-server/server/drivers/claude.js +28 -8
  174. package/dist-server/server/drivers/codex-instructions.js +48 -6
  175. package/dist-server/server/drivers/codex-mcp-names.js +44 -0
  176. package/dist-server/server/drivers/codex-tool-surface.js +20 -0
  177. package/dist-server/server/drivers/codex.js +121 -34
  178. package/dist-server/server/drivers/grok.js +2 -1
  179. package/dist-server/server/drivers/mistral.js +115 -0
  180. package/dist-server/server/drivers/openai-chat.js +155 -32
  181. package/dist-server/server/drivers/openai-compat.js +16 -8
  182. package/dist-server/server/drivers/permission-command.js +35 -0
  183. package/dist-server/server/drivers/phone-proxy.js +47 -2
  184. package/dist-server/server/drivers/pi.js +75 -4
  185. package/dist-server/server/drivers/prompt-split.js +87 -0
  186. package/dist-server/server/enterprise.js +97 -16
  187. package/dist-server/server/external-runtime.js +83 -0
  188. package/dist-server/server/fleet.js +1 -1
  189. package/dist-server/server/group-thread-usage.js +103 -0
  190. package/dist-server/server/guarded-requests.js +60 -0
  191. package/dist-server/server/harness/http.js +61 -0
  192. package/dist-server/server/harness/registry.js +1 -0
  193. package/dist-server/server/hosted-models.js +151 -0
  194. package/dist-server/server/hosted-slack.js +11 -0
  195. package/dist-server/server/index.js +3091 -658
  196. package/dist-server/server/local-computer.js +72 -23
  197. package/dist-server/server/managed-desktop.js +79 -14
  198. package/dist-server/server/managed-policy.js +153 -0
  199. package/dist-server/server/mcp-bridge.js +6 -6
  200. package/dist-server/server/message-db.js +28 -0
  201. package/dist-server/server/model-context-window.js +7 -0
  202. package/dist-server/server/model-prices.js +121 -0
  203. package/dist-server/server/new-bot-defaults.js +115 -0
  204. package/dist-server/server/notify.js +10 -1
  205. package/dist-server/server/org-library.js +1051 -0
  206. package/dist-server/server/package-export.js +627 -39
  207. package/dist-server/server/package-import.js +458 -0
  208. package/dist-server/server/package-parts.js +87 -0
  209. package/dist-server/server/peer-approval.js +16 -7
  210. package/dist-server/server/peer-roster.js +23 -2
  211. package/dist-server/server/presets.js +492 -0
  212. package/dist-server/server/provider-key-check.js +3 -2
  213. package/dist-server/server/published-teams.js +80 -0
  214. package/dist-server/server/recent-work.js +15 -0
  215. package/dist-server/server/request-auth.js +96 -0
  216. package/dist-server/server/room-handoffs.js +44 -14
  217. package/dist-server/server/routes/bot-presets.js +25 -0
  218. package/dist-server/server/routes/hosted-slack.js +20 -0
  219. package/dist-server/server/routes/table.js +14 -0
  220. package/dist-server/server/routine-requests.js +6 -3
  221. package/dist-server/server/routines.js +63 -1
  222. package/dist-server/server/section-context.js +10 -0
  223. package/dist-server/server/skills.js +34 -61
  224. package/dist-server/server/spend.js +111 -16
  225. package/dist-server/server/steer-queue.js +9 -3
  226. package/dist-server/server/store.js +550 -35
  227. package/dist-server/server/surface.js +10 -4
  228. package/dist-server/server/system-prompt.js +12 -4
  229. package/dist-server/server/team-backup.js +16 -3
  230. package/dist-server/server/team-computers.js +10 -0
  231. package/dist-server/server/team-library.js +6 -2
  232. package/dist-server/server/team-manifest.js +4 -3
  233. package/dist-server/server/team-setup-requests.js +16 -5
  234. package/dist-server/server/thread-events.js +17 -1
  235. package/dist-server/server/thread-starters.js +53 -0
  236. package/dist-server/server/turn-context.js +2 -0
  237. package/dist-server/server/turn-watchdog.js +12 -3
  238. package/dist-server/server/usage-ledger.js +43 -9
  239. package/dist-server/server/vps-computer.js +139 -51
  240. package/dist-server/server/vps-container-mcp.js +7 -0
  241. package/dist-server/server/vps-ssh.js +36 -7
  242. package/dist-server/server/workspace-backup-policy.js +21 -2
  243. package/dist-server/server/workspace-backup.js +7 -4
  244. package/dist-server/server/workspace.js +1 -1
  245. package/dist-server/shared/approval-mode.js +1 -1
  246. package/dist-server/shared/ask-question.js +188 -12
  247. package/dist-server/shared/bot-profile.js +15 -0
  248. package/dist-server/shared/command-allowlist.js +1 -0
  249. package/dist-server/shared/credential-request.js +1 -1
  250. package/dist-server/shared/mention-boundary.js +15 -0
  251. package/dist-server/shared/package-format.js +967 -0
  252. package/dist-server/shared/routines.js +7 -1
  253. package/dist-server/shared/skill-md.js +64 -0
  254. package/dist-server/shared/team-backup.js +9 -0
  255. package/dist-server/shared/wire.js +4 -0
  256. package/dist-server/src/lib/composer-attachments.js +16 -0
  257. package/dist-server/vps-container-mcp.js +1147 -179
  258. package/enterprise/README.md +33 -12
  259. package/enterprise/server/index.js +5 -5
  260. package/package.json +1 -1
  261. package/dist/assets/index-Ba9G44HI.js +0 -310
  262. package/dist/assets/index-CkRNp7wX.css +0 -1
@@ -1,3 +1,4 @@
1
+ import { codexToolSurfaceArgs } from "./codex-tool-surface.js";
1
2
  // Codex driver — upstream CodexDriver skeleton over agentcal's
2
3
  // drivers/codex.js runtime: the official `codex` CLI headless over its
3
4
  // app-server JSON-RPC protocol (newline-delimited JSON on stdio).
@@ -11,6 +12,7 @@
11
12
  // and preserves that history or reports a failed resume.
12
13
  import { existsSync } from "node:fs";
13
14
  import { homedir } from "node:os";
15
+ import { codexConfigMcpServerNames, mountedMcpServerName } from "./codex-mcp-names.js";
14
16
  import { stripWorkspaceCredentialEnv } from "../config.js";
15
17
  import { describeSpawnFailure, execCli, killCliTree, spawnCli } from "../procs.js";
16
18
  import { isHarnessOwnedMcpEnvName } from "../mcp-registry.js";
@@ -20,12 +22,15 @@ import { codexLocalProviderArgs } from "./local-inject.js";
20
22
  import { augmentedPath, splitCliString } from "../env-path.js";
21
23
  import { classifyError, computeBackoff, interruptibleDelay, RETRY_MAX_ATTEMPTS } from "./retry.js";
22
24
  import { appendNative } from "./native.js";
25
+ import { permissionCommand, permissionLaunchCwd } from "./permission-command.js";
23
26
  import { commandSummary, toolDetailPreview } from "../tool-summary.js";
24
27
  import { codexDeveloperInstructions, syncCodexInstructions } from "./codex-instructions.js";
28
+ import { volatileContextNote, withContextNote } from "./prompt-split.js";
25
29
  import { CodexDeviceAuthController } from "./codex-device-auth.js";
26
30
  import { codexAccountEmail } from "./codex-identity.js";
27
31
  import { classifyResumeFailure, mayReplay, recoveryPromptFor } from "../resume-recovery.js";
28
32
  import { extractMcpImages } from "../mcp-tool-images.js";
33
+ import { parseProtocolAskQuestions, questionAnswersById, questionChoices } from "../../shared/ask-question.js";
29
34
  export { decodeCodexSelection, readCodexModelCatalog, STATIC_CODEX_MODELS } from "./codex-catalog.js";
30
35
  const DRIVER_KIND = "codex";
31
36
  const ASTRA_MODEL_ID = "gpt-6-astra";
@@ -125,9 +130,16 @@ export function managedCodexArgs(config) {
125
130
  "-c", "shell_environment_policy.ignore_default_excludes=false",
126
131
  ];
127
132
  }
128
- const QUESTION_TIMEOUT_NOTE = "No answer was given — use your best judgment.";
129
133
  const DENY_TIMEOUT_NOTE = "OpenMausBot: nobody answered this permission request in time. Skip this action and finish what you can without it.";
130
134
  const skippedSseServers = new Set();
135
+ const renamedMcpServers = new Set();
136
+ /** Logged once per name: the rename is deliberate, not a lost server. */
137
+ function noteRenamedMcpServer(name, mountName) {
138
+ if (renamedMcpServers.has(name))
139
+ return;
140
+ renamedMcpServers.add(name);
141
+ console.error(`codex: MCP server ${JSON.stringify(name)} is also declared in Codex's own config.toml — mounted as ${JSON.stringify(mountName)} for this bot so the two do not merge`);
142
+ }
131
143
  function noteSkippedSseServer(name) {
132
144
  if (skippedSseServers.has(name))
133
145
  return;
@@ -266,6 +278,21 @@ function namedApprovalParams(mode) {
266
278
  },
267
279
  };
268
280
  }
281
+ /** Keep the turn on the complete sandbox resolved by native start/resume. */
282
+ function withResolvedSandbox(params, session) {
283
+ const requested = plainRecord(params.turn.sandboxPolicy);
284
+ // Named permission profiles own their policy; do not mix both selectors.
285
+ if (!requested)
286
+ return params;
287
+ const sandbox = plainRecord(plainRecord(session)?.sandbox);
288
+ if (!sandbox || typeof sandbox.type !== "string") {
289
+ throw new Error("Codex did not return its resolved sandbox policy. Update Codex, then retry; the turn was not started because its permissions could not be verified.");
290
+ }
291
+ if (sandbox.type !== requested.type) {
292
+ throw new Error("Codex did not apply the requested sandbox mode; cannot safely start the turn.");
293
+ }
294
+ return { ...params, turn: { ...params.turn, sandboxPolicy: sandbox } };
295
+ }
269
296
  function effectiveApprovalPolicy(value) {
270
297
  if (value === "untrusted" || value === "on-request" || value === "never")
271
298
  return value;
@@ -578,7 +605,7 @@ export const CodexDriver = {
578
605
  const retryScale = Number(process.env.FAKE_CODEX_RETRY_SCALE ?? "1");
579
606
  const launchAttempt = async (attempt) => {
580
607
  const env = childEnv();
581
- const appServerArgs = ["app-server", ...(config.managed ? managedCodexArgs(config.managed) : codexLocalProviderArgs(env, turn.model))];
608
+ const appServerArgs = ["app-server", ...(config.managed ? managedCodexArgs(config.managed) : codexLocalProviderArgs(env, turn.model)), ...codexToolSurfaceArgs()];
582
609
  if (turn.integrations?.composio) {
583
610
  mountMcpServer(appServerArgs, env, "openmausbot_connectors", turn.integrations.composio);
584
611
  }
@@ -593,6 +620,11 @@ export const CodexDriver = {
593
620
  if (turn.integrations?.browser) {
594
621
  mountMcpServer(appServerArgs, env, "browser", turn.integrations.browser);
595
622
  }
623
+ // A custom server named like one in the user's own config.toml would
624
+ // be merged with it by the `-c` override — a stdio command over a
625
+ // remote url is "invalid configuration" and kills the turn before the
626
+ // model is asked. Such a server mounts under a name of its own.
627
+ const declaredInCodexConfig = codexConfigMcpServerNames(env);
596
628
  for (const [name, server] of Object.entries(turn.integrations?.custom ?? {})) {
597
629
  // codex speaks streamable HTTP to a remote server, not the older
598
630
  // SSE transport: such an entry still reaches Claude bots, and is
@@ -601,7 +633,10 @@ export const CodexDriver = {
601
633
  noteSkippedSseServer(name);
602
634
  continue;
603
635
  }
604
- mountMcpServer(appServerArgs, env, name, server, false);
636
+ const mountName = mountedMcpServerName(name, declaredInCodexConfig);
637
+ if (mountName !== name)
638
+ noteRenamedMcpServer(name, mountName);
639
+ mountMcpServer(appServerArgs, env, mountName, server, false);
605
640
  }
606
641
  if (turn.integrations?.phone) {
607
642
  const bridge = turn.integrations.phone;
@@ -609,6 +644,7 @@ export const CodexDriver = {
609
644
  const prefix = "mcp_servers.openmausbot_phone";
610
645
  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"`);
611
646
  }
647
+ const commandCwd = permissionLaunchCwd(turn.cwd ?? homedir());
612
648
  const child = spawnCli(config.cli, appServerArgs, {
613
649
  cwd: turn.cwd ?? homedir(),
614
650
  env,
@@ -821,23 +857,22 @@ export const CodexDriver = {
821
857
  }
822
858
  return;
823
859
  }
824
- // One ask card carries one question honestly: its choices would come
825
- // from the first question alone and its one reply (including the
826
- // timeout note) would be copied into every question id (#1237).
827
- // Refuse the bundled call with a teaching error instead of
828
- // fabricating per-question answers.
829
- if (isQuestion && (!Array.isArray(params.questions) || params.questions.length !== 1)) {
830
- const bundled = Array.isArray(params.questions) && params.questions.length > 1;
860
+ // The whole set rides one card, answered per id: the protocol pairs
861
+ // each question with its own id, and the card's single reply is
862
+ // mapped back block by block (or as the flat single-question
863
+ // fallback). The only refusal left is an ask with nothing answerable
864
+ // — #1237 is closed: a bundle no longer copies one answer into
865
+ // every id.
866
+ const protocolQuestions = isQuestion ? parseProtocolAskQuestions(params.questions) : null;
867
+ if (isQuestion && !protocolQuestions) {
831
868
  send({
832
869
  jsonrpc: "2.0",
833
870
  id: msg.id,
834
871
  error: {
835
872
  code: -32602,
836
- message: bundled
837
- ? `ask supports one question per call; this request bundled ${params.questions.length}. Split it into separate asks, one question each.`
838
- : Array.isArray(params.questions)
839
- ? "ask supports one question per call; this request sent none."
840
- : "ask supports one question per call; params.questions must be an array with exactly one question.",
873
+ message: Array.isArray(params.questions)
874
+ ? "ask needs at least one answerable question — a string id and question text each; this request sent none."
875
+ : "ask needs params.questions to be an array of questions, each with a string id and question text.",
841
876
  },
842
877
  });
843
878
  return;
@@ -877,23 +912,36 @@ export const CodexDriver = {
877
912
  ? (mcpAppApproval?.summary ?? (typeof params.message === "string" ? params.message : "MCP access requested"))
878
913
  : typeof params.command === "string"
879
914
  ? params.command
880
- : Array.isArray(params.questions)
881
- ? params.questions.map((q) => q.question ?? q.header).filter(Boolean).join(" · ")
882
- : typeof params.reason === "string"
883
- ? params.reason
884
- : tool;
885
- const choices = isQuestion
886
- ? (params.questions?.[0]?.options ?? []).map((o) => o.label).slice(0, 5)
887
- : undefined;
915
+ : protocolQuestions
916
+ ? protocolQuestions.map(({ question }) => question.question).join(" · ")
917
+ : Array.isArray(params.questions)
918
+ ? params.questions.map((q) => q.question ?? q.header).filter(Boolean).join(" · ")
919
+ : typeof params.reason === "string"
920
+ ? params.reason
921
+ : tool;
922
+ // Flat choices only when one non-multiselect question can actually
923
+ // be answered by a bare reply; a bundle's first-question buttons
924
+ // would be an unusable lie for flat clients.
925
+ const choices = isQuestion && protocolQuestions ? questionChoices(protocolQuestions.map(({ question }) => question)) : undefined;
888
926
  const finish = (behavior, message, source = "user") => {
889
927
  if (!asks.delete(requestId))
890
928
  return;
891
929
  clearTimeout(timer);
892
930
  if (isQuestion) {
893
- const answers = {};
894
- for (const q of Array.isArray(params.questions) ? params.questions : []) {
895
- answers[q.id] = { answers: [message || QUESTION_TIMEOUT_NOTE] };
896
- }
931
+ // Only the person's reply is filed: Q:/A: blocks mapped to the
932
+ // id that asked them, or the flat fallback for a single
933
+ // question. Timeout and turn teardown send empty answers so
934
+ // every id reads unanswered — system notes never occupy the
935
+ // slot the model reads as the person's words (the rule
936
+ // permission-proxy already follows).
937
+ const mapped = behavior === "answer" && source === "user" && typeof message === "string"
938
+ ? questionAnswersById(message, protocolQuestions ?? [])
939
+ : {};
940
+ // Null prototype so an opaque id like __proto__ becomes a real
941
+ // answer key instead of hitting the inherited setter.
942
+ const answers = Object.create(null);
943
+ for (const [id, answer] of Object.entries(mapped))
944
+ answers[id] = { answers: [answer] };
897
945
  send({ jsonrpc: "2.0", id: msg.id, result: { answers } });
898
946
  }
899
947
  else {
@@ -905,7 +953,11 @@ export const CodexDriver = {
905
953
  }
906
954
  emit({ ...base(threadId, turnId), type: "request.resolved", requestId, behavior, source });
907
955
  };
908
- const timer = setTimeout(() => (isQuestion ? finish("answer", QUESTION_TIMEOUT_NOTE, "timeout") : finish("deny", DENY_TIMEOUT_NOTE, "timeout")), 15 * 60_000);
956
+ const timer = setTimeout(
957
+ // A timed-out question resolves as denied, not answered: the card
958
+ // closes with no reply (answers stay empty either way), and the
959
+ // resolve event must not claim an answer that never happened.
960
+ () => finish("deny", isQuestion ? undefined : DENY_TIMEOUT_NOTE, "timeout"), 15 * 60_000);
909
961
  timer.unref?.();
910
962
  asks.set(requestId, finish);
911
963
  emit({
@@ -915,9 +967,15 @@ export const CodexDriver = {
915
967
  requestType: isQuestion ? "question" : "permission",
916
968
  tool,
917
969
  summary,
970
+ command: method === "execCommandApproval" || method === "item/commandExecution/requestApproval"
971
+ ? permissionCommand(params.command, params.cwd ?? (
972
+ // Helpers may have a different workspace from their parent.
973
+ !params.threadId || params.threadId === codexThreadId ? commandCwd : undefined)) : undefined,
918
974
  choices,
975
+ ...(protocolQuestions ? { questions: protocolQuestions.map((pair) => pair.question) } : {}),
919
976
  approvalScope: controlsHost ? "local-computer" : undefined,
920
- requiresExplicitApproval: isAdditionalPermission || undefined,
977
+ requiresExplicitApproval: isAdditionalPermission || ((method === "execCommandApproval" || method === "item/commandExecution/requestApproval") &&
978
+ (params.additionalPermissions != null || params.networkApprovalContext != null)) || undefined,
921
979
  });
922
980
  };
923
981
  const handleNotification = (msg) => {
@@ -1288,12 +1346,28 @@ export const CodexDriver = {
1288
1346
  });
1289
1347
  effectiveConfig = configured?.config;
1290
1348
  }
1291
- catch {
1349
+ catch (error) {
1292
1350
  // Do not expose a possibly secret-bearing native config error or
1293
- // overwrite unknown instructions with an empty fallback.
1294
- throw new Error("Could not read Codex configuration; cannot safely update bot instructions. Retry after checking Codex.");
1351
+ // overwrite unknown instructions with an empty fallback. A config
1352
+ // codex itself rejected is the one case worth naming: the person
1353
+ // can act on it, and the message carries no credential.
1354
+ const invalid = error instanceof CodexRpcError && /^invalid configuration\b/i.test(error.message);
1355
+ throw new Error(invalid
1356
+ ? "Codex rejected its configuration as invalid. Check ~/.codex/config.toml (or CODEX_HOME) for a broken entry, then retry."
1357
+ : "Could not read Codex configuration; cannot safely update bot instructions. Retry after checking Codex.");
1295
1358
  }
1296
- const developerInstructions = codexDeveloperInstructions(effectiveConfig, turn.system ?? "");
1359
+ // Only the stable half of the prompt belongs in the developer slot:
1360
+ // it is the part that must survive compaction unchanged, and any
1361
+ // change to it invalidates the provider's cached prefix. The volatile
1362
+ // half (memory, mentions, outstanding teammate work) is delivered
1363
+ // inside the turn that changed it, after the cached prefix, the same
1364
+ // contract SendTurnInput.systemStable documents. Without the split
1365
+ // the driver keeps the previous single-block behaviour.
1366
+ const stableInstructions = typeof turn.systemStable === "string" && typeof turn.systemVolatile === "string"
1367
+ ? turn.systemStable
1368
+ : null;
1369
+ const promptSplit = stableInstructions !== null;
1370
+ const developerInstructions = codexDeveloperInstructions(effectiveConfig, stableInstructions ?? turn.system ?? "");
1297
1371
  let approvalParams;
1298
1372
  if (approvalMode === "custom") {
1299
1373
  // config/read returns the effective global + project config for this
@@ -1336,6 +1410,7 @@ export const CodexDriver = {
1336
1410
  approvalParams = approvalParams.fallback;
1337
1411
  resumed = await resumeThread();
1338
1412
  }
1413
+ approvalParams = withResolvedSandbox(approvalParams, resumed);
1339
1414
  codexThreadId = resumed?.thread?.id ?? cursor;
1340
1415
  resumedNativeThread = true;
1341
1416
  }
@@ -1381,12 +1456,20 @@ export const CodexDriver = {
1381
1456
  approvalParams = approvalParams.fallback;
1382
1457
  started = await startThread();
1383
1458
  }
1459
+ approvalParams = withResolvedSandbox(approvalParams, started);
1384
1460
  codexThreadId = started?.thread?.id ?? null;
1385
1461
  startedModel = started?.model ?? null;
1386
1462
  }
1387
1463
  if (!codexThreadId)
1388
1464
  throw new Error("Codex did not return a native thread id");
1389
- await syncCodexInstructions(threadId, codexThreadId, developerInstructions, resumedNativeThread, request);
1465
+ const { deliverVolatile, hadVolatile, commitVolatile } = await syncCodexInstructions(threadId, codexThreadId, developerInstructions, promptSplit ? turn.systemVolatile ?? "" : "", resumedNativeThread, request, Boolean(turn.mentionTurn));
1466
+ // A changed volatile half rides the next user input as a labelled
1467
+ // context block. It never touches the developer slot, so an
1468
+ // ordinary memory write or roster change neither appends a second
1469
+ // copy of the prompt to history nor re-uploads the conversation.
1470
+ if (deliverVolatile) {
1471
+ promptText = withContextNote(volatileContextNote(promptSplit ? turn.systemVolatile ?? "" : "", hadVolatile), promptText);
1472
+ }
1390
1473
  emit({ ...base(threadId, turnId), type: "session.started", sessionId: codexThreadId, model: startedModel ?? turn.model ?? null, ...(rebuiltFromReplay ? { rebuilt: true } : {}) });
1391
1474
  const turnInput = [
1392
1475
  ...(promptText ? [{ type: "text", text: promptText }] : []),
@@ -1420,6 +1503,10 @@ export const CodexDriver = {
1420
1503
  approvalParams = approvalParams.fallback;
1421
1504
  await startTurn();
1422
1505
  }
1506
+ // turn/start accepted the input: only now may the receipt claim the
1507
+ // volatile half was delivered, so a rejected turn redelivers on retry.
1508
+ if (commitVolatile)
1509
+ commitVolatile();
1423
1510
  }
1424
1511
  catch (e) {
1425
1512
  const failure = e instanceof Error ? e : { text: String(e) };
@@ -2,8 +2,9 @@ import { createOpenAIChatRuntime } from "./openai-chat.js";
2
2
  const DRIVER_KIND = "grok";
3
3
  const DEFAULT_URL = "https://api.x.ai/v1";
4
4
  const MODELS = {
5
- default: "grok-4",
5
+ default: "grok-4.7",
6
6
  options: [
7
+ { id: "grok-4.7", label: "Grok 4.7", contextWindow: 500_000 },
7
8
  { id: "grok-4", label: "Grok 4" },
8
9
  { id: "grok-4-fast", label: "Grok 4 Fast" },
9
10
  { id: "grok-3-mini", label: "Grok 3 Mini" },
@@ -0,0 +1,115 @@
1
+ import { z } from "zod";
2
+ import { createOpenAIChatRuntime } from "./openai-chat.js";
3
+ const DEFAULT_URL = "https://api.mistral.ai/v1";
4
+ const DEFAULT_MODELS = {
5
+ default: "mistral-large-latest",
6
+ options: [
7
+ { id: "mistral-large-latest", label: "Mistral Large (latest)" },
8
+ { id: "mistral-small-latest", label: "Mistral Small (latest)" },
9
+ ],
10
+ };
11
+ const configSchema = z.object({
12
+ url: z.string().trim().url().default(DEFAULT_URL),
13
+ model: z.string().trim().min(1).optional(),
14
+ tools: z.boolean().optional(),
15
+ });
16
+ function decodeConfig(raw) {
17
+ const config = configSchema.parse(raw ?? {});
18
+ config.url = config.url.replace(/\/+$/, "");
19
+ const url = new URL(config.url);
20
+ if (url.protocol !== "https:" && !(url.protocol === "http:" &&
21
+ ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) {
22
+ throw new Error("Mistral requires HTTPS, except for a local test endpoint.");
23
+ }
24
+ return config;
25
+ }
26
+ const modelCard = z.object({
27
+ id: z.string().min(1),
28
+ name: z.string().nullable().optional(),
29
+ aliases: z.array(z.string()).optional(),
30
+ archived: z.boolean().optional(),
31
+ capabilities: z.object({ completion_chat: z.boolean().optional() }).optional(),
32
+ max_context_length: z.number().int().positive().optional(),
33
+ });
34
+ /** Mistral can return plain text or a list of typed content chunks. */
35
+ function contentText(content) {
36
+ if (typeof content === "string")
37
+ return content;
38
+ if (!Array.isArray(content))
39
+ return "";
40
+ return content.map((chunk) => chunk?.type === "text" && typeof chunk.text === "string" ? chunk.text : "").join("");
41
+ }
42
+ export const MistralDriver = {
43
+ driverKind: "mistral",
44
+ metadata: { displayName: "Mistral (API)", supportsMultipleInstances: true, access: "api" },
45
+ models: DEFAULT_MODELS,
46
+ install: {
47
+ docsUrl: "https://console.mistral.ai/api-keys",
48
+ signInCommand: "Save a Mistral API key in Settings → Connections, or set MISTRAL_API_KEY on the server.",
49
+ },
50
+ decodeConfig,
51
+ defaultConfig: () => decodeConfig({}),
52
+ async create(input) {
53
+ const { config } = input;
54
+ const apiKey = (input.environment.MISTRAL_API_KEY ?? process.env.MISTRAL_API_KEY ?? "").trim();
55
+ const withConfiguredModel = (options) => {
56
+ const preferred = config.model ?? DEFAULT_MODELS.default;
57
+ if (config.model && !options.some((option) => option.id === config.model)) {
58
+ options = [{ id: config.model, label: config.model }, ...options];
59
+ }
60
+ return { default: options.some((option) => option.id === preferred) ? preferred : options[0].id, options };
61
+ };
62
+ let catalog = withConfiguredModel(DEFAULT_MODELS.options);
63
+ const refreshModels = async () => {
64
+ if (!apiKey)
65
+ return;
66
+ try {
67
+ const response = await fetch(`${config.url}/models`, {
68
+ headers: { authorization: `Bearer ${apiKey}` },
69
+ redirect: "error",
70
+ signal: AbortSignal.timeout(8_000),
71
+ });
72
+ if (!response.ok)
73
+ return;
74
+ const json = await response.json();
75
+ const rows = Array.isArray(json) ? json : z.object({ data: z.array(z.unknown()) }).parse(json).data;
76
+ const options = [];
77
+ const seen = new Set();
78
+ for (const row of rows) {
79
+ const parsed = modelCard.safeParse(row);
80
+ if (!parsed.success || parsed.data.archived || parsed.data.capabilities?.completion_chat === false)
81
+ continue;
82
+ const card = parsed.data;
83
+ for (const id of [card.id, ...(card.aliases ?? [])]) {
84
+ if (!id.trim() || seen.has(id))
85
+ continue;
86
+ seen.add(id);
87
+ options.push({ id, label: id === card.id ? card.name?.trim() || id : id,
88
+ ...(card.max_context_length ? { contextWindow: card.max_context_length } : {}) });
89
+ }
90
+ }
91
+ if (options.length)
92
+ catalog = withConfiguredModel(options);
93
+ }
94
+ catch {
95
+ // A failed refresh keeps the last usable catalog, including custom models.
96
+ }
97
+ };
98
+ if (apiKey)
99
+ void refreshModels();
100
+ return createOpenAIChatRuntime({
101
+ input, driverKind: "mistral", apiKey, apiUrl: config.url,
102
+ tools: config.tools, models: () => catalog, refreshModels, contentText,
103
+ requestBody: (model, messages, stream) => ({ model, messages, stream }),
104
+ httpErrorLabel: "Mistral",
105
+ missingKeyError: "Save a Mistral API key in Settings → Connections, or set MISTRAL_API_KEY.",
106
+ unavailableReason: "No Mistral API key — open Settings → Connections.",
107
+ timeoutMs: 180_000, billing: "metered", includeUsageInCompleted: true,
108
+ nativeLog: {
109
+ source: "mistral.chat.completions",
110
+ outgoing: (_turn, messages, model) => ({ model, messageCount: messages.length }),
111
+ incoming: ({ text, usage }) => ({ textLength: text.length, usage }),
112
+ },
113
+ });
114
+ },
115
+ };