chainlesschain 0.166.48 → 0.166.58

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.
Files changed (188) hide show
  1. package/README.md +22 -0
  2. package/package.json +2 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BLGEffkh.js → AIOps-DoyI29Aw.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Cz1z2VGD.js → ActionButton-eZKcEoxc.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C3j9cf6q.js → Analytics-B2C2e9A7.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-BR9ERuCA.js → AppLayout-DkZJiu4X.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-CozJCm4y.js → Artifacts-8PD3BXVa.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DC5g9Ra2.js → Audit-Cv6sRlFu.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-B1zrQ0dW.js → BackgroundAgents-D-V84Qpm.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-ChVnfjpm.js → Backup-DYJQcxj1.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-8_vLQsLf.js → BaseInput-KL2IeywH.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-Dct9bm6L.js → Chat-Bt9wy_Yn.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BvoSsCKh.js → ChatBubbleRenderer-ChB6fH-W.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-DZQIsCIC.js → Checkbox-Dm-nfdnd.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CdzHxmnq.js → Codegen-CYi3yDzv.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DdAN8LeW.js → Col-C7sQywfG.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-CNgjT9MA.js → Community-DjxuqAM4.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BtQiX6G-.js → Compact-CD_4lbxr.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-OGX5ma3j.js → Compliance-BtDpNJrS.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-CgugQnGM.js → Cowork-l19ANH6Q.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-Bpg7cPFD.js → Cron-Br-rBoEK.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-D1hsuswf.js → Crosschain-60UVvKET.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-miDRsMBw.js → DID-B9dqar51.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-CGcl8htP.js → Dashboard-B39hhdHb.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BFaxTdVE.js → Dropdown-Bm5rCfvF.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-BQdGLZIO.js → EmailListRenderer-BI9HANDl.js} +1 -1
  28. package/src/assets/web-panel/assets/EvolutionSettings-Cfqn_yCq.js +1 -0
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-c1WVC_--.js → FamilyGuardDashboard-9Y4_zyhT.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-DyAcFrQY.js → Federation-vnbbmMdF.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-CfwpfD2B.js → FormItemContext-BEKNtNF9.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-DkJcbBpS.js → GenericCardRenderer-BmWr16IL.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-DgYIrPJh.js → Git-CUUwAXdn.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-BF1tYPJ6.js → Governance-Bw8XZmH9.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-BFkwRIrU.js → Inference-CNrml6dn.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-DpCoqhBa.js → KnowledgeGraph-BhxOwpW_.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-DU2qAJmD.js → Logs-BV8WViUG.js} +2 -2
  38. package/src/assets/web-panel/assets/MarkdownRenderer-CO1qbiOY.js +1 -0
  39. package/src/assets/web-panel/assets/{Marketplace-Dz9NUrfo.js → Marketplace-ztcu4mg4.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-B67I3d5v.js → McpTools-D-wzRO7z.js} +2 -2
  41. package/src/assets/web-panel/assets/{Memory-crkElJSy.js → Memory-ChdjWC1R.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-BO20VbdX.js → MobileBridge-B1QHzIjE.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-Ba-3gZZd.js → MobileProjects-DmaRtaMw.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-BAHZM084.js → Mtc-DRQMr4WP.js} +4 -4
  45. package/src/assets/web-panel/assets/{MtcAudit-CL3DzajL.js → MtcAudit-Cwo0on-u.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-BSWWzvWH.js → Multisig-QplgySFt.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-fnxqXM_3.js → NLProgramming-BWkMyuAy.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-qUZPIQXT.js → Notes-BI_pSEM0.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-x9Xrixqu.js → NotificationSettings-DiuFo6Xe.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-BtMtQUt7.js → OrderTableRenderer-DZzQhujZ.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-C946Hzna.js → Organization-DqEdqWB0.js} +2 -2
  52. package/src/assets/web-panel/assets/{Overflow-BYf8KIts.js → Overflow-CO7_2YLN.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-Cbqf5dnv.js → P2P-BN5RiNst.js} +2 -2
  54. package/src/assets/web-panel/assets/PdhVaultBrowser-CapfG4bl.js +7 -0
  55. package/src/assets/web-panel/assets/{Permissions-CZBw45EW.js → Permissions-B24c5fWW.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-BKwUAt43.js → PersonalDataHub-D7Mb3_vh.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-DmFD2QW4.js → Pipeline-BsfxlbOQ.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-KpuwWvt5.js → Privacy-Bq7c7IYy.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-Bt8UheX6.js → ProjectInit-Clse2DPl.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-2ZeAn9CP.js → ProjectSettings-CHAEdjri.js} +2 -2
  61. package/src/assets/web-panel/assets/Projects-C1UHvBEK.js +1 -0
  62. package/src/assets/web-panel/assets/{Providers-DcEAnw6L.js → Providers-DQyH3PXU.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-qF7liYG9.js → QrScannerModal-t2Z-N49o.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-qiESlVBJ.js → QuickAsk-BacR7PlI.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-Bk-cQyi9.js → Recommend-D-NAy0CQ.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-CwrekErS.js → RemoteSession-CjI3GeVQ.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-Bmw_UQtW.js → Reputation-HoQ3i2oW.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-CPdqZIUX.js → Row-CY2jczWa.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-D_W-xxvS.js → RssFeed-Bd3o20IY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Search-Hu0oDFj_.js → Search-C88KjgTv.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-DeRxUwmq.js → Security-yfmfvujK.js} +3 -3
  72. package/src/assets/web-panel/assets/{Services-CsNmHFkf.js → Services-DLPBWoxT.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-gv_Vz0tN.js → Skeleton-DzDjvVQi.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-Dgg-_rCB.js → Skills-DqfrJIyP.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-Bpu-bsEJ.js → Sla-BXhsGzkm.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-CrhleHuF.js → SpeechSettings-jxSl2JDo.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-JHjUN6I9.js → SyncSettings-BX2xKtI3.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-DKRWKkl9.js → Tasks-P9MmRYt4.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-leiWntLq.js → Templates-CTpj9f62.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant--1bYulvn.js → Tenant-nnQuhdPg.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-C0y6FvSS.js → Terminal-cJ0vbuTe.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-CQK4PVv4.js → TimelineRenderer-DN08D0SD.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-DbIvF-DV.js → Tokens-C8BWWBUD.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-CkXJ2vuq.js → Trigger-DhD-LUoe.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-B02WXJPw.js → Trust-D0nFyc_Z.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-VPMkfzmw.js → UkeySign-B59G57LP.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-C5ezUnah.js → VideoEditing-BpYACxVC.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CYfvYRrY.js → Wallet-DlZ3rKGD.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-DtKl-kpr.js → WebAuthn-De4dNyig.js} +5 -5
  90. package/src/assets/web-panel/assets/{WorkflowEditor-DACITF-u.js → WorkflowEditor-C2Tewnhp.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-qnygfnLi.js → chat-CDBn4JLE.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-D4mISLBD.js → colors-BNawl0d_.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-BBPfxYGL.js → compact-item-n65WDhze.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-Byx4ndHp.js → createContext-DAKgOdIy.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-sBSSW127.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-CGjLkkn3.js → hasIn-DhxFG46W.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CWhI8G14.js → index--76HBS1k.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Dmfe0L2Z.js → index-0Y-ncsVy.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BzeBBAl5.js → index-5oJjovnA.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CFMWjuav.js → index-BAogaHa9.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B8UpadHC.js → index-BBFjMjmp.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BmKYaaGP.js → index-BDTrf8WH.js} +1 -1
  103. package/src/assets/web-panel/assets/index-BIJTDSLY.js +1 -0
  104. package/src/assets/web-panel/assets/{index-CD5Btq6p.js → index-BQFAuU1c.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CVUJjQAT.js → index-BRyzRluv.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-O09jXbir.js → index-BZI8xwwt.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CMj8qmpX.js → index-BfXdTvku.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D_g9mA1N.js → index-BudfMkBy.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BN7_TSgU.js → index-By-zGgUp.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Ne8UGllV.js → index-ByDs4cW4.js} +3 -3
  111. package/src/assets/web-panel/assets/{index-CZjNamSU.js → index-BzONk4rW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-xzRKTy0R.js → index-CE6OcLof.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-J5JkStvY.js → index-CSOgoyqx.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BklGH3YI.js → index-CW_OUdP8.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CmHE-lY_.js → index-CZJrAg98.js} +1 -1
  116. package/src/assets/web-panel/assets/index-CrBauOkp.js +1 -0
  117. package/src/assets/web-panel/assets/{index-lKJWAmd4.js → index-Cv7LqHwv.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dzni_5Qw.js → index-D34AXxN9.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-c_tBkIlU.js → index-D6sWUgN6.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Y7el_3WS.js → index-DCujBaFg.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BxQF1wCU.js → index-DFkamspD.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CsVuQsY1.js → index-DLFRz0_7.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-ByA1NyFP.js → index-DV6iHrUO.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DboTeuPF.js → index-D_-6qQf0.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-C9tcHs46.js → index-DeTlqus4.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DH6iC5b2.js → index-DkW0HGr-.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B9jH-h4X.js → index-DleKcclQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Cpg3AWRy.js → index-Ds290C06.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-B_fHPZ8E.js → index-KT94nMha.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-C3abGsSW.js → index-KZkt4e9N.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DZXhKAk9.js → index-L1QnA0gW.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DfgpKd64.js → index-PlOBwfR1.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CoA8WsVr.js → index-gG1WJBpa.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BNe2lCk6.js → index-hA9VUXGr.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CydHMjwc.js → index-pzEYIGNA.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-XuhfEIhB.js → initDefaultProps-B3bCivrt.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-Cbxh6AxO.js → motion-CF5pGsxz.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-C7IEnFly.js → move-CaOFtFLU.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-lLADx4LL.js → mtc-parser-Z4-50Y1c.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-mIbic_k-.js → omit-AK4WqVTT.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-_xIYEqu3.js → pickAttrs-Dbye0px4.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-ClB-E0ny.js → placementArrow-DCmr-Dks.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-D36Bg2ut.js → responsiveObserve-CJ3Zm_-H.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-CUuO08Bl.js → slide-BuSk5-ue.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-BKahwLbv.js → statusUtils-8u_lKSFJ.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-2pT-T8vy.js → styleChecker-sy6_m42F.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-DyH5g7Rg.js → useFlexGapSupport-Di5EfYGo.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-CTOauB3X.js → useFs-BTfX_Ri1.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-B02JYIMe.js → usePersonalDataHub-kqurO26X.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-Do22wQrc.js → vnode-DRkH4mPV.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-BfnL7rit.js → zoom-ON_-UuD_.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/commands/agent.js +5 -0
  154. package/src/commands/config.js +19 -10
  155. package/src/commands/context.js +31 -11
  156. package/src/commands/eval.js +39 -4
  157. package/src/commands/evolution-deployment.js +70 -0
  158. package/src/data/changelog.json +91 -0
  159. package/src/gateways/ws/evolution-deployment-topic-handlers.js +20 -0
  160. package/src/harness/prompt-compressor.js +12 -4
  161. package/src/lib/diagnostic-excerpt.js +51 -0
  162. package/src/lib/diagnostic-workflow.js +10 -0
  163. package/src/lib/eval/evidence.js +19 -10
  164. package/src/lib/eval/outcomes.js +373 -0
  165. package/src/lib/eval/runner.js +6 -0
  166. package/src/lib/evolution/evolution-deployment-config.js +16 -1
  167. package/src/lib/evolution/evolution-deployment-profile.js +102 -58
  168. package/src/lib/evolution/evolution-test-deployment.js +386 -0
  169. package/src/lib/git-tool-input.js +37 -0
  170. package/src/lib/llm-config-defaults.js +32 -9
  171. package/src/lib/model-context-window.js +3 -2
  172. package/src/lib/read-file-loop-guard.js +54 -0
  173. package/src/lib/remote-read-loop-guard.js +12 -14
  174. package/src/lib/runtime-usage-ledger.js +5 -0
  175. package/src/lib/session-message-fabric.js +74 -37
  176. package/src/lib/shell-output.js +30 -0
  177. package/src/lib/task-progress-tracker.js +88 -0
  178. package/src/repl/agent-repl.js +19 -4
  179. package/src/runtime/agent-core.js +108 -22
  180. package/src/runtime/headless-runner.js +4 -0
  181. package/src/runtime/headless-stream.js +34 -1
  182. package/src/assets/web-panel/assets/EvolutionSettings-bMvgBk6r.js +0 -1
  183. package/src/assets/web-panel/assets/MarkdownRenderer-QWv7UJLq.js +0 -1
  184. package/src/assets/web-panel/assets/PdhVaultBrowser-rmxp0cuF.js +0 -7
  185. package/src/assets/web-panel/assets/Projects-zBuet8Zs.js +0 -1
  186. package/src/assets/web-panel/assets/devWarning-DBy6gsuI.js +0 -1
  187. package/src/assets/web-panel/assets/index-BfEDXk_E.js +0 -1
  188. package/src/assets/web-panel/assets/index-CrxUwNM8.js +0 -1
@@ -55,6 +55,7 @@ export async function reconcileConfigLlmProvider(cfgLlm = {}) {
55
55
  }
56
56
 
57
57
  import { resolveApiKeyFromHelper } from "./api-key-helper.js";
58
+ import { modelForeignToProvider } from "./runnable-provider.js";
58
59
 
59
60
  /**
60
61
  * Key backfill order: config llm.apiKey (plaintext, legacy) first, then
@@ -111,12 +112,27 @@ function optionalModel(value) {
111
112
  return model || null;
112
113
  }
113
114
 
115
+ /**
116
+ * Claude settings and ANTHROPIC_MODEL carry a model name but no provider.
117
+ * Do not pair an unmistakably provider-specific inherited model with another
118
+ * configured provider. Explicit CLI/--settings choices may opt out because
119
+ * they provide the user's intent for this invocation.
120
+ */
121
+ export function inheritedModelCompatible(provider, model) {
122
+ const candidate = optionalModel(model);
123
+ return (
124
+ !candidate || !provider || !modelForeignToProvider(provider, candidate)
125
+ );
126
+ }
127
+
114
128
  /**
115
129
  * Resolve the model precedence used by `cc agent` without allowing a default
116
130
  * from one source to silently outrank an explicit choice from another:
117
131
  *
118
- * CLI --model > ANTHROPIC_MODEL > settings.model > config/organization model
119
- * > ANTHROPIC_DEFAULT_MODEL (new sessions only) > runtime provider default.
132
+ * CLI --model > provider-compatible ANTHROPIC_MODEL/settings.model >
133
+ * config/organization model > ANTHROPIC_DEFAULT_MODEL (new sessions only)
134
+ * > runtime provider default. An explicit --settings file or managed setting
135
+ * may authorize a foreign-looking alias for a custom provider endpoint.
120
136
  *
121
137
  * Environment values are supplied by the caller from the process startup
122
138
  * environment, before settings-file `env` entries are merged. That prevents a
@@ -128,18 +144,25 @@ export function applyAgentModelDefaults(options = {}, cfgLlm = {}, opts = {}) {
128
144
  const settingsModel = optionalModel(opts.settingsModel);
129
145
  const defaultModel = optionalModel(opts.anthropicDefaultModel);
130
146
 
131
- if (!explicitModel) {
132
- if (environmentModel) {
133
- options.model = environmentModel;
134
- } else if (settingsModel) {
135
- options.model = settingsModel;
136
- }
147
+ let inheritedModel = environmentModel || settingsModel;
148
+ const targetProvider = options.provider || cfgLlm.provider;
149
+ if (
150
+ inheritedModel &&
151
+ opts.allowForeignInheritedModel !== true &&
152
+ !inheritedModelCompatible(targetProvider, inheritedModel)
153
+ ) {
154
+ if (options.model === inheritedModel) delete options.model;
155
+ inheritedModel = null;
156
+ }
157
+
158
+ if (!explicitModel && inheritedModel) {
159
+ options.model = inheritedModel;
137
160
  }
138
161
 
139
162
  applyConfigLlmDefaults(options, cfgLlm, {
140
163
  // Reuse the existing config/provider wiring, but do not let its configured
141
164
  // model overwrite one selected by a higher-priority source above.
142
- explicitModel: explicitModel || environmentModel || settingsModel,
165
+ explicitModel: explicitModel || inheritedModel,
143
166
  });
144
167
 
145
168
  if (!options.model && opts.isNewSession !== false && defaultModel) {
@@ -2,6 +2,7 @@ import { resolveModelCapabilityProfile } from "./model-capabilities.js";
2
2
 
3
3
  export { CONTEXT_WINDOWS } from "./model-context-catalog.js";
4
4
 
5
- export function getContextWindow(model, provider) {
6
- return resolveModelCapabilityProfile({ model, provider }).contextWindowTokens;
5
+ export function getContextWindow(model, provider, options = {}) {
6
+ return resolveModelCapabilityProfile({ ...options, model, provider })
7
+ .contextWindowTokens;
7
8
  }
@@ -30,6 +30,46 @@ export class ReadFileLoopGuard {
30
30
  this.recoveryOfferedAt = 0;
31
31
  this.batch = null;
32
32
  this.largeOutputs = new Set();
33
+ this.editRecoveryReads = new Map();
34
+ }
35
+
36
+ recordEditRecovery(result) {
37
+ if (result?.success === true && result.path) {
38
+ this.editRecoveryReads.delete(keyFor(result.path, { hashed: true }));
39
+ return;
40
+ }
41
+ const request = result?.recoveryRead;
42
+ if (
43
+ !request ||
44
+ !["hash_mismatch", "content_mismatch"].includes(result.error)
45
+ )
46
+ return;
47
+ const key = keyFor(request.path, { hashed: true });
48
+ // One refresh per file until an edit succeeds. Repeating the failed edit
49
+ // must not mint an unlimited read allowance.
50
+ if (
51
+ !this.editRecoveryReads.has(key) &&
52
+ this.editRecoveryReads.size < MAX_FILES
53
+ ) {
54
+ this.editRecoveryReads.set(key, { ...request, used: false });
55
+ }
56
+ }
57
+
58
+ get hasEditRecoveryReads() {
59
+ return [...this.editRecoveryReads.values()].some((entry) => !entry.used);
60
+ }
61
+
62
+ canRecoverEdit(filePath, args = {}) {
63
+ const entry = this.editRecoveryReads.get(keyFor(filePath, args));
64
+ return !!(
65
+ entry &&
66
+ !entry.used &&
67
+ args.hashed === true &&
68
+ args.raw !== true &&
69
+ args.column == null &&
70
+ args.offset === entry.offset &&
71
+ args.limit === entry.limit
72
+ );
33
73
  }
34
74
 
35
75
  startBatch() {
@@ -51,6 +91,16 @@ export class ReadFileLoopGuard {
51
91
  }
52
92
  let page = readPage(args);
53
93
  if (page.error || !page.readSpan) return page;
94
+ if (this.canRecoverEdit(filePath, args)) {
95
+ this.editRecoveryReads.get(key).used = true;
96
+ // The authorized read implementation has already refreshed stat/content.
97
+ // Return the requested anchors even if ordinary coverage is exhausted.
98
+ return {
99
+ ...page,
100
+ readRecovery: { action: "edit-anchor-refresh" },
101
+ readProgress: { newContent: false },
102
+ };
103
+ }
54
104
  const span = page.readSpan;
55
105
  const covered = entry.spans.some(
56
106
  ([start, end]) => start <= span.start && end >= span.end,
@@ -160,6 +210,8 @@ export class ReadFileLoopGuard {
160
210
  )
161
211
  return;
162
212
  if (tool === "read_file") {
213
+ if (result?.readRecovery?.action === "edit-anchor-refresh")
214
+ this.batch.advanced = true;
163
215
  if (result?.readProgress?.newContent === true) this.batch.advanced = true;
164
216
  if (result?.readProgress?.newContent === false) this.batch.duplicates++;
165
217
  } else if (
@@ -207,12 +259,14 @@ export class ReadFileLoopGuard {
207
259
 
208
260
  get hasRecoveryReads() {
209
261
  return (
262
+ this.hasEditRecoveryReads ||
210
263
  this.hasUnreadPages ||
211
264
  [...this.progress.values()].some((entry) => entry.rereads.size < 3)
212
265
  );
213
266
  }
214
267
 
215
268
  canContinue(filePath, args = {}) {
269
+ if (this.canRecoverEdit(filePath, args)) return true;
216
270
  const entry = this.progress.get(keyFor(filePath, args));
217
271
  return (
218
272
  entry?.summary?.reachedEnd === false ||
@@ -1,4 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
3
+ import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "./diagnostic-workflow.js";
2
4
 
3
5
  const MAX_TARGETS = 32;
4
6
  const RECOVERY_AFTER = 3;
@@ -196,19 +198,7 @@ function failedResult(result) {
196
198
  }
197
199
 
198
200
  function excerpt(value) {
199
- const text = typeof value === "string" ? value : JSON.stringify(value ?? "");
200
- if (text.length <= 1600) return text;
201
- // Retain failure lines from full CI logs as well as the opening context.
202
- const failures = text
203
- .split("\n")
204
- .filter((line) => /FAIL|Error:|AssertionError|error TS|×|✕/.test(line))
205
- .slice(0, 12)
206
- .join("\n");
207
- return (
208
- text.slice(0, 400) +
209
- "\n[excerpt]\n" +
210
- (failures || text.slice(-1100)).slice(0, 1100)
211
- );
201
+ return diagnosticExcerpt(value);
212
202
  }
213
203
 
214
204
  /** Per agentLoop, independent of history compaction and other agents' evidence. */
@@ -290,13 +280,19 @@ export class RemoteReadLoopGuard {
290
280
  this.activeKey = target.key;
291
281
  const previous = this.targets.get(target.key);
292
282
  const digests = previous?.digests || new Set();
293
- const content =
283
+ const primaryContent =
294
284
  result?.output ??
295
285
  result?.stdout ??
296
286
  result?.content ??
297
287
  result?.matches ??
298
288
  result?.body ??
299
289
  "";
290
+ const content =
291
+ result?.stdout_diagnostics || result?.stderr_diagnostics
292
+ ? [result.stdout_diagnostics, result.stderr_diagnostics, primaryContent]
293
+ .filter(Boolean)
294
+ .join("\n")
295
+ : primaryContent;
300
296
  const digest = createHash("sha256")
301
297
  .update(typeof content === "string" ? content : JSON.stringify(content))
302
298
  .digest("hex");
@@ -453,6 +449,8 @@ export class RemoteReadLoopGuard {
453
449
  "An aggregate or incomplete-matrix gate can be a downstream symptom; inspect the dependency jobs' conclusions before assuming that the gate itself is wrong. " +
454
450
  "When a job is cancelled or logs are unavailable, record that fact and the command error; do not cycle through the same web page and log downloads. " +
455
451
  "Use one focused run/job metadata query to resolve the missing status or cancellation reason, then inspect the relevant workflow and make the justified, authorized fix with validation. " +
452
+ DIAGNOSTIC_WORKFLOW_GUIDANCE +
453
+ " " +
456
454
  "If available evidence cannot establish a fix, report the specific missing evidence and what was verified. Do not disable a release or safety gate just to make CI pass. " +
457
455
  "For a research/review request, synthesize the findings without unsolicited edits; for monitoring, use status queries instead of repeated logs."
458
456
  );
@@ -162,6 +162,11 @@ export function projectRuntimeTokenUsage(event = {}) {
162
162
  ...(projectedOperationId ? { operationId: projectedOperationId } : {}),
163
163
  ...(event.source ? { source: source(event.source) } : {}),
164
164
  ...(attribution ? { attribution } : {}),
165
+ ...(Number.isSafeInteger(event.contextWindow) &&
166
+ event.contextWindow >= 1024 &&
167
+ event.contextWindow <= 16777216
168
+ ? { contextWindow: event.contextWindow }
169
+ : {}),
165
170
  };
166
171
  }
167
172
 
@@ -31,6 +31,31 @@ export const SESSION_MESSAGE_POLICIES = Object.freeze([
31
31
  "refuse",
32
32
  ]);
33
33
 
34
+ // Windows can briefly retain a handle on a just-written state file while an
35
+ // antivirus scanner, indexer, or another process observes it. Retrying the
36
+ // atomic write is safe while this process still owns the state lock: no
37
+ // business mutation is re-run, and no error after the final rename is retried.
38
+ // Keep this deliberately short so genuine filesystem failures remain
39
+ // fail-closed.
40
+ const TRANSIENT_ATOMIC_WRITE_ERROR_CODES = new Set([
41
+ "EACCES",
42
+ "EBUSY",
43
+ "EPERM",
44
+ ]);
45
+ const MAX_ATOMIC_WRITE_ATTEMPTS = 5;
46
+
47
+ function waitForAtomicWriteRetry(attempt) {
48
+ const delayMs = Math.min(100, 5 * 2 ** attempt);
49
+ try {
50
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
51
+ } catch {
52
+ const deadline = Date.now() + delayMs;
53
+ while (Date.now() < deadline) {
54
+ // SharedArrayBuffer is unavailable only in constrained runtimes.
55
+ }
56
+ }
57
+ }
58
+
34
59
  export const SESSION_MESSAGE_RECEIPT_STATUSES = Object.freeze([
35
60
  "delivered",
36
61
  "held",
@@ -756,50 +781,62 @@ export class SessionMessageFabric {
756
781
 
757
782
  _writeState(state) {
758
783
  const directory = path.dirname(this.statePath);
759
- const temporary = path.join(
760
- directory,
761
- `.${path.basename(this.statePath)}.${process.pid}.${this._createId()}.tmp`,
762
- );
763
- let descriptor = null;
764
- try {
765
- descriptor = fs.openSync(temporary, "wx", 0o600);
766
- fs.writeFileSync(
767
- descriptor,
768
- `${JSON.stringify(state, null, 2)}\n`,
769
- "utf8",
784
+ const serialized = `${JSON.stringify(state, null, 2)}\n`;
785
+ let lastCause;
786
+ for (let attempt = 0; attempt < MAX_ATOMIC_WRITE_ATTEMPTS; attempt += 1) {
787
+ const temporary = path.join(
788
+ directory,
789
+ `.${path.basename(this.statePath)}.${process.pid}.${this._createId()}.tmp`,
770
790
  );
771
- fs.fsyncSync(descriptor);
772
- fs.closeSync(descriptor);
773
- descriptor = null;
774
- fs.renameSync(temporary, this.statePath);
775
- if (process.platform !== "win32") {
776
- const directoryDescriptor = fs.openSync(directory, "r");
777
- try {
778
- fs.fsyncSync(directoryDescriptor);
779
- } finally {
780
- fs.closeSync(directoryDescriptor);
791
+ let descriptor = null;
792
+ let renamed = false;
793
+ try {
794
+ descriptor = fs.openSync(temporary, "wx", 0o600);
795
+ fs.writeFileSync(descriptor, serialized, "utf8");
796
+ fs.fsyncSync(descriptor);
797
+ fs.closeSync(descriptor);
798
+ descriptor = null;
799
+ fs.renameSync(temporary, this.statePath);
800
+ renamed = true;
801
+ if (process.platform !== "win32") {
802
+ const directoryDescriptor = fs.openSync(directory, "r");
803
+ try {
804
+ fs.fsyncSync(directoryDescriptor);
805
+ } finally {
806
+ fs.closeSync(directoryDescriptor);
807
+ }
808
+ }
809
+ return;
810
+ } catch (cause) {
811
+ lastCause = cause;
812
+ if (descriptor != null) {
813
+ try {
814
+ fs.closeSync(descriptor);
815
+ } catch {
816
+ // Preserve the original write failure.
817
+ }
781
818
  }
782
- }
783
- } catch (cause) {
784
- if (descriptor != null) {
785
819
  try {
786
- fs.closeSync(descriptor);
820
+ fs.unlinkSync(temporary);
787
821
  } catch {
788
- // Preserve the original write failure.
822
+ // The rename may already have committed, or no temporary was created.
789
823
  }
824
+ if (
825
+ renamed ||
826
+ !TRANSIENT_ATOMIC_WRITE_ERROR_CODES.has(cause?.code) ||
827
+ attempt + 1 >= MAX_ATOMIC_WRITE_ATTEMPTS
828
+ ) {
829
+ break;
830
+ }
831
+ waitForAtomicWriteRetry(attempt);
790
832
  }
791
- try {
792
- fs.unlinkSync(temporary);
793
- } catch {
794
- // The rename may already have committed, or no temporary was created.
795
- }
796
- throw fabricError(
797
- SESSION_MESSAGE_FABRIC_ERROR_CODES.STATE_UNAVAILABLE,
798
- "Could not atomically persist session message state",
799
- {},
800
- cause,
801
- );
802
833
  }
834
+ throw fabricError(
835
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.STATE_UNAVAILABLE,
836
+ "Could not atomically persist session message state",
837
+ {},
838
+ lastCause,
839
+ );
803
840
  }
804
841
 
805
842
  _transaction(mutator, { expectedRevision = null } = {}) {
@@ -0,0 +1,30 @@
1
+ import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
2
+
3
+ /** Page size includes JSON escaping so later serialization does not lose data. */
4
+ export function shellOutputPage(value, budget) {
5
+ const text = String(value ?? "");
6
+ let low = 0;
7
+ let high = Math.min(text.length, budget);
8
+ while (low < high) {
9
+ const mid = Math.ceil((low + high) / 2);
10
+ if (JSON.stringify(text.slice(0, mid)).length <= budget) low = mid;
11
+ else high = mid - 1;
12
+ }
13
+ if (low > 0 && /[\uD800-\uDBFF]/.test(text[low - 1])) low--;
14
+ return { text: text.slice(0, low), remaining: text.length - low };
15
+ }
16
+
17
+ /** Foreground commands cannot be repolled; expose omitted diagnostic context. */
18
+ export function shellOutputPreview(value, field, budget) {
19
+ const text = String(value ?? "");
20
+ const page = shellOutputPage(text, budget);
21
+ if (!page.remaining) return { [field]: page.text };
22
+ return {
23
+ [`${field}_truncated`]: true,
24
+ [`${field}_total_chars`]: text.length,
25
+ [`${field}_diagnostics`]: diagnosticExcerpt(text),
26
+ [field]: page.text,
27
+ output_hint:
28
+ "Output is incomplete. Diagnostic excerpts are source evidence, not a root-cause verdict. For long reproductions use run_in_background and drain check_shell with task_id; preserve a full CI log once before filtering it.",
29
+ };
30
+ }
@@ -1,5 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { remoteReadTarget } from "./remote-read-loop-guard.js";
3
+ import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
4
+ import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "./diagnostic-workflow.js";
3
5
 
4
6
  // Discovery and bookkeeping are useful, but are not evidence that the task
5
7
  // advanced. In particular, dispatching a child must not buy a fresh budget.
@@ -82,6 +84,7 @@ export class TaskProgressTracker {
82
84
  this.lastActions = [];
83
85
  this.remoteInspections = [];
84
86
  this.localFindings = [];
87
+ this.diagnostics = [];
85
88
  }
86
89
 
87
90
  retainChildResult(id, result, owner = "root") {
@@ -121,6 +124,82 @@ export class TaskProgressTracker {
121
124
  typeof args.old_string === "string" &&
122
125
  args.old_string === args.new_string);
123
126
 
127
+ // Preserve observed reproduction results through compaction, including
128
+ // failed runs and completed background tasks. They do not establish that
129
+ // the original CI failure was reproduced or fixed.
130
+ if (
131
+ ["run_shell", "run_code", "check_shell"].includes(tool) &&
132
+ result?.code !== "CC_TOOL_RECOVERY_PAUSED" &&
133
+ !(tool === "check_shell" && !args.task_id)
134
+ ) {
135
+ const taskId = result?.task_id || args.task_id || null;
136
+ const previous =
137
+ taskId &&
138
+ this.diagnostics.find(
139
+ (entry) => entry.owner === owner && entry.taskId === taskId,
140
+ );
141
+ const output = [
142
+ result?.stdout_diagnostics,
143
+ result?.stderr_diagnostics,
144
+ result?.error,
145
+ result?.output,
146
+ result?.stdout,
147
+ result?.stderr,
148
+ ]
149
+ .filter(Boolean)
150
+ .join("\n");
151
+ if (output || taskId || Number.isInteger(result?.exitCode)) {
152
+ const command = args.command || result?.command || args.code || "";
153
+ const remoteTarget = remoteReadTarget(tool, args);
154
+ const entry = {
155
+ owner,
156
+ tool,
157
+ taskId,
158
+ observedAt: this.now(),
159
+ remoteTarget: remoteTarget?.key || previous?.remoteTarget || null,
160
+ source:
161
+ previous?.source ||
162
+ (remoteTarget?.github || isRemoteInspectionCommand(command)
163
+ ? "remote-inspection"
164
+ : "local-execution"),
165
+ invocation: boundedText(command || previous?.invocation, 1000),
166
+ invocationTruncated: command
167
+ ? command.length > 1000
168
+ : previous?.invocationTruncated || false,
169
+ exitCode: result?.exitCode ?? result?.exit_code ?? null,
170
+ status:
171
+ result?.status ||
172
+ (result?.background ? "running" : failed ? "failed" : "completed"),
173
+ outputLost:
174
+ previous?.outputLost === true ||
175
+ !!result?.stdout_dropped_bytes ||
176
+ !!result?.stderr_dropped_bytes,
177
+ outputIncomplete:
178
+ previous?.outputLost === true ||
179
+ result?.has_more_output === true ||
180
+ result?.stdout_truncated === true ||
181
+ result?.stderr_truncated === true ||
182
+ result?.truncated === true ||
183
+ !!result?.stdout_dropped_bytes ||
184
+ !!result?.stderr_dropped_bytes,
185
+ output: diagnosticExcerpt(
186
+ [previous?.output, output].filter(Boolean).join("\n"),
187
+ 1200,
188
+ ),
189
+ };
190
+ const owned = [
191
+ ...this.diagnostics.filter(
192
+ (item) => item.owner === owner && item !== previous,
193
+ ),
194
+ entry,
195
+ ].slice(-4);
196
+ this.diagnostics = [
197
+ ...this.diagnostics.filter((item) => item.owner !== owner),
198
+ ...owned,
199
+ ].slice(-MAX_CHECKPOINT_OWNERS * 4);
200
+ }
201
+ }
202
+
124
203
  if (!failed && (tool === "search_files" || tool === "read_file")) {
125
204
  const evidence = result.content ?? result.matches ?? result.output;
126
205
  const excerpt = boundedText(
@@ -252,6 +331,8 @@ export class TaskProgressTracker {
252
331
  message +
253
332
  " For an implementation task, make the smallest justified, authorized change and validate it. " +
254
333
  "For a bug fix, state the observed failure, your leading root-cause hypothesis and the smallest test that can disprove it. Run that focused reproduction or diagnostic next; use its actual output to choose the next step. Do not keep reading adjacent files without naming the specific missing fact. A timeout alone does not establish that increasing the timeout is the fix. " +
334
+ DIAGNOSTIC_WORKFLOW_GUIDANCE +
335
+ " " +
255
336
  "For research/review, synthesize evidence-backed findings; do not write files merely to clear this warning. " +
256
337
  "If evidence is insufficient, name the exact missing fact and use a focused search or bounded computation. " +
257
338
  "Do not restart general investigation, rewrite the plan, or delegate the same research. " +
@@ -268,11 +349,15 @@ export class TaskProgressTracker {
268
349
  !this.childFindings.size &&
269
350
  !this.lastActions.length &&
270
351
  !this.remoteInspections.length &&
352
+ !this.diagnostics.length &&
271
353
  !this.localFindings.length
272
354
  )
273
355
  return null;
274
356
  const checkpoint = {
275
357
  bounded: true,
358
+ recentDiagnostics: this.diagnostics
359
+ .filter((entry) => entry.owner === owner)
360
+ .map(({ owner: _owner, ...entry }) => entry),
276
361
  recentLocalFindings: this.localFindings
277
362
  .filter((entry) => entry.owner === owner)
278
363
  .map(({ owner: _owner, ...entry }) => entry),
@@ -300,6 +385,7 @@ export class TaskProgressTracker {
300
385
  !checkpoint.recentLocalFindings.length &&
301
386
  !checkpoint.recentToolOutcomes.length &&
302
387
  !checkpoint.recentRemoteInspections.length &&
388
+ !checkpoint.recentDiagnostics.length &&
303
389
  !checkpoint.reportedPlans.length &&
304
390
  !checkpoint.childFindings.length
305
391
  )
@@ -312,6 +398,8 @@ export class TaskProgressTracker {
312
398
  checkpoint.recentLocalFindings.shift();
313
399
  else if (checkpoint.recentRemoteInspections.length)
314
400
  checkpoint.recentRemoteInspections.shift();
401
+ else if (checkpoint.recentDiagnostics.length)
402
+ checkpoint.recentDiagnostics.shift();
315
403
  else if (checkpoint.recentToolOutcomes.length)
316
404
  checkpoint.recentToolOutcomes.shift();
317
405
  else checkpoint.reportedPlans.pop();
@@ -5579,6 +5579,7 @@ async function startAgentReplInWorkspaceOwned(
5579
5579
  let _customStatus = false; // true when a settings.json command is configured
5580
5580
  let _curModel = model; // tracks the per-turn active model for the readout
5581
5581
  let _ctxUsedTokens = 0;
5582
+ let _ctxRequestWindow = null;
5582
5583
  let _turnCount = 0;
5583
5584
  const _costStore = newCostStore(); // running token spend for `/cost`
5584
5585
  let _renderStatus = null;
@@ -5598,7 +5599,8 @@ async function startAgentReplInWorkspaceOwned(
5598
5599
  provider,
5599
5600
  cwd: process.cwd(),
5600
5601
  usedTokens: _ctxUsedTokens,
5601
- contextWindow: getContextWindow(_curModel, provider),
5602
+ contextWindow:
5603
+ _ctxRequestWindow ?? getContextWindow(_curModel, provider, options),
5602
5604
  turn: _turnCount,
5603
5605
  });
5604
5606
  // Custom command wins; otherwise the built-in context-usage render.
@@ -7190,7 +7192,9 @@ async function startAgentReplInWorkspaceOwned(
7190
7192
  messages,
7191
7193
  estimateTokens,
7192
7194
  );
7193
- const window = getContextWindow(model, provider) || 0;
7195
+ const window =
7196
+ _ctxRequestWindow ??
7197
+ (getContextWindow(model, provider, options) || 0);
7194
7198
  logger.log(chalk.bold("\nContext usage (live session):"));
7195
7199
  const rows = [
7196
7200
  ["system", buckets.system, counts.system],
@@ -9756,8 +9760,19 @@ async function startAgentReplInWorkspaceOwned(
9756
9760
  _curModel = activeModel;
9757
9761
  _turnCount += 1;
9758
9762
  if (usageEvents?.length) {
9759
- const last = usageEvents[usageEvents.length - 1]?.usage || {};
9760
- const used = (last.input_tokens || 0) + (last.output_tokens || 0);
9763
+ const lastCall = [...usageEvents]
9764
+ .reverse()
9765
+ .find(
9766
+ (event) =>
9767
+ !event.attribution && (!event.source || event.source === "model"),
9768
+ );
9769
+ _ctxRequestWindow = lastCall?.contextWindow ?? null;
9770
+ const last = lastCall?.usage || {};
9771
+ const used =
9772
+ (last.input_tokens || 0) +
9773
+ (last.output_tokens || 0) +
9774
+ (last.cache_read_input_tokens || 0) +
9775
+ (last.cache_creation_input_tokens || 0);
9761
9776
  if (used > 0) _ctxUsedTokens = used;
9762
9777
  }
9763
9778