chainlesschain 0.166.102 → 0.166.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/assets/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-CyHIMEd8.js → AIOps-Bah3HREy.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-vuNxDpHm.js → ActionButton-DY2aeZMc.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D0quEM0q.js → Analytics-BFly8Xaa.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-xRSVpbCs.js → AppLayout-dp0Zz-JM.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-ktfVAZBC.js → Artifacts-DcIg09ai.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-UJW6maH4.js → Audit-DkN-lsjZ.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BYrfdCoT.js → BackgroundAgents-BjzgusLx.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-IBgsJ7BK.js → Backup-CU8S6qu9.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BmP8_zxy.js → BaseInput-B1pC2gjL.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-urkw8_BF.js → Chat-B6xFp6gD.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-Cw44zZbh.js → ChatBubbleRenderer-DxNCLZ_X.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CYboo-__.js → Checkbox-QHw7Vc4U.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DaqR_Ay8.js → Codegen-gdecsfjg.js} +1 -1
- package/src/assets/web-panel/assets/{Col-04BSIj_z.js → Col-B0_JmAmc.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DxFk7Trv.js → Community-DRCcP5xR.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-mjgWlMCB.js → Compact-DZtoGzhX.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CC5ee7QV.js → Compliance-BfNTvEnT.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BK4BhHZd.js → Cowork-CDXTLm84.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-CP6jAxq0.js → Cron-Big35CbN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DJPaViZS.js → Crosschain-C8ArrAVr.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BTLxkuOj.js → DID-D_77BI2O.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Djkiqh3d.js → Dashboard-D2SxTzEp.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-t_eUhqom.js → Dropdown-mR1-b9ud.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-7w6USx_J.js → EmailListRenderer-DPl7TE1c.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-mU5IHEk5.js → EvolutionSettings-BxRYLRSH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BCh-VBBC.js → FamilyGuardDashboard-rnGtXJ5Z.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Be_VpjI2.js → Federation-BpxenYK8.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-D_lzQ7PI.js → FormItemContext-Cz1kDf6l.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-LYu3KapV.js → GenericCardRenderer-CGV3KN6U.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BBeF-PP4.js → Git-CIRP4UTx.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CoF8Kzg7.js → Governance-DX4r8Vmt.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-C4DLMnXS.js → Inference-DQKk02Hl.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-B2mlNewk.js → KnowledgeGraph-DdD6J-By.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DMZ_44Ya.js → Logs-DA2w-1_m.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Ck7Iv_9e.js → MarkdownRenderer-C9aSGETh.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BqDzTew_.js → Marketplace-BsJ6_6KW.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BbwSbDN9.js → McpTools-_6k30z6u.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-DAaTWgrm.js → Memory-C0_dYY0A.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BjbTGxoR.js → MobileBridge-CCTnVqiC.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DMODOBr-.js → MobileProjects-RZdxDSwC.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DT9N2rCs.js → Mtc-D4Ys8lvX.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-DagMfUVv.js → MtcAudit-BKTC--a2.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-C1BKP6nM.js → Multisig-CGR1qOe1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-B5LFh72i.js → NLProgramming-hKKD4qWb.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Q77V3ltW.js → Notes-pCn7Ec40.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-HxdpNIBs.js → NotificationSettings-B9eEJXEs.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dc0DpvBH.js → OrderTableRenderer-CmJuhaT2.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QL3NZ7uW.js → Organization-CcQbrnwW.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-B_gqOKho.js → Overflow-HmD4Mn72.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BBJubU8Z.js → P2P-B7LIgx5H.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CFnGd-2E.js → PdhVaultBrowser-CXiHnBfX.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BKkznIUM.js → Permissions-CQRI3y_l.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DYK7ZAcd.js → PersonalDataHub-1EeHmsy5.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-CoeKpjoN.js → Pipeline-BFyoIdn8.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Diw4G-92.js → Privacy-BxWthXMF.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CHBOFkma.js → ProjectInit-DfigBilu.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BJIsgdOf.js → ProjectSettings-CFmIcVc-.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CR7H59v3.js +1 -0
- package/src/assets/web-panel/assets/{Providers-o8QCimor.js → Providers-2ulom-76.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-z2W-zvhW.js → QrScannerModal-BwhO07b_.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-_yubhsna.js → QuickAsk-_Wg34OwF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CucgJwih.js → Recommend-D_jq6Svg.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-EzfIIRg1.js → RemoteSession-BlzvEfsa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-_shttE67.js → Reputation-9CTXHfL4.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQA_S8-9.js → Row-DlCYCN-t.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BOqWawXT.js → RssFeed-BvlInFCH.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Dx1v2xOq.js → Search-CfaUzKuL.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bt4HHsT4.js → Security-bAWgLyve.js} +4 -4
- package/src/assets/web-panel/assets/{Services-sruHiQ-r.js → Services-C4LOGACW.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CiBxvmOJ.js → Skeleton-Dd0Uwh3_.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-De9qKODU.js → Skills-ZYF1kYCU.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-B_6yn8Wp.js → Sla-DpsmIaEo.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C2CafExS.js → SpeechSettings-BBXoJ4yc.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Dhh3Qmb0.js → SyncSettings-SOnROi2Y.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Mvo5mTWm.js → Tasks-HFA_ZlDk.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D7WVTQQy.js → Templates-fsK-RCEc.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-7ur9Yfxd.js → Tenant-CuqYxamb.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BgzrOA6k.js → Terminal-GKlI2R5F.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Dco20pT1.js → TimelineRenderer-DMDMHLeY.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-hN6qBPag.js → Tokens-BoV60rh3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-C4f_twAM.js → Trigger-CE344-Ws.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Wf1SMypp.js → Trust-DENwgkVc.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DUXSyg3N.js → UkeySign-FrcM7yfm.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BvqsJaL0.js → VideoEditing-DAY9WaC2.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CgAbcjKl.js → Wallet-BuUR5i82.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DaLT0W3s.js → WebAuthn-nfSQuq85.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-CQqAXoaS.js → WorkflowEditor-DHhPOp9l.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BhlohFTB.js → chat-C4nsLzYy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D7HqXIL8.js → colors-BwXH0Wjn.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Bqk4FCLY.js → compact-item-uApFG30S.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CEmWrWf0.js → createContext-BpyYs4fe.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Bk6HGn3N.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CDY0pFi2.js → hasIn-MXCLi00p.js} +1 -1
- package/src/assets/web-panel/assets/{index-e9oCy7_4.js → index-1LxX2IwJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEzL7s7P.js → index-61CBBrHE.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0nfNmbq.js → index-B0P1Z_oa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiLu-ImR.js → index-B7jojVc7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqK0J4Sj.js → index-BEGMfaOo.js} +1 -1
- package/src/assets/web-panel/assets/{index-aBHlgMrq.js → index-BOhjapd5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPBoWNwq.js → index-BaBwXpAp.js} +1 -1
- package/src/assets/web-panel/assets/index-Bb1LPYdi.js +1 -0
- package/src/assets/web-panel/assets/{index-BxXFRLTZ.js → index-Btv--Nnt.js} +1 -1
- package/src/assets/web-panel/assets/index-C-FsTuFG.js +1 -0
- package/src/assets/web-panel/assets/{index-BxYO6nde.js → index-CF0bUOP4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGKRAO61.js → index-CNjUPfJP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkjgSHVz.js → index-CRmNdEk_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrsM-oiD.js → index-C_oOpXhP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNrvBWDS.js → index-ChUTKrOq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYcjOnDX.js → index-CiIpIYhs.js} +1 -1
- package/src/assets/web-panel/assets/{index-C02yDu4f.js → index-CwVvk2i7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmKdCWq1.js → index-D3_tj06B.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkqBoCiu.js → index-DE4W4vLO.js} +3 -3
- package/src/assets/web-panel/assets/{index-B8L3_-sG.js → index-DLxKx5_J.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dwob5XoD.js → index-DTXi_gQh.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuMt3ewl.js → index-Daco83me.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpI1LqJX.js → index-DffWVpZv.js} +1 -1
- package/src/assets/web-panel/assets/{index-lBAbMOzg.js → index-Dkncxel3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRcDWMwP.js → index-Dn12gJJU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVrwozwH.js → index-Dt-nq2eF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3opQg47.js → index-Du01X9N9.js} +1 -1
- package/src/assets/web-panel/assets/{index-o_lc3eww.js → index-DucLR6YX.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cur88hoN.js → index-Gz2rGWef.js} +1 -1
- package/src/assets/web-panel/assets/{index-XErJQ1yF.js → index-IvgszJ3C.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuKGuH_y.js → index-S80LLjdx.js} +1 -1
- package/src/assets/web-panel/assets/{index-Chxt0t-A.js → index-WKbnwbKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-1HBam9t1.js → index-XHBXtvqp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXRooi-O.js → index-nn22NSDK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHGOdvOQ.js → index-pCIAE2yS.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf1ha8l0.js → index-pRpMLhMt.js} +1 -1
- package/src/assets/web-panel/assets/{index-6f6n0KFR.js → index-pgHGBAoF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPmRkxeX.js → index-ugIUt5TL.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9mvnSii.js → index-xLGf7f9K.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CIeEUxIN.js → initDefaultProps-C58CkKEo.js} +1 -1
- package/src/assets/web-panel/assets/{motion-IPIj1ZtP.js → motion-BLWPO2NV.js} +1 -1
- package/src/assets/web-panel/assets/{move-BfSeXZ-f.js → move-BgrFMwYp.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DX03-Xp4.js → mtc-parser-Cq2AuGzl.js} +1 -1
- package/src/assets/web-panel/assets/{omit-z9EannfX.js → omit-Bb-9L7if.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DXKmRkqZ.js → pickAttrs-vLmOxYr1.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-k2HV7cIU.js → placementArrow-BNySU_dJ.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-bX7CsyWy.js → responsiveObserve-xnbwagnm.js} +1 -1
- package/src/assets/web-panel/assets/{slide-NskjQLbJ.js → slide-BccbY5Cn.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B3IoG-Er.js → statusUtils-DOp9d2-w.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DHv_UilX.js → styleChecker-Bu1w6ho9.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DEqGB5Ng.js → useFlexGapSupport-ZsUOrVdL.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Bl9CbrqZ.js → useFs-C0zpzKtO.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-O-Ukv93M.js → usePersonalDataHub-DO1OdgXK.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hGCHTaZt.js → vnode-jTIRyhR5.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BWhIVeKQ.js → zoom-DKvS7-Qs.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +56 -20
- package/src/commands/ask.js +6 -4
- package/src/commands/batch.js +17 -12
- package/src/commands/eval.js +24 -8
- package/src/commands/team.js +28 -19
- package/src/data/changelog.json +26 -0
- package/src/harness/mcp-client.js +187 -26
- 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/background-session-transport.js +21 -5
- 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/cowork-adapter.js +4 -3
- package/src/lib/decision-layer/providers.js +16 -11
- package/src/lib/decision-layer/runtime.js +180 -10
- package/src/lib/direct-model-usage.js +34 -0
- package/src/lib/evolution/agent-evolution-ingress.js +63 -7
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +10 -0
- package/src/lib/evolution/evolution-evidence-projector.js +46 -7
- 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/evolution/skill-decision-projection.js +211 -0
- package/src/lib/evolution/skill-retrieval-revocation-authority.js +17 -2
- 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/hub-llm-client.js +25 -16
- package/src/lib/llm-connection-probe.js +17 -10
- package/src/lib/llm-providers.js +4 -1
- package/src/lib/model-failure-policy.js +2 -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-loader.js +16 -0
- package/src/lib/stream-retry.js +2 -0
- package/src/lib/sub-agent-context.js +6 -3
- package/src/lib/web-fetch.js +2 -0
- package/src/lib/web-search.js +5 -0
- package/src/repl/agent-repl.js +86 -7
- package/src/runtime/agent-core.js +141 -22
- package/src/runtime/headless-runner.js +212 -22
- package/src/runtime/headless-stream.js +189 -25
- package/src/runtime/mcp-config.js +26 -17
- package/src/runtime/mcp-tool-search.js +44 -14
- package/src/assets/web-panel/assets/Projects-GJjItXIk.js +0 -1
- package/src/assets/web-panel/assets/devWarning-YPX_TU7w.js +0 -1
- package/src/assets/web-panel/assets/index-D53szVZQ.js +0 -1
- package/src/assets/web-panel/assets/index-Ub2nJbV2.js +0 -1
package/src/lib/chat-core.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { providerFetch } from "./formal-budget-provider-scope.js";
|
|
1
2
|
/**
|
|
2
3
|
* Chat Core — transport-independent streaming chat logic
|
|
3
4
|
*
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
waitForStreamRetry,
|
|
22
23
|
} from "./stream-retry.js";
|
|
23
24
|
import { throwIfAborted } from "./abort-utils.js";
|
|
25
|
+
import { isFormalBudgetError } from "./formal-budget-errors.js";
|
|
24
26
|
|
|
25
27
|
// A streaming chat call must not hang forever if the API accepts the connection
|
|
26
28
|
// but then goes silent (TCP up, no bytes). Abort the request if no data arrives
|
|
@@ -132,7 +134,7 @@ export async function streamOllama(
|
|
|
132
134
|
guard.bump();
|
|
133
135
|
let response;
|
|
134
136
|
try {
|
|
135
|
-
response = await
|
|
137
|
+
response = await providerFetch(`${baseUrl}/api/chat`, {
|
|
136
138
|
method: "POST",
|
|
137
139
|
headers: { "Content-Type": "application/json" },
|
|
138
140
|
body: JSON.stringify({
|
|
@@ -146,7 +148,7 @@ export async function streamOllama(
|
|
|
146
148
|
});
|
|
147
149
|
} catch (e) {
|
|
148
150
|
guard.stop();
|
|
149
|
-
throw guard.stalled()
|
|
151
|
+
throw guard.stalled() && !isFormalBudgetError(e)
|
|
150
152
|
? new Error(
|
|
151
153
|
`Ollama request stalled (no response in ${STREAM_STALL_MS / 1000}s)`,
|
|
152
154
|
)
|
|
@@ -213,7 +215,7 @@ export async function streamOllama(
|
|
|
213
215
|
}
|
|
214
216
|
} catch (e) {
|
|
215
217
|
throw attachStreamRetryMetadata(
|
|
216
|
-
guard.stalled()
|
|
218
|
+
guard.stalled() && !isFormalBudgetError(e)
|
|
217
219
|
? new Error(
|
|
218
220
|
`Ollama stream stalled (no data in ${STREAM_STALL_MS / 1000}s)`,
|
|
219
221
|
)
|
|
@@ -247,7 +249,7 @@ export async function streamOpenAI(
|
|
|
247
249
|
guard.bump();
|
|
248
250
|
let response;
|
|
249
251
|
try {
|
|
250
|
-
response = await
|
|
252
|
+
response = await providerFetch(`${baseUrl}/chat/completions`, {
|
|
251
253
|
method: "POST",
|
|
252
254
|
headers: {
|
|
253
255
|
"Content-Type": "application/json",
|
|
@@ -267,7 +269,7 @@ export async function streamOpenAI(
|
|
|
267
269
|
});
|
|
268
270
|
} catch (e) {
|
|
269
271
|
guard.stop();
|
|
270
|
-
throw guard.stalled()
|
|
272
|
+
throw guard.stalled() && !isFormalBudgetError(e)
|
|
271
273
|
? new Error(
|
|
272
274
|
`API request stalled (no response in ${STREAM_STALL_MS / 1000}s)`,
|
|
273
275
|
)
|
|
@@ -344,7 +346,7 @@ export async function streamOpenAI(
|
|
|
344
346
|
}
|
|
345
347
|
} catch (e) {
|
|
346
348
|
throw attachStreamRetryMetadata(
|
|
347
|
-
guard.stalled()
|
|
349
|
+
guard.stalled() && !isFormalBudgetError(e)
|
|
348
350
|
? new Error(
|
|
349
351
|
`API stream stalled (no data in ${STREAM_STALL_MS / 1000}s)`,
|
|
350
352
|
)
|
|
@@ -392,7 +394,7 @@ export async function streamAnthropic(
|
|
|
392
394
|
guard.bump();
|
|
393
395
|
let response;
|
|
394
396
|
try {
|
|
395
|
-
response = await
|
|
397
|
+
response = await providerFetch(`${baseUrl}/messages`, {
|
|
396
398
|
method: "POST",
|
|
397
399
|
headers: {
|
|
398
400
|
"Content-Type": "application/json",
|
|
@@ -412,7 +414,7 @@ export async function streamAnthropic(
|
|
|
412
414
|
});
|
|
413
415
|
} catch (e) {
|
|
414
416
|
guard.stop();
|
|
415
|
-
throw guard.stalled()
|
|
417
|
+
throw guard.stalled() && !isFormalBudgetError(e)
|
|
416
418
|
? new Error(
|
|
417
419
|
`Anthropic request stalled (no response in ${STREAM_STALL_MS / 1000}s)`,
|
|
418
420
|
)
|
|
@@ -486,7 +488,7 @@ export async function streamAnthropic(
|
|
|
486
488
|
}
|
|
487
489
|
} catch (e) {
|
|
488
490
|
throw attachStreamRetryMetadata(
|
|
489
|
-
guard.stalled()
|
|
491
|
+
guard.stalled() && !isFormalBudgetError(e)
|
|
490
492
|
? new Error(
|
|
491
493
|
`Anthropic stream stalled (no data in ${STREAM_STALL_MS / 1000}s)`,
|
|
492
494
|
)
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import { join } from "node:path";
|
|
29
29
|
import { getHomeDir } from "../paths.js";
|
|
30
30
|
import { ResilientGatewayClient } from "../gateway-client-resilience.js";
|
|
31
|
+
import { requireUnscopedDescendantControl } from "../formal-budget-provider-scope.js";
|
|
31
32
|
|
|
32
33
|
export const _deps = { fetch: (...a) => globalThis.fetch(...a) };
|
|
33
34
|
|
|
@@ -78,6 +79,7 @@ export class CloudClient {
|
|
|
78
79
|
* @param {object} opts { baseUrl, token, deps? }
|
|
79
80
|
*/
|
|
80
81
|
constructor(opts = {}) {
|
|
82
|
+
requireUnscopedDescendantControl();
|
|
81
83
|
this.baseUrl = String(opts.baseUrl || "").replace(/\/+$/, "");
|
|
82
84
|
if (!this.baseUrl) {
|
|
83
85
|
throw new Error(
|
|
@@ -109,6 +111,7 @@ export class CloudClient {
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
async _json(method, path, body) {
|
|
114
|
+
requireUnscopedDescendantControl();
|
|
112
115
|
const res = await this.deps.fetch(`${this.baseUrl}${path}`, {
|
|
113
116
|
method,
|
|
114
117
|
headers: this._headers(),
|
|
@@ -121,7 +124,9 @@ export class CloudClient {
|
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
/** Submit a job. `bundle` is a base64 git bundle of the branch. */
|
|
124
|
-
async submit(
|
|
127
|
+
async submit(payload) {
|
|
128
|
+
requireUnscopedDescendantControl();
|
|
129
|
+
const { task, bundle, branch, baseSha } = payload;
|
|
125
130
|
const data = await this._json("POST", "/v1/jobs", {
|
|
126
131
|
task,
|
|
127
132
|
bundle,
|
|
@@ -133,10 +138,12 @@ export class CloudClient {
|
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
async status(jobId) {
|
|
141
|
+
requireUnscopedDescendantControl();
|
|
136
142
|
return this._json("GET", `/v1/jobs/${encodeURIComponent(jobId)}`);
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
async result(jobId) {
|
|
146
|
+
requireUnscopedDescendantControl();
|
|
140
147
|
return this._json("GET", `/v1/jobs/${encodeURIComponent(jobId)}/result`);
|
|
141
148
|
}
|
|
142
149
|
|
|
@@ -146,6 +153,7 @@ export class CloudClient {
|
|
|
146
153
|
* runners remain compatible with status()/result().
|
|
147
154
|
*/
|
|
148
155
|
async events(jobId, options = {}) {
|
|
156
|
+
requireUnscopedDescendantControl();
|
|
149
157
|
if (!/^[\w.-]+$/.test(String(jobId || ""))) {
|
|
150
158
|
throw new Error(`invalid job id: ${jobId}`);
|
|
151
159
|
}
|
|
@@ -175,6 +183,7 @@ export const TERMINAL_CLOUD_STATUS = new Set(["done", "failed"]);
|
|
|
175
183
|
* `sleep`/`now` injected for tests.
|
|
176
184
|
*/
|
|
177
185
|
export async function pollUntilDone(client, jobId, opts = {}) {
|
|
186
|
+
requireUnscopedDescendantControl();
|
|
178
187
|
const intervalMs = opts.intervalMs ?? 3000;
|
|
179
188
|
const timeoutMs = opts.timeoutMs ?? 30 * 60_000;
|
|
180
189
|
const sleep = opts.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Module initialization helper
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { providerFetch } from "./formal-budget-provider-scope.js";
|
|
10
11
|
import { LLMProviderRegistry, BUILT_IN_PROVIDERS } from "./llm-providers.js";
|
|
11
12
|
import { loadConfig } from "./config-manager.js";
|
|
12
13
|
import { applyConfigLlmDefaults } from "./llm-config-defaults.js";
|
|
@@ -174,7 +175,7 @@ export function createChatFn(options = {}) {
|
|
|
174
175
|
currentModel,
|
|
175
176
|
async ({ signal = opts.signal || resolved.signal } = {}) => {
|
|
176
177
|
const modelMessages = await prepareMessages();
|
|
177
|
-
const res = await
|
|
178
|
+
const res = await providerFetch(`${baseUrl}/api/chat`, {
|
|
178
179
|
method: "POST",
|
|
179
180
|
signal: requestSignal(signal),
|
|
180
181
|
headers: { "Content-Type": "application/json" },
|
|
@@ -214,7 +215,7 @@ export function createChatFn(options = {}) {
|
|
|
214
215
|
? { system: systemMsgs.map((m) => m.content).join("\n") }
|
|
215
216
|
: {}),
|
|
216
217
|
};
|
|
217
|
-
const res = await
|
|
218
|
+
const res = await providerFetch(`${baseUrl}/messages`, {
|
|
218
219
|
method: "POST",
|
|
219
220
|
signal: requestSignal(signal),
|
|
220
221
|
headers: {
|
|
@@ -245,7 +246,7 @@ export function createChatFn(options = {}) {
|
|
|
245
246
|
currentModel,
|
|
246
247
|
async ({ signal = opts.signal || resolved.signal } = {}) => {
|
|
247
248
|
const modelMessages = await prepareMessages();
|
|
248
|
-
const res = await
|
|
249
|
+
const res = await providerFetch(`${baseUrl}/chat/completions`, {
|
|
249
250
|
method: "POST",
|
|
250
251
|
signal: requestSignal(signal),
|
|
251
252
|
headers: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { openDecisionProviderAuthority } from "./provider-authority.js";
|
|
2
|
+
import { providerFetch } from "../formal-budget-provider-scope.js";
|
|
2
3
|
|
|
3
4
|
const PROVIDERS = Object.freeze({
|
|
4
5
|
typesafe: {
|
|
@@ -195,18 +196,22 @@ export function createDecisionProvider({
|
|
|
195
196
|
if (Buffer.byteLength(body, "utf8") > MAX_DECISION_REQUEST_BYTES) {
|
|
196
197
|
throw bodyTooLarge("request", MAX_DECISION_REQUEST_BYTES);
|
|
197
198
|
}
|
|
198
|
-
const response = await
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
const response = await providerFetch(
|
|
200
|
+
endpoint,
|
|
201
|
+
{
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: {
|
|
204
|
+
...(secret === undefined
|
|
205
|
+
? {}
|
|
206
|
+
: { authorization: `Bearer ${secret}` }),
|
|
207
|
+
"content-type": "application/json",
|
|
208
|
+
},
|
|
209
|
+
body,
|
|
210
|
+
signal,
|
|
211
|
+
redirect: "error",
|
|
205
212
|
},
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
redirect: "error",
|
|
209
|
-
});
|
|
213
|
+
fetchImpl,
|
|
214
|
+
);
|
|
210
215
|
if (!response?.ok) {
|
|
211
216
|
throw providerError(response?.status ?? 0, options.provider);
|
|
212
217
|
}
|
|
@@ -12,12 +12,17 @@ import {
|
|
|
12
12
|
SKILL_DECISION_OBSERVATION_SCHEMA,
|
|
13
13
|
} from "./contracts.js";
|
|
14
14
|
import { captureDecisionProviderAuthority } from "./provider-authority.js";
|
|
15
|
+
import {
|
|
16
|
+
AGENT_EVOLUTION_INGRESS_FAILED_CODE,
|
|
17
|
+
captureAgentEvolutionIngress,
|
|
18
|
+
} from "../evolution/agent-evolution-ingress.js";
|
|
15
19
|
|
|
16
20
|
export const SKILL_DECISION_RUNTIME_SCHEMA =
|
|
17
21
|
"chainlesschain.skill-decision-runtime/v1";
|
|
18
22
|
|
|
19
23
|
const RUNTIMES = new WeakSet();
|
|
20
24
|
const DEFAULT_TIMEOUT_MS = 800;
|
|
25
|
+
const DEFAULT_BOUNDARY_TIMEOUT_MS = 30_000;
|
|
21
26
|
|
|
22
27
|
function requiredText(value, label, maximum = 256) {
|
|
23
28
|
if (
|
|
@@ -36,6 +41,53 @@ function timeoutSignal(signal, timeoutMs) {
|
|
|
36
41
|
return signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
function settleBeforeAbort(value, signal) {
|
|
45
|
+
const pending = Promise.resolve(value);
|
|
46
|
+
if (!signal) return pending;
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
let settled = false;
|
|
49
|
+
const finish = (settle, result) => {
|
|
50
|
+
if (settled) return;
|
|
51
|
+
settled = true;
|
|
52
|
+
signal.removeEventListener("abort", onAbort);
|
|
53
|
+
settle(result);
|
|
54
|
+
};
|
|
55
|
+
const onAbort = () => finish(reject, signal.reason);
|
|
56
|
+
pending.then(
|
|
57
|
+
(result) => finish(resolve, result),
|
|
58
|
+
(error) => finish(reject, error),
|
|
59
|
+
);
|
|
60
|
+
if (signal.aborted) {
|
|
61
|
+
onAbort();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function waitForGovernedPhase(value, signal, timeoutMs, onTimeout) {
|
|
69
|
+
const phaseSignal = timeoutSignal(signal, timeoutMs);
|
|
70
|
+
try {
|
|
71
|
+
return await settleBeforeAbort(value, phaseSignal);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (signal?.aborted) throw signal.reason;
|
|
74
|
+
if (
|
|
75
|
+
phaseSignal.aborted &&
|
|
76
|
+
phaseSignal.reason === error &&
|
|
77
|
+
error?.name === "TimeoutError"
|
|
78
|
+
) {
|
|
79
|
+
throw onTimeout(error);
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function admissionTimeoutError(cause) {
|
|
86
|
+
const error = new Error("Skill decision admission timed out", { cause });
|
|
87
|
+
error.code = AGENT_EVOLUTION_INGRESS_FAILED_CODE;
|
|
88
|
+
return error;
|
|
89
|
+
}
|
|
90
|
+
|
|
39
91
|
function projectUnavailable(error) {
|
|
40
92
|
if (error?.name === "TimeoutError") return "provider-timeout";
|
|
41
93
|
if (error?.code === "CC_DECISION_USAGE_UNKNOWN_BLOCKED") {
|
|
@@ -66,7 +118,7 @@ function asPersistenceError(error) {
|
|
|
66
118
|
return markRuntimeLedgerPersistenceError(error);
|
|
67
119
|
} catch {
|
|
68
120
|
return markRuntimeLedgerPersistenceError(
|
|
69
|
-
new Error("decision
|
|
121
|
+
new Error("decision persistence failed", { cause: error }),
|
|
70
122
|
);
|
|
71
123
|
}
|
|
72
124
|
}
|
|
@@ -76,12 +128,14 @@ export function createSkillDecisionRuntime({
|
|
|
76
128
|
tenantId = null,
|
|
77
129
|
sessionId = null,
|
|
78
130
|
provider = null,
|
|
131
|
+
evolutionIngress = null,
|
|
79
132
|
persist = null,
|
|
80
133
|
observe = null,
|
|
81
134
|
sessionBudget = null,
|
|
82
135
|
initialUsageUnknown = false,
|
|
83
136
|
thresholds = {},
|
|
84
137
|
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
138
|
+
boundaryTimeoutMs = DEFAULT_BOUNDARY_TIMEOUT_MS,
|
|
85
139
|
idGenerator = () => randomUUID(),
|
|
86
140
|
now = () => new Date(),
|
|
87
141
|
} = {}) {
|
|
@@ -95,17 +149,36 @@ export function createSkillDecisionRuntime({
|
|
|
95
149
|
"decision timeout must be an integer from 50 to 30000 ms",
|
|
96
150
|
);
|
|
97
151
|
}
|
|
152
|
+
if (
|
|
153
|
+
!Number.isSafeInteger(boundaryTimeoutMs) ||
|
|
154
|
+
boundaryTimeoutMs < 50 ||
|
|
155
|
+
boundaryTimeoutMs > 30_000
|
|
156
|
+
) {
|
|
157
|
+
throw new TypeError(
|
|
158
|
+
"decision boundary timeout must be an integer from 50 to 30000 ms",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
98
161
|
if (typeof idGenerator !== "function" || typeof now !== "function") {
|
|
99
162
|
throw new TypeError("decision runtime generators must be functions");
|
|
100
163
|
}
|
|
101
164
|
let providerAuthority = null;
|
|
102
165
|
let boundTenantId = null;
|
|
103
166
|
let boundSessionId = null;
|
|
167
|
+
let ingress = null;
|
|
104
168
|
let unknownUsageSeen = initialUsageUnknown === true;
|
|
169
|
+
const unsettledUsageCalls = new Set();
|
|
170
|
+
let usageCallInFlight = false;
|
|
171
|
+
let usagePersistenceFailure = null;
|
|
172
|
+
let observationPersistenceFailure = null;
|
|
105
173
|
if (normalizedMode !== "off") {
|
|
106
174
|
providerAuthority = captureDecisionProviderAuthority(provider);
|
|
107
175
|
boundTenantId = requiredText(tenantId, "tenantId");
|
|
108
176
|
boundSessionId = requiredText(sessionId, "sessionId");
|
|
177
|
+
if (evolutionIngress !== null) {
|
|
178
|
+
ingress = captureAgentEvolutionIngress(evolutionIngress, {
|
|
179
|
+
tenantId: boundTenantId,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
109
182
|
if (typeof persist !== "function" || typeof observe !== "function") {
|
|
110
183
|
throw new TypeError(
|
|
111
184
|
"enabled decision runtime requires usage and observation persistence",
|
|
@@ -118,13 +191,33 @@ export function createSkillDecisionRuntime({
|
|
|
118
191
|
try {
|
|
119
192
|
await observe(observation);
|
|
120
193
|
} catch (error) {
|
|
121
|
-
|
|
194
|
+
observationPersistenceFailure ??= asPersistenceError(error);
|
|
195
|
+
throw observationPersistenceFailure;
|
|
122
196
|
}
|
|
123
197
|
};
|
|
124
198
|
|
|
125
199
|
const persistDecisionUsage = async (type, event) => {
|
|
126
|
-
|
|
127
|
-
|
|
200
|
+
if (usagePersistenceFailure !== null) throw usagePersistenceFailure;
|
|
201
|
+
try {
|
|
202
|
+
await waitForGovernedPhase(
|
|
203
|
+
persist(type, event),
|
|
204
|
+
null,
|
|
205
|
+
boundaryTimeoutMs,
|
|
206
|
+
(cause) =>
|
|
207
|
+
new Error("Skill decision usage persistence timed out", { cause }),
|
|
208
|
+
);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
usagePersistenceFailure ??= asPersistenceError(error);
|
|
211
|
+
throw usagePersistenceFailure;
|
|
212
|
+
}
|
|
213
|
+
if (type === "model_usage_started") {
|
|
214
|
+
unsettledUsageCalls.add(event.callId);
|
|
215
|
+
} else if (type === "model_usage_unknown") {
|
|
216
|
+
unknownUsageSeen = true;
|
|
217
|
+
unsettledUsageCalls.delete(event.callId);
|
|
218
|
+
} else if (type === "token_usage") {
|
|
219
|
+
unsettledUsageCalls.delete(event.callId);
|
|
220
|
+
}
|
|
128
221
|
};
|
|
129
222
|
|
|
130
223
|
const runtime = Object.freeze({
|
|
@@ -148,6 +241,10 @@ export function createSkillDecisionRuntime({
|
|
|
148
241
|
});
|
|
149
242
|
}
|
|
150
243
|
if (signal?.aborted) throw signal.reason;
|
|
244
|
+
if (usagePersistenceFailure !== null) throw usagePersistenceFailure;
|
|
245
|
+
if (observationPersistenceFailure !== null) {
|
|
246
|
+
throw observationPersistenceFailure;
|
|
247
|
+
}
|
|
151
248
|
const decisionId = `skill-${requiredText(idGenerator(), "decisionId")}`;
|
|
152
249
|
const request = createSkillDecisionRequest({
|
|
153
250
|
decisionId,
|
|
@@ -167,11 +264,41 @@ export function createSkillDecisionRuntime({
|
|
|
167
264
|
}
|
|
168
265
|
let outcome;
|
|
169
266
|
let requestSignal = null;
|
|
170
|
-
|
|
267
|
+
let admissionReceipt = null;
|
|
268
|
+
if (unknownUsageSeen || unsettledUsageCalls.size > 0) {
|
|
171
269
|
outcome = unavailableOutcome("provider-usage-unknown-blocked");
|
|
172
270
|
} else {
|
|
173
271
|
try {
|
|
174
|
-
|
|
272
|
+
// Admission is not provider work: a refusal must not create an
|
|
273
|
+
// unknown-charge ledger row or consume the provider's budget turn.
|
|
274
|
+
const admission =
|
|
275
|
+
ingress === null
|
|
276
|
+
? null
|
|
277
|
+
: await waitForGovernedPhase(
|
|
278
|
+
ingress.prepareSkillDecisionRequest(request),
|
|
279
|
+
signal,
|
|
280
|
+
boundaryTimeoutMs,
|
|
281
|
+
admissionTimeoutError,
|
|
282
|
+
);
|
|
283
|
+
signal?.throwIfAborted();
|
|
284
|
+
admissionReceipt = admission?.receipt ?? null;
|
|
285
|
+
// Keep local content identities out of the provider capability. Only
|
|
286
|
+
// the authenticated projection is available for governed dispatch.
|
|
287
|
+
const providerRequest =
|
|
288
|
+
admission === null
|
|
289
|
+
? request
|
|
290
|
+
: Object.freeze({ payload: admission.payload });
|
|
291
|
+
if (
|
|
292
|
+
unknownUsageSeen ||
|
|
293
|
+
unsettledUsageCalls.size > 0 ||
|
|
294
|
+
usageCallInFlight
|
|
295
|
+
) {
|
|
296
|
+
const error = new Error("decision usage recovery is required");
|
|
297
|
+
error.code = "CC_DECISION_USAGE_UNKNOWN_BLOCKED";
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
usageCallInFlight = true;
|
|
301
|
+
const meteredCall = runMeteredDirectModelCall({
|
|
175
302
|
sessionId: boundSessionId,
|
|
176
303
|
persist: persistDecisionUsage,
|
|
177
304
|
provider: providerAuthority.provider,
|
|
@@ -180,18 +307,36 @@ export function createSkillDecisionRuntime({
|
|
|
180
307
|
operationId: `decision:${decisionId}`,
|
|
181
308
|
sessionBudget,
|
|
182
309
|
includeSettlement: true,
|
|
310
|
+
dispatchSignal: signal ?? null,
|
|
183
311
|
call: () => {
|
|
184
312
|
if (unknownUsageSeen) {
|
|
185
313
|
const error = new Error("decision usage recovery is required");
|
|
186
314
|
error.code = "CC_DECISION_USAGE_UNKNOWN_BLOCKED";
|
|
187
315
|
throw error;
|
|
188
316
|
}
|
|
317
|
+
// timeoutMs remains the provider-call deadline. Admission uses
|
|
318
|
+
// its own authority lifecycle and checks caller cancellation.
|
|
189
319
|
requestSignal = timeoutSignal(signal, timeoutMs);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
320
|
+
requestSignal.throwIfAborted();
|
|
321
|
+
return settleBeforeAbort(
|
|
322
|
+
providerAuthority.decide(providerRequest, {
|
|
323
|
+
signal: requestSignal,
|
|
324
|
+
}),
|
|
325
|
+
requestSignal,
|
|
326
|
+
);
|
|
193
327
|
},
|
|
194
328
|
});
|
|
329
|
+
let meteredResult;
|
|
330
|
+
try {
|
|
331
|
+
meteredResult = await meteredCall;
|
|
332
|
+
} finally {
|
|
333
|
+
usageCallInFlight = false;
|
|
334
|
+
}
|
|
335
|
+
const { result: raw, settlement } = meteredResult;
|
|
336
|
+
// A provider may resolve successfully after ignoring cancellation or
|
|
337
|
+
// its deadline. Usage is settled above; the late answer is unusable.
|
|
338
|
+
signal?.throwIfAborted();
|
|
339
|
+
requestSignal?.throwIfAborted();
|
|
195
340
|
if (settlement !== "known") {
|
|
196
341
|
// The metered call already persisted model_usage_unknown. Without a
|
|
197
342
|
// budget root it may return the answer; never expose that answer.
|
|
@@ -254,6 +399,7 @@ export function createSkillDecisionRuntime({
|
|
|
254
399
|
reasonCode: outcome.reasonCode,
|
|
255
400
|
selectedDigest: outcome.selectedDigest,
|
|
256
401
|
resultDigest: outcome.resultDigest,
|
|
402
|
+
...(admissionReceipt === null ? {} : { admissionReceipt }),
|
|
257
403
|
startedAt: startedAt.toISOString(),
|
|
258
404
|
finishedAt: finishedAt.toISOString(),
|
|
259
405
|
latencyMs: Math.max(0, finishedAt.getTime() - startedAt.getTime()),
|
|
@@ -265,7 +411,31 @@ export function createSkillDecisionRuntime({
|
|
|
265
411
|
observationCore,
|
|
266
412
|
),
|
|
267
413
|
});
|
|
268
|
-
|
|
414
|
+
try {
|
|
415
|
+
await waitForGovernedPhase(
|
|
416
|
+
writeObservation(observation),
|
|
417
|
+
signal,
|
|
418
|
+
boundaryTimeoutMs,
|
|
419
|
+
(cause) =>
|
|
420
|
+
asPersistenceError(
|
|
421
|
+
new Error("Skill decision observation timed out", { cause }),
|
|
422
|
+
),
|
|
423
|
+
);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (error?.runtimeLedgerPersistence === true) {
|
|
426
|
+
observationPersistenceFailure ??= error;
|
|
427
|
+
} else if (signal?.aborted) {
|
|
428
|
+
// The caller stops waiting before the durable observer acknowledges
|
|
429
|
+
// the write. Its eventual outcome is unknown to this runtime.
|
|
430
|
+
observationPersistenceFailure ??= asPersistenceError(
|
|
431
|
+
new Error("Skill decision observation completion is unknown", {
|
|
432
|
+
cause: error,
|
|
433
|
+
}),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
signal?.throwIfAborted();
|
|
269
439
|
return Object.freeze({
|
|
270
440
|
schema: SKILL_DECISION_OBSERVATION_SCHEMA,
|
|
271
441
|
decisionId,
|
|
@@ -54,6 +54,8 @@ function unknownUsageEvent({
|
|
|
54
54
|
* Meter one direct provider call that runs outside agentLoop. The started row is
|
|
55
55
|
* durable before the callback resumes into provider work; every outcome then
|
|
56
56
|
* settles known or unknown without persisting prompts, responses, or errors.
|
|
57
|
+
* A dispatch signal closes an acknowledged, canceled pre-dispatch intent with
|
|
58
|
+
* locally proven zero usage; cancellation after dispatch remains unknown.
|
|
57
59
|
* Opt-in includeSettlement returns that persisted status with the raw result.
|
|
58
60
|
*/
|
|
59
61
|
export async function runMeteredDirectModelCall({
|
|
@@ -65,11 +67,20 @@ export async function runMeteredDirectModelCall({
|
|
|
65
67
|
operationId = undefined,
|
|
66
68
|
sessionBudget = null,
|
|
67
69
|
includeSettlement = false,
|
|
70
|
+
dispatchSignal = null,
|
|
68
71
|
call,
|
|
69
72
|
}) {
|
|
70
73
|
if (typeof call !== "function") {
|
|
71
74
|
throw new TypeError("metered direct model call requires a callback");
|
|
72
75
|
}
|
|
76
|
+
if (
|
|
77
|
+
dispatchSignal !== null &&
|
|
78
|
+
(typeof dispatchSignal.aborted !== "boolean" ||
|
|
79
|
+
typeof dispatchSignal.throwIfAborted !== "function")
|
|
80
|
+
) {
|
|
81
|
+
throw new TypeError("metered direct model dispatch signal is invalid");
|
|
82
|
+
}
|
|
83
|
+
dispatchSignal?.throwIfAborted();
|
|
73
84
|
const callId = `direct-${randomUUID()}`;
|
|
74
85
|
if (typeof sessionBudget?.consumeTurn === "function") {
|
|
75
86
|
const admission = sessionBudget.consumeTurn({ id: callId });
|
|
@@ -84,6 +95,7 @@ export async function runMeteredDirectModelCall({
|
|
|
84
95
|
if (includeSettlement) {
|
|
85
96
|
throw new TypeError("direct model settlement requires a session");
|
|
86
97
|
}
|
|
98
|
+
dispatchSignal?.throwIfAborted();
|
|
87
99
|
return call();
|
|
88
100
|
}
|
|
89
101
|
if (typeof persist !== "function") {
|
|
@@ -102,6 +114,28 @@ export async function runMeteredDirectModelCall({
|
|
|
102
114
|
{ callId, provider, model, source, operationId },
|
|
103
115
|
`direct model call ${source}`,
|
|
104
116
|
);
|
|
117
|
+
if (dispatchSignal?.aborted) {
|
|
118
|
+
// A durable intent exists, but no provider callback has run. Close it as
|
|
119
|
+
// a locally proven zero-charge call instead of inventing unknown usage.
|
|
120
|
+
const notDispatched = {
|
|
121
|
+
...projectRuntimeTokenUsage({
|
|
122
|
+
callId,
|
|
123
|
+
provider,
|
|
124
|
+
model,
|
|
125
|
+
source,
|
|
126
|
+
operationId,
|
|
127
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
128
|
+
}),
|
|
129
|
+
settlementOrigin: "local-no-dispatch",
|
|
130
|
+
};
|
|
131
|
+
await persistUsageEvent(persist, "token_usage", notDispatched);
|
|
132
|
+
recordSessionBudgetUsage(
|
|
133
|
+
sessionBudget,
|
|
134
|
+
{ callId, usage: null },
|
|
135
|
+
`direct model call ${source} canceled before dispatch`,
|
|
136
|
+
);
|
|
137
|
+
throw dispatchSignal.reason;
|
|
138
|
+
}
|
|
105
139
|
let result;
|
|
106
140
|
let settlement = "unknown";
|
|
107
141
|
const returnSettled = (status) =>
|