chainlesschain 0.166.102 → 0.166.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/package.json +1 -1
  2. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  3. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CyHIMEd8.js → AIOps-Bah3HREy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-vuNxDpHm.js → ActionButton-DY2aeZMc.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-D0quEM0q.js → Analytics-BFly8Xaa.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-xRSVpbCs.js → AppLayout-dp0Zz-JM.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-ktfVAZBC.js → Artifacts-DcIg09ai.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-UJW6maH4.js → Audit-DkN-lsjZ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BYrfdCoT.js → BackgroundAgents-BjzgusLx.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-IBgsJ7BK.js → Backup-CU8S6qu9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-BmP8_zxy.js → BaseInput-B1pC2gjL.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-urkw8_BF.js → Chat-B6xFp6gD.js} +6 -6
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Cw44zZbh.js → ChatBubbleRenderer-DxNCLZ_X.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-CYboo-__.js → Checkbox-QHw7Vc4U.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-DaqR_Ay8.js → Codegen-gdecsfjg.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-04BSIj_z.js → Col-B0_JmAmc.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DxFk7Trv.js → Community-DRCcP5xR.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-mjgWlMCB.js → Compact-DZtoGzhX.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-CC5ee7QV.js → Compliance-BfNTvEnT.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-BK4BhHZd.js → Cowork-CDXTLm84.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-CP6jAxq0.js → Cron-Big35CbN.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-DJPaViZS.js → Crosschain-C8ArrAVr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-BTLxkuOj.js → DID-D_77BI2O.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Djkiqh3d.js → Dashboard-D2SxTzEp.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-t_eUhqom.js → Dropdown-mR1-b9ud.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-7w6USx_J.js → EmailListRenderer-DPl7TE1c.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-mU5IHEk5.js → EvolutionSettings-BxRYLRSH.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BCh-VBBC.js → FamilyGuardDashboard-rnGtXJ5Z.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-Be_VpjI2.js → Federation-BpxenYK8.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-D_lzQ7PI.js → FormItemContext-Cz1kDf6l.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-LYu3KapV.js → GenericCardRenderer-CGV3KN6U.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-BBeF-PP4.js → Git-CIRP4UTx.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-CoF8Kzg7.js → Governance-DX4r8Vmt.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-C4DLMnXS.js → Inference-DQKk02Hl.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-B2mlNewk.js → KnowledgeGraph-DdD6J-By.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-DMZ_44Ya.js → Logs-DA2w-1_m.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-Ck7Iv_9e.js → MarkdownRenderer-C9aSGETh.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-BqDzTew_.js → Marketplace-BsJ6_6KW.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-BbwSbDN9.js → McpTools-_6k30z6u.js} +2 -2
  41. package/src/assets/web-panel/assets/{Memory-DAaTWgrm.js → Memory-C0_dYY0A.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-BjbTGxoR.js → MobileBridge-CCTnVqiC.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-DMODOBr-.js → MobileProjects-RZdxDSwC.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-DT9N2rCs.js → Mtc-D4Ys8lvX.js} +2 -2
  45. package/src/assets/web-panel/assets/{MtcAudit-DagMfUVv.js → MtcAudit-BKTC--a2.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-C1BKP6nM.js → Multisig-CGR1qOe1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-B5LFh72i.js → NLProgramming-hKKD4qWb.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-Q77V3ltW.js → Notes-pCn7Ec40.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-HxdpNIBs.js → NotificationSettings-B9eEJXEs.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-Dc0DpvBH.js → OrderTableRenderer-CmJuhaT2.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-QL3NZ7uW.js → Organization-CcQbrnwW.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-B_gqOKho.js → Overflow-HmD4Mn72.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-BBJubU8Z.js → P2P-B7LIgx5H.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-CFnGd-2E.js → PdhVaultBrowser-CXiHnBfX.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-BKkznIUM.js → Permissions-CQRI3y_l.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-DYK7ZAcd.js → PersonalDataHub-1EeHmsy5.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-CoeKpjoN.js → Pipeline-BFyoIdn8.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-Diw4G-92.js → Privacy-BxWthXMF.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-CHBOFkma.js → ProjectInit-DfigBilu.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-BJIsgdOf.js → ProjectSettings-CFmIcVc-.js} +2 -2
  61. package/src/assets/web-panel/assets/Projects-CR7H59v3.js +1 -0
  62. package/src/assets/web-panel/assets/{Providers-o8QCimor.js → Providers-2ulom-76.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-z2W-zvhW.js → QrScannerModal-BwhO07b_.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-_yubhsna.js → QuickAsk-_Wg34OwF.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-CucgJwih.js → Recommend-D_jq6Svg.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-EzfIIRg1.js → RemoteSession-BlzvEfsa.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-_shttE67.js → Reputation-9CTXHfL4.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-DQA_S8-9.js → Row-DlCYCN-t.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-BOqWawXT.js → RssFeed-BvlInFCH.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-Dx1v2xOq.js → Search-CfaUzKuL.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-Bt4HHsT4.js → Security-bAWgLyve.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-sruHiQ-r.js → Services-C4LOGACW.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-CiBxvmOJ.js → Skeleton-Dd0Uwh3_.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-De9qKODU.js → Skills-ZYF1kYCU.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-B_6yn8Wp.js → Sla-DpsmIaEo.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-C2CafExS.js → SpeechSettings-BBXoJ4yc.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-Dhh3Qmb0.js → SyncSettings-SOnROi2Y.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-Mvo5mTWm.js → Tasks-HFA_ZlDk.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-D7WVTQQy.js → Templates-fsK-RCEc.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-7ur9Yfxd.js → Tenant-CuqYxamb.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-BgzrOA6k.js → Terminal-GKlI2R5F.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-Dco20pT1.js → TimelineRenderer-DMDMHLeY.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-hN6qBPag.js → Tokens-BoV60rh3.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-C4f_twAM.js → Trigger-CE344-Ws.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-Wf1SMypp.js → Trust-DENwgkVc.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-DUXSyg3N.js → UkeySign-FrcM7yfm.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-BvqsJaL0.js → VideoEditing-DAY9WaC2.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CgAbcjKl.js → Wallet-BuUR5i82.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-DaLT0W3s.js → WebAuthn-nfSQuq85.js} +2 -2
  90. package/src/assets/web-panel/assets/{WorkflowEditor-CQqAXoaS.js → WorkflowEditor-DHhPOp9l.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-BhlohFTB.js → chat-C4nsLzYy.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-D7HqXIL8.js → colors-BwXH0Wjn.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-Bqk4FCLY.js → compact-item-uApFG30S.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-CEmWrWf0.js → createContext-BpyYs4fe.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-Bk6HGn3N.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-CDY0pFi2.js → hasIn-MXCLi00p.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-e9oCy7_4.js → index-1LxX2IwJ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DEzL7s7P.js → index-61CBBrHE.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D0nfNmbq.js → index-B0P1Z_oa.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BiLu-ImR.js → index-B7jojVc7.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CqK0J4Sj.js → index-BEGMfaOo.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-aBHlgMrq.js → index-BOhjapd5.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DPBoWNwq.js → index-BaBwXpAp.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bb1LPYdi.js +1 -0
  105. package/src/assets/web-panel/assets/{index-BxXFRLTZ.js → index-Btv--Nnt.js} +1 -1
  106. package/src/assets/web-panel/assets/index-C-FsTuFG.js +1 -0
  107. package/src/assets/web-panel/assets/{index-BxYO6nde.js → index-CF0bUOP4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DGKRAO61.js → index-CNjUPfJP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CkjgSHVz.js → index-CRmNdEk_.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CrsM-oiD.js → index-C_oOpXhP.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BNrvBWDS.js → index-ChUTKrOq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CYcjOnDX.js → index-CiIpIYhs.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C02yDu4f.js → index-CwVvk2i7.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DmKdCWq1.js → index-D3_tj06B.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CkqBoCiu.js → index-DE4W4vLO.js} +3 -3
  116. package/src/assets/web-panel/assets/{index-B8L3_-sG.js → index-DLxKx5_J.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Dwob5XoD.js → index-DTXi_gQh.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DuMt3ewl.js → index-Daco83me.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DpI1LqJX.js → index-DffWVpZv.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-lBAbMOzg.js → index-Dkncxel3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DRcDWMwP.js → index-Dn12gJJU.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CVrwozwH.js → index-Dt-nq2eF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C3opQg47.js → index-Du01X9N9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-o_lc3eww.js → index-DucLR6YX.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Cur88hoN.js → index-Gz2rGWef.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-XErJQ1yF.js → index-IvgszJ3C.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BuKGuH_y.js → index-S80LLjdx.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Chxt0t-A.js → index-WKbnwbKa.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-1HBam9t1.js → index-XHBXtvqp.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BXRooi-O.js → index-nn22NSDK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BHGOdvOQ.js → index-pCIAE2yS.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Cf1ha8l0.js → index-pRpMLhMt.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-6f6n0KFR.js → index-pgHGBAoF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BPmRkxeX.js → index-ugIUt5TL.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-C9mvnSii.js → index-xLGf7f9K.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-CIeEUxIN.js → initDefaultProps-C58CkKEo.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-IPIj1ZtP.js → motion-BLWPO2NV.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-BfSeXZ-f.js → move-BgrFMwYp.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-DX03-Xp4.js → mtc-parser-Cq2AuGzl.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-z9EannfX.js → omit-Bb-9L7if.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-DXKmRkqZ.js → pickAttrs-vLmOxYr1.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-k2HV7cIU.js → placementArrow-BNySU_dJ.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-bX7CsyWy.js → responsiveObserve-xnbwagnm.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-NskjQLbJ.js → slide-BccbY5Cn.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-B3IoG-Er.js → statusUtils-DOp9d2-w.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-DHv_UilX.js → styleChecker-Bu1w6ho9.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-DEqGB5Ng.js → useFlexGapSupport-ZsUOrVdL.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-Bl9CbrqZ.js → useFs-C0zpzKtO.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-O-Ukv93M.js → usePersonalDataHub-DO1OdgXK.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-hGCHTaZt.js → vnode-jTIRyhR5.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-BWhIVeKQ.js → zoom-DKvS7-Qs.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/commands/agent.js +56 -20
  154. package/src/commands/ask.js +6 -4
  155. package/src/commands/batch.js +17 -12
  156. package/src/commands/eval.js +24 -8
  157. package/src/commands/team.js +28 -19
  158. package/src/data/changelog.json +26 -0
  159. package/src/harness/mcp-client.js +187 -26
  160. package/src/harness/prompt-compressor.js +2 -0
  161. package/src/lib/advisor-runtime.js +4 -2
  162. package/src/lib/agent-ipc-bus.js +2 -0
  163. package/src/lib/background-agent-keeper-client.js +3 -0
  164. package/src/lib/background-session-transport.js +21 -5
  165. package/src/lib/browser-automation.js +4 -0
  166. package/src/lib/cc-android-bridge.js +4 -0
  167. package/src/lib/chat-core.js +11 -9
  168. package/src/lib/cloud/cloud-client.js +10 -1
  169. package/src/lib/cowork-adapter.js +4 -3
  170. package/src/lib/decision-layer/providers.js +16 -11
  171. package/src/lib/decision-layer/runtime.js +180 -10
  172. package/src/lib/direct-model-usage.js +34 -0
  173. package/src/lib/evolution/agent-evolution-ingress.js +63 -7
  174. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +10 -0
  175. package/src/lib/evolution/evolution-evidence-projector.js +46 -7
  176. package/src/lib/evolution/governed-hub-llm.js +31 -16
  177. package/src/lib/evolution/governed-hub-resolver.js +4 -0
  178. package/src/lib/evolution/governed-skill-synthesis-process-grader.js +4 -0
  179. package/src/lib/evolution/skill-decision-projection.js +211 -0
  180. package/src/lib/evolution/skill-retrieval-revocation-authority.js +17 -2
  181. package/src/lib/evomap-client.js +14 -2
  182. package/src/lib/formal-budget-authority.js +191 -59
  183. package/src/lib/formal-budget-contract.js +7 -13
  184. package/src/lib/formal-budget-errors.js +31 -0
  185. package/src/lib/formal-budget-lineage-registry.js +612 -0
  186. package/src/lib/formal-budget-provider-scope.js +143 -0
  187. package/src/lib/formal-budget-transport.js +235 -100
  188. package/src/lib/gateway-client-resilience.js +5 -0
  189. package/src/lib/hub-llm-client.js +25 -16
  190. package/src/lib/llm-connection-probe.js +17 -10
  191. package/src/lib/llm-providers.js +4 -1
  192. package/src/lib/model-failure-policy.js +2 -0
  193. package/src/lib/plugin-runtime/marketplace-network.js +9 -5
  194. package/src/lib/process-execution-broker/credential-agent.js +7 -2
  195. package/src/lib/process-execution-broker/credential-transport.js +4 -0
  196. package/src/lib/process-execution-broker/index.js +8 -0
  197. package/src/lib/provider-stream.js +28 -13
  198. package/src/lib/sandbox-egress-worker.js +3 -0
  199. package/src/lib/skill-loader.js +16 -0
  200. package/src/lib/stream-retry.js +2 -0
  201. package/src/lib/sub-agent-context.js +6 -3
  202. package/src/lib/web-fetch.js +2 -0
  203. package/src/lib/web-search.js +5 -0
  204. package/src/repl/agent-repl.js +86 -7
  205. package/src/runtime/agent-core.js +141 -22
  206. package/src/runtime/headless-runner.js +212 -22
  207. package/src/runtime/headless-stream.js +189 -25
  208. package/src/runtime/mcp-config.js +26 -17
  209. package/src/runtime/mcp-tool-search.js +44 -14
  210. package/src/assets/web-panel/assets/Projects-GJjItXIk.js +0 -1
  211. package/src/assets/web-panel/assets/devWarning-YPX_TU7w.js +0 -1
  212. package/src/assets/web-panel/assets/index-D53szVZQ.js +0 -1
  213. package/src/assets/web-panel/assets/index-Ub2nJbV2.js +0 -1
@@ -1,4 +1,5 @@
1
1
  import { isAbortError } from "./abort-utils.js";
2
+ import { isFormalBudgetError } from "./formal-budget-errors.js";
2
3
 
3
4
  export function isEvolutionIngressFailure(error) {
4
5
  const visited = new Set();
@@ -21,6 +22,7 @@ export function isTerminalModelFailure(error) {
21
22
  current.code === "CC_AGENT_EVOLUTION_INGRESS_FAILED" ||
22
23
  current.runtimeLedgerPersistence === true ||
23
24
  current.workflowEffectOutcomeUnknown === true ||
25
+ isFormalBudgetError(current) ||
24
26
  /^(?:CC|ERR)_SESSION_BUDGET_/u.test(current.code || "") ||
25
27
  isAbortError(current)
26
28
  ) {
@@ -3,6 +3,10 @@ import fs from "node:fs";
3
3
  import tls from "node:tls";
4
4
  import { Worker } from "node:worker_threads";
5
5
  import { Agent, ProxyAgent, fetch as undiciFetch } from "undici";
6
+ import {
7
+ requireUnscopedDescendantControl,
8
+ requireUnscopedExternalToolAccess,
9
+ } from "../formal-budget-provider-scope.js";
6
10
 
7
11
  export const MAX_MARKETPLACE_CA_BUNDLE_BYTES = 1024 * 1024;
8
12
  export const MAX_MARKETPLACE_PROXY_URL_LENGTH = 4096;
@@ -15,6 +19,7 @@ export const DEFAULT_MARKETPLACE_PAC_TIMEOUT_MS = 2_000;
15
19
  * deliberately ignored: callers pass CLI flags or CC_PLUGIN_REGISTRY_* values.
16
20
  */
17
21
  export function createMarketplaceNetworkTransport(options = {}) {
22
+ requireUnscopedExternalToolAccess();
18
23
  const proxy = normalizeProxyUrl(options.proxyUrl);
19
24
  const pac = options.pacFile ? readMarketplacePacFile(options.pacFile) : null;
20
25
  if (proxy && pac) {
@@ -49,6 +54,7 @@ export function createMarketplaceNetworkTransport(options = {}) {
49
54
 
50
55
  return {
51
56
  fetch: async (url, fetchOptions = {}) => {
57
+ requireUnscopedExternalToolAccess();
52
58
  const route = pac
53
59
  ? parsePacResult(
54
60
  await resolveMarketplacePac(pac.script, url, {
@@ -99,11 +105,9 @@ export function readMarketplacePacFile(file) {
99
105
  };
100
106
  }
101
107
 
102
- export function resolveMarketplacePac(
103
- script,
104
- url,
105
- { timeoutMs = DEFAULT_MARKETPLACE_PAC_TIMEOUT_MS } = {},
106
- ) {
108
+ export function resolveMarketplacePac(script, url, options = {}) {
109
+ requireUnscopedDescendantControl();
110
+ const { timeoutMs = DEFAULT_MARKETPLACE_PAC_TIMEOUT_MS } = options;
107
111
  const timeout = Number(timeoutMs);
108
112
  if (!Number.isInteger(timeout) || timeout < 100 || timeout > 10_000) {
109
113
  throw new Error("Marketplace PAC timeout must be between 100 and 10000 ms");
@@ -14,6 +14,7 @@
14
14
 
15
15
  import crypto from "node:crypto";
16
16
  import { CredentialTransport } from "./credential-transport.js";
17
+ import { requireUnscopedDescendantControl } from "../formal-budget-provider-scope.js";
17
18
 
18
19
  const REDACTED_VALUE = "***REDACTED***";
19
20
  const DEFAULT_TTL_MS = 60_000;
@@ -644,6 +645,7 @@ class CredentialAgent {
644
645
  * self-asserted approval metadata.
645
646
  */
646
647
  issueCredentialRef(request = {}) {
648
+ requireUnscopedDescendantControl();
647
649
  if (!this.isEnabled()) {
648
650
  this._recordAudit({
649
651
  event: "credential_ref_issue",
@@ -842,6 +844,7 @@ class CredentialAgent {
842
844
  * request object; child-provided claims are not sufficient attestation.
843
845
  */
844
846
  resolveCredentialRef(refId, request = {}) {
847
+ requireUnscopedDescendantControl();
845
848
  const refFingerprint = this._fingerprint(refId);
846
849
  if (!this.isEnabled()) {
847
850
  this._recordAudit({
@@ -1002,8 +1005,8 @@ class CredentialAgent {
1002
1005
  _canIssueForContext(context) {
1003
1006
  return Boolean(
1004
1007
  context?.approved === true &&
1005
- context.approvalId &&
1006
- normalizeProcessTarget(context.target?.process ?? context.process),
1008
+ context.approvalId &&
1009
+ normalizeProcessTarget(context.target?.process ?? context.process),
1007
1010
  );
1008
1011
  }
1009
1012
 
@@ -1025,6 +1028,7 @@ class CredentialAgent {
1025
1028
  * Strip sensitive and reserved environment variables from spawn options.
1026
1029
  */
1027
1030
  filterEnvironment(env = process.env, context = null) {
1031
+ requireUnscopedDescendantControl();
1028
1032
  const safeEnv = {};
1029
1033
  const redacted = [];
1030
1034
  const reservedRemoved = [];
@@ -1232,6 +1236,7 @@ class CredentialAgent {
1232
1236
  * Apply filtering and issue references for a broker-approved spawn.
1233
1237
  */
1234
1238
  applyWithReport(spawnOptions) {
1239
+ requireUnscopedDescendantControl();
1235
1240
  if (!spawnOptions || typeof spawnOptions !== "object") {
1236
1241
  throw credentialError(
1237
1242
  CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
@@ -10,6 +10,7 @@ import net from "node:net";
10
10
  import os from "node:os";
11
11
  import path from "node:path";
12
12
  import { Worker } from "node:worker_threads";
13
+ import { requireUnscopedDescendantControl } from "../formal-budget-provider-scope.js";
13
14
 
14
15
  const TRANSPORT_VERSION = "local-ipc-v1";
15
16
  const DEFAULT_TIMEOUT_MS = 5000;
@@ -34,6 +35,7 @@ export function credentialTransportEndpoint(agentId, options = {}) {
34
35
 
35
36
  export class CredentialTransport {
36
37
  constructor(options = {}) {
38
+ requireUnscopedDescendantControl();
37
39
  if (!options.agentId) {
38
40
  throw transportError(
39
41
  "CC_CREDENTIAL_INVALID_REQUEST",
@@ -156,6 +158,7 @@ export class CredentialTransport {
156
158
  }
157
159
 
158
160
  registerCredential(record) {
161
+ requireUnscopedDescendantControl();
159
162
  if (!this.isAvailable()) {
160
163
  throw (
161
164
  this.error ||
@@ -265,6 +268,7 @@ async function connectWithRetry(endpoint, deadline) {
265
268
  * approved target process.
266
269
  */
267
270
  export async function resolveCredentialRefOverTransport(refId, options = {}) {
271
+ requireUnscopedDescendantControl();
268
272
  const env = options.env || process.env;
269
273
  const endpoint = options.endpoint || env.CC_CREDENTIAL_ENDPOINT;
270
274
  const capabilityToken =
@@ -30,6 +30,7 @@ import * as fs from "node:fs";
30
30
  import * as tty from "node:tty";
31
31
  import { createRequire } from "node:module";
32
32
  import { AsyncLocalStorage } from "node:async_hooks";
33
+ import { requireUnscopedDescendantControl } from "../formal-budget-provider-scope.js";
33
34
 
34
35
  const require = createRequire(import.meta.url);
35
36
 
@@ -4692,6 +4693,7 @@ class ProcessExecutionBroker extends EventEmitter {
4692
4693
  }
4693
4694
 
4694
4695
  spawn(command, args, options = {}) {
4696
+ requireUnscopedDescendantControl();
4695
4697
  assertProcessOwnershipAvailable();
4696
4698
  options = this._withAmbientProcessContext(command, options);
4697
4699
  const executionId = crypto.randomUUID();
@@ -5156,6 +5158,7 @@ class ProcessExecutionBroker extends EventEmitter {
5156
5158
  }
5157
5159
 
5158
5160
  spawnSync(command, args, options = {}) {
5161
+ requireUnscopedDescendantControl();
5159
5162
  assertProcessOwnershipAvailable();
5160
5163
  options = this._withAmbientProcessContext(command, options);
5161
5164
  const executionId = crypto.randomUUID();
@@ -5745,6 +5748,7 @@ class ProcessExecutionBroker extends EventEmitter {
5745
5748
  }
5746
5749
 
5747
5750
  exec(command, options, callback) {
5751
+ requireUnscopedDescendantControl();
5748
5752
  const opts = typeof options === "function" ? {} : options;
5749
5753
  const cb = typeof options === "function" ? options : callback;
5750
5754
  if (this._requiresExplicitLinuxWorkspaceShell(opts.cwd || process.cwd())) {
@@ -5763,6 +5767,7 @@ class ProcessExecutionBroker extends EventEmitter {
5763
5767
  }
5764
5768
 
5765
5769
  execSync(command, options = {}) {
5770
+ requireUnscopedDescendantControl();
5766
5771
  if (
5767
5772
  this._requiresExplicitLinuxWorkspaceShell(options.cwd || process.cwd())
5768
5773
  ) {
@@ -5804,6 +5809,7 @@ class ProcessExecutionBroker extends EventEmitter {
5804
5809
  }
5805
5810
 
5806
5811
  execFile(file, args, options, callback) {
5812
+ requireUnscopedDescendantControl();
5807
5813
  if (typeof args === "function") {
5808
5814
  callback = args;
5809
5815
  args = [];
@@ -5897,6 +5903,7 @@ class ProcessExecutionBroker extends EventEmitter {
5897
5903
  }
5898
5904
 
5899
5905
  execFileSync(file, args, options = {}) {
5906
+ requireUnscopedDescendantControl();
5900
5907
  if (!Array.isArray(args)) {
5901
5908
  options = args || {};
5902
5909
  args = [];
@@ -5917,6 +5924,7 @@ class ProcessExecutionBroker extends EventEmitter {
5917
5924
  }
5918
5925
 
5919
5926
  fork(modulePath, args, options = {}) {
5927
+ requireUnscopedDescendantControl();
5920
5928
  return this.spawn(process.execPath, [modulePath, ...(args || [])], {
5921
5929
  ...options,
5922
5930
  origin: options.origin || "fork",
@@ -1,3 +1,7 @@
1
+ import {
2
+ hasFormalBudgetProviderScope,
3
+ providerFetch,
4
+ } from "./formal-budget-provider-scope.js";
1
5
  /**
2
6
  * Provider streaming adapters — shared by `cc stream` and the Hosted Session
3
7
  * API `stream.run` WS route. Each builder returns an AsyncIterable<string>
@@ -47,16 +51,19 @@ export async function* ollamaTokenStream({
47
51
  signal,
48
52
  requireCompletion = false,
49
53
  }) {
50
- const res = await fetch(`${baseUrl}/api/${messages ? "chat" : "generate"}`, {
51
- method: "POST",
52
- headers: { "Content-Type": "application/json" },
53
- body: JSON.stringify(
54
- messages
55
- ? { model, messages, stream: true }
56
- : { model, prompt, stream: true },
57
- ),
58
- signal,
59
- });
54
+ const res = await providerFetch(
55
+ `${baseUrl}/api/${messages ? "chat" : "generate"}`,
56
+ {
57
+ method: "POST",
58
+ headers: { "Content-Type": "application/json" },
59
+ body: JSON.stringify(
60
+ messages
61
+ ? { model, messages, stream: true }
62
+ : { model, prompt, stream: true },
63
+ ),
64
+ signal,
65
+ },
66
+ );
60
67
  if (!res.ok || !res.body) {
61
68
  throw new Error(`Ollama ${res.status} ${res.statusText}`);
62
69
  }
@@ -88,7 +95,8 @@ export async function* openAIStream({
88
95
  signal,
89
96
  requireCompletion = false,
90
97
  }) {
91
- const res = await fetch(`${baseUrl}/chat/completions`, {
98
+ const governed = hasFormalBudgetProviderScope();
99
+ const res = await providerFetch(`${baseUrl}/chat/completions`, {
92
100
  method: "POST",
93
101
  headers: {
94
102
  "Content-Type": "application/json",
@@ -98,17 +106,24 @@ export async function* openAIStream({
98
106
  model,
99
107
  messages: messages ?? [{ role: "user", content: prompt }],
100
108
  stream: true,
109
+ ...(governed ? { stream_options: { include_usage: true } } : {}),
101
110
  }),
102
111
  signal,
103
112
  });
104
113
  if (!res.ok || !res.body) {
105
114
  throw new Error(`${res.status} ${res.statusText}`);
106
115
  }
116
+ let completed = false;
107
117
  for await (const raw of responseLines(res.body)) {
108
118
  const line = raw.trim();
109
119
  if (!line || !line.startsWith("data:")) continue;
110
120
  const payload = line.slice(5).trim();
111
- if (payload === "[DONE]") return;
121
+ if (payload === "[DONE]") {
122
+ completed = true;
123
+ if (!governed) return;
124
+ // A governed stream must reach EOF so durable accounting can settle.
125
+ continue;
126
+ }
112
127
  try {
113
128
  const obj = JSON.parse(payload);
114
129
  if (requireCompletion && obj.error)
@@ -120,7 +135,7 @@ export async function* openAIStream({
120
135
  /* skip */
121
136
  }
122
137
  }
123
- if (requireCompletion)
138
+ if (requireCompletion && !completed)
124
139
  throw new Error("OpenAI stream ended before its completion marker");
125
140
  }
126
141
 
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { Worker } from "node:worker_threads";
7
7
  import path from "node:path";
8
+ import { requireUnscopedDescendantControl } from "./formal-budget-provider-scope.js";
8
9
 
9
10
  const RESPONSE_TIMEOUT_MS = 10_000;
10
11
 
@@ -15,6 +16,7 @@ function workerError(code) {
15
16
  }
16
17
 
17
18
  export async function startEgressProxyWorker(policy, options = {}) {
19
+ requireUnscopedDescendantControl();
18
20
  const bindHost = options.bindHost || "127.0.0.1";
19
21
  const socketPath = options.socketPath || null;
20
22
  if (socketPath) {
@@ -156,6 +158,7 @@ export async function startEgressProxyWorker(policy, options = {}) {
156
158
  return revision;
157
159
  },
158
160
  async updatePolicy(nextPolicy, expectedRevision) {
161
+ requireUnscopedDescendantControl();
159
162
  const response = await request("update", {
160
163
  policy: nextPolicy,
161
164
  expectedRevision,
@@ -392,6 +392,7 @@ export class CLISkillLoader {
392
392
  throw new TypeError("reauthorizeSkill must be a function or null");
393
393
  }
394
394
  this._cache = null;
395
+ this._lastLoadOptions = null;
395
396
  bindSkillRuntimeAdmission(this, skillRuntimeAdmission, {
396
397
  required: skillRuntimeAdmissionRequired,
397
398
  });
@@ -1264,6 +1265,12 @@ export class CLISkillLoader {
1264
1265
  */
1265
1266
  loadAll(options = {}) {
1266
1267
  this._cacheStats.descriptorScans += 1;
1268
+ // Preserve explicit layer policy for a later freshness scan. In particular,
1269
+ // refreshing an opt-in decision must not re-enable a disabled skill layer.
1270
+ this._lastLoadOptions = {
1271
+ ...options,
1272
+ ...(options.env ? { env: { ...options.env } } : {}),
1273
+ };
1267
1274
  // `cc agent --bare` (CC_SKILLS=0): no skill layer loads at all — cache the
1268
1275
  // empty result so getResolvedSkills stays cheap on every call.
1269
1276
  if (allSkillsDisabled(options)) {
@@ -1336,6 +1343,14 @@ export class CLISkillLoader {
1336
1343
  return this.loadAll();
1337
1344
  }
1338
1345
 
1346
+ /** Rescan the same layers under the options used for the current cache. */
1347
+ refreshResolvedSkills() {
1348
+ // A failed scan must not leave a previous descriptor set available as if
1349
+ // it were current on the next lookup.
1350
+ this._cache = null;
1351
+ return this.loadAll(this._lastLoadOptions ?? {});
1352
+ }
1353
+
1339
1354
  /**
1340
1355
  * Get auto-activated persona skills
1341
1356
  * @returns {object[]} skills with category "persona" and activation "auto"
@@ -1367,6 +1382,7 @@ export class CLISkillLoader {
1367
1382
  */
1368
1383
  clearCache() {
1369
1384
  this._cache = null;
1385
+ this._lastLoadOptions = null;
1370
1386
  this._bodyCache.clear();
1371
1387
  this._cacheStats = {
1372
1388
  descriptorScans: 0,
@@ -18,6 +18,7 @@ import {
18
18
  raceWithAbort,
19
19
  throwIfAborted,
20
20
  } from "./abort-utils.js";
21
+ import { isFormalBudgetError } from "./formal-budget-errors.js";
21
22
 
22
23
  /** Bounded auto-retry budget for streaming connection drops (Claude-Code 2.1.181). */
23
24
  export const STREAM_RETRY_MAX = 2;
@@ -243,6 +244,7 @@ export function resolveStreamRetryMax(env = process.env) {
243
244
  * @returns {boolean}
244
245
  */
245
246
  export function isRetryableStreamError(err, signal) {
247
+ if (isFormalBudgetError(err)) return false;
246
248
  if (isAbortError(err)) return false;
247
249
  if (signal && signal.aborted) return false;
248
250
  if (!err) return false;
@@ -21,6 +21,7 @@ import {
21
21
  } from "../runtime/agent-core.js";
22
22
  import { feature } from "./feature-flags.js";
23
23
  import { isEvolutionIngressFailure } from "./model-failure-policy.js";
24
+ import { isFormalBudgetError } from "./formal-budget-errors.js";
24
25
  import { captureAgentEvolutionIngress } from "./evolution/agent-evolution-ingress.js";
25
26
  import {
26
27
  createWorktree,
@@ -609,6 +610,7 @@ export class SubAgentContext {
609
610
  } catch (err) {
610
611
  if (
611
612
  isEvolutionIngressFailure(err) ||
613
+ isFormalBudgetError(err) ||
612
614
  err?.runtimeLedgerPersistence === true ||
613
615
  this._workflowEffectId
614
616
  ) {
@@ -1212,6 +1214,7 @@ export class SubAgentContext {
1212
1214
  } catch (err) {
1213
1215
  if (
1214
1216
  isEvolutionIngressFailure(err) ||
1217
+ isFormalBudgetError(err) ||
1215
1218
  err?.runtimeLedgerPersistence === true ||
1216
1219
  this._workflowEffectId
1217
1220
  ) {
@@ -1543,9 +1546,9 @@ export class SubAgentContext {
1543
1546
  isAborted() {
1544
1547
  return Boolean(
1545
1548
  this._signal?.aborted ||
1546
- this._abortController.signal.aborted ||
1547
- this._runAbortController?.signal.aborted ||
1548
- this.sessionBudget?.signal?.aborted,
1549
+ this._abortController.signal.aborted ||
1550
+ this._runAbortController?.signal.aborted ||
1551
+ this.sessionBudget?.signal?.aborted,
1549
1552
  );
1550
1553
  }
1551
1554
 
@@ -12,6 +12,7 @@ import { URL } from "node:url";
12
12
  import { createHash } from "node:crypto";
13
13
  import { StringDecoder } from "node:string_decoder";
14
14
  import { webFetchSnapshots } from "./web-fetch-snapshots.js";
15
+ import { requireUnscopedExternalToolAccess } from "./formal-budget-provider-scope.js";
15
16
 
16
17
  const DEFAULT_MAX_BYTES = 10_000_000;
17
18
  const DEFAULT_MAX_CHARS = 20_000;
@@ -343,6 +344,7 @@ async function _doRequest(
343
344
  }
344
345
 
345
346
  export async function webFetch(url, options = {}) {
347
+ requireUnscopedExternalToolAccess();
346
348
  const {
347
349
  format = "markdown",
348
350
  maxBytes = DEFAULT_MAX_BYTES,
@@ -14,6 +14,7 @@
14
14
  import http from "http";
15
15
  import https from "https";
16
16
  import { URL } from "url";
17
+ import { requireUnscopedPaidService } from "./formal-budget-provider-scope.js";
17
18
 
18
19
  const DEFAULT_MAX_BYTES = 10_000_000;
19
20
  const DEFAULT_TIMEOUT_MS = 15_000;
@@ -510,6 +511,10 @@ export async function webSearch(query, options = {}) {
510
511
  const q = String(query || "").trim();
511
512
  if (!q) return { error: "web_search: empty query" };
512
513
 
514
+ // A model-token reservation does not price API search or configured proxy
515
+ // services. Reject before resolving keys or opening an HTTP request.
516
+ requireUnscopedPaidService();
517
+
513
518
  const config = options.config || {};
514
519
  const provider = resolveProvider(options, config);
515
520
  let maxResults = normalizeMaxResults(options.maxResults ?? config.maxResults);
@@ -86,7 +86,9 @@ import {
86
86
  } from "../lib/session-host-snapshot.js";
87
87
  import { acquireSessionHostLease } from "../lib/session-host-lease.js";
88
88
  import {
89
+ normalizeSessionBudgetRootConfig,
89
90
  openProductionSessionBudgetRoot,
91
+ readProductionSessionBudget,
90
92
  sessionBudgetAdmissionError,
91
93
  } from "../lib/session-budget-production-root.js";
92
94
  import {
@@ -2265,9 +2267,25 @@ export function prepareReplJsonlResumeCandidate(sessionId, dependencies = {}) {
2265
2267
  error.code = "CC_REPL_SESSION_AUTHORITY_MISMATCH";
2266
2268
  throw error;
2267
2269
  }
2270
+ const declaredBudget = dataDescriptorValue(
2271
+ stateDescriptors,
2272
+ "sessionBudgetRoot",
2273
+ "REPL session host resume state",
2274
+ { required: false },
2275
+ );
2276
+ const sessionBudgetRoot =
2277
+ declaredBudget == null
2278
+ ? null
2279
+ : normalizeSessionBudgetRootConfig(
2280
+ snapshotReplMessageValue(
2281
+ declaredBudget,
2282
+ "REPL session budget declaration",
2283
+ ),
2284
+ );
2268
2285
  return brandedFrozenCandidate(REPL_JSONL_RESUME_CANDIDATES, {
2269
2286
  ok: true,
2270
2287
  sessionId,
2288
+ sessionBudgetRoot,
2271
2289
  rebuiltMessages,
2272
2290
  replayMessages: normalizedReplay.replayMessages,
2273
2291
  canonicalSystemMessages: normalizedReplay.canonicalSystemMessages,
@@ -2786,8 +2804,60 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2786
2804
  ) {
2787
2805
  leaseScope.lease = acquireHostLease(leaseSessionId, { hostKind: "repl" });
2788
2806
  }
2789
- let workspaceOptions = options;
2790
- if (options.sessionBudgetRoot?.enabled === true) {
2807
+ const injectedBudgetReader = dependencyDataFunction(
2808
+ startupDependencies,
2809
+ "readProductionSessionBudget",
2810
+ null,
2811
+ );
2812
+ const sidecar =
2813
+ leaseSessionId &&
2814
+ (process.platform === "linux" || injectedBudgetReader !== null)
2815
+ ? synchronousReplRecoveryValue(
2816
+ (injectedBudgetReader || readProductionSessionBudget)(leaseSessionId),
2817
+ "REPL budget sidecar",
2818
+ )
2819
+ : null;
2820
+ let effectiveBudgetConfig = options.sessionBudgetRoot;
2821
+ const declaredBudgetConfig = startupCandidate?.sessionBudgetRoot;
2822
+ const requestedBudgetConfig = options.sessionBudgetRoot?.enabled
2823
+ ? normalizeSessionBudgetRootConfig(options.sessionBudgetRoot)
2824
+ : null;
2825
+ if (startupCandidate?.ok === true) {
2826
+ if (declaredBudgetConfig?.enabled) {
2827
+ if (!sidecar) {
2828
+ throw Object.assign(
2829
+ new Error("budgeted REPL session is missing its budget sidecar"),
2830
+ { code: "CC_SESSION_BUDGET_SIDECAR_REQUIRED" },
2831
+ );
2832
+ }
2833
+ if (
2834
+ requestedBudgetConfig &&
2835
+ JSON.stringify(requestedBudgetConfig) !==
2836
+ JSON.stringify(declaredBudgetConfig)
2837
+ ) {
2838
+ throw Object.assign(
2839
+ new Error("REPL budget does not match its JSONL declaration"),
2840
+ { code: "CC_SESSION_BUDGET_CONFIG_MISMATCH" },
2841
+ );
2842
+ }
2843
+ effectiveBudgetConfig = declaredBudgetConfig;
2844
+ } else if (requestedBudgetConfig || sidecar) {
2845
+ throw Object.assign(
2846
+ new Error("existing REPL session has no canonical budget declaration"),
2847
+ { code: "CC_SESSION_BUDGET_JSONL_REQUIRED" },
2848
+ );
2849
+ }
2850
+ } else if (sidecar) {
2851
+ throw Object.assign(
2852
+ new Error("budget sidecar has no canonical REPL session"),
2853
+ { code: "CC_SESSION_BUDGET_JSONL_REQUIRED" },
2854
+ );
2855
+ }
2856
+ let workspaceOptions = {
2857
+ ...options,
2858
+ sessionBudgetRoot: effectiveBudgetConfig,
2859
+ };
2860
+ if (effectiveBudgetConfig?.enabled === true) {
2791
2861
  if (!leaseSessionId || !leaseScope?.lease) {
2792
2862
  throw Object.assign(
2793
2863
  new Error(
@@ -2813,10 +2883,10 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2813
2883
  "CC_REPL_STARTUP_DEPENDENCY_INVALID",
2814
2884
  );
2815
2885
  }
2816
- const root = openBudgetRoot(leaseSessionId, options.sessionBudgetRoot, {
2886
+ const root = openBudgetRoot(leaseSessionId, effectiveBudgetConfig, {
2817
2887
  persist: true,
2818
2888
  signal: combineReplSignals(options.signal, leaseScope.lease.signal),
2819
- table: options.sessionBudgetRoot.table,
2889
+ table: effectiveBudgetConfig.table,
2820
2890
  });
2821
2891
  if (
2822
2892
  root?.enabled !== true ||
@@ -2850,7 +2920,7 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2850
2920
  throw admissionError;
2851
2921
  }
2852
2922
  budgetScope.root = root;
2853
- workspaceOptions = { ...options, ...root.options };
2923
+ workspaceOptions = { ...workspaceOptions, ...root.options };
2854
2924
  }
2855
2925
  const trustedWorkspaceRoot = readCwd();
2856
2926
  return enterWorkspace(trustedWorkspaceRoot, () =>
@@ -2867,11 +2937,17 @@ export function startReplJsonlSession(
2867
2937
  options = {},
2868
2938
  ) {
2869
2939
  const requireDurable =
2870
- observabilityScope != null || options.requireDurable === true;
2940
+ observabilityScope != null ||
2941
+ options.requireDurable === true ||
2942
+ options.sessionBudgetRoot?.enabled === true;
2871
2943
  try {
2944
+ const budgetDeclaration = options.sessionBudgetRoot?.enabled
2945
+ ? normalizeSessionBudgetRootConfig(options.sessionBudgetRoot)
2946
+ : null;
2872
2947
  const sessionId = startSession(requestedId, {
2873
2948
  ...meta,
2874
2949
  ...(observabilityScope != null ? { observabilityScope } : {}),
2950
+ ...(budgetDeclaration ? { sessionBudgetRoot: budgetDeclaration } : {}),
2875
2951
  });
2876
2952
  if (requireDurable && (typeof sessionId !== "string" || !sessionId)) {
2877
2953
  throw new Error("JSONL session creation returned no durable session id");
@@ -3966,7 +4042,10 @@ async function startAgentReplInWorkspaceOwned(
3966
4042
  options.sessionId || null,
3967
4043
  meta,
3968
4044
  options.observabilityScope,
3969
- { requireDurable: Boolean(_sessionBudget) },
4045
+ {
4046
+ requireDurable: Boolean(_sessionBudget),
4047
+ sessionBudgetRoot: options.sessionBudgetRoot,
4048
+ },
3970
4049
  );
3971
4050
  if (
3972
4051
  sessionId &&