chainlesschain 0.165.3 → 0.165.4

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 (203) hide show
  1. package/README.md +151 -29
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-BiHNUlbU.js → AIOps-C__nnraW.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-D-UFjDRQ.js → ActionButton-CsKWHEZ-.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-DazVsDgW.js → Analytics-BhIjfjWO.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DXh9UfNJ.js → AppLayout-BjuXFzWZ.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-CZ6tsceY.js → Artifacts-k6whDkZJ.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-RyxnBr_h.js → Audit-C4FBMYRh.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BWS51Q6X.js → BackgroundAgents-C_vRrSXk.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-BRtPcUul.js → Backup-CngFZeVp.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-CGd4vZYY.js → BaseInput-B_H9EBRm.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-h8ijVwCg.js → Chat-DLEC5kt7.js} +4 -4
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-NyF6dPl8.js → Checkbox-m3ClR4xh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-Ci4OOxr5.js → Codegen-CmLSFrgp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CwTkc9BM.js → Col-DFGQteHN.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DW_tM82m.js → Community-EUUVORHE.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DEvHRV4S.js → Compact-2PSg0Yr7.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BbjBEEfD.js → Compliance-CcKur7gB.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-10IXBaBa.js → Cowork-DK_Fezfa.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-BZkbpd5I.js → Cron-Dbw89xl7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CFQ8L_6v.js → Crosschain-CH66h_Ec.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-BDF6AkXe.js → DID-D1igRb17.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CoOdWMi4.js → Dashboard-DVAsrobk.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DEsGmu8Y.js → Dropdown-Dl7W8AQs.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BS89SWlK.js → EmailListRenderer-D5tpaEQC.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C1FpCHiI.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BzvpVmLA.js → Federation-BTVcgS9n.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BeC259PQ.js → FormItemContext-BRnkJAIz.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-azgG3bmV.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BmSSowc5.js → Git-WXLYTQj2.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CTBCSyQZ.js → Governance-BezaUhtc.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-DA5xyk4a.js → Inference-CuRay3Gu.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-D1gPHNig.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-BPmLf1AR.js → Logs-C55y2pPY.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-DtUFcR0E.js → Marketplace-Dk08Uycg.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DS_kqBv-.js → McpTools-C11fSB5V.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-Fvh7XL7V.js → Memory-mr0eZc97.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-C7itAP45.js → MobileBridge-C0iCSRbb.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-CY-xMG8Z.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-B57MiNLK.js → Mtc-Bplqc_Wc.js} +6 -6
  43. package/src/assets/web-panel/assets/{MtcAudit-BXfCUcM7.js → MtcAudit-BlzwsoSM.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-yaJ-ZcF6.js → Multisig-BBLmTty6.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-CoaUCKrO.js → NLProgramming-DtT1uf1W.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BKLM_65R.js → Notes-DcqQAxTK.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DoDtK9uZ.js → NotificationSettings-cb0kWoZe.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-BJ28TGr-.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-MuGsdoIb.js → Organization-DlTlbkUN.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-BLUN9J4R.js → Overflow-pp1qs-oY.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-DWuoBUPd.js → P2P-BESLnyYz.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-CUi37YPj.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-BYQg2pcr.js → Permissions-B36PeT1_.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-CAxoV2xu.js → PersonalDataHub-CTK2ad-3.js} +6 -6
  55. package/src/assets/web-panel/assets/{Pipeline-B33wGvIO.js → Pipeline-zJNGXsac.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-A8yXTORP.js → Privacy-b9fz0Qf0.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-D49I2I1O.js → ProjectInit-DjZbZgiB.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Ci73jJDw.js → ProjectSettings-C-hHJXlD.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-Brza0ykJ.js → QrScannerModal-A0yoQ3-3.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DFydhjxL.js → QuickAsk-DrFgFJNC.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-DLF8_NhA.js → Recommend-KC8hF1g4.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-dyuStW3x.js → RemoteSession-DGTPIk6u.js} +3 -3
  65. package/src/assets/web-panel/assets/{Reputation-5F3tA6yw.js → Reputation-QmbwM7Yk.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-Ca2A57S5.js → Row-BxjJylia.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-B0bxBkq_.js → RssFeed-B7Wda1J6.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-CFNvHalZ.js → Search-_plQ_YSA.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-DSoH3Hav.js → Security-CD2YJZfx.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BHpfEmBQ.js → Services-qQE7NmwE.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-Dsh-QNmn.js → Skeleton-DgQqkFf7.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-BhT1NkSU.js → Skills-C4C0oFFt.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-DY9snPeJ.js → Sla-Px2N2B5c.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-CWaX0_Cx.js → SpeechSettings-DN6C3lQd.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-B690y_TC.js → SyncSettings-jm5aH_m7.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-2N-w3zWd.js → Tasks-DlkJcv67.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-BjwQOQYC.js → Templates-DyrTHUBs.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-CUqHErxY.js → Tenant-mC434PtL.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-C13rXvNy.js → Terminal-o0_s3NgK.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-CpbWcya_.js → TimelineRenderer-DAGlAXOw.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BXHLGXYC.js → Tokens-BnulS_bs.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-1e5i65o3.js → Trigger-suQbVXYM.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-FLGqNHs5.js → Trust-DbIJAPo5.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-BbaR_LJB.js → UkeySign-CQNpbIHi.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-NOp4RIov.js → VideoEditing-D8aWCERk.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-B7DW7bG4.js → Wallet-CuLpYfZc.js} +3 -3
  87. package/src/assets/web-panel/assets/{WebAuthn-BptSUWix.js → WebAuthn-CsfG8ZMc.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CAJnDOXS.js → WorkflowEditor-BYsEAZbd.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-I8LgG1-Z.js → chat-FT4Um3-I.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-CWe7SOVD.js → colors-CKbS_Wlt.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-Cx3u3CA3.js → compact-item-CFJtzuJM.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CgD5ArwY.js → createContext-B9kSRW6K.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-BfQWiXN8.js → hasIn-DR-x0Xpi.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DAVy98u7.js → index-7jHr_A_k.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DlIyt1qF.js → index-B3ZX81mK.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CWZ3REg6.js → index-B7osrXqy.js} +3 -3
  98. package/src/assets/web-panel/assets/{index-DGMYTN5W.js → index-BGbPnC5x.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DFb-XIKy.js → index-BI5hP_qy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CAUnl4Jv.js → index-BK3TiKVq.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BUAR7RY-.js → index-BVPU43b1.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cd79NQAx.js → index-BW-t9n-q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BiI-xPRH.js → index-BfFCQHL3.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
  105. package/src/assets/web-panel/assets/{index-B6yChzt1.js → index-Bt4fS2eL.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-e8BM1yY1.js → index-BwR7yc2h.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-V-kIHzAV.js → index-Bzz0C1BZ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Bx7BNIRH.js → index-C2JvmYRc.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-87gMxqhN.js → index-CGaXUsIs.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cy_cCJlr.js → index-CJmIQ9XR.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C1UjHdgZ.js → index-C_enk9I_.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Bi24S7Dw.js → index-Cr2FLUsD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CcX4FSPf.js → index-CvrCCqJt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DmrdUwzU.js → index-D4LfPvWA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Dcdm_Z2c.js → index-DMGmg497.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DCJEY2T0.js → index-DShSkww-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C0ztiqtB.js → index-DTxNy0ZU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C-rIKccl.js → index-DW4BWjTk.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CdRkCwm4.js → index-DWdqx7zI.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C0nDKhWv.js → index-Dap9c3eD.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CQDKYbnv.js → index-DcfxhKXR.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BuEQSorC.js → index-DdkIhfi-.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-3iKP9OTJ.js → index-DeSgw-WV.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Doc0Ftpd.js → index-DiNUEpSQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BREnPKGo.js → index-DjEP7WkH.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CNYFQxAW.js → index-DtkFqPVA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DslTuqro.js → index-KqDaCApw.js} +1 -1
  128. package/src/assets/web-panel/assets/index-OHSl-xCh.js +1 -0
  129. package/src/assets/web-panel/assets/{index-CMu16vPC.js → index-OSzjTzGM.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DsQcURkA.js → index-QBSeNVO3.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DeizoQVZ.js → index-WE08gMJP.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B1cX4Rxu.js → index-r2VI1Ikk.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DmgtY58H.js → index-tqSEHPRT.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DiBszVdG.js → initDefaultProps-Dd7n7VgI.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BGIFFozv.js → motion-BwxouRw1.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-D5W7mp7B.js → move-DObN0PG9.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-JFIlxJ7F.js → mtc-parser-B3nOUQc7.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-CQ3o6LIU.js → omit-o9WU2Nbl.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-R8MIlXo3.js → pickAttrs-CL_vvfQ_.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-sW7ylSky.js → placementArrow-CYrM2tyK.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Cnl39Vbz.js → responsiveObserve-k_06cQ96.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-sn8tFd14.js → slide-CPKG0pxS.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-IKKY_QxZ.js → statusUtils-BDDEeLTw.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BNpV4Nqs.js → styleChecker-Cmb7RTh7.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-BWyT_eoU.js → useFlexGapSupport-e2IedC4N.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-D09p6_U9.js → useFs-CE_gxVnm.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-CzW3TkJZ.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-3s3WoAKB.js → vnode-DW886XV1.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-Cjy_673s.js → zoom-Dwo_adaf.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +3 -3
  152. package/src/commands/agent.js +105 -35
  153. package/src/commands/artifacts.js +363 -33
  154. package/src/commands/cowork.js +202 -36
  155. package/src/commands/session-budget.js +155 -0
  156. package/src/commands/session-location.js +948 -0
  157. package/src/commands/session.js +72 -46
  158. package/src/data/changelog.json +13 -0
  159. package/src/gateways/ws/artifact-protocol.js +77 -13
  160. package/src/gateways/ws/session-protocol.js +391 -24
  161. package/src/gateways/ws/ws-agent-handler.js +191 -14
  162. package/src/gateways/ws/ws-server.js +14 -3
  163. package/src/gateways/ws/ws-session-gateway.js +101 -5
  164. package/src/harness/jsonl-session-store.js +1210 -0
  165. package/src/lib/agent-team/team-runner.js +43 -13
  166. package/src/lib/artifact-access-ledger.js +461 -0
  167. package/src/lib/artifact-cleanup-ledger.js +861 -0
  168. package/src/lib/artifact-deletion-ledger.js +677 -0
  169. package/src/lib/artifact-store.js +256 -51
  170. package/src/lib/command-lifecycle-report.js +8 -4
  171. package/src/lib/cowork-adapter.js +9 -5
  172. package/src/lib/cowork-task-runner.js +20 -0
  173. package/src/lib/cowork-workflow.js +178 -20
  174. package/src/lib/direct-model-usage.js +62 -22
  175. package/src/lib/dynamic-workflow-runtime.js +3184 -111
  176. package/src/lib/execution-location-result-apply.js +339 -0
  177. package/src/lib/execution-location-result-artifact.js +271 -0
  178. package/src/lib/execution-location-result-review.js +253 -0
  179. package/src/lib/execution-location-result-store.js +331 -0
  180. package/src/lib/execution-location-result.js +617 -0
  181. package/src/lib/execution-location-target.js +236 -1
  182. package/src/lib/managed-tool-checkpoint.js +47 -4
  183. package/src/lib/process-execution-broker/workspace-transaction.js +3 -0
  184. package/src/lib/session-budget-production-root.js +376 -0
  185. package/src/lib/session-budget-runtime.js +109 -18
  186. package/src/lib/session-budget-usage.js +63 -0
  187. package/src/lib/session-host-snapshot.js +19 -2
  188. package/src/lib/session-resource-budget.js +766 -105
  189. package/src/lib/sub-agent-context.js +69 -22
  190. package/src/lib/web-ui-server.js +12 -1
  191. package/src/repl/agent-repl.js +237 -16
  192. package/src/runtime/agent-core.js +123 -0
  193. package/src/runtime/headless-runner.js +135 -46
  194. package/src/runtime/headless-stream.js +133 -25
  195. package/src/runtime/policies/agent-policy.js +10 -0
  196. package/src/assets/web-panel/assets/ChatBubbleRenderer-D7tcdDS9.js +0 -1
  197. package/src/assets/web-panel/assets/MarkdownRenderer-SVLOg49H.js +0 -1
  198. package/src/assets/web-panel/assets/MobileProjects-DGbP36-3.js +0 -1
  199. package/src/assets/web-panel/assets/Projects-CF-yD6Cn.js +0 -1
  200. package/src/assets/web-panel/assets/Providers-D-etEhg5.js +0 -1
  201. package/src/assets/web-panel/assets/devWarning-DLv_872g.js +0 -1
  202. package/src/assets/web-panel/assets/index-CdiGCibe.js +0 -1
  203. package/src/assets/web-panel/assets/index-D_0z-gyz.js +0 -1
@@ -23,6 +23,12 @@ import {
23
23
  import { isGitRepo } from "./git-integration.js";
24
24
  import { markRuntimeLedgerPersistenceError } from "./runtime-usage-ledger.js";
25
25
  import { IterationBudget } from "./iteration-budget.js";
26
+ import {
27
+ beginSessionBudgetUsage,
28
+ markSessionBudgetUsageUnknown,
29
+ recordSessionBudgetUsage,
30
+ rejectSessionBudgetUsageUnknown,
31
+ } from "./session-budget-usage.js";
26
32
 
27
33
  // ─── Constants ──────────────────────────────────────────────────────────────
28
34
 
@@ -162,6 +168,8 @@ export class SubAgentContext {
162
168
  * work cannot begin without a durable parent boundary.
163
169
  * @param {function} [options.onUsageSettlement] - Synchronous observer for
164
170
  * real child token-usage/model-usage-unknown settlements.
171
+ * @param {function} [options.onProviderReceipt] - Synchronous observer for
172
+ * a provider-returned receipt before the matching usage settlement.
165
173
  * @param {function} [options.onToolCallBoundary] - Synchronous observer for
166
174
  * real child tool-executing boundaries.
167
175
  * @param {function} [options.onToolCallSettlement] - Synchronous observer for
@@ -284,6 +292,10 @@ export class SubAgentContext {
284
292
  typeof options.onUsageSettlement === "function"
285
293
  ? options.onUsageSettlement
286
294
  : null;
295
+ this._onProviderReceipt =
296
+ typeof options.onProviderReceipt === "function"
297
+ ? options.onProviderReceipt
298
+ : null;
287
299
  this._onToolCallBoundary =
288
300
  typeof options.onToolCallBoundary === "function"
289
301
  ? options.onToolCallBoundary
@@ -690,6 +702,7 @@ export class SubAgentContext {
690
702
  const pendingToolCalls = new Map();
691
703
  let observeUsageBoundary = null;
692
704
  let observeUsageSettlement = null;
705
+ let observeProviderReceipt = null;
693
706
  let observeToolBoundary = null;
694
707
  let observeToolSettlement = null;
695
708
  const providerReceiptForSettlement = (event) => {
@@ -739,6 +752,17 @@ export class SubAgentContext {
739
752
  requireSyncUsageObserver(this._onUsageSettlement, event, "settlement");
740
753
  pendingUsageCalls.delete(callId);
741
754
  };
755
+ observeProviderReceipt = (event) => {
756
+ const callId = requireUsageCallId(event, "receipt");
757
+ if (!pendingUsageCalls.has(callId)) {
758
+ throw runtimeUsageObserverFailure(
759
+ null,
760
+ "CC_USAGE_RECEIPT_BOUNDARY_MISSING",
761
+ `Child provider receipt has no matching boundary: ${callId}`,
762
+ );
763
+ }
764
+ requireSyncUsageObserver(this._onProviderReceipt, event, "receipt");
765
+ };
742
766
 
743
767
  // agentLoop yields model-usage-started before provider work. Its consumer
744
768
  // runs synchronously before requesting the next event, so a durable host
@@ -747,6 +771,9 @@ export class SubAgentContext {
747
771
  // directly, before its provider query, rather than yielding a start event.
748
772
  options.onUsageBoundary = observeUsageBoundary;
749
773
  options.onUsageSettlement = observeUsageSettlement;
774
+ if (this._onProviderReceipt) {
775
+ options.onProviderReceipt = observeProviderReceipt;
776
+ }
750
777
  options.strictUsageTelemetry = true;
751
778
 
752
779
  observeToolBoundary = (event) => {
@@ -869,21 +896,28 @@ export class SubAgentContext {
869
896
  }
870
897
  }
871
898
  if (event.type === "provider-request-receipt") {
872
- this._providerRequestReceipts.push(
873
- Object.freeze({
874
- protocol: event.protocol,
875
- provider: event.provider,
876
- workflowEffectId: event.workflowEffectId,
877
- callId: event.callId,
878
- callSequence: event.callSequence,
879
- source: event.source,
880
- clientRequestId: event.clientRequestId,
881
- requestId: event.requestId || null,
882
- responseId: event.responseId || null,
883
- requestIdentitySemantics: event.requestIdentitySemantics,
884
- independentlyReadable: event.independentlyReadable,
885
- }),
886
- );
899
+ const receipt = Object.freeze({
900
+ protocol: event.protocol,
901
+ provider: event.provider,
902
+ workflowEffectId: event.workflowEffectId,
903
+ callId: event.callId,
904
+ callSequence: event.callSequence,
905
+ source: event.source,
906
+ clientRequestId: event.clientRequestId,
907
+ requestId: event.requestId || null,
908
+ responseId: event.responseId || null,
909
+ requestIdentitySemantics: event.requestIdentitySemantics,
910
+ independentlyReadable: event.independentlyReadable,
911
+ });
912
+ this._providerRequestReceipts.push(receipt);
913
+ if (strictUsageTelemetry && this._onProviderReceipt) {
914
+ observeProviderReceipt({
915
+ type: "provider-request-receipt",
916
+ ...receipt,
917
+ workflowRequestSource: receipt.source,
918
+ providerReceipt: { ...receipt },
919
+ });
920
+ }
887
921
  }
888
922
 
889
923
  if (event.type === "token-usage") {
@@ -914,13 +948,11 @@ export class SubAgentContext {
914
948
  // call into a permanently open `started` row.
915
949
  // Attributed usage was already charged by the nested child sharing
916
950
  // this same authority, so only direct usage is folded here.
917
- if (!event.attribution && this.sessionBudget?.recordUsage) {
918
- this.sessionBudget.recordUsage({
919
- provider: event.provider || null,
920
- model: event.model || null,
921
- usage: event.usage || null,
922
- });
923
- }
951
+ recordSessionBudgetUsage(
952
+ this.sessionBudget,
953
+ event,
954
+ "sub-agent usage settlement",
955
+ );
924
956
  if (this._onUsage) {
925
957
  // Forward real usage to the spawner. A nested child's event already
926
958
  // carries its own attribution frame — preserve it (deepest wins).
@@ -954,6 +986,11 @@ export class SubAgentContext {
954
986
  observeUsageBoundary(event);
955
987
  }
956
988
  }
989
+ beginSessionBudgetUsage(
990
+ this.sessionBudget,
991
+ event,
992
+ "sub-agent provider call",
993
+ );
957
994
  }
958
995
 
959
996
  if (event.type === "tool-executing" && strictUsageTelemetry) {
@@ -990,6 +1027,10 @@ export class SubAgentContext {
990
1027
  observeUsageSettlement(forwardedUnknown);
991
1028
  forwardedUnknown.ledgerPersisted = true;
992
1029
  }
1030
+ const budgetUsageUnknown = markSessionBudgetUsageUnknown(
1031
+ this.sessionBudget,
1032
+ event,
1033
+ );
993
1034
  if (this._onUsage) {
994
1035
  if (strictUsageTelemetry) {
995
1036
  requireSyncUsageObserver(
@@ -1005,6 +1046,12 @@ export class SubAgentContext {
1005
1046
  }
1006
1047
  }
1007
1048
  }
1049
+ if (budgetUsageUnknown) {
1050
+ rejectSessionBudgetUsageUnknown(
1051
+ event,
1052
+ "sub-agent provider call",
1053
+ );
1054
+ }
1008
1055
  }
1009
1056
 
1010
1057
  if (event.type === "tool-executing") {
@@ -23,6 +23,7 @@ import crypto from "crypto";
23
23
  import { fileURLToPath } from "url";
24
24
  import { getInlineSource as getEnvelopeInlineSource } from "./web-ui-envelope.js";
25
25
  import { CLISkillLoader } from "./skill-loader.js";
26
+ import { authorizeArtifactContentAccess } from "./artifact-access-ledger.js";
26
27
 
27
28
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
29
 
@@ -1333,6 +1334,15 @@ export function handleApiRequest(req, res, ctx = {}) {
1333
1334
  res.end(JSON.stringify({ error: "Stored copy missing" }));
1334
1335
  return;
1335
1336
  }
1337
+ const accessIdHeader = req.headers["x-chainlesschain-access-id"];
1338
+ const authorization = authorizeArtifactContentAccess(store, {
1339
+ artifactId: entry.id,
1340
+ client: "web",
1341
+ action: "download",
1342
+ ...(typeof accessIdHeader === "string"
1343
+ ? { accessId: accessIdHeader }
1344
+ : {}),
1345
+ });
1336
1346
  const downloadName =
1337
1347
  entry.title && path.extname(entry.title)
1338
1348
  ? entry.title
@@ -1343,8 +1353,9 @@ export function handleApiRequest(req, res, ctx = {}) {
1343
1353
  "Content-Disposition": encodeDispositionFilename(downloadName),
1344
1354
  "Cache-Control": "no-store",
1345
1355
  "X-Content-Type-Options": "nosniff",
1356
+ "X-ChainlessChain-Access-Digest": authorization.access.eventDigest,
1346
1357
  });
1347
- const stream = fs.createReadStream(storedPath);
1358
+ const stream = fs.createReadStream(authorization.storedPath);
1348
1359
  stream.on("error", () => res.destroy());
1349
1360
  stream.pipe(res);
1350
1361
  } catch (err) {
@@ -79,6 +79,16 @@ import {
79
79
  readSessionHostResumeState,
80
80
  } from "../lib/session-host-snapshot.js";
81
81
  import { acquireSessionHostLease } from "../lib/session-host-lease.js";
82
+ import {
83
+ openProductionSessionBudgetRoot,
84
+ sessionBudgetAdmissionError,
85
+ } from "../lib/session-budget-production-root.js";
86
+ import {
87
+ beginSessionBudgetUsage,
88
+ markSessionBudgetUsageUnknown,
89
+ recordSessionBudgetUsage,
90
+ rejectSessionBudgetUsageUnknown,
91
+ } from "../lib/session-budget-usage.js";
82
92
  import {
83
93
  SideEffectLedger,
84
94
  classifyToolSideEffect,
@@ -236,6 +246,29 @@ export function resolveReplMeteredSessionId(useJsonl, sessionId) {
236
246
  return useJsonl && sessionId ? sessionId : null;
237
247
  }
238
248
 
249
+ export function combineReplSignals(...signals) {
250
+ const active = [...new Set(signals.filter(Boolean))];
251
+ if (active.length === 0) return null;
252
+ if (active.length === 1) return active[0];
253
+ return AbortSignal.any(active);
254
+ }
255
+
256
+ export async function closeReplSessionBudgetRootScope(scope) {
257
+ const root = scope?.root || null;
258
+ if (!root) return false;
259
+ const closed = await root.close();
260
+ scope.root = null;
261
+ return closed;
262
+ }
263
+
264
+ function releaseReplSessionHostLeaseScope(scope) {
265
+ const lease = scope?.lease || null;
266
+ if (!lease) return false;
267
+ const released = lease.release?.();
268
+ scope.lease = null;
269
+ return released;
270
+ }
271
+
239
272
  function attachReplCompactionLedgerMetadata(error, callId, settled) {
240
273
  let target = error;
241
274
  if (!target || (typeof target !== "object" && typeof target !== "function")) {
@@ -293,6 +326,7 @@ export async function runReplMeteredModelCallWithLedger({
293
326
  provider,
294
327
  model,
295
328
  source = "model",
329
+ sessionBudget = null,
296
330
  call,
297
331
  attachErrorMetadata = false,
298
332
  }) {
@@ -311,6 +345,7 @@ export async function runReplMeteredModelCallWithLedger({
311
345
  provider,
312
346
  model,
313
347
  source,
348
+ sessionBudget,
314
349
  call,
315
350
  });
316
351
  return Object.freeze({ result, callId, usageLedgerSettled });
@@ -545,7 +580,7 @@ async function _persistAlwaysAllow(tool, args) {
545
580
  /**
546
581
  * Execute a tool call — delegates to agent-core with REPL's hookDb and cwd.
547
582
  */
548
- async function executeTool(name, args) {
583
+ async function executeTool(name, args, context = {}) {
549
584
  return coreExecuteTool(name, args, {
550
585
  hookDb: _hookDb,
551
586
  cwd: process.cwd(),
@@ -556,6 +591,9 @@ async function executeTool(name, args) {
556
591
  settingsHooks: _settingsHooks,
557
592
  classifyAllShell: _classifyAllShell,
558
593
  sandbox: _sandbox,
594
+ sessionId: context.sessionId || null,
595
+ sessionBudget: context.sessionBudget || null,
596
+ signal: context.signal || null,
559
597
  });
560
598
  }
561
599
 
@@ -938,6 +976,11 @@ export async function agentLoop(messages, options) {
938
976
  persistTokenUsage(options.sessionId, projectRuntimeTokenUsage(event)),
939
977
  );
940
978
  }
979
+ recordSessionBudgetUsage(
980
+ options.sessionBudget,
981
+ event,
982
+ "REPL usage settlement",
983
+ );
941
984
  } else if (
942
985
  event.type === "model-usage-started" ||
943
986
  event.type === "model-usage-unknown" ||
@@ -966,6 +1009,15 @@ export async function agentLoop(messages, options) {
966
1009
  ),
967
1010
  );
968
1011
  }
1012
+ if (event.type === "model-usage-started") {
1013
+ beginSessionBudgetUsage(
1014
+ options.sessionBudget,
1015
+ event,
1016
+ "REPL provider call",
1017
+ );
1018
+ } else if (markSessionBudgetUsageUnknown(options.sessionBudget, event)) {
1019
+ rejectSessionBudgetUsageUnknown(event, "REPL provider call");
1020
+ }
969
1021
  } else if (event.type === "iteration-warning") {
970
1022
  writeOut(chalk.yellow(`\n ${event.message}\n`));
971
1023
  } else if (event.type === "iteration-budget-exhausted") {
@@ -2630,6 +2682,17 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2630
2682
  }
2631
2683
  return refuseStartup(options, startupCandidate);
2632
2684
  }
2685
+ if (
2686
+ options.sessionBudgetRoot?.enabled === true &&
2687
+ startupAdmission.useJsonl !== true
2688
+ ) {
2689
+ throw Object.assign(
2690
+ new Error(
2691
+ "session budget root requires durable JSONL session persistence, but JSONL storage is unavailable",
2692
+ ),
2693
+ { code: "CC_SESSION_BUDGET_JSONL_REQUIRED" },
2694
+ );
2695
+ }
2633
2696
 
2634
2697
  // Only a frozen, branded admission capability may cross into workspace,
2635
2698
  // pipe, config, plugin, hook, MCP, model, or tool initialization.
@@ -2660,6 +2723,7 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2660
2723
  null,
2661
2724
  );
2662
2725
  const leaseScope = options._sessionHostLeaseScope || null;
2726
+ const budgetScope = options._sessionBudgetRootScope || null;
2663
2727
  const leaseSessionId = startupCandidate?.sessionId || options.sessionId;
2664
2728
  if (
2665
2729
  leaseScope &&
@@ -2669,9 +2733,79 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2669
2733
  ) {
2670
2734
  leaseScope.lease = acquireHostLease(leaseSessionId, { hostKind: "repl" });
2671
2735
  }
2736
+ let workspaceOptions = options;
2737
+ if (options.sessionBudgetRoot?.enabled === true) {
2738
+ if (!leaseSessionId || !leaseScope?.lease) {
2739
+ throw Object.assign(
2740
+ new Error(
2741
+ "session budget root requires an acquired REPL host lease for its durable session",
2742
+ ),
2743
+ { code: "CC_SESSION_BUDGET_LEASE_REQUIRED" },
2744
+ );
2745
+ }
2746
+ if (!budgetScope || budgetScope.root) {
2747
+ throw Object.assign(
2748
+ new Error("REPL session budget root scope is invalid"),
2749
+ { code: "CC_SESSION_BUDGET_SCOPE_INVALID" },
2750
+ );
2751
+ }
2752
+ const openBudgetRoot = dependencyDataFunction(
2753
+ startupDependencies,
2754
+ "openProductionSessionBudgetRoot",
2755
+ null,
2756
+ );
2757
+ if (typeof openBudgetRoot !== "function") {
2758
+ throw invalidReplRecovery(
2759
+ "REPL session budget root dependency is missing",
2760
+ "CC_REPL_STARTUP_DEPENDENCY_INVALID",
2761
+ );
2762
+ }
2763
+ const root = openBudgetRoot(
2764
+ leaseSessionId,
2765
+ options.sessionBudgetRoot,
2766
+ {
2767
+ persist: true,
2768
+ signal: combineReplSignals(options.signal, leaseScope.lease.signal),
2769
+ table: options.sessionBudgetRoot.table,
2770
+ },
2771
+ );
2772
+ if (
2773
+ root?.enabled !== true ||
2774
+ !root.budget ||
2775
+ !root.options ||
2776
+ root.options.sessionBudget !== root.budget ||
2777
+ typeof root.close !== "function"
2778
+ ) {
2779
+ try {
2780
+ root?.close?.();
2781
+ } catch {
2782
+ // The invalid root is rejected regardless of cleanup outcome.
2783
+ }
2784
+ throw Object.assign(new Error("REPL session budget root is invalid"), {
2785
+ code: "CC_SESSION_BUDGET_ROOT_INVALID",
2786
+ });
2787
+ }
2788
+ if (root.budget.signal?.aborted === true) {
2789
+ const admissionError = sessionBudgetAdmissionError(
2790
+ root.budget.reason?.(),
2791
+ "REPL startup",
2792
+ );
2793
+ try {
2794
+ await root.close();
2795
+ } catch (cleanupError) {
2796
+ throw new AggregateError(
2797
+ [admissionError, cleanupError],
2798
+ "REPL budget admission and authority cleanup both failed",
2799
+ );
2800
+ }
2801
+ throw admissionError;
2802
+ }
2803
+ budgetScope.root = root;
2804
+ workspaceOptions = { ...options, ...root.options };
2805
+ }
2672
2806
  const trustedWorkspaceRoot = readCwd();
2673
2807
  return enterWorkspace(trustedWorkspaceRoot, () =>
2674
- startWorkspace(options, startupAdmission),
2808
+ startWorkspace(workspaceOptions, startupAdmission),
2675
2809
  );
2676
2810
  }
2677
2811
 
@@ -2681,26 +2815,34 @@ export function startReplJsonlSession(
2681
2815
  requestedId,
2682
2816
  meta,
2683
2817
  observabilityScope,
2818
+ options = {},
2684
2819
  ) {
2820
+ const requireDurable =
2821
+ observabilityScope != null || options.requireDurable === true;
2685
2822
  try {
2686
2823
  const sessionId = startSession(requestedId, {
2687
2824
  ...meta,
2688
2825
  ...(observabilityScope != null ? { observabilityScope } : {}),
2689
2826
  });
2690
2827
  if (
2691
- observabilityScope != null &&
2828
+ requireDurable &&
2692
2829
  (typeof sessionId !== "string" || !sessionId)
2693
2830
  ) {
2694
2831
  throw new Error("JSONL session creation returned no durable session id");
2695
2832
  }
2696
2833
  return sessionId || null;
2697
2834
  } catch (error) {
2698
- if (observabilityScope == null) return null;
2835
+ if (!requireDurable) return null;
2699
2836
  const failure = new Error(
2700
- "scoped JSONL session could not be durably created",
2837
+ observabilityScope != null
2838
+ ? "scoped JSONL session could not be durably created"
2839
+ : "budgeted JSONL session could not be durably created",
2701
2840
  { cause: error },
2702
2841
  );
2703
- failure.code = "CC_OBSERVABILITY_SCOPE_START_FAILED";
2842
+ failure.code =
2843
+ observabilityScope != null
2844
+ ? "CC_OBSERVABILITY_SCOPE_START_FAILED"
2845
+ : "CC_SESSION_BUDGET_SESSION_START_FAILED";
2704
2846
  throw failure;
2705
2847
  }
2706
2848
  }
@@ -2708,23 +2850,45 @@ export function startReplJsonlSession(
2708
2850
  /** Start the agentic REPL with non-overridable production bindings. */
2709
2851
  export async function startAgentRepl(options = {}) {
2710
2852
  const sessionHostLeaseScope = { lease: null };
2853
+ const sessionBudgetRootScope = { root: null };
2711
2854
  const runtimeOptions = {
2712
2855
  ...options,
2713
2856
  _sessionHostLeaseScope: sessionHostLeaseScope,
2857
+ _sessionBudgetRootScope: sessionBudgetRootScope,
2714
2858
  };
2715
2859
  try {
2716
2860
  const result = await runReplStartupBoundary(runtimeOptions, {
2717
2861
  prepareReplStartupResume,
2718
2862
  refuseReplStartupResume,
2719
2863
  acquireSessionHostLease,
2864
+ openProductionSessionBudgetRoot,
2720
2865
  cwd: () => process.cwd(),
2721
2866
  runWithHostHooksV2Workspace,
2722
2867
  startAgentReplInWorkspace,
2723
2868
  });
2724
- if (result?.started === false) sessionHostLeaseScope.lease?.release?.();
2869
+ if (result?.started === false) {
2870
+ await closeReplSessionBudgetRootScope(sessionBudgetRootScope);
2871
+ releaseReplSessionHostLeaseScope(sessionHostLeaseScope);
2872
+ }
2725
2873
  return result;
2726
2874
  } catch (error) {
2727
- sessionHostLeaseScope.lease?.release?.();
2875
+ const cleanupErrors = [];
2876
+ try {
2877
+ await closeReplSessionBudgetRootScope(sessionBudgetRootScope);
2878
+ } catch (cleanupError) {
2879
+ cleanupErrors.push(cleanupError);
2880
+ }
2881
+ try {
2882
+ releaseReplSessionHostLeaseScope(sessionHostLeaseScope);
2883
+ } catch (cleanupError) {
2884
+ cleanupErrors.push(cleanupError);
2885
+ }
2886
+ if (cleanupErrors.length > 0) {
2887
+ throw new AggregateError(
2888
+ [error, ...cleanupErrors],
2889
+ "REPL startup and authority cleanup both failed",
2890
+ );
2891
+ }
2728
2892
  throw error;
2729
2893
  }
2730
2894
  }
@@ -2802,6 +2966,8 @@ async function startAgentReplInWorkspaceOwned(
2802
2966
  }
2803
2967
  };
2804
2968
  const _sessionHostLeaseScope = options._sessionHostLeaseScope || null;
2969
+ const _sessionBudgetRootScope = options._sessionBudgetRootScope || null;
2970
+ const _sessionBudget = options.sessionBudget || null;
2805
2971
  let _sessionHostLeaseAbortListener = null;
2806
2972
  let _sessionHostLeaseAbortSignal = null;
2807
2973
  const _detachSessionHostLease = () => {
@@ -2840,6 +3006,35 @@ async function startAgentReplInWorkspaceOwned(
2840
3006
  return previous;
2841
3007
  };
2842
3008
  _attachSessionHostLease();
3009
+ let _sessionBudgetAbortListener = null;
3010
+ let _sessionBudgetAbortSignal = null;
3011
+ const _detachSessionBudget = () => {
3012
+ if (_sessionBudgetAbortListener && _sessionBudgetAbortSignal) {
3013
+ _sessionBudgetAbortSignal.removeEventListener(
3014
+ "abort",
3015
+ _sessionBudgetAbortListener,
3016
+ );
3017
+ }
3018
+ _sessionBudgetAbortListener = null;
3019
+ _sessionBudgetAbortSignal = null;
3020
+ };
3021
+ const _attachSessionBudget = () => {
3022
+ const signal = _sessionBudget?.signal;
3023
+ if (!signal || _sessionBudgetAbortListener) return;
3024
+ _sessionBudgetAbortListener = () =>
3025
+ _requestReplOutputClose(
3026
+ sessionBudgetAdmissionError(
3027
+ _sessionBudget.reason?.() || "session-aborted",
3028
+ "REPL session",
3029
+ ),
3030
+ );
3031
+ _sessionBudgetAbortSignal = signal;
3032
+ signal.addEventListener("abort", _sessionBudgetAbortListener, {
3033
+ once: true,
3034
+ });
3035
+ if (signal.aborted) _sessionBudgetAbortListener();
3036
+ };
3037
+ _attachSessionBudget();
2843
3038
  const _replOutputFlow = outputScope.flow;
2844
3039
  outputScope.setFailureHandler(_requestReplOutputClose);
2845
3040
  const _disposeReplPipeSafety = installPipeSafety(undefined, () =>
@@ -2895,7 +3090,8 @@ async function startAgentReplInWorkspaceOwned(
2895
3090
  provider: callProvider || provider,
2896
3091
  model: callModel || model,
2897
3092
  source,
2898
- call,
3093
+ sessionBudget: _sessionBudget,
3094
+ call: () => call({ signal: options.signal || null }),
2899
3095
  attachErrorMetadata: includeLedgerMetadata,
2900
3096
  });
2901
3097
  return includeLedgerMetadata ? metered : metered.result;
@@ -2926,7 +3122,12 @@ async function startAgentReplInWorkspaceOwned(
2926
3122
  sessionId: durableSessionId,
2927
3123
  tool,
2928
3124
  args,
2929
- execute: executeTool,
3125
+ execute: (name, toolArgs) =>
3126
+ executeTool(name, toolArgs, {
3127
+ sessionId: durableSessionId,
3128
+ sessionBudget: _sessionBudget,
3129
+ signal: options.signal || null,
3130
+ }),
2930
3131
  now: options.now || Date.now,
2931
3132
  terminalLatch: _runtimeLedgerTerminalLatch,
2932
3133
  });
@@ -3089,13 +3290,13 @@ async function startAgentReplInWorkspaceOwned(
3089
3290
  callModel: model,
3090
3291
  source: "semantic-compaction",
3091
3292
  includeLedgerMetadata: true,
3092
- call: () =>
3293
+ call: ({ signal }) =>
3093
3294
  chatWithTools([{ role: "user", content: prompt }], {
3094
3295
  provider,
3095
3296
  model,
3096
3297
  baseUrl,
3097
3298
  apiKey,
3098
- signal: _sessionHostLeaseScope?.lease?.signal,
3299
+ signal,
3099
3300
  enabledToolNames: [],
3100
3301
  extraToolDefinitions: [],
3101
3302
  hostManagedToolPolicy: null,
@@ -3535,6 +3736,7 @@ async function startAgentReplInWorkspaceOwned(
3535
3736
  options.sessionId || null,
3536
3737
  meta,
3537
3738
  options.observabilityScope,
3739
+ { requireDurable: Boolean(_sessionBudget) },
3538
3740
  );
3539
3741
  if (
3540
3742
  sessionId &&
@@ -4410,7 +4612,11 @@ async function startAgentReplInWorkspaceOwned(
4410
4612
  callProvider: request?.provider,
4411
4613
  callModel: request?.model,
4412
4614
  source: "model",
4413
- call: () => invokeToolFreeAdvisor(request),
4615
+ call: ({ signal }) =>
4616
+ invokeToolFreeAdvisor({
4617
+ ...request,
4618
+ signal: combineReplSignals(request?.signal, signal),
4619
+ }),
4414
4620
  }),
4415
4621
  onEvent: (event) => {
4416
4622
  if (!useJsonl || !sessionId) return;
@@ -6953,6 +7159,14 @@ async function startAgentReplInWorkspaceOwned(
6953
7159
  if (sessionArg.startsWith("resume ")) {
6954
7160
  const resumeId = sessionArg.slice(7).trim();
6955
7161
  try {
7162
+ if (_sessionBudget && resumeId !== sessionId) {
7163
+ throw Object.assign(
7164
+ new Error(
7165
+ "a budgeted REPL is bound to its startup session; exit and restart to resume another session",
7166
+ ),
7167
+ { code: "CC_SESSION_BUDGET_REPL_SWITCH_UNSUPPORTED" },
7168
+ );
7169
+ }
6956
7170
  if (useJsonl) {
6957
7171
  const initialCandidate = _prepareJsonlResumeCandidate(resumeId);
6958
7172
  if (!initialCandidate.ok) throw initialCandidate.error;
@@ -9084,7 +9298,7 @@ async function startAgentReplInWorkspaceOwned(
9084
9298
  // of hanging forever. undefined → agent-core's 180s default (matches cc
9085
9299
  // chat/ask); config.llm.streamStallTimeoutMs tunes or disables (0).
9086
9300
  streamStallTimeoutMs: _streamStallTimeoutMs,
9087
- signal: _turnAbort.signal,
9301
+ signal: combineReplSignals(_turnAbort.signal, options.signal),
9088
9302
  // On an auto-detected image turn, switch to the vision LLM for this
9089
9303
  // turn only (provider/baseUrl/apiKey unchanged, model → vision model).
9090
9304
  provider: _visionLlm ? _visionLlm.provider : provider,
@@ -9200,12 +9414,13 @@ async function startAgentReplInWorkspaceOwned(
9200
9414
  callProvider: provider,
9201
9415
  callModel: activeModel,
9202
9416
  source: "model",
9203
- call: () =>
9417
+ call: ({ signal }) =>
9204
9418
  chatWithTools([{ role: "user", content: p }], {
9205
9419
  model: activeModel,
9206
9420
  provider,
9207
9421
  baseUrl,
9208
9422
  apiKey,
9423
+ signal,
9209
9424
  enabledToolNames: [],
9210
9425
  }),
9211
9426
  });
@@ -9743,9 +9958,15 @@ async function startAgentReplInWorkspaceOwned(
9743
9958
  }
9744
9959
 
9745
9960
  // Shutdown runtime
9961
+ _detachSessionBudget();
9962
+ try {
9963
+ await closeReplSessionBudgetRootScope(_sessionBudgetRootScope);
9964
+ } catch (error) {
9965
+ _replOutputFailure ||= error;
9966
+ }
9746
9967
  _detachSessionHostLease();
9747
9968
  try {
9748
- _sessionHostLeaseScope?.lease?.release?.();
9969
+ releaseReplSessionHostLeaseScope(_sessionHostLeaseScope);
9749
9970
  } catch (error) {
9750
9971
  _replOutputFailure ||= error;
9751
9972
  }