chainlesschain 0.166.101 → 0.166.103

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 (219) hide show
  1. package/package.json +2 -2
  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-BjBS_29I.js → AIOps-BLrXpdnP.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-CUnnQUaj.js → ActionButton-C0xy9uFy.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BqYZxgnY.js → Analytics-C6EephQP.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-m8B886ka.js → AppLayout-ZCXdOD9y.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-JEOxU3Ug.js → Artifacts-D4MfZ-pl.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DAXS5c9J.js → Audit-BQ8c3bBu.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-e3a9XHw3.js → BackgroundAgents-BOA7_ljM.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-D3Qmp68h.js → Backup-60uiQjGT.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-C7CXyiUl.js → BaseInput-BDvEbGs_.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-2AHlAGi8.js → Chat-B3IkAuFQ.js} +6 -6
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-n6BVStHf.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-YfBuQlBH.js → Checkbox-njGbfJWq.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-DCCQDfTL.js → Codegen-CF7DtdxC.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-03i2WfrV.js → Col-C_3Zix40.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-CJQIpng6.js → Community-CzU-ZC2d.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-CCtcQ2pq.js → Compact-BgeqsPRh.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-Vxl7ZszJ.js → Compliance-6l7cwiqp.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DN-BwA8I.js → Cowork-D4s905fI.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-Dc2Q46X7.js → Cron-CGTYSxRS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-CQCblsl-.js → Crosschain-BnSo-Yt3.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-YYvqUR8C.js → DID-Dm4vk2X1.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Cafy3DZT.js → Dashboard-D7H-PZ_N.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BOyYpIpv.js → Dropdown-COslkgxn.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-B7NYjc1j.js → EmailListRenderer-CJWEetmk.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-C5wfxztf.js → EvolutionSettings-BbzebAWe.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-aA7KnDGK.js → FamilyGuardDashboard-BsoTOXwk.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-DlgXUu1L.js → Federation-Ck5kM_5H.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-B5YsrLDg.js → FormItemContext-gShxCqtF.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-DaWojPFw.js → GenericCardRenderer-DarN1G-z.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-dkhh6Fx2.js → Git-CJfpLyP5.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-Cz6vmkjc.js → Governance-CVcJmpQz.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-DWt0hbh8.js → Inference-ComU_ig8.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-BZJ0Z-nL.js → KnowledgeGraph-35hprFJy.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-BaBBmL1A.js → Logs-BQDqBTz0.js} +2 -2
  38. package/src/assets/web-panel/assets/MarkdownRenderer-BpdGAVCC.js +1 -0
  39. package/src/assets/web-panel/assets/{Marketplace-ct8sgQ0s.js → Marketplace-B9O9OKnn.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-CXKCRGYQ.js → McpTools-VMKxA4Wc.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-O7ZwlS6a.js → Memory-CcBbf3Bc.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-By12C-eC.js → MobileBridge-D7myz46N.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-Ba-EVdex.js → MobileProjects-Da5KBXyx.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-g-932zV2.js → Mtc-x3NPZfuL.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-BW8hB0ly.js → MtcAudit-CwnzptEC.js} +4 -4
  46. package/src/assets/web-panel/assets/{Multisig-zspLaMbT.js → Multisig-CCAxf4Ye.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-dJeUPDoq.js → NLProgramming-CptHKz-Q.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-C4msiRQL.js → Notes-0uEp4DCJ.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-MOCHM-qg.js → NotificationSettings-DthvrOLd.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-CX0T66NM.js → OrderTableRenderer-CxXuPzZg.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-DOZurBfk.js → Organization-BWf2tmQQ.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-qOT9BhUp.js → Overflow-BbvAtEth.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-BMoy7Eo4.js → P2P-zBo6enit.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-ByyV1q-D.js → PdhVaultBrowser-D_LMQ4A4.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-6Qyro4ye.js → Permissions-C1ucb2ck.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-DR3DRNSX.js → PersonalDataHub-Bz64RddJ.js} +5 -5
  57. package/src/assets/web-panel/assets/{Pipeline-Cr6TTdud.js → Pipeline-DXJt3y3_.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-B70otG0l.js → Privacy-DiZ8b_ut.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-BHCqxoSk.js → ProjectInit-DfLKKrJ4.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-B80GZpZk.js → ProjectSettings-Ct6kJuIY.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-cq-b9-fQ.js → Projects-BBUMlZ3m.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-dsVTimHa.js → Providers-DYVQPHvc.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-DRJ8YepZ.js → QrScannerModal-23Il7iPp.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-BSXdEjIB.js → QuickAsk-WgOZKrv1.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-CQLJGyKC.js → Recommend-CRIka6xT.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-BF9WUFVZ.js → RemoteSession-DOy3GjVZ.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-Cy9o5tvW.js → Reputation-BXyi6xeR.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-BP346_3b.js → Row-CoMxuIcR.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-BKulxXf3.js → RssFeed-87hPkoM6.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-EemQj2Dv.js → Search-B1EqFCO4.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-DcWPAwwk.js → Security-HcC41eGs.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-Bltqciay.js → Services-BqLwmvCM.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-BwRyOGY8.js → Skeleton-CIMmjW4r.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-D98qlvpJ.js → Skills-CgurezJU.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-BUTtBMY0.js → Sla-CJuRuqPE.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-C7kcY_98.js → SpeechSettings-BUwsbd1o.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-Bsk6lKvT.js → SyncSettings-qo04TOX-.js} +2 -2
  78. package/src/assets/web-panel/assets/Tasks-QEFENVsp.js +1 -0
  79. package/src/assets/web-panel/assets/{Templates-BUM6LwPs.js → Templates-D1PTczPW.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-D9TkOm7z.js → Tenant-Bi0FkEPR.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-CP8ZVErN.js → Terminal-DfSt3bIF.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-V3SoKZ1T.js → TimelineRenderer-B7bIVs4R.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-CCRlUTOA.js → Tokens-B6w5XrMb.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-CznUcPme.js → Trigger-XXVSDWiF.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-D1GIk_NF.js → Trust-DYvlgcK-.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-DHtvtyC-.js → UkeySign-XyYwJMBM.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-CVxdaJIq.js → VideoEditing-C47WbtHH.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-D221OjyE.js → Wallet-CYfqTj_C.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-Cw0-ESsq.js → WebAuthn-3AduFn1l.js} +3 -3
  90. package/src/assets/web-panel/assets/{WorkflowEditor-ZYScrBx3.js → WorkflowEditor-DHYEHjMJ.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-BU0e1obR.js → chat-AeehErSw.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-gzYweuha.js → colors-DKXJ8paX.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-ChAXal_k.js → compact-item-BOlCiomD.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-of3FolD7.js → createContext-BAP6U85b.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-rGldFhxo.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-4PsK_Kx8.js → hasIn-BLmeL1Xe.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Cn1clrxx.js → index-B0IjlB9m.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BOeX7BzS.js → index-BAS2QqLn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-Cx4Z6nUl.js → index-BRcJlKSa.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DjRAIRX7.js → index-BSOenF5s.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Djfgsjfy.js → index-BTnp4Nt3.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BfI4dXbi.js → index-BhcpWl5z.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D_5LTPMH.js → index-Bn54FU3i.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CyDQNprx.js → index-Bpx54vfs.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DEXBckNR.js → index-Bs2O3f81.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Ef5YkVAn.js → index-Bt-FCVzh.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-E8oDMSAo.js → index-BuUwKmEI.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BdxRVNPY.js → index-C5AKY6bJ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Wr91cnWr.js → index-C8qi53Ki.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B5L7zddz.js → index-CFKzWIo8.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Dy0ojH6G.js → index-CRa4pAaM.js} +3 -3
  112. package/src/assets/web-panel/assets/{index-wo0De0iG.js → index-CaHhfpBi.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-D-Zss5KL.js → index-CdNSmvog.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DEzjUgkl.js → index-CgtaOzMo.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DDYj14Q4.js → index-CiqeF0Pf.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-D_9k680X.js → index-ClpwxOXo.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-D1PGrfHE.js → index-Co7pjF53.js} +1 -1
  118. package/src/assets/web-panel/assets/index-Cw-lfvvV.js +1 -0
  119. package/src/assets/web-panel/assets/{index-CeEE_SGZ.js → index-DE7cbDDe.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CxzBcXOa.js → index-DQLQuwpA.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DEB5fQqG.js → index-DWYqOI1A.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CO2owKe0.js → index-De2fPxdb.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-1dzYRBbg.js → index-DpZqbc4w.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CGFL59q4.js → index-DsQ38fVY.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DsoV9rzn.js → index-Dv8-g1pY.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-yMf7zmWv.js → index-EKNODw4P.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CHv_63Cp.js → index-FPXLIXoy.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CCLsbul3.js → index-IlzfIRNh.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-cFkMIx1x.js → index-OLKhib9T.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Bftq5rJR.js → index-PEe9OWEQ.js} +1 -1
  131. package/src/assets/web-panel/assets/index-QBXRSyoZ.js +1 -0
  132. package/src/assets/web-panel/assets/{index-DJVrzXtk.js → index-T28naovw.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-By-pper5.js → index-ZMbXl5lo.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DZ0oKtBg.js → index-urPvyUVk.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-D_ialOHx.js → index-wyKpTsGM.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-D0Bx6pgr.js → initDefaultProps-CDWa1rnn.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-BnLuJqJ5.js → motion-CX2Cnsd5.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-Bxpq9xgR.js → move-Bfe1NcEF.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-BdmrNhOs.js → mtc-parser-DbdqleHE.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-tjJD34BZ.js → omit-CE2K7NVv.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-CMFzLt7b.js → pickAttrs-y_bZgxWG.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-BZf6O3cd.js → placementArrow-BOHR4zwr.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-DFDLazsV.js → responsiveObserve-B8Jwq05-.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-CMbTvp4Y.js → slide-BD7RJ3Kn.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-FOan8MI7.js → statusUtils-BuxDJeDx.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-DTABspd4.js → styleChecker-DgYQ2Q0o.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-CNHXWBpY.js → useFlexGapSupport-DAHF0Fwi.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-5K-c_RoL.js → useFs-D4kDyLFp.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-Di0hL4G-.js → usePersonalDataHub-BHtSxW4A.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-DTyDv4ja.js → vnode-CHKtdqW0.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-ontRt8ft.js → zoom-CIZAMma9.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/command-help-index.json +1 -1
  154. package/src/commands/agent.js +64 -40
  155. package/src/commands/ask.js +6 -4
  156. package/src/commands/team.js +28 -19
  157. package/src/data/changelog.json +26 -0
  158. package/src/harness/prompt-compressor.js +2 -0
  159. package/src/lib/advisor-runtime.js +4 -2
  160. package/src/lib/agent-ipc-bus.js +2 -0
  161. package/src/lib/background-agent-keeper-client.js +3 -0
  162. package/src/lib/browser-automation.js +4 -0
  163. package/src/lib/cc-android-bridge.js +4 -0
  164. package/src/lib/chat-core.js +11 -9
  165. package/src/lib/cloud/cloud-client.js +10 -1
  166. package/src/lib/config-schema.js +18 -5
  167. package/src/lib/context-memory-kernel/memory-query-index.js +5 -2
  168. package/src/lib/context-memory-kernel/runtime.js +4 -0
  169. package/src/lib/context-memory-kernel/segmented-memory-port.js +28 -3
  170. package/src/lib/context-source-ledger.js +52 -9
  171. package/src/lib/cowork-adapter.js +4 -3
  172. package/src/lib/decision-layer/providers.js +16 -11
  173. package/src/lib/evolution/governed-hub-llm.js +31 -16
  174. package/src/lib/evolution/governed-hub-resolver.js +4 -0
  175. package/src/lib/evolution/governed-skill-synthesis-process-grader.js +4 -0
  176. package/src/lib/evomap-client.js +14 -2
  177. package/src/lib/formal-budget-authority.js +191 -59
  178. package/src/lib/formal-budget-contract.js +7 -13
  179. package/src/lib/formal-budget-errors.js +31 -0
  180. package/src/lib/formal-budget-lineage-registry.js +612 -0
  181. package/src/lib/formal-budget-provider-scope.js +143 -0
  182. package/src/lib/formal-budget-transport.js +235 -100
  183. package/src/lib/gateway-client-resilience.js +5 -0
  184. package/src/lib/goal-condition-engine.js +25 -2
  185. package/src/lib/hub-llm-client.js +25 -16
  186. package/src/lib/llm-config-defaults.js +10 -4
  187. package/src/lib/llm-connection-probe.js +17 -10
  188. package/src/lib/llm-pricing.js +16 -4
  189. package/src/lib/llm-providers.js +4 -1
  190. package/src/lib/model-capabilities.js +26 -0
  191. package/src/lib/model-catalog-review.js +18 -1
  192. package/src/lib/model-context-catalog.js +41 -1
  193. package/src/lib/model-failure-policy.js +2 -0
  194. package/src/lib/openai-responses.js +8 -0
  195. package/src/lib/plugin-runtime/marketplace-network.js +9 -5
  196. package/src/lib/process-execution-broker/credential-agent.js +7 -2
  197. package/src/lib/process-execution-broker/credential-transport.js +4 -0
  198. package/src/lib/process-execution-broker/index.js +8 -0
  199. package/src/lib/provider-stream.js +28 -13
  200. package/src/lib/sandbox-egress-worker.js +3 -0
  201. package/src/lib/skill-retrieval-router.js +1 -0
  202. package/src/lib/stream-retry.js +2 -0
  203. package/src/lib/sub-agent-context.js +6 -3
  204. package/src/lib/usage-pricing-context.js +33 -2
  205. package/src/lib/web-fetch.js +2 -0
  206. package/src/lib/web-search.js +5 -0
  207. package/src/repl/agent-repl.js +247 -153
  208. package/src/runtime/agent-core.js +228 -96
  209. package/src/runtime/coding-agent-contract-shared.cjs +14 -1
  210. package/src/runtime/headless-runner.js +1 -0
  211. package/src/runtime/headless-stream.js +1 -0
  212. package/src/runtime/mcp-tool-search.js +0 -0
  213. package/src/runtime/provider-stream-state.js +14 -1
  214. package/src/assets/web-panel/assets/ChatBubbleRenderer-D3KsyCUV.js +0 -1
  215. package/src/assets/web-panel/assets/MarkdownRenderer-C1yByobo.js +0 -1
  216. package/src/assets/web-panel/assets/Tasks-DvSrtz0P.js +0 -1
  217. package/src/assets/web-panel/assets/devWarning-Pa13TVrO.js +0 -1
  218. package/src/assets/web-panel/assets/index-BhQg-v6V.js +0 -1
  219. package/src/assets/web-panel/assets/index-DUD5o-vW.js +0 -1
@@ -0,0 +1,143 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { budgetError, label, requireBudget } from "./formal-budget-errors.js";
3
+
4
+ const providerScope = new AsyncLocalStorage();
5
+
6
+ export function hasFormalBudgetProviderScope() {
7
+ return !!providerScope.getStore();
8
+ }
9
+
10
+ /** Guard an adapter which cannot pass its physical calls to the authority. */
11
+ export function requireUnscopedModelTransport() {
12
+ requireBudget(
13
+ !providerScope.getStore(),
14
+ "This model adapter has no governed transport for the active budget scope",
15
+ "CC_BUDGET_TRANSPORT_NOT_COVERED",
16
+ );
17
+ }
18
+
19
+ /** Search/service charges need their own verified contract, not a Chat quote. */
20
+ export function requireUnscopedPaidService() {
21
+ const scope = providerScope.getStore();
22
+ if (!scope) return;
23
+ requireBudget(
24
+ !scope.closed,
25
+ "Provider budget scope closed",
26
+ "CC_BUDGET_SCOPE_CLOSED",
27
+ );
28
+ throw budgetError(
29
+ "CC_BUDGET_SERVICE_NOT_COVERED",
30
+ "This external service has no verified billing contract for the active budget scope",
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Canonical Agent tools can launch shells, MCP peers, skills, remote work or
36
+ * descendants. None has a verified physical-request contract in this scope.
37
+ * Deny before inspecting caller-controlled tool arguments or host callbacks.
38
+ */
39
+ export function requireUnscopedToolExecution() {
40
+ const scope = providerScope.getStore();
41
+ if (!scope) return;
42
+ requireBudget(
43
+ !scope.closed,
44
+ "Provider budget scope closed",
45
+ "CC_BUDGET_SCOPE_CLOSED",
46
+ );
47
+ throw budgetError(
48
+ "CC_BUDGET_TOOL_NOT_COVERED",
49
+ "Agent tool execution has no governed descendant or external-service contract for the active budget scope",
50
+ );
51
+ }
52
+
53
+ /** Direct process/keeper controls bypass executeTool and cannot inherit its guard. */
54
+ export function requireUnscopedDescendantControl() {
55
+ const scope = providerScope.getStore();
56
+ if (!scope) return;
57
+ requireBudget(
58
+ !scope.closed,
59
+ "Provider budget scope closed",
60
+ "CC_BUDGET_SCOPE_CLOSED",
61
+ );
62
+ throw budgetError(
63
+ "CC_BUDGET_DESCENDANT_NOT_COVERED",
64
+ "Descendant process control has no governed physical-request contract for the active budget scope",
65
+ );
66
+ }
67
+
68
+ /** Direct URL/browser tools have no governed target or remote-cost contract. */
69
+ export function requireUnscopedExternalToolAccess() {
70
+ const scope = providerScope.getStore();
71
+ if (!scope) return;
72
+ requireBudget(
73
+ !scope.closed,
74
+ "Provider budget scope closed",
75
+ "CC_BUDGET_SCOPE_CLOSED",
76
+ );
77
+ throw budgetError(
78
+ "CC_BUDGET_EXTERNAL_TOOL_NOT_COVERED",
79
+ "Direct external tool access has no governed target or cost contract for the active budget scope",
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Trusted-host opt-in only. Consume agent generators and response bodies inside
85
+ * operation; outstanding requests are aborted when it returns. This binds async
86
+ * calls in this process, not subprocesses, workers, arbitrary fetch or OS egress.
87
+ * It neither admits formal evaluation nor creates/renews a budget lineage.
88
+ */
89
+ export async function withFormalBudgetProviderScope(
90
+ { transport, runId },
91
+ operation,
92
+ ) {
93
+ requireBudget(
94
+ !providerScope.getStore(),
95
+ "A provider budget scope cannot be replaced by a nested scope",
96
+ "CC_BUDGET_SCOPE_NESTED",
97
+ );
98
+ label(runId, "run ID");
99
+ requireBudget(
100
+ typeof transport?.createFetch === "function" &&
101
+ typeof operation === "function",
102
+ "Trusted budget transport and operation required",
103
+ );
104
+ const controller = new AbortController();
105
+ const scope = {
106
+ fetch: transport.createFetch({ runId }),
107
+ signal: controller.signal,
108
+ closed: false,
109
+ };
110
+ return providerScope.run(scope, async () => {
111
+ try {
112
+ const result = await operation();
113
+ requireBudget(
114
+ typeof result?.[Symbol.asyncIterator] !== "function",
115
+ "Consume model generators inside the provider budget scope",
116
+ "CC_BUDGET_SCOPE_UNCONSUMED",
117
+ );
118
+ return result;
119
+ } finally {
120
+ scope.closed = true;
121
+ controller.abort(
122
+ budgetError("CC_BUDGET_SCOPE_CLOSED", "Provider budget scope closed"),
123
+ );
124
+ }
125
+ });
126
+ }
127
+
128
+ /** The injected fallback is used only outside a host-owned budget scope. */
129
+ export async function providerFetch(input, init, fetchImpl = globalThis.fetch) {
130
+ const scope = providerScope.getStore();
131
+ if (!scope) return fetchImpl(input, init);
132
+ requireBudget(
133
+ !scope.closed,
134
+ "Provider budget scope closed",
135
+ "CC_BUDGET_SCOPE_CLOSED",
136
+ );
137
+ return scope.fetch(input, {
138
+ ...init,
139
+ signal: init?.signal
140
+ ? AbortSignal.any([scope.signal, init.signal])
141
+ : scope.signal,
142
+ });
143
+ }
@@ -1,6 +1,9 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { raceWithAbort } from "./abort-utils.js";
1
3
  import {
2
4
  budgetError,
3
5
  canonical,
6
+ label,
4
7
  requireBudget,
5
8
  } from "./formal-budget-contract.js";
6
9
 
@@ -124,36 +127,6 @@ export function formalBudgetTaskEnvironment(environment = process.env) {
124
127
  );
125
128
  }
126
129
 
127
- async function boundedText(response, maximumBytes) {
128
- requireBudget(response.body?.getReader, "Response body unavailable");
129
- const reader = response.body.getReader(),
130
- chunks = [];
131
- let size = 0;
132
- try {
133
- for (;;) {
134
- const { done, value } = await reader.read();
135
- if (done) break;
136
- size += value.byteLength;
137
- requireBudget(
138
- size <= maximumBytes,
139
- "Provider response exceeded bounded parser",
140
- );
141
- chunks.push(Buffer.from(value));
142
- }
143
- } catch (error) {
144
- try {
145
- await reader.cancel();
146
- } catch {
147
- /* keep original unknown outcome */
148
- }
149
- throw error;
150
- } finally {
151
- reader.releaseLock();
152
- }
153
- return new TextDecoder("utf-8", { fatal: true }).decode(
154
- Buffer.concat(chunks),
155
- );
156
- }
157
130
  function streamReceipt(text) {
158
131
  let terminal = false,
159
132
  receipt = null,
@@ -237,10 +210,32 @@ function streamReceipt(text) {
237
210
  return { ...receipt, usage };
238
211
  }
239
212
 
213
+ function providerReceipt(text, streaming) {
214
+ if (streaming) return streamReceipt(text);
215
+ const receipt = parseBudgetJson(text);
216
+ requireBudget(
217
+ receipt && !Object.hasOwn(receipt, "error"),
218
+ "Provider error envelope cannot settle",
219
+ );
220
+ requireBudget(
221
+ Array.isArray(receipt.choices) &&
222
+ receipt.choices.length === 1 &&
223
+ receipt.choices[0].index === 0 &&
224
+ ["stop", "length", "tool_calls"].includes(
225
+ receipt.choices[0].finish_reason,
226
+ ),
227
+ "Incomplete or multiple completion response",
228
+ );
229
+ return receipt;
230
+ }
231
+
240
232
  /**
241
233
  * Trusted-host JSON/SSE transport: credential stays in this closure, and every
242
234
  * physical request consumes a durable dispatch exactly once. No auto retries.
243
- * This is not the unrestricted Agent/Chat transport and is not auto-enabled.
235
+ * createFetch is for an explicitly scoped, in-process host. It delivers chunks
236
+ * as they arrive, but EOF is observable only after durable settlement. Missing
237
+ * usage, cancellation and abandoned bodies retain the full reservation.
238
+ * Neither API is auto-enabled or an OS/descendant isolation boundary.
244
239
  */
245
240
  export function createFormalBudgetTransport({
246
241
  authority,
@@ -265,81 +260,221 @@ export function createFormalBudgetTransport({
265
260
  Number.isSafeInteger(timeoutMs) && timeoutMs > 0 && timeoutMs <= 300_000,
266
261
  "Invalid timeout",
267
262
  );
268
- return Object.freeze({
269
- async dispatch({ runId, callId, body, signal }) {
270
- // Snapshot before await so caller mutation cannot alter the reserved body.
271
- const request = JSON.parse(canonical(body));
272
- authority.reserve({ runId, callId, body: request });
273
- let settled = false;
274
- try {
275
- const reserved = authority.reservedContract(callId);
276
- const credential = await credentialProvider({
277
- contractId: reserved.id,
278
- provider: reserved.provider,
279
- endpoint: reserved.endpoint,
280
- });
281
- requireBudget(
282
- typeof credential === "string" &&
283
- credential.length > 0 &&
284
- !/[\r\n]/u.test(credential),
285
- "Credential unavailable",
286
- );
287
- const deadline = AbortSignal.timeout(timeoutMs);
288
- const options = {
289
- method: "POST",
290
- redirect: "error",
291
- headers: {
292
- "Content-Type": "application/json",
293
- Authorization: `Bearer ${credential}`,
294
- },
295
- body: JSON.stringify(request),
296
- signal: signal ? AbortSignal.any([signal, deadline]) : deadline,
297
- };
298
- // Final expiry/request check is AFTER every awaited preparation. There
299
- // must be no await between this durable admission and physical fetch.
300
- const contract = authority.markDispatched(callId, request);
301
- const response = await fetchImpl(contract.endpoint, options);
302
- requireBudget(response.ok, "Provider HTTP response not successful");
303
- const text = await boundedText(response, maximumResponseBytes);
304
- let receipt;
305
- if (request.stream === true) {
306
- requireBudget(
307
- response.headers.get("content-type")?.includes("text/event-stream"),
308
- "Unexpected stream content type",
309
- );
310
- receipt = streamReceipt(text);
311
- } else {
312
- receipt = parseBudgetJson(text);
313
- requireBudget(
314
- receipt && !Object.hasOwn(receipt, "error"),
315
- "Provider error envelope cannot settle",
316
- );
317
- requireBudget(
318
- Array.isArray(receipt.choices) &&
319
- receipt.choices.length === 1 &&
320
- receipt.choices[0].index === 0 &&
321
- ["stop", "length", "tool_calls"].includes(
322
- receipt.choices[0].finish_reason,
323
- ),
324
- "Incomplete or multiple completion response",
325
- );
326
- }
327
- const cost = authority.settle(callId, receipt);
328
- settled = true;
329
- return { body: text, streaming: request.stream === true, cost };
330
- } catch {
331
- if (!settled) {
332
- // Do not persist provider errors which may contain credentials/content.
263
+ async function open({ runId, callId, body, endpoint, signal, traceId }) {
264
+ signal?.throwIfAborted();
265
+ // Snapshot before await so caller mutation cannot alter the reserved body.
266
+ const request = JSON.parse(canonical(body));
267
+ authority.reserve({ runId, callId, body: request, endpoint });
268
+ const controller = new AbortController();
269
+ const combined = AbortSignal.any([
270
+ controller.signal,
271
+ AbortSignal.timeout(timeoutMs),
272
+ ...(signal ? [signal] : []),
273
+ ]);
274
+ let finished = false,
275
+ reader,
276
+ responseBody,
277
+ output,
278
+ cost;
279
+ const cleanup = () => combined.removeEventListener("abort", onAbort);
280
+ const unknown = () => {
281
+ const error = budgetError(
282
+ "CC_BUDGET_TRANSPORT_UNKNOWN",
283
+ "Provider outcome unknown; reservation retained and further dispatch blocked",
284
+ );
285
+ if (!finished) {
286
+ finished = true;
287
+ cleanup();
288
+ // Never persist provider errors, content or credentials.
289
+ try {
333
290
  authority.markUnknown(
334
291
  callId,
335
292
  "transport or settlement outcome unknown",
336
293
  );
294
+ } catch {
295
+ // The durable held/dispatched row still blocks another reservation.
296
+ // An abort event must not throw an uncaught storage error.
297
+ } finally {
298
+ output?.error(error);
299
+ controller.abort();
300
+ // Do not let a peer that ignores cancellation block local shutdown.
301
+ if (reader)
302
+ void reader
303
+ .cancel()
304
+ .catch(() => {})
305
+ .finally(() => {
306
+ try {
307
+ reader.releaseLock();
308
+ } catch {
309
+ /* another read is unwinding */
310
+ }
311
+ });
312
+ else if (responseBody) void responseBody.cancel().catch(() => {});
337
313
  }
338
- throw budgetError(
339
- "CC_BUDGET_TRANSPORT_UNKNOWN",
340
- "Provider outcome unknown; reservation retained and further dispatch blocked",
341
- );
342
314
  }
315
+ return error;
316
+ };
317
+ function onAbort() {
318
+ unknown();
319
+ }
320
+ combined.addEventListener("abort", onAbort, { once: true });
321
+ try {
322
+ const reserved = authority.reservedContract(callId);
323
+ const credential = await raceWithAbort(
324
+ Promise.resolve().then(() =>
325
+ credentialProvider({
326
+ contractId: reserved.id,
327
+ provider: reserved.provider,
328
+ endpoint: reserved.endpoint,
329
+ }),
330
+ ),
331
+ combined,
332
+ );
333
+ combined.throwIfAborted();
334
+ requireBudget(
335
+ typeof credential === "string" &&
336
+ credential.length > 0 &&
337
+ !/[\r\n]/u.test(credential),
338
+ "Credential unavailable",
339
+ );
340
+ const options = {
341
+ method: "POST",
342
+ redirect: "error",
343
+ headers: {
344
+ "Content-Type": "application/json",
345
+ Authorization: `Bearer ${credential}`,
346
+ ...(traceId ? { "X-Client-Request-Id": traceId } : {}),
347
+ },
348
+ body: JSON.stringify(request),
349
+ signal: combined,
350
+ };
351
+ // No await between durable admission and physical fetch.
352
+ const contract = authority.markDispatched(callId, request);
353
+ const pendingResponse = Promise.resolve(
354
+ fetchImpl(contract.endpoint, options),
355
+ );
356
+ // Dispose a late response even when the peer ignores AbortSignal.
357
+ void pendingResponse
358
+ .then((response) => {
359
+ responseBody = response.body;
360
+ if (finished && responseBody)
361
+ void responseBody.cancel().catch(() => {});
362
+ })
363
+ .catch(() => {});
364
+ const response = await raceWithAbort(pendingResponse, combined);
365
+ combined.throwIfAborted();
366
+ requireBudget(response.ok, "Provider HTTP response not successful");
367
+ requireBudget(response.body?.getReader, "Response body unavailable");
368
+ if (request.stream === true)
369
+ requireBudget(
370
+ response.headers.get("content-type")?.includes("text/event-stream"),
371
+ "Unexpected stream content type",
372
+ );
373
+ reader = response.body.getReader();
374
+ const chunks = [];
375
+ let size = 0;
376
+ const stream = new ReadableStream(
377
+ {
378
+ start(streamController) {
379
+ output = streamController;
380
+ },
381
+ async pull(streamController) {
382
+ try {
383
+ combined.throwIfAborted();
384
+ const { done, value } = await reader.read();
385
+ combined.throwIfAborted();
386
+ if (done) {
387
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(
388
+ Buffer.concat(chunks, size),
389
+ );
390
+ cost = authority.settle(
391
+ callId,
392
+ providerReceipt(text, request.stream === true),
393
+ );
394
+ finished = true;
395
+ cleanup();
396
+ reader.releaseLock();
397
+ streamController.close();
398
+ return;
399
+ }
400
+ size += value.byteLength;
401
+ requireBudget(
402
+ size <= maximumResponseBytes,
403
+ "Provider response exceeded bounded parser",
404
+ );
405
+ const chunk = Buffer.from(value);
406
+ chunks.push(chunk);
407
+ // Keep a private accounting snapshot; consumers may mutate bytes.
408
+ streamController.enqueue(new Uint8Array(chunk));
409
+ } catch {
410
+ unknown();
411
+ }
412
+ },
413
+ cancel() {
414
+ unknown();
415
+ },
416
+ },
417
+ { highWaterMark: 0 },
418
+ );
419
+ return {
420
+ response: new Response(stream, {
421
+ status: response.status,
422
+ headers: response.headers,
423
+ }),
424
+ cost: () => cost,
425
+ streaming: request.stream === true,
426
+ };
427
+ } catch {
428
+ throw unknown();
429
+ }
430
+ }
431
+
432
+ return Object.freeze({
433
+ async dispatch(options) {
434
+ const result = await open(options);
435
+ const body = await result.response.text();
436
+ return {
437
+ body,
438
+ streaming: result.streaming,
439
+ cost: result.cost(),
440
+ };
441
+ },
442
+ createFetch({ runId }) {
443
+ label(runId, "run ID");
444
+ return async (endpoint, init = {}) => {
445
+ requireBudget(
446
+ typeof endpoint === "string" &&
447
+ init.method === "POST" &&
448
+ typeof init.body === "string" &&
449
+ Object.keys(init).every((key) =>
450
+ ["method", "headers", "body", "signal", "redirect"].includes(key),
451
+ ),
452
+ "Budget fetch requires an explicit JSON POST without transport overrides",
453
+ );
454
+ const headers = new Headers(init.headers);
455
+ requireBudget(
456
+ /^application\/json(?:\s*;\s*charset=utf-8)?$/iu.test(
457
+ headers.get("content-type") || "",
458
+ ) &&
459
+ [...headers.keys()].every((key) =>
460
+ ["content-type", "authorization", "x-client-request-id"].includes(
461
+ key,
462
+ ),
463
+ ),
464
+ "Unsupported provider headers in budget scope",
465
+ );
466
+ const traceId = headers.get("x-client-request-id");
467
+ requireBudget(!traceId || traceId.length <= 512, "Invalid trace ID");
468
+ const result = await open({
469
+ runId,
470
+ callId: randomUUID(),
471
+ endpoint,
472
+ body: parseBudgetJson(init.body),
473
+ signal: init.signal,
474
+ traceId,
475
+ });
476
+ return result.response;
477
+ };
343
478
  },
344
479
  });
345
480
  }
@@ -6,6 +6,8 @@
6
6
  * can use a managed credential source that is refreshed before every connect.
7
7
  */
8
8
 
9
+ import { requireUnscopedDescendantControl } from "./formal-budget-provider-scope.js";
10
+
9
11
  const MAX_RECONNECT_ATTEMPTS = 8;
10
12
  const MAX_IDLE_TIMEOUT_MS = 10 * 60_000;
11
13
  const MIN_IDLE_TIMEOUT_MS = 100;
@@ -424,6 +426,7 @@ function transportDispatcher(value) {
424
426
  */
425
427
  export class ResilientGatewayClient {
426
428
  constructor(options = {}) {
429
+ requireUnscopedDescendantControl();
427
430
  this.baseUrl = validateBaseUrl(options.baseUrl);
428
431
  if (typeof options.fetch !== "function") {
429
432
  throw gatewayError(
@@ -456,6 +459,7 @@ export class ResilientGatewayClient {
456
459
  }
457
460
 
458
461
  async _request(path, options = {}) {
462
+ requireUnscopedDescendantControl();
459
463
  const relativePath = safeRelativePath(path);
460
464
  const prefix = this.baseUrl.pathname.replace(/\/+$/u, "");
461
465
  const endpoint = new URL(`${prefix}${relativePath}`, this.baseUrl.origin);
@@ -520,6 +524,7 @@ export class ResilientGatewayClient {
520
524
  }
521
525
 
522
526
  async stream(path, options = {}) {
527
+ requireUnscopedDescendantControl();
523
528
  const idleTimeoutMs = positiveInteger(
524
529
  options.idleTimeoutMs,
525
530
  30_000,
@@ -246,6 +246,16 @@ function whichLimitHit(state, budget, nowMs) {
246
246
  * the loop stops. Does NOT mutate `state` — returns the next one.
247
247
  */
248
248
  export function evaluateGoalStep(state, evaluation, budget, opts = {}) {
249
+ if (state.done === true) {
250
+ return {
251
+ state: { ...state },
252
+ decision:
253
+ state.outcome === "completed"
254
+ ? GOAL_DECISION.COMPLETE
255
+ : GOAL_DECISION.EXHAUSTED,
256
+ events: [],
257
+ };
258
+ }
249
259
  const nowMs = Number(opts.now ?? state.startedAtMs);
250
260
  const next = {
251
261
  ...state,
@@ -326,10 +336,23 @@ export class GoalConditionEngine {
326
336
 
327
337
  /** Accumulate a turn's usage before evaluating it. */
328
338
  recordTurnUsage({ tokens = 0, costUsd = 0 } = {}) {
339
+ if (
340
+ !Number.isSafeInteger(tokens) ||
341
+ tokens < 0 ||
342
+ typeof costUsd !== "number" ||
343
+ !Number.isFinite(costUsd) ||
344
+ costUsd < 0 ||
345
+ !Number.isSafeInteger(this.state.tokens + tokens) ||
346
+ !Number.isFinite(this.state.costUsd + costUsd)
347
+ ) {
348
+ throw new RangeError(
349
+ "Goal usage requires finite non-negative cost and safe token counts",
350
+ );
351
+ }
329
352
  this.state = {
330
353
  ...this.state,
331
- tokens: this.state.tokens + (Number(tokens) || 0),
332
- costUsd: this.state.costUsd + (Number(costUsd) || 0),
354
+ tokens: this.state.tokens + tokens,
355
+ costUsd: this.state.costUsd + costUsd,
333
356
  };
334
357
  return this.state;
335
358
  }
@@ -1,3 +1,4 @@
1
+ import { providerFetch } from "./formal-budget-provider-scope.js";
1
2
  /**
2
3
  * hub-llm-client — build a hub LLMClient from the user's cc config / env so
3
4
  * standalone `cc hub ask` can use a configured CLOUD provider instead of the
@@ -40,7 +41,7 @@
40
41
  * stitches system + user-with-facts and hands them off opaque).
41
42
  */
42
43
 
43
- "use strict";
44
+ ("use strict");
44
45
 
45
46
  import { BUILT_IN_PROVIDERS } from "./llm-providers.js";
46
47
 
@@ -202,14 +203,18 @@ export async function openAiCompatChat({
202
203
  const maxTokens = opts.maxTokens ?? opts.max_tokens;
203
204
  if (Number.isFinite(maxTokens)) body.max_tokens = maxTokens;
204
205
 
205
- const resp = await fetchImpl(`${trimBase(base)}/chat/completions`, {
206
- method: "POST",
207
- headers: {
208
- "Content-Type": "application/json",
209
- Authorization: `Bearer ${key}`,
206
+ const resp = await providerFetch(
207
+ `${trimBase(base)}/chat/completions`,
208
+ {
209
+ method: "POST",
210
+ headers: {
211
+ "Content-Type": "application/json",
212
+ Authorization: `Bearer ${key}`,
213
+ },
214
+ body: JSON.stringify(body),
210
215
  },
211
- body: JSON.stringify(body),
212
- });
216
+ fetchImpl,
217
+ );
213
218
  if (!resp.ok) {
214
219
  const t = await resp.text().catch(() => "");
215
220
  throw new Error(
@@ -266,15 +271,19 @@ export async function anthropicChat({
266
271
  if (system) body.system = system;
267
272
  if (typeof opts.temperature === "number") body.temperature = opts.temperature;
268
273
 
269
- const resp = await fetchImpl(`${trimBase(base)}/messages`, {
270
- method: "POST",
271
- headers: {
272
- "Content-Type": "application/json",
273
- "x-api-key": key,
274
- "anthropic-version": "2023-06-01",
274
+ const resp = await providerFetch(
275
+ `${trimBase(base)}/messages`,
276
+ {
277
+ method: "POST",
278
+ headers: {
279
+ "Content-Type": "application/json",
280
+ "x-api-key": key,
281
+ "anthropic-version": "2023-06-01",
282
+ },
283
+ body: JSON.stringify(body),
275
284
  },
276
- body: JSON.stringify(body),
277
- });
285
+ fetchImpl,
286
+ );
278
287
  if (!resp.ok) {
279
288
  const t = await resp.text().catch(() => "");
280
289
  throw new Error(