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.
- package/README.md +2 -2
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/abnfDiagram-O67JEVCF-BTiuL79F.js +1 -0
- package/dist/assets/arc-C88Blc2v.js +1 -0
- package/dist/assets/architectureDiagram-NJMV4G6O-BZsJIZrb.js +36 -0
- package/dist/assets/blockDiagram-BEXU5L5S-CVeMe9Ah.js +140 -0
- package/dist/assets/c4Diagram-YGBWAQC7-Cedk3Bhx.js +38 -0
- package/dist/assets/channel-taCNRlUU.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-D-VRqzTB.js +1 -0
- package/dist/assets/chunk-3FUC2YCW-BChov01Y.js +2 -0
- package/dist/assets/chunk-5VM5RSS4-C-TfwvHy.js +15 -0
- package/dist/assets/chunk-GTNCS2PH-Db7CGj5Y.js +88 -0
- package/dist/assets/chunk-GWA4HPMP-CNvIfqKT.js +1 -0
- package/dist/assets/chunk-JWPE2WC7-C_bEp5tq.js +1 -0
- package/dist/assets/chunk-NETBCI7D-Coi4BOls.js +1 -0
- package/dist/assets/chunk-WEXAMYUT-CzylEuL3.js +1 -0
- package/dist/assets/chunk-XXDRQBXY-m7p7ztaD.js +1 -0
- package/dist/assets/classDiagram-v2-NBCMYWYE-BMy6o9x2.js +217 -0
- package/dist/assets/cose-bilkent-JH36ORCC-BDQ__0_J.js +1 -0
- package/dist/assets/cynefin-OW5HDTMX-Djv4ghUb.js +166 -0
- package/dist/assets/cynefinDiagram-VND7K2PF-BULrlGYH.js +62 -0
- package/dist/assets/cytoscape.esm-BB4DxJjf.js +321 -0
- package/dist/assets/dagre-6A5THRUB-D-jrT_hU.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-22UHCM2B-p2UhQmfL.js +200 -0
- package/dist/assets/diagram-3UASUU5V-DHvGWZ7q.js +24 -0
- package/dist/assets/diagram-ATOU4E4O-ChhKDot-.js +3 -0
- package/dist/assets/diagram-CDSNMT55-B-T7A43R.js +30 -0
- package/dist/assets/diagram-MLGK6HIB-CJm8ENNY.js +24 -0
- package/dist/assets/diagram-MPIPVDR6-510tlza8.js +41 -0
- package/dist/assets/ebnfDiagram-ZINNZB2B-PkvRPHel.js +1 -0
- package/dist/assets/elk-276RUBZZ-BVBVPoX3.js +27 -0
- package/dist/assets/erDiagram-OPXOYQCR-BgYMVm7L.js +99 -0
- package/dist/assets/flowDiagram-KWPJA3E3-BGqsnc_3.js +213 -0
- package/dist/assets/ganttDiagram-FUAMR5RP-D_-UeReH.js +292 -0
- package/dist/assets/gitGraphDiagram-X574FWY7-D0uooONx.js +106 -0
- package/dist/assets/graph-D8UmR1vK.js +1 -0
- package/dist/assets/index-BwdMAQ6p.css +1 -0
- package/dist/assets/index-ByJennr7.js +581 -0
- package/dist/assets/{index-CTNNoqSJ.js → index-zRsJERGJ.js} +1 -1
- package/dist/assets/infoDiagram-VRGFBTTK-B9e1z77f.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-OU5B5YK6-BB4vNwvD.js +70 -0
- package/dist/assets/journeyDiagram-ZHPQQLJL-DpkosOTB.js +139 -0
- package/dist/assets/kanban-definition-PNTS6WVX-DbDP4kUu.js +89 -0
- package/dist/assets/linear-DBJGPmjT.js +1 -0
- package/dist/assets/mermaid.core-DWPRi-ue.js +311 -0
- package/dist/assets/mindmap-definition-NLK3R4M7-nszBbJmx.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pegDiagram-GJSIUBJH-CWykJWu_.js +1 -0
- package/dist/assets/pieDiagram-5QR66LMP-g3Da-Vnv.js +39 -0
- package/dist/assets/quadrantDiagram-O4NWA36T-Wzrlpzgh.js +7 -0
- package/dist/assets/railroadDiagram-XR7U4H2S-moHTFMC0.js +1 -0
- package/dist/assets/requirementDiagram-PLB6GJNP-CwfXdFH8.js +84 -0
- package/dist/assets/sankeyDiagram-IPEJSGJF-3ZROYvnq.js +40 -0
- package/dist/assets/sequenceDiagram-PO4LG4MO-E1TBebP8.js +169 -0
- package/dist/assets/sizeCapture-INFHLROL-DHKEU5pu.js +1 -0
- package/dist/assets/stateDiagram-v2-GCMORJYK-BguEj09D.js +291 -0
- package/dist/assets/swimlanes-2SLR337P-C7IDYnhd.js +1 -0
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +8 -0
- package/dist/assets/timeline-definition-EJHVYXUP-DCW-F5PW.js +120 -0
- package/dist/assets/usecaseDiagram-POWQR4AR-xBMFtWYV.js +370 -0
- package/dist/assets/vennDiagram-UO4OBE2U-B5nIkuH8.js +34 -0
- package/dist/assets/wardleyDiagram-VNRHLVJA-DYFAhdbr.js +78 -0
- package/dist/assets/xychartDiagram-PMCCYNJV-DeXwX6Cz.js +7 -0
- package/dist/index.html +2 -2
- package/dist-server/browser-proxy.js +2 -1
- package/dist-server/container-mcp.js +1047 -173
- package/dist-server/drivers/agents-proxy.js +16121 -1504
- package/dist-server/drivers/phone-proxy.js +33 -2
- package/dist-server/enterprise/LICENSE +32 -0
- package/dist-server/enterprise/server/index.js +5 -5
- package/dist-server/index.js +16134 -7446
- package/dist-server/local-computer-proxy.js +3 -3
- package/dist-server/local-computer.js +53 -17
- package/dist-server/mcp-server.js +3 -1
- package/dist-server/openmausbot.js +9353 -6124
- package/dist-server/pair-cli.js +9353 -6124
- package/dist-server/permission-proxy.js +29 -9
- package/dist-server/server/admin-activity.js +410 -0
- 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/bot-visibility.js +462 -0
- package/dist-server/server/box-create-idempotency.js +3 -3
- package/dist-server/server/box.js +59 -56
- package/dist-server/server/browser-proxy.js +3 -2
- package/dist-server/server/browser-runtime.js +38 -2
- package/dist-server/server/channel-queue.js +2 -0
- package/dist-server/server/cli-prompts.js +24 -2
- package/dist-server/server/cli.js +81 -12
- package/dist-server/server/command-allowlist.js +154 -0
- package/dist-server/server/composio.js +49 -11
- package/dist-server/server/computer-wait.js +22 -9
- package/dist-server/server/config.js +133 -17
- package/dist-server/server/connector-verdict.js +141 -0
- package/dist-server/server/container-computer.js +1 -1
- package/dist-server/server/decision-log.js +209 -48
- package/dist-server/server/default-model-selection.js +29 -3
- package/dist-server/server/delegations.js +97 -44
- package/dist-server/server/drivers/acp/core.js +163 -33
- package/dist-server/server/drivers/acp/grok.js +4 -3
- package/dist-server/server/drivers/agents-call.js +1162 -0
- package/dist-server/server/drivers/agents-catalog.js +689 -0
- package/dist-server/server/drivers/agents-client.js +28 -0
- package/dist-server/server/drivers/agents-proxy.js +18 -1722
- 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 +55 -10
- package/dist-server/server/drivers/chat-tool-approval.js +70 -18
- package/dist-server/server/drivers/claude.js +28 -8
- package/dist-server/server/drivers/codex-instructions.js +48 -6
- package/dist-server/server/drivers/codex-mcp-names.js +44 -0
- package/dist-server/server/drivers/codex-tool-surface.js +20 -0
- package/dist-server/server/drivers/codex.js +121 -34
- package/dist-server/server/drivers/grok.js +2 -1
- 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 +16 -8
- 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/enterprise.js +97 -16
- package/dist-server/server/external-runtime.js +83 -0
- package/dist-server/server/fleet.js +1 -1
- package/dist-server/server/group-thread-usage.js +103 -0
- package/dist-server/server/guarded-requests.js +60 -0
- package/dist-server/server/harness/http.js +61 -0
- package/dist-server/server/harness/registry.js +1 -0
- package/dist-server/server/hosted-models.js +151 -0
- package/dist-server/server/hosted-slack.js +11 -0
- package/dist-server/server/index.js +3091 -658
- package/dist-server/server/local-computer.js +72 -23
- package/dist-server/server/managed-desktop.js +79 -14
- package/dist-server/server/managed-policy.js +153 -0
- package/dist-server/server/mcp-bridge.js +6 -6
- package/dist-server/server/message-db.js +28 -0
- package/dist-server/server/model-context-window.js +7 -0
- package/dist-server/server/model-prices.js +121 -0
- package/dist-server/server/new-bot-defaults.js +115 -0
- package/dist-server/server/notify.js +10 -1
- 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 +23 -2
- 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/recent-work.js +15 -0
- package/dist-server/server/request-auth.js +96 -0
- package/dist-server/server/room-handoffs.js +44 -14
- package/dist-server/server/routes/bot-presets.js +25 -0
- package/dist-server/server/routes/hosted-slack.js +20 -0
- package/dist-server/server/routes/table.js +14 -0
- package/dist-server/server/routine-requests.js +6 -3
- package/dist-server/server/routines.js +63 -1
- package/dist-server/server/section-context.js +10 -0
- package/dist-server/server/skills.js +34 -61
- package/dist-server/server/spend.js +111 -16
- package/dist-server/server/steer-queue.js +9 -3
- package/dist-server/server/store.js +550 -35
- package/dist-server/server/surface.js +10 -4
- package/dist-server/server/system-prompt.js +12 -4
- package/dist-server/server/team-backup.js +16 -3
- 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/thread-starters.js +53 -0
- 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 +43 -9
- 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 +21 -2
- package/dist-server/server/workspace-backup.js +7 -4
- 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/mention-boundary.js +15 -0
- package/dist-server/shared/package-format.js +967 -0
- package/dist-server/shared/routines.js +7 -1
- package/dist-server/shared/skill-md.js +64 -0
- package/dist-server/shared/team-backup.js +9 -0
- package/dist-server/shared/wire.js +4 -0
- package/dist-server/src/lib/composer-attachments.js +16 -0
- package/dist-server/vps-container-mcp.js +1147 -179
- package/enterprise/README.md +33 -12
- package/enterprise/server/index.js +5 -5
- package/package.json +1 -1
- package/dist/assets/index-Ba9G44HI.js +0 -310
- package/dist/assets/index-CkRNp7wX.css +0 -1
|
@@ -1,33 +1,76 @@
|
|
|
1
|
+
// Spend limits over the usage ledger: what this workspace has spent this
|
|
2
|
+
// month against its cap, the refusal a turn gets once the cap is reached,
|
|
3
|
+
// and the one-per-month notices when the month crosses the warning
|
|
4
|
+
// threshold and the cap. The figure is every cost in the ledger: what
|
|
5
|
+
// engines reported (on a workspace of personal subscriptions, their
|
|
6
|
+
// equivalents too) plus the estimates booked for engines that report tokens
|
|
7
|
+
// but no price (server/model-prices.ts). A turn on an unpriced model counts
|
|
8
|
+
// nothing. Enforced only with the `budgets` entitlement; without it the
|
|
9
|
+
// setting is inert.
|
|
10
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
1
12
|
import { entitled } from "./enterprise.js";
|
|
2
|
-
import { readUsage } from "./usage-ledger.js";
|
|
13
|
+
import { readUsage, usageRowKey } from "./usage-ledger.js";
|
|
3
14
|
const DEFAULT_WARN_AT_PERCENT = 80;
|
|
4
15
|
const CACHE_MS = 15_000;
|
|
5
16
|
// Every turn start asks; reading the month file each time would be silly.
|
|
17
|
+
// A read keeps the keys of the rows it summed, so a booked row can be told
|
|
18
|
+
// apart from the same row read back later.
|
|
6
19
|
const cache = new Map();
|
|
20
|
+
// Booked turns whose ledger row may not be on disk yet: dataDir → row key →
|
|
21
|
+
// cost. The append is asynchronous, and a turn that ran longer than CACHE_MS
|
|
22
|
+
// would otherwise be re-read out of the month before its own row landed.
|
|
23
|
+
const unwritten = new Map();
|
|
7
24
|
function monthOf(now) {
|
|
8
25
|
return now.toISOString().slice(0, 7);
|
|
9
26
|
}
|
|
10
|
-
|
|
27
|
+
const costOf = (value) => (typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0);
|
|
28
|
+
/** Reported and estimated cost this month so far: the ledger file (read at
|
|
29
|
+
* most every CACHE_MS) plus turns booked since whose rows have not landed. */
|
|
11
30
|
export function monthToDateSpend(dataDir, now = new Date()) {
|
|
12
31
|
const month = monthOf(now);
|
|
13
|
-
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return spentUsd;
|
|
20
|
-
}
|
|
21
|
-
/** Called right after a turn is booked, so the next check sees it without
|
|
22
|
-
* waiting for the ledger's append to land or the cache to expire. */
|
|
23
|
-
export function noteSpend(dataDir, costUsd, now = new Date()) {
|
|
24
|
-
const hit = cache.get(dataDir);
|
|
25
|
-
if (hit && hit.month === monthOf(now) && typeof costUsd === "number" && Number.isFinite(costUsd) && costUsd > 0) {
|
|
26
|
-
hit.spentUsd += costUsd;
|
|
32
|
+
let read = cache.get(dataDir);
|
|
33
|
+
if (!read || read.month !== month || now.getTime() - read.at >= CACHE_MS) {
|
|
34
|
+
const from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
35
|
+
const rows = readUsage(dataDir, { from, to: now });
|
|
36
|
+
read = { at: now.getTime(), month, fileUsd: rows.reduce((sum, row) => sum + costOf(row.costUsd), 0), keys: new Set(rows.map(usageRowKey)) };
|
|
37
|
+
cache.set(dataDir, read);
|
|
27
38
|
}
|
|
39
|
+
let pending = 0;
|
|
40
|
+
for (const [key, entry] of unwritten.get(dataDir) ?? []) {
|
|
41
|
+
if (entry.month === month && !read.keys.has(key))
|
|
42
|
+
pending += entry.costUsd;
|
|
43
|
+
}
|
|
44
|
+
return read.fileUsd + pending;
|
|
45
|
+
}
|
|
46
|
+
/** Called as a turn is booked, with the ledger's write for its row
|
|
47
|
+
* (appendUsage's promise). The cost counts from memory at once; when the
|
|
48
|
+
* row lands it moves into the cached read, and a re-read that already sees
|
|
49
|
+
* the row never counts it twice. A write that failed stops counting, as
|
|
50
|
+
* the ledger has no row for it. */
|
|
51
|
+
export function noteSpend(dataDir, row, written) {
|
|
52
|
+
const costUsd = costOf(row.costUsd);
|
|
53
|
+
if (!costUsd)
|
|
54
|
+
return;
|
|
55
|
+
const key = usageRowKey(row);
|
|
56
|
+
const month = row.at.slice(0, 7);
|
|
57
|
+
const entries = unwritten.get(dataDir) ?? new Map();
|
|
58
|
+
unwritten.set(dataDir, entries);
|
|
59
|
+
entries.set(key, { month, costUsd });
|
|
60
|
+
void written.then((landed) => {
|
|
61
|
+
entries.delete(key);
|
|
62
|
+
if (entries.size === 0 && unwritten.get(dataDir) === entries)
|
|
63
|
+
unwritten.delete(dataDir);
|
|
64
|
+
const read = cache.get(dataDir);
|
|
65
|
+
if (landed && read && read.month === month && !read.keys.has(key)) {
|
|
66
|
+
read.fileUsd += costUsd;
|
|
67
|
+
read.keys.add(key);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
28
70
|
}
|
|
29
71
|
export function resetSpendCacheForTests() {
|
|
30
72
|
cache.clear();
|
|
73
|
+
unwritten.clear();
|
|
31
74
|
}
|
|
32
75
|
/** The cap and where the month stands against it; null when there is no
|
|
33
76
|
* enforceable cap (no entitlement, or none set). */
|
|
@@ -59,3 +102,55 @@ export function assertWithinBudget(cfg, dataDir, now = new Date(), isEntitled =
|
|
|
59
102
|
return;
|
|
60
103
|
throw Object.assign(new Error(`this workspace has reached its monthly spend limit of $${usd(state.monthlyUsd)} — an admin can raise it under Settings → Usage`), { status: 409, code: "spend_cap" });
|
|
61
104
|
}
|
|
105
|
+
const ALERTS_FILE = "alerts.json";
|
|
106
|
+
function readMarks(dataDir) {
|
|
107
|
+
try {
|
|
108
|
+
const value = JSON.parse(readFileSync(join(dataDir, "usage", ALERTS_FILE), "utf8"));
|
|
109
|
+
if (typeof value !== "object" || value === null)
|
|
110
|
+
return null;
|
|
111
|
+
const marks = value;
|
|
112
|
+
return typeof marks.month === "string" && typeof marks.monthlyUsd === "number" ? marks : null;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** The notice this booking should raise, if any: the cap the first time the
|
|
119
|
+
* month reaches it, the warning the first time it crosses the threshold, and
|
|
120
|
+
* nothing again for either that month. A new month, or a different cap, is a
|
|
121
|
+
* new budget and starts over. Marks live beside the ledger so a restart does
|
|
122
|
+
* not repeat them; if they cannot be written the notice is still sent once
|
|
123
|
+
* for this run. */
|
|
124
|
+
export function takeSpendAlert(dataDir, state) {
|
|
125
|
+
if (!state || (!state.warn && !state.exceeded))
|
|
126
|
+
return null;
|
|
127
|
+
const saved = readMarks(dataDir) ?? memoryMarks.get(dataDir) ?? null;
|
|
128
|
+
const marks = saved && saved.month === state.month && saved.monthlyUsd === state.monthlyUsd
|
|
129
|
+
? saved
|
|
130
|
+
: { month: state.month, monthlyUsd: state.monthlyUsd };
|
|
131
|
+
const alert = state.exceeded && !marks.cap ? "cap" : state.warn && !marks.warn && !marks.cap ? "warn" : null;
|
|
132
|
+
if (!alert)
|
|
133
|
+
return null;
|
|
134
|
+
// Reaching the cap also answers the warning: one notice, the stronger one.
|
|
135
|
+
const next = { ...marks, warn: true, ...(alert === "cap" ? { cap: true } : {}) };
|
|
136
|
+
memoryMarks.set(dataDir, next);
|
|
137
|
+
try {
|
|
138
|
+
mkdirSync(join(dataDir, "usage"), { recursive: true, mode: 0o700 });
|
|
139
|
+
writeFileSync(join(dataDir, "usage", ALERTS_FILE), JSON.stringify(next), { mode: 0o600 });
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
/* bookkeeping must never take down the turn */
|
|
143
|
+
}
|
|
144
|
+
return alert;
|
|
145
|
+
}
|
|
146
|
+
const memoryMarks = new Map();
|
|
147
|
+
export function resetSpendAlertsForTests() {
|
|
148
|
+
memoryMarks.clear();
|
|
149
|
+
}
|
|
150
|
+
/** The notice's words. English like the server's other notification titles. */
|
|
151
|
+
export function spendAlertText(alert, state) {
|
|
152
|
+
const spent = `$${state.spentUsd.toFixed(2)} of $${usd(state.monthlyUsd)} spent this month (${state.month})`;
|
|
153
|
+
return alert === "cap"
|
|
154
|
+
? { title: "Monthly spend limit reached", body: `${spent}. New turns are refused until an admin raises the limit under Settings → Usage.` }
|
|
155
|
+
: { title: `Spend is at ${state.percent}% of the monthly limit`, body: `${spent}. New turns stop when the limit is reached.` };
|
|
156
|
+
}
|
|
@@ -72,6 +72,8 @@ export function queueSteeredMessage(botId, threadId, text, options = {}) {
|
|
|
72
72
|
reason: options.reason,
|
|
73
73
|
unattended: options.unattended,
|
|
74
74
|
peerAsk: options.peerAsk,
|
|
75
|
+
sender: options.sender,
|
|
76
|
+
trigger: options.trigger,
|
|
75
77
|
};
|
|
76
78
|
saveChatFollowup({ id, kind: "bot", ownerId: botId, threadId, payload: item });
|
|
77
79
|
entry.items.push(item);
|
|
@@ -79,14 +81,15 @@ export function queueSteeredMessage(botId, threadId, text, options = {}) {
|
|
|
79
81
|
changed();
|
|
80
82
|
return { id };
|
|
81
83
|
}
|
|
82
|
-
/** Where a thread stands among this bot's threads waiting for
|
|
84
|
+
/** Where a thread stands among this bot's threads waiting for the bot to
|
|
85
|
+
* become available (a full slot list or an active room turn):
|
|
83
86
|
* 1 for the next to start. The drain visits queues in insertion order, so
|
|
84
87
|
* insertion order is the line. Null when nothing of this bot's is waiting
|
|
85
88
|
* on that thread. */
|
|
86
89
|
export function queuedThreadPosition(botId, threadId) {
|
|
87
90
|
let position = 0;
|
|
88
91
|
for (const [candidate, entry] of queues) {
|
|
89
|
-
if (entry.botId !== botId || !entry.items.some((item) => item.reason === "capacity"))
|
|
92
|
+
if (entry.botId !== botId || !entry.items.some((item) => item.reason === "capacity" || item.reason === "group-turn"))
|
|
90
93
|
continue;
|
|
91
94
|
position += 1;
|
|
92
95
|
if (candidate === threadId)
|
|
@@ -140,6 +143,7 @@ export function drainSteeredMessages(store, run, isBlocked) {
|
|
|
140
143
|
sendId: item.sendId,
|
|
141
144
|
queueId: item.messageId,
|
|
142
145
|
peerAsk: item.peerAsk,
|
|
146
|
+
sender: item.sender,
|
|
143
147
|
}));
|
|
144
148
|
}
|
|
145
149
|
const last = appended.at(-1);
|
|
@@ -153,7 +157,9 @@ export function drainSteeredMessages(store, run, isBlocked) {
|
|
|
153
157
|
const running = run(entry.botId, threadId, prompt, last, appended.map((message) => message.id),
|
|
154
158
|
// one unattended line makes the whole drained turn unattended: a
|
|
155
159
|
// person's words in the same queue cannot re-attend a bot's own
|
|
156
|
-
entry.items.some((item) => item.unattended === true)
|
|
160
|
+
entry.items.some((item) => item.unattended === true),
|
|
161
|
+
// the first waiting line is the one that starts this turn
|
|
162
|
+
{ trigger: entry.items[0].trigger, sender: entry.items[0].sender, peerAsk: entry.items[0].peerAsk });
|
|
157
163
|
void Promise.resolve(running).then(() => settleChatFollowups(ids, null), () => settleChatFollowups(ids, "interrupted")).catch((error) => console.warn("steer-queue: could not settle durable follow-up", error));
|
|
158
164
|
}
|
|
159
165
|
}
|