openmausbot 0.1.86 → 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 (166) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/{abnfDiagram-O67JEVCF-DpUZNnFD.js → abnfDiagram-O67JEVCF-BTiuL79F.js} +1 -1
  3. package/dist/assets/{arc-DW7mrI4p.js → arc-C88Blc2v.js} +1 -1
  4. package/dist/assets/{architectureDiagram-NJMV4G6O-CyeZ5WLZ.js → architectureDiagram-NJMV4G6O-BZsJIZrb.js} +1 -1
  5. package/dist/assets/{blockDiagram-BEXU5L5S-CBVaNjO7.js → blockDiagram-BEXU5L5S-CVeMe9Ah.js} +1 -1
  6. package/dist/assets/{c4Diagram-YGBWAQC7-sf8d41uT.js → c4Diagram-YGBWAQC7-Cedk3Bhx.js} +1 -1
  7. package/dist/assets/channel-taCNRlUU.js +1 -0
  8. package/dist/assets/{chunk-2Q5K7J3B-BOGvy7Y_.js → chunk-2Q5K7J3B-D-VRqzTB.js} +1 -1
  9. package/dist/assets/{chunk-3FUC2YCW-BiF9zyiz.js → chunk-3FUC2YCW-BChov01Y.js} +2 -2
  10. package/dist/assets/{chunk-5VM5RSS4-EFuZ1SBZ.js → chunk-5VM5RSS4-C-TfwvHy.js} +1 -1
  11. package/dist/assets/{chunk-GTNCS2PH-pJLNSAT7.js → chunk-GTNCS2PH-Db7CGj5Y.js} +1 -1
  12. package/dist/assets/{chunk-GWA4HPMP-Cb-3_3IT.js → chunk-GWA4HPMP-CNvIfqKT.js} +1 -1
  13. package/dist/assets/{chunk-JWPE2WC7-DAf5aogn.js → chunk-JWPE2WC7-C_bEp5tq.js} +1 -1
  14. package/dist/assets/{chunk-NETBCI7D-DR3kiwZz.js → chunk-NETBCI7D-Coi4BOls.js} +1 -1
  15. package/dist/assets/{chunk-WEXAMYUT-BmU8PeyM.js → chunk-WEXAMYUT-CzylEuL3.js} +1 -1
  16. package/dist/assets/{chunk-XXDRQBXY-Buh8g8OT.js → chunk-XXDRQBXY-m7p7ztaD.js} +1 -1
  17. package/dist/assets/{classDiagram-v2-NBCMYWYE-BurfmMWM.js → classDiagram-v2-NBCMYWYE-BMy6o9x2.js} +1 -1
  18. package/dist/assets/{cose-bilkent-JH36ORCC-KkGZUEkU.js → cose-bilkent-JH36ORCC-BDQ__0_J.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-C3PpV96d.js → cynefin-OW5HDTMX-Djv4ghUb.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-VND7K2PF-Dt8JwJaI.js → cynefinDiagram-VND7K2PF-BULrlGYH.js} +1 -1
  21. package/dist/assets/{dagre-6A5THRUB-DkjN8266.js → dagre-6A5THRUB-D-jrT_hU.js} +1 -1
  22. package/dist/assets/{diagram-22UHCM2B-DUAJS0JV.js → diagram-22UHCM2B-p2UhQmfL.js} +1 -1
  23. package/dist/assets/{diagram-3UASUU5V-Dw4KhF1a.js → diagram-3UASUU5V-DHvGWZ7q.js} +1 -1
  24. package/dist/assets/{diagram-ATOU4E4O-Dd1gmQqf.js → diagram-ATOU4E4O-ChhKDot-.js} +1 -1
  25. package/dist/assets/{diagram-CDSNMT55-CjwGs0Yw.js → diagram-CDSNMT55-B-T7A43R.js} +1 -1
  26. package/dist/assets/{diagram-MLGK6HIB-BL-69Zrq.js → diagram-MLGK6HIB-CJm8ENNY.js} +1 -1
  27. package/dist/assets/{diagram-MPIPVDR6-5Zi2N7wo.js → diagram-MPIPVDR6-510tlza8.js} +1 -1
  28. package/dist/assets/{ebnfDiagram-ZINNZB2B-D9NekWUX.js → ebnfDiagram-ZINNZB2B-PkvRPHel.js} +1 -1
  29. package/dist/assets/{elk-276RUBZZ-CkaSQjxw.js → elk-276RUBZZ-BVBVPoX3.js} +1 -1
  30. package/dist/assets/{erDiagram-OPXOYQCR-CdAVBV8-.js → erDiagram-OPXOYQCR-BgYMVm7L.js} +1 -1
  31. package/dist/assets/{flowDiagram-KWPJA3E3-Bm_lWWH7.js → flowDiagram-KWPJA3E3-BGqsnc_3.js} +1 -1
  32. package/dist/assets/{ganttDiagram-FUAMR5RP-FZ8ejYWJ.js → ganttDiagram-FUAMR5RP-D_-UeReH.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-X574FWY7-Dlv9rizZ.js → gitGraphDiagram-X574FWY7-D0uooONx.js} +1 -1
  34. package/dist/assets/index-BwdMAQ6p.css +1 -0
  35. package/dist/assets/index-ByJennr7.js +581 -0
  36. package/dist/assets/{index-h9FFNwTu.js → index-zRsJERGJ.js} +1 -1
  37. package/dist/assets/{infoDiagram-VRGFBTTK-BdQ_gW5B.js → infoDiagram-VRGFBTTK-B9e1z77f.js} +1 -1
  38. package/dist/assets/{ishikawaDiagram-OU5B5YK6-EwssEp2X.js → ishikawaDiagram-OU5B5YK6-BB4vNwvD.js} +1 -1
  39. package/dist/assets/{journeyDiagram-ZHPQQLJL-BX1G1vOd.js → journeyDiagram-ZHPQQLJL-DpkosOTB.js} +1 -1
  40. package/dist/assets/{kanban-definition-PNTS6WVX-BB4kAsyC.js → kanban-definition-PNTS6WVX-DbDP4kUu.js} +1 -1
  41. package/dist/assets/{linear-3su2TG93.js → linear-DBJGPmjT.js} +1 -1
  42. package/dist/assets/{mermaid.core-CDD1E0s9.js → mermaid.core-DWPRi-ue.js} +5 -5
  43. package/dist/assets/{mindmap-definition-NLK3R4M7-Ba50MsNK.js → mindmap-definition-NLK3R4M7-nszBbJmx.js} +1 -1
  44. package/dist/assets/{pegDiagram-GJSIUBJH-DZLkkZlM.js → pegDiagram-GJSIUBJH-CWykJWu_.js} +1 -1
  45. package/dist/assets/{pieDiagram-5QR66LMP-DaTz77Uq.js → pieDiagram-5QR66LMP-g3Da-Vnv.js} +1 -1
  46. package/dist/assets/{quadrantDiagram-O4NWA36T-CpaiidpR.js → quadrantDiagram-O4NWA36T-Wzrlpzgh.js} +1 -1
  47. package/dist/assets/{railroadDiagram-XR7U4H2S-mN4JrMeI.js → railroadDiagram-XR7U4H2S-moHTFMC0.js} +1 -1
  48. package/dist/assets/{requirementDiagram-PLB6GJNP-BSNDRpg7.js → requirementDiagram-PLB6GJNP-CwfXdFH8.js} +1 -1
  49. package/dist/assets/{sankeyDiagram-IPEJSGJF-FngzGNug.js → sankeyDiagram-IPEJSGJF-3ZROYvnq.js} +1 -1
  50. package/dist/assets/{sequenceDiagram-PO4LG4MO-CM4pCC3L.js → sequenceDiagram-PO4LG4MO-E1TBebP8.js} +1 -1
  51. package/dist/assets/{sizeCapture-INFHLROL-CRICHXkw.js → sizeCapture-INFHLROL-DHKEU5pu.js} +1 -1
  52. package/dist/assets/{stateDiagram-v2-GCMORJYK-oyTWpxH2.js → stateDiagram-v2-GCMORJYK-BguEj09D.js} +1 -1
  53. package/dist/assets/{swimlanes-2SLR337P-D9h9mPBq.js → swimlanes-2SLR337P-C7IDYnhd.js} +1 -1
  54. package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +8 -0
  55. package/dist/assets/{timeline-definition-EJHVYXUP-BJUlN9bW.js → timeline-definition-EJHVYXUP-DCW-F5PW.js} +1 -1
  56. package/dist/assets/{usecaseDiagram-POWQR4AR-3uBNkbAb.js → usecaseDiagram-POWQR4AR-xBMFtWYV.js} +1 -1
  57. package/dist/assets/{vennDiagram-UO4OBE2U-D2odalzT.js → vennDiagram-UO4OBE2U-B5nIkuH8.js} +1 -1
  58. package/dist/assets/{wardleyDiagram-VNRHLVJA-DHgFaB9S.js → wardleyDiagram-VNRHLVJA-DYFAhdbr.js} +1 -1
  59. package/dist/assets/{xychartDiagram-PMCCYNJV-w5GYM2h0.js → xychartDiagram-PMCCYNJV-DeXwX6Cz.js} +1 -1
  60. package/dist/index.html +2 -2
  61. package/dist-server/browser-proxy.js +2 -1
  62. package/dist-server/container-mcp.js +1029 -164
  63. package/dist-server/drivers/agents-proxy.js +64 -10
  64. package/dist-server/drivers/phone-proxy.js +33 -2
  65. package/dist-server/index.js +14577 -9001
  66. package/dist-server/local-computer-proxy.js +3 -3
  67. package/dist-server/local-computer.js +53 -17
  68. package/dist-server/openmausbot.js +3981 -1165
  69. package/dist-server/pair-cli.js +3981 -1165
  70. package/dist-server/permission-proxy.js +29 -9
  71. package/dist-server/server/attachments.js +55 -0
  72. package/dist-server/server/auto-approve.js +4 -3
  73. package/dist-server/server/auto-vm-claims.js +1 -0
  74. package/dist-server/server/bot-package.js +18 -292
  75. package/dist-server/server/bot-profile.js +5 -17
  76. package/dist-server/server/box-create-idempotency.js +3 -3
  77. package/dist-server/server/box.js +57 -55
  78. package/dist-server/server/browser-proxy.js +3 -2
  79. package/dist-server/server/browser-runtime.js +37 -1
  80. package/dist-server/server/cli.js +2 -2
  81. package/dist-server/server/command-allowlist.js +154 -0
  82. package/dist-server/server/computer-wait.js +22 -9
  83. package/dist-server/server/config.js +59 -9
  84. package/dist-server/server/connector-verdict.js +141 -0
  85. package/dist-server/server/container-computer.js +1 -1
  86. package/dist-server/server/default-model-selection.js +9 -0
  87. package/dist-server/server/delegations.js +85 -34
  88. package/dist-server/server/drivers/acp/core.js +57 -15
  89. package/dist-server/server/drivers/agents-call.js +42 -3
  90. package/dist-server/server/drivers/agents-catalog.js +34 -4
  91. package/dist-server/server/drivers/boxagent.js +174 -36
  92. package/dist-server/server/drivers/builtIn.js +2 -0
  93. package/dist-server/server/drivers/chat-box-tools.js +90 -0
  94. package/dist-server/server/drivers/chat-images.js +54 -0
  95. package/dist-server/server/drivers/chat-mcp-tools.js +38 -8
  96. package/dist-server/server/drivers/chat-tool-approval.js +70 -18
  97. package/dist-server/server/drivers/claude.js +7 -2
  98. package/dist-server/server/drivers/codex-instructions.js +48 -6
  99. package/dist-server/server/drivers/codex-tool-surface.js +20 -0
  100. package/dist-server/server/drivers/codex.js +95 -30
  101. package/dist-server/server/drivers/mistral.js +115 -0
  102. package/dist-server/server/drivers/openai-chat.js +155 -32
  103. package/dist-server/server/drivers/openai-compat.js +1 -0
  104. package/dist-server/server/drivers/permission-command.js +35 -0
  105. package/dist-server/server/drivers/phone-proxy.js +47 -2
  106. package/dist-server/server/drivers/pi.js +75 -4
  107. package/dist-server/server/drivers/prompt-split.js +87 -0
  108. package/dist-server/server/fleet.js +1 -1
  109. package/dist-server/server/group-thread-usage.js +103 -0
  110. package/dist-server/server/harness/registry.js +1 -0
  111. package/dist-server/server/hosted-models.js +1 -1
  112. package/dist-server/server/index.js +1385 -322
  113. package/dist-server/server/local-computer.js +72 -23
  114. package/dist-server/server/managed-desktop.js +1 -1
  115. package/dist-server/server/mcp-bridge.js +6 -6
  116. package/dist-server/server/new-bot-defaults.js +115 -0
  117. package/dist-server/server/org-library.js +1051 -0
  118. package/dist-server/server/package-export.js +627 -39
  119. package/dist-server/server/package-import.js +458 -0
  120. package/dist-server/server/package-parts.js +87 -0
  121. package/dist-server/server/peer-approval.js +16 -7
  122. package/dist-server/server/peer-roster.js +10 -0
  123. package/dist-server/server/presets.js +492 -0
  124. package/dist-server/server/provider-key-check.js +3 -2
  125. package/dist-server/server/published-teams.js +80 -0
  126. package/dist-server/server/room-handoffs.js +19 -3
  127. package/dist-server/server/routes/bot-presets.js +25 -0
  128. package/dist-server/server/routine-requests.js +6 -3
  129. package/dist-server/server/routines.js +41 -1
  130. package/dist-server/server/section-context.js +10 -0
  131. package/dist-server/server/skills.js +34 -61
  132. package/dist-server/server/steer-queue.js +3 -2
  133. package/dist-server/server/store.js +259 -15
  134. package/dist-server/server/surface.js +10 -4
  135. package/dist-server/server/system-prompt.js +12 -4
  136. package/dist-server/server/team-backup.js +6 -2
  137. package/dist-server/server/team-computers.js +10 -0
  138. package/dist-server/server/team-library.js +6 -2
  139. package/dist-server/server/team-manifest.js +4 -3
  140. package/dist-server/server/team-setup-requests.js +16 -5
  141. package/dist-server/server/thread-events.js +17 -1
  142. package/dist-server/server/turn-context.js +2 -0
  143. package/dist-server/server/turn-watchdog.js +12 -3
  144. package/dist-server/server/usage-ledger.js +12 -1
  145. package/dist-server/server/vps-computer.js +139 -51
  146. package/dist-server/server/vps-container-mcp.js +7 -0
  147. package/dist-server/server/vps-ssh.js +36 -7
  148. package/dist-server/server/workspace-backup-policy.js +12 -0
  149. package/dist-server/server/workspace-backup.js +2 -2
  150. package/dist-server/server/workspace.js +1 -1
  151. package/dist-server/shared/approval-mode.js +1 -1
  152. package/dist-server/shared/ask-question.js +188 -12
  153. package/dist-server/shared/bot-profile.js +15 -0
  154. package/dist-server/shared/command-allowlist.js +1 -0
  155. package/dist-server/shared/credential-request.js +1 -1
  156. package/dist-server/shared/package-format.js +967 -0
  157. package/dist-server/shared/skill-md.js +64 -0
  158. package/dist-server/shared/team-backup.js +7 -0
  159. package/dist-server/shared/wire.js +4 -0
  160. package/dist-server/vps-container-mcp.js +1129 -170
  161. package/enterprise/README.md +1 -1
  162. package/package.json +1 -1
  163. package/dist/assets/channel-BXFkgus9.js +0 -1
  164. package/dist/assets/index-BGgqlWPJ.css +0 -1
  165. package/dist/assets/index-BtHo8ZKl.js +0 -577
  166. package/dist/assets/swimlanesDiagram-TC7HE7FX-BWrvB2ak.js +0 -8
@@ -0,0 +1,87 @@
1
+ // The stable/volatile prompt split, shared by the drivers that deliver it.
2
+ // The stable half is everything that must stay byte-identical for a
3
+ // provider's cached prefix (or a spawned CLI's session contract) to
4
+ // survive; the volatile half (memory, mentions, outstanding teammate
5
+ // work, recent work) legitimately changes mid-conversation and reaches
6
+ // the model inside the turn that changed it, after the cacheable prefix.
7
+ import { createHash } from "node:crypto";
8
+ import { mkdirSync, readFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { DATA_DIR } from "../config.js";
11
+ import { writeFileAtomic } from "../atomic.js";
12
+ /** Read the split off a turn. Both halves must be present: a driver that
13
+ * receives only the unsplit system field was called through a path that
14
+ * never split it. */
15
+ export function promptHalves(turn) {
16
+ const { systemStable, systemVolatile } = turn;
17
+ if (typeof systemStable !== "string" || typeof systemVolatile !== "string") {
18
+ return { stable: null, volatile: "" };
19
+ }
20
+ return { stable: systemStable, volatile: systemVolatile };
21
+ }
22
+ export const VOLATILE_CONTEXT_NOTE_PREFIX = "Context from OpenMausBot updated since this conversation started; it replaces any earlier copy:";
23
+ export const VOLATILE_CONTEXT_CLEARED_NOTE = "The OpenMausBot context notes from earlier in this conversation (memory, mentions, outstanding teammate work) have been cleared; the standing instructions still apply.";
24
+ /** The labelled block that carries a changed volatile half inside a user
25
+ * turn. A half that is empty and always was needs no note; one that was
26
+ * cleared announces the removal so the model stops relying on it. */
27
+ export function volatileContextNote(volatile, hadVolatile) {
28
+ const text = volatile.trim();
29
+ if (text)
30
+ return VOLATILE_CONTEXT_NOTE_PREFIX + "\n\n" + text;
31
+ return hadVolatile ? VOLATILE_CONTEXT_CLEARED_NOTE : "";
32
+ }
33
+ /** Prepend a delivered note to the turn text: the one composition shape
34
+ * every split-aware driver uses. */
35
+ export function withContextNote(note, text) {
36
+ if (!note)
37
+ return text;
38
+ return text ? note + "\n\n" + text : note;
39
+ }
40
+ const digest = (value) => createHash("sha256").update(value).digest("hex");
41
+ const EMPTY_FINGERPRINT = digest("");
42
+ export function promptSplitFingerprints(stable, volatile) {
43
+ return { stable: digest(stable), volatile: digest(volatile) };
44
+ }
45
+ const receiptPath = (scope, key) => join(DATA_DIR, "prompt-split", digest(JSON.stringify([scope, key])) + ".json");
46
+ /** Which halves a native session (an ACP session id, a pi session file)
47
+ * last carried. Unknown - never tracked, or tracked before this file
48
+ * existed - reads as null, and the caller re-delivers the full prompt
49
+ * once. */
50
+ export function readPromptSplitReceipt(scope, key) {
51
+ try {
52
+ const raw = JSON.parse(readFileSync(receiptPath(scope, key), "utf8"));
53
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
54
+ const record = raw;
55
+ if (typeof record.stable === "string" && typeof record.volatile === "string") {
56
+ return { stable: record.stable, volatile: record.volatile };
57
+ }
58
+ }
59
+ }
60
+ catch {
61
+ /* a missing or corrupt receipt is simply unknown */
62
+ }
63
+ return null;
64
+ }
65
+ export function writePromptSplitReceipt(scope, key, receipt) {
66
+ mkdirSync(join(DATA_DIR, "prompt-split"), { recursive: true });
67
+ writeFileAtomic(receiptPath(scope, key), JSON.stringify(receipt), { mode: 0o600 });
68
+ }
69
+ /** Compose the prompt for a session that persists its own history (ACP
70
+ * agents, pi): the full system block rides only when this native session
71
+ * has not carried it - or carried a different one, which re-delivers the
72
+ * current copy the way a pre-split turn always did - and a changed
73
+ * volatile half rides as a labelled note. Otherwise the turn text goes
74
+ * through bare, so an ordinary memory write neither appends a second copy
75
+ * of the prompt to the session nor re-prices its cached prefix.
76
+ * perTurnVolatile marks a turn whose volatile half describes this very
77
+ * turn (a mention): its note is delivered even when the text is unchanged. */
78
+ export function splitSessionPrompt(stable, volatile, previous, fullSystem, text, perTurnVolatile = false) {
79
+ const receipt = promptSplitFingerprints(stable, volatile);
80
+ if (previous === null || previous.stable !== receipt.stable) {
81
+ return { text: fullSystem ? fullSystem + "\n\n" + text : text, receipt };
82
+ }
83
+ const note = previous.volatile === receipt.volatile && !perTurnVolatile
84
+ ? ""
85
+ : volatileContextNote(volatile, previous.volatile !== EMPTY_FINGERPRINT);
86
+ return { text: withContextNote(note, text), receipt };
87
+ }
@@ -250,7 +250,7 @@ export function initPlan(input) {
250
250
  ...(input.operator ? [{ kind: "run", argv: ["systemctl", "enable", "--now", "openmausbot-fleet.service"], why: `start the fleet agent for ${input.operator}` }] : []),
251
251
  { kind: "run", argv: ["systemctl", "reload", "caddy"], why: "start serving the workspaces folder" },
252
252
  { kind: "note", text: `point *.${registry.domain} at this server (a wildcard A/AAAA record); each workspace gets its own certificate when created` },
253
- ...(input.operator ? [{ kind: "note", text: `the workspace running as ${input.operator} can now manage workspaces from Settings → Workspaces` }] : []),
253
+ ...(input.operator ? [{ kind: "note", text: `the installation running as ${input.operator} can now manage installations from Settings → Installations` }] : []),
254
254
  ];
255
255
  return { steps, registry };
256
256
  }
@@ -0,0 +1,103 @@
1
+ import { readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
4
+ const valid = (row) => Boolean(row && typeof row.threadId === "string" && typeof row.botId === "string" && typeof row.at === "string" && Number.isFinite(Date.parse(row.at)) && finite(row.input) && finite(row.output));
5
+ function addRow(totals, row) {
6
+ const summary = totals.get(row.threadId) ?? { usage: { input: 0, output: 0, costUsd: null, turns: 0, cachedInput: 0 }, lastAt: -Infinity };
7
+ const total = summary.usage;
8
+ const input = Math.trunc(row.input), output = Math.trunc(row.output);
9
+ const cached = finite(row.cachedInput) ? Math.min(input, Math.trunc(row.cachedInput)) : undefined;
10
+ total.input += input;
11
+ total.output += output;
12
+ total.turns++;
13
+ if (cached === undefined)
14
+ delete total.cachedInput;
15
+ else if (total.cachedInput !== undefined)
16
+ total.cachedInput += cached;
17
+ if (finite(row.costUsd))
18
+ total.costUsd = (total.costUsd ?? 0) + row.costUsd;
19
+ const at = Date.parse(row.at);
20
+ if (at >= summary.lastAt) {
21
+ summary.lastAt = at;
22
+ total.lastTurn = { input, output, ...(cached === undefined ? {} : { cachedInput: cached }), costUsd: finite(row.costUsd) ? row.costUsd : null };
23
+ total.lastSpeaker = { botId: row.botId, name: typeof row.botName === "string" && row.botName ? row.botName : row.botId };
24
+ }
25
+ totals.set(row.threadId, summary);
26
+ }
27
+ /** Cache per-month/per-thread summaries, not the complete ledger in memory. */
28
+ export class GroupUsageReader {
29
+ files = new Map();
30
+ signature = "";
31
+ totals = new Map();
32
+ dataDir;
33
+ constructor(dataDir) { this.dataDir = dataDir; }
34
+ /** Refresh only at startup or after ledger persistence, never on an SSE lookup. */
35
+ refresh() {
36
+ const dir = join(this.dataDir, "usage");
37
+ let names;
38
+ try {
39
+ names = readdirSync(dir).filter(name => /^\d{4}-\d{2}\.jsonl$/.test(name)).sort();
40
+ }
41
+ catch (error) {
42
+ if (error.code !== "ENOENT")
43
+ throw error;
44
+ names = [];
45
+ }
46
+ const files = names.map(name => {
47
+ const stat = statSync(join(dir, name));
48
+ return { name, stamp: `${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}:${stat.ino}` };
49
+ });
50
+ const signature = JSON.stringify(files);
51
+ if (signature !== this.signature) {
52
+ const next = new Map();
53
+ for (const { name, stamp } of files) {
54
+ let entry = this.files.get(name);
55
+ if (entry?.stamp !== stamp) {
56
+ const totals = new Map();
57
+ for (const line of readFileSync(join(dir, name), "utf8").split("\n")) {
58
+ try {
59
+ const row = JSON.parse(line);
60
+ if (valid(row))
61
+ addRow(totals, row);
62
+ }
63
+ catch { /* incomplete or damaged row */ }
64
+ }
65
+ entry = { stamp, totals };
66
+ }
67
+ next.set(name, entry);
68
+ }
69
+ const summaries = new Map();
70
+ for (const entry of next.values()) {
71
+ for (const [threadId, part] of entry.totals) {
72
+ const total = summaries.get(threadId);
73
+ if (!total)
74
+ summaries.set(threadId, structuredClone(part));
75
+ else {
76
+ total.usage.input += part.usage.input;
77
+ total.usage.output += part.usage.output;
78
+ total.usage.turns += part.usage.turns;
79
+ if (total.usage.cachedInput === undefined || part.usage.cachedInput === undefined)
80
+ delete total.usage.cachedInput;
81
+ else
82
+ total.usage.cachedInput += part.usage.cachedInput;
83
+ if (part.usage.costUsd !== null)
84
+ total.usage.costUsd = (total.usage.costUsd ?? 0) + part.usage.costUsd;
85
+ if (part.lastAt >= total.lastAt) {
86
+ total.lastAt = part.lastAt;
87
+ total.usage.lastTurn = part.usage.lastTurn;
88
+ total.usage.lastSpeaker = part.usage.lastSpeaker;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ const totals = new Map([...summaries].map(([id, value]) => [id, value.usage]));
94
+ this.files = next;
95
+ this.totals = totals;
96
+ this.signature = signature;
97
+ }
98
+ }
99
+ forThread(threadId) {
100
+ const usage = this.totals.get(threadId);
101
+ return usage ? structuredClone(usage) : undefined;
102
+ }
103
+ }
@@ -228,6 +228,7 @@ export class ProviderRegistry {
228
228
  modelVariants: inst.adapter.capabilities.modelVariants === true,
229
229
  queueing: inst.adapter.capabilities.queueing === true,
230
230
  localComputerMcp: inst.adapter.capabilities.localComputerMcp === true,
231
+ cloudComputerMcp: inst.adapter.capabilities.cloudComputerMcp === true,
231
232
  approvalReview: inst.reviewPermission !== undefined,
232
233
  },
233
234
  access: driver?.metadata.access ?? "subscription",
@@ -21,7 +21,7 @@ export function hostedModelPolicy(dataDirectory, env = process.env) {
21
21
  return null;
22
22
  const hosted = hostedWorkspaceConfiguration(env);
23
23
  if (!hosted?.portalMembership || env.OMB_DESKTOP_PARENT === "1" || !/^omb_workspace_[A-Za-z0-9_-]{43}$/.test(env.OMB_HOSTED_MODEL_TOKEN ?? "")) {
24
- throw new Error("Hosted model access requires complete portal-managed workspace configuration.");
24
+ throw new Error("Hosted model access requires complete portal-managed configuration.");
25
25
  }
26
26
  let catalog;
27
27
  try {