chainlesschain 0.166.103 → 0.166.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/package.json +1 -1
  2. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  3. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BLrXpdnP.js → AIOps-Bah3HREy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-C0xy9uFy.js → ActionButton-DY2aeZMc.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C6EephQP.js → Analytics-BFly8Xaa.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-ZCXdOD9y.js → AppLayout-dp0Zz-JM.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-D4MfZ-pl.js → Artifacts-DcIg09ai.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-BQ8c3bBu.js → Audit-DkN-lsjZ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BOA7_ljM.js → BackgroundAgents-BjzgusLx.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-60uiQjGT.js → Backup-CU8S6qu9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-BDvEbGs_.js → BaseInput-B1pC2gjL.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-B3IkAuFQ.js → Chat-B6xFp6gD.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-DxNCLZ_X.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-njGbfJWq.js → Checkbox-QHw7Vc4U.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CF7DtdxC.js → Codegen-gdecsfjg.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C_3Zix40.js → Col-B0_JmAmc.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-CzU-ZC2d.js → Community-DRCcP5xR.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BgeqsPRh.js → Compact-DZtoGzhX.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-6l7cwiqp.js → Compliance-BfNTvEnT.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-D4s905fI.js → Cowork-CDXTLm84.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-CGTYSxRS.js → Cron-Big35CbN.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BnSo-Yt3.js → Crosschain-C8ArrAVr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-Dm4vk2X1.js → DID-D_77BI2O.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-D7H-PZ_N.js → Dashboard-D2SxTzEp.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-COslkgxn.js → Dropdown-mR1-b9ud.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-CJWEetmk.js → EmailListRenderer-DPl7TE1c.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-BbzebAWe.js → EvolutionSettings-BxRYLRSH.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BsoTOXwk.js → FamilyGuardDashboard-rnGtXJ5Z.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-Ck5kM_5H.js → Federation-BpxenYK8.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-gShxCqtF.js → FormItemContext-Cz1kDf6l.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-DarN1G-z.js → GenericCardRenderer-CGV3KN6U.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-CJfpLyP5.js → Git-CIRP4UTx.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-CVcJmpQz.js → Governance-DX4r8Vmt.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-ComU_ig8.js → Inference-DQKk02Hl.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-35hprFJy.js → KnowledgeGraph-DdD6J-By.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-BQDqBTz0.js → Logs-DA2w-1_m.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-BpdGAVCC.js → MarkdownRenderer-C9aSGETh.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-B9O9OKnn.js → Marketplace-BsJ6_6KW.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-VMKxA4Wc.js → McpTools-_6k30z6u.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-CcBbf3Bc.js → Memory-C0_dYY0A.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-D7myz46N.js → MobileBridge-CCTnVqiC.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-Da5KBXyx.js → MobileProjects-RZdxDSwC.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-x3NPZfuL.js → Mtc-D4Ys8lvX.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-CwnzptEC.js → MtcAudit-BKTC--a2.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-CCAxf4Ye.js → Multisig-CGR1qOe1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-CptHKz-Q.js → NLProgramming-hKKD4qWb.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-0uEp4DCJ.js → Notes-pCn7Ec40.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-DthvrOLd.js → NotificationSettings-B9eEJXEs.js} +1 -1
  50. package/src/assets/web-panel/assets/OrderTableRenderer-CmJuhaT2.js +1 -0
  51. package/src/assets/web-panel/assets/{Organization-BWf2tmQQ.js → Organization-CcQbrnwW.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-BbvAtEth.js → Overflow-HmD4Mn72.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-zBo6enit.js → P2P-B7LIgx5H.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-D_LMQ4A4.js → PdhVaultBrowser-CXiHnBfX.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-C1ucb2ck.js → Permissions-CQRI3y_l.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-Bz64RddJ.js → PersonalDataHub-1EeHmsy5.js} +5 -5
  57. package/src/assets/web-panel/assets/{Pipeline-DXJt3y3_.js → Pipeline-BFyoIdn8.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-DiZ8b_ut.js → Privacy-BxWthXMF.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-DfLKKrJ4.js → ProjectInit-DfigBilu.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-Ct6kJuIY.js → ProjectSettings-CFmIcVc-.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-BBUMlZ3m.js → Projects-CR7H59v3.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-DYVQPHvc.js → Providers-2ulom-76.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-23Il7iPp.js → QrScannerModal-BwhO07b_.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-WgOZKrv1.js → QuickAsk-_Wg34OwF.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-CRIka6xT.js → Recommend-D_jq6Svg.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-DOy3GjVZ.js → RemoteSession-BlzvEfsa.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-BXyi6xeR.js → Reputation-9CTXHfL4.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-CoMxuIcR.js → Row-DlCYCN-t.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-87hPkoM6.js → RssFeed-BvlInFCH.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-B1EqFCO4.js → Search-CfaUzKuL.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-HcC41eGs.js → Security-bAWgLyve.js} +3 -3
  72. package/src/assets/web-panel/assets/{Services-BqLwmvCM.js → Services-C4LOGACW.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-CIMmjW4r.js → Skeleton-Dd0Uwh3_.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-CgurezJU.js → Skills-ZYF1kYCU.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-CJuRuqPE.js → Sla-DpsmIaEo.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-BUwsbd1o.js → SpeechSettings-BBXoJ4yc.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-qo04TOX-.js → SyncSettings-SOnROi2Y.js} +2 -2
  78. package/src/assets/web-panel/assets/Tasks-HFA_ZlDk.js +1 -0
  79. package/src/assets/web-panel/assets/{Templates-D1PTczPW.js → Templates-fsK-RCEc.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-Bi0FkEPR.js → Tenant-CuqYxamb.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-DfSt3bIF.js → Terminal-GKlI2R5F.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-B7bIVs4R.js → TimelineRenderer-DMDMHLeY.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-B6w5XrMb.js → Tokens-BoV60rh3.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-XXVSDWiF.js → Trigger-CE344-Ws.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-DYvlgcK-.js → Trust-DENwgkVc.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-XyYwJMBM.js → UkeySign-FrcM7yfm.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-C47WbtHH.js → VideoEditing-DAY9WaC2.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CYfqTj_C.js → Wallet-BuUR5i82.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-3AduFn1l.js → WebAuthn-nfSQuq85.js} +3 -3
  90. package/src/assets/web-panel/assets/{WorkflowEditor-DHYEHjMJ.js → WorkflowEditor-DHhPOp9l.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-AeehErSw.js → chat-C4nsLzYy.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-DKXJ8paX.js → colors-BwXH0Wjn.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-BOlCiomD.js → compact-item-uApFG30S.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BAP6U85b.js → createContext-BpyYs4fe.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-Bk6HGn3N.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-BLmeL1Xe.js → hasIn-MXCLi00p.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CdNSmvog.js → index-1LxX2IwJ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Dv8-g1pY.js → index-61CBBrHE.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-ClpwxOXo.js → index-B0P1Z_oa.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CgtaOzMo.js → index-B7jojVc7.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BRcJlKSa.js → index-BEGMfaOo.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-wyKpTsGM.js → index-BOhjapd5.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BuUwKmEI.js → index-BaBwXpAp.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bb1LPYdi.js +1 -0
  105. package/src/assets/web-panel/assets/{index-Co7pjF53.js → index-Btv--Nnt.js} +1 -1
  106. package/src/assets/web-panel/assets/index-C-FsTuFG.js +1 -0
  107. package/src/assets/web-panel/assets/{index-CFKzWIo8.js → index-CF0bUOP4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CaHhfpBi.js → index-CNjUPfJP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-ZMbXl5lo.js → index-CRmNdEk_.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BTnp4Nt3.js → index-C_oOpXhP.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-OLKhib9T.js → index-ChUTKrOq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-T28naovw.js → index-CiIpIYhs.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bpx54vfs.js → index-CwVvk2i7.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BAS2QqLn.js → index-D3_tj06B.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CRa4pAaM.js → index-DE4W4vLO.js} +3 -3
  116. package/src/assets/web-panel/assets/{index-BSOenF5s.js → index-DLxKx5_J.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DE7cbDDe.js → index-DTXi_gQh.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DWYqOI1A.js → index-Daco83me.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DQLQuwpA.js → index-DffWVpZv.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiqeF0Pf.js → index-Dkncxel3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-IlzfIRNh.js → index-Dn12gJJU.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Bn54FU3i.js → index-Dt-nq2eF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Bt-FCVzh.js → index-Du01X9N9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-EKNODw4P.js → index-DucLR6YX.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-C5AKY6bJ.js → index-Gz2rGWef.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C8qi53Ki.js → index-IvgszJ3C.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-urPvyUVk.js → index-S80LLjdx.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bs2O3f81.js → index-WKbnwbKa.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DpZqbc4w.js → index-XHBXtvqp.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-De2fPxdb.js → index-nn22NSDK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B0IjlB9m.js → index-pCIAE2yS.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-PEe9OWEQ.js → index-pRpMLhMt.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-FPXLIXoy.js → index-pgHGBAoF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BhcpWl5z.js → index-ugIUt5TL.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DsQ38fVY.js → index-xLGf7f9K.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-CDWa1rnn.js → initDefaultProps-C58CkKEo.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-CX2Cnsd5.js → motion-BLWPO2NV.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-Bfe1NcEF.js → move-BgrFMwYp.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-DbdqleHE.js → mtc-parser-Cq2AuGzl.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-CE2K7NVv.js → omit-Bb-9L7if.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-y_bZgxWG.js → pickAttrs-vLmOxYr1.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-BOHR4zwr.js → placementArrow-BNySU_dJ.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-B8Jwq05-.js → responsiveObserve-xnbwagnm.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-BD7RJ3Kn.js → slide-BccbY5Cn.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-BuxDJeDx.js → statusUtils-DOp9d2-w.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-DgYQ2Q0o.js → styleChecker-Bu1w6ho9.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-DAHF0Fwi.js → useFlexGapSupport-ZsUOrVdL.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-D4kDyLFp.js → useFs-C0zpzKtO.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-BHtSxW4A.js → usePersonalDataHub-DO1OdgXK.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-CHKtdqW0.js → vnode-jTIRyhR5.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-CIZAMma9.js → zoom-DKvS7-Qs.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/commands/agent.js +56 -20
  154. package/src/commands/batch.js +17 -12
  155. package/src/commands/eval.js +24 -8
  156. package/src/data/changelog.json +13 -0
  157. package/src/harness/mcp-client.js +187 -26
  158. package/src/lib/background-session-transport.js +21 -5
  159. package/src/lib/decision-layer/runtime.js +180 -10
  160. package/src/lib/direct-model-usage.js +34 -0
  161. package/src/lib/evolution/agent-evolution-ingress.js +63 -7
  162. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +10 -0
  163. package/src/lib/evolution/evolution-evidence-projector.js +46 -7
  164. package/src/lib/evolution/skill-decision-projection.js +211 -0
  165. package/src/lib/evolution/skill-retrieval-revocation-authority.js +17 -2
  166. package/src/lib/skill-loader.js +16 -0
  167. package/src/repl/agent-repl.js +86 -7
  168. package/src/runtime/agent-core.js +111 -13
  169. package/src/runtime/headless-runner.js +212 -22
  170. package/src/runtime/headless-stream.js +189 -25
  171. package/src/runtime/mcp-config.js +26 -17
  172. package/src/runtime/mcp-tool-search.js +44 -14
  173. package/src/assets/web-panel/assets/ChatBubbleRenderer-n6BVStHf.js +0 -1
  174. package/src/assets/web-panel/assets/OrderTableRenderer-CxXuPzZg.js +0 -1
  175. package/src/assets/web-panel/assets/Tasks-QEFENVsp.js +0 -1
  176. package/src/assets/web-panel/assets/devWarning-rGldFhxo.js +0 -1
  177. package/src/assets/web-panel/assets/index-Cw-lfvvV.js +0 -1
  178. package/src/assets/web-panel/assets/index-QBXRSyoZ.js +0 -1
@@ -744,6 +744,51 @@ function mcpTransportError(code, message, details = {}) {
744
744
  return error;
745
745
  }
746
746
 
747
+ function pendingToolFailure(error, entry, method, dispatched) {
748
+ const httpStatus = safeRpcProperty(error, "status");
749
+ if (
750
+ method !== "tools/call" ||
751
+ !dispatched ||
752
+ isMcpRpcError(error) ||
753
+ safeRpcProperty(error, "outcomeUnknown") != null ||
754
+ (safeRpcProperty(error, "code") === "CC_MCP_HTTP_STATUS" &&
755
+ (httpStatus === 401 || httpStatus === 403 || httpStatus === 404))
756
+ ) {
757
+ return error;
758
+ }
759
+ const originalCode = safeRpcProperty(error, "code");
760
+ if (
761
+ typeof originalCode === "string" &&
762
+ originalCode.startsWith("CC_MCP_") &&
763
+ !isProxy(error) &&
764
+ error instanceof Error
765
+ ) {
766
+ try {
767
+ error.dispatched = true;
768
+ error.outcomeUnknown = true;
769
+ return error;
770
+ } catch {
771
+ // A frozen host error still needs a public uncertainty marker.
772
+ }
773
+ }
774
+ const message = safeRpcErrorText(error) || "MCP tool request failed";
775
+ return mcpTransportError(
776
+ typeof originalCode === "string" && originalCode.startsWith("CC_MCP_")
777
+ ? originalCode
778
+ : "CC_MCP_TOOL_OUTCOME_UNKNOWN",
779
+ `${message}; tool outcome is unknown and was not replayed`,
780
+ {
781
+ transport: entry?.transportKind,
782
+ url: entry?.config?.url,
783
+ status: httpStatus,
784
+ closeCode: safeRpcProperty(error, "closeCode"),
785
+ limitBytes: safeRpcProperty(error, "limitBytes"),
786
+ dispatched: true,
787
+ outcomeUnknown: true,
788
+ },
789
+ );
790
+ }
791
+
747
792
  function dispatchMcpToolRequest(options, metadata, dispatch) {
748
793
  const admission = options?.dispatchAdmission ?? null;
749
794
  if (metadata.method !== "tools/call" || admission === null) {
@@ -916,8 +961,8 @@ function mcpInboundBudgetError(serverName, entry, budget) {
916
961
  function mcpJsonBudgetError(serverName, entry, budget) {
917
962
  const dispatched = Boolean(
918
963
  (entry?._pending instanceof Map && entry._pending.size > 0) ||
919
- (entry?._httpRequestControllers instanceof Set &&
920
- entry._httpRequestControllers.size > 0),
964
+ (entry?._httpRequestControllers instanceof Set &&
965
+ entry._httpRequestControllers.size > 0),
921
966
  );
922
967
  if (budget === "depth") {
923
968
  const limitDepth = mcpJsonDepthLimit(entry?.config?.maxJsonDepth);
@@ -956,6 +1001,15 @@ function mcpSseEventTimeoutError(timeoutMs) {
956
1001
  return error;
957
1002
  }
958
1003
 
1004
+ function mcpSseHeaderTimeoutError(timeoutMs) {
1005
+ const error = new Error(
1006
+ `MCP HTTP message stream headers exceeded the ${timeoutMs}ms host deadline`,
1007
+ );
1008
+ error.code = "CC_MCP_SSE_HEADER_TIMEOUT";
1009
+ error.timeoutMs = timeoutMs;
1010
+ return error;
1011
+ }
1012
+
959
1013
  function mcpHttpResponseTimeoutError(
960
1014
  serverName,
961
1015
  entry,
@@ -999,6 +1053,7 @@ const MCP_NON_RETRYABLE_PROTOCOL_ERROR_CODES = new Set([
999
1053
  "CC_MCP_HTTP_RESPONSE_TIMEOUT",
1000
1054
  "CC_MCP_HTTP_DISCARD_TIMEOUT",
1001
1055
  "CC_MCP_SSE_EVENT_TIMEOUT",
1056
+ "CC_MCP_SSE_HEADER_TIMEOUT",
1002
1057
  "CC_MCP_INBOUND_RATE_EXCEEDED",
1003
1058
  "CC_MCP_INBOUND_TRAFFIC_EXCEEDED",
1004
1059
  "CC_MCP_JSON_DEPTH_EXCEEDED",
@@ -1009,6 +1064,8 @@ const MCP_NON_RETRYABLE_PROTOCOL_ERROR_CODES = new Set([
1009
1064
  "CC_MCP_RPC_ERROR_INVALID",
1010
1065
  "CC_MCP_RPC_RESPONSE_INVALID",
1011
1066
  "CC_MCP_RPC_MESSAGE_INVALID",
1067
+ "CC_MCP_TOOL_CATALOG_STALE",
1068
+ "CC_MCP_TOOL_NOT_ADVERTISED",
1012
1069
  ]);
1013
1070
 
1014
1071
  function isMcpNonRetryableProtocolError(error) {
@@ -1422,6 +1479,7 @@ export class MCPClient extends EventEmitter {
1422
1479
  this._toolMetadataBytes = 0;
1423
1480
  this._nextId = 1;
1424
1481
  this._reconnectors = new Map(); // name → async () => config|null
1482
+ this._unknownOutcomeRecovery = new Set(); // reconnect only for future explicit calls
1425
1483
  this._reconnecting = new Map(); // name → in-flight reconnect promise
1426
1484
  // progressToken → reset-idle callback for in-flight tools/call requests.
1427
1485
  // Tokens are generated by this client and are removed on every terminal
@@ -2162,15 +2220,25 @@ export class MCPClient extends EventEmitter {
2162
2220
  * Register a reconnector for a server: an async function that returns a
2163
2221
  * FRESH connection config (or null when the server can't be found anymore).
2164
2222
  * When a `callTool` on that server fails with a connection-shaped error,
2165
- * the client re-resolves the config, reconnects, and retries the call once.
2223
+ * the client re-resolves the config. An opt-in bridge may also reconnect
2224
+ * after an unknown tool outcome, without replaying that call.
2166
2225
  *
2167
2226
  * Used by the IDE bridge: a window reload / extension update restarts the
2168
2227
  * editor's MCP server on a new port with a new token, so the original
2169
2228
  * config is permanently dead but a lockfile re-scan finds the new one.
2170
2229
  */
2171
- setReconnector(name, fn) {
2172
- if (typeof fn === "function") this._reconnectors.set(name, fn);
2173
- else this._reconnectors.delete(name);
2230
+ setReconnector(name, fn, options = {}) {
2231
+ if (typeof fn === "function") {
2232
+ this._reconnectors.set(name, fn);
2233
+ if (options.recoverUnknownOutcome === true) {
2234
+ this._unknownOutcomeRecovery.add(name);
2235
+ } else {
2236
+ this._unknownOutcomeRecovery.delete(name);
2237
+ }
2238
+ } else {
2239
+ this._reconnectors.delete(name);
2240
+ this._unknownOutcomeRecovery.delete(name);
2241
+ }
2174
2242
  }
2175
2243
 
2176
2244
  /** Return the opaque host authority used to resolve policy-bearing MCP cwd. */
@@ -3057,6 +3125,7 @@ export class MCPClient extends EventEmitter {
3057
3125
  this._replaceToolInventory(name, entry, toolsResult?.tools || []);
3058
3126
  } catch (err) {
3059
3127
  if (isMcpFatalDiscoveryError(err)) throw err;
3128
+ entry._toolInventoryReady = true;
3060
3129
  if (advertisesTools) {
3061
3130
  toolsDiscoveryFailed = true;
3062
3131
  entry.toolsError = err?.message || String(err);
@@ -3464,6 +3533,7 @@ export class MCPClient extends EventEmitter {
3464
3533
  clientBytesUsed,
3465
3534
  });
3466
3535
  entry.tools = admitted.tools;
3536
+ entry._toolInventoryReady = true;
3467
3537
  entry._toolMetadataBytes = admitted.metadataBytes;
3468
3538
  this._toolMetadataBytes = clientBytesUsed + admitted.metadataBytes;
3469
3539
  return entry.tools;
@@ -3525,9 +3595,9 @@ export class MCPClient extends EventEmitter {
3525
3595
  * Call a tool on a specific server. If the server has a registered
3526
3596
  * reconnector and the call fails with a connection-shaped error (server
3527
3597
  * restarted / token rotated / entry dropped), re-resolve the config,
3528
- * reconnect, and retry the call exactly once. HTTP 404 only reinitializes
3529
- * the connection: an already-dispatched tool has an unknown outcome and
3530
- * must never be retried automatically just because its session expired.
3598
+ * reconnect, and retry a call only when its outcome is known not to have
3599
+ * occurred. An already-dispatched tool with an unknown outcome may refresh
3600
+ * the connection for a later explicit call, but is never replayed here.
3531
3601
  * @param {string} serverName - Server name
3532
3602
  * @param {string} toolName - Tool name
3533
3603
  * @param {object} args - Tool arguments
@@ -3589,7 +3659,28 @@ export class MCPClient extends EventEmitter {
3589
3659
  // Retry exactly once after every out-of-band flow reports completion.
3590
3660
  return this._callToolOnce(serverName, toolName, args, options);
3591
3661
  }
3592
- if (safeRpcProperty(err, "outcomeUnknown") === true) throw err;
3662
+ if (safeRpcProperty(err, "outcomeUnknown") === true) {
3663
+ if (
3664
+ !options?.signal?.aborted &&
3665
+ isLikelyConnectionError(err) &&
3666
+ this._unknownOutcomeRecovery.has(serverName)
3667
+ ) {
3668
+ const current = this.servers.get(serverName);
3669
+ const recovered =
3670
+ current && current !== attemptedEntry
3671
+ ? current.state === ServerState.CONNECTED
3672
+ : await this._tryReconnect(serverName, {
3673
+ connectAuthRetryUsed: true,
3674
+ expectedEntry: attemptedEntry,
3675
+ });
3676
+ try {
3677
+ err.connectionRecovered = Boolean(recovered);
3678
+ } catch {
3679
+ // A frozen error still retains its original unknown-outcome marker.
3680
+ }
3681
+ }
3682
+ throw err;
3683
+ }
3593
3684
  if (
3594
3685
  !this._reconnectors.has(serverName) &&
3595
3686
  !this.servers.get(serverName)?.config?.headersHelper
@@ -3704,6 +3795,23 @@ export class MCPClient extends EventEmitter {
3704
3795
  if (entry.state !== ServerState.CONNECTED) {
3705
3796
  throw new Error(`Server "${serverName}" is not connected`);
3706
3797
  }
3798
+ if (entry._toolInventoryDirty) {
3799
+ throw mcpTransportError(
3800
+ "CC_MCP_TOOL_CATALOG_STALE",
3801
+ `MCP tool catalog for server "${serverName}" is refreshing; retry after it settles`,
3802
+ { transport: entry.transportKind, dispatched: false },
3803
+ );
3804
+ }
3805
+ if (
3806
+ entry._toolInventoryReady &&
3807
+ !entry.tools.some((tool) => tool.name === toolName)
3808
+ ) {
3809
+ throw mcpTransportError(
3810
+ "CC_MCP_TOOL_NOT_ADVERTISED",
3811
+ `MCP tool "${toolName}" is not advertised by server "${serverName}"`,
3812
+ { transport: entry.transportKind, dispatched: false },
3813
+ );
3814
+ }
3707
3815
 
3708
3816
  const timeoutMs = mcpToolIdleTimeoutMs(entry.config, options);
3709
3817
  const progressToken = `cc:${randomUUID()}`;
@@ -4137,7 +4245,8 @@ export class MCPClient extends EventEmitter {
4137
4245
  };
4138
4246
  const pending = {
4139
4247
  resolve: (value) => settle(resolve, value),
4140
- reject: (error) => settle(reject, error),
4248
+ reject: (error) =>
4249
+ settle(reject, pendingToolFailure(error, entry, method, dispatched)),
4141
4250
  };
4142
4251
  entry._pending.set(id, pending);
4143
4252
 
@@ -4184,7 +4293,7 @@ export class MCPClient extends EventEmitter {
4184
4293
  if (!longRunning && timeoutMs > 0) {
4185
4294
  timeout = setTimeout(() => {
4186
4295
  const timeoutError = new Error(`Request timeout: ${method}`);
4187
- if (!settle(reject, timeoutError)) return;
4296
+ if (!pending.reject(timeoutError)) return;
4188
4297
  this._sendRequestCancellation(
4189
4298
  serverName,
4190
4299
  method,
@@ -4276,7 +4385,8 @@ export class MCPClient extends EventEmitter {
4276
4385
  };
4277
4386
  const pending = {
4278
4387
  resolve: (value) => settle(resolve, value),
4279
- reject: (error) => settle(reject, error),
4388
+ reject: (error) =>
4389
+ settle(reject, pendingToolFailure(error, entry, method, dispatched)),
4280
4390
  };
4281
4391
  entry._pending.set(id, pending);
4282
4392
 
@@ -4314,7 +4424,7 @@ export class MCPClient extends EventEmitter {
4314
4424
  `Request timeout: ${method} (WebSocket, no response in ${timeoutMs}ms)`,
4315
4425
  { transport: entry.transportKind, url: entry.config?.url },
4316
4426
  );
4317
- if (!settle(reject, timeoutError)) return;
4427
+ if (!pending.reject(timeoutError)) return;
4318
4428
  this._sendRequestCancellation(
4319
4429
  serverName,
4320
4430
  method,
@@ -4472,7 +4582,10 @@ export class MCPClient extends EventEmitter {
4472
4582
  ? { dispatcher: entry._httpDispatcher }
4473
4583
  : {}),
4474
4584
  }),
4475
- stream.controller?.signal,
4585
+ {
4586
+ controller: stream.controller,
4587
+ timeoutMs: _sseEventTimeout(entry.config?.requestTimeoutMs),
4588
+ },
4476
4589
  );
4477
4590
  if (response.status === 405) {
4478
4591
  _cancelHttpResponseBody(response);
@@ -4547,6 +4660,7 @@ export class MCPClient extends EventEmitter {
4547
4660
  return;
4548
4661
  }
4549
4662
  if (error?.code === "CC_MCP_SSE_EVENT_TIMEOUT") return;
4663
+ if (error?.code === "CC_MCP_SSE_HEADER_TIMEOUT") return;
4550
4664
  if (error?.code === "CC_MCP_INBOUND_RATE_EXCEEDED") return;
4551
4665
  if (error?.code === "CC_MCP_INBOUND_TRAFFIC_EXCEEDED") return;
4552
4666
  if (error?.code === "CC_MCP_JSON_DEPTH_EXCEEDED") return;
@@ -4853,10 +4967,15 @@ export class MCPClient extends EventEmitter {
4853
4967
  throwIfAborted();
4854
4968
  } catch (abortError) {
4855
4969
  settleLifecycle("failed-closed");
4856
- throw abortError;
4970
+ throw pendingToolFailure(
4971
+ abortError,
4972
+ entry,
4973
+ method,
4974
+ request?.dispatched,
4975
+ );
4857
4976
  }
4858
4977
  settleLifecycle("failed-closed");
4859
- throw err;
4978
+ throw pendingToolFailure(err, entry, method, request?.dispatched);
4860
4979
  } finally {
4861
4980
  if (timer) clearTimeout(timer);
4862
4981
  if (responseTimer) clearTimeout(responseTimer);
@@ -5272,15 +5391,22 @@ export class MCPClient extends EventEmitter {
5272
5391
  async _refreshServerList(serverName, kind) {
5273
5392
  const entry = this.servers.get(serverName);
5274
5393
  if (!entry) return;
5394
+ if (kind === "tools") {
5395
+ entry._toolInventoryDirty = true;
5396
+ }
5275
5397
  const flags = (entry._listRefresh = entry._listRefresh || {});
5276
5398
  if (flags[`${kind}Running`]) {
5277
5399
  flags[`${kind}Dirty`] = true;
5278
5400
  return;
5279
5401
  }
5280
5402
  flags[`${kind}Running`] = true;
5403
+ let refreshed = false;
5404
+ let toolCount = 0;
5281
5405
  try {
5282
5406
  do {
5407
+ if (this.servers.get(serverName) !== entry) return;
5283
5408
  flags[`${kind}Dirty`] = false;
5409
+ refreshed = false;
5284
5410
  try {
5285
5411
  const method =
5286
5412
  kind === "tools"
@@ -5289,6 +5415,10 @@ export class MCPClient extends EventEmitter {
5289
5415
  ? "resources/templates/list"
5290
5416
  : "resources/list";
5291
5417
  const result = await this._sendRequest(serverName, method, {});
5418
+ // A reconnect can replace this entry while the old list request is
5419
+ // pending. Never let its late result mutate the replacement's global
5420
+ // metadata budget or publish a stale inventory event.
5421
+ if (this.servers.get(serverName) !== entry) return;
5292
5422
  const list =
5293
5423
  (kind === "tools"
5294
5424
  ? result?.tools
@@ -5298,14 +5428,17 @@ export class MCPClient extends EventEmitter {
5298
5428
  if (kind === "tools") {
5299
5429
  this._replaceToolInventory(serverName, entry, list);
5300
5430
  entry.toolsError = null;
5431
+ toolCount = list.length;
5301
5432
  } else {
5302
5433
  entry[kind] = list;
5434
+ this.emit(`${kind}-changed`, {
5435
+ server: serverName,
5436
+ count: list.length,
5437
+ });
5303
5438
  }
5304
- this.emit(`${kind}-changed`, {
5305
- server: serverName,
5306
- count: list.length,
5307
- });
5439
+ refreshed = true;
5308
5440
  } catch (error) {
5441
+ if (this.servers.get(serverName) !== entry) return;
5309
5442
  if (kind === "tools" && isMcpToolMetadataError(error)) {
5310
5443
  entry.toolsError = error.message;
5311
5444
  this.emit("server-error", {
@@ -5331,6 +5464,19 @@ export class MCPClient extends EventEmitter {
5331
5464
  } while (flags[`${kind}Dirty`]);
5332
5465
  } finally {
5333
5466
  flags[`${kind}Running`] = false;
5467
+ if (
5468
+ kind === "tools" &&
5469
+ refreshed &&
5470
+ this.servers.get(serverName) === entry
5471
+ ) {
5472
+ // Event listeners may call a newly advertised tool immediately.
5473
+ entry._toolInventoryDirty = false;
5474
+ try {
5475
+ this.emit("tools-changed", { server: serverName, count: toolCount });
5476
+ } catch {
5477
+ // An observer cannot make a successfully refreshed catalog stale.
5478
+ }
5479
+ }
5334
5480
  }
5335
5481
  }
5336
5482
 
@@ -5601,9 +5747,12 @@ function _readHttpReaderChunk(reader, signal) {
5601
5747
  });
5602
5748
  }
5603
5749
 
5604
- function _waitForHttpMessageStreamResponse(responsePromise, signal) {
5605
- if (!signal?.addEventListener) return Promise.resolve(responsePromise);
5606
- if (signal.aborted) {
5750
+ function _waitForHttpMessageStreamResponse(
5751
+ responsePromise,
5752
+ { controller, timeoutMs } = {},
5753
+ ) {
5754
+ const signal = controller?.signal;
5755
+ if (signal?.aborted) {
5607
5756
  Promise.resolve(responsePromise).then(
5608
5757
  (response) => _cancelHttpResponseBody(response),
5609
5758
  () => {},
@@ -5613,7 +5762,11 @@ function _waitForHttpMessageStreamResponse(responsePromise, signal) {
5613
5762
  return new Promise((resolve, reject) => {
5614
5763
  let settled = false;
5615
5764
  let aborted = false;
5616
- const cleanup = () => signal.removeEventListener?.("abort", onAbort);
5765
+ let timer;
5766
+ const cleanup = () => {
5767
+ clearTimeout(timer);
5768
+ signal?.removeEventListener?.("abort", onAbort);
5769
+ };
5617
5770
  const finish = (settler, value) => {
5618
5771
  if (settled) return false;
5619
5772
  settled = true;
@@ -5625,7 +5778,15 @@ function _waitForHttpMessageStreamResponse(responsePromise, signal) {
5625
5778
  aborted = true;
5626
5779
  finish(reject, _httpReadAbortedError());
5627
5780
  };
5628
- signal.addEventListener("abort", onAbort, { once: true });
5781
+ if (Number.isFinite(timeoutMs) && timeoutMs > 0) {
5782
+ timer = setTimeout(() => {
5783
+ aborted = true;
5784
+ if (finish(reject, mcpSseHeaderTimeoutError(timeoutMs))) {
5785
+ controller?.abort();
5786
+ }
5787
+ }, timeoutMs);
5788
+ }
5789
+ signal?.addEventListener?.("abort", onAbort, { once: true });
5629
5790
  Promise.resolve(responsePromise).then(
5630
5791
  (response) => {
5631
5792
  if (!finish(resolve, response) && aborted) {
@@ -50,17 +50,28 @@ export function transportPipePath(id, dir, options = {}) {
50
50
  */
51
51
  export function createNdjsonReader(onMessage, onError = () => {}) {
52
52
  let carry = "";
53
+ let droppingOversizedLine = false;
54
+ const decoder = new TextDecoder("utf-8");
53
55
  return (chunk) => {
54
- carry += chunk.toString("utf8");
55
- if (carry.length > MAX_LINE_BYTES) {
56
- carry = "";
57
- onError(new Error("line too long"));
58
- return;
56
+ let text =
57
+ typeof chunk === "string"
58
+ ? chunk
59
+ : decoder.decode(chunk, { stream: true });
60
+ if (droppingOversizedLine) {
61
+ const end = text.indexOf("\n");
62
+ if (end < 0) return;
63
+ droppingOversizedLine = false;
64
+ text = text.slice(end + 1);
59
65
  }
66
+ carry += text;
60
67
  let index;
61
68
  while ((index = carry.indexOf("\n")) !== -1) {
62
69
  const line = carry.slice(0, index).replace(/\r$/, "");
63
70
  carry = carry.slice(index + 1);
71
+ if (Buffer.byteLength(line, "utf8") > MAX_LINE_BYTES) {
72
+ onError(new Error("line too long"));
73
+ continue;
74
+ }
64
75
  if (!line.trim()) continue;
65
76
  let message;
66
77
  try {
@@ -71,6 +82,11 @@ export function createNdjsonReader(onMessage, onError = () => {}) {
71
82
  }
72
83
  onMessage(message);
73
84
  }
85
+ if (Buffer.byteLength(carry, "utf8") > MAX_LINE_BYTES) {
86
+ carry = "";
87
+ droppingOversizedLine = true;
88
+ onError(new Error("line too long"));
89
+ }
74
90
  };
75
91
  }
76
92