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
@@ -29,8 +29,9 @@ import { homedir } from "node:os";
29
29
  import { lstat, mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
30
30
  import { basename, dirname, isAbsolute, relative, resolve } from "node:path";
31
31
  import { createHash } from "node:crypto";
32
- import { PROVIDER_CREDENTIAL_ENV, WORKSPACE_CREDENTIAL_ENV } from "../../config.js";
32
+ import { PROVIDER_CREDENTIAL_ENV, stripControlPlaneEnv, WORKSPACE_CREDENTIAL_ENV } from "../../config.js";
33
33
  import { decodeInjectId } from "../local-inject.js";
34
+ import { promptHalves, readPromptSplitReceipt, splitSessionPrompt, writePromptSplitReceipt } from "../prompt-split.js";
34
35
  import { describeSpawnFailure, execCli, killCliTree, spawnCli } from "../../procs.js";
35
36
  /**
36
37
  * A `host::model` pick talks to a loopback server with its own key.
@@ -42,9 +43,38 @@ export function skipSubscriptionAuthForLocalInject(model) {
42
43
  import { newEventId, newId } from "../../contracts.js";
43
44
  import { augmentedPath } from "../../env-path.js";
44
45
  import { supportsApprovalMode } from "../../../shared/approval-mode.js";
46
+ import { parseAskQuestions, parseChoices, questionAnswersByQuestion } from "../../../shared/ask-question.js";
45
47
  import { appendNative } from "../native.js";
48
+ import { acpPermissionCommand, permissionLaunchCwd } from "../permission-command.js";
46
49
  import { commandSummary, toolDetailPreview } from "../../tool-summary.js";
47
50
  import { extractMcpImages } from "../../mcp-tool-images.js";
51
+ import { redactSecretsInText } from "../../redact.js";
52
+ import { recoveryPromptFor } from "../../resume-recovery.js";
53
+ /** ACP vendors put the actionable cause in error.data while keeping the
54
+ * JSON-RPC message generic. Only surface known text fields, never a response
55
+ * body/config dump, and redact before bounding the displayed diagnostic. */
56
+ function acpRpcError(value, method) {
57
+ const message = typeof value?.message === "string" ? value.message : "ACP request failed";
58
+ const data = value?.data;
59
+ const detail = typeof data === "string" ? data
60
+ : typeof data?.details === "string" ? data.details
61
+ : typeof data?.message === "string" ? data.message
62
+ : typeof data?.error?.message === "string" ? data.error.message
63
+ : "";
64
+ const context = [
65
+ method,
66
+ typeof data?.service === "string" ? `service: ${data.service}` : "",
67
+ typeof data?.errorName === "string" ? data.errorName : "",
68
+ ].filter(Boolean).join(", ");
69
+ const diagnostic = `${detail && detail !== message ? `${message}: ${detail}` : message} (${context})`;
70
+ const error = new Error(redactSecretsInText(diagnostic).slice(0, 1500));
71
+ return Object.assign(error, { code: value?.code, data,
72
+ // -32603 is JSON-RPC's internal error. Invalid params, unsupported
73
+ // methods and auth refusals are user/configuration issues, not evidence
74
+ // of a broken process. Preserve their session instead of retrying them.
75
+ acpSessionFailure: value?.code === -32603,
76
+ });
77
+ }
48
78
  const envOr = (key, fallback) => Number(process.env[key] ?? fallback);
49
79
  const INIT_TIMEOUT = envOr("OPENMAUS_ACP_INIT_TIMEOUT_MS", 300_000);
50
80
  const SESSION_CONFIG_TIMEOUT = envOr("OPENMAUS_ACP_SESSION_CONFIG_TIMEOUT_MS", 300_000); // configureSession's per-request default
@@ -171,6 +201,17 @@ function sessionOperationKey(toolCall) {
171
201
  locations: toolCall?.locations,
172
202
  }));
173
203
  }
204
+ /** The banner an ACP CLI prints for `--version`. Hermes writes its whole banner
205
+ * to stderr with an empty stdout, so an stdout-only read reports a perfectly
206
+ * good install as "CLI not found". Prefer stdout; fall back to the first
207
+ * stderr line; null when both are empty. */
208
+ export function versionFromProbe(stdout, stderr) {
209
+ const out = (stdout ?? "").trim();
210
+ if (out)
211
+ return out;
212
+ const err = (stderr ?? "").trim().split(/\r\n|\n|\r/, 1)[0]?.trim() ?? "";
213
+ return err || null;
214
+ }
174
215
  export function createAcpDriver(support) {
175
216
  const DRIVER_KIND = support.driverKind;
176
217
  const SOURCE = support.nativeSource;
@@ -205,6 +246,8 @@ export function createAcpDriver(support) {
205
246
  if (!allowedCredentials.has(key))
206
247
  delete env[key];
207
248
  }
249
+ // The operator's own secrets are outside any driver's allowlist.
250
+ stripControlPlaneEnv(env);
208
251
  support.transformEnv?.(env, activeConfig, instanceId);
209
252
  return env;
210
253
  };
@@ -385,7 +428,25 @@ export function createAcpDriver(support) {
385
428
  session.current = null;
386
429
  active.delete(threadId);
387
430
  current.flushAssistantText();
388
- emit({ ...base(threadId, current.turnId), type: "turn.completed", ok, stopReason, cost: null });
431
+ // `end_turn` with nothing to show for it — no reply, no image, no
432
+ // tool result — is a lost turn, not a success. An engine can report
433
+ // exactly that (a provider may cut a reasoning-only stream and
434
+ // still answer end_turn), and ok:true would end the thread quietly
435
+ // while the person's message went unanswered. Keep the completion,
436
+ // but report it as a failure so terminal chips, incidents and
437
+ // follow-ups see what happened.
438
+ let finalOk = ok;
439
+ let finalStopReason = stopReason;
440
+ if (finalOk && finalStopReason === null && !current.state.producedItem) {
441
+ finalOk = false;
442
+ finalStopReason = "empty_turn";
443
+ emit({
444
+ ...base(threadId, current.turnId),
445
+ type: "runtime.error",
446
+ message: `${DRIVER_KIND} ended the turn with no reply, image, or tool result`,
447
+ });
448
+ }
449
+ emit({ ...base(threadId, current.turnId), type: "turn.completed", ok: finalOk, stopReason: finalStopReason, cost: null });
389
450
  if (session.child.exitCode === null && !session.closing && !session.dead) {
390
451
  armIdle(threadId);
391
452
  }
@@ -400,6 +461,7 @@ export function createAcpDriver(support) {
400
461
  * session.current, so a request that lands between turns is answered
401
462
  * (never brokered) instead of left hanging. */
402
463
  const openSession = (threadId, launch, argv, env, cwd, contractKey) => {
464
+ const commandCwd = permissionLaunchCwd(cwd);
403
465
  const child = spawnCli(launch.command, argv, {
404
466
  cwd,
405
467
  env,
@@ -426,7 +488,7 @@ export function createAcpDriver(support) {
426
488
  if (timeoutMs) {
427
489
  timer = setTimeout(() => {
428
490
  rpcPending.delete(id);
429
- reject(new Error(`${method} timed out`));
491
+ reject(Object.assign(new Error(`${method} timed out`), { acpSessionFailure: true }));
430
492
  }, timeoutMs);
431
493
  timer.unref?.();
432
494
  }
@@ -455,6 +517,7 @@ export function createAcpDriver(support) {
455
517
  };
456
518
  armIdle();
457
519
  rpcPending.set(id, {
520
+ method,
458
521
  // Consume configuration in wire order: an update following this
459
522
  // response may arrive before the awaiting continuation resumes.
460
523
  resolve: (result) => { receive?.(result); resolve(result); },
@@ -601,16 +664,34 @@ export function createAcpDriver(support) {
601
664
  }
602
665
  }
603
666
  const tool = kind === "execute" ? "shell" : kind === "edit" ? "edit" : kind || "tool";
667
+ const isShellCommand = !isQuestion && kind === "execute" && !/^mcp(?:__|[.:])/i.test(String(toolCall.title ?? ""));
604
668
  const summary = String(toolCall.rawInput?.command ?? toolCall.title ?? tool).slice(0, 200);
669
+ // One structured question beside the flat choices: the richer card
670
+ // renders from it while older clients keep answering through
671
+ // `choices`. Built once here so the emit and the answer path can
672
+ // never disagree. parseAskQuestions enforces the shared caps.
673
+ const questionChoices = isQuestion
674
+ ? options.flatMap((option) => typeof option.name === "string" && option.name.trim() ? [option.name.trim()] : [])
675
+ : [];
676
+ const askQuestions = isQuestion && questionChoices.length
677
+ ? parseAskQuestions({ questions: [{ question: summary, options: questionChoices }] }) ?? undefined
678
+ : undefined;
605
679
  const requestId = newId();
606
680
  const finish = (behavior, source = "user", message, always) => {
607
681
  if (!current.asks.delete(requestId))
608
- return;
682
+ return "unavailable";
609
683
  clearTimeout(timer);
610
684
  const want = behavior === "allow" ? "allow" : "reject";
611
685
  const forSession = want === "allow" && always === true && !isQuestion && !current.controlsHost;
686
+ // A structured card replies in the Q:/A: block format; recover the
687
+ // picked label from it so exact-match keeps working. Flat clients
688
+ // send the bare label, which the single-question fallback inside
689
+ // questionAnswersByQuestion already returns unchanged.
690
+ const picked = askQuestions
691
+ ? questionAnswersByQuestion(message ?? "", askQuestions)[askQuestions[0].question] ?? message
692
+ : message;
612
693
  const named = isQuestion && behavior === "answer"
613
- ? options.filter((option) => option.optionId === message || option.name?.trim() === message)
694
+ ? options.filter((option) => option.optionId === picked || parseChoices([option.name], 1)?.[0] === picked)
614
695
  : [];
615
696
  const optionId = behavior === "cancel"
616
697
  ? null
@@ -641,6 +722,7 @@ export function createAcpDriver(support) {
641
722
  source: optionId ? source : "system",
642
723
  approvalScope: current.controlsHost ? "local-computer" : undefined,
643
724
  });
725
+ return !optionId ? "rejected" : isQuestion ? "answered" : behavior === "allow" ? "allowed-once" : "rejected";
644
726
  };
645
727
  const timer = setTimeout(() => {
646
728
  emit({ ...base(threadId, current.turnId), type: "runtime.error", message: DENY_TIMEOUT_NOTE });
@@ -655,9 +737,10 @@ export function createAcpDriver(support) {
655
737
  requestType: isQuestion ? "question" : "permission",
656
738
  tool,
657
739
  summary,
658
- choices: isQuestion
659
- ? options.flatMap((option) => typeof option.name === "string" && option.name.trim() ? [option.name.trim()] : [])
660
- : undefined,
740
+ command: isShellCommand ? acpPermissionCommand(toolCall.rawInput, commandCwd) : undefined,
741
+ requiresExplicitApproval: isShellCommand && (toolCall.rawInput?.dangerouslyDisableSandbox === true || toolCall.rawInput?.sandbox_permissions === "require_escalated") || undefined,
742
+ choices: askQuestions?.[0]?.options.map(option => option.label) ?? (isQuestion ? questionChoices : undefined),
743
+ ...(askQuestions ? { questions: askQuestions } : {}),
661
744
  approvalScope: current.controlsHost ? "local-computer" : undefined,
662
745
  // the driver can honor a session-wide allow either way
663
746
  allowSession: !isQuestion && !current.controlsHost ? true : undefined,
@@ -686,6 +769,7 @@ export function createAcpDriver(support) {
686
769
  const delta = content?.text;
687
770
  if (content?.type === "image" && typeof content.data === "string" && content.data) {
688
771
  current.flushAssistantText();
772
+ current.state.producedItem = true;
689
773
  emit({
690
774
  ...base(threadId, current.turnId),
691
775
  type: "item.completed",
@@ -722,6 +806,7 @@ export function createAcpDriver(support) {
722
806
  }
723
807
  case "tool_call_update": {
724
808
  if (u.status === "completed" || u.status === "failed") {
809
+ current.state.producedItem = true;
725
810
  emit({
726
811
  ...base(threadId, current.turnId),
727
812
  type: "item.completed",
@@ -788,9 +873,7 @@ export function createAcpDriver(support) {
788
873
  if (pend.idleTimer)
789
874
  clearTimeout(pend.idleTimer);
790
875
  if (msg.error) {
791
- const error = new Error(msg.error.message ?? JSON.stringify(msg.error));
792
- Object.assign(error, { code: msg.error.code, data: msg.error.data });
793
- pend.reject(error);
876
+ pend.reject(acpRpcError(msg.error, pend.method));
794
877
  }
795
878
  else {
796
879
  pend.resolve(msg.result);
@@ -923,6 +1006,10 @@ export function createAcpDriver(support) {
923
1006
  const envFingerprint = createHash("sha256").update(JSON.stringify(spawnEnv)).digest("hex").slice(0, 16);
924
1007
  const contractKey = JSON.stringify([launch.command, launch.args ?? [], spawnArgs, cwd, turnConfig.fullAuto === true, envFingerprint]);
925
1008
  const sessionKey = JSON.stringify(mcpServers);
1009
+ if (turn.sessionReset) {
1010
+ closeSession(threadId, "reset");
1011
+ sessionAllows.delete(threadId);
1012
+ }
926
1013
  const pooled = sessions.get(threadId);
927
1014
  let session;
928
1015
  if (pooled && !pooled.dead && !pooled.closing && pooled.contractKey === contractKey) {
@@ -948,7 +1035,7 @@ export function createAcpDriver(support) {
948
1035
  // respawns the child, every wire call must reach the live record, so
949
1036
  // nothing captures the connection off it.
950
1037
  const request = (method, params, timeoutMs, receive, idleMs, idleMessage) => session.acp.request(method, params, timeoutMs, receive, idleMs, idleMessage);
951
- const state = { settled: false, promptSent: false, text: "" };
1038
+ const state = { settled: false, promptSent: false, text: "", producedItem: false };
952
1039
  const asks = new Map();
953
1040
  const modelOf = (result) => {
954
1041
  const option = (Array.isArray(result?.configOptions) ? result.configOptions : []).find((entry) => entry?.id === (support.selectModel?.configId ?? "model"));
@@ -987,6 +1074,7 @@ export function createAcpDriver(support) {
987
1074
  state.text = "";
988
1075
  if (!text.trim())
989
1076
  return;
1077
+ state.producedItem = true;
990
1078
  emit({ ...base(threadId, turnId), type: "item.completed", itemType: "assistant_text", text });
991
1079
  };
992
1080
  const current = {
@@ -1073,8 +1161,10 @@ export function createAcpDriver(support) {
1073
1161
  };
1074
1162
  let runtimeAcceptsImages = await handshake();
1075
1163
  let init = session.initResult;
1076
- const cursor = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
1164
+ const cursor = !turn.sessionReset && typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
1077
1165
  let sessionResult = null;
1166
+ let promptTurn = turn;
1167
+ let rebuiltFromReplay = false;
1078
1168
  for (;;) {
1079
1169
  const liveSessionId = session.sessionId;
1080
1170
  if (liveSessionId !== null && session.sessionKey === sessionKey && (cursor === null || cursor === liveSessionId)) {
@@ -1100,7 +1190,17 @@ export function createAcpDriver(support) {
1100
1190
  }
1101
1191
  });
1102
1192
  }
1103
- catch {
1193
+ catch (error) {
1194
+ const classification = support.classifyError?.(error);
1195
+ // OpenCode encodes ACPSessionNotFoundError as invalidParams
1196
+ // with just the rejected sessionId. Other invalidParams
1197
+ // responses (model/config errors) must not erase history.
1198
+ const data = error?.data;
1199
+ const missingSession = data && typeof data === "object" && !Array.isArray(data)
1200
+ && data.sessionId === cursor && Object.keys(data).length === 1;
1201
+ if (classification === "invalid_credentials" || classification === "inactive_subscription"
1202
+ || (error?.code === -32602 && !missingSession))
1203
+ throw error;
1104
1204
  /* session gone, load unsupported, or too slow — the
1105
1205
  * fallbacks below choose between one fresh process and a
1106
1206
  * genuinely new session */
@@ -1127,8 +1227,16 @@ export function createAcpDriver(support) {
1127
1227
  }
1128
1228
  // a genuinely fresh native session forgets what the previous
1129
1229
  // one allowed
1130
- if (!cursor)
1131
- sessionAllows.delete(threadId);
1230
+ sessionAllows.delete(threadId);
1231
+ if (cursor) {
1232
+ const recovery = recoveryPromptFor({
1233
+ recoveryText: turn.recoveryText,
1234
+ currentText: turn.text,
1235
+ failure: "before-accept",
1236
+ });
1237
+ promptTurn = { ...turn, text: recovery.text };
1238
+ rebuiltFromReplay = recovery.replayed;
1239
+ }
1132
1240
  sessionResult = await request("session/new", { cwd, mcpServers: sessionServers }, NEW_SESSION_TIMEOUT, (result) => {
1133
1241
  session.sessionId = typeof result?.sessionId === "string" ? result.sessionId : null;
1134
1242
  session.sessionKey = sessionKey;
@@ -1151,6 +1259,7 @@ export function createAcpDriver(support) {
1151
1259
  type: "session.started",
1152
1260
  sessionId,
1153
1261
  model: selectedModel ?? init?._meta?.modelState?.currentModelId ?? cliTurn.model ?? null,
1262
+ ...(rebuiltFromReplay ? { rebuilt: true } : {}),
1154
1263
  });
1155
1264
  };
1156
1265
  try {
@@ -1177,7 +1286,7 @@ export function createAcpDriver(support) {
1177
1286
  ? sessionResult.models.availableModels
1178
1287
  : [],
1179
1288
  });
1180
- // initialize's currentModelId is the CLI default (grok-4.6),
1289
+ // initialize's currentModelId is the CLI default,
1181
1290
  // not the model this turn asked for. After a successful pin,
1182
1291
  // report the slug we set so the UI does not claim otherwise.
1183
1292
  if (!selectedModel && cliTurn.model)
@@ -1198,11 +1307,28 @@ export function createAcpDriver(support) {
1198
1307
  throw error;
1199
1308
  }
1200
1309
  emitSessionStarted();
1310
+ // The stable/volatile split: the full prompt rides only the turn
1311
+ // that establishes - or re-instructs, after a soul edit - this
1312
+ // native session. Later turns go through bare unless the volatile
1313
+ // half changed, so a memory edit neither appends a second copy of
1314
+ // the prompt to the agent's session history nor re-prices the
1315
+ // prefix its provider cached. Receipts are durable because the
1316
+ // native session outlives this process; an un-split turn (a direct
1317
+ // adapter call) keeps the legacy full-prompt shape.
1318
+ const halves = promptHalves(turn);
1319
+ let promptInput = promptTurn;
1320
+ let pendingSplitReceipt = null;
1321
+ if (halves.stable !== null) {
1322
+ const receiptKey = JSON.stringify([threadId, sessionId]);
1323
+ const composed = splitSessionPrompt(halves.stable, halves.volatile, readPromptSplitReceipt(DRIVER_KIND, receiptKey), promptTurn.system, promptTurn.text, Boolean(turn.mentionTurn));
1324
+ promptInput = { ...promptTurn, system: "", text: composed.text };
1325
+ pendingSplitReceipt = { key: receiptKey, receipt: composed.receipt };
1326
+ }
1201
1327
  const text = support.buildPromptText
1202
- ? support.buildPromptText(turn)
1203
- : turn.system
1204
- ? `${turn.system}\n\n${turn.text}`
1205
- : turn.text;
1328
+ ? support.buildPromptText(promptInput)
1329
+ : promptInput.system
1330
+ ? `${promptInput.system}\n\n${promptInput.text}`
1331
+ : promptInput.text;
1206
1332
  const imageBlocks = support.images === true && runtimeAcceptsImages
1207
1333
  ? await readAcpImageBlocks(turn.images ?? [])
1208
1334
  : [];
@@ -1216,6 +1342,12 @@ export function createAcpDriver(support) {
1216
1342
  const promptIdleMs = promptIdleTimeoutMs();
1217
1343
  const result = await request("session/prompt", { sessionId, prompt: [{ type: "text", text }, ...imageBlocks] }, undefined, undefined, promptIdleMs, `${DRIVER_KIND} went fully silent ${Math.round(promptIdleMs / 1000)} s after the message and the turn was stopped. ` +
1218
1344
  "Raise OPENMAUS_ACP_PROMPT_IDLE_TIMEOUT_MS if this model legitimately takes longer to answer.");
1345
+ if (pendingSplitReceipt) {
1346
+ // session/prompt resolving is the acceptance boundary: a
1347
+ // rejected prompt leaves the receipt unwritten, so the next
1348
+ // turn redelivers what this one never received.
1349
+ writePromptSplitReceipt(DRIVER_KIND, pendingSplitReceipt.key, pendingSplitReceipt.receipt);
1350
+ }
1219
1351
  // opencode 1.18.18 reports usage at the result root; grok and
1220
1352
  // gemini put it under _meta. Read both rather than lose the count.
1221
1353
  const usage = result?.usage ?? result?._meta ?? {};
@@ -1261,11 +1393,14 @@ export function createAcpDriver(support) {
1261
1393
  message,
1262
1394
  ...(needsAuth ? { setup: true } : {}),
1263
1395
  });
1264
- // A prompt that went idle has a wedged child under the RPC — it
1265
- // will never answer the next prompt either. Do not leave it
1266
- // pooled: close it so the next turn spawns a fresh agent.
1267
- if (e?.acpPromptStall === true && session.child.exitCode === null && !session.closing) {
1268
- closeSession(threadId, "prompt-stall");
1396
+ // Internal RPC failures can leave a live child poisoned just
1397
+ // like a silent prompt. Evict before completion listeners can
1398
+ // start the next turn. Never replay this accepted prompt: it
1399
+ // may already have executed tools. The next explicit turn can
1400
+ // resume the recorded session on a fresh process.
1401
+ if (!needsAuth && (e?.acpPromptStall === true || e?.acpSessionFailure === true
1402
+ || code === "upstream_outage") && session.child.exitCode === null && !session.closing) {
1403
+ closeSession(threadId, e?.acpPromptStall === true ? "prompt-stall" : "rpc-failure");
1269
1404
  }
1270
1405
  settle(threadId, session, false, needsAuth ? "auth_required" : "rpc_error");
1271
1406
  }
@@ -1278,7 +1413,7 @@ export function createAcpDriver(support) {
1278
1413
  if (support.snapshot)
1279
1414
  return support.snapshot(env, config, instanceId);
1280
1415
  const version = await new Promise((resolve) => {
1281
- execCli(config.cli, ["--version"], { timeout: 8000, env }, (err, stdout) => resolve(err ? null : stdout.trim()));
1416
+ execCli(config.cli, ["--version"], { timeout: 8000, env }, (err, stdout, stderr) => resolve(err ? null : versionFromProbe(stdout, stderr)));
1282
1417
  });
1283
1418
  if (!version)
1284
1419
  return { state: "unavailable", reason: `\`${config.cli}\` CLI not found` };
@@ -1319,12 +1454,7 @@ export function createAcpDriver(support) {
1319
1454
  const finish = turn?.asks.get(requestId);
1320
1455
  if (!finish)
1321
1456
  return "unavailable"; // settled, timed out, or turn gone
1322
- finish(decision.behavior, "user", decision.message, decision.always === true);
1323
- return decision.behavior === "allow"
1324
- ? "allowed-once"
1325
- : decision.behavior === "answer"
1326
- ? "answered"
1327
- : "rejected";
1457
+ return finish(decision.behavior, "user", decision.message, decision.always === true);
1328
1458
  },
1329
1459
  hasSession: (threadId) => active.has(threadId),
1330
1460
  stopAll: async () => {
@@ -9,8 +9,9 @@ import { join } from "node:path";
9
9
  import { decodeInjectId, hostApiKey, localHost, mergeLocalInject } from "../local-inject.js";
10
10
  import { createAcpDriver } from "./core.js";
11
11
  export const STATIC_GROK_MODELS = {
12
- default: "grok-4.6",
12
+ default: "grok-4.7",
13
13
  options: [
14
+ { id: "grok-4.7", label: "Grok 4.7", contextWindow: 500_000 },
14
15
  { id: "grok-4.6", label: "Grok 4.6" },
15
16
  { id: "grok-4.5", label: "Grok 4.5" },
16
17
  ],
@@ -28,7 +29,7 @@ function unquote(raw) {
28
29
  }
29
30
  return value;
30
31
  }
31
- /** Local slugs from ~/.grok/config.toml, plus the two cloud defaults.
32
+ /** Local slugs from ~/.grok/config.toml, plus the cloud defaults.
32
33
  * `grok -m <slug>` already accepts these; the picker just didn't list them. */
33
34
  export function readGrokModelCatalog(env = process.env) {
34
35
  const path = join(grokHome(env), "config.toml");
@@ -218,7 +219,7 @@ const support = {
218
219
  // agent flags: Grok 1.0.6 only applies them when they sit AFTER `agent`
219
220
  // and BEFORE `stdio` (`grok agent -m slug stdio`). Putting -m first is
220
221
  // accepted as a TUI option and then ignored, so ACP session/new keeps
221
- // [models].default (grok-4.6) and oMLX never sees a request.
222
+ // [models].default (currently grok-4.7) and oMLX never sees a request.
222
223
  // Auto selects Grok's native classifier; if its feature gate is disabled,
223
224
  // residual requests still ask. Never replace it with bypassPermissions.
224
225
  // Verified: grok 1.0.3 --help and xai-org/grok-build@37949780,