chainlesschain 0.162.186 → 0.162.188
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/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DTXiO-TB.js → AIOps-Brr1LVcE.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BbmDW-Pz.js → ActionButton-DQU74DKG.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CSfgXJuD.js → Analytics-CYp9178y.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BS0FFLb7.js → AppLayout-DW4SCYsW.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-B19WFPK2.js → Artifacts-CYlD_ed1.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CQhZBi7n.js → Audit-CWl1rZOi.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOLZCpGH.js → BackgroundAgents-BOR-LPsa.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Dz3R_ePD.js → Backup-DLpG7j1X.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CYfgMpax.js → BaseInput-B35K9wdr.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-5CymMr1j.js → Chat-CwUJb9gB.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-B2A_TJtl.js → ChatBubbleRenderer-1v6aMS1m.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CDXHVYcu.js → Checkbox-pi3-tPNW.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-uq9Z9oiS.js → Codegen-FyNF35eL.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BuW1K_65.js → Col-tGt_JDEr.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Bbg-PvRY.js → Community-CC7LonQX.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-co0pPPTw.js → Compact-BGXrHHjE.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B-pFAv7z.js → Compliance-DSW0TBZ8.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-54ZQS4gC.js → Cowork-Bhk7rbvE.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-B1BF2Seq.js → Cron-DxyiQ2NZ.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Dey6qQTj.js → Crosschain-B2ZkxHok.js} +1 -1
- package/src/assets/web-panel/assets/{DID-ltERvfAj.js → DID-yOJV99eJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DuWxOcqN.js → Dashboard-CKsttz7D.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CrZ8ekTo.js → Dropdown-CVFldXSL.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-fIyHXhUS.js → EmailListRenderer-Bufv638J.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BtRyrRPm.js → FamilyGuardDashboard-4ncjoIjL.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-EUC6Wydj.js → Federation-DsknEDoX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BOpka2G-.js → FormItemContext-Cqe5jWXA.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BXtrrYHn.js → GenericCardRenderer-NAJaFKxc.js} +1 -1
- package/src/assets/web-panel/assets/{Git-8WMb7PiR.js → Git-CszKQpnl.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DNWOeDTV.js → Governance-BXGodXi6.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Bne9b_Lb.js → Inference-CkoYuG2c.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cp-6Us9b.js → KnowledgeGraph-DFlEr_T0.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Ck-uSPAx.js → Logs-CVaENddA.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Vdq7brrJ.js → Marketplace-CDlLT0fw.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-ggLnw4hY.js → McpTools-DdwOyszN.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BKIjNVCU.js → Memory-DKQnkKuG.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BElCY25a.js → MobileBridge-DGseqlzJ.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DpLgovdH.js → MobileProjects-COBbEOlj.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-bNdrF_Zh.js → Mtc-O61Vwyli.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Dw8w1Cqk.js → MtcAudit-Dw2SyasS.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CfGKvlqc.js → Multisig-hSu_l8kf.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-D6OJf7YN.js → NLProgramming-CX1Epurr.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B3n_LzIS.js → Notes-R9xX8o8_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Uu1V_ICM.js → NotificationSettings-jtSNLCuK.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-aGyWwWcp.js → OrderTableRenderer-BU2Yqq5H.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-qjFFmuwK.js → Organization-Pu2WO-U3.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-J4ZgnwZz.js → Overflow-BjnqUyFp.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-YEZF-uVH.js → P2P-jjkvz44c.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-yqx7fwqk.js → PdhVaultBrowser-69ca_WeR.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DdEjlZ02.js → Permissions-D4yDpivd.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DbAPYvns.js → PersonalDataHub-0qsL6iH1.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Bxlk_ZJU.js → Pipeline-Cx7Ybrk2.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CpE-05tz.js → Privacy-PbfR_M_V.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DWlhcGHT.js → ProjectInit-CyV6Gsub.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BnH6BiTQ.js → ProjectSettings-C7KZ2Wdk.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DQDjO-yK.js +1 -0
- package/src/assets/web-panel/assets/{Providers-Bdz_sTyb.js → Providers-DyFVBBMX.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BHh2b3pH.js → QrScannerModal-CzB1BC9_.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D84M09pe.js → QuickAsk-Bk636mC1.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Czc30K6M.js → Recommend-DoWxHIT3.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-q_TU5qxt.js → RemoteSession-oOVIv9wq.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CnTce4WM.js → Reputation-BucoReGG.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C60UFLay.js → Row-DzmOdJU7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BOUFEu9_.js → RssFeed-C1tK7KGU.js} +3 -3
- package/src/assets/web-panel/assets/{Search-D4__blE-.js → Search-CSnInOqk.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BoOm7_JK.js → Security-dlejr15l.js} +3 -3
- package/src/assets/web-panel/assets/{Services-AvzrW6HA.js → Services-BioPZuNh.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BZlrHozi.js → Skeleton-DWMibwpC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CKaMLuk0.js → Skills-BAAnx1B2.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DYCNJF8h.js → Sla-jWNp4hNO.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-y5kqKha-.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-B6tW8dVO.js → SyncSettings-B9GNsc3e.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-oNC_2NVz.js → Tasks-BDBYKRen.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DvLPvvjL.js → Templates-DRiE5eaN.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BDt9UBn_.js → Tenant-BGlAvExF.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-ddCUnH-X.js → Terminal-B8uzrbNY.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CA4sDL47.js → TimelineRenderer-Df48iveY.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-By03hnVs.js → Tokens-CcmHndN6.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dz2mH-ZU.js → Trigger-DPkWxmQ2.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DShSWHeJ.js → Trust-DTb5a5AV.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-JN8lP3gt.js → UkeySign-CBmg5vch.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BgPy4gV3.js → VideoEditing-B0mZrz4O.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CQGEsXw_.js → Wallet-Bxs7_mpd.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-D8xR-ds_.js → WebAuthn-C8x3cMxU.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Df61_p1p.js → WorkflowEditor-DPrNznrP.js} +1 -1
- package/src/assets/web-panel/assets/{chat-fdPby427.js → chat-D3bYWu7S.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Dimqhufo.js → colors-DY-PwMu4.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DHayKcqZ.js → compact-item-LDk10Kw6.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-hZUhypRH.js → createContext-C-2MZnFq.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Dw7zC_dV.js → hasIn-BQIBjcnB.js} +1 -1
- package/src/assets/web-panel/assets/{index-y5LHQ5Ck.js → index-7xKu7Fns.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBQK-LEF.js → index-8JC9BJa1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuMSdwno.js → index-B5pgtZnY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM5BNqp9.js → index-BI4KgcYC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwnUluxu.js → index-BN-xdSWZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7jQEwB6.js → index-BRI3ngNg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIxxvaGN.js → index-BX2b9Orc.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1yMYKi-.js → index-BdXnYMCZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuaVa8tX.js → index-BrnRO9yE.js} +1 -1
- package/src/assets/web-panel/assets/index-C-C7DaG6.js +1 -0
- package/src/assets/web-panel/assets/{index-CIOE8tt6.js → index-C3GNvExG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8XXPz2n.js → index-C7FC8cP2.js} +1 -1
- package/src/assets/web-panel/assets/{index-cEnkyHtX.js → index-COG61sRv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJP7TjWm.js → index-CQVb2dWp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvKHpaao.js → index-CY4KnaO9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxoPzPHy.js → index-CaslYch6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSlda4-6.js → index-CbR6ROOa.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPZ3U5iH.js → index-CiTvBAr-.js} +1 -1
- package/src/assets/web-panel/assets/{index-j0pxzOu7.js → index-Cx6D_dx2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzJXkoZs.js → index-Cz4b6Z_7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVHdkz8F.js → index-D3Oq-DLL.js} +17 -17
- package/src/assets/web-panel/assets/{index-ulvBurtw.js → index-D4K1weKc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyEhrvpm.js → index-DSvo_bp0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DC2R4zvM.js → index-DTIZpPtG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsCo_A0I.js → index-DY2FIrmz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTiVrnpR.js → index-DYyLJPlF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHJUptSx.js → index-Dd2Km97U.js} +1 -1
- package/src/assets/web-panel/assets/{index-CcpDboUd.js → index-Dh-tkpKQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnwDYBnG.js → index-DhRt9cYJ.js} +1 -1
- package/src/assets/web-panel/assets/index-DkfWzWtd.js +1 -0
- package/src/assets/web-panel/assets/{index--DVpUpz3.js → index-DlRYqt3S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVM_KgzF.js → index-HasPt4-h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX1DwDFI.js → index-KFZO_lcx.js} +1 -1
- package/src/assets/web-panel/assets/{index-lPjBQJCy.js → index-L3smcoQX.js} +1 -1
- package/src/assets/web-panel/assets/{index-Urt2R5aE.js → index-L6qixyLc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYdNwhkv.js → index-VCh5zNJo.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bhmpp3cm.js → index-_HRO_cIu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAjvkQ4K.js → index-k7C_oDCO.js} +1 -1
- package/src/assets/web-panel/assets/{index-JE6Udz2r.js → index-lmm9p-jm.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-B4pR3px5.js → initDefaultProps-zQPSHf2C.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CE7dWPPs.js → motion-CKCStGVk.js} +1 -1
- package/src/assets/web-panel/assets/{move-Hw8cUObc.js → move-BufVDGYF.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CvRjE4Fa.js → mtc-parser-DcaFxy5D.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CkFeLVre.js → omit-EScpkGdo.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-5ZMqmbbb.js → pickAttrs-72hG_qfT.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BZ4oeZ0q.js → placementArrow-CMFWiH20.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-lwoiX_Us.js → responsiveObserve-DKQHS41p.js} +1 -1
- package/src/assets/web-panel/assets/{slide-D_as37l8.js → slide-QVAFHTIQ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BsA-4Bs7.js → statusUtils-DIz38e2G.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BVar4sLQ.js → styleChecker-zK5cyLby.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-j1IwVpGd.js → useFlexGapSupport-DhZngk62.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-7Q9bEnUp.js → useFs-BvV2cvkg.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BBwTIRMd.js → usePersonalDataHub-CRmxb9l3.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C7Dk-M8w.js → vnode-CgQIbobP.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D3UHaTFB.js → zoom-D4XrrO0R.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/auto-mode.js +47 -0
- package/src/commands/team.js +1805 -473
- package/src/data/auto-mode-safety-eval-v1.json +3294 -0
- package/src/data/changelog.json +26 -0
- package/src/harness/worktree-isolator.js +671 -79
- package/src/lib/agent-team/task-lease.js +378 -78
- package/src/lib/agent-team/team-agent-stream.js +488 -0
- package/src/lib/agent-team/team-budget.js +216 -20
- package/src/lib/agent-team/team-mailbox.js +389 -15
- package/src/lib/agent-team/team-run-state-lock.js +209 -0
- package/src/lib/agent-team/team-runner.js +547 -63
- package/src/lib/agent-team/team-scope-lock.js +409 -0
- package/src/lib/agent-team/team-task-contract.js +277 -0
- package/src/lib/agent-team/team-worktree.js +1171 -62
- package/src/lib/agent-worktree.js +19 -20
- package/src/lib/auto-mode-safety-classifier.js +2034 -0
- package/src/lib/auto-mode-safety-eval.js +1022 -0
- package/src/lib/collaboration-run-store.js +983 -68
- package/src/lib/with-file-lock.js +190 -24
- package/src/lib/worktree-isolator.js +3 -0
- package/src/lib/worktree-sparse.js +23 -0
- package/src/runtime/headless-stream.js +6 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-DxjsdAXw.js +0 -1
- package/src/assets/web-panel/assets/Projects-CHw2fXDB.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-BvCSU6d2.js +0 -1
- package/src/assets/web-panel/assets/devWarning-ePxoBp92.js +0 -1
- package/src/assets/web-panel/assets/index-BTBDdCoo.js +0 -1
- package/src/assets/web-panel/assets/index-CfOGx8Ns.js +0 -1
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
* (same pricing/cache-token rules as `--max-budget-usd`)
|
|
12
12
|
* - maxWallMs wall-clock since the first task started (time cap)
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* Concurrent agent tasks reserve a bounded slice of the remaining token/USD
|
|
15
|
+
* ceilings before they start. The slice becomes the child task's own hard stop,
|
|
16
|
+
* preventing a 64-worker frontier from each spending the full team remainder.
|
|
17
|
+
* A dimension left null is inactive. Snapshot/restore carries settled totals;
|
|
18
|
+
* in-flight reservations are intentionally discarded after a crash because the
|
|
19
|
+
* caller must adjudicate those executions before resuming.
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
22
|
import { CostBudget } from "../cost-budget.js";
|
|
@@ -25,6 +26,58 @@ const pos = (n) => {
|
|
|
25
26
|
return Number.isFinite(v) && v > 0 ? v : null;
|
|
26
27
|
};
|
|
27
28
|
|
|
29
|
+
function assertBudgetSnapshot(snap) {
|
|
30
|
+
if (
|
|
31
|
+
!snap ||
|
|
32
|
+
typeof snap !== "object" ||
|
|
33
|
+
Array.isArray(snap) ||
|
|
34
|
+
!snap.limits ||
|
|
35
|
+
typeof snap.limits !== "object" ||
|
|
36
|
+
!snap.totals ||
|
|
37
|
+
typeof snap.totals !== "object"
|
|
38
|
+
) {
|
|
39
|
+
throw new TypeError("invalid team budget snapshot");
|
|
40
|
+
}
|
|
41
|
+
for (const field of ["maxTasks", "maxTokens", "maxUsd", "maxWallMs"]) {
|
|
42
|
+
const value = snap.limits[field];
|
|
43
|
+
if (value !== null && (!Number.isFinite(value) || value <= 0)) {
|
|
44
|
+
throw new TypeError(`invalid team budget limit: ${field}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const field of ["tasks", "tokens", "spentUsd", "elapsedMs"]) {
|
|
48
|
+
const value = snap.totals[field];
|
|
49
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
50
|
+
throw new TypeError(`invalid team budget total: ${field}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const field of ["maxTasks", "maxTokens"]) {
|
|
54
|
+
const value = snap.limits[field];
|
|
55
|
+
if (value !== null && !Number.isSafeInteger(value)) {
|
|
56
|
+
throw new TypeError(`invalid team budget integer limit: ${field}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const field of ["tasks", "tokens"]) {
|
|
60
|
+
if (!Number.isSafeInteger(snap.totals[field])) {
|
|
61
|
+
throw new TypeError(`invalid team budget integer total: ${field}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (
|
|
65
|
+
typeof snap.totals.started !== "boolean" ||
|
|
66
|
+
typeof snap.totals.unpricedUsage !== "boolean" ||
|
|
67
|
+
(snap.totals.startedAt !== null &&
|
|
68
|
+
(!Number.isFinite(snap.totals.startedAt) || snap.totals.startedAt < 0))
|
|
69
|
+
) {
|
|
70
|
+
throw new TypeError("invalid team budget wall-clock state");
|
|
71
|
+
}
|
|
72
|
+
if (
|
|
73
|
+
(snap.totals.started && snap.totals.startedAt === null) ||
|
|
74
|
+
(!snap.totals.started &&
|
|
75
|
+
(snap.totals.startedAt !== null || snap.totals.elapsedMs !== 0))
|
|
76
|
+
) {
|
|
77
|
+
throw new TypeError("inconsistent team budget wall-clock state");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
28
81
|
export class TeamBudget {
|
|
29
82
|
constructor({
|
|
30
83
|
maxTasks = null,
|
|
@@ -42,6 +95,10 @@ export class TeamBudget {
|
|
|
42
95
|
this.tasks = 0;
|
|
43
96
|
this.tokens = 0;
|
|
44
97
|
this.startedAt = null; // set on the first recorded task
|
|
98
|
+
this.unpricedUsage = false;
|
|
99
|
+
this._reservations = new Map();
|
|
100
|
+
this.reservedTokens = 0;
|
|
101
|
+
this.reservedUsd = 0;
|
|
45
102
|
}
|
|
46
103
|
|
|
47
104
|
/** Any dimension active? (an all-null budget never stops anything). */
|
|
@@ -66,17 +123,132 @@ export class TeamBudget {
|
|
|
66
123
|
* task-count / wall-clock dimensions move for it.
|
|
67
124
|
* @returns {this}
|
|
68
125
|
*/
|
|
69
|
-
record(
|
|
126
|
+
record(
|
|
127
|
+
{
|
|
128
|
+
usage = null,
|
|
129
|
+
usageRecords = null,
|
|
130
|
+
provider,
|
|
131
|
+
model,
|
|
132
|
+
reservationId = null,
|
|
133
|
+
} = {},
|
|
134
|
+
now = this._now(),
|
|
135
|
+
) {
|
|
70
136
|
this.start(now);
|
|
137
|
+
if (reservationId) this.releaseReservation(reservationId);
|
|
71
138
|
this.tasks += 1;
|
|
72
139
|
if (usage) {
|
|
73
140
|
this.tokens +=
|
|
74
141
|
(Number(usage.input_tokens) || 0) + (Number(usage.output_tokens) || 0);
|
|
75
|
-
|
|
142
|
+
if (Array.isArray(usageRecords) && usageRecords.length > 0) {
|
|
143
|
+
for (const record of usageRecords) {
|
|
144
|
+
this._recordCost(record);
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
this._recordCost({ provider, model, usage });
|
|
148
|
+
}
|
|
76
149
|
}
|
|
77
150
|
return this;
|
|
78
151
|
}
|
|
79
152
|
|
|
153
|
+
_recordCost(record) {
|
|
154
|
+
const estimate = this.cost.add(record);
|
|
155
|
+
if (!this.cost.enabled()) return estimate;
|
|
156
|
+
const value = record?.usage || {};
|
|
157
|
+
const tokens =
|
|
158
|
+
(Number(value.input_tokens) || 0) +
|
|
159
|
+
(Number(value.output_tokens) || 0) +
|
|
160
|
+
(Number(value.cache_read_input_tokens) || 0) +
|
|
161
|
+
(Number(value.cache_creation_input_tokens) || 0);
|
|
162
|
+
const price = Number(estimate?.totalCost);
|
|
163
|
+
if (
|
|
164
|
+
tokens > 0 &&
|
|
165
|
+
estimate?.free !== true &&
|
|
166
|
+
(estimate?.matched !== true || !Number.isFinite(price) || price < 0)
|
|
167
|
+
) {
|
|
168
|
+
this.unpricedUsage = true;
|
|
169
|
+
}
|
|
170
|
+
return estimate;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Reserve one fair slice of the remaining token/USD ceilings.
|
|
175
|
+
*
|
|
176
|
+
* `slots` is the number of claim slots sharing the current remainder. A
|
|
177
|
+
* task-level cap can only tighten its slice. The returned limits are suitable
|
|
178
|
+
* for passing directly to the child agent.
|
|
179
|
+
*/
|
|
180
|
+
reserve(id, { maxTokens = null, maxUsd = null, slots = 1 } = {}) {
|
|
181
|
+
const key = String(id || "");
|
|
182
|
+
if (!key) throw new Error("team budget reservation id is required");
|
|
183
|
+
const existing = this._reservations.get(key);
|
|
184
|
+
if (existing) return { ok: true, ...existing, reused: true };
|
|
185
|
+
|
|
186
|
+
const divisor =
|
|
187
|
+
Number.isSafeInteger(slots) && slots > 0 ? Math.max(1, slots) : 1;
|
|
188
|
+
const requestedTokens = pos(maxTokens);
|
|
189
|
+
const requestedUsd = pos(maxUsd);
|
|
190
|
+
let tokenSlice = requestedTokens;
|
|
191
|
+
let usdSlice = requestedUsd;
|
|
192
|
+
|
|
193
|
+
if (this.maxTokens != null) {
|
|
194
|
+
const remaining = Math.max(
|
|
195
|
+
0,
|
|
196
|
+
Math.floor(this.maxTokens - this.tokens - this.reservedTokens),
|
|
197
|
+
);
|
|
198
|
+
if (remaining <= 0) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
reason: "max-tokens",
|
|
202
|
+
temporary: this.reservedTokens > 0,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const fair = Math.max(1, Math.ceil(remaining / divisor));
|
|
206
|
+
tokenSlice =
|
|
207
|
+
requestedTokens == null ? fair : Math.min(requestedTokens, fair);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (this.cost.limitUsd != null) {
|
|
211
|
+
const remaining = Math.max(
|
|
212
|
+
0,
|
|
213
|
+
this.cost.limitUsd - this.cost.spentUsd - this.reservedUsd,
|
|
214
|
+
);
|
|
215
|
+
if (remaining <= Number.EPSILON) {
|
|
216
|
+
return {
|
|
217
|
+
ok: false,
|
|
218
|
+
reason: "max-usd",
|
|
219
|
+
temporary: this.reservedUsd > 0,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const fair = remaining / divisor;
|
|
223
|
+
usdSlice = requestedUsd == null ? fair : Math.min(requestedUsd, fair);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const reservation = {
|
|
227
|
+
id: key,
|
|
228
|
+
maxTokens: tokenSlice,
|
|
229
|
+
maxUsd: usdSlice,
|
|
230
|
+
reservedTokens: this.maxTokens == null ? 0 : tokenSlice || 0,
|
|
231
|
+
reservedUsd: this.cost.limitUsd == null ? 0 : usdSlice || 0,
|
|
232
|
+
};
|
|
233
|
+
this._reservations.set(key, reservation);
|
|
234
|
+
this.reservedTokens += reservation.reservedTokens;
|
|
235
|
+
this.reservedUsd += reservation.reservedUsd;
|
|
236
|
+
return { ok: true, ...reservation, reused: false };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
releaseReservation(id) {
|
|
240
|
+
const key = String(id || "");
|
|
241
|
+
const reservation = this._reservations.get(key);
|
|
242
|
+
if (!reservation) return false;
|
|
243
|
+
this._reservations.delete(key);
|
|
244
|
+
this.reservedTokens = Math.max(
|
|
245
|
+
0,
|
|
246
|
+
this.reservedTokens - reservation.reservedTokens,
|
|
247
|
+
);
|
|
248
|
+
this.reservedUsd = Math.max(0, this.reservedUsd - reservation.reservedUsd);
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
80
252
|
/**
|
|
81
253
|
* Which cap (if any) is now reached — checked BEFORE starting a new task.
|
|
82
254
|
* @returns {null|"max-tasks"|"max-tokens"|"max-usd"|"max-wall-ms"}
|
|
@@ -88,6 +260,9 @@ export class TeamBudget {
|
|
|
88
260
|
if (this.maxTokens != null && this.tokens >= this.maxTokens) {
|
|
89
261
|
return "max-tokens";
|
|
90
262
|
}
|
|
263
|
+
if (this.cost.enabled() && this.unpricedUsage) {
|
|
264
|
+
return "unpriced-usage";
|
|
265
|
+
}
|
|
91
266
|
if (this.cost.exceeded()) return "max-usd";
|
|
92
267
|
if (
|
|
93
268
|
this.maxWallMs != null &&
|
|
@@ -110,15 +285,21 @@ export class TeamBudget {
|
|
|
110
285
|
maxTasks: this.maxTasks,
|
|
111
286
|
tokens: this.tokens,
|
|
112
287
|
maxTokens: this.maxTokens,
|
|
288
|
+
reservedTokens: this.reservedTokens,
|
|
113
289
|
spentUsd: this.cost.spentUsd,
|
|
114
290
|
maxUsd: this.cost.limitUsd,
|
|
291
|
+
reservedUsd: this.reservedUsd,
|
|
292
|
+
reservations: this._reservations.size,
|
|
293
|
+
unpricedUsage: this.unpricedUsage,
|
|
115
294
|
elapsedMs: this.startedAt == null ? 0 : now - this.startedAt,
|
|
116
295
|
maxWallMs: this.maxWallMs,
|
|
117
296
|
reason: this.reason(now),
|
|
118
297
|
};
|
|
119
298
|
}
|
|
120
299
|
|
|
121
|
-
snapshot() {
|
|
300
|
+
snapshot(now = this._now()) {
|
|
301
|
+
const started = this.startedAt != null;
|
|
302
|
+
const elapsedMs = started ? Math.max(0, now - this.startedAt) : 0;
|
|
122
303
|
return {
|
|
123
304
|
limits: {
|
|
124
305
|
maxTasks: this.maxTasks,
|
|
@@ -131,18 +312,26 @@ export class TeamBudget {
|
|
|
131
312
|
tokens: this.tokens,
|
|
132
313
|
spentUsd: this.cost.spentUsd,
|
|
133
314
|
startedAt: this.startedAt,
|
|
315
|
+
started,
|
|
316
|
+
elapsedMs,
|
|
317
|
+
unpricedUsage: this.unpricedUsage,
|
|
134
318
|
},
|
|
135
319
|
};
|
|
136
320
|
}
|
|
137
321
|
|
|
138
322
|
/**
|
|
139
|
-
* Restore running totals + caps from a snapshot.
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* with no flags can't silently drop a safety cap).
|
|
323
|
+
* Restore running totals + caps from a snapshot. A provided override can only
|
|
324
|
+
* tighten a persisted cap; an omitted value keeps the prior cap so resume
|
|
325
|
+
* cannot silently widen its resource authority.
|
|
143
326
|
*/
|
|
144
327
|
static restore(snap, { now = () => Date.now(), table, overrides = {} } = {}) {
|
|
145
|
-
|
|
328
|
+
assertBudgetSnapshot(snap);
|
|
329
|
+
const pick = (override, stored) => {
|
|
330
|
+
const next = pos(override);
|
|
331
|
+
const prior = pos(stored);
|
|
332
|
+
if (next == null) return prior;
|
|
333
|
+
return prior == null ? next : Math.min(prior, next);
|
|
334
|
+
};
|
|
146
335
|
const b = new TeamBudget({
|
|
147
336
|
maxTasks: pick(overrides.maxTasks, snap?.limits?.maxTasks),
|
|
148
337
|
maxTokens: pick(overrides.maxTokens, snap?.limits?.maxTokens),
|
|
@@ -151,15 +340,22 @@ export class TeamBudget {
|
|
|
151
340
|
table,
|
|
152
341
|
now,
|
|
153
342
|
});
|
|
154
|
-
b.tasks =
|
|
155
|
-
b.tokens =
|
|
343
|
+
b.tasks = snap.totals.tasks;
|
|
344
|
+
b.tokens = snap.totals.tokens;
|
|
156
345
|
// Preserve prior USD spend so the cap keeps counting across a resume.
|
|
157
|
-
const spent =
|
|
158
|
-
b.cost.spentUsd =
|
|
346
|
+
const spent = snap.totals.spentUsd;
|
|
347
|
+
b.cost.spentUsd = spent;
|
|
159
348
|
if (b.cost.spentUsd > 0) b.cost.priced = true;
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
|
|
349
|
+
b.unpricedUsage = snap.totals.unpricedUsage;
|
|
350
|
+
// Preserve active execution time without charging process downtime. This
|
|
351
|
+
// prevents repeated resume from refreshing the team-wide wall-clock cap.
|
|
352
|
+
const restoredAt = b._now();
|
|
353
|
+
const elapsed = snap.totals.elapsedMs;
|
|
354
|
+
const started = snap.totals.started;
|
|
355
|
+
b.startedAt =
|
|
356
|
+
started && Number.isFinite(elapsed) && elapsed >= 0
|
|
357
|
+
? restoredAt - elapsed
|
|
358
|
+
: null;
|
|
163
359
|
return b;
|
|
164
360
|
}
|
|
165
361
|
}
|