chainlesschain 0.165.5 → 0.165.6

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 (193) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-CEeYTeDB.js → AIOps-vfoDb6HZ.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-Cm5f7p93.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-yRuvgWxa.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BROsC4h1.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-CimHApb1.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DI8kr8zI.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-DSLzKy-n.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-DkH_n2r1.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-CKSrUoRA.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-ClwfMXUS.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-BYTehcQG.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-B8AMcp6J.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-BNSogPo3.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-JxPvGb8O.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DF7bFAgE.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-Bvhqnwrq.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-BDS0CJvM.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-DqvRfxoQ.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-CDcIvnqS.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-D7jaUxU3.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-B5A18wEV.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-_6IVkGag.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9PeiKCG.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-B4IAR9zv.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-CxQeSNx1.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BS8SouUX.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-Dq9aYb5Q.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-BrCEe9AJ.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-DybOpiY2.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Bseca1_I.js → Logs-DBpzbRfx.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-d3kM4DDL.js → Marketplace-Cc7CCSCu.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-DFaIhOfu.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-KCqlp9ol.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-Bys84ySN.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-B-6qU-je.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-BgY-fvCq.js} +4 -4
  42. package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-B8pCmqQJ.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-BbOmYutA.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-BocLQz_M.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CSwbxEop.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-CqFNSYG3.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-QrDXVdMJ.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-Cxntotjt.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-CIdBOwNq.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-DH7OM8XU.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-CnJqbWce.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-5C3HG1kH.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-B4yIp7xK.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BMvt-2M0.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-CvSTEMRG.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-BKBJxEek.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BdwUqFWN.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-DtnCKlTm.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-sTbgwyNr.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-Bzr14gSb.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-C_TF5IaJ.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-GCAM4H2K.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-Dir0r6OQ.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CpGnlecX.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-DMMamC6u.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-yqay5yG-.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-C5i2wh9G.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-vUqk35yj.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-DzcSkhot.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-yM5y9yvK.js → Sla-CB6qbYNr.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DPy1XMYh.js → SpeechSettings-BHUI7cox.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CFVhEyyr.js → SyncSettings-D-RrsxTJ.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-DFxdOJiq.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-SKAob_Aq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BPfsfHQ-.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-8iw7FpUf.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-COYhN18e.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-B-JzXcf6.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-BJ5bg0nI.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BG_ByRMf.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-B3kOnSiU.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-CcoGFnK7.js} +3 -3
  86. package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-DdT8vRC_.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-CZu3M9N8.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-DxJt3Qba.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DdYfwJaB.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-BG3SUzwj.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-By51z2-1.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-B0apZ3KJ.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-3jWkIu5j.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-B3rWuPMW.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-B5rLiEo0.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-BBPf3TpM.js} +1 -1
  98. package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
  99. package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-BIrJfqO4.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BLIbGOLX.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-BP94CrBV.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-BWF8_mkx.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-BYINoRLK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BZ_0qm4g.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-BewKYrX9.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-BfWYsB9t.js} +1 -1
  107. package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
  108. package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-Bissefox.js} +3 -3
  109. package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-BkvcEo4p.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-Bos5_aMV.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-C1C9i79A.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-CAhHirVH.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-CNIxXMmN.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CW93tZDv.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-CWEuZWCK.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-Cp6e-hGw.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-CpjZhgNd.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-CzmQElZy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-DCn8IcxV.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-DE76D-kA.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-DaIz0NAW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-Da_WBdLX.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-Dz49VOyQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-Q2SCX-MI.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-RD6O-0C6.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-SQqOlR3d.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-ZFbps9po.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-ZxtP8jG7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-bP_VlDb4.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-kf57LJ93.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-oxRrmFO9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-p1_8SbI1.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-BWJktcg8.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-BEXBzUFV.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-BUKwn2sx.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-FTI7btqQ.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CsLtSPH3.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-DWZxsT_S.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-Bcfs43CX.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-a9bcC1p-.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-BDTzgOXN.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-Iq1j_HUg.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-DAZEYxeh.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-wXTpNTHt.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-hjEoG85v.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B5g7TN7r.js → zoom-BHVpyKg_.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +2 -2
  151. package/src/commands/agent.js +14 -2
  152. package/src/commands/background-session.js +100 -10
  153. package/src/commands/mcp.js +5 -0
  154. package/src/commands/plugin.js +145 -29
  155. package/src/commands/remote-control.js +295 -110
  156. package/src/commands/session-message.js +171 -0
  157. package/src/commands/session.js +11 -0
  158. package/src/data/changelog.json +15 -2
  159. package/src/harness/mcp-client.js +141 -10
  160. package/src/lib/background-session-transport.js +153 -27
  161. package/src/lib/mcp-client.js +2 -0
  162. package/src/lib/mcp-headers-helper.js +47 -5
  163. package/src/lib/mcp-oauth.js +55 -6
  164. package/src/lib/mcp-tls.js +230 -0
  165. package/src/lib/output-styles.js +12 -0
  166. package/src/lib/plugin-runtime/install.js +614 -71
  167. package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
  168. package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
  169. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
  170. package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
  171. package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
  172. package/src/lib/plugin-runtime/remote-source.js +435 -55
  173. package/src/lib/plugin-security.js +393 -41
  174. package/src/lib/plugin-source-identity.js +850 -0
  175. package/src/lib/remote-approval-bridge.js +136 -46
  176. package/src/lib/remote-control.js +217 -31
  177. package/src/lib/session-message-fabric.js +1105 -0
  178. package/src/lib/session-projection.js +45 -2
  179. package/src/lib/session-runtime-retention.js +155 -0
  180. package/src/lib/settings-loader.cjs +29 -0
  181. package/src/repl/agent-repl.js +51 -14
  182. package/src/repl/permission-prompt.js +31 -8
  183. package/src/repl/repl-keybindings.js +55 -0
  184. package/src/runtime/agent-core.js +51 -2
  185. package/src/runtime/headless-runner.js +47 -66
  186. package/src/runtime/headless-stream.js +18 -24
  187. package/src/runtime/mcp-config.js +2 -0
  188. package/src/runtime/policies/agent-policy.js +2 -0
  189. package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
  190. package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
  191. package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
  192. package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
  193. package/src/assets/web-panel/assets/index-z1kFadaX.js +0 -1
@@ -0,0 +1,171 @@
1
+ import { logger } from "../lib/logger.js";
2
+ import { SessionMessageFabric } from "../lib/session-message-fabric.js";
3
+
4
+ function output(value, options = {}) {
5
+ if (options.json) {
6
+ logger.log(JSON.stringify(value, null, 2));
7
+ return;
8
+ }
9
+ if (Array.isArray(value)) {
10
+ if (value.length === 0) {
11
+ logger.log("No session messages.");
12
+ return;
13
+ }
14
+ for (const item of value) logger.log(JSON.stringify(item));
15
+ return;
16
+ }
17
+ logger.log(JSON.stringify(value, null, 2));
18
+ }
19
+
20
+ function numericOption(value) {
21
+ if (value == null) return null;
22
+ const number = Number(value);
23
+ return Number.isSafeInteger(number) && number > 0 ? number : value;
24
+ }
25
+
26
+ function fabric() {
27
+ return new SessionMessageFabric();
28
+ }
29
+
30
+ export function registerSessionMessageCommands(session) {
31
+ const message = session
32
+ .command("message")
33
+ .description(
34
+ "Durable cross-session inbox, addressing, policy and receipts",
35
+ );
36
+
37
+ message
38
+ .command("register <session-id>")
39
+ .description("Register or reconnect a uniquely named session endpoint")
40
+ .option("--name <name>", "Unique @name (defaults to session id)")
41
+ .option("--machine <id>", "Stable machine identity", "local")
42
+ .option(
43
+ "--policy <policy>",
44
+ "Inbound policy: accept | hold | refuse",
45
+ "accept",
46
+ )
47
+ .option("--busy", "Register as busy instead of idle")
48
+ .option("--json", "Output as JSON")
49
+ .action((sessionId, options) => {
50
+ output(
51
+ fabric().register({
52
+ sessionId,
53
+ name: options.name || sessionId,
54
+ machineId: options.machine,
55
+ policy: options.policy,
56
+ idle: options.busy !== true,
57
+ }),
58
+ options,
59
+ );
60
+ });
61
+
62
+ message
63
+ .command("unregister <endpoint>")
64
+ .description("Retire an endpoint epoch without leaking its inbox to reuse")
65
+ .option("--json", "Output as JSON")
66
+ .action((endpoint, options) => {
67
+ output(
68
+ { unregistered: fabric().unregister(endpoint), endpoint },
69
+ options,
70
+ );
71
+ });
72
+
73
+ message
74
+ .command("send <from> <to> <body...>")
75
+ .description("Send one bounded, idempotent message and print its receipt")
76
+ .option("--subject <text>", "Optional bounded subject")
77
+ .option("--message-id <id>", "Stable idempotency key")
78
+ .option("--sequence <n>", "Per sender/recipient ordering sequence")
79
+ .option("--ttl-ms <n>", "Message expiry in milliseconds")
80
+ .option(
81
+ "--notify-when-idle",
82
+ "Emit one receipt update when target becomes idle",
83
+ )
84
+ .option("--json", "Output as JSON")
85
+ .action((from, to, body, options) => {
86
+ output(
87
+ fabric().send({
88
+ from,
89
+ to,
90
+ body: body.join(" "),
91
+ subject: options.subject,
92
+ messageId: options.messageId,
93
+ sequence: numericOption(options.sequence),
94
+ ttlMs: numericOption(options.ttlMs),
95
+ notifyWhenIdle: options.notifyWhenIdle === true,
96
+ }),
97
+ options,
98
+ );
99
+ });
100
+
101
+ message
102
+ .command("inbox <endpoint>")
103
+ .description("Read an endpoint inbox in sender sequence order")
104
+ .option("--ack", "Acknowledge and release returned live message bodies")
105
+ .option("--json", "Output as JSON")
106
+ .action((endpoint, options) => {
107
+ output(fabric().inbox(endpoint, { acknowledge: options.ack }), options);
108
+ });
109
+
110
+ message
111
+ .command("receipts <endpoint>")
112
+ .description("List durable delivered/refused/full/expired receipts")
113
+ .option("--json", "Output as JSON")
114
+ .action((endpoint, options) => {
115
+ output(fabric().receipts(endpoint), options);
116
+ });
117
+
118
+ message
119
+ .command("policy <endpoint> <policy>")
120
+ .description("Set accept, hold or refuse with optional revision CAS")
121
+ .option("--expected-revision <n>", "Reject a stale rendered policy action")
122
+ .option("--json", "Output as JSON")
123
+ .action((endpoint, policy, options) => {
124
+ output(
125
+ fabric().setPolicy(endpoint, policy, {
126
+ expectedRevision: numericOption(options.expectedRevision),
127
+ }),
128
+ options,
129
+ );
130
+ });
131
+
132
+ message
133
+ .command("idle <endpoint> <state>")
134
+ .description("Set idle/busy and deliver notify_when_idle exactly once")
135
+ .option("--expected-revision <n>", "Reject a stale rendered idle action")
136
+ .option("--json", "Output as JSON")
137
+ .action((endpoint, state, options) => {
138
+ const normalized = String(state).trim().toLowerCase();
139
+ if (!["idle", "busy", "true", "false"].includes(normalized)) {
140
+ const error = new Error("state must be idle, busy, true or false");
141
+ error.code = "SESSION_MESSAGE_INVALID_ARGUMENT";
142
+ throw error;
143
+ }
144
+ output(
145
+ fabric().setIdle(endpoint, ["idle", "true"].includes(normalized), {
146
+ expectedRevision: numericOption(options.expectedRevision),
147
+ }),
148
+ options,
149
+ );
150
+ });
151
+
152
+ for (const action of ["disconnect", "reconnect"]) {
153
+ message
154
+ .command(`${action} <endpoint>`)
155
+ .description(
156
+ action === "disconnect"
157
+ ? "Mark transport offline while retaining its durable inbox"
158
+ : "Mark a durable endpoint transport online again",
159
+ )
160
+ .option("--json", "Output as JSON")
161
+ .action((endpoint, options) => {
162
+ output({ endpoint, [action]: fabric()[action](endpoint) }, options);
163
+ });
164
+ }
165
+
166
+ message
167
+ .command("status")
168
+ .description("Emit the bounded CLI-owned message projection")
169
+ .option("--json", "Output as JSON")
170
+ .action((options) => output(fabric().projection(), options));
171
+ }
@@ -68,6 +68,7 @@ import { registerSessionShowSubcommand } from "./session-show.js";
68
68
  import { registerSessionObservabilitySubcommand } from "./session-observability.js";
69
69
  import { registerSessionLocationSubcommands } from "./session-location.js";
70
70
  import { registerSessionBudgetCommands } from "./session-budget.js";
71
+ import { registerSessionMessageCommands } from "./session-message.js";
71
72
 
72
73
  export const _deps = {
73
74
  execFileSync: (...args) => executionBroker.execFileSync(...args),
@@ -226,6 +227,7 @@ export function registerSessionCommand(program) {
226
227
  registerSessionMcpRecoveryCommands(session);
227
228
  registerSessionLocationSubcommands(session);
228
229
  registerSessionBudgetCommands(session);
230
+ registerSessionMessageCommands(session);
229
231
 
230
232
  // session list
231
233
  session
@@ -321,6 +323,7 @@ export function registerSessionCommand(program) {
321
323
  let team = [];
322
324
  let workflow = [];
323
325
  let dynamicWorkflow = [];
326
+ let sessionMessageFabric = null;
324
327
  let artifacts = [];
325
328
  let prLinks = {};
326
329
 
@@ -462,6 +465,13 @@ export function registerSessionCommand(program) {
462
465
  } catch {
463
466
  // Summaries are optional; absence never removes a session.
464
467
  }
468
+ try {
469
+ const { SessionMessageFabric } =
470
+ await import("../lib/session-message-fabric.js");
471
+ sessionMessageFabric = new SessionMessageFabric().projection();
472
+ } catch (error) {
473
+ sourceErrors.sessionMessageFabric = error?.message || String(error);
474
+ }
465
475
 
466
476
  const { buildSessionProjection } =
467
477
  await import("../lib/session-projection.js");
@@ -474,6 +484,7 @@ export function registerSessionCommand(program) {
474
484
  dynamicWorkflow,
475
485
  artifacts,
476
486
  prLinks,
487
+ sessionMessageFabric,
477
488
  sourceErrors,
478
489
  });
479
490
  if (options.json) {
@@ -1,16 +1,29 @@
1
1
  {
2
2
  "source": "CHANGELOG.md",
3
3
  "releases": [
4
+ {
5
+ "productVersion": null,
6
+ "cliVersion": "0.165.6",
7
+ "cliVersions": ["0.165.5", "0.165.6"],
8
+ "date": "2026-08-21",
9
+ "title": "",
10
+ "sections": [
11
+ {
12
+ "heading": "Added - cc CLI 0.165.6: safer remote sessions and governed plugin sources",
13
+ "body": "> `chainlesschain` **0.165.5 -> 0.165.6** (candidate; not yet published,\n> 2026-08-21).\n> CLI-only release; `@chainlesschain/personal-data-hub` remains **0.4.59**\n> and `@chainlesschain/agent-sdk` remains **0.1.7**.\n\n- **Remote Control safe defaults**: direct sessions bind to loopback by\n default. Trusted-LAN exposure requires explicit command authority, preserves\n the selected bind address, fails closed on relay/direct fallback, and keeps\n IDE pairing surfaces from presenting loopback endpoints as phone-ready QR\n links.\n- **Governed plugin source identity**: managed Marketplace policy now evaluates\n canonical Git, registry, file, alias, ref, path, host, port, principal, and\n registry-resolution identities before target I/O or process spawn. Clone\n execution is isolated from ambient Git/SSH configuration, and cache\n provenance remains bound to the approved source and registry document.\n- **Digest-pinned Marketplace archives**: registry install and update can\n materialize same-origin, catalog-bound `tgz`/`tar.gz` sources only after\n candidate preflight. SHA-256 verification and bounded extraction reject\n traversal, links, special entries, case collisions, and expansion bombs;\n archive and extracted-payload digest authority are revalidated for offline\n cache replay. Dynamic command and headers-helper sources remain disabled by\n product policy.\n- **Cross-platform policy enforcement**: Windows runner temporary roots are\n canonicalized without probing caller-controlled targets. Other existing 8.3\n spellings resolve to their long-path authority, while unresolved or forged\n aliases remain rejected. Linux, Windows, and macOS Marketplace campaigns\n exercise air-gapped cache, proxy, PAC, and private-registry boundaries.\n- **Durable session and MCP lifecycle**: cross-session messaging, bounded\n long-running live state, MCP reconnect subscription recovery, lifecycle\n compatibility, and structured headless observability now share explicit\n identity, cleanup, and backpressure contracts.\n- **Focused terminal ergonomics**: the `concise` output style and persisted\n `classic`/`readline` keybinding flavor extend interactive use without\n changing existing default behavior.\n- **Release status**: publication requires the final candidate commit to pass\n the complete Linux, Windows, and macOS `CLI CI` and `CLI Strict Sandbox`\n matrices. The immutable npm tag, provenance, and registry readback will be\n recorded only after those exact-SHA gates succeed."
14
+ }
15
+ ]
16
+ },
4
17
  {
5
18
  "productVersion": null,
6
19
  "cliVersion": "0.165.5",
7
20
  "cliVersions": ["0.165.4", "0.165.5"],
8
- "date": "2026-08-20",
21
+ "date": "2026-08-21",
9
22
  "title": "",
10
23
  "sections": [
11
24
  {
12
25
  "heading": "Added - cc CLI 0.165.5: governed recovery and native add-on policy",
13
- "body": "> `chainlesschain` **0.165.4 -> 0.165.5** (candidate; not yet published,\n> 2026-08-20).\n> CLI-only release; `@chainlesschain/personal-data-hub` remains **0.4.59**\n> and `@chainlesschain/agent-sdk` remains **0.1.7**.\n\n- **Recoverable Artifact workflows**: inspect and settle orphaned Artifact\n writes through a bounded CLI-owned recovery projection, retain exact\n access/deletion evidence, and surface workbench state without letting IDE\n clients derive storage mutations.\n- **Durable workflow recovery**: plan and resume interrupted workflow stages\n through persisted authority and checkpoint evidence, with tighter session\n and execution-location projections for CLI, WebSocket, and IDE consumers.\n- **Native MCP add-on policy**: admit only supported native loader shapes and\n validated executable/package identities. Unsupported or ambiguous native\n add-ons now return typed fail-closed rejections instead of crossing the\n strict process boundary.\n- **Release-candidate gates**: add cross-host Marketplace, accessibility,\n execution-location, context/permission, and live-provider campaigns while\n keeping CI dependency installation clean and deterministic.\n- **Release status**: publication requires this exact candidate commit to pass\n the complete Linux, Windows, and macOS `CLI CI` and `CLI Strict Sandbox`\n matrices. The immutable npm tag and registry readback will be recorded only\n after those gates succeed."
26
+ "body": "> `chainlesschain` **0.165.4 -> 0.165.5** (published from exact SHA\n> `11aef634aa3ab88994698eca0ce2f5cfa65faf48`, 2026-08-21).\n> CLI-only release; `@chainlesschain/personal-data-hub` remains **0.4.59**\n> and `@chainlesschain/agent-sdk` remains **0.1.7**.\n\n- **Recoverable Artifact workflows**: inspect and settle orphaned Artifact\n writes through a bounded CLI-owned recovery projection, retain exact\n access/deletion evidence, and surface workbench state without letting IDE\n clients derive storage mutations.\n- **Durable workflow recovery**: plan and resume interrupted workflow stages\n through persisted authority and checkpoint evidence, with tighter session\n and execution-location projections for CLI, WebSocket, and IDE consumers.\n- **Native MCP add-on policy**: admit only supported native loader shapes and\n validated executable/package identities. Unsupported or ambiguous native\n add-ons now return typed fail-closed rejections instead of crossing the\n strict process boundary.\n- **Release gates**: add cross-host Marketplace, accessibility,\n execution-location, context/permission, and live-provider campaigns while\n keeping CI dependency installation clean and deterministic.\n- **Published release evidence**: immutable tag `v-npm-0-165-5`, three-OS\n `CLI CI` (`32413582642`), `CLI Strict Sandbox` (`32413582490`), npm release\n (`32428314006`), and independent registry/provenance readback\n (`32429710720`) all bind exact SHA `11aef634aa`. npm reports\n `latest=0.165.5`; the public tarball SHA-1 is\n `c2cc7fb18ea572749d747de02355c6a0a5aef357`."
14
27
  }
15
28
  ]
16
29
  },
@@ -45,6 +45,11 @@ import {
45
45
  prepareMcpStdioExecutableIdentity,
46
46
  } from "../lib/mcp-stdio-executable-identity.js";
47
47
  import { resolveMcpStdioSandboxContext } from "../lib/mcp-stdio-workspace-authority.js";
48
+ import {
49
+ closeMcpTlsDispatcher,
50
+ createMcpTlsDispatcher,
51
+ loadMcpTlsMaterial,
52
+ } from "../lib/mcp-tls.js";
48
53
 
49
54
  /**
50
55
  * Injectable dependencies — overridable from tests.
@@ -63,6 +68,9 @@ export const _deps = {
63
68
  verifyMcpStdioApprovedWorkingDirectory,
64
69
  prepareMcpStdioExecutableIdentity,
65
70
  resolveMcpStdioSandboxContext,
71
+ loadMcpTlsMaterial,
72
+ createMcpTlsDispatcher,
73
+ closeMcpTlsDispatcher,
66
74
  // Backoff sleep seam (tests override with a no-op so retries don't wait).
67
75
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
68
76
  };
@@ -230,6 +238,24 @@ const MCP_HTTP_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
230
238
  */
231
239
  const STDIO_REQUEST_TIMEOUT_MS = 30000;
232
240
  const MCP_PROTOCOL_VERSION = "2025-11-25";
241
+ export const SUPPORTED_MCP_PROTOCOL_VERSIONS = Object.freeze([
242
+ "2024-11-05",
243
+ "2025-03-26",
244
+ "2025-06-18",
245
+ MCP_PROTOCOL_VERSION,
246
+ ]);
247
+ const supportedMcpProtocolVersions = new Set(SUPPORTED_MCP_PROTOCOL_VERSIONS);
248
+
249
+ export function assertSupportedMcpProtocolVersion(value) {
250
+ if (typeof value !== "string" || !supportedMcpProtocolVersions.has(value)) {
251
+ const error = new Error(
252
+ "MCP server selected an unsupported protocol version",
253
+ );
254
+ error.code = "CC_MCP_PROTOCOL_VERSION_UNSUPPORTED";
255
+ throw error;
256
+ }
257
+ return value;
258
+ }
233
259
  const URL_ELICITATION_REQUIRED = -32042;
234
260
  const MCP_RPC_ERROR_CODE = "CC_MCP_RPC_ERROR";
235
261
  const mcpRpcControlData = new WeakMap();
@@ -1287,6 +1313,10 @@ export class MCPClient extends EventEmitter {
1287
1313
  this._nextId = 1;
1288
1314
  this._reconnectors = new Map(); // name → async () => config|null
1289
1315
  this._reconnecting = new Map(); // name → in-flight reconnect promise
1316
+ // Desired resource subscriptions outlive a single transport entry. Keeping
1317
+ // them on the client lets a later reconnect retry restoration even when a
1318
+ // previous partial reconnect was torn down fail-closed.
1319
+ this._resourceSubscriptions = new Map(); // name → Set<uri>
1290
1320
  this._sessionId =
1291
1321
  options && options.sessionId != null ? String(options.sessionId) : null;
1292
1322
  this._workspaceBinding =
@@ -2082,6 +2112,17 @@ export class MCPClient extends EventEmitter {
2082
2112
  };
2083
2113
  }
2084
2114
  }
2115
+ let tlsMaterial = null;
2116
+ if (sourceConfig.tls != null) {
2117
+ if (transportKind !== "https" && transportKind !== "wss") {
2118
+ throw mcpTransportError(
2119
+ "CC_MCP_TLS_TRANSPORT_REQUIRED",
2120
+ `MCP TLS configuration requires https or wss (server "${name}")`,
2121
+ { transport: transportKind, url: sourceConfig.url },
2122
+ );
2123
+ }
2124
+ tlsMaterial = _deps.loadMcpTlsMaterial(sourceConfig.tls);
2125
+ }
2085
2126
  const connectionHeaders = await this._connectionHeaders(
2086
2127
  name,
2087
2128
  sourceConfig,
@@ -2104,6 +2145,8 @@ export class MCPClient extends EventEmitter {
2104
2145
  _httpRequestControllers: new Set(),
2105
2146
  _httpDiscardControllers: new Set(),
2106
2147
  _httpDiscardStopping: false,
2148
+ _httpDispatcher: null,
2149
+ _tlsIdentityDigest: tlsMaterial?.identityDigest || null,
2107
2150
  _disconnectPromise: null,
2108
2151
  _webSocketPayloadError: null,
2109
2152
  _webSocketInboundError: null,
@@ -2157,6 +2200,9 @@ export class MCPClient extends EventEmitter {
2157
2200
  }
2158
2201
  entry.httpUrl = config.url;
2159
2202
  entry.httpHeaders = connectionHeaders;
2203
+ entry._httpDispatcher = tlsMaterial
2204
+ ? _deps.createMcpTlsDispatcher(tlsMaterial)
2205
+ : null;
2160
2206
  } else if (isWebSocketTransport(transportKind)) {
2161
2207
  if (!config.url) {
2162
2208
  throw mcpTransportError(
@@ -2203,6 +2249,7 @@ export class MCPClient extends EventEmitter {
2203
2249
  ...(transportKind === "wss" && config.rejectUnauthorized === false
2204
2250
  ? { rejectUnauthorized: false }
2205
2251
  : {}),
2252
+ ...(tlsMaterial?.connectOptions || {}),
2206
2253
  });
2207
2254
  entry.socket = socket;
2208
2255
 
@@ -2680,8 +2727,8 @@ export class MCPClient extends EventEmitter {
2680
2727
  clientInfo: { name: "chainlesschain-cli", version: "0.37.9" },
2681
2728
  });
2682
2729
 
2683
- entry.protocolVersion = String(
2684
- initResult?.protocolVersion || this._protocolVersion,
2730
+ entry.protocolVersion = assertSupportedMcpProtocolVersion(
2731
+ initResult?.protocolVersion,
2685
2732
  );
2686
2733
  // Send initialized notification
2687
2734
  this._sendNotification(name, "notifications/initialized", {});
@@ -2831,6 +2878,12 @@ export class MCPClient extends EventEmitter {
2831
2878
  // teardown is best-effort
2832
2879
  }
2833
2880
  }
2881
+ try {
2882
+ await _deps.closeMcpTlsDispatcher(entry._httpDispatcher);
2883
+ } catch {
2884
+ // Preserve the connection failure; dispatcher cleanup is best-effort.
2885
+ }
2886
+ entry._httpDispatcher = null;
2834
2887
  this._releaseToolInventory(entry);
2835
2888
  this.servers.delete(name);
2836
2889
  if (
@@ -2850,7 +2903,10 @@ export class MCPClient extends EventEmitter {
2850
2903
  /**
2851
2904
  * Disconnect from an MCP server.
2852
2905
  */
2853
- disconnect(name) {
2906
+ disconnect(name, options = {}) {
2907
+ if (options.preserveResourceSubscriptions !== true) {
2908
+ this._resourceSubscriptions.delete(name);
2909
+ }
2854
2910
  const entry = this.servers.get(name);
2855
2911
  if (!entry) {
2856
2912
  this._cancelServerElicitations(name);
@@ -2906,6 +2962,12 @@ export class MCPClient extends EventEmitter {
2906
2962
  }
2907
2963
  return true;
2908
2964
  } finally {
2965
+ try {
2966
+ await _deps.closeMcpTlsDispatcher(entry._httpDispatcher);
2967
+ } catch {
2968
+ // Transport teardown remains best-effort.
2969
+ }
2970
+ entry._httpDispatcher = null;
2909
2971
  entry.state = ServerState.DISCONNECTED;
2910
2972
  this._releaseToolInventory(entry);
2911
2973
  // A future connection may reuse the same name. Never let a delayed
@@ -2928,8 +2990,15 @@ export class MCPClient extends EventEmitter {
2928
2990
  */
2929
2991
  async disconnectAll() {
2930
2992
  const names = [...this.servers.keys()];
2931
- for (const name of names) {
2932
- await this.disconnect(name);
2993
+ try {
2994
+ for (const name of names) {
2995
+ await this.disconnect(name);
2996
+ }
2997
+ } finally {
2998
+ // A failed reconnect may have already removed its transport entry while
2999
+ // retaining desired subscriptions for another retry. disconnectAll()
3000
+ // is the explicit terminal teardown and must clear those tombstones too.
3001
+ this._resourceSubscriptions.clear();
2933
3002
  }
2934
3003
  }
2935
3004
 
@@ -3173,11 +3242,22 @@ export class MCPClient extends EventEmitter {
3173
3242
  try {
3174
3243
  const currentEntry = this.servers.get(name) || null;
3175
3244
  const currentConfig = currentEntry?.config || null;
3245
+ // Resource subscriptions belong to the logical client/server
3246
+ // relationship, not to one transport connection. Snapshot them before
3247
+ // disconnect() removes the old entry so a hot reconnect cannot
3248
+ // silently stop delivering resource update notifications.
3249
+ const resourceSubscriptions = new Set([
3250
+ ...(this._resourceSubscriptions.get(name) || []),
3251
+ ...(currentEntry?.resourceSubscriptions || []),
3252
+ ]);
3253
+ if (resourceSubscriptions.size > 0) {
3254
+ this._resourceSubscriptions.set(name, new Set(resourceSubscriptions));
3255
+ }
3176
3256
  const resolver = this._reconnectors.get(name);
3177
3257
  const fresh = resolver ? await resolver() : currentConfig;
3178
3258
  if (!fresh) return false;
3179
3259
  try {
3180
- await this.disconnect(name);
3260
+ await this.disconnect(name, { preserveResourceSubscriptions: true });
3181
3261
  } catch {
3182
3262
  // entry may already be gone — connect() below is what matters
3183
3263
  }
@@ -3195,6 +3275,25 @@ export class MCPClient extends EventEmitter {
3195
3275
  reconnectConfig,
3196
3276
  options.connectAuthRetryUsed === true,
3197
3277
  );
3278
+ try {
3279
+ for (const uri of resourceSubscriptions) {
3280
+ await this.subscribeResource(name, uri);
3281
+ }
3282
+ } catch {
3283
+ // A partially restored connection is unsafe: callers would believe
3284
+ // the reconnect recovered their live subscriptions while some were
3285
+ // already lost. Tear it down and let the original operation surface
3286
+ // its connection error.
3287
+ try {
3288
+ await this.disconnect(name, {
3289
+ preserveResourceSubscriptions: true,
3290
+ });
3291
+ } catch {
3292
+ // The outer reconnect result remains fail-closed even if cleanup
3293
+ // itself reports an error.
3294
+ }
3295
+ return false;
3296
+ }
3198
3297
  this.emit("server-reconnected", { name, url: fresh.url || null });
3199
3298
  return true;
3200
3299
  } catch {
@@ -3269,9 +3368,23 @@ export class MCPClient extends EventEmitter {
3269
3368
  async subscribeResource(serverName, uri) {
3270
3369
  const entry = this.servers.get(serverName);
3271
3370
  if (!entry) throw new Error(`Server "${serverName}" not found`);
3272
- await this._sendRequest(serverName, "resources/subscribe", { uri });
3371
+ if (typeof uri !== "string" || !uri.trim()) {
3372
+ throw new TypeError(
3373
+ "MCP resource subscription URI must be a non-empty string",
3374
+ );
3375
+ }
3376
+ const normalizedUri = uri;
3377
+ await this._sendRequest(serverName, "resources/subscribe", {
3378
+ uri: normalizedUri,
3379
+ });
3273
3380
  entry.resourceSubscriptions ||= new Set();
3274
- entry.resourceSubscriptions.add(String(uri));
3381
+ entry.resourceSubscriptions.add(normalizedUri);
3382
+ let desired = this._resourceSubscriptions.get(serverName);
3383
+ if (!desired) {
3384
+ desired = new Set();
3385
+ this._resourceSubscriptions.set(serverName, desired);
3386
+ }
3387
+ desired.add(normalizedUri);
3275
3388
  return true;
3276
3389
  }
3277
3390
 
@@ -3279,8 +3392,19 @@ export class MCPClient extends EventEmitter {
3279
3392
  async unsubscribeResource(serverName, uri) {
3280
3393
  const entry = this.servers.get(serverName);
3281
3394
  if (!entry) throw new Error(`Server "${serverName}" not found`);
3282
- await this._sendRequest(serverName, "resources/unsubscribe", { uri });
3283
- entry.resourceSubscriptions?.delete(String(uri));
3395
+ if (typeof uri !== "string" || !uri.trim()) {
3396
+ throw new TypeError(
3397
+ "MCP resource subscription URI must be a non-empty string",
3398
+ );
3399
+ }
3400
+ const normalizedUri = uri;
3401
+ await this._sendRequest(serverName, "resources/unsubscribe", {
3402
+ uri: normalizedUri,
3403
+ });
3404
+ entry.resourceSubscriptions?.delete(normalizedUri);
3405
+ const desired = this._resourceSubscriptions.get(serverName);
3406
+ desired?.delete(normalizedUri);
3407
+ if (desired?.size === 0) this._resourceSubscriptions.delete(serverName);
3284
3408
  return true;
3285
3409
  }
3286
3410
 
@@ -3752,6 +3876,9 @@ export class MCPClient extends EventEmitter {
3752
3876
  ...(stream.controller
3753
3877
  ? { signal: stream.controller.signal }
3754
3878
  : {}),
3879
+ ...(entry._httpDispatcher
3880
+ ? { dispatcher: entry._httpDispatcher }
3881
+ : {}),
3755
3882
  }),
3756
3883
  stream.controller?.signal,
3757
3884
  );
@@ -3997,6 +4124,9 @@ export class MCPClient extends EventEmitter {
3997
4124
  headers,
3998
4125
  body,
3999
4126
  ...(controller ? { signal: controller.signal } : {}),
4127
+ ...(entry._httpDispatcher
4128
+ ? { dispatcher: entry._httpDispatcher }
4129
+ : {}),
4000
4130
  });
4001
4131
  },
4002
4132
  );
@@ -4729,6 +4859,7 @@ async function _fetchAndDiscardHttpResponse(entry, options) {
4729
4859
  const response = await _deps.fetch(entry.httpUrl, {
4730
4860
  ...options,
4731
4861
  ...(controller ? { signal: controller.signal } : {}),
4862
+ ...(entry._httpDispatcher ? { dispatcher: entry._httpDispatcher } : {}),
4732
4863
  });
4733
4864
  _cancelHttpResponseBody(response);
4734
4865
  if (timedOut) {