chainlesschain 0.166.85 → 0.166.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 +8 -0
- package/package.json +3 -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-Cgo7OFpi.js → AIOps-DdQjhXg9.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-lUMnE2Ln.js → ActionButton-CuwA3hPA.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CsvKzTot.js → Analytics-DTVXJSAC.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-C2z1h1Pv.js → AppLayout-j06JBfEQ.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-C5Q0ZhNb.js → Artifacts-BFAK8LEW.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DXnfPKsI.js → Audit-BiGxRpOf.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DjbGcLm3.js → BackgroundAgents-B6uHaPkZ.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9bVrKvXE.js → Backup-BNAesB8q.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-bx5ri7BD.js → BaseInput-BOuetfCd.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CCFmeUMY.js → Chat-cVzo3m_D.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DZ2Ys-CI.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-ei7MQXe9.js → Checkbox-BaCTS-H8.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CXgBJ9iu.js → Codegen-CBE8NiYh.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DMkMsizS.js → Col-C9wLiUTN.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CvpB_UFR.js → Community-jdClBSIp.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-gay5hbFX.js → Compact-BIjauIlM.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-dG8JQyRL.js → Compliance-CZ-AoaNj.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BA3xfaTW.js → Cowork-BbTKtDS4.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-D1_HsxyY.js → Cron-ImKdtwNi.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DZqoqoy5.js → Crosschain-B54GSHf5.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Cwnr8hCO.js → DID-BQt5WUzs.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CxevypiI.js → Dashboard--gNOffif.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Bs7-yWgr.js → Dropdown-342A5YJw.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CcEs2Puo.js → EmailListRenderer-CtIZ3ZHd.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-C9ipBzAQ.js → EvolutionSettings-CyZPoTio.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dt6--mGn.js → FamilyGuardDashboard-B1MCkFEu.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CD0JN5FF.js → Federation-DZWoERUy.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-POypz3Xv.js → FormItemContext-Onmb0mp3.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Dv9f-0F5.js → GenericCardRenderer-u1qhNNc7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BbTh5Atv.js → Git-BEowR1C5.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-C71dgoir.js → Governance-BiYd3AAg.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DwSHxbJB.js → Inference-CsC4Vjh5.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cx1oQYQZ.js → KnowledgeGraph-BXA7ikPM.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BAJWe00o.js → Logs-C9WoplQm.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-CD8a7lHT.js → MarkdownRenderer-DecEsXsy.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-fwuY7P7Y.js → Marketplace-CCoJBfRL.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Di4693uc.js → McpTools-CzaOWBoA.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Cz3L-GUB.js → Memory-BPwh7u1-.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BuXKIkSb.js → MobileBridge-Bk47v6z2.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-D-CY--Sb.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-LnACMFd0.js → Mtc-ExVyAGlW.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BZy7yE0t.js → MtcAudit-Rrw10csE.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-BFG5iVrE.js → Multisig-8TxxwxZZ.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DgXi04vJ.js → NLProgramming-CNckjtnY.js} +1 -1
- package/src/assets/web-panel/assets/Notes-C4j6cZTu.js +7 -0
- package/src/assets/web-panel/assets/{NotificationSettings-AixrsHw5.js → NotificationSettings-CchU8rWd.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-ZJBoyJ16.js +1 -0
- package/src/assets/web-panel/assets/{Organization-UNCBC3ux.js → Organization-F0Je7EdZ.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-Drh8oAFT.js → Overflow-B2k-JA3u.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-bc2q0xEB.js → P2P-yui9Xxe6.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BGwKxcDM.js → PdhVaultBrowser-CegM7hT6.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DOnUxUzu.js → Permissions-CSFL0uvh.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B3qPZkrr.js → PersonalDataHub-BrMcd-vr.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-B6hfa4-i.js → Pipeline-fzrliaVP.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Bun3c35F.js → Privacy-BrkUjJ_g.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CCObdzk9.js → ProjectInit-CF_QCsW9.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C6lKrSXY.js → ProjectSettings-Duwrd8aC.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DYcta7MZ.js → Projects-B0dLVXzU.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BtykxKCe.js → Providers-Db8iS1xQ.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-D9CDyroY.js → QrScannerModal-CHbNzEQs.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CJauCxXx.js → QuickAsk-CIESMfeR.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BbiYZofR.js → Recommend-BTMbCMtR.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BymCrgFW.js → RemoteSession-DavyK6J-.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-BWrGpMpF.js → Reputation-BFKtMelA.js} +1 -1
- package/src/assets/web-panel/assets/{Row-9pOYIhiW.js → Row-BI9AZUvw.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D3bQJ4qB.js → RssFeed-1nwXt8NY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BpKOOFE0.js → Search-Ck8PDYbk.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DC6C0NSz.js → Security-BgDqpCbq.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Lkf1046J.js → Services-DF8anXm4.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-eCQ_SsDb.js → Skeleton-CoHyY4ZX.js} +1 -1
- package/src/assets/web-panel/assets/Skills-dOaD4Ztl.js +1 -0
- package/src/assets/web-panel/assets/{Sla-BQbKy8XJ.js → Sla-XuOawvQY.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DHsRx-XA.js → SpeechSettings-DFJ7nAiu.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CC281X_J.js → SyncSettings-B2xYj-Pi.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-D_SzNTLi.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Cr2vC0tg.js → Templates-BXFmKh7W.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DeYO5pIG.js → Tenant-CRg2AXbd.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBNgbfPr.js → Terminal-DQgxnuep.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-MoxgaF3x.js → TimelineRenderer-BrPQhQVc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CT2QfAN7.js → Tokens-RByy4USy.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DPUnomxs.js → Trigger-pE3y8u73.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DnufuaTx.js → Trust-DhID9C33.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CJ6iOdPs.js → UkeySign-CPxEPNBH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-xBWkH8bL.js → VideoEditing-ZD8DUxeQ.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-WYV-4hMR.js → Wallet-Br9JREEd.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DJ5Mhd9L.js → WebAuthn-Cn-YacJg.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B__UdOjN.js → WorkflowEditor-BTu0oqMp.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CLlmBVhp.js → chat-CEjws_QF.js} +1 -1
- package/src/assets/web-panel/assets/{colors-B8zoPQYm.js → colors-Bv2RNebW.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CvJ-dUsH.js → compact-item-gSc3EofP.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-LAtMm8SC.js → createContext-kyFDaqWC.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Dcc8pILJ.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CLYvg42H.js → hasIn-BuMWcjhx.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cv7vwX3z.js → index--nrQmk8X.js} +1 -1
- package/src/assets/web-panel/assets/index-05NB95_V.js +1 -0
- package/src/assets/web-panel/assets/{index-BUVrYDNV.js → index-42PTBO7a.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6zlMzZz.js → index-BCS5j4SQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-oX216HRH.js → index-BSSIJ-rv.js} +1 -1
- package/src/assets/web-panel/assets/{index-B02qgCao.js → index-BUOCcXou.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_RPWP_5.js → index-Bg5jis0q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX99e8Xy.js → index-C-LPpwe9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDm9m4vC.js → index-C56Drzol.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHq2Of6z.js → index-C7Jo20JB.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_dq-6_3.js → index-C9U9SjcT.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0Z13xcZ.js → index-CF3TjDNz.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1hHp9_M.js → index-CMS3Mk93.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfOOACfd.js → index-CNt2X-aK.js} +1 -1
- package/src/assets/web-panel/assets/index-Cb6q8wlI.js +1 -0
- package/src/assets/web-panel/assets/{index-DYW4Z3y-.js → index-Cj4RA7oA.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2KFnUsv.js → index-CkfvP2dN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkcQJvsz.js → index-Cm8K8Evn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVDcRQuX.js → index-CvSuM_CB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCzyJ1xS.js → index-Czo_hw97.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cpda0SAz.js → index-D3m3EXbL.js} +1 -1
- package/src/assets/web-panel/assets/{index-iJ6s24bA.js → index-DR561Sqc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Di9vKira.js → index-DRa4R7vv.js} +1 -1
- package/src/assets/web-panel/assets/{index-09YzRb6Z.js → index-DYKSuD02.js} +3 -3
- package/src/assets/web-panel/assets/{index-C5v7cEtm.js → index-DYcPWVN9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGrQ2uEl.js → index-DbVUA5l3.js} +1 -1
- package/src/assets/web-panel/assets/{index-4rr5jFOL.js → index-DbkyTMj-.js} +1 -1
- package/src/assets/web-panel/assets/{index-GR3omApz.js → index-Dej4AGOz.js} +1 -1
- package/src/assets/web-panel/assets/{index-f1ptYBMr.js → index-EpxWlmip.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3uD5H7u.js → index-GpvWtvw3.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6PETyel.js → index-NQ0c4hJ1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnLXLaEF.js → index-OjZUHz5z.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpeV-6y4.js → index-U-ytt84A.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNQUdPam.js → index-_0bfDTDu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM2HqHdT.js → index-jmMAqBAm.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bu7-I2L4.js → index-nLemnBZ2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUcE6rIa.js → index-q65c1kp8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX0aHEHL.js → index-sJ1F5Txa.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxJ4NPVq.js → index-xfS_WO9b.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-QD-koeaw.js → initDefaultProps-B-wqiSu7.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CTItLW4-.js → motion-BK4_B4MD.js} +1 -1
- package/src/assets/web-panel/assets/{move-CGsdTOdN.js → move-BU2TgDDO.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C1kW_CNU.js → mtc-parser-DA3E4_EX.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B4PilaYy.js → omit-CaHz1e_q.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DBGUE12e.js → pickAttrs-NXZ19amf.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DZxCLjuo.js → placementArrow-BCXI9AHO.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CZjQiocN.js → responsiveObserve-DdlZ9tLk.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Btz5hh2_.js → slide-DCZm_Vc4.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CfuxuLIt.js → statusUtils-CuEULKQr.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DdO0Obo_.js → styleChecker-Bo_UXG8x.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CxftJ-Zs.js → useFlexGapSupport-B9PaTcFH.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-pVJChh6K.js → useFs-DwSTGdVR.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-wv7Q0uNE.js → usePersonalDataHub-FswSxnDC.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DVOVfAqC.js → vnode-EUztvYTj.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B5tebZuT.js → zoom-D41MAgV-.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/eval.js +118 -18
- package/src/commands/memory.js +34 -15
- package/src/commands/sandbox.js +9 -0
- package/src/data/changelog.json +26 -0
- package/src/lib/causal-observability.js +6 -45
- package/src/lib/context-memory-kernel/durable-memory-port.js +54 -12
- package/src/lib/context-memory-kernel/index.js +1 -0
- package/src/lib/context-memory-kernel/memory-query-index.js +278 -0
- package/src/lib/context-memory-kernel/memory-service.js +60 -18
- package/src/lib/context-memory-kernel/runtime.js +5 -3
- package/src/lib/context-memory-kernel/segmented-memory-port.js +955 -0
- package/src/lib/eval/outcomes.js +20 -10
- package/src/lib/eval/runner.js +49 -5
- package/src/lib/eval/tasks.js +8 -3
- package/src/lib/eval/verify01-contracts.js +349 -0
- package/src/lib/eval/verify01-execution.js +605 -0
- package/src/lib/execution-support.js +58 -0
- package/src/lib/llm-pricing.js +98 -11
- package/src/lib/model-catalog-review.js +116 -0
- package/src/lib/model-context-catalog.js +23 -1
- package/src/lib/openai-responses.js +39 -0
- package/src/lib/packer/pack-update-applier.js +13 -8
- package/src/lib/permission-authority.js +146 -10
- package/src/lib/process-execution-broker/index.js +8 -0
- package/src/lib/process-execution-broker/linux-subreaper-process.js +24 -2
- package/src/lib/process-execution-broker/process-ownership-journal.js +168 -7
- package/src/lib/process-execution-broker/process-ownership-quarantine.js +56 -1
- package/src/lib/process-execution-broker/process-recovery-cgroup.js +272 -0
- package/src/lib/sandbox-egress-authority-monitor.js +62 -5
- package/src/lib/scoped-permission-store.js +104 -1
- package/src/lib/settings-authority-domain.cjs +37 -2
- package/src/lib/settings-loader.cjs +67 -34
- package/src/lib/settings-permission-authority.cjs +373 -0
- package/src/runtime/agent-core.js +88 -407
- package/src/runtime/headless-runner.js +8 -3
- package/src/runtime/headless-stream.js +10 -3
- package/src/runtime/permission-authority-host.js +151 -0
- package/src/runtime/provider-stream-state.js +379 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-ClqsIfTT.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-Bk-lK4Um.js +0 -1
- package/src/assets/web-panel/assets/Notes-BAj0Kgg4.js +0 -7
- package/src/assets/web-panel/assets/OrderTableRenderer-Dwqk3FlG.js +0 -1
- package/src/assets/web-panel/assets/Skills-BvDSQ3-d.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CHHSWHom.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DgZ4fdGI.js +0 -1
- package/src/assets/web-panel/assets/index-jLEdASOV.js +0 -1
- package/src/assets/web-panel/assets/index-jesGo4rl.js +0 -1
package/src/lib/llm-pricing.js
CHANGED
|
@@ -40,6 +40,7 @@ export const PRICE_TABLE = Object.freeze({
|
|
|
40
40
|
match,
|
|
41
41
|
...model.pricing,
|
|
42
42
|
exact: true,
|
|
43
|
+
...(model.pricingTerms ? { terms: model.pricingTerms } : {}),
|
|
43
44
|
})),
|
|
44
45
|
// Current Anthropic list prices (USD per 1M tokens), verified 2026-06-21
|
|
45
46
|
// against the claude-api reference. The Opus tier dropped to $5/$25 with
|
|
@@ -64,7 +65,12 @@ export const PRICE_TABLE = Object.freeze({
|
|
|
64
65
|
match,
|
|
65
66
|
...model.pricing,
|
|
66
67
|
exact: true,
|
|
67
|
-
terms:
|
|
68
|
+
terms: Object.freeze({
|
|
69
|
+
...GPT6_PRICING_TERMS,
|
|
70
|
+
...(model.cacheReadMultiplier == null
|
|
71
|
+
? {}
|
|
72
|
+
: { cacheReadMultiplier: model.cacheReadMultiplier }),
|
|
73
|
+
}),
|
|
68
74
|
})),
|
|
69
75
|
// GPT-5 family (2026). Matching is longest-pattern-first, so dated/variant
|
|
70
76
|
// ids (gpt-5.5, gpt-5.5-pro, gpt-5.5-instant, …) resolve to the most
|
|
@@ -120,14 +126,16 @@ export const PRICE_TABLE = Object.freeze({
|
|
|
120
126
|
/**
|
|
121
127
|
* Merge user-supplied price overrides (typically `config.llm.pricing`) onto the
|
|
122
128
|
* built-in table. Override shape mirrors PRICE_TABLE:
|
|
123
|
-
* { "<provider>": [ { match: "<
|
|
129
|
+
* { "<provider>": [ { match: "<model>", in: <num>, out: <num>, exact?, terms? }, ... ] }
|
|
124
130
|
*
|
|
125
131
|
* Per provider, a user entry whose `match` equals a built-in pattern REPLACES
|
|
126
132
|
* it; brand-new patterns are prepended so they win ties; unknown providers are
|
|
127
133
|
* added. Malformed entries (missing match / non-numeric / negative / Infinity
|
|
128
134
|
* rate) are skipped so a bad config line can't crash cost reporting or produce a
|
|
129
135
|
* negative cost that undercounts spend. Returns a new table; never mutates
|
|
130
|
-
* PRICE_TABLE or the input.
|
|
136
|
+
* PRICE_TABLE or the input. Explicit terms replace (rather than partly inherit)
|
|
137
|
+
* the matched terms. Invalid terms throw: silently ignoring a requested tier
|
|
138
|
+
* or cache policy can materially misstate a hard budget.
|
|
131
139
|
*
|
|
132
140
|
* @param {object} [overrides]
|
|
133
141
|
* @param {object} [base=PRICE_TABLE]
|
|
@@ -168,6 +176,12 @@ export function mergePricing(overrides, base = PRICE_TABLE) {
|
|
|
168
176
|
match: e.match.toLowerCase(),
|
|
169
177
|
in: Number(e.in),
|
|
170
178
|
out: Number(e.out),
|
|
179
|
+
...(Object.hasOwn(e, "exact")
|
|
180
|
+
? { exact: validateExactMatch(e.exact) }
|
|
181
|
+
: {}),
|
|
182
|
+
...(Object.hasOwn(e, "terms")
|
|
183
|
+
? { terms: normalizePricingTerms(e.terms) }
|
|
184
|
+
: {}),
|
|
171
185
|
explicitOverride: true,
|
|
172
186
|
}));
|
|
173
187
|
if (valid.length === 0) continue;
|
|
@@ -180,6 +194,79 @@ export function mergePricing(overrides, base = PRICE_TABLE) {
|
|
|
180
194
|
return merged;
|
|
181
195
|
}
|
|
182
196
|
|
|
197
|
+
function validateExactMatch(value) {
|
|
198
|
+
if (typeof value !== "boolean")
|
|
199
|
+
throw new TypeError("pricing exact must be a boolean");
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Validate and copy the documented request-level override schema. */
|
|
204
|
+
export function normalizePricingTerms(value) {
|
|
205
|
+
const object = (v) =>
|
|
206
|
+
v !== null &&
|
|
207
|
+
typeof v === "object" &&
|
|
208
|
+
(Object.getPrototypeOf(v) === Object.prototype ||
|
|
209
|
+
Object.getPrototypeOf(v) === null);
|
|
210
|
+
const finite = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
|
|
211
|
+
const fail = () => {
|
|
212
|
+
throw new TypeError(
|
|
213
|
+
"Invalid pricing terms: expected finite non-negative multipliers and a positive long-context threshold",
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
const allowed = new Set([
|
|
217
|
+
"cacheReadMultiplier",
|
|
218
|
+
"cacheWriteMultiplier",
|
|
219
|
+
"longContext",
|
|
220
|
+
"serviceMultipliers",
|
|
221
|
+
]);
|
|
222
|
+
if (!object(value) || Reflect.ownKeys(value).some((key) => !allowed.has(key)))
|
|
223
|
+
fail();
|
|
224
|
+
const result = {};
|
|
225
|
+
for (const key of ["cacheReadMultiplier", "cacheWriteMultiplier"]) {
|
|
226
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
227
|
+
if (!finite(value[key])) fail();
|
|
228
|
+
result[key] = value[key];
|
|
229
|
+
}
|
|
230
|
+
if (Object.hasOwn(value, "longContext")) {
|
|
231
|
+
const tier = value.longContext;
|
|
232
|
+
if (
|
|
233
|
+
!object(tier) ||
|
|
234
|
+
Reflect.ownKeys(tier).some(
|
|
235
|
+
(key) =>
|
|
236
|
+
!["threshold", "inputMultiplier", "outputMultiplier"].includes(key),
|
|
237
|
+
) ||
|
|
238
|
+
!Number.isSafeInteger(tier.threshold) ||
|
|
239
|
+
tier.threshold <= 0 ||
|
|
240
|
+
!finite(tier.inputMultiplier) ||
|
|
241
|
+
!finite(tier.outputMultiplier)
|
|
242
|
+
)
|
|
243
|
+
fail();
|
|
244
|
+
result.longContext = Object.freeze({
|
|
245
|
+
threshold: tier.threshold,
|
|
246
|
+
inputMultiplier: tier.inputMultiplier,
|
|
247
|
+
outputMultiplier: tier.outputMultiplier,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (Object.hasOwn(value, "serviceMultipliers")) {
|
|
251
|
+
const tiers = value.serviceMultipliers;
|
|
252
|
+
if (
|
|
253
|
+
!object(tiers) ||
|
|
254
|
+
Reflect.ownKeys(tiers).length === 0 ||
|
|
255
|
+
Reflect.ownKeys(tiers).some(
|
|
256
|
+
(key) =>
|
|
257
|
+
!["standard", "default", "auto", "batch", "flex", "fast"].includes(
|
|
258
|
+
key,
|
|
259
|
+
) || !finite(tiers[key]),
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
fail();
|
|
263
|
+
result.serviceMultipliers = Object.freeze(
|
|
264
|
+
Object.fromEntries(Object.entries(tiers)),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return Object.freeze(result);
|
|
268
|
+
}
|
|
269
|
+
|
|
183
270
|
/**
|
|
184
271
|
* Look up the rate for a provider/model, or null if unpriced.
|
|
185
272
|
* @param {string} provider
|
|
@@ -199,14 +286,12 @@ export function lookupRate(provider, model, table = PRICE_TABLE) {
|
|
|
199
286
|
const sorted = [...entries].sort((a, b) => b.match.length - a.match.length);
|
|
200
287
|
for (const e of sorted) {
|
|
201
288
|
if (e.exact ? m === e.match : m.includes(e.match)) {
|
|
202
|
-
// An unrecognized new
|
|
289
|
+
// An unrecognized new generation must not inherit an older family price.
|
|
203
290
|
// Explicit operator overrides remain authoritative.
|
|
204
291
|
if (
|
|
205
|
-
e.match
|
|
206
|
-
Number(/^claude-opus-(\d+)(?:[.-]|$)/u.exec(m)?.[1]) >= 5 &&
|
|
207
|
-
!e.explicitOverride
|
|
208
|
-
e.in === 5 &&
|
|
209
|
-
e.out === 25
|
|
292
|
+
["opus", "sonnet"].includes(e.match) &&
|
|
293
|
+
Number(/^claude-(?:opus|sonnet)-(\d+)(?:[.-]|$)/u.exec(m)?.[1]) >= 5 &&
|
|
294
|
+
!e.explicitOverride
|
|
210
295
|
)
|
|
211
296
|
continue;
|
|
212
297
|
return {
|
|
@@ -258,7 +343,7 @@ export function estimateCost({
|
|
|
258
343
|
table,
|
|
259
344
|
} = {}) {
|
|
260
345
|
const rate = lookupRate(provider, model, table);
|
|
261
|
-
const tierMultiplier = rate?.terms
|
|
346
|
+
const tierMultiplier = rate?.terms?.serviceMultipliers
|
|
262
347
|
? Object.hasOwn(rate.terms.serviceMultipliers, serviceTier)
|
|
263
348
|
? rate.terms.serviceMultipliers[serviceTier]
|
|
264
349
|
: null
|
|
@@ -359,7 +444,9 @@ export function priceRollup(aggregate, { table } = {}) {
|
|
|
359
444
|
est[field] = estimates.reduce((sum, entry) => sum + entry[field], 0);
|
|
360
445
|
}
|
|
361
446
|
} else if (
|
|
362
|
-
lookupRate(row.provider, row.model, table)?.terms
|
|
447
|
+
(lookupRate(row.provider, row.model, table)?.terms?.longContext ||
|
|
448
|
+
lookupRate(row.provider, row.model, table)?.terms
|
|
449
|
+
?.serviceMultipliers) &&
|
|
363
450
|
row.calls > 1
|
|
364
451
|
) {
|
|
365
452
|
// A legacy aggregate cannot establish which individual requests crossed
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { resolveModelCapabilityProfile } from "./model-capabilities.js";
|
|
2
|
+
import { lookupRate } from "./llm-pricing.js";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
|
|
5
|
+
/** Compare reviewed release facts with local behavior; never change a catalog. */
|
|
6
|
+
export function reviewModelCatalog(review, snapshots = {}) {
|
|
7
|
+
if (
|
|
8
|
+
review?.schema !== "chainlesschain.model-catalog-review/v1" ||
|
|
9
|
+
!Array.isArray(review.models) ||
|
|
10
|
+
!review.models.length
|
|
11
|
+
) {
|
|
12
|
+
throw new TypeError("Invalid reviewed model catalog");
|
|
13
|
+
}
|
|
14
|
+
const mismatches = [];
|
|
15
|
+
for (const expected of review.models) {
|
|
16
|
+
const actual = resolveModelCapabilityProfile(expected);
|
|
17
|
+
const rate = lookupRate(expected.provider, expected.model);
|
|
18
|
+
for (const field of [
|
|
19
|
+
"runtimeProtocol",
|
|
20
|
+
"contextWindowTokens",
|
|
21
|
+
"advertisedMaxOutputTokens",
|
|
22
|
+
]) {
|
|
23
|
+
if (Object.hasOwn(expected, field) && actual[field] !== expected[field])
|
|
24
|
+
mismatches.push({
|
|
25
|
+
model: expected.model,
|
|
26
|
+
field,
|
|
27
|
+
expected: expected[field],
|
|
28
|
+
actual: actual[field],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (!actual.sources.length)
|
|
32
|
+
mismatches.push({
|
|
33
|
+
model: expected.model,
|
|
34
|
+
field: "official-source",
|
|
35
|
+
actual: null,
|
|
36
|
+
});
|
|
37
|
+
for (const field of ["in", "out"]) {
|
|
38
|
+
if (rate?.[field] !== expected.pricing[field])
|
|
39
|
+
mismatches.push({
|
|
40
|
+
model: expected.model,
|
|
41
|
+
field,
|
|
42
|
+
expected: expected.pricing[field],
|
|
43
|
+
actual: rate?.[field] ?? null,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
for (const field of [
|
|
47
|
+
"cacheReadMultiplier",
|
|
48
|
+
"cacheWriteMultiplier",
|
|
49
|
+
"longContext",
|
|
50
|
+
"serviceMultipliers",
|
|
51
|
+
]) {
|
|
52
|
+
if (
|
|
53
|
+
Object.hasOwn(expected.pricing, field) &&
|
|
54
|
+
!isDeepStrictEqual(
|
|
55
|
+
rate?.terms?.[field] ?? null,
|
|
56
|
+
expected.pricing[field],
|
|
57
|
+
)
|
|
58
|
+
) {
|
|
59
|
+
mismatches.push({
|
|
60
|
+
model: expected.model,
|
|
61
|
+
field,
|
|
62
|
+
expected: expected.pricing[field],
|
|
63
|
+
actual: rate?.terms?.[field] ?? null,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
expected.reasoningEfforts &&
|
|
69
|
+
JSON.stringify(actual.reasoningEfforts) !==
|
|
70
|
+
JSON.stringify(expected.reasoningEfforts)
|
|
71
|
+
)
|
|
72
|
+
mismatches.push({
|
|
73
|
+
model: expected.model,
|
|
74
|
+
field: "reasoningEfforts",
|
|
75
|
+
expected: expected.reasoningEfforts,
|
|
76
|
+
actual: actual.reasoningEfforts ?? null,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const upstream = [];
|
|
80
|
+
for (const [name, source] of Object.entries(snapshots)) {
|
|
81
|
+
if (
|
|
82
|
+
!["codex", "claude"].includes(name) ||
|
|
83
|
+
typeof source !== "string" ||
|
|
84
|
+
source.length > 16 * 1024 * 1024
|
|
85
|
+
)
|
|
86
|
+
throw new TypeError("Invalid upstream release snapshot");
|
|
87
|
+
const text = source
|
|
88
|
+
.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1>/gi, "")
|
|
89
|
+
.replace(/<[^>]+>/g, " ");
|
|
90
|
+
const version =
|
|
91
|
+
name === "codex"
|
|
92
|
+
? /Codex CLI\s+(\d+\.\d+\.\d+)/.exec(text)?.[1]
|
|
93
|
+
: /<Update\s+label="(\d+\.\d+\.\d+)"/u.exec(source)?.[1] ||
|
|
94
|
+
/^##\s+(\d+\.\d+\.\d+)/m.exec(text)?.[1];
|
|
95
|
+
if (!version)
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Cannot identify the ${name} release in the official snapshot`,
|
|
98
|
+
);
|
|
99
|
+
upstream.push({
|
|
100
|
+
name,
|
|
101
|
+
reviewedVersion: review.upstream[name],
|
|
102
|
+
observedVersion: version,
|
|
103
|
+
reviewRequired: version !== review.upstream[name],
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
schema: "chainlesschain.model-catalog-review-result/v1",
|
|
108
|
+
reviewedAt: review.reviewedAt,
|
|
109
|
+
localContractPassed: mismatches.length === 0,
|
|
110
|
+
mismatches,
|
|
111
|
+
upstream,
|
|
112
|
+
reviewRequired:
|
|
113
|
+
mismatches.length > 0 || upstream.some((row) => row.reviewRequired),
|
|
114
|
+
automaticEnablement: false,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -45,7 +45,7 @@ export const CONTEXT_WINDOWS = Object.freeze({
|
|
|
45
45
|
}),
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
export const MODEL_CAPABILITY_CATALOG_VERSION = "2026-
|
|
48
|
+
export const MODEL_CAPABILITY_CATALOG_VERSION = "2026-10-05";
|
|
49
49
|
|
|
50
50
|
// Explicit ownership prevents a model name served by an unrelated provider
|
|
51
51
|
// from silently inheriting another provider's catalog window.
|
|
@@ -85,6 +85,18 @@ export const LEGACY_MODEL_PROVIDERS = Object.freeze({
|
|
|
85
85
|
// Official pages reviewed on the catalog version date. These describe the
|
|
86
86
|
// named models, not endpoint access, account entitlement, or a live CLI run.
|
|
87
87
|
export const DOCUMENTED_OPENAI_MODELS = Object.freeze({
|
|
88
|
+
"gpt-6.1-sol": Object.freeze({
|
|
89
|
+
contextWindowTokens: 1050000,
|
|
90
|
+
advertisedMaxOutputTokens: 128000,
|
|
91
|
+
requiresResponsesForTools: true,
|
|
92
|
+
reasoningEfforts: Object.freeze(["low", "medium", "high", "xhigh", "max"]),
|
|
93
|
+
pricing: Object.freeze({ in: 2, out: 10 }),
|
|
94
|
+
// This generation has a 5% cache-read rate, unlike GPT-6 Sol's 10%.
|
|
95
|
+
cacheReadMultiplier: 0.05,
|
|
96
|
+
sources: Object.freeze([
|
|
97
|
+
"https://developers.openai.com/api/docs/models/gpt-6.1-sol",
|
|
98
|
+
]),
|
|
99
|
+
}),
|
|
88
100
|
"gpt-4o": Object.freeze({
|
|
89
101
|
contextWindowTokens: 128000,
|
|
90
102
|
advertisedMaxOutputTokens: 16384,
|
|
@@ -132,11 +144,21 @@ export const DOCUMENTED_OPENAI_MODELS = Object.freeze({
|
|
|
132
144
|
});
|
|
133
145
|
|
|
134
146
|
export const DOCUMENTED_ANTHROPIC_MODELS = Object.freeze({
|
|
147
|
+
"claude-sonnet-5-5": Object.freeze({
|
|
148
|
+
contextWindowTokens: 1000000,
|
|
149
|
+
advertisedMaxOutputTokens: null,
|
|
150
|
+
pricing: Object.freeze({ in: 2, out: 10 }),
|
|
151
|
+
pricingTerms: Object.freeze({ cacheReadMultiplier: 0.1 }),
|
|
152
|
+
sources: Object.freeze([
|
|
153
|
+
"https://code.claude.com/docs/en/changelog#2-1-284",
|
|
154
|
+
]),
|
|
155
|
+
}),
|
|
135
156
|
"claude-opus-5-5": Object.freeze({
|
|
136
157
|
contextWindowTokens: 1000000,
|
|
137
158
|
// The release notes establish the window and pricing, not an output cap.
|
|
138
159
|
advertisedMaxOutputTokens: null,
|
|
139
160
|
pricing: Object.freeze({ in: 4, out: 20 }),
|
|
161
|
+
pricingTerms: Object.freeze({ cacheReadMultiplier: 0.05 }),
|
|
140
162
|
sources: Object.freeze([
|
|
141
163
|
"https://code.claude.com/docs/en/changelog#2-1-280",
|
|
142
164
|
]),
|
|
@@ -176,7 +176,16 @@ export function createOpenAIResponsesBody({
|
|
|
176
176
|
maxOutputTokens = null,
|
|
177
177
|
reasoning = null,
|
|
178
178
|
stream = false,
|
|
179
|
+
modelProfile = null,
|
|
179
180
|
}) {
|
|
181
|
+
if (
|
|
182
|
+
modelProfile?.advertisedMaxOutputTokens != null &&
|
|
183
|
+
maxOutputTokens > modelProfile.advertisedMaxOutputTokens
|
|
184
|
+
) {
|
|
185
|
+
throw new RangeError(
|
|
186
|
+
`maxOutputTokens exceeds the documented limit (${modelProfile.advertisedMaxOutputTokens}) for ${model}`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
180
189
|
return {
|
|
181
190
|
model,
|
|
182
191
|
input: toOpenAIResponsesInput(messages),
|
|
@@ -189,6 +198,36 @@ export function createOpenAIResponsesBody({
|
|
|
189
198
|
};
|
|
190
199
|
}
|
|
191
200
|
|
|
201
|
+
/** Translate UI intensity without sending an effort rejected by the model. */
|
|
202
|
+
export function createOpenAIResponsesReasoning(
|
|
203
|
+
options = {},
|
|
204
|
+
modelProfile = null,
|
|
205
|
+
) {
|
|
206
|
+
const want = options.thinking;
|
|
207
|
+
if (!want || want === "off" || want === "none") return null;
|
|
208
|
+
const requested =
|
|
209
|
+
typeof options.thinkingEffort === "string"
|
|
210
|
+
? options.thinkingEffort.toLowerCase()
|
|
211
|
+
: null;
|
|
212
|
+
const fallback = ["ultra", "ultrathink"].includes(String(want))
|
|
213
|
+
? "xhigh"
|
|
214
|
+
: want === "think"
|
|
215
|
+
? "medium"
|
|
216
|
+
: "high";
|
|
217
|
+
const generic = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
218
|
+
const effort =
|
|
219
|
+
requested && generic.includes(requested) ? requested : fallback;
|
|
220
|
+
if (
|
|
221
|
+
modelProfile?.reasoningEfforts &&
|
|
222
|
+
!modelProfile.reasoningEfforts.includes(requested || effort)
|
|
223
|
+
) {
|
|
224
|
+
throw new RangeError(
|
|
225
|
+
`Unsupported reasoning effort for ${modelProfile.model}; allowed: ${modelProfile.reasoningEfforts.join(", ")}`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return { effort, summary: "auto" };
|
|
229
|
+
}
|
|
230
|
+
|
|
192
231
|
export function normalizeOpenAIResponsesUsage(usage) {
|
|
193
232
|
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null;
|
|
194
233
|
const input = nonNegativeInteger(usage.input_tokens);
|
|
@@ -853,6 +853,11 @@ async function scheduleWindowsTransaction(ctx) {
|
|
|
853
853
|
function windowsGenerationJournalHelperSource() {
|
|
854
854
|
return String.raw`param()
|
|
855
855
|
$ErrorActionPreference = 'Stop'
|
|
856
|
+
# The journal contains only JSON primitives. Use the inbox .NET serializer and
|
|
857
|
+
# file/path APIs so each phase does not repeat PowerShell module discovery.
|
|
858
|
+
# Keep the same synchronous atomic replacement and durable flush protocol.
|
|
859
|
+
[void][Reflection.Assembly]::Load('System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35')
|
|
860
|
+
$Serializer = [Web.Script.Serialization.JavaScriptSerializer]::new()
|
|
856
861
|
$Action = [string]$env:JOURNAL_ACTION
|
|
857
862
|
$JournalPath = [string]$env:JOURNAL_FILE
|
|
858
863
|
$RetiredPath = [string]$env:JOURNAL_RETIRED
|
|
@@ -872,7 +877,7 @@ if (($Phase -eq 'committed') -ne ($Decision -eq 'commit')) { throw 'Invalid phas
|
|
|
872
877
|
if ($Action -eq 'write' -and $Phase -eq 'prepared') {
|
|
873
878
|
if ([IO.File]::Exists($JournalPath)) { throw 'A generation journal already exists' }
|
|
874
879
|
$Bytes = [Convert]::FromBase64String([string]$env:JOURNAL_INITIAL_BASE64)
|
|
875
|
-
$Journal = [Text.Encoding]::UTF8.GetString($Bytes)
|
|
880
|
+
$Journal = $Serializer.DeserializeObject([Text.Encoding]::UTF8.GetString($Bytes))
|
|
876
881
|
$ParentSource = @'
|
|
877
882
|
using System;
|
|
878
883
|
using System.Runtime.InteropServices;
|
|
@@ -900,10 +905,10 @@ public static class CcParentProcess {
|
|
|
900
905
|
}
|
|
901
906
|
'@
|
|
902
907
|
Add-Type -TypeDefinition $ParentSource -Language CSharp
|
|
903
|
-
$Journal
|
|
908
|
+
$Journal.Add('ownerPid', [CcParentProcess]::Get())
|
|
904
909
|
} else {
|
|
905
910
|
if (-not [IO.File]::Exists($JournalPath)) { throw 'Generation journal is missing' }
|
|
906
|
-
$Journal =
|
|
911
|
+
$Journal = $Serializer.DeserializeObject([IO.File]::ReadAllText($JournalPath, [Text.Encoding]::UTF8))
|
|
907
912
|
}
|
|
908
913
|
|
|
909
914
|
$ValidBefore = {
|
|
@@ -931,7 +936,7 @@ if ($Action -eq 'retire') {
|
|
|
931
936
|
if ([string]$Journal.phase -ne $Phase -or [string]$Journal.decision -ne $Decision) {
|
|
932
937
|
throw 'Generation journal decision changed before retirement'
|
|
933
938
|
}
|
|
934
|
-
$ReplacedPath =
|
|
939
|
+
$ReplacedPath = [IO.Path]::Combine([IO.Path]::GetDirectoryName($JournalPath), ('.chainlesschain.journal-retired-previous-' + [guid]::NewGuid().ToString('N')))
|
|
935
940
|
try {
|
|
936
941
|
if ([IO.File]::Exists($RetiredPath)) {
|
|
937
942
|
[IO.File]::Replace($JournalPath, $RetiredPath, $ReplacedPath, $true)
|
|
@@ -985,11 +990,11 @@ if ($Phase -ne 'prepared') {
|
|
|
985
990
|
$Journal.phase = $Phase
|
|
986
991
|
$Journal.decision = $Decision
|
|
987
992
|
$Journal.updatedAt = [DateTimeOffset]::UtcNow.ToString('o')
|
|
988
|
-
$Directory =
|
|
989
|
-
$StagingPath =
|
|
990
|
-
$ReplacedPath =
|
|
993
|
+
$Directory = [IO.Path]::GetDirectoryName($JournalPath)
|
|
994
|
+
$StagingPath = [IO.Path]::Combine($Directory, ('.chainlesschain.journal-' + [guid]::NewGuid().ToString('N')))
|
|
995
|
+
$ReplacedPath = [IO.Path]::Combine($Directory, ('.chainlesschain.journal-previous-' + [guid]::NewGuid().ToString('N')))
|
|
991
996
|
try {
|
|
992
|
-
$Payload = $Journal
|
|
997
|
+
$Payload = $Serializer.Serialize($Journal)
|
|
993
998
|
$PayloadBytes = [Text.UTF8Encoding]::new($false).GetBytes($Payload + [Environment]::NewLine)
|
|
994
999
|
$Stream = [IO.File]::Open($StagingPath, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
|
|
995
1000
|
try {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/** Resolve static settings plus CLI-owned workspace-scoped permission rules. */
|
|
2
2
|
|
|
3
3
|
import settingsLoader from "./settings-loader.cjs";
|
|
4
|
+
import persistentSettings from "./settings-permission-authority.cjs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import {
|
|
6
7
|
ScopedPermissionStore,
|
|
7
8
|
getScopedPermissionRevision,
|
|
8
9
|
subscribeScopedPermissionRevision,
|
|
10
|
+
projectScopedPermissionObservation,
|
|
9
11
|
} from "./scoped-permission-store.js";
|
|
10
12
|
|
|
11
13
|
const {
|
|
@@ -86,16 +88,41 @@ export function loadPermissionAuthority({
|
|
|
86
88
|
env = process.env,
|
|
87
89
|
baseRules = null,
|
|
88
90
|
scopedStore = null,
|
|
91
|
+
settingsAuthority: persistentAuthority = null,
|
|
89
92
|
} = {}) {
|
|
90
93
|
const settingsRevision = getSettingsPermissionRevision();
|
|
91
94
|
const scopedRevision = baseRules ? null : getScopedPermissionRevision();
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
const persistent = persistentAuthority
|
|
96
|
+
? persistentSettings.readSettingsPermissionAuthority(persistentAuthority)
|
|
97
|
+
: null;
|
|
98
|
+
if (persistentAuthority) {
|
|
99
|
+
const bound =
|
|
100
|
+
persistentSettings.settingsPermissionAuthorityOptions(
|
|
101
|
+
persistentAuthority,
|
|
102
|
+
);
|
|
103
|
+
if (
|
|
104
|
+
path.resolve(cwd) !== bound.cwd ||
|
|
105
|
+
(settingsFile &&
|
|
106
|
+
path.resolve(cwd, settingsFile) !== bound.settingsFile) ||
|
|
107
|
+
(managedSettingsFile &&
|
|
108
|
+
path.resolve(managedSettingsFile) !== bound.managedSettingsFile)
|
|
109
|
+
)
|
|
110
|
+
throw new TypeError("persistent settings authority options mismatch");
|
|
111
|
+
}
|
|
112
|
+
const settingsObservation =
|
|
113
|
+
persistent?.observation ||
|
|
114
|
+
inspectSettingsSources({
|
|
115
|
+
cwd,
|
|
116
|
+
settingsFile,
|
|
117
|
+
managedSettingsFile,
|
|
118
|
+
env,
|
|
119
|
+
});
|
|
120
|
+
const loaded = persistent
|
|
121
|
+
? persistentSettings.projectSettingsPermissionObservation(
|
|
122
|
+
settingsObservation,
|
|
123
|
+
{ env },
|
|
124
|
+
)
|
|
125
|
+
: projectSettingsObservation(settingsObservation, { env });
|
|
99
126
|
const sources = baseRules ? {} : { ...loaded.sources };
|
|
100
127
|
let rules = baseRules
|
|
101
128
|
? applyManagedPermissionPolicy(baseRules, loaded.managed, sources)
|
|
@@ -105,7 +132,29 @@ export function loadPermissionAuthority({
|
|
|
105
132
|
// Explicit caller rules retain their historical replacement semantics.
|
|
106
133
|
// Managed-only policy also suppresses every user-owned scoped grant.
|
|
107
134
|
if (!baseRules) {
|
|
108
|
-
|
|
135
|
+
const bound = persistentAuthority
|
|
136
|
+
? persistentSettings.settingsPermissionAuthorityOptions(
|
|
137
|
+
persistentAuthority,
|
|
138
|
+
)
|
|
139
|
+
: null;
|
|
140
|
+
const store =
|
|
141
|
+
scopedStore ||
|
|
142
|
+
new ScopedPermissionStore({
|
|
143
|
+
cwd,
|
|
144
|
+
...(bound
|
|
145
|
+
? {
|
|
146
|
+
filePath: bound.scopedFile,
|
|
147
|
+
settingsAuthority: persistentAuthority,
|
|
148
|
+
}
|
|
149
|
+
: {}),
|
|
150
|
+
});
|
|
151
|
+
scoped = persistent
|
|
152
|
+
? projectScopedPermissionObservation(
|
|
153
|
+
store,
|
|
154
|
+
persistentAuthority,
|
|
155
|
+
persistent.scopedObservation,
|
|
156
|
+
)
|
|
157
|
+
: store.list();
|
|
109
158
|
const managedOnly =
|
|
110
159
|
loaded.managed?.allowManagedPermissionRulesOnly === true;
|
|
111
160
|
scoped = {
|
|
@@ -143,6 +192,17 @@ export function loadPermissionAuthority({
|
|
|
143
192
|
error.code = "CC_SCOPED_PERMISSION_AUTHORITY_CHANGED";
|
|
144
193
|
throw error;
|
|
145
194
|
}
|
|
195
|
+
if (
|
|
196
|
+
persistent &&
|
|
197
|
+
persistentSettings.readSettingsPermissionAuthority(persistentAuthority)
|
|
198
|
+
.snapshot !== persistent.snapshot
|
|
199
|
+
) {
|
|
200
|
+
const error = new Error(
|
|
201
|
+
"persistent settings changed while permission authority was loading",
|
|
202
|
+
);
|
|
203
|
+
error.code = "CC_SETTINGS_PERMISSION_AUTHORITY_CHANGED";
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
146
206
|
return freezeSnapshot({
|
|
147
207
|
...loaded,
|
|
148
208
|
rules,
|
|
@@ -151,12 +211,17 @@ export function loadPermissionAuthority({
|
|
|
151
211
|
hasRules: hasRules(rules),
|
|
152
212
|
settingsRevision,
|
|
153
213
|
settingsObservation,
|
|
214
|
+
...(persistent ? { persistentSettingsRevision: persistent.snapshot } : {}),
|
|
154
215
|
...(scopedRevision ? { scopedRevision } : {}),
|
|
155
216
|
});
|
|
156
217
|
}
|
|
157
218
|
|
|
158
219
|
export function createPermissionRulesProvider(options = {}) {
|
|
159
|
-
const
|
|
220
|
+
const persistentAuthority = options.settingsAuthority ?? null;
|
|
221
|
+
const bound = persistentAuthority
|
|
222
|
+
? persistentSettings.settingsPermissionAuthorityOptions(persistentAuthority)
|
|
223
|
+
: null;
|
|
224
|
+
const cwd = path.resolve(options.cwd || bound?.cwd || process.cwd());
|
|
160
225
|
const captured = Object.freeze({
|
|
161
226
|
cwd,
|
|
162
227
|
settingsFile: options.settingsFile
|
|
@@ -172,15 +237,86 @@ export function createPermissionRulesProvider(options = {}) {
|
|
|
172
237
|
? freezeSnapshot(cloneRules(options.baseRules))
|
|
173
238
|
: null,
|
|
174
239
|
scopedStore: options.scopedStore || null,
|
|
240
|
+
settingsAuthority: persistentAuthority,
|
|
175
241
|
});
|
|
242
|
+
if (
|
|
243
|
+
bound &&
|
|
244
|
+
(cwd !== bound.cwd ||
|
|
245
|
+
(captured.settingsFile && captured.settingsFile !== bound.settingsFile) ||
|
|
246
|
+
(captured.managedSettingsFile &&
|
|
247
|
+
captured.managedSettingsFile !== bound.managedSettingsFile))
|
|
248
|
+
)
|
|
249
|
+
throw new TypeError("persistent settings authority options mismatch");
|
|
176
250
|
const provider = () => loadPermissionAuthority(captured);
|
|
251
|
+
const localAuthority = captured.baseRules
|
|
252
|
+
? settingsAuthority
|
|
253
|
+
: combinedAuthority;
|
|
177
254
|
providers.set(
|
|
178
255
|
provider,
|
|
179
|
-
|
|
256
|
+
persistentAuthority
|
|
257
|
+
? persistentProviderAuthority(persistentAuthority, localAuthority)
|
|
258
|
+
: localAuthority,
|
|
180
259
|
);
|
|
181
260
|
return Object.freeze(provider);
|
|
182
261
|
}
|
|
183
262
|
|
|
263
|
+
function persistentProviderAuthority(binding, localAuthority) {
|
|
264
|
+
let last = null;
|
|
265
|
+
function getSnapshot() {
|
|
266
|
+
const local = localAuthority.getSnapshot();
|
|
267
|
+
const durable =
|
|
268
|
+
persistentSettings.readSettingsPermissionAuthority(binding).snapshot;
|
|
269
|
+
if (last?.local !== local || last?.durable !== durable)
|
|
270
|
+
last = Object.freeze({ local, durable });
|
|
271
|
+
return last;
|
|
272
|
+
}
|
|
273
|
+
return Object.freeze({
|
|
274
|
+
getSnapshot,
|
|
275
|
+
assertWorkspace(cwd) {
|
|
276
|
+
if (
|
|
277
|
+
path.resolve(cwd) !==
|
|
278
|
+
persistentSettings.settingsPermissionAuthorityOptions(binding).cwd
|
|
279
|
+
)
|
|
280
|
+
throw Object.assign(
|
|
281
|
+
new Error("persistent permission authority workspace mismatch"),
|
|
282
|
+
{ code: "CC_SETTINGS_AUTHORITY_BINDING_CHANGED" },
|
|
283
|
+
);
|
|
284
|
+
},
|
|
285
|
+
assertWritableRoots(roots) {
|
|
286
|
+
persistentSettings.assertSettingsPermissionWritableRoots(binding, roots);
|
|
287
|
+
},
|
|
288
|
+
subscribePolicyRevision(listener) {
|
|
289
|
+
if (typeof listener !== "function")
|
|
290
|
+
throw new TypeError("permission revision listener must be a function");
|
|
291
|
+
let known = getSnapshot();
|
|
292
|
+
const remove = localAuthority.subscribePolicyRevision(listener);
|
|
293
|
+
// Persistence prevents official ABA loss between polls. Timers provide
|
|
294
|
+
// bounded observation, not a promise that another process has stopped
|
|
295
|
+
// before synchronous addRule returns.
|
|
296
|
+
const timer = setInterval(() => {
|
|
297
|
+
let next;
|
|
298
|
+
try {
|
|
299
|
+
next = getSnapshot();
|
|
300
|
+
} catch {
|
|
301
|
+
next = null;
|
|
302
|
+
}
|
|
303
|
+
if (next === known) return;
|
|
304
|
+
known = next;
|
|
305
|
+
try {
|
|
306
|
+
listener(next);
|
|
307
|
+
} catch {
|
|
308
|
+
/* isolate observers */
|
|
309
|
+
}
|
|
310
|
+
}, 100);
|
|
311
|
+
timer.unref?.();
|
|
312
|
+
return () => {
|
|
313
|
+
clearInterval(timer);
|
|
314
|
+
remove();
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
184
320
|
// A legacy callback cannot advertise a trusted revision subscription simply
|
|
185
321
|
// by copying properties onto itself. It still receives sampled revalidation.
|
|
186
322
|
export function permissionRulesProviderAuthority(provider) {
|