openmausbot 0.1.85 → 0.1.86

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 (193) hide show
  1. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  2. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  3. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  4. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  5. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  6. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  7. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  8. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  10. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  11. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  12. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  13. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  14. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  16. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  17. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  18. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  19. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  20. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  21. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  22. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  23. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  24. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  25. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  26. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  28. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  29. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  30. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  31. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  32. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  33. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  34. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  35. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  36. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  37. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  38. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  43. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  44. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  45. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  46. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  47. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  48. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  49. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  50. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  51. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  52. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  53. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  54. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  55. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  56. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  57. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  58. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  59. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  60. package/dist/assets/abnfDiagram-O67JEVCF-DpUZNnFD.js +1 -0
  61. package/dist/assets/arc-DW7mrI4p.js +1 -0
  62. package/dist/assets/architectureDiagram-NJMV4G6O-CyeZ5WLZ.js +36 -0
  63. package/dist/assets/blockDiagram-BEXU5L5S-CBVaNjO7.js +140 -0
  64. package/dist/assets/c4Diagram-YGBWAQC7-sf8d41uT.js +38 -0
  65. package/dist/assets/channel-BXFkgus9.js +1 -0
  66. package/dist/assets/chunk-2Q5K7J3B-BOGvy7Y_.js +1 -0
  67. package/dist/assets/chunk-3FUC2YCW-BiF9zyiz.js +2 -0
  68. package/dist/assets/chunk-5VM5RSS4-EFuZ1SBZ.js +15 -0
  69. package/dist/assets/chunk-GTNCS2PH-pJLNSAT7.js +88 -0
  70. package/dist/assets/chunk-GWA4HPMP-Cb-3_3IT.js +1 -0
  71. package/dist/assets/chunk-JWPE2WC7-DAf5aogn.js +1 -0
  72. package/dist/assets/chunk-NETBCI7D-DR3kiwZz.js +1 -0
  73. package/dist/assets/chunk-WEXAMYUT-BmU8PeyM.js +1 -0
  74. package/dist/assets/chunk-XXDRQBXY-Buh8g8OT.js +1 -0
  75. package/dist/assets/classDiagram-v2-NBCMYWYE-BurfmMWM.js +217 -0
  76. package/dist/assets/cose-bilkent-JH36ORCC-KkGZUEkU.js +1 -0
  77. package/dist/assets/cynefin-OW5HDTMX-C3PpV96d.js +166 -0
  78. package/dist/assets/cynefinDiagram-VND7K2PF-Dt8JwJaI.js +62 -0
  79. package/dist/assets/cytoscape.esm-BB4DxJjf.js +321 -0
  80. package/dist/assets/dagre-6A5THRUB-DkjN8266.js +4 -0
  81. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  82. package/dist/assets/diagram-22UHCM2B-DUAJS0JV.js +200 -0
  83. package/dist/assets/diagram-3UASUU5V-Dw4KhF1a.js +24 -0
  84. package/dist/assets/diagram-ATOU4E4O-Dd1gmQqf.js +3 -0
  85. package/dist/assets/diagram-CDSNMT55-CjwGs0Yw.js +30 -0
  86. package/dist/assets/diagram-MLGK6HIB-BL-69Zrq.js +24 -0
  87. package/dist/assets/diagram-MPIPVDR6-5Zi2N7wo.js +41 -0
  88. package/dist/assets/ebnfDiagram-ZINNZB2B-D9NekWUX.js +1 -0
  89. package/dist/assets/elk-276RUBZZ-CkaSQjxw.js +27 -0
  90. package/dist/assets/erDiagram-OPXOYQCR-CdAVBV8-.js +99 -0
  91. package/dist/assets/flowDiagram-KWPJA3E3-Bm_lWWH7.js +213 -0
  92. package/dist/assets/ganttDiagram-FUAMR5RP-FZ8ejYWJ.js +292 -0
  93. package/dist/assets/gitGraphDiagram-X574FWY7-Dlv9rizZ.js +106 -0
  94. package/dist/assets/graph-D8UmR1vK.js +1 -0
  95. package/dist/assets/index-BGgqlWPJ.css +1 -0
  96. package/dist/assets/index-BtHo8ZKl.js +577 -0
  97. package/dist/assets/{index-CTNNoqSJ.js → index-h9FFNwTu.js} +1 -1
  98. package/dist/assets/infoDiagram-VRGFBTTK-BdQ_gW5B.js +2 -0
  99. package/dist/assets/init-Gi6I4Gst.js +1 -0
  100. package/dist/assets/ishikawaDiagram-OU5B5YK6-EwssEp2X.js +70 -0
  101. package/dist/assets/journeyDiagram-ZHPQQLJL-BX1G1vOd.js +139 -0
  102. package/dist/assets/kanban-definition-PNTS6WVX-BB4kAsyC.js +89 -0
  103. package/dist/assets/linear-3su2TG93.js +1 -0
  104. package/dist/assets/mermaid.core-CDD1E0s9.js +311 -0
  105. package/dist/assets/mindmap-definition-NLK3R4M7-Ba50MsNK.js +96 -0
  106. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  107. package/dist/assets/pegDiagram-GJSIUBJH-DZLkkZlM.js +1 -0
  108. package/dist/assets/pieDiagram-5QR66LMP-DaTz77Uq.js +39 -0
  109. package/dist/assets/quadrantDiagram-O4NWA36T-CpaiidpR.js +7 -0
  110. package/dist/assets/railroadDiagram-XR7U4H2S-mN4JrMeI.js +1 -0
  111. package/dist/assets/requirementDiagram-PLB6GJNP-BSNDRpg7.js +84 -0
  112. package/dist/assets/sankeyDiagram-IPEJSGJF-FngzGNug.js +40 -0
  113. package/dist/assets/sequenceDiagram-PO4LG4MO-CM4pCC3L.js +169 -0
  114. package/dist/assets/sizeCapture-INFHLROL-CRICHXkw.js +1 -0
  115. package/dist/assets/stateDiagram-v2-GCMORJYK-oyTWpxH2.js +291 -0
  116. package/dist/assets/swimlanes-2SLR337P-D9h9mPBq.js +1 -0
  117. package/dist/assets/swimlanesDiagram-TC7HE7FX-BWrvB2ak.js +8 -0
  118. package/dist/assets/timeline-definition-EJHVYXUP-BJUlN9bW.js +120 -0
  119. package/dist/assets/usecaseDiagram-POWQR4AR-3uBNkbAb.js +370 -0
  120. package/dist/assets/vennDiagram-UO4OBE2U-D2odalzT.js +34 -0
  121. package/dist/assets/wardleyDiagram-VNRHLVJA-DHgFaB9S.js +78 -0
  122. package/dist/assets/xychartDiagram-PMCCYNJV-w5GYM2h0.js +7 -0
  123. package/dist/index.html +2 -2
  124. package/dist-server/container-mcp.js +19 -10
  125. package/dist-server/drivers/agents-proxy.js +16159 -1596
  126. package/dist-server/enterprise/LICENSE +32 -0
  127. package/dist-server/enterprise/server/index.js +5 -5
  128. package/dist-server/index.js +4779 -1667
  129. package/dist-server/mcp-server.js +3 -1
  130. package/dist-server/openmausbot.js +4679 -4266
  131. package/dist-server/pair-cli.js +4679 -4266
  132. package/dist-server/server/admin-activity.js +410 -0
  133. package/dist-server/server/bot-visibility.js +462 -0
  134. package/dist-server/server/box.js +2 -1
  135. package/dist-server/server/browser-runtime.js +1 -1
  136. package/dist-server/server/channel-queue.js +2 -0
  137. package/dist-server/server/cli-prompts.js +24 -2
  138. package/dist-server/server/cli.js +79 -10
  139. package/dist-server/server/composio.js +49 -11
  140. package/dist-server/server/config.js +79 -13
  141. package/dist-server/server/decision-log.js +209 -48
  142. package/dist-server/server/default-model-selection.js +20 -3
  143. package/dist-server/server/delegations.js +11 -9
  144. package/dist-server/server/drivers/acp/core.js +110 -22
  145. package/dist-server/server/drivers/acp/grok.js +4 -3
  146. package/dist-server/server/drivers/agents-call.js +1123 -0
  147. package/dist-server/server/drivers/agents-catalog.js +659 -0
  148. package/dist-server/server/drivers/agents-client.js +28 -0
  149. package/dist-server/server/drivers/agents-proxy.js +18 -1722
  150. package/dist-server/server/drivers/chat-mcp-tools.js +17 -2
  151. package/dist-server/server/drivers/claude.js +21 -6
  152. package/dist-server/server/drivers/codex-mcp-names.js +44 -0
  153. package/dist-server/server/drivers/codex.js +26 -4
  154. package/dist-server/server/drivers/grok.js +2 -1
  155. package/dist-server/server/drivers/openai-compat.js +15 -8
  156. package/dist-server/server/enterprise.js +97 -16
  157. package/dist-server/server/external-runtime.js +83 -0
  158. package/dist-server/server/guarded-requests.js +60 -0
  159. package/dist-server/server/harness/http.js +61 -0
  160. package/dist-server/server/hosted-models.js +151 -0
  161. package/dist-server/server/hosted-slack.js +11 -0
  162. package/dist-server/server/index.js +1718 -348
  163. package/dist-server/server/managed-desktop.js +79 -14
  164. package/dist-server/server/managed-policy.js +153 -0
  165. package/dist-server/server/message-db.js +28 -0
  166. package/dist-server/server/model-context-window.js +7 -0
  167. package/dist-server/server/model-prices.js +121 -0
  168. package/dist-server/server/notify.js +10 -1
  169. package/dist-server/server/peer-roster.js +13 -2
  170. package/dist-server/server/recent-work.js +15 -0
  171. package/dist-server/server/request-auth.js +96 -0
  172. package/dist-server/server/room-handoffs.js +25 -11
  173. package/dist-server/server/routes/hosted-slack.js +20 -0
  174. package/dist-server/server/routes/table.js +14 -0
  175. package/dist-server/server/routines.js +22 -0
  176. package/dist-server/server/spend.js +111 -16
  177. package/dist-server/server/steer-queue.js +6 -1
  178. package/dist-server/server/store.js +292 -21
  179. package/dist-server/server/team-backup.js +10 -1
  180. package/dist-server/server/thread-starters.js +53 -0
  181. package/dist-server/server/usage-ledger.js +31 -8
  182. package/dist-server/server/workspace-backup-policy.js +9 -2
  183. package/dist-server/server/workspace-backup.js +7 -4
  184. package/dist-server/shared/mention-boundary.js +15 -0
  185. package/dist-server/shared/routines.js +7 -1
  186. package/dist-server/shared/team-backup.js +2 -0
  187. package/dist-server/src/lib/composer-attachments.js +16 -0
  188. package/dist-server/vps-container-mcp.js +19 -10
  189. package/enterprise/README.md +33 -12
  190. package/enterprise/server/index.js +5 -5
  191. package/package.json +1 -1
  192. package/dist/assets/index-Ba9G44HI.js +0 -310
  193. package/dist/assets/index-CkRNp7wX.css +0 -1
@@ -24,7 +24,15 @@ function monthKey(at) {
24
24
  export function usageFileFor(dataDir, at) {
25
25
  return join(dataDir, DIR, `${monthKey(at)}.jsonl`);
26
26
  }
27
- /** Append one settled turn. Fire-and-forget; see the module comment. */
27
+ /** Identifies one row: a thread runs one turn at a time, so a bot cannot
28
+ * settle two turns on the same thread in the same millisecond. Lets the
29
+ * spend cap tell a booked row it already counted from memory apart from the
30
+ * same row read back from the file. */
31
+ export function usageRowKey(row) {
32
+ return `${row.at}|${row.threadId}|${row.botId}`;
33
+ }
34
+ /** Append one settled turn. Fire-and-forget (see the module comment): the
35
+ * returned promise never rejects, and says whether the row reached disk. */
28
36
  export function appendUsage(dataDir, row) {
29
37
  const record = {
30
38
  ...row,
@@ -34,10 +42,14 @@ export function appendUsage(dataDir, row) {
34
42
  ...(typeof row.cachedInput === "number" ? { cachedInput: clean(row.cachedInput) } : {}),
35
43
  costUsd: finiteOrNull(row.costUsd),
36
44
  };
45
+ // A cost is labelled with where it came from; an unpriced row carries no label.
46
+ if (record.costUsd === null)
47
+ delete record.costSource;
48
+ else
49
+ record.costSource = row.costSource === "estimated" ? "estimated" : "reported";
37
50
  const previous = writeQueues.get(dataDir) ?? Promise.resolve();
38
- const queued = previous
39
- .then(() => write(dataDir, record))
40
- .catch(() => {
51
+ const attempt = previous.then(() => write(dataDir, record));
52
+ const queued = attempt.then(() => undefined, () => {
41
53
  /* bookkeeping must never take down the turn */
42
54
  });
43
55
  writeQueues.set(dataDir, queued);
@@ -45,6 +57,7 @@ export function appendUsage(dataDir, row) {
45
57
  if (writeQueues.get(dataDir) === queued)
46
58
  writeQueues.delete(dataDir);
47
59
  });
60
+ return attempt.then(() => true, () => false);
48
61
  }
49
62
  async function write(dataDir, record) {
50
63
  await mkdir(join(dataDir, DIR), { recursive: true, mode: 0o700 });
@@ -165,7 +178,13 @@ function groupOf(row, groupBy) {
165
178
  }
166
179
  }
167
180
  function emptyGroup(key, label) {
168
- return { key, label, turns: 0, input: 0, output: 0, cachedInput: 0, costUsd: null, unpriced: 0, billableUsd: null };
181
+ return { key, label, turns: 0, input: 0, output: 0, cachedInput: 0, costUsd: null, estimatedUsd: null, unpriced: 0, billableUsd: null };
182
+ }
183
+ /** Where a row's cost came from, for rows old and new; null when it has none. */
184
+ export function costSourceOf(row) {
185
+ if (finiteOrNull(row.costUsd) === null)
186
+ return null;
187
+ return row.costSource === "estimated" ? "estimated" : "reported";
169
188
  }
170
189
  function add(group, row, prices) {
171
190
  group.turns += 1;
@@ -175,8 +194,11 @@ function add(group, row, prices) {
175
194
  const cost = finiteOrNull(row.costUsd);
176
195
  if (cost === null)
177
196
  group.unpriced += 1;
178
- else
197
+ else {
179
198
  group.costUsd = (group.costUsd ?? 0) + cost;
199
+ if (costSourceOf(row) === "estimated")
200
+ group.estimatedUsd = (group.estimatedUsd ?? 0) + cost;
201
+ }
180
202
  const billable = prices ? billableFor(row, prices) : null;
181
203
  if (billable !== null)
182
204
  group.billableUsd = (group.billableUsd ?? 0) + billable;
@@ -207,7 +229,7 @@ export function summarizeUsage(rows, groupBy, prices = null) {
207
229
  }
208
230
  return { groups: ordered, total };
209
231
  }
210
- function csvCell(value) {
232
+ export function csvCell(value) {
211
233
  if (value === null)
212
234
  return "";
213
235
  const text = String(value);
@@ -218,7 +240,7 @@ function csvCell(value) {
218
240
  }
219
241
  /** One line per turn, spreadsheet-ready; a billable column when a price list is given. */
220
242
  export function usageCsv(rows, prices = null) {
221
- const header = ["time", "bot", "model", "engine", "triggered_by", "input_tokens", "output_tokens", "cached_input_tokens", "cost_usd", ...(prices ? ["billable_usd"] : []), "thread"];
243
+ const header = ["time", "bot", "model", "engine", "triggered_by", "input_tokens", "output_tokens", "cached_input_tokens", "cost_usd", "cost_source", ...(prices ? ["billable_usd"] : []), "thread"];
222
244
  const lines = [header.join(",")];
223
245
  for (const row of rows) {
224
246
  lines.push([
@@ -231,6 +253,7 @@ export function usageCsv(rows, prices = null) {
231
253
  clean(row.output),
232
254
  clean(row.cachedInput),
233
255
  finiteOrNull(row.costUsd),
256
+ costSourceOf(row),
234
257
  ...(prices ? [billableFor(row, prices)] : []),
235
258
  row.threadId,
236
259
  ].map(csvCell).join(","));
@@ -22,7 +22,14 @@ export function restoredWorkspaceConfig(portable, destination) {
22
22
  }
23
23
  /** Exact app-owned authentication paths, not a scan of user document text. */
24
24
  export function excludedWorkspaceAuthPath(path) {
25
- return /^(?:(?:providers|caddy|chrome-profile|\.agent-browser)(?:\/|$)|workspace-credentials\.json$|browser-engine-key$)/.test(path) ||
26
- /^(?:config\.json|webhooks\.json|workspace-credentials\.json|browser-engine-key|sessions\.json|tunnel-account\.json)\.\d+(?:\.[0-9a-f-]+)?\.tmp$/.test(path) ||
25
+ return /^(?:(?:providers|caddy|chrome-profile|\.agent-browser)(?:\/|$)|workspace-credentials\.json$|external-runtimes\.json$|browser-engine-key$)/.test(path) ||
26
+ /^(?:config\.json|webhooks\.json|workspace-credentials\.json|external-runtimes\.json|browser-engine-key|sessions\.json|tunnel-account\.json)\.\d+(?:\.[0-9a-f-]+)?\.tmp$/.test(path) ||
27
27
  /^(?:vm-home|vm-homes\/[^/]+)\/\.browser-profiles(?:\/|$)/.test(path);
28
28
  }
29
+ /** Per-turn engine hook bearers (`hook-tokens/<digest>.token`, written by the
30
+ * Claude driver). They are dead once their turn settles, so they are never
31
+ * exported. Unlike the saved auth paths above they are not refused on import:
32
+ * v0.1.85 did export them, and such a backup must still restore, without them. */
33
+ export function ephemeralWorkspaceTokenPath(path) {
34
+ return /^hook-tokens(?:\/|$)/.test(path);
35
+ }
@@ -11,7 +11,7 @@ import { fromMarkdown } from "mdast-util-from-markdown";
11
11
  import { writeFileAtomic } from "./atomic.js";
12
12
  import { escapeAttribute, splitTranscriptAttachments } from "../src/lib/composer-attachments.js";
13
13
  import { WORKSPACE_BACKUP_CLIENT_KEYS } from "../shared/workspace-backup-client.js";
14
- import { excludedWorkspaceAuthPath, portableWorkspaceConfig, restoredWorkspaceConfig } from "./workspace-backup-policy.js";
14
+ import { ephemeralWorkspaceTokenPath, excludedWorkspaceAuthPath, portableWorkspaceConfig, restoredWorkspaceConfig } from "./workspace-backup-policy.js";
15
15
  export const MAX_WORKSPACE_BACKUP_BYTES = 10 * 1024 ** 3;
16
16
  export const MAX_WORKSPACE_BACKUP_FILES = 100_000;
17
17
  export const MAX_WORKSPACE_BACKUP_UPLOAD_BYTES = MAX_WORKSPACE_BACKUP_BYTES + 256 * 1024 ** 2;
@@ -311,7 +311,7 @@ export async function createWorkspaceBackup(dataDir, options) {
311
311
  if (!prefix && excluded(name))
312
312
  continue;
313
313
  const path = prefix ? `${prefix}/${name}` : name;
314
- if (excludedWorkspaceAuthPath(path))
314
+ if (excludedWorkspaceAuthPath(path) || ephemeralWorkspaceTokenPath(path))
315
315
  continue;
316
316
  // Do not silently skip noncanonical source spellings: reject them so
317
317
  // a case-sensitive host cannot export auth paths active on Windows/Mac.
@@ -781,9 +781,12 @@ function prepareRestore(dataDir, id, manifest) {
781
781
  folder(prepared);
782
782
  // Keep the authenticated original staging tree intact for reinspection and
783
783
  // recovery. Only this installation copy has paths/scheduling adapted.
784
- for (const entry of manifest.entries.filter((entry) => entry.type === "directory"))
784
+ // An archive from a release that exported per-turn hook tokens still
785
+ // restores; the dead tokens themselves are never installed.
786
+ const installed = manifest.entries.filter((entry) => !ephemeralWorkspaceTokenPath(entry.path));
787
+ for (const entry of installed.filter((entry) => entry.type === "directory"))
785
788
  folder(join(prepared, entry.path));
786
- for (const entry of manifest.entries) {
789
+ for (const entry of installed) {
787
790
  const destination = join(prepared, entry.path);
788
791
  if (entry.type === "file")
789
792
  copyRegular(join(job, "staged", "data", entry.path), destination);
@@ -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
+ }
@@ -1 +1,7 @@
1
- export {};
1
+ /** The statuses both failure indicators count: a run that went wrong and has
2
+ * not been acknowledged. One home so the sidebar dot, the errors pill, the
3
+ * logs filter and the bulk-seen sweep cannot drift apart. */
4
+ export const ROUTINE_PROBLEM_STATUSES = ["failed", "missed"];
5
+ export function isRoutineProblemRun(run) {
6
+ return ROUTINE_PROBLEM_STATUSES.includes(run.status);
7
+ }
@@ -25,6 +25,8 @@ const task = z.object({
25
25
  titleFromFirstMessage: z.literal(true).optional(),
26
26
  openedBy: z.object({ botId: key, name, at: timestamp, kind: z.enum(["pair", "work"]).optional() }).optional(),
27
27
  closedBy: z.object({ botId: key, name, at: timestamp }).optional(),
28
+ /** Only true travels. Absence is unpinned, including backups from before pins. */
29
+ pinned: z.literal(true).optional(),
28
30
  activeLeafId: key.nullable(),
29
31
  messages: z.array(message).max(100_000),
30
32
  });
@@ -691,3 +691,19 @@ export async function intakeFiles(_files, _opts) {
691
691
  notice: pathless && failed ? `${pathless} (${failed})` : (pathless ?? failed),
692
692
  };
693
693
  }
694
+ /**
695
+ * Whether the composer may pull keyboard focus back into its textarea after an
696
+ * attachment lands. The draft was the writer's place when focus is still there,
697
+ * has fallen to the page (a disabled element drops it), or sits on a control
698
+ * inside the composer such as the paperclip button after the file dialog. A
699
+ * focused control elsewhere — a dialog, the thread list — is left alone.
700
+ */
701
+ export function composerShouldRefocus(active, input) {
702
+ if (!active || active === input)
703
+ return true;
704
+ const root = input.ownerDocument;
705
+ if (active === root.body || active === root.documentElement)
706
+ return true;
707
+ const composer = input.closest("[data-tour=composer]");
708
+ return Boolean(composer?.contains(active));
709
+ }
@@ -15372,6 +15372,20 @@ var defaultModelSelectionSchema = external_exports.object({
15372
15372
  (selection) => selection.variant === void 0 || selection.effort === void 0,
15373
15373
  "choose either a model variant or an effort level"
15374
15374
  );
15375
+ var threadsConfigSchema = external_exports.object({
15376
+ maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS),
15377
+ /** Cap each per-thread events/ and native/ NDJSON log at this many
15378
+ * bytes; absent (the default) keeps today's unbounded growth (#1280). */
15379
+ eventLogMaxBytes: external_exports.number().int().min(MIN_THREAD_EVENT_LOG_BYTES).max(MAX_THREAD_EVENT_LOG_BYTES).optional(),
15380
+ /** Days a closed or archived thread's event logs survive (#1280).
15381
+ * Absent keeps them forever. */
15382
+ eventLogRetentionDays: external_exports.number().int().min(1).max(3650).optional()
15383
+ }).strict();
15384
+ var threadsPatchSchema = threadsConfigSchema.extend({
15385
+ maxConcurrentPerBot: threadsConfigSchema.shape.maxConcurrentPerBot.optional(),
15386
+ eventLogMaxBytes: threadsConfigSchema.shape.eventLogMaxBytes.nullable(),
15387
+ eventLogRetentionDays: threadsConfigSchema.shape.eventLogRetentionDays.nullable()
15388
+ });
15375
15389
  var appConfigSchema = external_exports.object({
15376
15390
  /** Verified by the dedicated domain endpoint, never a generic config patch. */
15377
15391
  customDomain: external_exports.string().optional(),
@@ -15464,15 +15478,10 @@ var appConfigSchema = external_exports.object({
15464
15478
  compactAt: external_exports.number().positive().max(1e7).optional(),
15465
15479
  autoCompact: external_exports.boolean().optional()
15466
15480
  }).optional(),
15467
- threads: external_exports.object({
15468
- maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS),
15469
- /** Cap each per-thread events/ and native/ NDJSON log at this many
15470
- * bytes; absent (the default) keeps today's unbounded growth (#1280). */
15471
- eventLogMaxBytes: external_exports.number().int().min(MIN_THREAD_EVENT_LOG_BYTES).max(MAX_THREAD_EVENT_LOG_BYTES).optional(),
15472
- /** Days a closed or archived thread's event logs survive (#1280).
15473
- * Absent keeps them forever. */
15474
- eventLogRetentionDays: external_exports.number().int().min(1).max(3650).optional()
15475
- }).strict().optional(),
15481
+ threads: threadsConfigSchema.optional(),
15482
+ /** The authorization decision log (server/decision-log.ts): days of month
15483
+ * files kept, at least; OMB_DECISION_RETENTION_DAYS wins when set. */
15484
+ decisions: external_exports.object({ retentionDays: external_exports.number().int().min(1).max(3650).optional() }).strict().optional(),
15476
15485
  localVm: localVmConfigSchema.optional(),
15477
15486
  features: featureConfigSchema.optional(),
15478
15487
  onboarding: onboardingConfigSchema.optional(),
@@ -15489,7 +15498,7 @@ var appConfigSchema = external_exports.object({
15489
15498
  var storedAppConfigSchema = appConfigSchema.extend({
15490
15499
  browserProfiles: storedBrowserProfilesSchema.optional()
15491
15500
  });
15492
- var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true });
15501
+ var appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true }).extend({ threads: threadsPatchSchema.optional() });
15493
15502
  var jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
15494
15503
  var DATA_DIR = process.env.OMB_DATA_DIR ?? join2(homedir2(), ".openmausbot");
15495
15504
  var LEGACY_DATA_DIR = join2(homedir2(), ".opengrokbot");
@@ -5,24 +5,42 @@ folder has its own [LICENSE](./LICENSE); everything outside it is Apache 2.0.
5
5
 
6
6
  **Delete this folder and you have the open-source edition.** Core only
7
7
  reaches the layer through one hook point, `server/enterprise.ts`, which
8
- loads `enterprise/server/index.ts` if it exists and otherwise reports the
9
- open-source edition. No core file imports anything from here.
8
+ loads `enterprise/server/index.ts` (or the bundled `index.js`) if it exists
9
+ and otherwise reports the open-source edition. No core file imports anything
10
+ from here.
11
+
12
+ Where core looks, in order: `OMB_ENTERPRISE_DIR` when it is set (and then
13
+ only there); otherwise beside the server root (a checkout's `enterprise/`,
14
+ the npm package's `<package>/enterprise/`), then inside it
15
+ (`dist-server/enterprise/`, which is all the Docker image and the packaged
16
+ desktop carry; `scripts/bundle-server.mjs` writes the bundled layer and this
17
+ LICENSE there).
10
18
 
11
19
  ## How a deployment turns enterprise
12
20
 
13
21
  Set `OMB_LICENSE_KEY` on the server. The key is `omb1.<claims>.<signature>`:
14
22
  the claims are visible JSON (who it is for, which entitlements, when it
15
23
  expires), signed with an Ed25519 key whose public half is baked into
16
- `server/license.ts`. Verification is offline, and one build serves every
17
- customer, because the key decides the feature set rather than the code.
24
+ `enterprise/server/license.ts`. Verification is offline, and one build serves
25
+ every customer, because the key decides the feature set rather than the code.
18
26
 
19
27
  `GET /api/edition` reports the outcome:
20
28
 
21
29
  ```json
22
- { "edition": "enterprise", "customer": "Acme", "features": ["sso", "whitelabel"], "expiresAt": "2027-09-02" }
23
- { "edition": "oss", "features": [], "notice": "OMB_LICENSE_KEY expired on 2027-09-02; renew it to keep enterprise features" }
30
+ { "edition": "enterprise", "customer": "Acme", "features": ["budgets", "whitelabel"], "expiresAt": "2027-09-02", "expiresInDays": 12 }
31
+ { "edition": "enterprise", "customer": "Acme", "features": ["budgets", "whitelabel"], "expiresAt": "2027-09-02", "expiresInDays": -2, "graceEndsAt": "2027-09-09", "notice": "OMB_LICENSE_KEY expired on 2027-09-02; enterprise features keep working until 2027-09-09 while it is renewed" }
32
+ { "edition": "oss", "features": [], "notice": "enterprise layer disabled: OMB_LICENSE_KEY expired on 2027-09-02; renew it to keep enterprise features" }
24
33
  ```
25
34
 
35
+ `expiresInDays` is present whenever the key has an expiry; a non-admin
36
+ session's `/api/edition` leaves out `expiresInDays`, `graceEndsAt` and the
37
+ notice. From 30 days
38
+ before it, the server logs a warning at startup and admins see a banner in
39
+ Settings. After the expiry date the features keep working for a 7-day grace
40
+ period, with the notice above and a banner saying until when; then they stop,
41
+ without a restart. The key format and its signature check do not change: the
42
+ layer simply accepts a key that expired less than the grace period ago.
43
+
26
44
  A missing, altered, or expired key does not stop an ordinary standalone
27
45
  server: it runs the open-source edition and the notice says what to fix.
28
46
  A workspace explicitly configured for hosted sign-in fails closed for remote
@@ -31,12 +49,15 @@ must not silently fall back to standalone email or pairing credentials.
31
49
 
32
50
  ## Entitlement ids
33
51
 
34
- | id | grants |
35
- |---|---|
36
- | `whitelabel` | product name, tagline, accent colour, logo, favicon and support link from `brand.json` (below) |
37
- | `sso` | identity-header trust behind an OIDC proxy |
38
- | `admin` | Settings → Workspaces and the optional hosted workspace sign-in adapter |
39
- | `budgets` | per-bot and per-section spend limits |
52
+ | id | grants | status |
53
+ |---|---|---|
54
+ | `whitelabel` | product name, tagline, accent colour, logo, favicon and support link from `brand.json` (below) | shipped |
55
+ | `sso` | sign-in through a company identity provider | planned: nothing checks it yet |
56
+ | `admin` | Settings → Workspaces and the optional hosted workspace sign-in adapter | shipped |
57
+ | `budgets` | one monthly spend limit for the whole workspace, refusing new turns at the cap, with a warning percentage and admin notices (not per bot or per section) | shipped |
58
+ | `billing` | sell prices per model: a billable column in Usage → History and its CSV, and per-model prices that replace list prices in cost estimates | shipped |
59
+
60
+ [FEATURES](./FEATURES) is the authoritative list.
40
61
 
41
62
  Core gates a feature with `entitled("id")` from `server/enterprise.ts`.
42
63
  Unknown ids are carried in the key but grant nothing, so keys can be issued
@@ -14579,9 +14579,6 @@ function verifyLicenseKey(key, options = {}) {
14579
14579
  // enterprise/server/workspace-access.ts
14580
14580
  import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
14581
14581
 
14582
- // server/enterprise.ts
14583
- import { join as join2 } from "node:path";
14584
-
14585
14582
  // server/proxy-paths.ts
14586
14583
  import { existsSync } from "node:fs";
14587
14584
  import { dirname, join } from "node:path";
@@ -14615,7 +14612,7 @@ var layerSchema = external_exports.object({
14615
14612
  features: external_exports.array(external_exports.string().min(1)),
14616
14613
  expiresAt: external_exports.string().nullable()
14617
14614
  });
14618
- var DEFAULT_DIR = join2(SERVER_ROOT, "..", "enterprise");
14615
+ var DAY_MS = 24 * 60 * 6e4;
14619
14616
  function hostedWorkspaceConfiguration(env = process.env) {
14620
14617
  try {
14621
14618
  const origin = (raw) => {
@@ -14934,9 +14931,12 @@ function createWorkspaceAccess(options) {
14934
14931
  }
14935
14932
 
14936
14933
  // enterprise/server/index.ts
14934
+ var MAX_GRACE_DAYS = 14;
14935
+ var DAY_MS2 = 24 * 60 * 6e4;
14937
14936
  function register(options) {
14938
14937
  if (!options.licenseKey) return null;
14939
- const claims = verifyLicenseKey(options.licenseKey);
14938
+ const graceDays = Math.min(MAX_GRACE_DAYS, Math.max(0, Number.isFinite(options.graceDays) ? options.graceDays : 0));
14939
+ const claims = verifyLicenseKey(options.licenseKey, { now: new Date((options.now ?? Date.now()) - graceDays * DAY_MS2) });
14940
14940
  return { customer: claims.customer, features: claims.features, expiresAt: claims.expires };
14941
14941
  }
14942
14942
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openmausbot",
3
- "version": "0.1.85",
3
+ "version": "0.1.86",
4
4
  "description": "Run the OpenMausBot server anywhere and pair your devices to it",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",