chainlesschain 0.163.6 → 0.163.8
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 -1
- package/src/assets/web-panel/assets/{AIOps-DDIUofA8.js → AIOps-CavM7W3k.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Bm-5xYRn.js → ActionButton-CUvZGabQ.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D5UH4ljI.js → Analytics-DeBnWnbe.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D_ifjOOP.js → AppLayout-DbI5kxvd.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-BY1mXdC7.js → Artifacts-D93bigKm.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bujr4Ml3.js → Audit-CBNvfUZa.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-sioKSTcC.js → BackgroundAgents-CJLyh_YU.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DDOAK8RK.js → Backup-D_0yoehD.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CtCsVVke.js → BaseInput-TJxmbObW.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DpNj0vnd.js → Chat-CE0dto1q.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZta3Ftr.js → ChatBubbleRenderer-CZ5-yYiD.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DoHVNSXq.js → Checkbox-Ck1RVIVo.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DlVl7Qmv.js → Codegen-qpoyf6DK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-GIqoPYty.js → Col-DUyPWjIE.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CIAPEVji.js → Community-D_S1BemS.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BN7JiI_h.js → Compact-BjxeK-Gc.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CqUWCM9B.js → Compliance-Dd3CEJsu.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-A4iPp7A4.js → Cowork-B4QG7-ad.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-B2zeXXWb.js → Cron-BtZnids5.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BR2xmWr8.js → Crosschain-CHPfCBFD.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BzFDjrIv.js → DID-DT8DCEOK.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DKtDo3yA.js → Dashboard-COQab5_U.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-RNx43_YG.js → Dropdown-BY1PsQuU.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-SwMt4Rcy.js → EmailListRenderer-CsuAuaq3.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CsYebZ4X.js → FamilyGuardDashboard-BqpinD-j.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BmRKvBTy.js → Federation-D0gkfQFv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CVxGTA-0.js → FormItemContext-BqgE-axK.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-x2iXpyB7.js → GenericCardRenderer-D0hJ2KvC.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DfqbkiOP.js → Git-CmMB0uPY.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-RuHyyj5O.js → Governance-BnVEI0Dk.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Bn7uZ7rI.js → Inference-YTAMihRP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DlP7XefZ.js → KnowledgeGraph-0ax7VHop.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BYNdOiCd.js → Logs-B9TRcokL.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-yySN5YNM.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-JQYmDqM8.js → Marketplace-B6CSuD_H.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Cyx4yZ-9.js → McpTools-CjMXvOVU.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-VDuGZTN2.js → Memory-CXRF71U3.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-_yKo97gi.js → MobileBridge-DQgNp_oS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CXRXW3q7.js → MobileProjects-DlPwcCbq.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-acTGtfLH.js → Mtc-6hDTRPe1.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-ClpjXfkY.js → MtcAudit-BlVHU1DS.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-_6Tn3o96.js → Multisig-DP9pzEbV.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CK7pbZzP.js → NLProgramming-Djorh8EV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Czy6V_P8.js → Notes-oLZ-JkUg.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Cqx-UVv3.js → NotificationSettings-Cg288VH3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C69WDxGy.js → OrderTableRenderer-Bb1O3Ofr.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DFnA9vLQ.js → Organization-CBdo-bHs.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-ReZkk2yT.js → Overflow-qZdnVumx.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DzjSI0aI.js → P2P-D4kNfGNQ.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-DnlMemM7.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-DhQ5klhR.js → Permissions-DHucHFBJ.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-CUL1iHrW.js → PersonalDataHub-BovlOIPR.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DU88iu4N.js → Pipeline-Deru3CQY.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Bw-sYfyK.js → Privacy-Bq5oTTbU.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Bq3Q5y0z.js → ProjectInit-BfzxVWAz.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C8spzpho.js → ProjectSettings-CKC6XBtH.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CMolGhae.js +1 -0
- package/src/assets/web-panel/assets/{Providers-Bf4eTP9W.js → Providers-C_jx4TWX.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Dme9BDU1.js → QrScannerModal-BdIHeVGC.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CIXfv1rE.js → QuickAsk-BwZwAcPD.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CIxvdeZ-.js → Recommend-BNTt0L-m.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C6k7MExI.js → RemoteSession-Biyn2-Ro.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CISDz2bl.js → Reputation-D6VpG32B.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CdnDxXyf.js → Row-D_74OvQQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C-QpRHsl.js → RssFeed-BTr0a-BQ.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DBjaGzSn.js → Search-C3MCFoNU.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CQKukOeE.js → Security-CqW9aJ6s.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DZz86jWb.js → Services-Cw5GNaHF.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-jb8RMK0d.js → Skeleton-DZps5qeY.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CdY76fZG.js → Skills-C6NQ1znT.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C_CSiZX9.js → Sla-DFP8G2Sh.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-ESt6MB6v.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-qvimGO0O.js → SyncSettings-BHnN98aM.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Dng5LPJC.js → Tasks-NiSCaOf-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Bwqq5Wll.js → Templates-DsKFhW2C.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DNWuwP7Z.js → Tenant-CQ0E0ze5.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B-6Othpp.js → Terminal-D_rBu_by.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DKpKZfzx.js → TimelineRenderer-D3Vm4Po4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DzfGMd2J.js → Tokens-DNTxRH6q.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BXwUi74J.js → Trigger-Dgprz1ms.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BNzUq4ui.js → Trust-B2rUnVBD.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BYugxww4.js → UkeySign-DEGQNc6_.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DdauhtSJ.js → VideoEditing-jlsAx3Eh.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CSC9bCe_.js → Wallet-8jQih1ij.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Q8AN3bTO.js → WebAuthn-XLeO6xIB.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-d-LBGCuA.js → WorkflowEditor-De_dgDAG.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BLVDCgsZ.js → chat-CI19R_Sk.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BZlPSEHl.js → colors-DsYTOYN3.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-IWseP6R6.js → compact-item-CM3-t_Qr.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DnVRV3Ao.js → createContext-BjxlBRZv.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-S1ld2LwU.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLQZA_Wn.js → hasIn-RDaWX_4i.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSjilZ9L.js → index-9R6BxAr0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_N4-Gq9.js → index-B-Z8eFUJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dy0R5ZED.js → index-B0PMlbMs.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1ifZtXo.js → index-B5REnKt8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CINJQEj6.js → index-B7WBt_4T.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocEhinm.js → index-B9O9YyWE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjWnONdy.js → index-BCnZyIMA.js} +1 -1
- package/src/assets/web-panel/assets/{index-hwVDeoC0.js → index-BGqqJfL0.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bbj2NS_I.js → index-BHNYPO0b.js} +3 -3
- package/src/assets/web-panel/assets/{index-C5dgnilC.js → index-BKJr-TZp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT9vVN-X.js → index-BN6UPD2E.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpVrK6fr.js → index-Bi46Xu4u.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpBaJ3TD.js → index-BlT2VAEK.js} +1 -1
- package/src/assets/web-panel/assets/{index-1jeAudHd.js → index-C0yx-jtQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOoQpuQY.js → index-C4gkzxkw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZpYeqrh.js → index-CVQMeiwH.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhA3QEni.js → index-Cd3euP_4.js} +1 -1
- package/src/assets/web-panel/assets/{index-N_GoWZX4.js → index-Cd9OJhHG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOZJBTWZ.js → index-CiECJi-Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-CarMTIyQ.js → index-CjTrRt13.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZDekj7T.js → index-CnHH7bnD.js} +1 -1
- package/src/assets/web-panel/assets/{index-M7wxfAbm.js → index-Cpqm1qfU.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bxp7uDD8.js → index-CyOGN6a5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DalA0TKC.js → index-D0zcy54x.js} +1 -1
- package/src/assets/web-panel/assets/{index-COev0W2g.js → index-D6sm5-Xq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUq0c7gq.js → index-DIUnNOjG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZs5bBnk.js → index-DX7tuWVJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbQNL0ue.js → index-DXXtGMUJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTbxp4UH.js → index-DcPFB8qJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-YSw_gxlZ.js → index-DoQA0-52.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_LQodeJ.js → index-Drlnwr8F.js} +1 -1
- package/src/assets/web-panel/assets/index-Drz6yq0m.js +1 -0
- package/src/assets/web-panel/assets/{index-jWb70ZgZ.js → index-Dsij_KsN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DB0MJgF_.js → index-Dxge-xWl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEu2lMBy.js → index-DyIWssPC.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2FqfMvU.js → index-IeiL9RKc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dkwsi1Kn.js → index-nAH87mQP.js} +1 -1
- package/src/assets/web-panel/assets/{index-gt8bNefW.js → index-pPL66J85.js} +1 -1
- package/src/assets/web-panel/assets/index-uWiUs7S_.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-Co67Kqmb.js → initDefaultProps-BTL88of4.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BMOxQSF_.js → motion-BUB2pXwB.js} +1 -1
- package/src/assets/web-panel/assets/{move-C5fWDw-N.js → move-egCkkOMm.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BJevpL0e.js → mtc-parser-DAWhZC8c.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCzNU8s6.js → omit-CvOxe_6d.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CybWvDPk.js → pickAttrs-DZUSLZ5Z.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BMWS9HnW.js → placementArrow-BERxGNMY.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BoL2UnGY.js → responsiveObserve-DZuDfZ9n.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DWNfXraU.js → slide-DO756sgI.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BquTM3tX.js → statusUtils-BRQlIz28.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BEkCleim.js → styleChecker-DCSfdybl.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Oecnq7EA.js → useFlexGapSupport-LD8dWVve.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CkDffJ4F.js → useFs-BX9uW2LK.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-qbvKXesB.js → usePersonalDataHub-BPSlPuHU.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C6v8n6mA.js → vnode-BmOILC9V.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-4CTeFILV.js → zoom-1iKowKGg.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +5 -5
- package/src/commands/agent.js +90 -1
- package/src/commands/automation.js +103 -1
- package/src/commands/chat.js +11 -2
- package/src/commands/compact.js +106 -23
- package/src/commands/loop.js +28 -10
- package/src/commands/routine.js +1 -1
- package/src/commands/scheduler-daemon.js +359 -2
- package/src/commands/session-observability.js +305 -0
- package/src/commands/session.js +38 -0
- package/src/commands/team-merge-review.js +937 -0
- package/src/commands/team.js +2 -0
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +439 -5
- package/src/harness/jsonl-session-store.js +463 -125
- package/src/harness/prompt-compressor.js +86 -13
- package/src/harness/provider-backed-compaction.js +100 -27
- package/src/lib/advisor-runtime.js +3 -1
- package/src/lib/agent-schedule-store.js +549 -28
- package/src/lib/agent-team/team-adjudication.js +40 -1
- package/src/lib/agent-team/team-merge-review-store.js +765 -0
- package/src/lib/agent-team/team-merge-review-transaction.js +1223 -0
- package/src/lib/agent-team/team-merge-review.js +1847 -0
- package/src/lib/automation-center-incidents.js +314 -0
- package/src/lib/automation-center-runtime.js +448 -0
- package/src/lib/automation-center.js +182 -38
- package/src/lib/automation-engine.js +535 -0
- package/src/lib/automation-execution-authority.js +241 -12
- package/src/lib/automation-execution-incident.js +737 -0
- package/src/lib/background-agent-supervisor.js +793 -215
- package/src/lib/background-command-argv.js +24 -0
- package/src/lib/background-interaction-journal.js +361 -52
- package/src/lib/background-interaction-resolver.js +32 -0
- package/src/lib/background-launch-profile.js +2 -0
- package/src/lib/causal-observability.js +2661 -0
- package/src/lib/chat-session-admission.js +76 -0
- package/src/lib/cost-budget.js +5 -7
- package/src/lib/cowork/ab-comparator-cli.js +11 -0
- package/src/lib/cowork/debate-review-cli.js +12 -0
- package/src/lib/cowork-adapter.js +159 -38
- package/src/lib/cowork-cron.js +442 -15
- package/src/lib/cowork-task-runner.js +112 -17
- package/src/lib/delivery-coordinator.js +142 -0
- package/src/lib/direct-model-usage.js +121 -0
- package/src/lib/file-lines.js +183 -47
- package/src/lib/llm-pricing.js +12 -17
- package/src/lib/loop.js +26 -2
- package/src/lib/routine-store.js +380 -21
- package/src/lib/runtime-usage-ledger.js +182 -0
- package/src/lib/scheduler-kernel/agenda-adapter.js +325 -10
- package/src/lib/scheduler-kernel/automation-adapter.js +514 -18
- package/src/lib/scheduler-kernel/automation-event-adapter.js +123 -11
- package/src/lib/scheduler-kernel/contract.js +91 -0
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +297 -4
- package/src/lib/scheduler-kernel/loop-adapter.js +547 -6
- package/src/lib/scheduler-kernel/migration-admin.js +346 -0
- package/src/lib/scheduler-kernel/migration-rollback-admin.js +932 -0
- package/src/lib/scheduler-kernel/routine-adapter.js +400 -20
- package/src/lib/scheduler-kernel/runtime-control-capabilities.js +31 -0
- package/src/lib/scheduler-kernel/runtime.js +237 -2
- package/src/lib/scheduler-kernel/source-locator-path.js +142 -0
- package/src/lib/scheduler-kernel/store.js +3957 -248
- package/src/lib/session-host-lease.js +171 -2
- package/src/lib/session-host-snapshot.js +14 -3
- package/src/lib/session-persistence-failure.js +1 -0
- package/src/lib/session-tail.js +179 -34
- package/src/lib/session-transcript-structure.js +96 -0
- package/src/lib/sub-agent-context.js +328 -22
- package/src/lib/ws-agent-handler.js +4 -1
- package/src/repl/agent-repl.js +732 -91
- package/src/repl/chat-repl.js +5 -1
- package/src/runtime/agent-core.js +1282 -207
- package/src/runtime/fallback-model.js +5 -0
- package/src/runtime/headless-runner.js +324 -82
- package/src/runtime/headless-stream.js +232 -26
- package/src/runtime/mcp-config.js +81 -10
- package/src/runtime/policies/agent-policy.js +4 -0
- package/src/workers/background-agent-worker.js +403 -96
- package/src/assets/web-panel/assets/MarkdownRenderer-aMtgN-R6.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-DKTf5xIP.js +0 -7
- package/src/assets/web-panel/assets/Projects-Dd-xuPIc.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-C7tT9iad.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Jzu62CLC.js +0 -1
- package/src/assets/web-panel/assets/index-Zb2MeDSy.js +0 -1
- package/src/assets/web-panel/assets/index-fikqml4k.js +0 -1
|
@@ -2857,6 +2857,12 @@ export async function executeTool(name, args, context = {}) {
|
|
|
2857
2857
|
// 用量归因: shared per-run sink for child-loop (sub-agent / isolated
|
|
2858
2858
|
// skill) token usage, drained by agentLoop as attributed events.
|
|
2859
2859
|
subAgentUsageSink: context.subAgentUsageSink || null,
|
|
2860
|
+
strictUsageTelemetry: context.strictUsageTelemetry === true,
|
|
2861
|
+
onUsageBoundary: context.onUsageBoundary || null,
|
|
2862
|
+
onUsageSettlement: context.onUsageSettlement || null,
|
|
2863
|
+
onToolCallBoundary: context.onToolCallBoundary || null,
|
|
2864
|
+
onToolCallSettlement: context.onToolCallSettlement || null,
|
|
2865
|
+
backgroundUsageFailureState: context.backgroundUsageFailureState || null,
|
|
2860
2866
|
planManager,
|
|
2861
2867
|
permissionRules: context.permissionRules || null,
|
|
2862
2868
|
effectiveAllowedToolNames: context.effectiveAllowedToolNames ?? null,
|
|
@@ -4259,6 +4265,12 @@ async function executeToolInner(
|
|
|
4259
4265
|
signal = null,
|
|
4260
4266
|
backgroundSubAgents = null,
|
|
4261
4267
|
subAgentUsageSink = null,
|
|
4268
|
+
strictUsageTelemetry = false,
|
|
4269
|
+
onUsageBoundary = null,
|
|
4270
|
+
onUsageSettlement = null,
|
|
4271
|
+
onToolCallBoundary = null,
|
|
4272
|
+
onToolCallSettlement = null,
|
|
4273
|
+
backgroundUsageFailureState = null,
|
|
4262
4274
|
toolAdmission = null,
|
|
4263
4275
|
unattendedActionPolicy = null,
|
|
4264
4276
|
managedCheckpoint = false,
|
|
@@ -5715,6 +5727,12 @@ async function executeToolInner(
|
|
|
5715
5727
|
signal,
|
|
5716
5728
|
backgroundSubAgents,
|
|
5717
5729
|
subAgentUsageSink,
|
|
5730
|
+
strictUsageTelemetry,
|
|
5731
|
+
onUsageBoundary,
|
|
5732
|
+
onUsageSettlement,
|
|
5733
|
+
onToolCallBoundary,
|
|
5734
|
+
onToolCallSettlement,
|
|
5735
|
+
backgroundUsageFailureState,
|
|
5718
5736
|
toolAdmission,
|
|
5719
5737
|
}),
|
|
5720
5738
|
);
|
|
@@ -6597,7 +6615,75 @@ async function executeToolInner(
|
|
|
6597
6615
|
const skillUsageSink = Array.isArray(subAgentUsageSink)
|
|
6598
6616
|
? subAgentUsageSink
|
|
6599
6617
|
: null;
|
|
6618
|
+
if (strictUsageTelemetry === true) {
|
|
6619
|
+
_assertStrictUsageObserver(onUsageBoundary, "boundary");
|
|
6620
|
+
_assertStrictUsageObserver(onUsageSettlement, "settlement");
|
|
6621
|
+
}
|
|
6600
6622
|
let skillSubRef = null;
|
|
6623
|
+
const projectSkillUsage = (event) =>
|
|
6624
|
+
event && event.attribution
|
|
6625
|
+
? event
|
|
6626
|
+
: {
|
|
6627
|
+
...event,
|
|
6628
|
+
source:
|
|
6629
|
+
event?.source === "semantic-compaction"
|
|
6630
|
+
? "semantic-compaction"
|
|
6631
|
+
: "subagent",
|
|
6632
|
+
attribution: {
|
|
6633
|
+
origin: "skill",
|
|
6634
|
+
skill: args.skill_name,
|
|
6635
|
+
subagentId: skillSubRef?.id || null,
|
|
6636
|
+
parentSessionId: sessionId || null,
|
|
6637
|
+
depth: (subAgentDepth || 0) + 1,
|
|
6638
|
+
},
|
|
6639
|
+
};
|
|
6640
|
+
const observeSkillBoundary =
|
|
6641
|
+
strictUsageTelemetry === true
|
|
6642
|
+
? (event) =>
|
|
6643
|
+
_notifyStrictUsageBoundary(
|
|
6644
|
+
onUsageBoundary,
|
|
6645
|
+
projectSkillUsage(event),
|
|
6646
|
+
)
|
|
6647
|
+
: null;
|
|
6648
|
+
const observeSkillSettlement =
|
|
6649
|
+
strictUsageTelemetry === true
|
|
6650
|
+
? (event) =>
|
|
6651
|
+
_notifyStrictUsageSettlement(
|
|
6652
|
+
onUsageSettlement,
|
|
6653
|
+
projectSkillUsage(event),
|
|
6654
|
+
)
|
|
6655
|
+
: null;
|
|
6656
|
+
const projectSkillTool = (event) => ({
|
|
6657
|
+
...event,
|
|
6658
|
+
attribution: event?.attribution || projectSkillUsage({}).attribution,
|
|
6659
|
+
});
|
|
6660
|
+
const observeSkillToolBoundary =
|
|
6661
|
+
strictUsageTelemetry === true
|
|
6662
|
+
? (event) =>
|
|
6663
|
+
_notifyStrictToolObserver(
|
|
6664
|
+
onToolCallBoundary,
|
|
6665
|
+
projectSkillTool(event),
|
|
6666
|
+
"boundary",
|
|
6667
|
+
)
|
|
6668
|
+
: null;
|
|
6669
|
+
const observeSkillToolSettlement =
|
|
6670
|
+
strictUsageTelemetry === true
|
|
6671
|
+
? (event) =>
|
|
6672
|
+
_notifyStrictToolObserver(
|
|
6673
|
+
onToolCallSettlement,
|
|
6674
|
+
projectSkillTool(event),
|
|
6675
|
+
"settlement",
|
|
6676
|
+
)
|
|
6677
|
+
: null;
|
|
6678
|
+
const skillLlmOptions = { ...(llmOptions || {}) };
|
|
6679
|
+
if (typeof llmOptions?.onStreamRetry === "function") {
|
|
6680
|
+
skillLlmOptions.onStreamRetry = (attempt, error, telemetry = {}) =>
|
|
6681
|
+
_notifyStrictRetryObserver(llmOptions.onStreamRetry, [
|
|
6682
|
+
attempt,
|
|
6683
|
+
error,
|
|
6684
|
+
projectSkillUsage(telemetry),
|
|
6685
|
+
]);
|
|
6686
|
+
}
|
|
6601
6687
|
// Run skill through isolated sub-agent context
|
|
6602
6688
|
let subCtx;
|
|
6603
6689
|
try {
|
|
@@ -6613,7 +6699,7 @@ async function executeToolInner(
|
|
|
6613
6699
|
signal: skillExecutionSignal,
|
|
6614
6700
|
toolAdmission,
|
|
6615
6701
|
cwd,
|
|
6616
|
-
llmOptions:
|
|
6702
|
+
llmOptions: skillLlmOptions,
|
|
6617
6703
|
...(sessionBudget ? { sessionBudget } : {}),
|
|
6618
6704
|
...(permissionRules ? { permissionRules } : {}),
|
|
6619
6705
|
...(hostManagedToolPolicy
|
|
@@ -6637,29 +6723,29 @@ async function executeToolInner(
|
|
|
6637
6723
|
...(mcpConflictScheduler ? { mcpConflictScheduler } : {}),
|
|
6638
6724
|
onUsage: skillUsageSink
|
|
6639
6725
|
? (u) => {
|
|
6726
|
+
const forwarded = projectSkillUsage(u);
|
|
6640
6727
|
try {
|
|
6641
|
-
skillUsageSink.push(
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
origin: "skill",
|
|
6651
|
-
skill: args.skill_name,
|
|
6652
|
-
subagentId: skillSubRef?.id || null,
|
|
6653
|
-
parentSessionId: sessionId || null,
|
|
6654
|
-
depth: (subAgentDepth || 0) + 1,
|
|
6655
|
-
},
|
|
6656
|
-
},
|
|
6657
|
-
);
|
|
6658
|
-
} catch (_e) {
|
|
6659
|
-
// usage forwarding is best-effort
|
|
6728
|
+
skillUsageSink.push(forwarded);
|
|
6729
|
+
} catch (error) {
|
|
6730
|
+
if (strictUsageTelemetry === true) {
|
|
6731
|
+
throw _runtimeUsageBoundaryFailure(
|
|
6732
|
+
error,
|
|
6733
|
+
"CC_USAGE_FORWARDING_FAILED",
|
|
6734
|
+
"Isolated skill usage forwarding failed",
|
|
6735
|
+
);
|
|
6736
|
+
}
|
|
6660
6737
|
}
|
|
6661
6738
|
}
|
|
6662
6739
|
: null,
|
|
6740
|
+
...(strictUsageTelemetry === true
|
|
6741
|
+
? {
|
|
6742
|
+
strictUsageTelemetry: true,
|
|
6743
|
+
onUsageBoundary: observeSkillBoundary,
|
|
6744
|
+
onUsageSettlement: observeSkillSettlement,
|
|
6745
|
+
onToolCallBoundary: observeSkillToolBoundary,
|
|
6746
|
+
onToolCallSettlement: observeSkillToolSettlement,
|
|
6747
|
+
}
|
|
6748
|
+
: {}),
|
|
6663
6749
|
});
|
|
6664
6750
|
} catch (error) {
|
|
6665
6751
|
skillExecutionLease?.release?.();
|
|
@@ -6704,6 +6790,7 @@ async function executeToolInner(
|
|
|
6704
6790
|
toolsUsed: result.toolsUsed,
|
|
6705
6791
|
});
|
|
6706
6792
|
} catch (err) {
|
|
6793
|
+
if (err?.runtimeLedgerPersistence === true) throw err;
|
|
6707
6794
|
if (
|
|
6708
6795
|
signal?.aborted ||
|
|
6709
6796
|
skillExecutionSignal?.aborted ||
|
|
@@ -6786,6 +6873,16 @@ async function executeToolInner(
|
|
|
6786
6873
|
});
|
|
6787
6874
|
}
|
|
6788
6875
|
try {
|
|
6876
|
+
if (localToolExecutor.op === "list-templates") {
|
|
6877
|
+
const resourceTemplates =
|
|
6878
|
+
typeof mcpClient.listResourceTemplates === "function"
|
|
6879
|
+
? mcpClient.listResourceTemplates(args?.server || undefined)
|
|
6880
|
+
: [];
|
|
6881
|
+
return attachDescriptor({
|
|
6882
|
+
count: resourceTemplates.length,
|
|
6883
|
+
resourceTemplates,
|
|
6884
|
+
});
|
|
6885
|
+
}
|
|
6789
6886
|
if (localToolExecutor.op === "list") {
|
|
6790
6887
|
const resources =
|
|
6791
6888
|
typeof mcpClient.listResources === "function"
|
|
@@ -7839,6 +7936,34 @@ function _backgroundSubAgentResultText(entry) {
|
|
|
7839
7936
|
);
|
|
7840
7937
|
}
|
|
7841
7938
|
|
|
7939
|
+
function _throwBackgroundSubAgentUsageFailure(entry) {
|
|
7940
|
+
const error = entry?.outcome?.fatalError;
|
|
7941
|
+
if (error?.runtimeLedgerPersistence === true) throw error;
|
|
7942
|
+
}
|
|
7943
|
+
|
|
7944
|
+
function _throwSettledBackgroundUsageFailure(map) {
|
|
7945
|
+
if (!(map instanceof Map)) return;
|
|
7946
|
+
for (const entry of map.values()) {
|
|
7947
|
+
if (entry?.settled === true) {
|
|
7948
|
+
_throwBackgroundSubAgentUsageFailure(entry);
|
|
7949
|
+
}
|
|
7950
|
+
}
|
|
7951
|
+
}
|
|
7952
|
+
|
|
7953
|
+
function _throwBackgroundUsageFailureState(state) {
|
|
7954
|
+
const error = state?.error;
|
|
7955
|
+
if (error?.runtimeLedgerPersistence === true) throw error;
|
|
7956
|
+
}
|
|
7957
|
+
|
|
7958
|
+
async function _awaitBackgroundUsageSettlement(map, state) {
|
|
7959
|
+
_throwBackgroundUsageFailureState(state);
|
|
7960
|
+
if (map instanceof Map && map.size > 0) {
|
|
7961
|
+
await Promise.all([...map.values()].map((entry) => entry.promise));
|
|
7962
|
+
}
|
|
7963
|
+
_throwBackgroundUsageFailureState(state);
|
|
7964
|
+
_throwSettledBackgroundUsageFailure(map);
|
|
7965
|
+
}
|
|
7966
|
+
|
|
7842
7967
|
/**
|
|
7843
7968
|
* Drain the run's attributed child-loop usage sink (spawn_sub_agent /
|
|
7844
7969
|
* isolated run_skill — see toolContext.subAgentUsageSink), re-yielding each
|
|
@@ -7850,13 +7975,48 @@ function _backgroundSubAgentResultText(entry) {
|
|
|
7850
7975
|
function* _drainSubAgentUsage(sink) {
|
|
7851
7976
|
while (Array.isArray(sink) && sink.length > 0) {
|
|
7852
7977
|
const u = sink.shift();
|
|
7978
|
+
if (u?.type === "model-usage-unknown") {
|
|
7979
|
+
const boundary = {
|
|
7980
|
+
callId: u.callId,
|
|
7981
|
+
provider: u.provider ?? null,
|
|
7982
|
+
model: u.model ?? null,
|
|
7983
|
+
source: u.source || "subagent",
|
|
7984
|
+
attribution: u.attribution || null,
|
|
7985
|
+
};
|
|
7986
|
+
if (u.callId && u.boundaryNotified !== true) {
|
|
7987
|
+
yield { type: "model-usage-started", ...boundary };
|
|
7988
|
+
}
|
|
7989
|
+
yield {
|
|
7990
|
+
type: "model-usage-unknown",
|
|
7991
|
+
...boundary,
|
|
7992
|
+
code: u.code || "provider_transport_outcome_unknown",
|
|
7993
|
+
...(u.ledgerPersisted === true ? { ledgerPersisted: true } : {}),
|
|
7994
|
+
};
|
|
7995
|
+
continue;
|
|
7996
|
+
}
|
|
7997
|
+
if (u?.callId && u.boundaryNotified !== true) {
|
|
7998
|
+
yield {
|
|
7999
|
+
type: "model-usage-started",
|
|
8000
|
+
callId: u.callId,
|
|
8001
|
+
provider: u.provider ?? null,
|
|
8002
|
+
model: u.model ?? null,
|
|
8003
|
+
source: u.source || "subagent",
|
|
8004
|
+
attribution: u.attribution || null,
|
|
8005
|
+
};
|
|
8006
|
+
}
|
|
7853
8007
|
yield {
|
|
7854
8008
|
type: "token-usage",
|
|
8009
|
+
...(u?.callId ? { callId: u.callId } : {}),
|
|
7855
8010
|
provider: u?.provider ?? null,
|
|
7856
8011
|
model: u?.model ?? null,
|
|
7857
8012
|
usage: u?.usage || {},
|
|
7858
|
-
...(u?.source
|
|
8013
|
+
...(u?.source
|
|
8014
|
+
? { source: u.source }
|
|
8015
|
+
: u?.callId
|
|
8016
|
+
? { source: "subagent" }
|
|
8017
|
+
: {}),
|
|
7859
8018
|
attribution: u?.attribution || null,
|
|
8019
|
+
...(u?.ledgerPersisted === true ? { ledgerPersisted: true } : {}),
|
|
7860
8020
|
};
|
|
7861
8021
|
}
|
|
7862
8022
|
}
|
|
@@ -8313,32 +8473,126 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
8313
8473
|
const usageSink = Array.isArray(ctx.subAgentUsageSink)
|
|
8314
8474
|
? ctx.subAgentUsageSink
|
|
8315
8475
|
: null;
|
|
8476
|
+
if (ctx.strictUsageTelemetry === true) {
|
|
8477
|
+
_assertStrictUsageObserver(ctx.onUsageBoundary, "boundary");
|
|
8478
|
+
_assertStrictUsageObserver(ctx.onUsageSettlement, "settlement");
|
|
8479
|
+
}
|
|
8316
8480
|
let subCtxRef = null;
|
|
8481
|
+
const latchBackgroundUsageFailure = (error) => {
|
|
8482
|
+
if (
|
|
8483
|
+
error?.runtimeLedgerPersistence === true &&
|
|
8484
|
+
ctx.backgroundUsageFailureState &&
|
|
8485
|
+
!ctx.backgroundUsageFailureState.error
|
|
8486
|
+
) {
|
|
8487
|
+
ctx.backgroundUsageFailureState.error = error;
|
|
8488
|
+
}
|
|
8489
|
+
return error;
|
|
8490
|
+
};
|
|
8491
|
+
const projectSubagentUsage = (u) =>
|
|
8492
|
+
u && u.attribution
|
|
8493
|
+
? u
|
|
8494
|
+
: {
|
|
8495
|
+
...u,
|
|
8496
|
+
source:
|
|
8497
|
+
u?.source === "semantic-compaction"
|
|
8498
|
+
? "semantic-compaction"
|
|
8499
|
+
: "subagent",
|
|
8500
|
+
attribution: {
|
|
8501
|
+
origin: "subagent",
|
|
8502
|
+
subagentId: subCtxRef?.id || null,
|
|
8503
|
+
role: subCtxRef?.role || role || null,
|
|
8504
|
+
parentSessionId,
|
|
8505
|
+
depth: currentDepth + 1,
|
|
8506
|
+
},
|
|
8507
|
+
};
|
|
8317
8508
|
const onUsage = usageSink
|
|
8318
8509
|
? (u) => {
|
|
8510
|
+
const forwarded = projectSubagentUsage(u);
|
|
8319
8511
|
try {
|
|
8320
|
-
usageSink.push(
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
origin: "subagent",
|
|
8330
|
-
subagentId: subCtxRef?.id || null,
|
|
8331
|
-
role: subCtxRef?.role || role || null,
|
|
8332
|
-
parentSessionId,
|
|
8333
|
-
depth: currentDepth + 1,
|
|
8334
|
-
},
|
|
8335
|
-
},
|
|
8336
|
-
);
|
|
8337
|
-
} catch (_e) {
|
|
8338
|
-
// usage forwarding is best-effort
|
|
8512
|
+
usageSink.push(forwarded);
|
|
8513
|
+
} catch (error) {
|
|
8514
|
+
if (ctx.strictUsageTelemetry === true) {
|
|
8515
|
+
throw _runtimeUsageBoundaryFailure(
|
|
8516
|
+
error,
|
|
8517
|
+
"CC_USAGE_FORWARDING_FAILED",
|
|
8518
|
+
"Sub-agent usage forwarding failed",
|
|
8519
|
+
);
|
|
8520
|
+
}
|
|
8339
8521
|
}
|
|
8340
8522
|
}
|
|
8341
8523
|
: null;
|
|
8524
|
+
const observeSubagentBoundary =
|
|
8525
|
+
ctx.strictUsageTelemetry === true
|
|
8526
|
+
? (event) => {
|
|
8527
|
+
try {
|
|
8528
|
+
return _notifyStrictUsageBoundary(
|
|
8529
|
+
ctx.onUsageBoundary,
|
|
8530
|
+
projectSubagentUsage(event),
|
|
8531
|
+
);
|
|
8532
|
+
} catch (error) {
|
|
8533
|
+
throw latchBackgroundUsageFailure(error);
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
: null;
|
|
8537
|
+
const observeSubagentSettlement =
|
|
8538
|
+
ctx.strictUsageTelemetry === true
|
|
8539
|
+
? (event) => {
|
|
8540
|
+
try {
|
|
8541
|
+
return _notifyStrictUsageSettlement(
|
|
8542
|
+
ctx.onUsageSettlement,
|
|
8543
|
+
projectSubagentUsage(event),
|
|
8544
|
+
);
|
|
8545
|
+
} catch (error) {
|
|
8546
|
+
throw latchBackgroundUsageFailure(error);
|
|
8547
|
+
}
|
|
8548
|
+
}
|
|
8549
|
+
: null;
|
|
8550
|
+
const projectSubagentTool = (event) => ({
|
|
8551
|
+
...event,
|
|
8552
|
+
attribution:
|
|
8553
|
+
event?.attribution || projectSubagentUsage({}).attribution || null,
|
|
8554
|
+
});
|
|
8555
|
+
const observeSubagentToolBoundary =
|
|
8556
|
+
ctx.strictUsageTelemetry === true
|
|
8557
|
+
? (event) => {
|
|
8558
|
+
try {
|
|
8559
|
+
return _notifyStrictToolObserver(
|
|
8560
|
+
ctx.onToolCallBoundary,
|
|
8561
|
+
projectSubagentTool(event),
|
|
8562
|
+
"boundary",
|
|
8563
|
+
);
|
|
8564
|
+
} catch (error) {
|
|
8565
|
+
throw latchBackgroundUsageFailure(error);
|
|
8566
|
+
}
|
|
8567
|
+
}
|
|
8568
|
+
: null;
|
|
8569
|
+
const observeSubagentToolSettlement =
|
|
8570
|
+
ctx.strictUsageTelemetry === true
|
|
8571
|
+
? (event) => {
|
|
8572
|
+
try {
|
|
8573
|
+
return _notifyStrictToolObserver(
|
|
8574
|
+
ctx.onToolCallSettlement,
|
|
8575
|
+
projectSubagentTool(event),
|
|
8576
|
+
"settlement",
|
|
8577
|
+
);
|
|
8578
|
+
} catch (error) {
|
|
8579
|
+
throw latchBackgroundUsageFailure(error);
|
|
8580
|
+
}
|
|
8581
|
+
}
|
|
8582
|
+
: null;
|
|
8583
|
+
if (typeof parentLlm.onStreamRetry === "function") {
|
|
8584
|
+
subLlmOptions.onStreamRetry = (attempt, error, telemetry = {}) => {
|
|
8585
|
+
try {
|
|
8586
|
+
return _notifyStrictRetryObserver(parentLlm.onStreamRetry, [
|
|
8587
|
+
attempt,
|
|
8588
|
+
error,
|
|
8589
|
+
projectSubagentUsage(telemetry),
|
|
8590
|
+
]);
|
|
8591
|
+
} catch (observerError) {
|
|
8592
|
+
throw latchBackgroundUsageFailure(observerError);
|
|
8593
|
+
}
|
|
8594
|
+
};
|
|
8595
|
+
}
|
|
8342
8596
|
|
|
8343
8597
|
// Worktree sparse-checkout + dependency symlink (large-monorepo lever): when
|
|
8344
8598
|
// the child runs in an isolated worktree, only materialize the packages it
|
|
@@ -8394,6 +8648,15 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
8394
8648
|
subAgentBudget: ctx.subAgentBudget || null,
|
|
8395
8649
|
...(ctx.sessionBudget ? { sessionBudget: ctx.sessionBudget } : {}),
|
|
8396
8650
|
onUsage,
|
|
8651
|
+
...(ctx.strictUsageTelemetry === true
|
|
8652
|
+
? {
|
|
8653
|
+
strictUsageTelemetry: true,
|
|
8654
|
+
onUsageBoundary: observeSubagentBoundary,
|
|
8655
|
+
onUsageSettlement: observeSubagentSettlement,
|
|
8656
|
+
onToolCallBoundary: observeSubagentToolBoundary,
|
|
8657
|
+
onToolCallSettlement: observeSubagentToolSettlement,
|
|
8658
|
+
}
|
|
8659
|
+
: {}),
|
|
8397
8660
|
// Extended contract (gap 2026-07-11): per-agent iteration cap + opt-in
|
|
8398
8661
|
// worktree isolation. undefined keeps the profile/flag defaults intact.
|
|
8399
8662
|
...(subMaxTurns ? { maxIterations: subMaxTurns } : {}),
|
|
@@ -8574,7 +8837,13 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
8574
8837
|
(result) => ({ result, error: null }),
|
|
8575
8838
|
(err) => {
|
|
8576
8839
|
subCtx.forceComplete(err.message);
|
|
8577
|
-
return {
|
|
8840
|
+
return {
|
|
8841
|
+
result: subCtx.result,
|
|
8842
|
+
error: err.message,
|
|
8843
|
+
...(err?.runtimeLedgerPersistence === true
|
|
8844
|
+
? { fatalError: err }
|
|
8845
|
+
: {}),
|
|
8846
|
+
};
|
|
8578
8847
|
},
|
|
8579
8848
|
)
|
|
8580
8849
|
.then((outcome) => {
|
|
@@ -8704,6 +8973,8 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
8704
8973
|
completedAt: subCtx.completedAt,
|
|
8705
8974
|
});
|
|
8706
8975
|
|
|
8976
|
+
if (err?.runtimeLedgerPersistence === true) throw err;
|
|
8977
|
+
|
|
8707
8978
|
return {
|
|
8708
8979
|
error: `Sub-agent failed: ${err.message}`,
|
|
8709
8980
|
subAgentId: subCtx.id,
|
|
@@ -8739,6 +9010,49 @@ function getEffectiveToolDefinitions(options = {}) {
|
|
|
8739
9010
|
});
|
|
8740
9011
|
}
|
|
8741
9012
|
|
|
9013
|
+
function _isNonNegativeSafeInteger(value) {
|
|
9014
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
9015
|
+
}
|
|
9016
|
+
|
|
9017
|
+
function _providerUsageCount(usage, canonical, alias) {
|
|
9018
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null;
|
|
9019
|
+
const hasCanonical = Object.prototype.hasOwnProperty.call(usage, canonical);
|
|
9020
|
+
const hasAlias = Object.prototype.hasOwnProperty.call(usage, alias);
|
|
9021
|
+
if (!hasCanonical && !hasAlias) return null;
|
|
9022
|
+
if (hasCanonical && hasAlias && usage[canonical] !== usage[alias])
|
|
9023
|
+
return null;
|
|
9024
|
+
const value = hasCanonical ? usage[canonical] : usage[alias];
|
|
9025
|
+
return _isNonNegativeSafeInteger(value) ? value : null;
|
|
9026
|
+
}
|
|
9027
|
+
|
|
9028
|
+
function _optionalProviderUsageCount(usage, canonical, alias) {
|
|
9029
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null;
|
|
9030
|
+
const hasCanonical = Object.prototype.hasOwnProperty.call(usage, canonical);
|
|
9031
|
+
const hasAlias = Object.prototype.hasOwnProperty.call(usage, alias);
|
|
9032
|
+
if (!hasCanonical && !hasAlias) return 0;
|
|
9033
|
+
if (hasCanonical && hasAlias && usage[canonical] !== usage[alias])
|
|
9034
|
+
return null;
|
|
9035
|
+
const value = hasCanonical ? usage[canonical] : usage[alias];
|
|
9036
|
+
return _isNonNegativeSafeInteger(value) ? value : null;
|
|
9037
|
+
}
|
|
9038
|
+
|
|
9039
|
+
function _hasCompleteProviderUsage(usage) {
|
|
9040
|
+
return (
|
|
9041
|
+
_providerUsageCount(usage, "input_tokens", "prompt_tokens") != null &&
|
|
9042
|
+
_providerUsageCount(usage, "output_tokens", "completion_tokens") != null &&
|
|
9043
|
+
_optionalProviderUsageCount(
|
|
9044
|
+
usage,
|
|
9045
|
+
"cache_read_input_tokens",
|
|
9046
|
+
"cache_read_tokens",
|
|
9047
|
+
) != null &&
|
|
9048
|
+
_optionalProviderUsageCount(
|
|
9049
|
+
usage,
|
|
9050
|
+
"cache_creation_input_tokens",
|
|
9051
|
+
"cache_creation_tokens",
|
|
9052
|
+
) != null
|
|
9053
|
+
);
|
|
9054
|
+
}
|
|
9055
|
+
|
|
8742
9056
|
/**
|
|
8743
9057
|
* Send a chat completion request with tool definitions.
|
|
8744
9058
|
* Supports 8 providers: ollama, anthropic, openai, deepseek, dashscope, gemini, mistral, volcengine
|
|
@@ -8802,12 +9116,17 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
8802
9116
|
),
|
|
8803
9117
|
{
|
|
8804
9118
|
signal,
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
9119
|
+
strictRetryObserver: options.strictUsageTelemetry === true,
|
|
9120
|
+
...(typeof options.onStreamRetry === "function"
|
|
9121
|
+
? {
|
|
9122
|
+
onRetry: (attempt, error, telemetry) =>
|
|
9123
|
+
options.onStreamRetry(attempt, error, {
|
|
9124
|
+
...telemetry,
|
|
9125
|
+
provider: "ollama",
|
|
9126
|
+
model: model || null,
|
|
9127
|
+
}),
|
|
9128
|
+
}
|
|
9129
|
+
: {}),
|
|
8811
9130
|
},
|
|
8812
9131
|
);
|
|
8813
9132
|
}
|
|
@@ -8829,11 +9148,16 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
8829
9148
|
throw new Error(await formatProviderResponseError("ollama", response));
|
|
8830
9149
|
}
|
|
8831
9150
|
const data = await response.json();
|
|
8832
|
-
if (
|
|
9151
|
+
if (
|
|
9152
|
+
_isNonNegativeSafeInteger(data.prompt_eval_count) &&
|
|
9153
|
+
_isNonNegativeSafeInteger(data.eval_count)
|
|
9154
|
+
) {
|
|
8833
9155
|
data.usage = {
|
|
8834
|
-
input_tokens: data.prompt_eval_count
|
|
8835
|
-
output_tokens: data.eval_count
|
|
9156
|
+
input_tokens: data.prompt_eval_count,
|
|
9157
|
+
output_tokens: data.eval_count,
|
|
8836
9158
|
};
|
|
9159
|
+
} else {
|
|
9160
|
+
delete data.usage;
|
|
8837
9161
|
}
|
|
8838
9162
|
return data;
|
|
8839
9163
|
}
|
|
@@ -8941,12 +9265,17 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
8941
9265
|
),
|
|
8942
9266
|
{
|
|
8943
9267
|
signal,
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
9268
|
+
strictRetryObserver: options.strictUsageTelemetry === true,
|
|
9269
|
+
...(typeof options.onStreamRetry === "function"
|
|
9270
|
+
? {
|
|
9271
|
+
onRetry: (attempt, error, telemetry) =>
|
|
9272
|
+
options.onStreamRetry(attempt, error, {
|
|
9273
|
+
...telemetry,
|
|
9274
|
+
provider: "anthropic",
|
|
9275
|
+
model: effModel,
|
|
9276
|
+
}),
|
|
9277
|
+
}
|
|
9278
|
+
: {}),
|
|
8950
9279
|
},
|
|
8951
9280
|
);
|
|
8952
9281
|
}
|
|
@@ -8968,17 +9297,32 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
8968
9297
|
|
|
8969
9298
|
const data = await response.json();
|
|
8970
9299
|
const normalized = _normalizeAnthropicResponse(data);
|
|
8971
|
-
if (data.usage) {
|
|
9300
|
+
if (_hasCompleteProviderUsage(data.usage)) {
|
|
8972
9301
|
normalized.usage = {
|
|
8973
9302
|
// With prompt caching, Anthropic splits input into uncached
|
|
8974
9303
|
// `input_tokens` + cache read/write — capture all three so cost
|
|
8975
9304
|
// accounting prices the cached prefix correctly (read ≈ 0.1×,
|
|
8976
9305
|
// write ≈ 1.25× input). Absent (caching off) → 0, byte-identical.
|
|
8977
|
-
input_tokens:
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
9306
|
+
input_tokens: _providerUsageCount(
|
|
9307
|
+
data.usage,
|
|
9308
|
+
"input_tokens",
|
|
9309
|
+
"prompt_tokens",
|
|
9310
|
+
),
|
|
9311
|
+
output_tokens: _providerUsageCount(
|
|
9312
|
+
data.usage,
|
|
9313
|
+
"output_tokens",
|
|
9314
|
+
"completion_tokens",
|
|
9315
|
+
),
|
|
9316
|
+
cache_read_input_tokens: _optionalProviderUsageCount(
|
|
9317
|
+
data.usage,
|
|
9318
|
+
"cache_read_input_tokens",
|
|
9319
|
+
"cache_read_tokens",
|
|
9320
|
+
),
|
|
9321
|
+
cache_creation_input_tokens: _optionalProviderUsageCount(
|
|
9322
|
+
data.usage,
|
|
9323
|
+
"cache_creation_input_tokens",
|
|
9324
|
+
"cache_creation_tokens",
|
|
9325
|
+
),
|
|
8982
9326
|
};
|
|
8983
9327
|
}
|
|
8984
9328
|
return normalized;
|
|
@@ -9056,12 +9400,17 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
9056
9400
|
),
|
|
9057
9401
|
{
|
|
9058
9402
|
signal,
|
|
9059
|
-
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9403
|
+
strictRetryObserver: options.strictUsageTelemetry === true,
|
|
9404
|
+
...(typeof options.onStreamRetry === "function"
|
|
9405
|
+
? {
|
|
9406
|
+
onRetry: (attempt, error, telemetry) =>
|
|
9407
|
+
options.onStreamRetry(attempt, error, {
|
|
9408
|
+
...telemetry,
|
|
9409
|
+
provider,
|
|
9410
|
+
model: model || defaultModels[provider] || "gpt-4o-mini",
|
|
9411
|
+
}),
|
|
9412
|
+
}
|
|
9413
|
+
: {}),
|
|
9065
9414
|
},
|
|
9066
9415
|
);
|
|
9067
9416
|
}
|
|
@@ -9093,16 +9442,28 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
9093
9442
|
}
|
|
9094
9443
|
const choice = data.choices[0];
|
|
9095
9444
|
const out = { message: choice.message };
|
|
9096
|
-
if (data.usage) {
|
|
9445
|
+
if (_hasCompleteProviderUsage(data.usage)) {
|
|
9097
9446
|
// OpenAI/DeepSeek/volcengine report cached prompt tokens AS PART of
|
|
9098
9447
|
// prompt_tokens — split them out so cost prices the cached prefix at the
|
|
9099
9448
|
// provider's cache rate, not full input. 0 (or absent) → input unchanged.
|
|
9449
|
+
const promptTokens = _providerUsageCount(
|
|
9450
|
+
data.usage,
|
|
9451
|
+
"input_tokens",
|
|
9452
|
+
"prompt_tokens",
|
|
9453
|
+
);
|
|
9454
|
+
const completionTokens = _providerUsageCount(
|
|
9455
|
+
data.usage,
|
|
9456
|
+
"output_tokens",
|
|
9457
|
+
"completion_tokens",
|
|
9458
|
+
);
|
|
9100
9459
|
const cached = _openaiCachedTokens(data.usage);
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9460
|
+
if (cached != null && cached <= promptTokens) {
|
|
9461
|
+
out.usage = {
|
|
9462
|
+
input_tokens: promptTokens - cached,
|
|
9463
|
+
output_tokens: completionTokens,
|
|
9464
|
+
cache_read_input_tokens: cached,
|
|
9465
|
+
};
|
|
9466
|
+
}
|
|
9106
9467
|
}
|
|
9107
9468
|
return out;
|
|
9108
9469
|
}
|
|
@@ -9121,8 +9482,9 @@ function _ollamaInitState() {
|
|
|
9121
9482
|
role: "assistant",
|
|
9122
9483
|
content: "",
|
|
9123
9484
|
toolCalls: null,
|
|
9124
|
-
promptEval:
|
|
9125
|
-
evalCount:
|
|
9485
|
+
promptEval: null,
|
|
9486
|
+
evalCount: null,
|
|
9487
|
+
usageInvalid: false,
|
|
9126
9488
|
};
|
|
9127
9489
|
}
|
|
9128
9490
|
|
|
@@ -9188,8 +9550,20 @@ function _ollamaReduceLine(state, line, onToken) {
|
|
|
9188
9550
|
state.toolCalls = (state.toolCalls || []).concat(msg.tool_calls);
|
|
9189
9551
|
}
|
|
9190
9552
|
}
|
|
9191
|
-
if (obj
|
|
9192
|
-
|
|
9553
|
+
if (Object.prototype.hasOwnProperty.call(obj, "prompt_eval_count")) {
|
|
9554
|
+
if (_isNonNegativeSafeInteger(obj.prompt_eval_count)) {
|
|
9555
|
+
state.promptEval = obj.prompt_eval_count;
|
|
9556
|
+
} else {
|
|
9557
|
+
state.usageInvalid = true;
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9560
|
+
if (Object.prototype.hasOwnProperty.call(obj, "eval_count")) {
|
|
9561
|
+
if (_isNonNegativeSafeInteger(obj.eval_count)) {
|
|
9562
|
+
state.evalCount = obj.eval_count;
|
|
9563
|
+
} else {
|
|
9564
|
+
state.usageInvalid = true;
|
|
9565
|
+
}
|
|
9566
|
+
}
|
|
9193
9567
|
return state;
|
|
9194
9568
|
}
|
|
9195
9569
|
|
|
@@ -9199,7 +9573,11 @@ function _ollamaFinalize(state) {
|
|
|
9199
9573
|
message.tool_calls = state.toolCalls;
|
|
9200
9574
|
}
|
|
9201
9575
|
const data = { message };
|
|
9202
|
-
if (
|
|
9576
|
+
if (
|
|
9577
|
+
state.usageInvalid !== true &&
|
|
9578
|
+
_isNonNegativeSafeInteger(state.promptEval) &&
|
|
9579
|
+
_isNonNegativeSafeInteger(state.evalCount)
|
|
9580
|
+
) {
|
|
9203
9581
|
data.usage = {
|
|
9204
9582
|
input_tokens: state.promptEval,
|
|
9205
9583
|
output_tokens: state.evalCount,
|
|
@@ -9292,7 +9670,7 @@ const _STREAM_STALL = Symbol("stream-stall");
|
|
|
9292
9670
|
* last error — strictly better than today (one drop → instant error).
|
|
9293
9671
|
*
|
|
9294
9672
|
* @param {() => Promise<any>} streamFn invokes one `_chat*Streaming` attempt
|
|
9295
|
-
* @param {object} opts { signal?, retries?, baseDelayMs?, onRetry?, sleep? }
|
|
9673
|
+
* @param {object} opts { signal?, retries?, baseDelayMs?, onRetry?, strictRetryObserver?, sleep? }
|
|
9296
9674
|
*/
|
|
9297
9675
|
export async function _retryStreamingChat(streamFn, opts = {}) {
|
|
9298
9676
|
// Default budget honors CC_MAX_RETRIES / CLAUDE_CODE_MAX_RETRIES (capped 15);
|
|
@@ -9311,13 +9689,46 @@ export async function _retryStreamingChat(streamFn, opts = {}) {
|
|
|
9311
9689
|
if (attempt >= retries || !_isRetryableStreamError(err, signal))
|
|
9312
9690
|
throw err;
|
|
9313
9691
|
attempt++;
|
|
9692
|
+
if (
|
|
9693
|
+
opts.strictRetryObserver === true &&
|
|
9694
|
+
typeof opts.onRetry !== "function"
|
|
9695
|
+
) {
|
|
9696
|
+
throw _runtimeUsageBoundaryFailure(
|
|
9697
|
+
null,
|
|
9698
|
+
"CC_RETRY_OBSERVER_REQUIRED",
|
|
9699
|
+
"Strict streaming retry requires a synchronous persistence observer",
|
|
9700
|
+
);
|
|
9701
|
+
}
|
|
9314
9702
|
if (typeof opts.onRetry === "function") {
|
|
9315
9703
|
try {
|
|
9316
|
-
opts.onRetry(attempt, err, {
|
|
9704
|
+
const observation = opts.onRetry(attempt, err, {
|
|
9317
9705
|
durationMs: Math.max(0, now() - attemptStartedAt),
|
|
9318
9706
|
});
|
|
9319
|
-
|
|
9320
|
-
|
|
9707
|
+
if (observation && typeof observation.then === "function") {
|
|
9708
|
+
void Promise.resolve(observation).catch(() => {});
|
|
9709
|
+
if (opts.strictRetryObserver === true) {
|
|
9710
|
+
const observerError = new Error(
|
|
9711
|
+
"Strict retry observer must be synchronous",
|
|
9712
|
+
);
|
|
9713
|
+
observerError.code = "CC_RETRY_OBSERVER_ASYNC";
|
|
9714
|
+
throw observerError;
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9717
|
+
} catch (observerError) {
|
|
9718
|
+
// A persisted usage ledger must record the failed attempt before a
|
|
9719
|
+
// second billable transport attempt starts. Strict hosts opt into a
|
|
9720
|
+
// fail-closed observer; legacy/unpersisted callers retain the former
|
|
9721
|
+
// best-effort notification behavior.
|
|
9722
|
+
if (
|
|
9723
|
+
opts.strictRetryObserver === true ||
|
|
9724
|
+
observerError?.code === "CC_SESSION_PERSISTENCE_FAILED"
|
|
9725
|
+
) {
|
|
9726
|
+
throw _runtimeUsageBoundaryFailure(
|
|
9727
|
+
observerError,
|
|
9728
|
+
observerError?.code || "CC_RETRY_PERSISTENCE_FAILED",
|
|
9729
|
+
"Streaming retry persistence failed",
|
|
9730
|
+
);
|
|
9731
|
+
}
|
|
9321
9732
|
}
|
|
9322
9733
|
}
|
|
9323
9734
|
await sleep(base * Math.pow(2, attempt - 1));
|
|
@@ -9489,10 +9900,11 @@ function _anthropicInitState() {
|
|
|
9489
9900
|
return {
|
|
9490
9901
|
text: "",
|
|
9491
9902
|
blocks: {},
|
|
9492
|
-
inputTokens:
|
|
9493
|
-
outputTokens:
|
|
9903
|
+
inputTokens: null,
|
|
9904
|
+
outputTokens: null,
|
|
9494
9905
|
cacheReadTokens: 0,
|
|
9495
9906
|
cacheCreationTokens: 0,
|
|
9907
|
+
usageInvalid: false,
|
|
9496
9908
|
};
|
|
9497
9909
|
}
|
|
9498
9910
|
|
|
@@ -9508,13 +9920,35 @@ function _anthropicReduceLine(state, raw, onToken, onThinking) {
|
|
|
9508
9920
|
return state;
|
|
9509
9921
|
}
|
|
9510
9922
|
if (obj.type === "message_start") {
|
|
9511
|
-
const
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9923
|
+
const usage = obj.message?.usage;
|
|
9924
|
+
if (usage != null) {
|
|
9925
|
+
const inputTokens = _providerUsageCount(
|
|
9926
|
+
usage,
|
|
9927
|
+
"input_tokens",
|
|
9928
|
+
"prompt_tokens",
|
|
9929
|
+
);
|
|
9930
|
+
const cacheReadTokens = _optionalProviderUsageCount(
|
|
9931
|
+
usage,
|
|
9932
|
+
"cache_read_input_tokens",
|
|
9933
|
+
"cache_read_tokens",
|
|
9934
|
+
);
|
|
9935
|
+
const cacheCreationTokens = _optionalProviderUsageCount(
|
|
9936
|
+
usage,
|
|
9937
|
+
"cache_creation_input_tokens",
|
|
9938
|
+
"cache_creation_tokens",
|
|
9939
|
+
);
|
|
9940
|
+
if (
|
|
9941
|
+
inputTokens == null ||
|
|
9942
|
+
cacheReadTokens == null ||
|
|
9943
|
+
cacheCreationTokens == null
|
|
9944
|
+
) {
|
|
9945
|
+
state.usageInvalid = true;
|
|
9946
|
+
} else {
|
|
9947
|
+
state.inputTokens = inputTokens;
|
|
9948
|
+
state.cacheReadTokens = cacheReadTokens;
|
|
9949
|
+
state.cacheCreationTokens = cacheCreationTokens;
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9518
9952
|
} else if (obj.type === "content_block_start") {
|
|
9519
9953
|
const cb = obj.content_block || {};
|
|
9520
9954
|
state.blocks[obj.index] =
|
|
@@ -9553,7 +9987,18 @@ function _anthropicReduceLine(state, raw, onToken, onThinking) {
|
|
|
9553
9987
|
(state.blocks[obj.index].signature || "") + (d.signature || "");
|
|
9554
9988
|
}
|
|
9555
9989
|
} else if (obj.type === "message_delta") {
|
|
9556
|
-
|
|
9990
|
+
if (obj.usage != null) {
|
|
9991
|
+
const outputTokens = _providerUsageCount(
|
|
9992
|
+
obj.usage,
|
|
9993
|
+
"output_tokens",
|
|
9994
|
+
"completion_tokens",
|
|
9995
|
+
);
|
|
9996
|
+
if (outputTokens == null) {
|
|
9997
|
+
state.usageInvalid = true;
|
|
9998
|
+
} else {
|
|
9999
|
+
state.outputTokens = outputTokens;
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
9557
10002
|
}
|
|
9558
10003
|
return state;
|
|
9559
10004
|
}
|
|
@@ -9594,10 +10039,9 @@ function _anthropicFinalize(state) {
|
|
|
9594
10039
|
if (thinkingBlocks.length) message._thinkingBlocks = thinkingBlocks;
|
|
9595
10040
|
const out = { message };
|
|
9596
10041
|
if (
|
|
9597
|
-
state.
|
|
9598
|
-
state.
|
|
9599
|
-
state.
|
|
9600
|
-
state.cacheCreationTokens
|
|
10042
|
+
state.usageInvalid !== true &&
|
|
10043
|
+
_isNonNegativeSafeInteger(state.inputTokens) &&
|
|
10044
|
+
_isNonNegativeSafeInteger(state.outputTokens)
|
|
9601
10045
|
) {
|
|
9602
10046
|
out.usage = {
|
|
9603
10047
|
input_tokens: state.inputTokens,
|
|
@@ -9683,21 +10127,49 @@ async function _chatAnthropicStreaming(
|
|
|
9683
10127
|
// cached prefix twice. Verified live against volcengine (field present, 0 when
|
|
9684
10128
|
// the provider does not auto-cache).
|
|
9685
10129
|
function _openaiCachedTokens(usage) {
|
|
9686
|
-
if (!usage || typeof usage !== "object") return
|
|
9687
|
-
const
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
10130
|
+
if (!usage || typeof usage !== "object" || Array.isArray(usage)) return null;
|
|
10131
|
+
const candidates = [];
|
|
10132
|
+
const hasDirect =
|
|
10133
|
+
Object.prototype.hasOwnProperty.call(usage, "cache_read_input_tokens") ||
|
|
10134
|
+
Object.prototype.hasOwnProperty.call(usage, "cache_read_tokens");
|
|
10135
|
+
if (hasDirect) {
|
|
10136
|
+
const direct = _optionalProviderUsageCount(
|
|
10137
|
+
usage,
|
|
10138
|
+
"cache_read_input_tokens",
|
|
10139
|
+
"cache_read_tokens",
|
|
10140
|
+
);
|
|
10141
|
+
if (direct == null) return null;
|
|
10142
|
+
candidates.push(direct);
|
|
10143
|
+
}
|
|
10144
|
+
|
|
10145
|
+
if (Object.prototype.hasOwnProperty.call(usage, "prompt_tokens_details")) {
|
|
10146
|
+
const details = usage.prompt_tokens_details;
|
|
10147
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
10148
|
+
return null;
|
|
10149
|
+
}
|
|
10150
|
+
if (Object.prototype.hasOwnProperty.call(details, "cached_tokens")) {
|
|
10151
|
+
if (!_isNonNegativeSafeInteger(details.cached_tokens)) return null;
|
|
10152
|
+
candidates.push(details.cached_tokens);
|
|
10153
|
+
}
|
|
10154
|
+
}
|
|
10155
|
+
|
|
10156
|
+
if (Object.prototype.hasOwnProperty.call(usage, "prompt_cache_hit_tokens")) {
|
|
10157
|
+
if (!_isNonNegativeSafeInteger(usage.prompt_cache_hit_tokens)) return null;
|
|
10158
|
+
candidates.push(usage.prompt_cache_hit_tokens);
|
|
10159
|
+
}
|
|
10160
|
+
|
|
10161
|
+
if (candidates.some((value) => value !== candidates[0])) return null;
|
|
10162
|
+
return candidates[0] ?? 0;
|
|
9692
10163
|
}
|
|
9693
10164
|
|
|
9694
10165
|
function _openaiInitState() {
|
|
9695
10166
|
return {
|
|
9696
10167
|
text: "",
|
|
9697
10168
|
tools: [],
|
|
9698
|
-
inputTokens:
|
|
9699
|
-
outputTokens:
|
|
10169
|
+
inputTokens: null,
|
|
10170
|
+
outputTokens: null,
|
|
9700
10171
|
cacheReadTokens: 0,
|
|
10172
|
+
usageInvalid: false,
|
|
9701
10173
|
};
|
|
9702
10174
|
}
|
|
9703
10175
|
|
|
@@ -9734,14 +10206,26 @@ function _openaiReduceLine(state, raw, onToken) {
|
|
|
9734
10206
|
state.tools[idx].args += tc.function.arguments;
|
|
9735
10207
|
}
|
|
9736
10208
|
}
|
|
9737
|
-
if (obj.usage) {
|
|
10209
|
+
if (obj.usage != null) {
|
|
10210
|
+
const usage = obj.usage;
|
|
10211
|
+
const prompt = _providerUsageCount(usage, "input_tokens", "prompt_tokens");
|
|
10212
|
+
const completion = _providerUsageCount(
|
|
10213
|
+
usage,
|
|
10214
|
+
"output_tokens",
|
|
10215
|
+
"completion_tokens",
|
|
10216
|
+
);
|
|
9738
10217
|
const cached = _openaiCachedTokens(obj.usage);
|
|
9739
|
-
|
|
9740
|
-
|
|
10218
|
+
if (
|
|
10219
|
+
!_hasCompleteProviderUsage(usage) ||
|
|
10220
|
+
cached == null ||
|
|
10221
|
+
cached > prompt
|
|
10222
|
+
) {
|
|
10223
|
+
state.usageInvalid = true;
|
|
10224
|
+
} else {
|
|
9741
10225
|
state.inputTokens = Math.max(0, prompt - cached);
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
10226
|
+
state.outputTokens = completion;
|
|
10227
|
+
state.cacheReadTokens = cached;
|
|
10228
|
+
}
|
|
9745
10229
|
}
|
|
9746
10230
|
return state;
|
|
9747
10231
|
}
|
|
@@ -9755,11 +10239,15 @@ function _openaiFinalize(state) {
|
|
|
9755
10239
|
const message = { role: "assistant", content: state.text };
|
|
9756
10240
|
if (toolCalls.length) message.tool_calls = toolCalls;
|
|
9757
10241
|
const out = { message };
|
|
9758
|
-
if (
|
|
10242
|
+
if (
|
|
10243
|
+
state.usageInvalid !== true &&
|
|
10244
|
+
_isNonNegativeSafeInteger(state.inputTokens) &&
|
|
10245
|
+
_isNonNegativeSafeInteger(state.outputTokens)
|
|
10246
|
+
) {
|
|
9759
10247
|
out.usage = {
|
|
9760
10248
|
input_tokens: state.inputTokens,
|
|
9761
10249
|
output_tokens: state.outputTokens,
|
|
9762
|
-
cache_read_input_tokens: state.cacheReadTokens
|
|
10250
|
+
cache_read_input_tokens: state.cacheReadTokens,
|
|
9763
10251
|
};
|
|
9764
10252
|
}
|
|
9765
10253
|
return out;
|
|
@@ -10119,11 +10607,293 @@ function _managedCheckpointUncoveredWriterReason(toolContext) {
|
|
|
10119
10607
|
* { type: "checkpoint", id, tool } — auto-checkpoint before a mutating tool
|
|
10120
10608
|
* { type: "tool-executing", tool, args }
|
|
10121
10609
|
* { type: "tool-result", tool, result, error }
|
|
10610
|
+
* { type: "model-usage-started", callId, provider, model, source }
|
|
10611
|
+
* { type: "token-usage", callId, provider, model, usage, source? }
|
|
10612
|
+
* { type: "model-usage-unknown", callId, provider, model, source, code }
|
|
10122
10613
|
* { type: "response-complete", content }
|
|
10123
10614
|
*
|
|
10124
10615
|
* @param {Array} messages - mutable messages array (will be appended to)
|
|
10125
10616
|
* @param {object} options - provider, model, baseUrl, apiKey, contextEngine, hookDb, skillLoader, cwd, slotFiller, interaction
|
|
10126
10617
|
*/
|
|
10618
|
+
const _autoCompactionUsageStates = new WeakMap();
|
|
10619
|
+
const _autoCompactorInstrumentation = new WeakMap();
|
|
10620
|
+
|
|
10621
|
+
function _newModelUsageCallId(source = "model") {
|
|
10622
|
+
// UUIDs are generated locally and contain no session/run/user material. The
|
|
10623
|
+
// fixed prefixes keep the identifier useful in diagnostics while remaining
|
|
10624
|
+
// far below the persisted ledger's 128-character ceiling.
|
|
10625
|
+
const prefix =
|
|
10626
|
+
source === "semantic-compaction"
|
|
10627
|
+
? "cmp"
|
|
10628
|
+
: source === "subagent"
|
|
10629
|
+
? "sub"
|
|
10630
|
+
: "mdl";
|
|
10631
|
+
return `${prefix}-${randomUUID()}`;
|
|
10632
|
+
}
|
|
10633
|
+
|
|
10634
|
+
function _runtimeUsageBoundaryFailure(error, code, fallbackMessage) {
|
|
10635
|
+
const failure =
|
|
10636
|
+
error && (typeof error === "object" || typeof error === "function")
|
|
10637
|
+
? error
|
|
10638
|
+
: new Error(fallbackMessage);
|
|
10639
|
+
try {
|
|
10640
|
+
failure.runtimeLedgerPersistence = true;
|
|
10641
|
+
if (!failure.code && code) failure.code = code;
|
|
10642
|
+
} catch {
|
|
10643
|
+
const wrapped = new Error(fallbackMessage, { cause: error });
|
|
10644
|
+
wrapped.runtimeLedgerPersistence = true;
|
|
10645
|
+
wrapped.code = code;
|
|
10646
|
+
return wrapped;
|
|
10647
|
+
}
|
|
10648
|
+
return failure;
|
|
10649
|
+
}
|
|
10650
|
+
|
|
10651
|
+
/**
|
|
10652
|
+
* Strict child loops are consumed inside executeTool, so their yielded model
|
|
10653
|
+
* boundaries cannot reach the parent host before provider work. Require the
|
|
10654
|
+
* parent's synchronous persistence observer and prewrite one conservative
|
|
10655
|
+
* subagent call. The matching unknown event is forwarded after child settlement.
|
|
10656
|
+
*/
|
|
10657
|
+
function _notifyStrictUsageBoundary(observer, boundary) {
|
|
10658
|
+
if (typeof observer !== "function") {
|
|
10659
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10660
|
+
null,
|
|
10661
|
+
"CC_USAGE_BOUNDARY_OBSERVER_REQUIRED",
|
|
10662
|
+
"Strict child usage telemetry requires a synchronous boundary observer",
|
|
10663
|
+
);
|
|
10664
|
+
}
|
|
10665
|
+
let observation;
|
|
10666
|
+
try {
|
|
10667
|
+
observation = observer(boundary);
|
|
10668
|
+
} catch (error) {
|
|
10669
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10670
|
+
error,
|
|
10671
|
+
"CC_USAGE_BOUNDARY_PERSISTENCE_FAILED",
|
|
10672
|
+
"Child usage boundary persistence failed",
|
|
10673
|
+
);
|
|
10674
|
+
}
|
|
10675
|
+
let isThenable = false;
|
|
10676
|
+
try {
|
|
10677
|
+
isThenable = Boolean(observation && typeof observation.then === "function");
|
|
10678
|
+
} catch (error) {
|
|
10679
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10680
|
+
error,
|
|
10681
|
+
"CC_USAGE_BOUNDARY_OBSERVER_ASYNC",
|
|
10682
|
+
"Child usage boundary observer must be synchronous",
|
|
10683
|
+
);
|
|
10684
|
+
}
|
|
10685
|
+
if (isThenable) {
|
|
10686
|
+
void Promise.resolve(observation).catch(() => {});
|
|
10687
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10688
|
+
null,
|
|
10689
|
+
"CC_USAGE_BOUNDARY_OBSERVER_ASYNC",
|
|
10690
|
+
"Child usage boundary observer must be synchronous",
|
|
10691
|
+
);
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
|
|
10695
|
+
function _notifyStrictUsageSettlement(observer, settlement) {
|
|
10696
|
+
if (typeof observer !== "function") {
|
|
10697
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10698
|
+
null,
|
|
10699
|
+
"CC_USAGE_SETTLEMENT_OBSERVER_REQUIRED",
|
|
10700
|
+
"Strict child usage telemetry requires a synchronous settlement observer",
|
|
10701
|
+
);
|
|
10702
|
+
}
|
|
10703
|
+
let observation;
|
|
10704
|
+
try {
|
|
10705
|
+
observation = observer(settlement);
|
|
10706
|
+
} catch (error) {
|
|
10707
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10708
|
+
error,
|
|
10709
|
+
"CC_USAGE_SETTLEMENT_PERSISTENCE_FAILED",
|
|
10710
|
+
"Child usage settlement persistence failed",
|
|
10711
|
+
);
|
|
10712
|
+
}
|
|
10713
|
+
let isThenable = false;
|
|
10714
|
+
try {
|
|
10715
|
+
isThenable = Boolean(observation && typeof observation.then === "function");
|
|
10716
|
+
} catch (error) {
|
|
10717
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10718
|
+
error,
|
|
10719
|
+
"CC_USAGE_SETTLEMENT_OBSERVER_ASYNC",
|
|
10720
|
+
"Child usage settlement observer must be synchronous",
|
|
10721
|
+
);
|
|
10722
|
+
}
|
|
10723
|
+
if (isThenable) {
|
|
10724
|
+
void Promise.resolve(observation).catch(() => {});
|
|
10725
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10726
|
+
null,
|
|
10727
|
+
"CC_USAGE_SETTLEMENT_OBSERVER_ASYNC",
|
|
10728
|
+
"Child usage settlement observer must be synchronous",
|
|
10729
|
+
);
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
|
|
10733
|
+
function _notifyStrictToolObserver(observer, event, phase) {
|
|
10734
|
+
const upper = phase.toUpperCase();
|
|
10735
|
+
if (typeof observer !== "function") {
|
|
10736
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10737
|
+
null,
|
|
10738
|
+
`CC_TOOL_${upper}_OBSERVER_REQUIRED`,
|
|
10739
|
+
`Strict child tool ${phase} requires a synchronous persistence observer`,
|
|
10740
|
+
);
|
|
10741
|
+
}
|
|
10742
|
+
let observation;
|
|
10743
|
+
try {
|
|
10744
|
+
observation = observer(event);
|
|
10745
|
+
} catch (error) {
|
|
10746
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10747
|
+
error,
|
|
10748
|
+
`CC_TOOL_${upper}_PERSISTENCE_FAILED`,
|
|
10749
|
+
`Child tool ${phase} persistence failed`,
|
|
10750
|
+
);
|
|
10751
|
+
}
|
|
10752
|
+
if (observation && typeof observation.then === "function") {
|
|
10753
|
+
void Promise.resolve(observation).catch(() => {});
|
|
10754
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10755
|
+
null,
|
|
10756
|
+
`CC_TOOL_${upper}_OBSERVER_ASYNC`,
|
|
10757
|
+
`Child tool ${phase} observer must be synchronous`,
|
|
10758
|
+
);
|
|
10759
|
+
}
|
|
10760
|
+
}
|
|
10761
|
+
|
|
10762
|
+
function _notifyStrictRetryObserver(observer, args) {
|
|
10763
|
+
if (typeof observer !== "function") {
|
|
10764
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10765
|
+
null,
|
|
10766
|
+
"CC_RETRY_OBSERVER_REQUIRED",
|
|
10767
|
+
"Strict child streaming retry requires a synchronous persistence observer",
|
|
10768
|
+
);
|
|
10769
|
+
}
|
|
10770
|
+
let observation;
|
|
10771
|
+
try {
|
|
10772
|
+
observation = observer(...args);
|
|
10773
|
+
} catch (error) {
|
|
10774
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10775
|
+
error,
|
|
10776
|
+
"CC_RETRY_PERSISTENCE_FAILED",
|
|
10777
|
+
"Child streaming retry persistence failed",
|
|
10778
|
+
);
|
|
10779
|
+
}
|
|
10780
|
+
if (observation && typeof observation.then === "function") {
|
|
10781
|
+
void Promise.resolve(observation).catch(() => {});
|
|
10782
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10783
|
+
null,
|
|
10784
|
+
"CC_RETRY_OBSERVER_ASYNC",
|
|
10785
|
+
"Child streaming retry observer must be synchronous",
|
|
10786
|
+
);
|
|
10787
|
+
}
|
|
10788
|
+
}
|
|
10789
|
+
|
|
10790
|
+
function _assertStrictUsageObserver(observer, phase) {
|
|
10791
|
+
if (typeof observer !== "function") {
|
|
10792
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10793
|
+
null,
|
|
10794
|
+
`CC_USAGE_${phase.toUpperCase()}_OBSERVER_REQUIRED`,
|
|
10795
|
+
`Strict child usage telemetry requires a synchronous ${phase} observer`,
|
|
10796
|
+
);
|
|
10797
|
+
}
|
|
10798
|
+
}
|
|
10799
|
+
|
|
10800
|
+
function _autoCompactionUsageState(options) {
|
|
10801
|
+
let state = _autoCompactionUsageStates.get(options);
|
|
10802
|
+
if (!state) {
|
|
10803
|
+
state = { calls: [] };
|
|
10804
|
+
_autoCompactionUsageStates.set(options, state);
|
|
10805
|
+
}
|
|
10806
|
+
return state;
|
|
10807
|
+
}
|
|
10808
|
+
|
|
10809
|
+
/**
|
|
10810
|
+
* Wrap only the compressor's actual provider-query seam. `shouldAutoCompact`,
|
|
10811
|
+
* microcompact, and extractive/count-only compaction never enter this wrapper,
|
|
10812
|
+
* so they cannot manufacture a billable-call boundary.
|
|
10813
|
+
*/
|
|
10814
|
+
function _instrumentAutoCompactorUsage(compactor, options) {
|
|
10815
|
+
if (!compactor || typeof compactor.llmQuery !== "function") return compactor;
|
|
10816
|
+
const installed = _autoCompactorInstrumentation.get(compactor);
|
|
10817
|
+
if (
|
|
10818
|
+
installed?.options === options &&
|
|
10819
|
+
compactor.llmQuery === installed.wrapper
|
|
10820
|
+
) {
|
|
10821
|
+
return compactor;
|
|
10822
|
+
}
|
|
10823
|
+
|
|
10824
|
+
const original = installed?.original || compactor.llmQuery;
|
|
10825
|
+
const state = _autoCompactionUsageState(options);
|
|
10826
|
+
const wrapper = async function instrumentedCompactionQuery(...args) {
|
|
10827
|
+
const call = {
|
|
10828
|
+
callId: _newModelUsageCallId("semantic-compaction"),
|
|
10829
|
+
provider: options.provider || null,
|
|
10830
|
+
model: options.model || null,
|
|
10831
|
+
source: "semantic-compaction",
|
|
10832
|
+
observerError: null,
|
|
10833
|
+
observerFailed: false,
|
|
10834
|
+
boundaryNotified: false,
|
|
10835
|
+
};
|
|
10836
|
+
state.calls.push(call);
|
|
10837
|
+
try {
|
|
10838
|
+
if (typeof options.onUsageBoundary === "function") {
|
|
10839
|
+
// Synchronous and before `original`: a host can durably append the
|
|
10840
|
+
// start row, and any append failure prevents provider spend.
|
|
10841
|
+
const observation = options.onUsageBoundary({
|
|
10842
|
+
type: "model-usage-started",
|
|
10843
|
+
callId: call.callId,
|
|
10844
|
+
provider: call.provider,
|
|
10845
|
+
model: call.model,
|
|
10846
|
+
source: call.source,
|
|
10847
|
+
});
|
|
10848
|
+
if (observation && typeof observation.then === "function") {
|
|
10849
|
+
void Promise.resolve(observation).catch(() => {});
|
|
10850
|
+
const error = new Error(
|
|
10851
|
+
"Automatic compaction usage boundary observer must be synchronous",
|
|
10852
|
+
);
|
|
10853
|
+
error.code = "CC_USAGE_BOUNDARY_OBSERVER_ASYNC";
|
|
10854
|
+
throw error;
|
|
10855
|
+
}
|
|
10856
|
+
call.boundaryNotified = true;
|
|
10857
|
+
}
|
|
10858
|
+
} catch (error) {
|
|
10859
|
+
call.observerError = error;
|
|
10860
|
+
call.observerFailed = true;
|
|
10861
|
+
throw error;
|
|
10862
|
+
}
|
|
10863
|
+
|
|
10864
|
+
return await original.apply(this, args);
|
|
10865
|
+
};
|
|
10866
|
+
|
|
10867
|
+
try {
|
|
10868
|
+
compactor.llmQuery = wrapper;
|
|
10869
|
+
} catch (error) {
|
|
10870
|
+
if (options.strictUsageTelemetry === true) {
|
|
10871
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10872
|
+
error,
|
|
10873
|
+
"CC_COMPACTION_USAGE_INSTRUMENTATION_REQUIRED",
|
|
10874
|
+
"Strict semantic compaction requires an instrumentable provider boundary",
|
|
10875
|
+
);
|
|
10876
|
+
}
|
|
10877
|
+
// A frozen injected compactor cannot be instrumented safely. Production
|
|
10878
|
+
// PromptCompressor instances are mutable; legacy injected fakes stay as-is.
|
|
10879
|
+
return compactor;
|
|
10880
|
+
}
|
|
10881
|
+
if (compactor.llmQuery === wrapper) {
|
|
10882
|
+
_autoCompactorInstrumentation.set(compactor, {
|
|
10883
|
+
original,
|
|
10884
|
+
options,
|
|
10885
|
+
wrapper,
|
|
10886
|
+
});
|
|
10887
|
+
} else if (options.strictUsageTelemetry === true) {
|
|
10888
|
+
throw _runtimeUsageBoundaryFailure(
|
|
10889
|
+
null,
|
|
10890
|
+
"CC_COMPACTION_USAGE_INSTRUMENTATION_REQUIRED",
|
|
10891
|
+
"Strict semantic compaction requires an instrumentable provider boundary",
|
|
10892
|
+
);
|
|
10893
|
+
}
|
|
10894
|
+
return compactor;
|
|
10895
|
+
}
|
|
10896
|
+
|
|
10127
10897
|
/**
|
|
10128
10898
|
* Lazily build (and cache on `options`) the PromptCompressor used for in-loop
|
|
10129
10899
|
* auto-compaction. Returns null when the feature is off or the module can't be
|
|
@@ -10132,7 +10902,7 @@ function _managedCheckpointUncoveredWriterReason(toolContext) {
|
|
|
10132
10902
|
*/
|
|
10133
10903
|
async function _getAutoCompactor(options) {
|
|
10134
10904
|
if (Object.prototype.hasOwnProperty.call(options, "_autoCompactor")) {
|
|
10135
|
-
return options._autoCompactor;
|
|
10905
|
+
return _instrumentAutoCompactorUsage(options._autoCompactor, options);
|
|
10136
10906
|
}
|
|
10137
10907
|
let compressor = null;
|
|
10138
10908
|
try {
|
|
@@ -10180,6 +10950,7 @@ async function _getAutoCompactor(options) {
|
|
|
10180
10950
|
llmQuery,
|
|
10181
10951
|
summaryInputMaxChars: options.compactionInputMaxChars,
|
|
10182
10952
|
});
|
|
10953
|
+
compressor = _instrumentAutoCompactorUsage(compressor, options);
|
|
10183
10954
|
}
|
|
10184
10955
|
} catch {
|
|
10185
10956
|
compressor = null;
|
|
@@ -10194,19 +10965,38 @@ async function _getAutoCompactor(options) {
|
|
|
10194
10965
|
|
|
10195
10966
|
function _compactionTokenUsage(stats) {
|
|
10196
10967
|
const summaryUsage = stats?.summaryUsage;
|
|
10197
|
-
if (
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
|
|
10968
|
+
if (
|
|
10969
|
+
!summaryUsage ||
|
|
10970
|
+
typeof summaryUsage !== "object" ||
|
|
10971
|
+
Array.isArray(summaryUsage) ||
|
|
10972
|
+
!_isNonNegativeSafeInteger(summaryUsage.inputTokens) ||
|
|
10973
|
+
!_isNonNegativeSafeInteger(summaryUsage.outputTokens)
|
|
10974
|
+
) {
|
|
10975
|
+
return null;
|
|
10976
|
+
}
|
|
10977
|
+
const optionalTokenCount = (key) => {
|
|
10978
|
+
if (!Object.prototype.hasOwnProperty.call(summaryUsage, key)) return 0;
|
|
10979
|
+
return _isNonNegativeSafeInteger(summaryUsage[key])
|
|
10980
|
+
? summaryUsage[key]
|
|
10981
|
+
: null;
|
|
10201
10982
|
};
|
|
10983
|
+
const cacheReadTokens = optionalTokenCount("cacheReadTokens");
|
|
10984
|
+
const cacheCreationTokens = optionalTokenCount("cacheCreationTokens");
|
|
10985
|
+
if (cacheReadTokens == null || cacheCreationTokens == null) return null;
|
|
10202
10986
|
return {
|
|
10203
|
-
input_tokens:
|
|
10204
|
-
output_tokens:
|
|
10205
|
-
cache_read_input_tokens:
|
|
10206
|
-
cache_creation_input_tokens:
|
|
10987
|
+
input_tokens: summaryUsage.inputTokens,
|
|
10988
|
+
output_tokens: summaryUsage.outputTokens,
|
|
10989
|
+
cache_read_input_tokens: cacheReadTokens,
|
|
10990
|
+
cache_creation_input_tokens: cacheCreationTokens,
|
|
10207
10991
|
};
|
|
10208
10992
|
}
|
|
10209
10993
|
|
|
10994
|
+
function _compactionUsageUnknownReason(stats) {
|
|
10995
|
+
return stats?.summaryUsageUnknownReason === "provider_usage_not_reported"
|
|
10996
|
+
? "provider_usage_not_reported"
|
|
10997
|
+
: "provider_transport_outcome_unknown";
|
|
10998
|
+
}
|
|
10999
|
+
|
|
10210
11000
|
function _sameMessageSnapshot(messages, expectedMessages) {
|
|
10211
11001
|
return (
|
|
10212
11002
|
messages.length === expectedMessages.length &&
|
|
@@ -10519,7 +11309,30 @@ export async function* agentLoop(messages, options) {
|
|
|
10519
11309
|
model: options.model || null,
|
|
10520
11310
|
baseUrl: options.baseUrl || null,
|
|
10521
11311
|
apiKey: options.apiKey || null,
|
|
11312
|
+
...(typeof options.onUsageBoundary === "function"
|
|
11313
|
+
? { onUsageBoundary: options.onUsageBoundary }
|
|
11314
|
+
: {}),
|
|
11315
|
+
...(typeof options.onUsageSettlement === "function"
|
|
11316
|
+
? { onUsageSettlement: options.onUsageSettlement }
|
|
11317
|
+
: {}),
|
|
11318
|
+
...(typeof options.onStreamRetry === "function"
|
|
11319
|
+
? { onStreamRetry: options.onStreamRetry }
|
|
11320
|
+
: {}),
|
|
11321
|
+
...(typeof options.onToolCallBoundary === "function"
|
|
11322
|
+
? { onToolCallBoundary: options.onToolCallBoundary }
|
|
11323
|
+
: {}),
|
|
11324
|
+
...(typeof options.onToolCallSettlement === "function"
|
|
11325
|
+
? { onToolCallSettlement: options.onToolCallSettlement }
|
|
11326
|
+
: {}),
|
|
11327
|
+
...(options.strictUsageTelemetry === true
|
|
11328
|
+
? { strictUsageTelemetry: true }
|
|
11329
|
+
: {}),
|
|
10522
11330
|
},
|
|
11331
|
+
strictUsageTelemetry: options.strictUsageTelemetry === true,
|
|
11332
|
+
onUsageBoundary: options.onUsageBoundary || null,
|
|
11333
|
+
onUsageSettlement: options.onUsageSettlement || null,
|
|
11334
|
+
onToolCallBoundary: options.onToolCallBoundary || null,
|
|
11335
|
+
onToolCallSettlement: options.onToolCallSettlement || null,
|
|
10523
11336
|
parentMessages: messages, // pass parent messages for sub-agent auto-condensation
|
|
10524
11337
|
interaction: options.interaction || null,
|
|
10525
11338
|
shellPolicyOverrides: options.shellPolicyOverrides || null,
|
|
@@ -10583,6 +11396,10 @@ export async function* agentLoop(messages, options) {
|
|
|
10583
11396
|
// any are still running (it waits, injects, and gives the model one more
|
|
10584
11397
|
// turn) so a background result can never be silently lost.
|
|
10585
11398
|
backgroundSubAgents: new Map(),
|
|
11399
|
+
// A background child reports strict observer failure synchronously here,
|
|
11400
|
+
// before its promise rejection necessarily reaches the entry's `settled`
|
|
11401
|
+
// continuation. Provider/tool admission fences consult this latch directly.
|
|
11402
|
+
backgroundUsageFailureState: { error: null },
|
|
10586
11403
|
// 用量归因: per-run sink for child-loop (spawn_sub_agent / isolated
|
|
10587
11404
|
// run_skill) token usage. Child loops consume their own generator events,
|
|
10588
11405
|
// so their real usage never reaches this loop's consumers — the spawn
|
|
@@ -10594,6 +11411,7 @@ export async function* agentLoop(messages, options) {
|
|
|
10594
11411
|
: [],
|
|
10595
11412
|
};
|
|
10596
11413
|
const backgroundSubAgents = toolContext.backgroundSubAgents;
|
|
11414
|
+
const backgroundUsageFailureState = toolContext.backgroundUsageFailureState;
|
|
10597
11415
|
const subAgentUsageSink = toolContext.subAgentUsageSink;
|
|
10598
11416
|
const lifecycleHookEmitter = hermeticExecution ? () => {} : emitHooksV2Event;
|
|
10599
11417
|
|
|
@@ -10658,7 +11476,15 @@ export async function* agentLoop(messages, options) {
|
|
|
10658
11476
|
// wrong / expired, self-heal to a provider we can actually run (endpoint-
|
|
10659
11477
|
// inferred, then env-keyed) instead of failing the turn. Opt out with
|
|
10660
11478
|
// `runnableProviderFallback: false`. Transparent on the happy path.
|
|
10661
|
-
|
|
11479
|
+
// A runnable fallback may perform an additional (and cross-provider) model
|
|
11480
|
+
// attempt inside one logical `llmCall`, where this generator cannot durably
|
|
11481
|
+
// bracket each transport attempt. Strict call-ledger sessions therefore
|
|
11482
|
+
// disable that recovery path and surface the configured provider's failure
|
|
11483
|
+
// as unknown; legacy sessions keep the existing self-healing behavior.
|
|
11484
|
+
if (
|
|
11485
|
+
options.runnableProviderFallback !== false &&
|
|
11486
|
+
options.strictUsageTelemetry !== true
|
|
11487
|
+
) {
|
|
10662
11488
|
const { makeRunnableProviderFallback } =
|
|
10663
11489
|
await import("../lib/runnable-provider.js");
|
|
10664
11490
|
llmCall = makeRunnableProviderFallback(llmCall, {
|
|
@@ -10742,6 +11568,12 @@ export async function* agentLoop(messages, options) {
|
|
|
10742
11568
|
budget.consume();
|
|
10743
11569
|
throwIfAborted(signal);
|
|
10744
11570
|
|
|
11571
|
+
// A detached child may fail its synchronous ledger write between parent
|
|
11572
|
+
// turns. Surface that original marked error before any further paid parent
|
|
11573
|
+
// provider call is admitted.
|
|
11574
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
11575
|
+
_throwSettledBackgroundUsageFailure(backgroundSubAgents);
|
|
11576
|
+
|
|
10745
11577
|
// Surface attributed child-loop usage collected since the last boundary
|
|
10746
11578
|
// (blocking spawns push during executeTool; background spawns push live).
|
|
10747
11579
|
yield* _drainSubAgentUsage(subAgentUsageSink);
|
|
@@ -10787,9 +11619,14 @@ export async function* agentLoop(messages, options) {
|
|
|
10787
11619
|
!automaticCompactionSettlementBlocked &&
|
|
10788
11620
|
messages.length > 4
|
|
10789
11621
|
) {
|
|
11622
|
+
let compactionUsageState = null;
|
|
11623
|
+
let compactionUsageCall = null;
|
|
11624
|
+
let compactionObserverFailure = null;
|
|
10790
11625
|
try {
|
|
10791
11626
|
const compactor = await _getAutoCompactor(options);
|
|
10792
11627
|
if (compactor && compactor.shouldAutoCompact(messages)) {
|
|
11628
|
+
compactionUsageState = _autoCompactionUsageState(options);
|
|
11629
|
+
compactionUsageState.calls = [];
|
|
10793
11630
|
// This is the canonical pre-provider authority snapshot. A local
|
|
10794
11631
|
// micro-compact may change the working input, but the durable CAS must
|
|
10795
11632
|
// still compare against the transcript state that existed before any
|
|
@@ -10876,36 +11713,88 @@ export async function* agentLoop(messages, options) {
|
|
|
10876
11713
|
preserveToolPairs: true,
|
|
10877
11714
|
...pinOpts,
|
|
10878
11715
|
});
|
|
11716
|
+
const observerFailure = compactionUsageState.calls.find(
|
|
11717
|
+
(call) => call.observerFailed,
|
|
11718
|
+
);
|
|
11719
|
+
if (observerFailure) {
|
|
11720
|
+
compactionObserverFailure = observerFailure.observerError;
|
|
11721
|
+
compactionUsageState.calls = [];
|
|
11722
|
+
throw compactionObserverFailure;
|
|
11723
|
+
}
|
|
11724
|
+
compactionUsageCall = compactionUsageState.calls.at(-1) || null;
|
|
10879
11725
|
const compactionUsage = _compactionTokenUsage(stats);
|
|
10880
11726
|
if (stats.degraded === true) {
|
|
11727
|
+
const usageOutcomeUnknown = stats.summaryUsageUnknown === true;
|
|
11728
|
+
const projectedStats = usageOutcomeUnknown
|
|
11729
|
+
? {
|
|
11730
|
+
...stats,
|
|
11731
|
+
degradedReason: "semantic-summary-provider-outcome-unknown",
|
|
11732
|
+
summaryUsageUnknownReason:
|
|
11733
|
+
_compactionUsageUnknownReason(stats),
|
|
11734
|
+
}
|
|
11735
|
+
: stats;
|
|
10881
11736
|
yield {
|
|
10882
11737
|
type: "compaction-degraded",
|
|
10883
11738
|
runId,
|
|
10884
|
-
reason:
|
|
11739
|
+
reason: usageOutcomeUnknown
|
|
11740
|
+
? "semantic-summary-provider-outcome-unknown"
|
|
11741
|
+
: stats.degradedReason || "semantic-summary-degraded",
|
|
10885
11742
|
summaryMode: stats.summaryMode || "extractive-fallback",
|
|
10886
|
-
stats,
|
|
11743
|
+
stats: projectedStats,
|
|
10887
11744
|
};
|
|
10888
11745
|
}
|
|
11746
|
+
const compactionUsageMissing =
|
|
11747
|
+
stats.summaryUsageUnknown !== true &&
|
|
11748
|
+
compactionUsageCall != null &&
|
|
11749
|
+
compactionUsage == null;
|
|
11750
|
+
if (stats.summaryUsageUnknown === true || compactionUsageMissing) {
|
|
11751
|
+
automaticCompactionSettlementBlocked = true;
|
|
11752
|
+
const unknownReason = compactionUsageMissing
|
|
11753
|
+
? "provider_usage_not_reported"
|
|
11754
|
+
: _compactionUsageUnknownReason(stats);
|
|
11755
|
+
if (
|
|
11756
|
+
options.strictUsageTelemetry === true &&
|
|
11757
|
+
!compactionUsageCall?.callId
|
|
11758
|
+
) {
|
|
11759
|
+
throw _runtimeUsageBoundaryFailure(
|
|
11760
|
+
null,
|
|
11761
|
+
"CC_COMPACTION_USAGE_BOUNDARY_MISSING",
|
|
11762
|
+
"Strict semantic compaction produced usage without a real call boundary",
|
|
11763
|
+
);
|
|
11764
|
+
}
|
|
11765
|
+
yield {
|
|
11766
|
+
type: "compaction-usage-unknown",
|
|
11767
|
+
callId:
|
|
11768
|
+
compactionUsageCall?.callId ||
|
|
11769
|
+
_newModelUsageCallId("semantic-compaction"),
|
|
11770
|
+
runId,
|
|
11771
|
+
provider:
|
|
11772
|
+
compactionUsageCall?.boundaryNotified === true
|
|
11773
|
+
? compactionUsageCall.provider
|
|
11774
|
+
: stats.summaryProvider || options.provider || null,
|
|
11775
|
+
model:
|
|
11776
|
+
compactionUsageCall?.boundaryNotified === true
|
|
11777
|
+
? compactionUsageCall.model
|
|
11778
|
+
: stats.summaryModel || options.model || null,
|
|
11779
|
+
reason: unknownReason,
|
|
11780
|
+
code:
|
|
11781
|
+
unknownReason === "provider_usage_not_reported"
|
|
11782
|
+
? "provider_usage_missing"
|
|
11783
|
+
: "provider_call_failed",
|
|
11784
|
+
source: "semantic-compaction",
|
|
11785
|
+
};
|
|
11786
|
+
// Unknown compaction spend makes every hard budget unverifiable;
|
|
11787
|
+
// never apply the fallback or initiate the main model call.
|
|
11788
|
+
await _awaitBackgroundUsageSettlement(
|
|
11789
|
+
backgroundSubAgents,
|
|
11790
|
+
backgroundUsageFailureState,
|
|
11791
|
+
);
|
|
11792
|
+
return;
|
|
11793
|
+
}
|
|
10889
11794
|
if (
|
|
10890
11795
|
stats.saved > 0 &&
|
|
10891
11796
|
!_sameMessageSnapshot(compacted, authorityExpectedMessages)
|
|
10892
11797
|
) {
|
|
10893
|
-
if (stats.summaryUsageUnknown === true) {
|
|
10894
|
-
automaticCompactionSettlementBlocked = true;
|
|
10895
|
-
yield {
|
|
10896
|
-
type: "compaction-usage-unknown",
|
|
10897
|
-
runId,
|
|
10898
|
-
provider: stats.summaryProvider || options.provider || null,
|
|
10899
|
-
model: stats.summaryModel || options.model || null,
|
|
10900
|
-
reason:
|
|
10901
|
-
stats.summaryUsageUnknownReason ||
|
|
10902
|
-
"provider_transport_outcome_unknown",
|
|
10903
|
-
source: "semantic-compaction",
|
|
10904
|
-
};
|
|
10905
|
-
// Do not apply the fallback or start another paid model request.
|
|
10906
|
-
// A transport failure may already have incurred unknown usage.
|
|
10907
|
-
return;
|
|
10908
|
-
}
|
|
10909
11798
|
const liveExpectedMessages = [...messages];
|
|
10910
11799
|
try {
|
|
10911
11800
|
await _settleAutomaticCompaction({
|
|
@@ -10931,8 +11820,17 @@ export async function* agentLoop(messages, options) {
|
|
|
10931
11820
|
if (compactionUsage) {
|
|
10932
11821
|
yield {
|
|
10933
11822
|
type: "token-usage",
|
|
10934
|
-
|
|
10935
|
-
|
|
11823
|
+
...(compactionUsageCall
|
|
11824
|
+
? { callId: compactionUsageCall.callId }
|
|
11825
|
+
: {}),
|
|
11826
|
+
provider:
|
|
11827
|
+
compactionUsageCall?.boundaryNotified === true
|
|
11828
|
+
? compactionUsageCall.provider
|
|
11829
|
+
: stats.summaryProvider || options.provider || null,
|
|
11830
|
+
model:
|
|
11831
|
+
compactionUsageCall?.boundaryNotified === true
|
|
11832
|
+
? compactionUsageCall.model
|
|
11833
|
+
: stats.summaryModel || options.model || null,
|
|
10936
11834
|
usage: compactionUsage,
|
|
10937
11835
|
source: "semantic-compaction",
|
|
10938
11836
|
runId,
|
|
@@ -10940,6 +11838,10 @@ export async function* agentLoop(messages, options) {
|
|
|
10940
11838
|
}
|
|
10941
11839
|
// The paid summary call is never retried, and an unknown/stale
|
|
10942
11840
|
// canonical settlement cannot be followed by another model call.
|
|
11841
|
+
await _awaitBackgroundUsageSettlement(
|
|
11842
|
+
backgroundSubAgents,
|
|
11843
|
+
backgroundUsageFailureState,
|
|
11844
|
+
);
|
|
10943
11845
|
return;
|
|
10944
11846
|
}
|
|
10945
11847
|
if (!hermeticExecution) {
|
|
@@ -10967,8 +11869,17 @@ export async function* agentLoop(messages, options) {
|
|
|
10967
11869
|
// next model call, without entering the normal response path.
|
|
10968
11870
|
yield {
|
|
10969
11871
|
type: "token-usage",
|
|
10970
|
-
|
|
10971
|
-
|
|
11872
|
+
...(compactionUsageCall
|
|
11873
|
+
? { callId: compactionUsageCall.callId }
|
|
11874
|
+
: {}),
|
|
11875
|
+
provider:
|
|
11876
|
+
compactionUsageCall?.boundaryNotified === true
|
|
11877
|
+
? compactionUsageCall.provider
|
|
11878
|
+
: stats.summaryProvider || options.provider || null,
|
|
11879
|
+
model:
|
|
11880
|
+
compactionUsageCall?.boundaryNotified === true
|
|
11881
|
+
? compactionUsageCall.model
|
|
11882
|
+
: stats.summaryModel || options.model || null,
|
|
10972
11883
|
usage: compactionUsage,
|
|
10973
11884
|
source: "semantic-compaction",
|
|
10974
11885
|
runId,
|
|
@@ -10977,6 +11888,35 @@ export async function* agentLoop(messages, options) {
|
|
|
10977
11888
|
}
|
|
10978
11889
|
} catch (_e) {
|
|
10979
11890
|
if (isAbortError(_e) || signal?.aborted) throw _e;
|
|
11891
|
+
if (_e?.runtimeLedgerPersistence === true) throw _e;
|
|
11892
|
+
if (compactionObserverFailure === _e) throw _e;
|
|
11893
|
+
const observerFailure = compactionUsageState?.calls?.find(
|
|
11894
|
+
(call) => call.observerFailed && call.observerError === _e,
|
|
11895
|
+
);
|
|
11896
|
+
if (observerFailure) {
|
|
11897
|
+
compactionUsageState.calls = [];
|
|
11898
|
+
throw _e;
|
|
11899
|
+
}
|
|
11900
|
+
compactionUsageCall =
|
|
11901
|
+
compactionUsageCall || compactionUsageState?.calls?.at(-1) || null;
|
|
11902
|
+
if (compactionUsageCall) {
|
|
11903
|
+
automaticCompactionSettlementBlocked = true;
|
|
11904
|
+
yield {
|
|
11905
|
+
type: "compaction-usage-unknown",
|
|
11906
|
+
callId: compactionUsageCall.callId,
|
|
11907
|
+
runId,
|
|
11908
|
+
provider: compactionUsageCall.provider,
|
|
11909
|
+
model: compactionUsageCall.model,
|
|
11910
|
+
reason: "provider_transport_outcome_unknown",
|
|
11911
|
+
code: "provider_call_failed",
|
|
11912
|
+
source: "semantic-compaction",
|
|
11913
|
+
};
|
|
11914
|
+
await _awaitBackgroundUsageSettlement(
|
|
11915
|
+
backgroundSubAgents,
|
|
11916
|
+
backgroundUsageFailureState,
|
|
11917
|
+
);
|
|
11918
|
+
return;
|
|
11919
|
+
}
|
|
10980
11920
|
yield {
|
|
10981
11921
|
type: "compaction-degraded",
|
|
10982
11922
|
runId,
|
|
@@ -10994,6 +11934,7 @@ export async function* agentLoop(messages, options) {
|
|
|
10994
11934
|
for (const entry of _takeSettledBackgroundSubAgents(
|
|
10995
11935
|
backgroundSubAgents,
|
|
10996
11936
|
)) {
|
|
11937
|
+
_throwBackgroundSubAgentUsageFailure(entry);
|
|
10997
11938
|
// SubagentStop fires at RESULT time for background spawns (the
|
|
10998
11939
|
// spawn-time handle skips it); a block reason rides along as feedback.
|
|
10999
11940
|
if (!hermeticExecution && options.settingsHooks) {
|
|
@@ -11033,6 +11974,12 @@ export async function* agentLoop(messages, options) {
|
|
|
11033
11974
|
}
|
|
11034
11975
|
}
|
|
11035
11976
|
|
|
11977
|
+
// Result-delivery hooks and context preparation can yield long enough for
|
|
11978
|
+
// a detached child's synchronous ledger failure to settle. Keep the final
|
|
11979
|
+
// dispatch fence adjacent to construction of the paid parent request.
|
|
11980
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
11981
|
+
_throwSettledBackgroundUsageFailure(backgroundSubAgents);
|
|
11982
|
+
|
|
11036
11983
|
// Turn-scoped context injection (open-agents prepareCall parity).
|
|
11037
11984
|
// prepareCall runs fresh each iteration and returns an ephemeral
|
|
11038
11985
|
// system-message supplement that is NOT persisted to messages history.
|
|
@@ -11071,67 +12018,139 @@ export async function* agentLoop(messages, options) {
|
|
|
11071
12018
|
promptId: `${runId}:t${budget.consumed}:p`,
|
|
11072
12019
|
})
|
|
11073
12020
|
: null;
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
12021
|
+
throwIfAborted(signal);
|
|
12022
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
12023
|
+
_throwSettledBackgroundUsageFailure(backgroundSubAgents);
|
|
12024
|
+
const modelUsageCall = {
|
|
12025
|
+
type: "model-usage-started",
|
|
12026
|
+
callId: _newModelUsageCallId("model"),
|
|
12027
|
+
provider: options.provider || "ollama",
|
|
12028
|
+
model: options.model || "unknown",
|
|
12029
|
+
source: "model",
|
|
12030
|
+
};
|
|
12031
|
+
// The consumer receives this boundary before `.next()` resumes into the
|
|
12032
|
+
// provider call, so a durable append failure can prevent spend.
|
|
12033
|
+
yield modelUsageCall;
|
|
12034
|
+
// A background child can fail its synchronous settlement write while the
|
|
12035
|
+
// parent boundary is being persisted by the host. Re-check at the last
|
|
12036
|
+
// synchronous admission point before dispatching this paid call.
|
|
12037
|
+
try {
|
|
12038
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
12039
|
+
_throwSettledBackgroundUsageFailure(backgroundSubAgents);
|
|
12040
|
+
} catch (backgroundFailure) {
|
|
12041
|
+
// The host has already persisted this parent boundary. Close it with the
|
|
12042
|
+
// same id before surfacing the detached child's authoritative failure.
|
|
12043
|
+
yield {
|
|
12044
|
+
type: "model-usage-unknown",
|
|
12045
|
+
callId: modelUsageCall.callId,
|
|
12046
|
+
provider: modelUsageCall.provider,
|
|
12047
|
+
model: modelUsageCall.model,
|
|
12048
|
+
source: modelUsageCall.source,
|
|
12049
|
+
code: "provider_call_failed",
|
|
12050
|
+
};
|
|
12051
|
+
throw backgroundFailure;
|
|
12052
|
+
}
|
|
12053
|
+
|
|
12054
|
+
let result;
|
|
12055
|
+
try {
|
|
12056
|
+
result = await _withSpan(
|
|
12057
|
+
recorder,
|
|
12058
|
+
"agent.model",
|
|
12059
|
+
{
|
|
12060
|
+
"gen_ai.system": options.provider || "ollama",
|
|
12061
|
+
"gen_ai.request.model": options.model || "unknown",
|
|
12062
|
+
"agent.iteration": budget.consumed,
|
|
12063
|
+
...(modelIdAttrs || {}),
|
|
12064
|
+
},
|
|
12065
|
+
() => llmCall(callMessages, effectiveToolOptions),
|
|
12066
|
+
(span, r) => {
|
|
12067
|
+
const t = _usageTokens(r?.usage);
|
|
12068
|
+
if (t) {
|
|
12069
|
+
if (t.input != null)
|
|
12070
|
+
span.setAttribute("gen_ai.usage.input_tokens", t.input);
|
|
12071
|
+
if (t.output != null)
|
|
12072
|
+
span.setAttribute("gen_ai.usage.output_tokens", t.output);
|
|
12073
|
+
// Cache read/write tokens — the prompt-caching hit rate the plan's
|
|
12074
|
+
// reliability telemetry cares about.
|
|
12075
|
+
if (t.cacheRead != null)
|
|
12076
|
+
span.setAttribute("gen_ai.usage.cache_read_tokens", t.cacheRead);
|
|
12077
|
+
if (t.cacheWrite != null)
|
|
12078
|
+
span.setAttribute(
|
|
12079
|
+
"gen_ai.usage.cache_write_tokens",
|
|
12080
|
+
t.cacheWrite,
|
|
12081
|
+
);
|
|
12082
|
+
}
|
|
12083
|
+
span.setAttribute(
|
|
12084
|
+
"agent.has_tool_calls",
|
|
12085
|
+
Array.isArray(r?.message?.tool_calls) &&
|
|
12086
|
+
r.message.tool_calls.length > 0,
|
|
11110
12087
|
);
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
12088
|
+
// response CONTENT is opt-in (--otlp-content): stamped only when
|
|
12089
|
+
// enabled, redacted + length-capped through the same normalizer; the
|
|
12090
|
+
// field is entirely absent by default so default OTLP stays unchanged.
|
|
12091
|
+
if (options.otlpIncludeContent === true && r?.message?.content) {
|
|
12092
|
+
const respAttrs = buildTelemetryAttributes(
|
|
12093
|
+
{ response: r.message.content },
|
|
12094
|
+
{ includeContent: true },
|
|
11115
12095
|
);
|
|
12096
|
+
if (respAttrs["content.response"] != null) {
|
|
12097
|
+
span.setAttribute(
|
|
12098
|
+
"content.response",
|
|
12099
|
+
respAttrs["content.response"],
|
|
12100
|
+
);
|
|
12101
|
+
}
|
|
11116
12102
|
}
|
|
11117
|
-
}
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
)
|
|
12103
|
+
},
|
|
12104
|
+
"model_error",
|
|
12105
|
+
);
|
|
12106
|
+
} catch (error) {
|
|
12107
|
+
// Never retain provider error text in the usage ledger. Pause on a fixed
|
|
12108
|
+
// unknown settlement, then rethrow only after the consumer resumes.
|
|
12109
|
+
yield {
|
|
12110
|
+
type: "model-usage-unknown",
|
|
12111
|
+
callId: modelUsageCall.callId,
|
|
12112
|
+
provider: modelUsageCall.provider,
|
|
12113
|
+
model: modelUsageCall.model,
|
|
12114
|
+
source: modelUsageCall.source,
|
|
12115
|
+
code: "provider_call_failed",
|
|
12116
|
+
};
|
|
12117
|
+
await _awaitBackgroundUsageSettlement(
|
|
12118
|
+
backgroundSubAgents,
|
|
12119
|
+
backgroundUsageFailureState,
|
|
12120
|
+
);
|
|
12121
|
+
throw error;
|
|
12122
|
+
}
|
|
11121
12123
|
if (recorder) recorder.counter("agent.model.calls", 1);
|
|
11122
|
-
throwIfAborted(signal);
|
|
11123
12124
|
const msg = result?.message;
|
|
11124
12125
|
|
|
11125
|
-
|
|
12126
|
+
// Close the already-persisted call boundary before honoring an abort that
|
|
12127
|
+
// raced with a successful provider response. Once the transport returned,
|
|
12128
|
+
// dropping this settlement would strand a durable `started` row forever.
|
|
12129
|
+
if (_hasCompleteProviderUsage(result?.usage)) {
|
|
11126
12130
|
yield {
|
|
11127
12131
|
type: "token-usage",
|
|
11128
|
-
|
|
11129
|
-
|
|
12132
|
+
callId: modelUsageCall.callId,
|
|
12133
|
+
provider: modelUsageCall.provider,
|
|
12134
|
+
model: modelUsageCall.model,
|
|
11130
12135
|
usage: result.usage,
|
|
11131
12136
|
};
|
|
12137
|
+
} else {
|
|
12138
|
+
yield {
|
|
12139
|
+
type: "model-usage-unknown",
|
|
12140
|
+
callId: modelUsageCall.callId,
|
|
12141
|
+
provider: modelUsageCall.provider,
|
|
12142
|
+
model: modelUsageCall.model,
|
|
12143
|
+
source: modelUsageCall.source,
|
|
12144
|
+
code: "provider_usage_missing",
|
|
12145
|
+
};
|
|
11132
12146
|
}
|
|
12147
|
+
throwIfAborted(signal);
|
|
11133
12148
|
|
|
11134
12149
|
if (!msg) {
|
|
12150
|
+
await _awaitBackgroundUsageSettlement(
|
|
12151
|
+
backgroundSubAgents,
|
|
12152
|
+
backgroundUsageFailureState,
|
|
12153
|
+
);
|
|
11135
12154
|
yield { type: "response-complete", content: "(No response from LLM)" };
|
|
11136
12155
|
yield { type: "run-ended", runId, reason: "no-response" };
|
|
11137
12156
|
return;
|
|
@@ -11154,11 +12173,13 @@ export async function* agentLoop(messages, options) {
|
|
|
11154
12173
|
await Promise.all(
|
|
11155
12174
|
[...backgroundSubAgents.values()].map((e) => e.promise),
|
|
11156
12175
|
);
|
|
12176
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
11157
12177
|
throwIfAborted(signal);
|
|
11158
12178
|
messages.push({ role: "assistant", content: msg.content || "" });
|
|
11159
12179
|
for (const entry of _takeSettledBackgroundSubAgents(
|
|
11160
12180
|
backgroundSubAgents,
|
|
11161
12181
|
)) {
|
|
12182
|
+
_throwBackgroundSubAgentUsageFailure(entry);
|
|
11162
12183
|
messages.push({
|
|
11163
12184
|
role: "user",
|
|
11164
12185
|
content:
|
|
@@ -11298,9 +12319,9 @@ export async function* agentLoop(messages, options) {
|
|
|
11298
12319
|
// no process spawn, no shared-state writes) and there is no interactive
|
|
11299
12320
|
// confirmer in play, run them concurrently so wall-clock drops from the SUM
|
|
11300
12321
|
// of the reads to their MAX — the common "read these N files" case. Crucially,
|
|
11301
|
-
// `tool-executing
|
|
11302
|
-
//
|
|
11303
|
-
// correlation
|
|
12322
|
+
// Every `tool-executing` boundary is YIELDED before dispatch, giving the
|
|
12323
|
+
// host a chance to durably record every call. Tool results stay in call order;
|
|
12324
|
+
// correlation stays deterministic even though the reads themselves overlap.
|
|
11304
12325
|
// Any mutating/unknown/MCP tool in the batch, a single call, an interactive
|
|
11305
12326
|
// session (concurrent prompts would race), or `parallelReadOnlyTools: false`
|
|
11306
12327
|
// falls through to the strictly sequential loop below.
|
|
@@ -11317,10 +12338,7 @@ export async function* agentLoop(messages, options) {
|
|
|
11317
12338
|
throwIfAborted(signal);
|
|
11318
12339
|
const turnId = `${runId}:t${budget.consumed}`;
|
|
11319
12340
|
const toolBatchRecords = [];
|
|
11320
|
-
|
|
11321
|
-
// promise can never surface as an unhandled rejection if the loop aborts
|
|
11322
|
-
// before it is consumed.
|
|
11323
|
-
const inflight = toolCalls.map((call) => {
|
|
12341
|
+
const prepared = toolCalls.map((call) => {
|
|
11324
12342
|
let toolArgs;
|
|
11325
12343
|
try {
|
|
11326
12344
|
toolArgs =
|
|
@@ -11330,6 +12348,44 @@ export async function* agentLoop(messages, options) {
|
|
|
11330
12348
|
} catch {
|
|
11331
12349
|
toolArgs = {};
|
|
11332
12350
|
}
|
|
12351
|
+
return { call, toolArgs };
|
|
12352
|
+
});
|
|
12353
|
+
// Yield every start boundary before dispatch. Async-generator consumers
|
|
12354
|
+
// persist each boundary before asking for the next item, so by the time
|
|
12355
|
+
// this loop finishes all parallel calls have durable started records.
|
|
12356
|
+
const startedPrepared = [];
|
|
12357
|
+
try {
|
|
12358
|
+
throwIfAborted(signal);
|
|
12359
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
12360
|
+
for (const { call, toolArgs } of prepared) {
|
|
12361
|
+
yield {
|
|
12362
|
+
type: "tool-executing",
|
|
12363
|
+
tool: call.function.name,
|
|
12364
|
+
args: toolArgs,
|
|
12365
|
+
tool_use_id: call.id,
|
|
12366
|
+
turn_id: `${runId}:t${budget.consumed}`,
|
|
12367
|
+
};
|
|
12368
|
+
startedPrepared.push({ call, toolArgs });
|
|
12369
|
+
throwIfAborted(signal);
|
|
12370
|
+
}
|
|
12371
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
12372
|
+
} catch (admissionFailure) {
|
|
12373
|
+
for (const { call } of startedPrepared) {
|
|
12374
|
+
yield {
|
|
12375
|
+
type: "tool-result",
|
|
12376
|
+
tool: call.function.name,
|
|
12377
|
+
result: { error: "Tool execution cancelled before dispatch" },
|
|
12378
|
+
error: "tool execution cancelled before dispatch",
|
|
12379
|
+
tool_use_id: call.id,
|
|
12380
|
+
turn_id: `${runId}:t${budget.consumed}`,
|
|
12381
|
+
};
|
|
12382
|
+
}
|
|
12383
|
+
throw admissionFailure;
|
|
12384
|
+
}
|
|
12385
|
+
// Only after every start has been consumed do we kick every read off.
|
|
12386
|
+
// Settle each promise into {result,error} so an abort before its ordered
|
|
12387
|
+
// result is consumed can never surface an unhandled rejection.
|
|
12388
|
+
const inflight = prepared.map(({ call, toolArgs }) => {
|
|
11333
12389
|
const promise = executeTool(call.function.name, toolArgs, {
|
|
11334
12390
|
...toolContext,
|
|
11335
12391
|
toolCallId: call.id,
|
|
@@ -11342,13 +12398,7 @@ export async function* agentLoop(messages, options) {
|
|
|
11342
12398
|
});
|
|
11343
12399
|
for (const { call, toolArgs, promise } of inflight) {
|
|
11344
12400
|
throwIfAborted(signal);
|
|
11345
|
-
|
|
11346
|
-
type: "tool-executing",
|
|
11347
|
-
tool: call.function.name,
|
|
11348
|
-
args: toolArgs,
|
|
11349
|
-
tool_use_id: call.id,
|
|
11350
|
-
turn_id: `${runId}:t${budget.consumed}`,
|
|
11351
|
-
};
|
|
12401
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
11352
12402
|
const { result: toolResult, error: toolError } = await promise;
|
|
11353
12403
|
throwIfAborted(signal);
|
|
11354
12404
|
const failed = emitToolHookLifecycle({
|
|
@@ -11410,6 +12460,7 @@ export async function* agentLoop(messages, options) {
|
|
|
11410
12460
|
const toolBatchRecords = [];
|
|
11411
12461
|
for (const call of toolCalls) {
|
|
11412
12462
|
throwIfAborted(signal);
|
|
12463
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
11413
12464
|
const fn = call?.function;
|
|
11414
12465
|
const toolName = fn?.name;
|
|
11415
12466
|
// A malformed tool call (no `function` / no `name` — a provider quirk or a
|
|
@@ -11592,6 +12643,25 @@ export async function* agentLoop(messages, options) {
|
|
|
11592
12643
|
tool_use_id: call.id,
|
|
11593
12644
|
turn_id: `${runId}:t${budget.consumed}`,
|
|
11594
12645
|
};
|
|
12646
|
+
// The event yield is a host persistence boundary. A detached child's
|
|
12647
|
+
// fatal usage write can settle while the consumer handles it; fence
|
|
12648
|
+
// the next (possibly provider-backed) tool immediately before start.
|
|
12649
|
+
try {
|
|
12650
|
+
_throwBackgroundUsageFailureState(backgroundUsageFailureState);
|
|
12651
|
+
} catch (backgroundFailure) {
|
|
12652
|
+
yield {
|
|
12653
|
+
type: "tool-result",
|
|
12654
|
+
tool: toolName,
|
|
12655
|
+
result: {
|
|
12656
|
+
error:
|
|
12657
|
+
"Tool execution blocked by an authoritative background usage persistence failure",
|
|
12658
|
+
},
|
|
12659
|
+
error: "authoritative background usage persistence failure",
|
|
12660
|
+
tool_use_id: call.id,
|
|
12661
|
+
turn_id: `${runId}:t${budget.consumed}`,
|
|
12662
|
+
};
|
|
12663
|
+
throw backgroundFailure;
|
|
12664
|
+
}
|
|
11595
12665
|
}
|
|
11596
12666
|
|
|
11597
12667
|
let toolResult;
|
|
@@ -11676,6 +12746,7 @@ export async function* agentLoop(messages, options) {
|
|
|
11676
12746
|
"tool_error",
|
|
11677
12747
|
);
|
|
11678
12748
|
} catch (err) {
|
|
12749
|
+
if (err?.runtimeLedgerPersistence === true) throw err;
|
|
11679
12750
|
toolResult = { error: err.message };
|
|
11680
12751
|
toolError = err.message;
|
|
11681
12752
|
}
|
|
@@ -11845,6 +12916,10 @@ export async function* agentLoop(messages, options) {
|
|
|
11845
12916
|
|
|
11846
12917
|
// Budget exhausted — flush any child usage the final iteration produced,
|
|
11847
12918
|
// then yield exhaustion event + final message
|
|
12919
|
+
await _awaitBackgroundUsageSettlement(
|
|
12920
|
+
backgroundSubAgents,
|
|
12921
|
+
backgroundUsageFailureState,
|
|
12922
|
+
);
|
|
11848
12923
|
yield* _drainSubAgentUsage(subAgentUsageSink);
|
|
11849
12924
|
yield { type: "iteration-budget-exhausted", budget: budget.toSummary() };
|
|
11850
12925
|
yield {
|