chainlesschain 0.162.193 → 0.162.198

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 (215) hide show
  1. package/README.md +29 -6
  2. package/completions/_cc +2 -2
  3. package/completions/cc.bash +2 -2
  4. package/completions/cc.fish +2 -2
  5. package/completions/cc.ps1 +2 -2
  6. package/package.json +1 -1
  7. package/src/assets/web-panel/assets/{AIOps-DBLkaTje.js → AIOps-CSN-D2xb.js} +1 -1
  8. package/src/assets/web-panel/assets/{ActionButton-BAy-Qebo.js → ActionButton-CBSMlGzv.js} +1 -1
  9. package/src/assets/web-panel/assets/{Analytics-Dfb1Mlau.js → Analytics-3yr3Nt2t.js} +3 -3
  10. package/src/assets/web-panel/assets/{AppLayout-BbFkN8m0.js → AppLayout-1b-o3lMH.js} +4 -4
  11. package/src/assets/web-panel/assets/{Artifacts-kM4-cEEH.js → Artifacts-Dr8rJi17.js} +1 -1
  12. package/src/assets/web-panel/assets/{Audit-BSQfYWf3.js → Audit-2V689HC2.js} +1 -1
  13. package/src/assets/web-panel/assets/{BackgroundAgents-CWDbI_fG.js → BackgroundAgents-DIxkNrD_.js} +1 -1
  14. package/src/assets/web-panel/assets/{Backup-BxLPbq_Q.js → Backup-Pg6BcwrE.js} +1 -1
  15. package/src/assets/web-panel/assets/{BaseInput-DuIbdrp6.js → BaseInput-CLUpqyfH.js} +1 -1
  16. package/src/assets/web-panel/assets/{Chat-NEmjGahU.js → Chat-qVS8rD4G.js} +5 -5
  17. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DfJEo-5_.js → ChatBubbleRenderer-ti49X12Q.js} +1 -1
  18. package/src/assets/web-panel/assets/{Checkbox-CaoeG3Ns.js → Checkbox-Bs9AaEay.js} +1 -1
  19. package/src/assets/web-panel/assets/{Codegen-BFf_g3xY.js → Codegen-Dvc1mq8q.js} +1 -1
  20. package/src/assets/web-panel/assets/{Col-CNOd8mdQ.js → Col-C4_biO7A.js} +1 -1
  21. package/src/assets/web-panel/assets/{Community-BhpDqz-i.js → Community-al25XQUV.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compact-CQ42T7Z6.js → Compact-D-ZDB8M1.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compliance-DI5PcLMN.js → Compliance-6lIfDz1W.js} +1 -1
  24. package/src/assets/web-panel/assets/{Cowork-wYYZe0wR.js → Cowork-DdI48XMJ.js} +3 -3
  25. package/src/assets/web-panel/assets/{Cron-Dlq03DD_.js → Cron-DF5HZd2g.js} +2 -2
  26. package/src/assets/web-panel/assets/{Crosschain-BzqS8PP3.js → Crosschain-DuxeSvt3.js} +1 -1
  27. package/src/assets/web-panel/assets/{DID-It55Yem7.js → DID-B9yHkUTd.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dashboard-9HkH6oxn.js → Dashboard-Z5EuI3IM.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dropdown-00e2YWRB.js → Dropdown-vuRfO3dY.js} +1 -1
  30. package/src/assets/web-panel/assets/{EmailListRenderer-By-ugRDL.js → EmailListRenderer-QFLCc1uL.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C396yu5m.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-BME5sbZY.js → Federation-DycIEg2s.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-B4TEZCr-.js → FormItemContext-Ba0H_7yG.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-_9CX9ieY.js → GenericCardRenderer--JgSTK-C.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-BrS6WVSr.js → Git-DEvTbldE.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-BMdCWnKF.js → Governance-BJIljy18.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-Bcp3vzcA.js → Inference-CVyW3G3q.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-DlLuqGY5.js → KnowledgeGraph-v8-borge.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-D-W_6x09.js → Logs-BCdS7gGo.js} +2 -2
  40. package/src/assets/web-panel/assets/{MarkdownRenderer-DElGPxCZ.js → MarkdownRenderer-CU-JB2Ns.js} +1 -1
  41. package/src/assets/web-panel/assets/{Marketplace-Dzn2guvI.js → Marketplace-AN6tGA2B.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-uPict-KL.js → McpTools-DGViXASN.js} +6 -6
  43. package/src/assets/web-panel/assets/{Memory-BCw43kOb.js → Memory-Bpvmx9X4.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-DmV8Wv7F.js → MobileBridge-CTP-DOTC.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-C-HaCmSM.js → MobileProjects-C8qfktxn.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-C6jxJ34Y.js → Mtc-ndYr0M1s.js} +5 -5
  47. package/src/assets/web-panel/assets/{MtcAudit-Za8ExwhQ.js → MtcAudit-Ci0-YK7W.js} +5 -5
  48. package/src/assets/web-panel/assets/{Multisig-KKDbuEgb.js → Multisig-BehSTFW7.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-Dj97zbCk.js → NLProgramming-B91_YBLv.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-DBXvMraU.js → Notes-BQlJ0-gq.js} +4 -4
  51. package/src/assets/web-panel/assets/{NotificationSettings-DOxITLYl.js → NotificationSettings-CDgYMjYK.js} +1 -1
  52. package/src/assets/web-panel/assets/{OrderTableRenderer-7DVjZwMM.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
  53. package/src/assets/web-panel/assets/{Organization-D1i73mjb.js → Organization-B-b3cWCp.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-kW6Q3rvg.js → Overflow-CILUL4nL.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-C-v_8p2v.js → P2P-BTCFBkF0.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-tUbbY64W.js → PdhVaultBrowser-BAR6AUx5.js} +5 -5
  57. package/src/assets/web-panel/assets/{Permissions-DkHxEZ58.js → Permissions-RRf6HbWJ.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-DWuMPGrK.js → PersonalDataHub-BGTMiICa.js} +4 -4
  59. package/src/assets/web-panel/assets/{Pipeline-30IYXWWm.js → Pipeline-945qY9YF.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-DZNQ4RrH.js → Privacy-DPZwI6lJ.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-De2MCElf.js → ProjectInit-BPH2NRKc.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-5uHk9wsC.js → ProjectSettings-BZCRNCvb.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-XjDfrFLb.js → Projects-BTI__tBt.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-Bgwj6gcE.js → Providers-DDCf_nIw.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-gKql9umv.js → QrScannerModal-N6rbqt1r.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-BWvmwkh7.js → QuickAsk-DrwjPS7M.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-Uok4hZPd.js → Recommend-Cog9Wd99.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-DLJzTk9a.js → RemoteSession-DC9pynjM.js} +3 -3
  69. package/src/assets/web-panel/assets/{Reputation-ZVwIPMSO.js → Reputation-D4th4gKS.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-CN04HxWR.js → Row-ogiWR6v9.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BdSrEG4g.js → RssFeed-uYlk-2Oj.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-5PwVd0IR.js → Search-DYzzwvtM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-DdBo4zjN.js → Security-Da7YOyX6.js} +4 -4
  74. package/src/assets/web-panel/assets/{Services-D3letU7M.js → Services-CLI1VM1X.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton--Jb83f1v.js → Skeleton-DK77dzDC.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-Bg-CulHX.js → Skills-C_z7Lmhl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-7QOa7S-X.js → Sla-D6413Q8L.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-DQwppjRw.js → SpeechSettings-C07ydbj2.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-dNPQGL-s.js → SyncSettings-DGOm2W22.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-Ggs-Z48q.js → Tasks-Cr_PVyLM.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-78TYHgRO.js → Templates-BhL2mZ0t.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-xHwwv9vO.js → Tenant-CPk5oyUr.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-BJXSJfm4.js → Terminal-ClhqUsqV.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-fLbjRhJw.js → TimelineRenderer-BCC7MV5a.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-oMSBSBmB.js → Tokens-DD1Jt4oO.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-CfnY_wX_.js → Trigger-BCydFvN2.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-AdyTgYKO.js → Trust-DDHT1noa.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-C5K_81dS.js → UkeySign-CWPZC7oI.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-vcGVDqki.js → VideoEditing-9Ea0nJR0.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-hhOc3hlY.js → Wallet-BjxhLxTf.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CVloC11s.js → WebAuthn-C6uCye67.js} +4 -4
  92. package/src/assets/web-panel/assets/{WorkflowEditor-CYeE32R5.js → WorkflowEditor-d2g4reYo.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-CNROR5bN.js → chat-frTc6ScX.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-Cjx-cnwU.js → colors-B5K6lG3v.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-DOIfelvD.js → compact-item-Cdn46r8D.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-DfA7UZ8u.js → createContext-DU1jqVQi.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-BMSCzcfM.js → hasIn-DdNH-aW_.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CAnvhwg6.js → index-32ajXmTb.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BEMwBBHk.js → index-7ehytj1K.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CVL27kXC.js → index-BC4eGkar.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-FBc_CJBd.js → index-BVLL-mq_.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-dCd8cSUq.js → index-BXKhhx9V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BemJ2u_E.js → index-BcGwGIaR.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CVm1_i3t.js → index-Be4OtL-N.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Cs6l7uRu.js → index-BoFak7le.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DPAWBrMf.js → index-BwlIxAmX.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DLmn0BoK.js → index-CGiwfwB9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B7gf77CJ.js → index-CHmkkdfT.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-m2vG9gG_.js → index-CQorP1Ng.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ClNMrqEY.js → index-CUoNAmE8.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DHtpHZAQ.js → index-Cj6P9Nst.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C2K9SNVX.js → index-CtRPi4YC.js} +1 -1
  114. package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
  115. package/src/assets/web-panel/assets/{index-BolaOQxx.js → index-D1qIC4GR.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BM1y_9hM.js → index-D23xGYjM.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Cyweh0Hy.js → index-D4l82Me1.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-By61ojUx.js → index-D4miNkWv.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BbQ2eQ8H.js → index-DIVyTmZ3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Icdoqmzt.js → index-DM5FmPZU.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DjEsxO5h.js → index-DRzBBdQ0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B4iYzpkL.js → index-Dca_i7H8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C3L5gdiD.js → index-DftZuyA7.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CdYBye8B.js → index-DtBCCkC4.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-AWg-0M1S.js → index-HjCvXHf5.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CEKMcOyU.js → index-LaivuXyF.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BxOk9vkG.js → index-MM4Zz2OE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CNC5onm4.js → index-NmEXVjzt.js} +1 -1
  129. package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
  130. package/src/assets/web-panel/assets/{index-BUonmKSE.js → index-PjAS0sIZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B5Udeb_6.js → index-VDM2KKo9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-5-HXgOX5.js → index-cYaWV4Mq.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DBpWXFed.js → index-gg3QPC3i.js} +3 -3
  134. package/src/assets/web-panel/assets/{index-C7xn0Nub.js → index-kVO_MDm-.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-ti_lnTWl.js → index-mgU3Q-lN.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CshQbilz.js → index-t_OBzIP4.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-mTpHoEzr.js → index-yZRuZpwK.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-Bu8k7H1Y.js → initDefaultProps-DwLTRNdo.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-s3iVK41k.js → motion-BchZDXnh.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-B6B-E5Jz.js → move-e8M-zZFl.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BiYgTidA.js → mtc-parser-Ci40fQb3.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-pU1RdUDA.js → omit-C7110_f2.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-Cdoq_SeM.js → pickAttrs-skteMgnM.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-DIuvAn2K.js → placementArrow-MGZ-lh2B.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-8w16YR2D.js → responsiveObserve-N4Sd7uy-.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-VZ01cZe3.js → slide-C7HDGRaa.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-lh8YMN8Z.js → statusUtils-BhRexlgy.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-CmDyreec.js → styleChecker-ChEoQkN2.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-hiJWMU5y.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-Thnu-Iaj.js → useFs-CUIx0tcJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-LBdOxjwY.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-Cg6lFY_X.js → vnode-B_ZvABIG.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-DVfsR1YO.js → zoom-D5shjpS8.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-help-index.json +4 -4
  156. package/src/command-manifest.json +214 -50
  157. package/src/command-surface-policy.js +115 -0
  158. package/src/commands/agent.js +3 -3
  159. package/src/commands/background-session.js +145 -0
  160. package/src/commands/mcp.js +125 -7
  161. package/src/commands/serve.js +1 -1
  162. package/src/commands/session-show.js +131 -0
  163. package/src/commands/session.js +50 -106
  164. package/src/commands/skill.js +87 -50
  165. package/src/data/changelog.json +18 -5
  166. package/src/gateways/ws/ws-agent-handler.js +8 -1
  167. package/src/harness/jsonl-session-store.js +428 -25
  168. package/src/harness/mcp-client.js +2652 -404
  169. package/src/harness/session-list-index.js +65 -7
  170. package/src/lazy-dispatch.js +76 -8
  171. package/src/lib/abort-utils.js +68 -0
  172. package/src/lib/agent-core.js +1 -59
  173. package/src/lib/chat-core.js +24 -6
  174. package/src/lib/cowork-mcp-tools.js +47 -2
  175. package/src/lib/cowork-task-runner.js +11 -3
  176. package/src/lib/doctor-checkup.js +9 -7
  177. package/src/lib/learning/skill-improver.js +21 -2
  178. package/src/lib/mcp-call-ledger-store.js +116 -3
  179. package/src/lib/mcp-call-ledger.js +59 -14
  180. package/src/lib/mcp-client.js +1 -0
  181. package/src/lib/mcp-stdio-executable-identity.js +598 -0
  182. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  183. package/src/lib/mcp-tool-metadata.js +436 -0
  184. package/src/lib/mcp-tool-result.js +367 -0
  185. package/src/lib/observability/index.js +71 -0
  186. package/src/lib/pr-link-ledger.js +8 -27
  187. package/src/lib/pr-link-store.js +26 -0
  188. package/src/lib/process-execution-broker/index.js +14 -0
  189. package/src/lib/process-tree-termination.js +253 -0
  190. package/src/lib/public-ide-capability-manifest.js +6 -0
  191. package/src/lib/session-host-snapshot.js +9 -5
  192. package/src/lib/session-projection.js +5 -1
  193. package/src/lib/session-tail.js +31 -1
  194. package/src/lib/skill-budget.js +222 -0
  195. package/src/lib/skill-execution-authority.js +381 -0
  196. package/src/lib/skill-execution-identity.js +39 -2
  197. package/src/lib/skill-loader.js +322 -86
  198. package/src/lib/skill-mcp.js +43 -1
  199. package/src/repl/agent-repl.js +349 -46
  200. package/src/repl/bg-dashboard.js +81 -28
  201. package/src/repl/chat-repl.js +77 -12
  202. package/src/repl/prompt-interactions.js +67 -7
  203. package/src/repl/slash-command-registry.js +3 -4
  204. package/src/runtime/agent-core.js +340 -108
  205. package/src/runtime/agent-runtime.js +26 -3
  206. package/src/runtime/headless-runner.js +150 -23
  207. package/src/runtime/headless-stream.js +309 -59
  208. package/src/runtime/mcp-config.js +157 -10
  209. package/src/runtime/mcp-tool-search.js +0 -0
  210. package/src/runtime/output-backpressure.js +366 -0
  211. package/src/runtime/pipe-safety.js +56 -21
  212. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +0 -1
  213. package/src/assets/web-panel/assets/index-Q2IajL54.js +0 -1
  214. package/src/assets/web-panel/assets/index-bRkHgOMc.js +0 -1
  215. package/src/lib/skill-process-broker.js +0 -85
@@ -36,6 +36,11 @@ import {
36
36
  } from "../lib/lsp/workspace-roots.js";
37
37
  import { getPlanModeManager } from "../lib/plan-mode.js";
38
38
  import { CLISkillLoader } from "../lib/skill-loader.js";
39
+ import {
40
+ admitSkillPrompt,
41
+ debitSkillPromptBudget,
42
+ resolveSkillLimits,
43
+ } from "../lib/skill-budget.js";
39
44
  import { executeHooks, HookEvents } from "../lib/hook-manager.js";
40
45
  import { detectPython } from "../lib/cli-anything-bridge.js";
41
46
  import { findProjectRoot, loadProjectConfig } from "../lib/project-detector.js";
@@ -52,7 +57,11 @@ import {
52
57
  } from "./coding-agent-contract.js";
53
58
  import { createToolContext } from "../tools/tool-context.js";
54
59
  import { createToolTelemetryRecord } from "../tools/tool-telemetry.js";
55
- import { isAbortError, throwIfAborted } from "../lib/abort-utils.js";
60
+ import {
61
+ isAbortError,
62
+ raceWithAbort,
63
+ throwIfAborted,
64
+ } from "../lib/abort-utils.js";
56
65
  import {
57
66
  classifyEditReplay,
58
67
  editIdempotencyKey,
@@ -100,6 +109,10 @@ import {
100
109
  McpEffect,
101
110
  snapshotMcpJsonRpcInput,
102
111
  } from "../lib/mcp-call-ledger.js";
112
+ import {
113
+ admitMcpToolResult,
114
+ isMcpToolResultAdmissionError,
115
+ } from "../lib/mcp-tool-result.js";
103
116
  import {
104
117
  MCP_OUTCOME_UNKNOWN_CODE,
105
118
  markMcpLedgerOutcomeUnknown,
@@ -112,6 +125,7 @@ import {
112
125
  buildExtractiveHandoff,
113
126
  formatStructuredHandoff,
114
127
  } from "../harness/structured-handoff.js";
128
+ import { isMcpRpcError } from "../harness/mcp-client.js";
115
129
  import { projectCanonicalResumeMessages } from "../lib/session-message-provenance.js";
116
130
 
117
131
  export { formatProviderHttpError };
@@ -968,11 +982,16 @@ export function getAgentToolDescriptors(options = {}) {
968
982
  const _defaultSkillLoader = new CLISkillLoader();
969
983
 
970
984
  /**
971
- * Re-scan all skill layers (Claude-Code `/reload-skills` parity): drops the
972
- * default loader's cache so newly added/edited SKILL.md dirs are picked up
973
- * without restarting the session. Returns the resolved skill count.
985
+ * Re-scan all skill layers (Claude-Code `/reload-skills` parity): revokes this
986
+ * process's Skill execution generation, then drops the default loader cache so
987
+ * newly added/edited SKILL.md dirs are picked up without restarting. Returns
988
+ * the resolved skill count.
974
989
  */
975
990
  export function reloadSkills() {
991
+ _defaultSkillLoader.revokeExecutionAuthorizations({
992
+ message: "Skill execution authorization was revoked by /reload-skills",
993
+ reasonCode: "reload-skills",
994
+ });
976
995
  _defaultSkillLoader.clearCache();
977
996
  return _defaultSkillLoader.loadAll().length;
978
997
  }
@@ -1246,13 +1265,34 @@ export function buildSystemPrompt(cwd, opts = {}) {
1246
1265
  turnId: opts.turnId,
1247
1266
  loadedBecause: "persona_auto",
1248
1267
  });
1268
+ // Re-admit custom/injected loader output at the final model boundary. Do
1269
+ // the complete aggregate pass before appending anything so an oversized
1270
+ // later persona cannot leave an earlier partial projection in the prompt.
1271
+ const limits = resolveSkillLimits(loader.getLimits?.());
1272
+ const promptBudget = { limits, bytes: 0, tokens: 0 };
1273
+ const personaProjections = [];
1274
+ for (const personaSkill of personaSkills) {
1275
+ const displayName =
1276
+ typeof personaSkill?.displayName === "string"
1277
+ ? personaSkill.displayName
1278
+ : typeof personaSkill?.id === "string"
1279
+ ? personaSkill.id
1280
+ : "Skill";
1281
+ const body = personaSkill?.body;
1282
+ admitSkillPrompt(body, limits);
1283
+ if (!body.trim()) continue;
1284
+ const projection = `\n\n## Persona: ${displayName}\n${body}`;
1285
+ debitSkillPromptBudget(
1286
+ promptBudget,
1287
+ admitSkillPrompt(projection, limits),
1288
+ );
1289
+ personaProjections.push(projection);
1290
+ }
1249
1291
  if (typeof opts.onSkillsLoaded === "function") {
1250
1292
  opts.onSkillsLoaded(personaSkills, loader.getCacheLedger());
1251
1293
  }
1252
- for (const p of personaSkills) {
1253
- if (p.body?.trim()) {
1254
- prompt += `\n\n## Persona: ${p.displayName}\n${p.body}`;
1255
- }
1294
+ for (const projection of personaProjections) {
1295
+ prompt += projection;
1256
1296
  }
1257
1297
  } catch {
1258
1298
  // Non-critical — skill loader may not be available
@@ -3354,21 +3394,41 @@ const MCP_PROTOCOL_RESULT_INVALID_CODE = "CC_MCP_PROTOCOL_RESULT_INVALID";
3354
3394
  const MCP_RESULT_PROJECTION_FAILED_CODE = "CC_MCP_RESULT_PROJECTION_FAILED";
3355
3395
 
3356
3396
  function safeMcpProperty(value, property) {
3397
+ if (!value || (typeof value !== "object" && typeof value !== "function")) {
3398
+ return undefined;
3399
+ }
3357
3400
  try {
3358
- return value?.[property];
3401
+ if (isProxy(value)) return undefined;
3402
+ const descriptor = Object.getOwnPropertyDescriptor(value, property);
3403
+ return descriptor && Object.hasOwn(descriptor, "value")
3404
+ ? descriptor.value
3405
+ : undefined;
3359
3406
  } catch {
3360
3407
  return undefined;
3361
3408
  }
3362
3409
  }
3363
3410
 
3364
3411
  function safeMcpErrorMessage(error) {
3412
+ if (safeMcpProperty(error, "mcpErrorCode") === "CC_MCP_RPC_ERROR") {
3413
+ const rpcCode = safeMcpProperty(error, "rpcCode");
3414
+ return Number.isSafeInteger(rpcCode)
3415
+ ? `MCP server returned a JSON-RPC error (code ${rpcCode})`
3416
+ : "MCP server returned a JSON-RPC error";
3417
+ }
3418
+ // HTTP status bodies are peer-controlled. The production transport omits
3419
+ // them at source, and this boundary independently prevents any MCP client
3420
+ // honoring the stable transport error code from projecting one into tool
3421
+ // results, models, or sessions.
3422
+ if (safeMcpProperty(error, "code") === "CC_MCP_HTTP_STATUS") {
3423
+ const status = safeMcpProperty(error, "status");
3424
+ return Number.isInteger(status) && status >= 100 && status <= 599
3425
+ ? `MCP HTTP request failed with status ${status}`
3426
+ : "MCP HTTP request failed";
3427
+ }
3365
3428
  const message = safeMcpProperty(error, "message");
3366
3429
  if (typeof message === "string" && message) return message;
3367
- try {
3368
- return String(error || "unknown error");
3369
- } catch {
3370
- return "unknown error";
3371
- }
3430
+ if (typeof error === "string" && error) return error;
3431
+ return "unknown error";
3372
3432
  }
3373
3433
 
3374
3434
  function safeMcpErrorCode(error, fallback) {
@@ -3376,6 +3436,15 @@ function safeMcpErrorCode(error, fallback) {
3376
3436
  return typeof code === "string" && code ? code : fallback;
3377
3437
  }
3378
3438
 
3439
+ function projectMcpCallError(error) {
3440
+ const projected = new Error(safeMcpErrorMessage(error));
3441
+ const code = isMcpRpcError(error)
3442
+ ? "CC_MCP_RPC_ERROR"
3443
+ : safeMcpErrorCode(error, null);
3444
+ if (code) projected.code = code;
3445
+ return projected;
3446
+ }
3447
+
3379
3448
  function invalidMcpProtocolResult(cause) {
3380
3449
  const error = new TypeError("MCP result could not be inspected safely", {
3381
3450
  cause,
@@ -5654,10 +5723,16 @@ async function executeToolInner(
5654
5723
  }
5655
5724
 
5656
5725
  case "run_skill": {
5657
- const allSkills = await filterSkillsByCwd(
5658
- skillLoader.getResolvedSkills().filter(skillAllowed),
5659
- cwd,
5726
+ throwIfAborted(signal, "run_skill interrupted before discovery");
5727
+ const allSkills = await raceWithAbort(
5728
+ filterSkillsByCwd(
5729
+ skillLoader.getResolvedSkills().filter(skillAllowed),
5730
+ cwd,
5731
+ ),
5732
+ signal,
5733
+ "run_skill interrupted during discovery",
5660
5734
  );
5735
+ throwIfAborted(signal, "run_skill interrupted during discovery");
5661
5736
  if (allSkills.length === 0) {
5662
5737
  return attachDescriptor({
5663
5738
  error: _skillAllowlist
@@ -5668,28 +5743,57 @@ async function executeToolInner(
5668
5743
  let match = allSkills.find(
5669
5744
  (s) => s.id === args.skill_name || s.dirName === args.skill_name,
5670
5745
  );
5746
+ let admittedSkillBody = "";
5671
5747
  if (!match || !match.hasHandler) {
5672
5748
  return attachDescriptor({
5673
5749
  error: `Skill "${args.skill_name}" not found or has no handler. Use list_skills to see available skills.`,
5674
5750
  });
5675
5751
  }
5752
+ const skillExecutionLease = skillLoader.acquireSkillExecution?.(match, {
5753
+ signal,
5754
+ });
5755
+ const skillExecutionSignal = skillExecutionLease?.signal || signal;
5676
5756
  try {
5757
+ skillExecutionLease?.assertActive?.();
5677
5758
  if (typeof skillLoader.materializeSkillForExecution === "function") {
5678
- match = await skillLoader.materializeSkillForExecution(match, {
5679
- sessionId,
5680
- turnId,
5681
- loadedBecause: "run_skill",
5682
- bodyIncluded: false,
5683
- });
5759
+ match = await raceWithAbort(
5760
+ skillLoader.materializeSkillForExecution(match, {
5761
+ sessionId,
5762
+ turnId,
5763
+ loadedBecause: "run_skill",
5764
+ bodyIncluded: true,
5765
+ signal: skillExecutionSignal,
5766
+ }),
5767
+ skillExecutionSignal,
5768
+ "run_skill interrupted during authorization",
5769
+ );
5684
5770
  } else if (typeof skillLoader.materializeSkill === "function") {
5685
5771
  match = skillLoader.materializeSkill(match, {
5686
5772
  sessionId,
5687
5773
  turnId,
5688
5774
  loadedBecause: "run_skill",
5689
- bodyIncluded: false,
5775
+ bodyIncluded: true,
5776
+ signal: skillExecutionSignal,
5690
5777
  });
5691
5778
  }
5779
+ skillExecutionLease?.assertActive?.();
5780
+ throwIfAborted(
5781
+ skillExecutionSignal,
5782
+ "run_skill interrupted during materialization",
5783
+ );
5784
+ // A custom loader is not an authority to raise host model-input caps.
5785
+ // Re-admit at the final consumer before constructing the child task.
5786
+ admittedSkillBody = match?.body;
5787
+ admitSkillPrompt(admittedSkillBody, skillLoader.getLimits?.());
5692
5788
  } catch (error) {
5789
+ skillExecutionLease?.release?.();
5790
+ if (
5791
+ signal?.aborted ||
5792
+ skillExecutionSignal?.aborted ||
5793
+ isAbortError(error)
5794
+ ) {
5795
+ throw error;
5796
+ }
5693
5797
  return attachDescriptor({
5694
5798
  error: `Skill "${args.skill_name}" body could not be loaded: ${error.message}`,
5695
5799
  ...(error?.code ? { code: error.code } : {}),
@@ -5721,67 +5825,84 @@ async function executeToolInner(
5721
5825
  : null;
5722
5826
  let skillSubRef = null;
5723
5827
  // Run skill through isolated sub-agent context
5724
- const subCtx = SubAgentContext.create({
5725
- role: `skill-${args.skill_name}`,
5726
- task:
5727
- `Execute the "${args.skill_name}" skill using only the approved tools.\n\n` +
5728
- `Authoritative skill instructions:\n${String(match.body || "").substring(0, 48000)}\n\n` +
5729
- `User input:\n${String(args.input || "").substring(0, 8000)}`,
5730
- allowedTools: isolatedSkillTools,
5731
- hookParentTraceId: hookTraceId || null,
5732
- toolAdmission,
5733
- cwd,
5734
- llmOptions: llmOptions || null,
5735
- ...(sessionBudget ? { sessionBudget } : {}),
5736
- ...(permissionRules ? { permissionRules } : {}),
5737
- ...(hostManagedToolPolicy
5738
- ? {
5739
- hostManagedToolPolicy: {
5740
- ...hostManagedToolPolicy,
5741
- toolDefinitions: [],
5742
- },
5743
- }
5744
- : {}),
5745
- ...(planManager ? { planManager } : {}),
5746
- ...(sandbox ? { sandbox } : {}),
5747
- ...(Array.isArray(additionalDirectories)
5748
- ? { additionalDirectories: [...additionalDirectories] }
5749
- : {}),
5750
- ...(approvalGate ? { approvalGate } : {}),
5751
- ...(shellPolicyOverrides ? { shellPolicyOverrides } : {}),
5752
- ...(classifyAllShell ? { classifyAllShell: true } : {}),
5753
- ...(unattendedActionPolicy ? { unattendedActionPolicy } : {}),
5754
- ...(mcpCallLedger ? { mcpCallLedger } : {}),
5755
- ...(mcpConflictScheduler ? { mcpConflictScheduler } : {}),
5756
- onUsage: skillUsageSink
5757
- ? (u) => {
5758
- try {
5759
- skillUsageSink.push(
5760
- u && u.attribution
5761
- ? u
5762
- : {
5763
- provider: u?.provider ?? null,
5764
- model: u?.model ?? null,
5765
- usage: u?.usage || null,
5766
- ...(u?.source ? { source: u.source } : {}),
5767
- attribution: {
5768
- origin: "skill",
5769
- skill: args.skill_name,
5770
- subagentId: skillSubRef?.id || null,
5771
- parentSessionId: sessionId || null,
5772
- depth: (subAgentDepth || 0) + 1,
5828
+ let subCtx;
5829
+ try {
5830
+ skillExecutionLease?.assertActive?.();
5831
+ subCtx = SubAgentContext.create({
5832
+ role: `skill-${args.skill_name}`,
5833
+ task:
5834
+ `Execute the "${args.skill_name}" skill using only the approved tools.\n\n` +
5835
+ `Authoritative skill instructions:\n${admittedSkillBody}\n\n` +
5836
+ `User input:\n${String(args.input || "").substring(0, 8000)}`,
5837
+ allowedTools: isolatedSkillTools,
5838
+ hookParentTraceId: hookTraceId || null,
5839
+ signal: skillExecutionSignal,
5840
+ toolAdmission,
5841
+ cwd,
5842
+ llmOptions: llmOptions || null,
5843
+ ...(sessionBudget ? { sessionBudget } : {}),
5844
+ ...(permissionRules ? { permissionRules } : {}),
5845
+ ...(hostManagedToolPolicy
5846
+ ? {
5847
+ hostManagedToolPolicy: {
5848
+ ...hostManagedToolPolicy,
5849
+ toolDefinitions: [],
5850
+ },
5851
+ }
5852
+ : {}),
5853
+ ...(planManager ? { planManager } : {}),
5854
+ ...(sandbox ? { sandbox } : {}),
5855
+ ...(Array.isArray(additionalDirectories)
5856
+ ? { additionalDirectories: [...additionalDirectories] }
5857
+ : {}),
5858
+ ...(approvalGate ? { approvalGate } : {}),
5859
+ ...(shellPolicyOverrides ? { shellPolicyOverrides } : {}),
5860
+ ...(classifyAllShell ? { classifyAllShell: true } : {}),
5861
+ ...(unattendedActionPolicy ? { unattendedActionPolicy } : {}),
5862
+ ...(mcpCallLedger ? { mcpCallLedger } : {}),
5863
+ ...(mcpConflictScheduler ? { mcpConflictScheduler } : {}),
5864
+ onUsage: skillUsageSink
5865
+ ? (u) => {
5866
+ try {
5867
+ skillUsageSink.push(
5868
+ u && u.attribution
5869
+ ? u
5870
+ : {
5871
+ provider: u?.provider ?? null,
5872
+ model: u?.model ?? null,
5873
+ usage: u?.usage || null,
5874
+ ...(u?.source ? { source: u.source } : {}),
5875
+ attribution: {
5876
+ origin: "skill",
5877
+ skill: args.skill_name,
5878
+ subagentId: skillSubRef?.id || null,
5879
+ parentSessionId: sessionId || null,
5880
+ depth: (subAgentDepth || 0) + 1,
5881
+ },
5773
5882
  },
5774
- },
5775
- );
5776
- } catch (_e) {
5777
- // usage forwarding is best-effort
5883
+ );
5884
+ } catch (_e) {
5885
+ // usage forwarding is best-effort
5886
+ }
5778
5887
  }
5779
- }
5780
- : null,
5781
- });
5888
+ : null,
5889
+ });
5890
+ } catch (error) {
5891
+ skillExecutionLease?.release?.();
5892
+ throw error;
5893
+ }
5782
5894
  skillSubRef = subCtx;
5783
5895
  try {
5784
- const result = await subCtx.run(args.input);
5896
+ const result = await raceWithAbort(
5897
+ subCtx.run(args.input),
5898
+ skillExecutionSignal,
5899
+ "run_skill interrupted while the isolated child was running",
5900
+ );
5901
+ skillExecutionLease?.assertActive?.();
5902
+ throwIfAborted(
5903
+ skillExecutionSignal,
5904
+ "run_skill interrupted before child handoff",
5905
+ );
5785
5906
  const resolvedFailure =
5786
5907
  subCtx.status === "failed" ||
5787
5908
  result?.success === false ||
@@ -5809,6 +5930,13 @@ async function executeToolInner(
5809
5930
  toolsUsed: result.toolsUsed,
5810
5931
  });
5811
5932
  } catch (err) {
5933
+ if (
5934
+ signal?.aborted ||
5935
+ skillExecutionSignal?.aborted ||
5936
+ isAbortError(err)
5937
+ ) {
5938
+ throw err;
5939
+ }
5812
5940
  return attachDescriptor({
5813
5941
  success: false,
5814
5942
  isolated: true,
@@ -5816,6 +5944,8 @@ async function executeToolInner(
5816
5944
  code: err?.code || "CC_SKILL_ISOLATED_EXECUTION_FAILED",
5817
5945
  error: `Isolated skill execution failed: ${err.message}`,
5818
5946
  });
5947
+ } finally {
5948
+ skillExecutionLease?.release?.();
5819
5949
  }
5820
5950
  }
5821
5951
 
@@ -5823,6 +5953,7 @@ async function executeToolInner(
5823
5953
  // reaching here. Keep the runtime fence explicit so a custom/legacy
5824
5954
  // loader can never re-enable arbitrary handler.js imports in the CLI
5825
5955
  // process or hand a Skill the raw MCP client/process broker.
5956
+ skillExecutionLease?.release?.();
5826
5957
  return attachDescriptor({
5827
5958
  error: `Skill "${args.skill_name}" cannot execute handler.js directly. Add isolation: true and use the controlled agent-tool path.`,
5828
5959
  code: "CC_SKILL_DIRECT_HANDLER_BLOCKED",
@@ -5907,13 +6038,15 @@ async function executeToolInner(
5907
6038
  error: `Could not resolve which MCP server owns resource "${uri}". Pass 'server' explicitly.`,
5908
6039
  });
5909
6040
  }
5910
- const result = await mcpClient.readResource(server, uri);
6041
+ const result = signal
6042
+ ? await mcpClient.readResource(server, uri, { signal })
6043
+ : await mcpClient.readResource(server, uri);
5911
6044
  return attachDescriptor(
5912
6045
  result && typeof result === "object" ? result : { result },
5913
6046
  );
5914
6047
  } catch (err) {
5915
6048
  return attachDescriptor({
5916
- error: `MCP resource access failed: ${err.message}`,
6049
+ error: `MCP resource access failed: ${safeMcpErrorMessage(err)}`,
5917
6050
  });
5918
6051
  }
5919
6052
  }
@@ -6015,19 +6148,31 @@ async function executeToolInner(
6015
6148
  });
6016
6149
  }
6017
6150
 
6018
- const transportFailure = async (callError) => {
6019
- if (mcpTransportOutcomeIsUnsafe(ledgerEffectContract)) {
6151
+ const transportFailure = async (callError, phase = "call") => {
6152
+ const definitiveRpcFailure = isMcpRpcError(callError);
6153
+ if (
6154
+ mcpTransportOutcomeIsUnsafe(ledgerEffectContract) &&
6155
+ !definitiveRpcFailure
6156
+ ) {
6157
+ const resultAdmissionFailure =
6158
+ isMcpToolResultAdmissionError(callError);
6020
6159
  return attachDescriptor(
6021
6160
  mcpOutcomeUnknownPayload(ledger, ledgerTicket, {
6022
- phase: "call",
6023
- reasonCode: MCP_TRANSPORT_OUTCOME_UNKNOWN_CODE,
6161
+ phase,
6162
+ reasonCode: resultAdmissionFailure
6163
+ ? safeMcpErrorCode(
6164
+ callError,
6165
+ MCP_PROTOCOL_RESULT_INVALID_CODE,
6166
+ )
6167
+ : MCP_TRANSPORT_OUTCOME_UNKNOWN_CODE,
6024
6168
  }),
6025
6169
  );
6026
6170
  }
6171
+ const projectedError = projectMcpCallError(callError);
6027
6172
  try {
6028
6173
  await ledgerTicket.settle({
6029
6174
  status: "failed",
6030
- error: callError,
6175
+ error: projectedError,
6031
6176
  });
6032
6177
  } catch (ledgerError) {
6033
6178
  return attachDescriptor(
@@ -6041,20 +6186,29 @@ async function executeToolInner(
6041
6186
  );
6042
6187
  }
6043
6188
  return attachDescriptor({
6044
- error: `MCP tool execution failed: ${safeMcpErrorMessage(callError)}`,
6189
+ error: `MCP tool execution failed: ${projectedError.message}`,
6045
6190
  mcpLedgerId: safeMcpProperty(ledgerTicket, "ledgerId") || null,
6046
6191
  });
6047
6192
  };
6048
6193
 
6049
6194
  let pendingResult;
6050
6195
  try {
6051
- pendingResult = Reflect.apply(mcpClient.callTool, mcpClient, [
6196
+ const callArguments = [
6052
6197
  localToolExecutor.serverName,
6053
6198
  localToolExecutor.toolName,
6054
6199
  mcpWireInput,
6055
- ]);
6200
+ ];
6201
+ if (signal) callArguments.push({ signal });
6202
+ pendingResult = Reflect.apply(
6203
+ mcpClient.callTool,
6204
+ mcpClient,
6205
+ callArguments,
6206
+ );
6056
6207
  } catch (callError) {
6057
- return await transportFailure(callError);
6208
+ return await transportFailure(
6209
+ callError,
6210
+ isMcpToolResultAdmissionError(callError) ? "result" : "call",
6211
+ );
6058
6212
  }
6059
6213
 
6060
6214
  let result;
@@ -6091,7 +6245,10 @@ async function executeToolInner(
6091
6245
  try {
6092
6246
  result = await pendingResult;
6093
6247
  } catch (callError) {
6094
- return await transportFailure(callError);
6248
+ return await transportFailure(
6249
+ callError,
6250
+ isMcpToolResultAdmissionError(callError) ? "result" : "call",
6251
+ );
6095
6252
  }
6096
6253
  } else {
6097
6254
  let then;
@@ -6136,6 +6293,16 @@ async function executeToolInner(
6136
6293
  result = pendingResult;
6137
6294
  }
6138
6295
 
6296
+ try {
6297
+ result = admitMcpToolResult(
6298
+ localToolExecutor.serverName,
6299
+ result,
6300
+ localToolExecutor.toolResultConfig,
6301
+ ).result;
6302
+ } catch (resultError) {
6303
+ return await transportFailure(resultError, "result");
6304
+ }
6305
+
6139
6306
  let protocolError;
6140
6307
  try {
6141
6308
  if (
@@ -6683,12 +6850,21 @@ export const MAX_SUB_AGENTS_PER_RUN = 32;
6683
6850
  * `substring(0, 5000)` that sliced mid-content with no marker and undercut even
6684
6851
  * read_file's own 50k self-limit. Tools that self-limit (read_file, notebook
6685
6852
  * render) stay below this; it is the final safety net for the ones that don't
6686
- * (run_shell, search_files, run_code, MCP). Override with CC_MAX_TOOL_RESULT_CHARS.
6853
+ * (run_shell, search_files, run_code, MCP). CC_MAX_TOOL_RESULT_CHARS may only
6854
+ * tighten the host ceiling; it cannot raise or disable it.
6687
6855
  */
6688
- export const MAX_TOOL_RESULT_CHARS = (() => {
6689
- const n = Number(process.env.CC_MAX_TOOL_RESULT_CHARS);
6690
- return Number.isFinite(n) && n > 0 ? Math.floor(n) : 50000;
6691
- })();
6856
+ export const MAX_TOOL_RESULT_CHARS_HARD_LIMIT = 50000;
6857
+
6858
+ export function resolveMaxToolResultChars(configured) {
6859
+ const value = Number(configured);
6860
+ return Number.isFinite(value) && value > 0
6861
+ ? Math.min(MAX_TOOL_RESULT_CHARS_HARD_LIMIT, Math.max(1, Math.floor(value)))
6862
+ : MAX_TOOL_RESULT_CHARS_HARD_LIMIT;
6863
+ }
6864
+
6865
+ export const MAX_TOOL_RESULT_CHARS = resolveMaxToolResultChars(
6866
+ process.env.CC_MAX_TOOL_RESULT_CHARS,
6867
+ );
6692
6868
 
6693
6869
  /**
6694
6870
  * Cap a serialized tool result to `max` chars, appending a visible truncation
@@ -6698,10 +6874,11 @@ export const MAX_TOOL_RESULT_CHARS = (() => {
6698
6874
  */
6699
6875
  export function capToolResultString(serialized, max = MAX_TOOL_RESULT_CHARS) {
6700
6876
  const s = String(serialized ?? "");
6701
- if (s.length <= max) return s;
6877
+ const effectiveMax = resolveMaxToolResultChars(max);
6878
+ if (s.length <= effectiveMax) return s;
6702
6879
  return (
6703
- s.slice(0, max) +
6704
- `\n…[tool output truncated: showing the first ${max} of ${s.length} chars` +
6880
+ s.slice(0, effectiveMax) +
6881
+ `\n…[tool output truncated: showing the first ${effectiveMax} of ${s.length} chars` +
6705
6882
  ` — narrow the request (read a line range, grep, or paginate) to see the rest]`
6706
6883
  );
6707
6884
  }
@@ -8143,6 +8320,42 @@ function _ollamaInitState() {
8143
8320
  };
8144
8321
  }
8145
8322
 
8323
+ function _streamUiCallbacks(onToken, onThinking) {
8324
+ let pending = [];
8325
+ const capture = (callback) =>
8326
+ typeof callback === "function"
8327
+ ? (value) => {
8328
+ try {
8329
+ const result = callback(value);
8330
+ if (result && typeof result.then === "function") {
8331
+ pending.push(Promise.resolve(result));
8332
+ }
8333
+ } catch (error) {
8334
+ pending.push(Promise.reject(error));
8335
+ }
8336
+ }
8337
+ : callback;
8338
+ return {
8339
+ onToken: capture(onToken),
8340
+ onThinking: capture(onThinking),
8341
+ async settle() {
8342
+ if (pending.length === 0) return;
8343
+ const current = pending;
8344
+ pending = [];
8345
+ const outcomes = await Promise.allSettled(current);
8346
+ const fatal = outcomes.find(
8347
+ (outcome) =>
8348
+ outcome.status === "rejected" &&
8349
+ outcome.reason?.isOutputBackpressureFailure === true,
8350
+ );
8351
+ if (fatal) throw fatal.reason;
8352
+ // Generic UI callback failures retain their historical best-effort
8353
+ // semantics. Branded output failures are lifecycle failures and abort the
8354
+ // provider stream instead of allowing its terminal queue to grow.
8355
+ },
8356
+ };
8357
+ }
8358
+
8146
8359
  function _ollamaReduceLine(state, line, onToken) {
8147
8360
  const s = (line || "").trim();
8148
8361
  if (!s) return state;
@@ -8213,6 +8426,7 @@ export function _accumulateOllamaStream(lines, onToken) {
8213
8426
  * showing raw errors"). Pure + exported for unit tests.
8214
8427
  */
8215
8428
  export function _streamErrorDisposition(err, signal, partialText) {
8429
+ if (err?.isOutputBackpressureFailure === true) return "rethrow";
8216
8430
  if (isAbortError(err)) return "rethrow";
8217
8431
  if (signal && signal.aborted) return "rethrow";
8218
8432
  if (typeof partialText === "string" && partialText.trim()) return "preserve";
@@ -8427,6 +8641,7 @@ async function _chatOllamaStreaming(
8427
8641
  throw new Error(await formatProviderResponseError("ollama", response));
8428
8642
  }
8429
8643
  const state = _ollamaInitState();
8644
+ const ui = _streamUiCallbacks(onToken);
8430
8645
  const reader = response.body.getReader();
8431
8646
  const decoder = new TextDecoder();
8432
8647
  let buf = "";
@@ -8439,11 +8654,15 @@ async function _chatOllamaStreaming(
8439
8654
  buf += decoder.decode(value, { stream: true });
8440
8655
  let idx;
8441
8656
  while ((idx = buf.indexOf("\n")) >= 0) {
8442
- _ollamaReduceLine(state, buf.slice(0, idx), onToken);
8657
+ _ollamaReduceLine(state, buf.slice(0, idx), ui.onToken);
8658
+ await ui.settle();
8443
8659
  buf = buf.slice(idx + 1);
8444
8660
  }
8445
8661
  }
8446
- if (buf.trim()) _ollamaReduceLine(state, buf, onToken);
8662
+ if (buf.trim()) {
8663
+ _ollamaReduceLine(state, buf, ui.onToken);
8664
+ await ui.settle();
8665
+ }
8447
8666
  } catch (err) {
8448
8667
  if (_streamErrorDisposition(err, signal, state.content) === "rethrow")
8449
8668
  throw err;
@@ -8613,6 +8832,7 @@ async function _chatAnthropicStreaming(
8613
8832
  throw new Error(await formatProviderResponseError("anthropic", response));
8614
8833
  }
8615
8834
  const state = _anthropicInitState();
8835
+ const ui = _streamUiCallbacks(onToken, onThinking);
8616
8836
  const reader = response.body.getReader();
8617
8837
  const decoder = new TextDecoder();
8618
8838
  let buf = "";
@@ -8625,10 +8845,15 @@ async function _chatAnthropicStreaming(
8625
8845
  buf += decoder.decode(value, { stream: true });
8626
8846
  const lines = buf.split("\n");
8627
8847
  buf = lines.pop() || "";
8628
- for (const line of lines)
8629
- _anthropicReduceLine(state, line, onToken, onThinking);
8848
+ for (const line of lines) {
8849
+ _anthropicReduceLine(state, line, ui.onToken, ui.onThinking);
8850
+ await ui.settle();
8851
+ }
8852
+ }
8853
+ if (buf.trim()) {
8854
+ _anthropicReduceLine(state, buf, ui.onToken, ui.onThinking);
8855
+ await ui.settle();
8630
8856
  }
8631
- if (buf.trim()) _anthropicReduceLine(state, buf, onToken, onThinking);
8632
8857
  } catch (err) {
8633
8858
  if (_streamErrorDisposition(err, signal, state.text) === "rethrow")
8634
8859
  throw err;
@@ -8765,6 +8990,7 @@ async function _chatOpenAIStreaming(
8765
8990
  throw new Error(await formatProviderResponseError(provider, response));
8766
8991
  }
8767
8992
  const state = _openaiInitState();
8993
+ const ui = _streamUiCallbacks(onToken);
8768
8994
  const reader = response.body.getReader();
8769
8995
  const decoder = new TextDecoder();
8770
8996
  let buf = "";
@@ -8777,9 +9003,15 @@ async function _chatOpenAIStreaming(
8777
9003
  buf += decoder.decode(value, { stream: true });
8778
9004
  const lines = buf.split("\n");
8779
9005
  buf = lines.pop() || "";
8780
- for (const line of lines) _openaiReduceLine(state, line, onToken);
9006
+ for (const line of lines) {
9007
+ _openaiReduceLine(state, line, ui.onToken);
9008
+ await ui.settle();
9009
+ }
9010
+ }
9011
+ if (buf.trim()) {
9012
+ _openaiReduceLine(state, buf, ui.onToken);
9013
+ await ui.settle();
8781
9014
  }
8782
- if (buf.trim()) _openaiReduceLine(state, buf, onToken);
8783
9015
  } catch (err) {
8784
9016
  if (_streamErrorDisposition(err, signal, state.text) === "rethrow")
8785
9017
  throw err;