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.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-CEeYTeDB.js → AIOps-vfoDb6HZ.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-Cm5f7p93.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-yRuvgWxa.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BROsC4h1.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-CimHApb1.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DI8kr8zI.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-DSLzKy-n.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-DkH_n2r1.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-CKSrUoRA.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-ClwfMXUS.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-BYTehcQG.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-B8AMcp6J.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-BNSogPo3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-JxPvGb8O.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DF7bFAgE.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-Bvhqnwrq.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-BDS0CJvM.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-DqvRfxoQ.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-CDcIvnqS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-D7jaUxU3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-B5A18wEV.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-_6IVkGag.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9PeiKCG.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-B4IAR9zv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-CxQeSNx1.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BS8SouUX.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-Dq9aYb5Q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-BrCEe9AJ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-DybOpiY2.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Bseca1_I.js → Logs-DBpzbRfx.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-d3kM4DDL.js → Marketplace-Cc7CCSCu.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-DFaIhOfu.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-KCqlp9ol.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-Bys84ySN.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-B-6qU-je.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-BgY-fvCq.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-B8pCmqQJ.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-BbOmYutA.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-BocLQz_M.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CSwbxEop.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-CqFNSYG3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-QrDXVdMJ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-Cxntotjt.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-CIdBOwNq.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-DH7OM8XU.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-CnJqbWce.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-5C3HG1kH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-B4yIp7xK.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BMvt-2M0.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-CvSTEMRG.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-BKBJxEek.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BdwUqFWN.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-DtnCKlTm.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-sTbgwyNr.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-Bzr14gSb.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-C_TF5IaJ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-GCAM4H2K.js} +1 -1
- package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-Dir0r6OQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CpGnlecX.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-DMMamC6u.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-yqay5yG-.js} +4 -4
- package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-C5i2wh9G.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-vUqk35yj.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-DzcSkhot.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-yM5y9yvK.js → Sla-CB6qbYNr.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DPy1XMYh.js → SpeechSettings-BHUI7cox.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CFVhEyyr.js → SyncSettings-D-RrsxTJ.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-DFxdOJiq.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-SKAob_Aq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BPfsfHQ-.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-8iw7FpUf.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-COYhN18e.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-B-JzXcf6.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-BJ5bg0nI.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BG_ByRMf.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-B3kOnSiU.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-CcoGFnK7.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-DdT8vRC_.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-CZu3M9N8.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-DxJt3Qba.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DdYfwJaB.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-BG3SUzwj.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-By51z2-1.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-B0apZ3KJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-3jWkIu5j.js} +1 -1
- package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-B3rWuPMW.js} +1 -1
- package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-B5rLiEo0.js} +1 -1
- package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-BBPf3TpM.js} +1 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
- package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-BIrJfqO4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BLIbGOLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-BP94CrBV.js} +1 -1
- package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-BWF8_mkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-BYINoRLK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BZ_0qm4g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-BewKYrX9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-BfWYsB9t.js} +1 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
- package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-Bissefox.js} +3 -3
- package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-BkvcEo4p.js} +1 -1
- package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-Bos5_aMV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-C1C9i79A.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-CAhHirVH.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-CNIxXMmN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CW93tZDv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-CWEuZWCK.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-Cp6e-hGw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-CpjZhgNd.js} +1 -1
- package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-CzmQElZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-DCn8IcxV.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-DE76D-kA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-DaIz0NAW.js} +1 -1
- package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-Da_WBdLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-Dz49VOyQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-Q2SCX-MI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-RD6O-0C6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-SQqOlR3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-ZFbps9po.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-ZxtP8jG7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-bP_VlDb4.js} +1 -1
- package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-kf57LJ93.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-oxRrmFO9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-p1_8SbI1.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-BWJktcg8.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-BEXBzUFV.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-BUKwn2sx.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-FTI7btqQ.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CsLtSPH3.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-DWZxsT_S.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-Bcfs43CX.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-a9bcC1p-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-BDTzgOXN.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-Iq1j_HUg.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-DAZEYxeh.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-wXTpNTHt.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-hjEoG85v.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B5g7TN7r.js → zoom-BHVpyKg_.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agent.js +14 -2
- package/src/commands/background-session.js +100 -10
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +145 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +11 -0
- package/src/data/changelog.json +15 -2
- package/src/harness/mcp-client.js +141 -10
- package/src/lib/background-session-transport.js +153 -27
- package/src/lib/mcp-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-oauth.js +55 -6
- package/src/lib/mcp-tls.js +230 -0
- package/src/lib/output-styles.js +12 -0
- package/src/lib/plugin-runtime/install.js +614 -71
- package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- package/src/lib/plugin-runtime/remote-source.js +435 -55
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- package/src/lib/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- package/src/lib/session-message-fabric.js +1105 -0
- package/src/lib/session-projection.js +45 -2
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/settings-loader.cjs +29 -0
- package/src/repl/agent-repl.js +51 -14
- package/src/repl/permission-prompt.js +31 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +51 -2
- package/src/runtime/headless-runner.js +47 -66
- package/src/runtime/headless-stream.js +18 -24
- package/src/runtime/mcp-config.js +2 -0
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
- package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
- 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
|
+
}
|
package/src/commands/session.js
CHANGED
|
@@ -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) {
|
package/src/data/changelog.json
CHANGED
|
@@ -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-
|
|
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** (
|
|
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 =
|
|
2684
|
-
initResult?.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
|
-
|
|
2932
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
3283
|
-
|
|
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) {
|