chainlesschain 0.166.101 → 0.166.103
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-BjBS_29I.js → AIOps-BLrXpdnP.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CUnnQUaj.js → ActionButton-C0xy9uFy.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BqYZxgnY.js → Analytics-C6EephQP.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-m8B886ka.js → AppLayout-ZCXdOD9y.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-JEOxU3Ug.js → Artifacts-D4MfZ-pl.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DAXS5c9J.js → Audit-BQ8c3bBu.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-e3a9XHw3.js → BackgroundAgents-BOA7_ljM.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D3Qmp68h.js → Backup-60uiQjGT.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C7CXyiUl.js → BaseInput-BDvEbGs_.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-2AHlAGi8.js → Chat-B3IkAuFQ.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-n6BVStHf.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-YfBuQlBH.js → Checkbox-njGbfJWq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DCCQDfTL.js → Codegen-CF7DtdxC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-03i2WfrV.js → Col-C_3Zix40.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CJQIpng6.js → Community-CzU-ZC2d.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CCtcQ2pq.js → Compact-BgeqsPRh.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Vxl7ZszJ.js → Compliance-6l7cwiqp.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DN-BwA8I.js → Cowork-D4s905fI.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-Dc2Q46X7.js → Cron-CGTYSxRS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CQCblsl-.js → Crosschain-BnSo-Yt3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-YYvqUR8C.js → DID-Dm4vk2X1.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cafy3DZT.js → Dashboard-D7H-PZ_N.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BOyYpIpv.js → Dropdown-COslkgxn.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-B7NYjc1j.js → EmailListRenderer-CJWEetmk.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-C5wfxztf.js → EvolutionSettings-BbzebAWe.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-aA7KnDGK.js → FamilyGuardDashboard-BsoTOXwk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DlgXUu1L.js → Federation-Ck5kM_5H.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B5YsrLDg.js → FormItemContext-gShxCqtF.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DaWojPFw.js → GenericCardRenderer-DarN1G-z.js} +1 -1
- package/src/assets/web-panel/assets/{Git-dkhh6Fx2.js → Git-CJfpLyP5.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Cz6vmkjc.js → Governance-CVcJmpQz.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DWt0hbh8.js → Inference-ComU_ig8.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BZJ0Z-nL.js → KnowledgeGraph-35hprFJy.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BaBBmL1A.js → Logs-BQDqBTz0.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BpdGAVCC.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-ct8sgQ0s.js → Marketplace-B9O9OKnn.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CXKCRGYQ.js → McpTools-VMKxA4Wc.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-O7ZwlS6a.js → Memory-CcBbf3Bc.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-By12C-eC.js → MobileBridge-D7myz46N.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Ba-EVdex.js → MobileProjects-Da5KBXyx.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-g-932zV2.js → Mtc-x3NPZfuL.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BW8hB0ly.js → MtcAudit-CwnzptEC.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-zspLaMbT.js → Multisig-CCAxf4Ye.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-dJeUPDoq.js → NLProgramming-CptHKz-Q.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-C4msiRQL.js → Notes-0uEp4DCJ.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-MOCHM-qg.js → NotificationSettings-DthvrOLd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CX0T66NM.js → OrderTableRenderer-CxXuPzZg.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DOZurBfk.js → Organization-BWf2tmQQ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-qOT9BhUp.js → Overflow-BbvAtEth.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BMoy7Eo4.js → P2P-zBo6enit.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-ByyV1q-D.js → PdhVaultBrowser-D_LMQ4A4.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-6Qyro4ye.js → Permissions-C1ucb2ck.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DR3DRNSX.js → PersonalDataHub-Bz64RddJ.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Cr6TTdud.js → Pipeline-DXJt3y3_.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B70otG0l.js → Privacy-DiZ8b_ut.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BHCqxoSk.js → ProjectInit-DfLKKrJ4.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-B80GZpZk.js → ProjectSettings-Ct6kJuIY.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-cq-b9-fQ.js → Projects-BBUMlZ3m.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-dsVTimHa.js → Providers-DYVQPHvc.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DRJ8YepZ.js → QrScannerModal-23Il7iPp.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BSXdEjIB.js → QuickAsk-WgOZKrv1.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CQLJGyKC.js → Recommend-CRIka6xT.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BF9WUFVZ.js → RemoteSession-DOy3GjVZ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Cy9o5tvW.js → Reputation-BXyi6xeR.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BP346_3b.js → Row-CoMxuIcR.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BKulxXf3.js → RssFeed-87hPkoM6.js} +3 -3
- package/src/assets/web-panel/assets/{Search-EemQj2Dv.js → Search-B1EqFCO4.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DcWPAwwk.js → Security-HcC41eGs.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Bltqciay.js → Services-BqLwmvCM.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BwRyOGY8.js → Skeleton-CIMmjW4r.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D98qlvpJ.js → Skills-CgurezJU.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BUTtBMY0.js → Sla-CJuRuqPE.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C7kcY_98.js → SpeechSettings-BUwsbd1o.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bsk6lKvT.js → SyncSettings-qo04TOX-.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-QEFENVsp.js +1 -0
- package/src/assets/web-panel/assets/{Templates-BUM6LwPs.js → Templates-D1PTczPW.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-D9TkOm7z.js → Tenant-Bi0FkEPR.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CP8ZVErN.js → Terminal-DfSt3bIF.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-V3SoKZ1T.js → TimelineRenderer-B7bIVs4R.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CCRlUTOA.js → Tokens-B6w5XrMb.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CznUcPme.js → Trigger-XXVSDWiF.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D1GIk_NF.js → Trust-DYvlgcK-.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DHtvtyC-.js → UkeySign-XyYwJMBM.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CVxdaJIq.js → VideoEditing-C47WbtHH.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D221OjyE.js → Wallet-CYfqTj_C.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Cw0-ESsq.js → WebAuthn-3AduFn1l.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-ZYScrBx3.js → WorkflowEditor-DHYEHjMJ.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BU0e1obR.js → chat-AeehErSw.js} +1 -1
- package/src/assets/web-panel/assets/{colors-gzYweuha.js → colors-DKXJ8paX.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-ChAXal_k.js → compact-item-BOlCiomD.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-of3FolD7.js → createContext-BAP6U85b.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-rGldFhxo.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-4PsK_Kx8.js → hasIn-BLmeL1Xe.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cn1clrxx.js → index-B0IjlB9m.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOeX7BzS.js → index-BAS2QqLn.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx4Z6nUl.js → index-BRcJlKSa.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjRAIRX7.js → index-BSOenF5s.js} +1 -1
- package/src/assets/web-panel/assets/{index-Djfgsjfy.js → index-BTnp4Nt3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfI4dXbi.js → index-BhcpWl5z.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_5LTPMH.js → index-Bn54FU3i.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyDQNprx.js → index-Bpx54vfs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEXBckNR.js → index-Bs2O3f81.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ef5YkVAn.js → index-Bt-FCVzh.js} +1 -1
- package/src/assets/web-panel/assets/{index-E8oDMSAo.js → index-BuUwKmEI.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdxRVNPY.js → index-C5AKY6bJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Wr91cnWr.js → index-C8qi53Ki.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5L7zddz.js → index-CFKzWIo8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dy0ojH6G.js → index-CRa4pAaM.js} +3 -3
- package/src/assets/web-panel/assets/{index-wo0De0iG.js → index-CaHhfpBi.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-Zss5KL.js → index-CdNSmvog.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEzjUgkl.js → index-CgtaOzMo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDYj14Q4.js → index-CiqeF0Pf.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_9k680X.js → index-ClpwxOXo.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1PGrfHE.js → index-Co7pjF53.js} +1 -1
- package/src/assets/web-panel/assets/index-Cw-lfvvV.js +1 -0
- package/src/assets/web-panel/assets/{index-CeEE_SGZ.js → index-DE7cbDDe.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxzBcXOa.js → index-DQLQuwpA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEB5fQqG.js → index-DWYqOI1A.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO2owKe0.js → index-De2fPxdb.js} +1 -1
- package/src/assets/web-panel/assets/{index-1dzYRBbg.js → index-DpZqbc4w.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGFL59q4.js → index-DsQ38fVY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsoV9rzn.js → index-Dv8-g1pY.js} +1 -1
- package/src/assets/web-panel/assets/{index-yMf7zmWv.js → index-EKNODw4P.js} +1 -1
- package/src/assets/web-panel/assets/{index-CHv_63Cp.js → index-FPXLIXoy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCLsbul3.js → index-IlzfIRNh.js} +1 -1
- package/src/assets/web-panel/assets/{index-cFkMIx1x.js → index-OLKhib9T.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bftq5rJR.js → index-PEe9OWEQ.js} +1 -1
- package/src/assets/web-panel/assets/index-QBXRSyoZ.js +1 -0
- package/src/assets/web-panel/assets/{index-DJVrzXtk.js → index-T28naovw.js} +1 -1
- package/src/assets/web-panel/assets/{index-By-pper5.js → index-ZMbXl5lo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZ0oKtBg.js → index-urPvyUVk.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_ialOHx.js → index-wyKpTsGM.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D0Bx6pgr.js → initDefaultProps-CDWa1rnn.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BnLuJqJ5.js → motion-CX2Cnsd5.js} +1 -1
- package/src/assets/web-panel/assets/{move-Bxpq9xgR.js → move-Bfe1NcEF.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BdmrNhOs.js → mtc-parser-DbdqleHE.js} +1 -1
- package/src/assets/web-panel/assets/{omit-tjJD34BZ.js → omit-CE2K7NVv.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CMFzLt7b.js → pickAttrs-y_bZgxWG.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BZf6O3cd.js → placementArrow-BOHR4zwr.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DFDLazsV.js → responsiveObserve-B8Jwq05-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CMbTvp4Y.js → slide-BD7RJ3Kn.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-FOan8MI7.js → statusUtils-BuxDJeDx.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DTABspd4.js → styleChecker-DgYQ2Q0o.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CNHXWBpY.js → useFlexGapSupport-DAHF0Fwi.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-5K-c_RoL.js → useFs-D4kDyLFp.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Di0hL4G-.js → usePersonalDataHub-BHtSxW4A.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DTyDv4ja.js → vnode-CHKtdqW0.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-ontRt8ft.js → zoom-CIZAMma9.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agent.js +64 -40
- package/src/commands/ask.js +6 -4
- package/src/commands/team.js +28 -19
- package/src/data/changelog.json +26 -0
- package/src/harness/prompt-compressor.js +2 -0
- package/src/lib/advisor-runtime.js +4 -2
- package/src/lib/agent-ipc-bus.js +2 -0
- package/src/lib/background-agent-keeper-client.js +3 -0
- package/src/lib/browser-automation.js +4 -0
- package/src/lib/cc-android-bridge.js +4 -0
- package/src/lib/chat-core.js +11 -9
- package/src/lib/cloud/cloud-client.js +10 -1
- package/src/lib/config-schema.js +18 -5
- package/src/lib/context-memory-kernel/memory-query-index.js +5 -2
- package/src/lib/context-memory-kernel/runtime.js +4 -0
- package/src/lib/context-memory-kernel/segmented-memory-port.js +28 -3
- package/src/lib/context-source-ledger.js +52 -9
- package/src/lib/cowork-adapter.js +4 -3
- package/src/lib/decision-layer/providers.js +16 -11
- package/src/lib/evolution/governed-hub-llm.js +31 -16
- package/src/lib/evolution/governed-hub-resolver.js +4 -0
- package/src/lib/evolution/governed-skill-synthesis-process-grader.js +4 -0
- package/src/lib/evomap-client.js +14 -2
- package/src/lib/formal-budget-authority.js +191 -59
- package/src/lib/formal-budget-contract.js +7 -13
- package/src/lib/formal-budget-errors.js +31 -0
- package/src/lib/formal-budget-lineage-registry.js +612 -0
- package/src/lib/formal-budget-provider-scope.js +143 -0
- package/src/lib/formal-budget-transport.js +235 -100
- package/src/lib/gateway-client-resilience.js +5 -0
- package/src/lib/goal-condition-engine.js +25 -2
- package/src/lib/hub-llm-client.js +25 -16
- package/src/lib/llm-config-defaults.js +10 -4
- package/src/lib/llm-connection-probe.js +17 -10
- package/src/lib/llm-pricing.js +16 -4
- package/src/lib/llm-providers.js +4 -1
- package/src/lib/model-capabilities.js +26 -0
- package/src/lib/model-catalog-review.js +18 -1
- package/src/lib/model-context-catalog.js +41 -1
- package/src/lib/model-failure-policy.js +2 -0
- package/src/lib/openai-responses.js +8 -0
- package/src/lib/plugin-runtime/marketplace-network.js +9 -5
- package/src/lib/process-execution-broker/credential-agent.js +7 -2
- package/src/lib/process-execution-broker/credential-transport.js +4 -0
- package/src/lib/process-execution-broker/index.js +8 -0
- package/src/lib/provider-stream.js +28 -13
- package/src/lib/sandbox-egress-worker.js +3 -0
- package/src/lib/skill-retrieval-router.js +1 -0
- package/src/lib/stream-retry.js +2 -0
- package/src/lib/sub-agent-context.js +6 -3
- package/src/lib/usage-pricing-context.js +33 -2
- package/src/lib/web-fetch.js +2 -0
- package/src/lib/web-search.js +5 -0
- package/src/repl/agent-repl.js +247 -153
- package/src/runtime/agent-core.js +228 -96
- package/src/runtime/coding-agent-contract-shared.cjs +14 -1
- package/src/runtime/headless-runner.js +1 -0
- package/src/runtime/headless-stream.js +1 -0
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/provider-stream-state.js +14 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-D3KsyCUV.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-C1yByobo.js +0 -1
- package/src/assets/web-panel/assets/Tasks-DvSrtz0P.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Pa13TVrO.js +0 -1
- package/src/assets/web-panel/assets/index-BhQg-v6V.js +0 -1
- package/src/assets/web-panel/assets/index-DUD5o-vW.js +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { budgetError, label, requireBudget } from "./formal-budget-errors.js";
|
|
3
|
+
|
|
4
|
+
const providerScope = new AsyncLocalStorage();
|
|
5
|
+
|
|
6
|
+
export function hasFormalBudgetProviderScope() {
|
|
7
|
+
return !!providerScope.getStore();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Guard an adapter which cannot pass its physical calls to the authority. */
|
|
11
|
+
export function requireUnscopedModelTransport() {
|
|
12
|
+
requireBudget(
|
|
13
|
+
!providerScope.getStore(),
|
|
14
|
+
"This model adapter has no governed transport for the active budget scope",
|
|
15
|
+
"CC_BUDGET_TRANSPORT_NOT_COVERED",
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Search/service charges need their own verified contract, not a Chat quote. */
|
|
20
|
+
export function requireUnscopedPaidService() {
|
|
21
|
+
const scope = providerScope.getStore();
|
|
22
|
+
if (!scope) return;
|
|
23
|
+
requireBudget(
|
|
24
|
+
!scope.closed,
|
|
25
|
+
"Provider budget scope closed",
|
|
26
|
+
"CC_BUDGET_SCOPE_CLOSED",
|
|
27
|
+
);
|
|
28
|
+
throw budgetError(
|
|
29
|
+
"CC_BUDGET_SERVICE_NOT_COVERED",
|
|
30
|
+
"This external service has no verified billing contract for the active budget scope",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Canonical Agent tools can launch shells, MCP peers, skills, remote work or
|
|
36
|
+
* descendants. None has a verified physical-request contract in this scope.
|
|
37
|
+
* Deny before inspecting caller-controlled tool arguments or host callbacks.
|
|
38
|
+
*/
|
|
39
|
+
export function requireUnscopedToolExecution() {
|
|
40
|
+
const scope = providerScope.getStore();
|
|
41
|
+
if (!scope) return;
|
|
42
|
+
requireBudget(
|
|
43
|
+
!scope.closed,
|
|
44
|
+
"Provider budget scope closed",
|
|
45
|
+
"CC_BUDGET_SCOPE_CLOSED",
|
|
46
|
+
);
|
|
47
|
+
throw budgetError(
|
|
48
|
+
"CC_BUDGET_TOOL_NOT_COVERED",
|
|
49
|
+
"Agent tool execution has no governed descendant or external-service contract for the active budget scope",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Direct process/keeper controls bypass executeTool and cannot inherit its guard. */
|
|
54
|
+
export function requireUnscopedDescendantControl() {
|
|
55
|
+
const scope = providerScope.getStore();
|
|
56
|
+
if (!scope) return;
|
|
57
|
+
requireBudget(
|
|
58
|
+
!scope.closed,
|
|
59
|
+
"Provider budget scope closed",
|
|
60
|
+
"CC_BUDGET_SCOPE_CLOSED",
|
|
61
|
+
);
|
|
62
|
+
throw budgetError(
|
|
63
|
+
"CC_BUDGET_DESCENDANT_NOT_COVERED",
|
|
64
|
+
"Descendant process control has no governed physical-request contract for the active budget scope",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Direct URL/browser tools have no governed target or remote-cost contract. */
|
|
69
|
+
export function requireUnscopedExternalToolAccess() {
|
|
70
|
+
const scope = providerScope.getStore();
|
|
71
|
+
if (!scope) return;
|
|
72
|
+
requireBudget(
|
|
73
|
+
!scope.closed,
|
|
74
|
+
"Provider budget scope closed",
|
|
75
|
+
"CC_BUDGET_SCOPE_CLOSED",
|
|
76
|
+
);
|
|
77
|
+
throw budgetError(
|
|
78
|
+
"CC_BUDGET_EXTERNAL_TOOL_NOT_COVERED",
|
|
79
|
+
"Direct external tool access has no governed target or cost contract for the active budget scope",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Trusted-host opt-in only. Consume agent generators and response bodies inside
|
|
85
|
+
* operation; outstanding requests are aborted when it returns. This binds async
|
|
86
|
+
* calls in this process, not subprocesses, workers, arbitrary fetch or OS egress.
|
|
87
|
+
* It neither admits formal evaluation nor creates/renews a budget lineage.
|
|
88
|
+
*/
|
|
89
|
+
export async function withFormalBudgetProviderScope(
|
|
90
|
+
{ transport, runId },
|
|
91
|
+
operation,
|
|
92
|
+
) {
|
|
93
|
+
requireBudget(
|
|
94
|
+
!providerScope.getStore(),
|
|
95
|
+
"A provider budget scope cannot be replaced by a nested scope",
|
|
96
|
+
"CC_BUDGET_SCOPE_NESTED",
|
|
97
|
+
);
|
|
98
|
+
label(runId, "run ID");
|
|
99
|
+
requireBudget(
|
|
100
|
+
typeof transport?.createFetch === "function" &&
|
|
101
|
+
typeof operation === "function",
|
|
102
|
+
"Trusted budget transport and operation required",
|
|
103
|
+
);
|
|
104
|
+
const controller = new AbortController();
|
|
105
|
+
const scope = {
|
|
106
|
+
fetch: transport.createFetch({ runId }),
|
|
107
|
+
signal: controller.signal,
|
|
108
|
+
closed: false,
|
|
109
|
+
};
|
|
110
|
+
return providerScope.run(scope, async () => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await operation();
|
|
113
|
+
requireBudget(
|
|
114
|
+
typeof result?.[Symbol.asyncIterator] !== "function",
|
|
115
|
+
"Consume model generators inside the provider budget scope",
|
|
116
|
+
"CC_BUDGET_SCOPE_UNCONSUMED",
|
|
117
|
+
);
|
|
118
|
+
return result;
|
|
119
|
+
} finally {
|
|
120
|
+
scope.closed = true;
|
|
121
|
+
controller.abort(
|
|
122
|
+
budgetError("CC_BUDGET_SCOPE_CLOSED", "Provider budget scope closed"),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The injected fallback is used only outside a host-owned budget scope. */
|
|
129
|
+
export async function providerFetch(input, init, fetchImpl = globalThis.fetch) {
|
|
130
|
+
const scope = providerScope.getStore();
|
|
131
|
+
if (!scope) return fetchImpl(input, init);
|
|
132
|
+
requireBudget(
|
|
133
|
+
!scope.closed,
|
|
134
|
+
"Provider budget scope closed",
|
|
135
|
+
"CC_BUDGET_SCOPE_CLOSED",
|
|
136
|
+
);
|
|
137
|
+
return scope.fetch(input, {
|
|
138
|
+
...init,
|
|
139
|
+
signal: init?.signal
|
|
140
|
+
? AbortSignal.any([scope.signal, init.signal])
|
|
141
|
+
: scope.signal,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { raceWithAbort } from "./abort-utils.js";
|
|
1
3
|
import {
|
|
2
4
|
budgetError,
|
|
3
5
|
canonical,
|
|
6
|
+
label,
|
|
4
7
|
requireBudget,
|
|
5
8
|
} from "./formal-budget-contract.js";
|
|
6
9
|
|
|
@@ -124,36 +127,6 @@ export function formalBudgetTaskEnvironment(environment = process.env) {
|
|
|
124
127
|
);
|
|
125
128
|
}
|
|
126
129
|
|
|
127
|
-
async function boundedText(response, maximumBytes) {
|
|
128
|
-
requireBudget(response.body?.getReader, "Response body unavailable");
|
|
129
|
-
const reader = response.body.getReader(),
|
|
130
|
-
chunks = [];
|
|
131
|
-
let size = 0;
|
|
132
|
-
try {
|
|
133
|
-
for (;;) {
|
|
134
|
-
const { done, value } = await reader.read();
|
|
135
|
-
if (done) break;
|
|
136
|
-
size += value.byteLength;
|
|
137
|
-
requireBudget(
|
|
138
|
-
size <= maximumBytes,
|
|
139
|
-
"Provider response exceeded bounded parser",
|
|
140
|
-
);
|
|
141
|
-
chunks.push(Buffer.from(value));
|
|
142
|
-
}
|
|
143
|
-
} catch (error) {
|
|
144
|
-
try {
|
|
145
|
-
await reader.cancel();
|
|
146
|
-
} catch {
|
|
147
|
-
/* keep original unknown outcome */
|
|
148
|
-
}
|
|
149
|
-
throw error;
|
|
150
|
-
} finally {
|
|
151
|
-
reader.releaseLock();
|
|
152
|
-
}
|
|
153
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(
|
|
154
|
-
Buffer.concat(chunks),
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
130
|
function streamReceipt(text) {
|
|
158
131
|
let terminal = false,
|
|
159
132
|
receipt = null,
|
|
@@ -237,10 +210,32 @@ function streamReceipt(text) {
|
|
|
237
210
|
return { ...receipt, usage };
|
|
238
211
|
}
|
|
239
212
|
|
|
213
|
+
function providerReceipt(text, streaming) {
|
|
214
|
+
if (streaming) return streamReceipt(text);
|
|
215
|
+
const receipt = parseBudgetJson(text);
|
|
216
|
+
requireBudget(
|
|
217
|
+
receipt && !Object.hasOwn(receipt, "error"),
|
|
218
|
+
"Provider error envelope cannot settle",
|
|
219
|
+
);
|
|
220
|
+
requireBudget(
|
|
221
|
+
Array.isArray(receipt.choices) &&
|
|
222
|
+
receipt.choices.length === 1 &&
|
|
223
|
+
receipt.choices[0].index === 0 &&
|
|
224
|
+
["stop", "length", "tool_calls"].includes(
|
|
225
|
+
receipt.choices[0].finish_reason,
|
|
226
|
+
),
|
|
227
|
+
"Incomplete or multiple completion response",
|
|
228
|
+
);
|
|
229
|
+
return receipt;
|
|
230
|
+
}
|
|
231
|
+
|
|
240
232
|
/**
|
|
241
233
|
* Trusted-host JSON/SSE transport: credential stays in this closure, and every
|
|
242
234
|
* physical request consumes a durable dispatch exactly once. No auto retries.
|
|
243
|
-
*
|
|
235
|
+
* createFetch is for an explicitly scoped, in-process host. It delivers chunks
|
|
236
|
+
* as they arrive, but EOF is observable only after durable settlement. Missing
|
|
237
|
+
* usage, cancellation and abandoned bodies retain the full reservation.
|
|
238
|
+
* Neither API is auto-enabled or an OS/descendant isolation boundary.
|
|
244
239
|
*/
|
|
245
240
|
export function createFormalBudgetTransport({
|
|
246
241
|
authority,
|
|
@@ -265,81 +260,221 @@ export function createFormalBudgetTransport({
|
|
|
265
260
|
Number.isSafeInteger(timeoutMs) && timeoutMs > 0 && timeoutMs <= 300_000,
|
|
266
261
|
"Invalid timeout",
|
|
267
262
|
);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
body: JSON.stringify(request),
|
|
296
|
-
signal: signal ? AbortSignal.any([signal, deadline]) : deadline,
|
|
297
|
-
};
|
|
298
|
-
// Final expiry/request check is AFTER every awaited preparation. There
|
|
299
|
-
// must be no await between this durable admission and physical fetch.
|
|
300
|
-
const contract = authority.markDispatched(callId, request);
|
|
301
|
-
const response = await fetchImpl(contract.endpoint, options);
|
|
302
|
-
requireBudget(response.ok, "Provider HTTP response not successful");
|
|
303
|
-
const text = await boundedText(response, maximumResponseBytes);
|
|
304
|
-
let receipt;
|
|
305
|
-
if (request.stream === true) {
|
|
306
|
-
requireBudget(
|
|
307
|
-
response.headers.get("content-type")?.includes("text/event-stream"),
|
|
308
|
-
"Unexpected stream content type",
|
|
309
|
-
);
|
|
310
|
-
receipt = streamReceipt(text);
|
|
311
|
-
} else {
|
|
312
|
-
receipt = parseBudgetJson(text);
|
|
313
|
-
requireBudget(
|
|
314
|
-
receipt && !Object.hasOwn(receipt, "error"),
|
|
315
|
-
"Provider error envelope cannot settle",
|
|
316
|
-
);
|
|
317
|
-
requireBudget(
|
|
318
|
-
Array.isArray(receipt.choices) &&
|
|
319
|
-
receipt.choices.length === 1 &&
|
|
320
|
-
receipt.choices[0].index === 0 &&
|
|
321
|
-
["stop", "length", "tool_calls"].includes(
|
|
322
|
-
receipt.choices[0].finish_reason,
|
|
323
|
-
),
|
|
324
|
-
"Incomplete or multiple completion response",
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
const cost = authority.settle(callId, receipt);
|
|
328
|
-
settled = true;
|
|
329
|
-
return { body: text, streaming: request.stream === true, cost };
|
|
330
|
-
} catch {
|
|
331
|
-
if (!settled) {
|
|
332
|
-
// Do not persist provider errors which may contain credentials/content.
|
|
263
|
+
async function open({ runId, callId, body, endpoint, signal, traceId }) {
|
|
264
|
+
signal?.throwIfAborted();
|
|
265
|
+
// Snapshot before await so caller mutation cannot alter the reserved body.
|
|
266
|
+
const request = JSON.parse(canonical(body));
|
|
267
|
+
authority.reserve({ runId, callId, body: request, endpoint });
|
|
268
|
+
const controller = new AbortController();
|
|
269
|
+
const combined = AbortSignal.any([
|
|
270
|
+
controller.signal,
|
|
271
|
+
AbortSignal.timeout(timeoutMs),
|
|
272
|
+
...(signal ? [signal] : []),
|
|
273
|
+
]);
|
|
274
|
+
let finished = false,
|
|
275
|
+
reader,
|
|
276
|
+
responseBody,
|
|
277
|
+
output,
|
|
278
|
+
cost;
|
|
279
|
+
const cleanup = () => combined.removeEventListener("abort", onAbort);
|
|
280
|
+
const unknown = () => {
|
|
281
|
+
const error = budgetError(
|
|
282
|
+
"CC_BUDGET_TRANSPORT_UNKNOWN",
|
|
283
|
+
"Provider outcome unknown; reservation retained and further dispatch blocked",
|
|
284
|
+
);
|
|
285
|
+
if (!finished) {
|
|
286
|
+
finished = true;
|
|
287
|
+
cleanup();
|
|
288
|
+
// Never persist provider errors, content or credentials.
|
|
289
|
+
try {
|
|
333
290
|
authority.markUnknown(
|
|
334
291
|
callId,
|
|
335
292
|
"transport or settlement outcome unknown",
|
|
336
293
|
);
|
|
294
|
+
} catch {
|
|
295
|
+
// The durable held/dispatched row still blocks another reservation.
|
|
296
|
+
// An abort event must not throw an uncaught storage error.
|
|
297
|
+
} finally {
|
|
298
|
+
output?.error(error);
|
|
299
|
+
controller.abort();
|
|
300
|
+
// Do not let a peer that ignores cancellation block local shutdown.
|
|
301
|
+
if (reader)
|
|
302
|
+
void reader
|
|
303
|
+
.cancel()
|
|
304
|
+
.catch(() => {})
|
|
305
|
+
.finally(() => {
|
|
306
|
+
try {
|
|
307
|
+
reader.releaseLock();
|
|
308
|
+
} catch {
|
|
309
|
+
/* another read is unwinding */
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
else if (responseBody) void responseBody.cancel().catch(() => {});
|
|
337
313
|
}
|
|
338
|
-
throw budgetError(
|
|
339
|
-
"CC_BUDGET_TRANSPORT_UNKNOWN",
|
|
340
|
-
"Provider outcome unknown; reservation retained and further dispatch blocked",
|
|
341
|
-
);
|
|
342
314
|
}
|
|
315
|
+
return error;
|
|
316
|
+
};
|
|
317
|
+
function onAbort() {
|
|
318
|
+
unknown();
|
|
319
|
+
}
|
|
320
|
+
combined.addEventListener("abort", onAbort, { once: true });
|
|
321
|
+
try {
|
|
322
|
+
const reserved = authority.reservedContract(callId);
|
|
323
|
+
const credential = await raceWithAbort(
|
|
324
|
+
Promise.resolve().then(() =>
|
|
325
|
+
credentialProvider({
|
|
326
|
+
contractId: reserved.id,
|
|
327
|
+
provider: reserved.provider,
|
|
328
|
+
endpoint: reserved.endpoint,
|
|
329
|
+
}),
|
|
330
|
+
),
|
|
331
|
+
combined,
|
|
332
|
+
);
|
|
333
|
+
combined.throwIfAborted();
|
|
334
|
+
requireBudget(
|
|
335
|
+
typeof credential === "string" &&
|
|
336
|
+
credential.length > 0 &&
|
|
337
|
+
!/[\r\n]/u.test(credential),
|
|
338
|
+
"Credential unavailable",
|
|
339
|
+
);
|
|
340
|
+
const options = {
|
|
341
|
+
method: "POST",
|
|
342
|
+
redirect: "error",
|
|
343
|
+
headers: {
|
|
344
|
+
"Content-Type": "application/json",
|
|
345
|
+
Authorization: `Bearer ${credential}`,
|
|
346
|
+
...(traceId ? { "X-Client-Request-Id": traceId } : {}),
|
|
347
|
+
},
|
|
348
|
+
body: JSON.stringify(request),
|
|
349
|
+
signal: combined,
|
|
350
|
+
};
|
|
351
|
+
// No await between durable admission and physical fetch.
|
|
352
|
+
const contract = authority.markDispatched(callId, request);
|
|
353
|
+
const pendingResponse = Promise.resolve(
|
|
354
|
+
fetchImpl(contract.endpoint, options),
|
|
355
|
+
);
|
|
356
|
+
// Dispose a late response even when the peer ignores AbortSignal.
|
|
357
|
+
void pendingResponse
|
|
358
|
+
.then((response) => {
|
|
359
|
+
responseBody = response.body;
|
|
360
|
+
if (finished && responseBody)
|
|
361
|
+
void responseBody.cancel().catch(() => {});
|
|
362
|
+
})
|
|
363
|
+
.catch(() => {});
|
|
364
|
+
const response = await raceWithAbort(pendingResponse, combined);
|
|
365
|
+
combined.throwIfAborted();
|
|
366
|
+
requireBudget(response.ok, "Provider HTTP response not successful");
|
|
367
|
+
requireBudget(response.body?.getReader, "Response body unavailable");
|
|
368
|
+
if (request.stream === true)
|
|
369
|
+
requireBudget(
|
|
370
|
+
response.headers.get("content-type")?.includes("text/event-stream"),
|
|
371
|
+
"Unexpected stream content type",
|
|
372
|
+
);
|
|
373
|
+
reader = response.body.getReader();
|
|
374
|
+
const chunks = [];
|
|
375
|
+
let size = 0;
|
|
376
|
+
const stream = new ReadableStream(
|
|
377
|
+
{
|
|
378
|
+
start(streamController) {
|
|
379
|
+
output = streamController;
|
|
380
|
+
},
|
|
381
|
+
async pull(streamController) {
|
|
382
|
+
try {
|
|
383
|
+
combined.throwIfAborted();
|
|
384
|
+
const { done, value } = await reader.read();
|
|
385
|
+
combined.throwIfAborted();
|
|
386
|
+
if (done) {
|
|
387
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
388
|
+
Buffer.concat(chunks, size),
|
|
389
|
+
);
|
|
390
|
+
cost = authority.settle(
|
|
391
|
+
callId,
|
|
392
|
+
providerReceipt(text, request.stream === true),
|
|
393
|
+
);
|
|
394
|
+
finished = true;
|
|
395
|
+
cleanup();
|
|
396
|
+
reader.releaseLock();
|
|
397
|
+
streamController.close();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
size += value.byteLength;
|
|
401
|
+
requireBudget(
|
|
402
|
+
size <= maximumResponseBytes,
|
|
403
|
+
"Provider response exceeded bounded parser",
|
|
404
|
+
);
|
|
405
|
+
const chunk = Buffer.from(value);
|
|
406
|
+
chunks.push(chunk);
|
|
407
|
+
// Keep a private accounting snapshot; consumers may mutate bytes.
|
|
408
|
+
streamController.enqueue(new Uint8Array(chunk));
|
|
409
|
+
} catch {
|
|
410
|
+
unknown();
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
cancel() {
|
|
414
|
+
unknown();
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
{ highWaterMark: 0 },
|
|
418
|
+
);
|
|
419
|
+
return {
|
|
420
|
+
response: new Response(stream, {
|
|
421
|
+
status: response.status,
|
|
422
|
+
headers: response.headers,
|
|
423
|
+
}),
|
|
424
|
+
cost: () => cost,
|
|
425
|
+
streaming: request.stream === true,
|
|
426
|
+
};
|
|
427
|
+
} catch {
|
|
428
|
+
throw unknown();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return Object.freeze({
|
|
433
|
+
async dispatch(options) {
|
|
434
|
+
const result = await open(options);
|
|
435
|
+
const body = await result.response.text();
|
|
436
|
+
return {
|
|
437
|
+
body,
|
|
438
|
+
streaming: result.streaming,
|
|
439
|
+
cost: result.cost(),
|
|
440
|
+
};
|
|
441
|
+
},
|
|
442
|
+
createFetch({ runId }) {
|
|
443
|
+
label(runId, "run ID");
|
|
444
|
+
return async (endpoint, init = {}) => {
|
|
445
|
+
requireBudget(
|
|
446
|
+
typeof endpoint === "string" &&
|
|
447
|
+
init.method === "POST" &&
|
|
448
|
+
typeof init.body === "string" &&
|
|
449
|
+
Object.keys(init).every((key) =>
|
|
450
|
+
["method", "headers", "body", "signal", "redirect"].includes(key),
|
|
451
|
+
),
|
|
452
|
+
"Budget fetch requires an explicit JSON POST without transport overrides",
|
|
453
|
+
);
|
|
454
|
+
const headers = new Headers(init.headers);
|
|
455
|
+
requireBudget(
|
|
456
|
+
/^application\/json(?:\s*;\s*charset=utf-8)?$/iu.test(
|
|
457
|
+
headers.get("content-type") || "",
|
|
458
|
+
) &&
|
|
459
|
+
[...headers.keys()].every((key) =>
|
|
460
|
+
["content-type", "authorization", "x-client-request-id"].includes(
|
|
461
|
+
key,
|
|
462
|
+
),
|
|
463
|
+
),
|
|
464
|
+
"Unsupported provider headers in budget scope",
|
|
465
|
+
);
|
|
466
|
+
const traceId = headers.get("x-client-request-id");
|
|
467
|
+
requireBudget(!traceId || traceId.length <= 512, "Invalid trace ID");
|
|
468
|
+
const result = await open({
|
|
469
|
+
runId,
|
|
470
|
+
callId: randomUUID(),
|
|
471
|
+
endpoint,
|
|
472
|
+
body: parseBudgetJson(init.body),
|
|
473
|
+
signal: init.signal,
|
|
474
|
+
traceId,
|
|
475
|
+
});
|
|
476
|
+
return result.response;
|
|
477
|
+
};
|
|
343
478
|
},
|
|
344
479
|
});
|
|
345
480
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* can use a managed credential source that is refreshed before every connect.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { requireUnscopedDescendantControl } from "./formal-budget-provider-scope.js";
|
|
10
|
+
|
|
9
11
|
const MAX_RECONNECT_ATTEMPTS = 8;
|
|
10
12
|
const MAX_IDLE_TIMEOUT_MS = 10 * 60_000;
|
|
11
13
|
const MIN_IDLE_TIMEOUT_MS = 100;
|
|
@@ -424,6 +426,7 @@ function transportDispatcher(value) {
|
|
|
424
426
|
*/
|
|
425
427
|
export class ResilientGatewayClient {
|
|
426
428
|
constructor(options = {}) {
|
|
429
|
+
requireUnscopedDescendantControl();
|
|
427
430
|
this.baseUrl = validateBaseUrl(options.baseUrl);
|
|
428
431
|
if (typeof options.fetch !== "function") {
|
|
429
432
|
throw gatewayError(
|
|
@@ -456,6 +459,7 @@ export class ResilientGatewayClient {
|
|
|
456
459
|
}
|
|
457
460
|
|
|
458
461
|
async _request(path, options = {}) {
|
|
462
|
+
requireUnscopedDescendantControl();
|
|
459
463
|
const relativePath = safeRelativePath(path);
|
|
460
464
|
const prefix = this.baseUrl.pathname.replace(/\/+$/u, "");
|
|
461
465
|
const endpoint = new URL(`${prefix}${relativePath}`, this.baseUrl.origin);
|
|
@@ -520,6 +524,7 @@ export class ResilientGatewayClient {
|
|
|
520
524
|
}
|
|
521
525
|
|
|
522
526
|
async stream(path, options = {}) {
|
|
527
|
+
requireUnscopedDescendantControl();
|
|
523
528
|
const idleTimeoutMs = positiveInteger(
|
|
524
529
|
options.idleTimeoutMs,
|
|
525
530
|
30_000,
|
|
@@ -246,6 +246,16 @@ function whichLimitHit(state, budget, nowMs) {
|
|
|
246
246
|
* the loop stops. Does NOT mutate `state` — returns the next one.
|
|
247
247
|
*/
|
|
248
248
|
export function evaluateGoalStep(state, evaluation, budget, opts = {}) {
|
|
249
|
+
if (state.done === true) {
|
|
250
|
+
return {
|
|
251
|
+
state: { ...state },
|
|
252
|
+
decision:
|
|
253
|
+
state.outcome === "completed"
|
|
254
|
+
? GOAL_DECISION.COMPLETE
|
|
255
|
+
: GOAL_DECISION.EXHAUSTED,
|
|
256
|
+
events: [],
|
|
257
|
+
};
|
|
258
|
+
}
|
|
249
259
|
const nowMs = Number(opts.now ?? state.startedAtMs);
|
|
250
260
|
const next = {
|
|
251
261
|
...state,
|
|
@@ -326,10 +336,23 @@ export class GoalConditionEngine {
|
|
|
326
336
|
|
|
327
337
|
/** Accumulate a turn's usage before evaluating it. */
|
|
328
338
|
recordTurnUsage({ tokens = 0, costUsd = 0 } = {}) {
|
|
339
|
+
if (
|
|
340
|
+
!Number.isSafeInteger(tokens) ||
|
|
341
|
+
tokens < 0 ||
|
|
342
|
+
typeof costUsd !== "number" ||
|
|
343
|
+
!Number.isFinite(costUsd) ||
|
|
344
|
+
costUsd < 0 ||
|
|
345
|
+
!Number.isSafeInteger(this.state.tokens + tokens) ||
|
|
346
|
+
!Number.isFinite(this.state.costUsd + costUsd)
|
|
347
|
+
) {
|
|
348
|
+
throw new RangeError(
|
|
349
|
+
"Goal usage requires finite non-negative cost and safe token counts",
|
|
350
|
+
);
|
|
351
|
+
}
|
|
329
352
|
this.state = {
|
|
330
353
|
...this.state,
|
|
331
|
-
tokens: this.state.tokens +
|
|
332
|
-
costUsd: this.state.costUsd +
|
|
354
|
+
tokens: this.state.tokens + tokens,
|
|
355
|
+
costUsd: this.state.costUsd + costUsd,
|
|
333
356
|
};
|
|
334
357
|
return this.state;
|
|
335
358
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { providerFetch } from "./formal-budget-provider-scope.js";
|
|
1
2
|
/**
|
|
2
3
|
* hub-llm-client — build a hub LLMClient from the user's cc config / env so
|
|
3
4
|
* standalone `cc hub ask` can use a configured CLOUD provider instead of the
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
* stitches system + user-with-facts and hands them off opaque).
|
|
41
42
|
*/
|
|
42
43
|
|
|
43
|
-
"use strict";
|
|
44
|
+
("use strict");
|
|
44
45
|
|
|
45
46
|
import { BUILT_IN_PROVIDERS } from "./llm-providers.js";
|
|
46
47
|
|
|
@@ -202,14 +203,18 @@ export async function openAiCompatChat({
|
|
|
202
203
|
const maxTokens = opts.maxTokens ?? opts.max_tokens;
|
|
203
204
|
if (Number.isFinite(maxTokens)) body.max_tokens = maxTokens;
|
|
204
205
|
|
|
205
|
-
const resp = await
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
const resp = await providerFetch(
|
|
207
|
+
`${trimBase(base)}/chat/completions`,
|
|
208
|
+
{
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: {
|
|
211
|
+
"Content-Type": "application/json",
|
|
212
|
+
Authorization: `Bearer ${key}`,
|
|
213
|
+
},
|
|
214
|
+
body: JSON.stringify(body),
|
|
210
215
|
},
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
fetchImpl,
|
|
217
|
+
);
|
|
213
218
|
if (!resp.ok) {
|
|
214
219
|
const t = await resp.text().catch(() => "");
|
|
215
220
|
throw new Error(
|
|
@@ -266,15 +271,19 @@ export async function anthropicChat({
|
|
|
266
271
|
if (system) body.system = system;
|
|
267
272
|
if (typeof opts.temperature === "number") body.temperature = opts.temperature;
|
|
268
273
|
|
|
269
|
-
const resp = await
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
274
|
+
const resp = await providerFetch(
|
|
275
|
+
`${trimBase(base)}/messages`,
|
|
276
|
+
{
|
|
277
|
+
method: "POST",
|
|
278
|
+
headers: {
|
|
279
|
+
"Content-Type": "application/json",
|
|
280
|
+
"x-api-key": key,
|
|
281
|
+
"anthropic-version": "2023-06-01",
|
|
282
|
+
},
|
|
283
|
+
body: JSON.stringify(body),
|
|
275
284
|
},
|
|
276
|
-
|
|
277
|
-
|
|
285
|
+
fetchImpl,
|
|
286
|
+
);
|
|
278
287
|
if (!resp.ok) {
|
|
279
288
|
const t = await resp.text().catch(() => "");
|
|
280
289
|
throw new Error(
|