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,7 +29,7 @@ function text(value, limit) {
29
29
  if (typeof value !== "string")
30
30
  return undefined;
31
31
  const trimmed = value.trim();
32
- return trimmed ? trimmed.slice(0, limit) : undefined;
32
+ return trimmed ? trimmed.slice(0, limit).trim() : undefined;
33
33
  }
34
34
  function isRecord(value) {
35
35
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -94,6 +94,36 @@ export function parseAskQuestions(input) {
94
94
  }
95
95
  return questions.length ? questions : null;
96
96
  }
97
+ /** The fenced block a turn-boundary agent ends its run with when it wants
98
+ * to ask the person something (the BoxAgent transport): the harness cannot
99
+ * pause mid-run, so the questions ride the final output and OMB parses them
100
+ * at settle. The block is model-authored — untrusted input like any tool
101
+ * call — so its body runs through parseAskQuestions and the same caps. */
102
+ const OMB_ASK_FENCE = /(^|\n)[ \t]{0,3}(`{3,}|~{3,})[ \t]*omb-ask[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\2[ \t]*(?=\r?\n|$)/;
103
+ const OMB_ASK_FENCE_GLOBAL = new RegExp(OMB_ASK_FENCE.source, OMB_ASK_FENCE.flags + "g");
104
+ /** The questions inside the first fenced omb-ask block in an output, or
105
+ * null when there is nothing worth showing (no fence, invalid JSON, or no
106
+ * entry that parses as a question). The first fence wins: a second block
107
+ * in the same output is ignored, not merged. */
108
+ export function parseOmbAskQuestions(output) {
109
+ const match = OMB_ASK_FENCE.exec(output);
110
+ if (!match)
111
+ return null;
112
+ try {
113
+ return parseAskQuestions(JSON.parse(match[3]));
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ }
119
+ /** The output with its omb-ask block(s) removed, for display: the person
120
+ * reads the prose around the ask, never the raw protocol JSON. Text
121
+ * without a block is returned untouched. */
122
+ export function stripOmbAskBlock(output) {
123
+ if (!OMB_ASK_FENCE.test(output))
124
+ return output;
125
+ return output.replace(OMB_ASK_FENCE_GLOBAL, "").replace(/\n{3,}/g, "\n\n").trim();
126
+ }
97
127
  /** The one line the card subtitle and a spoken prompt show. */
98
128
  export function askQuestionSummary(questions) {
99
129
  const first = questions[0]?.question ?? "";
@@ -139,6 +169,61 @@ export function questionChoices(questions) {
139
169
  return undefined;
140
170
  return only.options.map((option) => option.label);
141
171
  }
172
+ /** The chat runtime's own ask tool: the OpenAI-compatible engines'
173
+ * counterpart of ASK_USER_QUESTION. Named ask_user so the ASKS_A_PERSON
174
+ * backstop already covers it — no auto mode may answer a question. */
175
+ export const ASK_USER_TOOL = "ask_user";
176
+ /** The ask_user definition shared by every chat-runtime engine, shaped for
177
+ * an OpenAI function-calling tool list. The schema states the caps; the
178
+ * parser enforces them again on arrival, because a model-authored argument
179
+ * is a request to follow the schema, not a guarantee that it did. */
180
+ export const ASK_USER_TOOL_DEFINITION = {
181
+ type: "function",
182
+ function: {
183
+ name: ASK_USER_TOOL,
184
+ description: "Ask the person questions when the decision is theirs. Offer concrete options where they exist; they can always answer in their own words. Ask up to six questions in one call rather than one at a time.",
185
+ parameters: {
186
+ type: "object",
187
+ properties: {
188
+ questions: {
189
+ type: "array",
190
+ minItems: 1,
191
+ maxItems: MAX_QUESTIONS,
192
+ items: {
193
+ type: "object",
194
+ properties: {
195
+ question: { type: "string", description: "The question to show the person." },
196
+ header: { type: "string", description: "A short label for the question's tab." },
197
+ multiSelect: { type: "boolean", description: "Whether several options may be picked." },
198
+ options: {
199
+ type: "array",
200
+ maxItems: MAX_OPTIONS,
201
+ description: "Suggested answers; the person may always write their own.",
202
+ items: {
203
+ type: "object",
204
+ properties: {
205
+ label: { type: "string" },
206
+ description: { type: "string" },
207
+ },
208
+ required: ["label"],
209
+ additionalProperties: false,
210
+ },
211
+ },
212
+ },
213
+ required: ["question"],
214
+ additionalProperties: false,
215
+ },
216
+ },
217
+ },
218
+ required: ["questions"],
219
+ additionalProperties: false,
220
+ },
221
+ },
222
+ };
223
+ /** The synthetic tool string for an ask parsed out of model-authored final
224
+ * output (the BoxAgent turn-held transport): there is no tool call to name,
225
+ * but the event and the ASKS_A_PERSON backstop need one string. */
226
+ export const OMB_ASK_TOOL = "omb-ask";
142
227
  /** The lead-in on a formatted answer. It exists for the model — the answer
143
228
  * is delivered on the deny channel, so it has to say what it is — and the
144
229
  * card strips it back off when it shows the person what they sent. */
@@ -164,6 +249,23 @@ export function formatQuestionAnswers(questions, answers) {
164
249
  export function answerWithoutPreamble(answer) {
165
250
  return answer.startsWith(`${ANSWER_PREAMBLE}\n\n`) ? answer.slice(ANSWER_PREAMBLE.length + 2) : answer;
166
251
  }
252
+ /** The longest formatted answer OMB will echo back into a follow-up prompt
253
+ * (the BoxAgent continuation). formatQuestionAnswers itself does not
254
+ * truncate — the tool-result channel has no stated limit — but a prompt is
255
+ * not the place to find one: six answers at the custom-answer cap is the
256
+ * most a legitimate reply weighs, so that is the ceiling. */
257
+ export const MAX_ANSWER_ECHO = 6 * MAX_CUSTOM_ANSWER;
258
+ /** Cap a formatted answer for echoing into a follow-up prompt. An over-cap
259
+ * echo is cut back to the last whole block so no partial answer reads as
260
+ * one, and says it was truncated. */
261
+ export function capAnswerEcho(answer, limit = MAX_ANSWER_ECHO) {
262
+ if (answer.length <= limit)
263
+ return answer;
264
+ const cut = answer.slice(0, limit);
265
+ const boundary = cut.lastIndexOf("\n\nQ: ");
266
+ const kept = boundary > 0 ? cut.slice(0, boundary) : cut;
267
+ return kept + "\n\n[answer truncated]";
268
+ }
167
269
  /**
168
270
  * The answer text, read back as one value per question.
169
271
  *
@@ -180,21 +282,95 @@ export function answerWithoutPreamble(answer) {
180
282
  * With more than one there is, and nothing is filed.
181
283
  */
182
284
  export function questionAnswersByQuestion(message, questions) {
183
- const answers = {};
184
- const known = new Map(questions.map((entry) => [entry.question, entry.question]));
185
- for (const block of message.split("\n\n")) {
186
- const match = /^Q: ([\s\S]+?)\nA: ([\s\S]+)$/.exec(block.trim());
187
- if (!match)
285
+ return questionAnswersById(message, questions.map(question => ({ id: question.question, question })));
286
+ }
287
+ /** Longest protocol id accepted. Ids are harness-internal keys, never shown
288
+ * on the card, but they ride the reply — so they are capped like the rest. */
289
+ const MAX_QUESTION_ID = 200;
290
+ /** Parse a protocol questions array whose entries carry their own string
291
+ * ids (codex's item/tool/requestUserInput). The same caps and skip rules
292
+ * as parseAskQuestions; an entry without both a usable id and a usable
293
+ * question is skipped rather than fatal, and nothing answerable parses to
294
+ * null. */
295
+ export function parseProtocolAskQuestions(entries) {
296
+ if (!Array.isArray(entries))
297
+ return null;
298
+ const parsed = [];
299
+ const seenIds = new Set();
300
+ for (const raw of entries) {
301
+ if (!isRecord(raw))
302
+ continue;
303
+ const question = parseQuestion(raw);
304
+ // The id is opaque protocol data echoed back verbatim in the answer, so
305
+ // it is preserved exactly as sent — trimmed only to reject an
306
+ // all-whitespace id — and an overlong id is rejected whole rather than
307
+ // truncated. A duplicate id fails the whole request: two questions
308
+ // cannot share one answer slot, and silently dropping one would ask
309
+ // the person a question whose answer could never be delivered.
310
+ const id = raw.id;
311
+ if (!question || typeof id !== "string" || !id.trim() || id.length > MAX_QUESTION_ID)
312
+ continue;
313
+ if (seenIds.has(id))
314
+ return null;
315
+ seenIds.add(id);
316
+ parsed.push({ id, question });
317
+ if (parsed.length === MAX_QUESTIONS)
318
+ break;
319
+ }
320
+ return parsed.length ? parsed : null;
321
+ }
322
+ /**
323
+ * The id-keyed counterpart of questionAnswersByQuestion, for a protocol
324
+ * that answers by id (codex's requestUserInput) rather than by question
325
+ * text.
326
+ *
327
+ * Each protocol entry's id is paired with the AskQuestion parsed from that
328
+ * same entry, so a Q:/A: block the card wrote is matched against the text
329
+ * that produced it and filed under the id that asked. Only ids with a real
330
+ * block are answered — a question left unanswered gets no entry rather
331
+ * than a guess. When the reply carries no blocks at all, the flat path
332
+ * applies with exactly one question: the whole message is that question's
333
+ * answer, as it is for a phone or an older client. With more than one,
334
+ * nothing is filed.
335
+ */
336
+ export function questionAnswersById(message, questions) {
337
+ const answers = Object.create(null);
338
+ const idByText = new Map();
339
+ const ambiguous = new Set();
340
+ for (const { id, question } of questions) {
341
+ if (idByText.has(question.question)) {
342
+ // Two questions with identical text cannot be told apart in a block;
343
+ // answering either id would be a guess, so neither is answered.
344
+ ambiguous.add(question.question);
345
+ continue;
346
+ }
347
+ idByText.set(question.question, id);
348
+ }
349
+ // A block runs from one Q: header to the next (blank lines inside an
350
+ // answer are the answer's, not block boundaries), so the reply is scanned
351
+ // for header-to-header spans instead of split on every blank line.
352
+ const blocks = message.matchAll(/(?:^|\n\n)Q: ([\s\S]+?)\nA: ([\s\S]*?)(?=\n\nQ: |$)/g);
353
+ let sawBlock = false;
354
+ for (const match of blocks) {
355
+ sawBlock = true;
356
+ const asked = match[1].trim();
357
+ if (ambiguous.has(asked))
188
358
  continue;
189
- // Only a question this ask actually posed. An unrecognized block is
190
- // dropped rather than filed under a key the tool never asked about.
191
- const question = known.get(match[1].trim());
192
- if (question)
193
- answers[question] = match[2].trim();
359
+ const id = idByText.get(asked);
360
+ const answer = match[2].trim();
361
+ // A blank A: is no answer: the id stays unanswered rather than filed
362
+ // as an empty string.
363
+ if (id && answer)
364
+ answers[id] = answer;
194
365
  }
195
366
  if (Object.keys(answers).length)
196
367
  return answers;
368
+ // A structured reply that matched a block is authoritative even when it
369
+ // answered nothing: falling through to the flat fallback would file the
370
+ // literal Q:/A: text as the person's answer.
371
+ if (sawBlock)
372
+ return {};
197
373
  const only = questions.length === 1 ? questions[0] : undefined;
198
374
  const flat = message.trim();
199
- return only && flat ? { [only.question]: flat } : {};
375
+ return only && flat ? { [only.id]: flat } : {};
200
376
  }
@@ -9,3 +9,18 @@ export const BOT_PROFILE_LIMITS = {
9
9
  voice: 200,
10
10
  soul: 24_000,
11
11
  };
12
+ /** A name or title is quoted inside prompts and cards as one line — a
13
+ * roster entry, a "Name: …" speaker line, the bracketed provenance note.
14
+ * Text that can break out of that line (a newline, a control byte, the
15
+ * Unicode separators) is refused at the door rather than flattened later,
16
+ * so what the person sees in the sidebar is what every prompt sees too.
17
+ * Written as a scan because a control-character class is the kind of
18
+ * literal the linter (rightly) refuses. */
19
+ export function fitsOnOneLine(value) {
20
+ for (let i = 0; i < value.length; i += 1) {
21
+ const code = value.charCodeAt(i);
22
+ if (code < 0x20 || (code >= 0x7f && code <= 0x9f) || code === 0x2028 || code === 0x2029)
23
+ return false;
24
+ }
25
+ return true;
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -14,7 +14,7 @@ export const CREDENTIAL_TARGETS = {
14
14
  label: "Box API key",
15
15
  description: "Gives bots an isolated cloud computer when Box is selected.",
16
16
  placeholder: "Paste your Box API key",
17
- helpUrl: "https://docs.ascii.dev/box/api-keys",
17
+ helpUrl: "https://docs.boat.dev/api-keys",
18
18
  },
19
19
  opencodeGoApiKey: {
20
20
  label: "OpenCode API key",
@@ -0,0 +1,15 @@
1
+ // Keep mention recognition identical in the renderer and server routing.
2
+ // Deliberately allow only opening punctuation/Markdown markers here: `/@bot`
3
+ // remains a URL path, while `(@bot)`, `【@bot】`, and `**@bot**` are tags.
4
+ const OPENING_MENTION_BOUNDARY = /[(*_~[{<'"\u2018\u201c\u3008\u300a\u300c\u300e\u3010\uff08]/u;
5
+ /** Return whether an at-sign starts a standalone mention at this offset. */
6
+ export function isMentionBoundary(text, at) {
7
+ if (at === 0)
8
+ return true;
9
+ const before = text[at - 1];
10
+ return /\s/u.test(before) || OPENING_MENTION_BOUNDARY.test(before);
11
+ }
12
+ /** A matched name must not be the prefix of a longer Unicode word. */
13
+ export function isMentionNameContinuation(value) {
14
+ return value !== undefined && /^[\p{L}\p{N}\p{M}_]/u.test(value);
15
+ }