chainlesschain 0.166.100 → 0.166.101
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 +2 -2
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cd4iUOPt.js → AIOps-BjBS_29I.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton--ucr-zko.js → ActionButton-CUnnQUaj.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DbejYMWk.js → Analytics-BqYZxgnY.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CLgKxeGw.js → AppLayout-m8B886ka.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CTT5BIL5.js → Artifacts-JEOxU3Ug.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DP-WBWe9.js → Audit-DAXS5c9J.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-B6VqheJk.js → BackgroundAgents-e3a9XHw3.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-VWJK5zim.js → Backup-D3Qmp68h.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-YGFO9RHd.js → BaseInput-C7CXyiUl.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CVfydnCd.js → Chat-2AHlAGi8.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BICpB-qU.js → ChatBubbleRenderer-D3KsyCUV.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-Bd5vKRMk.js → Checkbox-YfBuQlBH.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DHNgPHvg.js → Codegen-DCCQDfTL.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CA2BYQ8T.js → Col-03i2WfrV.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DnKJ-qpI.js → Community-CJQIpng6.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-C9BZ8t4r.js → Compact-CCtcQ2pq.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-ByyQ2FOl.js → Compliance-Vxl7ZszJ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-rXTwXE_c.js → Cowork-DN-BwA8I.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BuW-HODy.js → Cron-Dc2Q46X7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-OudNtbYW.js → Crosschain-CQCblsl-.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BQ6BxYNN.js → DID-YYvqUR8C.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cz5HG8tz.js → Dashboard-Cafy3DZT.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BzclHOVv.js → Dropdown-BOyYpIpv.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CbJETFdy.js → EmailListRenderer-B7NYjc1j.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-Dx_pJG3d.js → EvolutionSettings-C5wfxztf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-6JCS1MNg.js → FamilyGuardDashboard-aA7KnDGK.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BrPkMYaf.js → Federation-DlgXUu1L.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Bhc9pobs.js → FormItemContext-B5YsrLDg.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BxOh07uQ.js → GenericCardRenderer-DaWojPFw.js} +1 -1
- package/src/assets/web-panel/assets/{Git-D_idoujr.js → Git-dkhh6Fx2.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-ZGfIRE-x.js → Governance-Cz6vmkjc.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DpAmcwbw.js → Inference-DWt0hbh8.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-D1S1UTgP.js → KnowledgeGraph-BZJ0Z-nL.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-2sc8V5XV.js → Logs-BaBBmL1A.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-C1yByobo.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-0xlFUKbe.js → Marketplace-ct8sgQ0s.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D25eJLGm.js → McpTools-CXKCRGYQ.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-BLRGd9hM.js → Memory-O7ZwlS6a.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-zpFINGzn.js → MobileBridge-By12C-eC.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Cbyd-8kx.js → MobileProjects-Ba-EVdex.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DgXgUlHc.js → Mtc-g-932zV2.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-DzcL-dKH.js → MtcAudit-BW8hB0ly.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-C3OrZ6VN.js → Multisig-zspLaMbT.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bqp_vWA7.js → NLProgramming-dJeUPDoq.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Dyn45-Ht.js → Notes-C4msiRQL.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CLjafGZr.js → NotificationSettings-MOCHM-qg.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Bx8wMauC.js → OrderTableRenderer-CX0T66NM.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-5gr9hyLm.js → Organization-DOZurBfk.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BRdU02hT.js → Overflow-qOT9BhUp.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DIi7fzh6.js → P2P-BMoy7Eo4.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BCbS1KpA.js → PdhVaultBrowser-ByyV1q-D.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BjeP9VtA.js → Permissions-6Qyro4ye.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-y8PXOQUc.js → PersonalDataHub-DR3DRNSX.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B0fL5uxY.js → Pipeline-Cr6TTdud.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-C8jCDn8Z.js → Privacy-B70otG0l.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B_L6Dc6V.js → ProjectInit-BHCqxoSk.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Bb1CYbZh.js → ProjectSettings-B80GZpZk.js} +2 -2
- package/src/assets/web-panel/assets/Projects-cq-b9-fQ.js +1 -0
- package/src/assets/web-panel/assets/{Providers-mBSTDJ0E.js → Providers-dsVTimHa.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CHxgalvt.js → QrScannerModal-DRJ8YepZ.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DI9KIMEV.js → QuickAsk-BSXdEjIB.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-B5_zW1JH.js → Recommend-CQLJGyKC.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BkH4bmM5.js → RemoteSession-BF9WUFVZ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DJrAqwAq.js → Reputation-Cy9o5tvW.js} +1 -1
- package/src/assets/web-panel/assets/{Row-xZ3MAM34.js → Row-BP346_3b.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CCsrLLiu.js → RssFeed-BKulxXf3.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CTCRKvaP.js → Search-EemQj2Dv.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D7joyfp2.js → Security-DcWPAwwk.js} +4 -4
- package/src/assets/web-panel/assets/{Services-oqHPmTG4.js → Services-Bltqciay.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BR_32bTG.js → Skeleton-BwRyOGY8.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BoRz9mln.js → Skills-D98qlvpJ.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-UAGUHZ9b.js → Sla-BUTtBMY0.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DeP_rMgv.js → SpeechSettings-C7kcY_98.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CSzzMmeA.js → SyncSettings-Bsk6lKvT.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CcqOLsLB.js → Tasks-DvSrtz0P.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-cDbkGSDG.js → Templates-BUM6LwPs.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DmOy58cq.js → Tenant-D9TkOm7z.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CsRZH5vr.js → Terminal-CP8ZVErN.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BgJrk6HG.js → TimelineRenderer-V3SoKZ1T.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CCQglKDE.js → Tokens-CCRlUTOA.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-t-Gt3oBY.js → Trigger-CznUcPme.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DQgu2kI0.js → Trust-D1GIk_NF.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Cod2WUGj.js → UkeySign-DHtvtyC-.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-SGN5zaVK.js → VideoEditing-CVxdaJIq.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CnLEL-LD.js → Wallet-D221OjyE.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C2keUpD6.js → WebAuthn-Cw0-ESsq.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-B1GvrwWJ.js → WorkflowEditor-ZYScrBx3.js} +1 -1
- package/src/assets/web-panel/assets/{chat-1E9_P_qY.js → chat-BU0e1obR.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DuU6Os4q.js → colors-gzYweuha.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C5NfOAcM.js → compact-item-ChAXal_k.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D4Fxz9qQ.js → createContext-of3FolD7.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Pa13TVrO.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-C6C49iFk.js → hasIn-4PsK_Kx8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DP2DCKD-.js → index-1dzYRBbg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPzunJER.js → index-B5L7zddz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBVqlelF.js → index-BOeX7BzS.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bk-paNL3.js → index-BdxRVNPY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJS6GTpv.js → index-BfI4dXbi.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlHkc48f.js → index-Bftq5rJR.js} +1 -1
- package/src/assets/web-panel/assets/index-BhQg-v6V.js +1 -0
- package/src/assets/web-panel/assets/{index-HQXFl8fh.js → index-By-pper5.js} +1 -1
- package/src/assets/web-panel/assets/{index-cnDfwWU5.js → index-CCLsbul3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSypQU1h.js → index-CGFL59q4.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-bb-R99.js → index-CHv_63Cp.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7mh6xlu.js → index-CO2owKe0.js} +1 -1
- package/src/assets/web-panel/assets/{index-gh11zJ9h.js → index-CeEE_SGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsH8mFp4.js → index-Cn1clrxx.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2nO02uW.js → index-Cx4Z6nUl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYg1dtne.js → index-CxzBcXOa.js} +1 -1
- package/src/assets/web-panel/assets/{index-AO6iDCSL.js → index-CyDQNprx.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dh-vezHe.js → index-D-Zss5KL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BULggEX6.js → index-D1PGrfHE.js} +1 -1
- package/src/assets/web-panel/assets/{index-bW6vfpLH.js → index-DDYj14Q4.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5lzRoHp.js → index-DEB5fQqG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXb8gEWh.js → index-DEXBckNR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZKRZYTX.js → index-DEzjUgkl.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpiOzt7U.js → index-DJVrzXtk.js} +1 -1
- package/src/assets/web-panel/assets/index-DUD5o-vW.js +1 -0
- package/src/assets/web-panel/assets/{index-jFg3CPlQ.js → index-DZ0oKtBg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZgT7pH-.js → index-D_5LTPMH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXn-e3ki.js → index-D_9k680X.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFK6PTGJ.js → index-D_ialOHx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmvU4Tp_.js → index-DjRAIRX7.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bu6qMjf4.js → index-Djfgsjfy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDVwN9Sv.js → index-DsoV9rzn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpaGe-dr.js → index-Dy0ojH6G.js} +3 -3
- package/src/assets/web-panel/assets/{index-CQJoh5rw.js → index-E8oDMSAo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BU5mPkNX.js → index-Ef5YkVAn.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1TXgNPA.js → index-Wr91cnWr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVE2wCeb.js → index-cFkMIx1x.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_ON9La1.js → index-wo0De0iG.js} +1 -1
- package/src/assets/web-panel/assets/{index-lNmDaf3J.js → index-yMf7zmWv.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-3DSfnIpD.js → initDefaultProps-D0Bx6pgr.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DxGLmgA0.js → motion-BnLuJqJ5.js} +1 -1
- package/src/assets/web-panel/assets/{move-DrlLG6m5.js → move-Bxpq9xgR.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BITbrJSb.js → mtc-parser-BdmrNhOs.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D7k5vIbk.js → omit-tjJD34BZ.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BJSvYFWo.js → pickAttrs-CMFzLt7b.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bf4to1jA.js → placementArrow-BZf6O3cd.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BXzeyzTa.js → responsiveObserve-DFDLazsV.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DLzNUNdj.js → slide-CMbTvp4Y.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-wBBnrRm_.js → statusUtils-FOan8MI7.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BnmJzXNr.js → styleChecker-DTABspd4.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DrN8MrJb.js → useFlexGapSupport-CNHXWBpY.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-dnkYGkjY.js → useFs-5K-c_RoL.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-k_xkv7-k.js → usePersonalDataHub-Di0hL4G-.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B-v8PXpK.js → vnode-DTyDv4ja.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Coue8wAK.js → zoom-ontRt8ft.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/eval.js +11 -8
- package/src/data/changelog.json +13 -0
- package/src/harness/provider-backed-compaction.js +13 -5
- package/src/lib/context-memory-kernel/live-compaction.js +142 -61
- package/src/lib/formal-budget-authority.js +533 -0
- package/src/lib/formal-budget-contract.js +609 -0
- package/src/lib/formal-budget-transport.js +345 -0
- package/src/runtime/agent-core.js +175 -12
- package/src/runtime/headless-stream.js +136 -4
- package/src/assets/web-panel/assets/MarkdownRenderer-575jjYE6.js +0 -1
- package/src/assets/web-panel/assets/Projects-CUSV10Cn.js +0 -1
- package/src/assets/web-panel/assets/devWarning-zDWbYMr7.js +0 -1
- package/src/assets/web-panel/assets/index-BPBsZZnT.js +0 -1
- package/src/assets/web-panel/assets/index-CtGQDg7m.js +0 -1
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import {
|
|
5
|
+
budgetError,
|
|
6
|
+
requireBudget,
|
|
7
|
+
canonical,
|
|
8
|
+
evidenceDigest,
|
|
9
|
+
label,
|
|
10
|
+
validateActivation,
|
|
11
|
+
validateRequest,
|
|
12
|
+
validateResponse,
|
|
13
|
+
quoteNative,
|
|
14
|
+
convertToUsd,
|
|
15
|
+
} from "./formal-budget-contract.js";
|
|
16
|
+
|
|
17
|
+
function database(filename) {
|
|
18
|
+
let db;
|
|
19
|
+
try {
|
|
20
|
+
const Database = createRequire(import.meta.url)("better-sqlite3");
|
|
21
|
+
db = new Database(filename, { fileMustExist: true, timeout: 5000 });
|
|
22
|
+
db.pragma("journal_mode = WAL");
|
|
23
|
+
db.pragma("synchronous = FULL");
|
|
24
|
+
db.pragma("foreign_keys = ON");
|
|
25
|
+
return db;
|
|
26
|
+
} catch (cause) {
|
|
27
|
+
try {
|
|
28
|
+
db?.close();
|
|
29
|
+
} catch {
|
|
30
|
+
/* Preserve original storage failure. */
|
|
31
|
+
}
|
|
32
|
+
throw Object.assign(
|
|
33
|
+
budgetError(
|
|
34
|
+
"CC_BUDGET_STORAGE_UNAVAILABLE",
|
|
35
|
+
"Durable budget authority requires the CLI's declared better-sqlite3 dependency and writable SQLite storage",
|
|
36
|
+
),
|
|
37
|
+
{ cause },
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function checkFile(filename) {
|
|
42
|
+
requireBudget(
|
|
43
|
+
path.isAbsolute(filename),
|
|
44
|
+
"Authority database path must be absolute",
|
|
45
|
+
);
|
|
46
|
+
const stat = fs.lstatSync(filename);
|
|
47
|
+
requireBudget(
|
|
48
|
+
stat.isFile() && !stat.isSymbolicLink(),
|
|
49
|
+
"Authority must be an existing regular database file",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Trusted-host service. Task processes must never receive this object or DB access. */
|
|
54
|
+
export class FormalBudgetAuthority {
|
|
55
|
+
#db;
|
|
56
|
+
#config;
|
|
57
|
+
#now;
|
|
58
|
+
constructor({
|
|
59
|
+
filename,
|
|
60
|
+
lineageId,
|
|
61
|
+
planDigest,
|
|
62
|
+
activationDigest,
|
|
63
|
+
verifyEvidence,
|
|
64
|
+
now = Date.now,
|
|
65
|
+
}) {
|
|
66
|
+
checkFile(filename);
|
|
67
|
+
this.#db = database(filename);
|
|
68
|
+
this.#now = now;
|
|
69
|
+
try {
|
|
70
|
+
const row = this.#db
|
|
71
|
+
.prepare("SELECT config, digest FROM authority WHERE id=1")
|
|
72
|
+
.get();
|
|
73
|
+
requireBudget(
|
|
74
|
+
row && row.digest === activationDigest,
|
|
75
|
+
"Activation receipt differs from pinned authority",
|
|
76
|
+
);
|
|
77
|
+
const config = JSON.parse(row.config);
|
|
78
|
+
requireBudget(
|
|
79
|
+
evidenceDigest(config) === row.digest,
|
|
80
|
+
"Stored activation corrupted",
|
|
81
|
+
);
|
|
82
|
+
requireBudget(
|
|
83
|
+
config.plan.lineageId === lineageId &&
|
|
84
|
+
config.plan.digest === planDigest,
|
|
85
|
+
"Authority lineage/plan mismatch",
|
|
86
|
+
);
|
|
87
|
+
this.#config = validateActivation(config, verifyEvidence, now());
|
|
88
|
+
requireBudget(
|
|
89
|
+
this.#db.pragma("quick_check", { simple: true }) === "ok",
|
|
90
|
+
"Corrupt budget database",
|
|
91
|
+
);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
this.#db.close();
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static initialize({ filename, config, verifyEvidence, now = Date.now }) {
|
|
99
|
+
const checked = validateActivation(config, verifyEvidence, now());
|
|
100
|
+
requireBudget(
|
|
101
|
+
path.isAbsolute(filename),
|
|
102
|
+
"Authority database path must be absolute",
|
|
103
|
+
);
|
|
104
|
+
// Explicit creation only. Opening a missing database never starts a fresh lineage.
|
|
105
|
+
const fd = fs.openSync(filename, "wx", 0o600);
|
|
106
|
+
fs.closeSync(fd);
|
|
107
|
+
const db = database(filename);
|
|
108
|
+
try {
|
|
109
|
+
db.exec(`BEGIN IMMEDIATE;
|
|
110
|
+
CREATE TABLE authority(id INTEGER PRIMARY KEY CHECK(id=1),config TEXT NOT NULL,digest TEXT NOT NULL,halted TEXT);
|
|
111
|
+
CREATE TABLE task(id TEXT PRIMARY KEY,cap INTEGER NOT NULL,historical_spent INTEGER NOT NULL,historical_reserved INTEGER NOT NULL,slots INTEGER NOT NULL);
|
|
112
|
+
CREATE TABLE run(id TEXT PRIMARY KEY,task TEXT NOT NULL REFERENCES task(id),phase TEXT NOT NULL,status TEXT NOT NULL);
|
|
113
|
+
CREATE TABLE call(id TEXT PRIMARY KEY,run TEXT NOT NULL REFERENCES run(id),contract TEXT NOT NULL,request_digest TEXT NOT NULL,state TEXT NOT NULL,reserved_usd INTEGER NOT NULL,reserved_native INTEGER NOT NULL,currency TEXT NOT NULL,cost_usd INTEGER,cost_native INTEGER,receipt_digest TEXT,receipt TEXT);
|
|
114
|
+
CREATE TABLE event(sequence INTEGER PRIMARY KEY,kind TEXT NOT NULL,body TEXT NOT NULL);`);
|
|
115
|
+
const halted = checked.history.tasks.some((t) => t.unknown)
|
|
116
|
+
? "unreconciled historical liability"
|
|
117
|
+
: null;
|
|
118
|
+
db.prepare("INSERT INTO authority VALUES (1,?,?,?)").run(
|
|
119
|
+
canonical(checked),
|
|
120
|
+
evidenceDigest(checked),
|
|
121
|
+
halted,
|
|
122
|
+
);
|
|
123
|
+
for (const task of checked.plan.tasks) {
|
|
124
|
+
const history = checked.history.tasks.find((t) => t.id === task.id);
|
|
125
|
+
db.prepare("INSERT INTO task VALUES (?,?,?,?,?)").run(
|
|
126
|
+
task.id,
|
|
127
|
+
task.capUsdMicros,
|
|
128
|
+
history.spentUsdMicros,
|
|
129
|
+
history.reservedUsdMicros,
|
|
130
|
+
history.runsUsed,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
db.prepare("INSERT INTO event(kind,body) VALUES (?,?)").run(
|
|
134
|
+
"initialized",
|
|
135
|
+
canonical({ activationDigest: evidenceDigest(checked), at: now() }),
|
|
136
|
+
);
|
|
137
|
+
db.exec("COMMIT");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
try {
|
|
140
|
+
db.exec("ROLLBACK");
|
|
141
|
+
} catch {
|
|
142
|
+
/* retain incomplete DB; never silently reset */
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
} finally {
|
|
146
|
+
db.close();
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
filename,
|
|
150
|
+
lineageId: checked.plan.lineageId,
|
|
151
|
+
planDigest: checked.plan.digest,
|
|
152
|
+
activationDigest: evidenceDigest(checked),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#transaction(action) {
|
|
157
|
+
this.#db.exec("BEGIN IMMEDIATE");
|
|
158
|
+
try {
|
|
159
|
+
const result = action();
|
|
160
|
+
this.#db.exec("COMMIT");
|
|
161
|
+
return result;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
try {
|
|
164
|
+
this.#db.exec("ROLLBACK");
|
|
165
|
+
} catch {
|
|
166
|
+
/* original error */
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
#event(kind, body) {
|
|
172
|
+
this.#db
|
|
173
|
+
.prepare("INSERT INTO event(kind,body) VALUES (?,?)")
|
|
174
|
+
.run(kind, canonical({ ...body, at: this.#now() }));
|
|
175
|
+
}
|
|
176
|
+
#admissible() {
|
|
177
|
+
const { halted } = this.#db
|
|
178
|
+
.prepare("SELECT halted FROM authority WHERE id=1")
|
|
179
|
+
.get();
|
|
180
|
+
requireBudget(!halted, `Authority halted: ${halted}`, "CC_BUDGET_HALTED");
|
|
181
|
+
requireBudget(
|
|
182
|
+
!this.#db
|
|
183
|
+
.prepare("SELECT id FROM call WHERE state!='settled' LIMIT 1")
|
|
184
|
+
.get(),
|
|
185
|
+
"Pending/unknown liability must be reconciled before another dispatch",
|
|
186
|
+
"CC_BUDGET_PENDING",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
#contract(id) {
|
|
190
|
+
const contract = this.#config.contracts.find((c) => c.id === id);
|
|
191
|
+
requireBudget(contract, "Unknown contract");
|
|
192
|
+
const fx = this.#config.fx.find(
|
|
193
|
+
(rate) => rate.currency === contract.currency,
|
|
194
|
+
);
|
|
195
|
+
const now = this.#now();
|
|
196
|
+
requireBudget(
|
|
197
|
+
contract.validFrom <= now &&
|
|
198
|
+
now < contract.validUntil &&
|
|
199
|
+
fx.validFrom <= now &&
|
|
200
|
+
now < fx.validUntil,
|
|
201
|
+
"Contract or FX expired",
|
|
202
|
+
"CC_BUDGET_EXPIRED",
|
|
203
|
+
);
|
|
204
|
+
return { contract, fx };
|
|
205
|
+
}
|
|
206
|
+
#exposure(taskId = null) {
|
|
207
|
+
const historical = taskId
|
|
208
|
+
? this.#db
|
|
209
|
+
.prepare(
|
|
210
|
+
"SELECT historical_spent+historical_reserved AS value FROM task WHERE id=?",
|
|
211
|
+
)
|
|
212
|
+
.get(taskId).value
|
|
213
|
+
: this.#db
|
|
214
|
+
.prepare(
|
|
215
|
+
"SELECT sum(historical_spent+historical_reserved) AS value FROM task",
|
|
216
|
+
)
|
|
217
|
+
.get().value;
|
|
218
|
+
const calls = taskId
|
|
219
|
+
? this.#db
|
|
220
|
+
.prepare(
|
|
221
|
+
"SELECT coalesce(sum(coalesce(cost_usd,reserved_usd)),0) AS value FROM call JOIN run ON run.id=call.run WHERE run.task=?",
|
|
222
|
+
)
|
|
223
|
+
.get(taskId).value
|
|
224
|
+
: this.#db
|
|
225
|
+
.prepare(
|
|
226
|
+
"SELECT coalesce(sum(coalesce(cost_usd,reserved_usd)),0) AS value FROM call",
|
|
227
|
+
)
|
|
228
|
+
.get().value;
|
|
229
|
+
return historical + calls;
|
|
230
|
+
}
|
|
231
|
+
beginRun({ taskId, runId, phase }) {
|
|
232
|
+
label(taskId, "task ID");
|
|
233
|
+
label(runId, "run ID");
|
|
234
|
+
requireBudget(
|
|
235
|
+
["initial", "remediation"].includes(phase),
|
|
236
|
+
"Unknown run phase",
|
|
237
|
+
);
|
|
238
|
+
return this.#transaction(() => {
|
|
239
|
+
const existing = this.#db
|
|
240
|
+
.prepare("SELECT * FROM run WHERE id=?")
|
|
241
|
+
.get(runId);
|
|
242
|
+
if (existing) {
|
|
243
|
+
requireBudget(
|
|
244
|
+
existing.task === taskId &&
|
|
245
|
+
existing.phase === phase &&
|
|
246
|
+
existing.status === "active",
|
|
247
|
+
"Run identity conflict or already closed",
|
|
248
|
+
);
|
|
249
|
+
return { runId, resumed: true };
|
|
250
|
+
}
|
|
251
|
+
this.#admissible();
|
|
252
|
+
const task = this.#db
|
|
253
|
+
.prepare("SELECT * FROM task WHERE id=?")
|
|
254
|
+
.get(taskId);
|
|
255
|
+
requireBudget(task, "Unknown task");
|
|
256
|
+
requireBudget(
|
|
257
|
+
task.slots === (phase === "initial" ? 0 : 1),
|
|
258
|
+
"Only initial plus one remediation permitted",
|
|
259
|
+
"CC_BUDGET_RUN_LIMIT",
|
|
260
|
+
);
|
|
261
|
+
requireBudget(
|
|
262
|
+
!this.#db
|
|
263
|
+
.prepare("SELECT id FROM run WHERE task=? AND status='active'")
|
|
264
|
+
.get(taskId),
|
|
265
|
+
"Prior run must close before remediation",
|
|
266
|
+
);
|
|
267
|
+
this.#db
|
|
268
|
+
.prepare("INSERT INTO run VALUES (?,?,?,'active')")
|
|
269
|
+
.run(runId, taskId, phase);
|
|
270
|
+
this.#db.prepare("UPDATE task SET slots=slots+1 WHERE id=?").run(taskId);
|
|
271
|
+
this.#event("run-start", { taskId, runId, phase });
|
|
272
|
+
return { runId, resumed: false };
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
finishRun(runId) {
|
|
276
|
+
return this.#transaction(() => {
|
|
277
|
+
this.#admissible();
|
|
278
|
+
requireBudget(
|
|
279
|
+
this.#db
|
|
280
|
+
.prepare(
|
|
281
|
+
"UPDATE run SET status='closed' WHERE id=? AND status='active'",
|
|
282
|
+
)
|
|
283
|
+
.run(runId).changes === 1,
|
|
284
|
+
"Unknown/closed run",
|
|
285
|
+
);
|
|
286
|
+
this.#event("run-close", { runId });
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
reserve({ runId, callId, body }) {
|
|
290
|
+
label(runId, "run ID");
|
|
291
|
+
label(callId, "physical call ID");
|
|
292
|
+
const request = JSON.parse(canonical(body));
|
|
293
|
+
return this.#transaction(() => {
|
|
294
|
+
this.#admissible();
|
|
295
|
+
requireBudget(
|
|
296
|
+
!this.#db.prepare("SELECT id FROM call WHERE id=?").get(callId),
|
|
297
|
+
"Physical call ID cannot be replayed",
|
|
298
|
+
"CC_BUDGET_CALL_REPLAY",
|
|
299
|
+
);
|
|
300
|
+
const run = this.#db
|
|
301
|
+
.prepare("SELECT * FROM run WHERE id=? AND status='active'")
|
|
302
|
+
.get(runId);
|
|
303
|
+
requireBudget(run, "Active run required");
|
|
304
|
+
const task = this.#config.plan.tasks.find((t) => t.id === run.task);
|
|
305
|
+
const { contract, fx } = this.#contract(task.contractId);
|
|
306
|
+
const requestDigest = validateRequest(contract, request);
|
|
307
|
+
const nativeMicros = quoteNative(contract),
|
|
308
|
+
usdMicros = convertToUsd(nativeMicros, fx);
|
|
309
|
+
requireBudget(
|
|
310
|
+
this.#exposure(run.task) + usdMicros <= task.capUsdMicros,
|
|
311
|
+
"Task budget cannot reserve this call",
|
|
312
|
+
"CC_BUDGET_TASK_LIMIT",
|
|
313
|
+
);
|
|
314
|
+
requireBudget(
|
|
315
|
+
this.#exposure() + usdMicros <= this.#config.plan.limitUsdMicros,
|
|
316
|
+
"Lineage budget cannot reserve this call",
|
|
317
|
+
"CC_BUDGET_LINEAGE_LIMIT",
|
|
318
|
+
);
|
|
319
|
+
this.#db
|
|
320
|
+
.prepare(
|
|
321
|
+
"INSERT INTO call(id,run,contract,request_digest,state,reserved_usd,reserved_native,currency) VALUES (?,?,?,?,'held',?,?,?)",
|
|
322
|
+
)
|
|
323
|
+
.run(
|
|
324
|
+
callId,
|
|
325
|
+
runId,
|
|
326
|
+
contract.id,
|
|
327
|
+
requestDigest,
|
|
328
|
+
usdMicros,
|
|
329
|
+
nativeMicros,
|
|
330
|
+
contract.currency,
|
|
331
|
+
);
|
|
332
|
+
this.#event("reserved", {
|
|
333
|
+
callId,
|
|
334
|
+
runId,
|
|
335
|
+
usdMicros,
|
|
336
|
+
nativeMicros,
|
|
337
|
+
currency: contract.currency,
|
|
338
|
+
requestDigest,
|
|
339
|
+
});
|
|
340
|
+
return {
|
|
341
|
+
callId,
|
|
342
|
+
requestDigest,
|
|
343
|
+
reservedUsdMicros: usdMicros,
|
|
344
|
+
reservedNativeMicros: nativeMicros,
|
|
345
|
+
currency: contract.currency,
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
reservedContract(callId) {
|
|
350
|
+
const call = this.#db.prepare("SELECT * FROM call WHERE id=?").get(callId);
|
|
351
|
+
requireBudget(
|
|
352
|
+
call?.state === "held",
|
|
353
|
+
"Physical call is not reserved",
|
|
354
|
+
"CC_BUDGET_CALL_REPLAY",
|
|
355
|
+
);
|
|
356
|
+
const { contract } = this.#contract(call.contract);
|
|
357
|
+
return JSON.parse(canonical(contract));
|
|
358
|
+
}
|
|
359
|
+
markDispatched(callId, body) {
|
|
360
|
+
return this.#transaction(() => {
|
|
361
|
+
requireBudget(
|
|
362
|
+
!this.#db.prepare("SELECT halted FROM authority WHERE id=1").get()
|
|
363
|
+
.halted,
|
|
364
|
+
"Authority halted",
|
|
365
|
+
"CC_BUDGET_HALTED",
|
|
366
|
+
);
|
|
367
|
+
const call = this.#db
|
|
368
|
+
.prepare("SELECT * FROM call WHERE id=?")
|
|
369
|
+
.get(callId);
|
|
370
|
+
requireBudget(
|
|
371
|
+
call?.state === "held",
|
|
372
|
+
"Physical call dispatch already consumed",
|
|
373
|
+
"CC_BUDGET_CALL_REPLAY",
|
|
374
|
+
);
|
|
375
|
+
const { contract } = this.#contract(call.contract);
|
|
376
|
+
requireBudget(
|
|
377
|
+
validateRequest(contract, body) === call.request_digest,
|
|
378
|
+
"Reserved request changed",
|
|
379
|
+
);
|
|
380
|
+
this.#db
|
|
381
|
+
.prepare("UPDATE call SET state='dispatched' WHERE id=?")
|
|
382
|
+
.run(callId);
|
|
383
|
+
this.#event("dispatch", { callId });
|
|
384
|
+
return JSON.parse(canonical(contract));
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
markUnknown(callId, reason = "provider outcome unknown") {
|
|
388
|
+
this.#transaction(() => {
|
|
389
|
+
const call = this.#db
|
|
390
|
+
.prepare("SELECT state FROM call WHERE id=?")
|
|
391
|
+
.get(callId);
|
|
392
|
+
requireBudget(
|
|
393
|
+
call && call.state !== "settled",
|
|
394
|
+
"Unknown or settled call",
|
|
395
|
+
);
|
|
396
|
+
this.#db
|
|
397
|
+
.prepare("UPDATE call SET state='unknown' WHERE id=?")
|
|
398
|
+
.run(callId);
|
|
399
|
+
this.#db
|
|
400
|
+
.prepare("UPDATE authority SET halted=? WHERE id=1")
|
|
401
|
+
.run(String(reason).slice(0, 200));
|
|
402
|
+
this.#event("unknown", { callId });
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
settle(callId, response) {
|
|
406
|
+
const result = this.#transaction(() => {
|
|
407
|
+
const call = this.#db
|
|
408
|
+
.prepare("SELECT * FROM call WHERE id=?")
|
|
409
|
+
.get(callId);
|
|
410
|
+
requireBudget(call, "Unknown physical call");
|
|
411
|
+
// Never persist response content, prompts, credentials, or hidden reasoning.
|
|
412
|
+
const receipt = {
|
|
413
|
+
id: response?.id ?? null,
|
|
414
|
+
model: response?.model ?? null,
|
|
415
|
+
service_tier: response?.service_tier ?? null,
|
|
416
|
+
usage: response?.usage ?? null,
|
|
417
|
+
};
|
|
418
|
+
if (response && Object.hasOwn(response, "error")) receipt.error = true;
|
|
419
|
+
if (call.state === "settled") {
|
|
420
|
+
let identical = false;
|
|
421
|
+
try {
|
|
422
|
+
identical = evidenceDigest(receipt) === call.receipt_digest;
|
|
423
|
+
} catch {
|
|
424
|
+
/* Non-canonical repeated usage is also a conflict. */
|
|
425
|
+
}
|
|
426
|
+
if (!identical) {
|
|
427
|
+
this.#db
|
|
428
|
+
.prepare(
|
|
429
|
+
"UPDATE call SET state='unknown',cost_usd=NULL,cost_native=NULL WHERE id=?",
|
|
430
|
+
)
|
|
431
|
+
.run(callId);
|
|
432
|
+
this.#db
|
|
433
|
+
.prepare(
|
|
434
|
+
"UPDATE authority SET halted='conflicting provider settlement' WHERE id=1",
|
|
435
|
+
)
|
|
436
|
+
.run();
|
|
437
|
+
this.#event("settlement-conflict", { callId });
|
|
438
|
+
return { conflict: true };
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
costUsdMicros: call.cost_usd,
|
|
442
|
+
costNativeMicros: call.cost_native,
|
|
443
|
+
currency: call.currency,
|
|
444
|
+
duplicate: true,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
requireBudget(
|
|
448
|
+
call.state === "dispatched",
|
|
449
|
+
"Only dispatched known calls can settle; unknown requires external reconciliation",
|
|
450
|
+
"CC_BUDGET_PENDING",
|
|
451
|
+
);
|
|
452
|
+
try {
|
|
453
|
+
// Settlement uses pinned prices even if their dispatch validity just expired.
|
|
454
|
+
const contract = this.#config.contracts.find(
|
|
455
|
+
(c) => c.id === call.contract,
|
|
456
|
+
);
|
|
457
|
+
const fx = this.#config.fx.find(
|
|
458
|
+
(rate) => rate.currency === contract.currency,
|
|
459
|
+
);
|
|
460
|
+
const usage = validateResponse(contract, receipt);
|
|
461
|
+
const native = quoteNative(contract, usage),
|
|
462
|
+
usd = convertToUsd(native, fx);
|
|
463
|
+
requireBudget(
|
|
464
|
+
native <= call.reserved_native && usd <= call.reserved_usd,
|
|
465
|
+
"Actual liability exceeds reservation",
|
|
466
|
+
);
|
|
467
|
+
this.#db
|
|
468
|
+
.prepare(
|
|
469
|
+
"UPDATE call SET state='settled',cost_usd=?,cost_native=?,receipt_digest=?,receipt=? WHERE id=?",
|
|
470
|
+
)
|
|
471
|
+
.run(
|
|
472
|
+
usd,
|
|
473
|
+
native,
|
|
474
|
+
evidenceDigest(receipt),
|
|
475
|
+
canonical(receipt),
|
|
476
|
+
callId,
|
|
477
|
+
);
|
|
478
|
+
this.#event("settled", {
|
|
479
|
+
callId,
|
|
480
|
+
costUsdMicros: usd,
|
|
481
|
+
costNativeMicros: native,
|
|
482
|
+
currency: contract.currency,
|
|
483
|
+
});
|
|
484
|
+
return {
|
|
485
|
+
costUsdMicros: usd,
|
|
486
|
+
costNativeMicros: native,
|
|
487
|
+
currency: contract.currency,
|
|
488
|
+
duplicate: false,
|
|
489
|
+
};
|
|
490
|
+
} catch {
|
|
491
|
+
this.#db
|
|
492
|
+
.prepare("UPDATE call SET state='unknown' WHERE id=?")
|
|
493
|
+
.run(callId);
|
|
494
|
+
this.#db
|
|
495
|
+
.prepare(
|
|
496
|
+
"UPDATE authority SET halted='invalid or unpriced provider usage' WHERE id=1",
|
|
497
|
+
)
|
|
498
|
+
.run();
|
|
499
|
+
this.#event("unknown", { callId });
|
|
500
|
+
return { invalid: true };
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
requireBudget(
|
|
504
|
+
!result.conflict,
|
|
505
|
+
"Conflicting settlement restored full reservation and halted authority",
|
|
506
|
+
"CC_BUDGET_SETTLEMENT_CONFLICT",
|
|
507
|
+
);
|
|
508
|
+
requireBudget(
|
|
509
|
+
!result.invalid,
|
|
510
|
+
"Unknown usage retained full reservation; authority halted",
|
|
511
|
+
"CC_BUDGET_UNKNOWN_USAGE",
|
|
512
|
+
);
|
|
513
|
+
return result;
|
|
514
|
+
}
|
|
515
|
+
status() {
|
|
516
|
+
return {
|
|
517
|
+
lineageId: this.#config.plan.lineageId,
|
|
518
|
+
planDigest: this.#config.plan.digest,
|
|
519
|
+
capUsdMicros: this.#config.plan.limitUsdMicros,
|
|
520
|
+
exposureUsdMicros: this.#exposure(),
|
|
521
|
+
halted: this.#db.prepare("SELECT halted FROM authority WHERE id=1").get()
|
|
522
|
+
.halted,
|
|
523
|
+
pending: this.#db
|
|
524
|
+
.prepare(
|
|
525
|
+
"SELECT id,state,reserved_usd,reserved_native,currency FROM call WHERE state!='settled'",
|
|
526
|
+
)
|
|
527
|
+
.all(),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
close() {
|
|
531
|
+
this.#db.close();
|
|
532
|
+
}
|
|
533
|
+
}
|