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.
- package/README.md +2 -2
- package/dist/assets/{abnfDiagram-O67JEVCF-DpUZNnFD.js → abnfDiagram-O67JEVCF-BTiuL79F.js} +1 -1
- package/dist/assets/{arc-DW7mrI4p.js → arc-C88Blc2v.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CyeZ5WLZ.js → architectureDiagram-NJMV4G6O-BZsJIZrb.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-CBVaNjO7.js → blockDiagram-BEXU5L5S-CVeMe9Ah.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-sf8d41uT.js → c4Diagram-YGBWAQC7-Cedk3Bhx.js} +1 -1
- package/dist/assets/channel-taCNRlUU.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BOGvy7Y_.js → chunk-2Q5K7J3B-D-VRqzTB.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BiF9zyiz.js → chunk-3FUC2YCW-BChov01Y.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-EFuZ1SBZ.js → chunk-5VM5RSS4-C-TfwvHy.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-pJLNSAT7.js → chunk-GTNCS2PH-Db7CGj5Y.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-Cb-3_3IT.js → chunk-GWA4HPMP-CNvIfqKT.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DAf5aogn.js → chunk-JWPE2WC7-C_bEp5tq.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-DR3kiwZz.js → chunk-NETBCI7D-Coi4BOls.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-BmU8PeyM.js → chunk-WEXAMYUT-CzylEuL3.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Buh8g8OT.js → chunk-XXDRQBXY-m7p7ztaD.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BurfmMWM.js → classDiagram-v2-NBCMYWYE-BMy6o9x2.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-KkGZUEkU.js → cose-bilkent-JH36ORCC-BDQ__0_J.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-C3PpV96d.js → cynefin-OW5HDTMX-Djv4ghUb.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-Dt8JwJaI.js → cynefinDiagram-VND7K2PF-BULrlGYH.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-DkjN8266.js → dagre-6A5THRUB-D-jrT_hU.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-DUAJS0JV.js → diagram-22UHCM2B-p2UhQmfL.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-Dw4KhF1a.js → diagram-3UASUU5V-DHvGWZ7q.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-Dd1gmQqf.js → diagram-ATOU4E4O-ChhKDot-.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-CjwGs0Yw.js → diagram-CDSNMT55-B-T7A43R.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-BL-69Zrq.js → diagram-MLGK6HIB-CJm8ENNY.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-5Zi2N7wo.js → diagram-MPIPVDR6-510tlza8.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-D9NekWUX.js → ebnfDiagram-ZINNZB2B-PkvRPHel.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CkaSQjxw.js → elk-276RUBZZ-BVBVPoX3.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CdAVBV8-.js → erDiagram-OPXOYQCR-BgYMVm7L.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-Bm_lWWH7.js → flowDiagram-KWPJA3E3-BGqsnc_3.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-FZ8ejYWJ.js → ganttDiagram-FUAMR5RP-D_-UeReH.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-Dlv9rizZ.js → gitGraphDiagram-X574FWY7-D0uooONx.js} +1 -1
- package/dist/assets/index-BwdMAQ6p.css +1 -0
- package/dist/assets/index-ByJennr7.js +581 -0
- package/dist/assets/{index-h9FFNwTu.js → index-zRsJERGJ.js} +1 -1
- package/dist/assets/{infoDiagram-VRGFBTTK-BdQ_gW5B.js → infoDiagram-VRGFBTTK-B9e1z77f.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-EwssEp2X.js → ishikawaDiagram-OU5B5YK6-BB4vNwvD.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-BX1G1vOd.js → journeyDiagram-ZHPQQLJL-DpkosOTB.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-BB4kAsyC.js → kanban-definition-PNTS6WVX-DbDP4kUu.js} +1 -1
- package/dist/assets/{linear-3su2TG93.js → linear-DBJGPmjT.js} +1 -1
- package/dist/assets/{mermaid.core-CDD1E0s9.js → mermaid.core-DWPRi-ue.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-Ba50MsNK.js → mindmap-definition-NLK3R4M7-nszBbJmx.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-DZLkkZlM.js → pegDiagram-GJSIUBJH-CWykJWu_.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DaTz77Uq.js → pieDiagram-5QR66LMP-g3Da-Vnv.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-CpaiidpR.js → quadrantDiagram-O4NWA36T-Wzrlpzgh.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-mN4JrMeI.js → railroadDiagram-XR7U4H2S-moHTFMC0.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-BSNDRpg7.js → requirementDiagram-PLB6GJNP-CwfXdFH8.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-FngzGNug.js → sankeyDiagram-IPEJSGJF-3ZROYvnq.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-CM4pCC3L.js → sequenceDiagram-PO4LG4MO-E1TBebP8.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-CRICHXkw.js → sizeCapture-INFHLROL-DHKEU5pu.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-oyTWpxH2.js → stateDiagram-v2-GCMORJYK-BguEj09D.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-D9h9mPBq.js → swimlanes-2SLR337P-C7IDYnhd.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BJUlN9bW.js → timeline-definition-EJHVYXUP-DCW-F5PW.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-3uBNkbAb.js → usecaseDiagram-POWQR4AR-xBMFtWYV.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-D2odalzT.js → vennDiagram-UO4OBE2U-B5nIkuH8.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DHgFaB9S.js → wardleyDiagram-VNRHLVJA-DYFAhdbr.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-w5GYM2h0.js → xychartDiagram-PMCCYNJV-DeXwX6Cz.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/browser-proxy.js +2 -1
- package/dist-server/container-mcp.js +1029 -164
- package/dist-server/drivers/agents-proxy.js +64 -10
- package/dist-server/drivers/phone-proxy.js +33 -2
- package/dist-server/index.js +14577 -9001
- package/dist-server/local-computer-proxy.js +3 -3
- package/dist-server/local-computer.js +53 -17
- package/dist-server/openmausbot.js +3981 -1165
- package/dist-server/pair-cli.js +3981 -1165
- package/dist-server/permission-proxy.js +29 -9
- package/dist-server/server/attachments.js +55 -0
- package/dist-server/server/auto-approve.js +4 -3
- package/dist-server/server/auto-vm-claims.js +1 -0
- package/dist-server/server/bot-package.js +18 -292
- package/dist-server/server/bot-profile.js +5 -17
- package/dist-server/server/box-create-idempotency.js +3 -3
- package/dist-server/server/box.js +57 -55
- package/dist-server/server/browser-proxy.js +3 -2
- package/dist-server/server/browser-runtime.js +37 -1
- package/dist-server/server/cli.js +2 -2
- package/dist-server/server/command-allowlist.js +154 -0
- package/dist-server/server/computer-wait.js +22 -9
- package/dist-server/server/config.js +59 -9
- package/dist-server/server/connector-verdict.js +141 -0
- package/dist-server/server/container-computer.js +1 -1
- package/dist-server/server/default-model-selection.js +9 -0
- package/dist-server/server/delegations.js +85 -34
- package/dist-server/server/drivers/acp/core.js +57 -15
- package/dist-server/server/drivers/agents-call.js +42 -3
- package/dist-server/server/drivers/agents-catalog.js +34 -4
- package/dist-server/server/drivers/boxagent.js +174 -36
- package/dist-server/server/drivers/builtIn.js +2 -0
- package/dist-server/server/drivers/chat-box-tools.js +90 -0
- package/dist-server/server/drivers/chat-images.js +54 -0
- package/dist-server/server/drivers/chat-mcp-tools.js +38 -8
- package/dist-server/server/drivers/chat-tool-approval.js +70 -18
- package/dist-server/server/drivers/claude.js +7 -2
- package/dist-server/server/drivers/codex-instructions.js +48 -6
- package/dist-server/server/drivers/codex-tool-surface.js +20 -0
- package/dist-server/server/drivers/codex.js +95 -30
- package/dist-server/server/drivers/mistral.js +115 -0
- package/dist-server/server/drivers/openai-chat.js +155 -32
- package/dist-server/server/drivers/openai-compat.js +1 -0
- package/dist-server/server/drivers/permission-command.js +35 -0
- package/dist-server/server/drivers/phone-proxy.js +47 -2
- package/dist-server/server/drivers/pi.js +75 -4
- package/dist-server/server/drivers/prompt-split.js +87 -0
- package/dist-server/server/fleet.js +1 -1
- package/dist-server/server/group-thread-usage.js +103 -0
- package/dist-server/server/harness/registry.js +1 -0
- package/dist-server/server/hosted-models.js +1 -1
- package/dist-server/server/index.js +1385 -322
- package/dist-server/server/local-computer.js +72 -23
- package/dist-server/server/managed-desktop.js +1 -1
- package/dist-server/server/mcp-bridge.js +6 -6
- package/dist-server/server/new-bot-defaults.js +115 -0
- package/dist-server/server/org-library.js +1051 -0
- package/dist-server/server/package-export.js +627 -39
- package/dist-server/server/package-import.js +458 -0
- package/dist-server/server/package-parts.js +87 -0
- package/dist-server/server/peer-approval.js +16 -7
- package/dist-server/server/peer-roster.js +10 -0
- package/dist-server/server/presets.js +492 -0
- package/dist-server/server/provider-key-check.js +3 -2
- package/dist-server/server/published-teams.js +80 -0
- package/dist-server/server/room-handoffs.js +19 -3
- package/dist-server/server/routes/bot-presets.js +25 -0
- package/dist-server/server/routine-requests.js +6 -3
- package/dist-server/server/routines.js +41 -1
- package/dist-server/server/section-context.js +10 -0
- package/dist-server/server/skills.js +34 -61
- package/dist-server/server/steer-queue.js +3 -2
- package/dist-server/server/store.js +259 -15
- package/dist-server/server/surface.js +10 -4
- package/dist-server/server/system-prompt.js +12 -4
- package/dist-server/server/team-backup.js +6 -2
- package/dist-server/server/team-computers.js +10 -0
- package/dist-server/server/team-library.js +6 -2
- package/dist-server/server/team-manifest.js +4 -3
- package/dist-server/server/team-setup-requests.js +16 -5
- package/dist-server/server/thread-events.js +17 -1
- package/dist-server/server/turn-context.js +2 -0
- package/dist-server/server/turn-watchdog.js +12 -3
- package/dist-server/server/usage-ledger.js +12 -1
- package/dist-server/server/vps-computer.js +139 -51
- package/dist-server/server/vps-container-mcp.js +7 -0
- package/dist-server/server/vps-ssh.js +36 -7
- package/dist-server/server/workspace-backup-policy.js +12 -0
- package/dist-server/server/workspace-backup.js +2 -2
- package/dist-server/server/workspace.js +1 -1
- package/dist-server/shared/approval-mode.js +1 -1
- package/dist-server/shared/ask-question.js +188 -12
- package/dist-server/shared/bot-profile.js +15 -0
- package/dist-server/shared/command-allowlist.js +1 -0
- package/dist-server/shared/credential-request.js +1 -1
- package/dist-server/shared/package-format.js +967 -0
- package/dist-server/shared/skill-md.js +64 -0
- package/dist-server/shared/team-backup.js +7 -0
- package/dist-server/shared/wire.js +4 -0
- package/dist-server/vps-container-mcp.js +1129 -170
- package/enterprise/README.md +1 -1
- package/package.json +1 -1
- package/dist/assets/channel-BXFkgus9.js +0 -1
- package/dist/assets/index-BGgqlWPJ.css +0 -1
- package/dist/assets/index-BtHo8ZKl.js +0 -577
- 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
|
|
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
|
|
24
|
+
throw new Error("Hosted model access requires complete portal-managed configuration.");
|
|
25
25
|
}
|
|
26
26
|
let catalog;
|
|
27
27
|
try {
|