chainlesschain 0.162.179 → 0.162.181
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 +6 -3
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BF13YdB-.js → AIOps-cq0dwbnf.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-B9y67AjF.js → ActionButton-CvcgGl3w.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-FcdDHoSC.js → Analytics-hM2-jFqg.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CRm_zwSp.js → AppLayout-B9_Gsd_g.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Cbo7-usf.js → Artifacts-B7bu52Og.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-vMaLpkHC.js → Audit-DThfDgqB.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-FiUY692t.js → BackgroundAgents-DjlFiD4v.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BK80j_GQ.js → Backup-CK9xJKnG.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-ChNMuYR1.js → BaseInput-DEiSU8K8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-928UREpm.js → Chat-CpT96czj.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BKPPNmZI.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-dFW-zDbe.js → Checkbox-C1GfDnO0.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BCcNJUj2.js → Codegen-3DAKgiLx.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DcEkLv70.js → Col-74cOwZU1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DihBRRqg.js → Community-tn9Kkz9j.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DGYxwWET.js → Compact-BcoXH7m4.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-OnYe1vS4.js → Compliance-DIPGUBgi.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CmWB54c2.js → Cowork-doD5_91c.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BrANb2uw.js → Cron-C-IDvubz.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-2HgWzSxg.js → Crosschain-CdcCoBdj.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DdDwNI6K.js → DID-DdH5aMMA.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-D6PnE78-.js → Dashboard-DHLA4mm0.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-ClzvSN4O.js → Dropdown-dEeCt7Md.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BUk9B727.js → EmailListRenderer-BMaecFv9.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C8hi3v7i.js → FamilyGuardDashboard-DUaUukTN.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B5SP8xkg.js → Federation-CMBE0_rp.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWzJ8AU5.js → FormItemContext-PG-JDbXq.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-3E3Q3rjG.js → GenericCardRenderer-Bcb0p88m.js} +1 -1
- package/src/assets/web-panel/assets/{Git-z8IdCMId.js → Git-Bgv15myB.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CgqCF9lX.js → Governance-DB8YTArI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-zgS-AuH9.js → Inference-BQMwCWaS.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BdbgmESJ.js → KnowledgeGraph-B1_ZziiB.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BHpknnjy.js → Logs-DovEEnt_.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-D201OL4T.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Di9sAR7j.js → Marketplace-BlCXSpyB.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DtnR9Z_A.js → McpTools-CsCR09Wj.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BaX9uuXD.js → Memory-CBVRLKno.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-xelyKWTY.js → MobileBridge-C9OIafgA.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BiFTOGBN.js → MobileProjects-D_zoJ8Xs.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-bV3OzTiO.js → Mtc-vE-HdvLj.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BkwvFTdZ.js → MtcAudit-CPLaekCd.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-_6HQQ-oC.js → Multisig-C_UP9C2o.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BBYhmWQZ.js → NLProgramming-2nODJcU1.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cq-6dQnU.js → Notes-BhE-SJGu.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-ILZ9zkX6.js → NotificationSettings-D21q7BNU.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DgmTBEvk.js → OrderTableRenderer-0b62GYOD.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CkLnWwCk.js → Organization-CEqmw_sb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CWVHZbhf.js → Overflow-MW-gMDO_.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BXbqETkr.js → P2P-CY0hjI_G.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser--0JR-0ex.js → PdhVaultBrowser-BbWh7Man.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Cz9_tfSy.js → Permissions-ChqEA_4Q.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dq7OWYtx.js → PersonalDataHub-B3YrbYfD.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-DHPUNcc-.js → Pipeline-B8WBhhlW.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CEAbrDkS.js → Privacy-Cmb0PXNx.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CJYfclXE.js → ProjectInit-hF6zW3Nz.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-oCTgPCgp.js → ProjectSettings-BFSVfpW0.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DAWkVFkc.js +1 -0
- package/src/assets/web-panel/assets/{Providers-BLTZtzTG.js → Providers-B8yux8Dx.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C0b0TQB3.js → QrScannerModal-N76cQluO.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-tOl6N_gM.js → QuickAsk-ClvZ0KYR.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DPzKr8yr.js → Recommend-B-GfgBpT.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-mlglCdQi.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-Cji1Vl79.js → Reputation-DJIZzQeG.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D-NERWf2.js → Row-COzxu_bb.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DzlgmAlB.js → RssFeed-CoYsJQuE.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Cs0SWSa4.js → Search-DC77Mq9S.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DL7EzcUO.js → Security-Loy2EdMO.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CT-PzgTd.js → Services-GUD07Eu0.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-tByJWdJg.js → Skeleton-DWzkptYU.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Dnr_6TTl.js → Skills-CEDn1r-Z.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-4O-yq0LY.js → Sla-ub_k4pXh.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BYBVg1oR.js → SpeechSettings-D7HLX9CA.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CpOYPbE5.js → SyncSettings-DbwOvl6m.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CGVhOVh6.js → Tasks-CBQzoAu5.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BuHFxR4X.js → Templates-BNVeIhud.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DxDxHzgf.js → Tenant-PS9AzhXU.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B9g_nISg.js → Terminal-Cgb4eLB_.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DlrBC_4A.js → TimelineRenderer-CrmXMOkg.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DvBlYZ6i.js → Tokens-DD-VDHw1.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dg8G9bhF.js → Trigger-BsoN_n-n.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-COICx4E-.js → Trust-n8LmN4ZV.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4pOBv7T.js → UkeySign-C9_7MImA.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Qt4NMA8Q.js → VideoEditing-DyBteNgn.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-X1EkQc2Z.js → Wallet-BS5ixeXI.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CyeEFBE5.js → WebAuthn-spc4UImU.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-nwzbMiVQ.js → WorkflowEditor-D7iVvaBO.js} +1 -1
- package/src/assets/web-panel/assets/chat-6CmBkHrL.js +1 -0
- package/src/assets/web-panel/assets/{colors-5Isz5lhJ.js → colors-BGoCLkxI.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DsFQS3Oy.js → compact-item-fYZBetMO.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-OcEXp2uz.js → createContext-BeM-1UVj.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BzPwHnw8.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CZvC4p09.js → hasIn-CE7ZuEnV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF1sab0Q.js → index--lap6cf7.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7yeL3rf.js → index-ATcSr_2O.js} +1 -1
- package/src/assets/web-panel/assets/{index-VkFXA9N-.js → index-B2U-ObMq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSmGGg5h.js → index-B3CDgvNW.js} +1 -1
- package/src/assets/web-panel/assets/{index-jrNIrpq4.js → index-B4wiTEOM.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8ibl7bc.js → index-B8BsdNV_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOLozRk8.js → index-BEVm0lCS.js} +10 -10
- package/src/assets/web-panel/assets/{index-BPgc_bdR.js → index-BNZDILcO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTZZzlMO.js → index-BU_2qiDk.js} +1 -1
- package/src/assets/web-panel/assets/{index-Vubi6Tf5.js → index-BV551878.js} +1 -1
- package/src/assets/web-panel/assets/{index-nXrnztXp.js → index-BfPL-m46.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN8lPVpQ.js → index-BowH1eLQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgE2BZl2.js → index-BsMfoaeo.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd5b2JHB.js → index-BunaQCa0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQlGw0nQ.js → index-C0v6YKKO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Do6FJK32.js → index-C4SJVjs4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqvo1f9T.js → index-C9Y3k0HI.js} +1 -1
- package/src/assets/web-panel/assets/{index-LUdgPZg9.js → index-CIpVrf5P.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpD2O7_p.js → index-CRLjEP1H.js} +1 -1
- package/src/assets/web-panel/assets/{index-r_9NG6P7.js → index-CRnUE8Cb.js} +1 -1
- package/src/assets/web-panel/assets/{index-FIgMAfBh.js → index-Cip8emOQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-RsQ1lFDV.js → index-CpIGIB85.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGJupV0S.js → index-D1r9tT3J.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpglwPWl.js → index-DT3zPSVX.js} +1 -1
- package/src/assets/web-panel/assets/index-DacrAdc-.js +1 -0
- package/src/assets/web-panel/assets/{index-CG3B2wCF.js → index-De0quFDw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxA2PDK1.js → index-DoXk5KJ8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaGKOVvQ.js → index-DsvfrESp.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7CtJYXq.js → index-Dw1PyYPm.js} +1 -1
- package/src/assets/web-panel/assets/index-EHzNJozo.js +1 -0
- package/src/assets/web-panel/assets/{index-Bu9NXvIa.js → index-MtOuq2i6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsZkTaa3.js → index-Puq6Luf1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPE4zHXF.js → index-QIpnpZgz.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bsb-iylv.js → index-RMZ8a7Yt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMtGRQqw.js → index-WUrJ6S1j.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3lWvhk4.js → index-YjcumOZ4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmVbCGx_.js → index-lJBjB1QX.js} +1 -1
- package/src/assets/web-panel/assets/{index-A5SHQW_M.js → index-ukg99D1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFJgt6qM.js → index-zb7rVOhV.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cdgep9YT.js → initDefaultProps-CYHMs1ag.js} +1 -1
- package/src/assets/web-panel/assets/{motion-7yn1ACEm.js → motion-DDB2Ffk5.js} +1 -1
- package/src/assets/web-panel/assets/{move-D8o133Xc.js → move-Bg3cD-dX.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CClHIG9V.js → mtc-parser-B5nedjTS.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CLnrPE2d.js → omit-BHyuUILu.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-1CTWoZWl.js → pickAttrs-C9YQnEN9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-xNNtS4Sp.js → placementArrow-DBUFYUe7.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-ypzAofs7.js → responsiveObserve-nBs2Obnk.js} +1 -1
- package/src/assets/web-panel/assets/{slide-QsrY9UwD.js → slide-DkCEaXNU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DSdThzOB.js → statusUtils-aqE1Wi6U.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B_kKM8Ob.js → styleChecker-CN5bhfHB.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CcdTBNV9.js → useFlexGapSupport-B_oX_kvJ.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BuAH2XFU.js → useFs-rCuXHeme.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-B3zWiG4A.js → usePersonalDataHub-DZR9Abyd.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B45Rc-CQ.js → vnode-ConCYQlX.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DaYZDU5f.js → zoom-CUdDxG5M.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +4 -1
- package/src/commands/background-session.js +2 -1
- package/src/commands/context.js +69 -2
- package/src/commands/hub.js +30 -24
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/background-agent-protocol.js +38 -0
- package/src/gateways/ws/message-dispatcher.js +1 -0
- package/src/gateways/ws/remote-session-protocol.js +65 -6
- package/src/gateways/ws/ws-server.js +8 -1
- package/src/harness/feature-flags.js +15 -8
- package/src/harness/mcp-client.js +618 -43
- package/src/lazy-dispatch.js +30 -1
- package/src/lib/agent-authority.js +22 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +161 -0
- package/src/lib/agent-team/team-runner.js +22 -0
- package/src/lib/artifact-store.js +35 -0
- package/src/lib/async-hook-supervisor.cjs +13 -2
- package/src/lib/audit-mtc.js +3 -2
- package/src/lib/background-agent-supervisor.js +104 -3
- package/src/lib/background-interaction-journal.js +407 -0
- package/src/lib/background-interaction-resolver.js +23 -34
- package/src/lib/background-session-transport.js +12 -2
- package/src/lib/capability-manifest.js +41 -3
- package/src/lib/capability-negotiation.js +1 -1
- package/src/lib/channels/channel-manager.js +33 -0
- package/src/lib/channels/telegram-channel.js +23 -5
- package/src/lib/channels/webhook-channel.js +38 -15
- package/src/lib/chrome-connector.js +169 -23
- package/src/lib/config-manager.js +23 -9
- package/src/lib/context-breakdown.js +31 -0
- package/src/lib/cowork-cron.js +274 -32
- package/src/lib/cross-chain-mtc.js +3 -2
- package/src/lib/durable-security-store.js +124 -0
- package/src/lib/event-runtime-host.js +67 -6
- package/src/lib/event-runtime-store.js +163 -0
- package/src/lib/goal-store.js +32 -12
- package/src/lib/headless-manifest.js +2 -1
- package/src/lib/hook-environment.cjs +58 -0
- package/src/lib/hook-manager.js +12 -5
- package/src/lib/hook-runner.cjs +122 -28
- package/src/lib/hook-runner.js +1 -0
- package/src/lib/hooks-v2-producers.js +36 -0
- package/src/lib/hooks-v2-runtime.js +329 -52
- package/src/lib/interaction-adapter.js +60 -19
- package/src/lib/interaction-binding.js +64 -0
- package/src/lib/json-schema-output.js +439 -14
- package/src/lib/json-schema-validate.js +514 -883
- package/src/lib/lan-pairing-tokens.js +115 -57
- package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
- package/src/lib/lsp/lsp-client.js +4 -0
- package/src/lib/lsp/lsp-manager.js +3 -0
- package/src/lib/lsp/lsp-server-registry.js +18 -0
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-oauth.js +35 -21
- package/src/lib/packer/pkg-config-generator.js +1 -0
- package/src/lib/permission-decision.js +99 -0
- package/src/lib/plugin-monitor-supervisor.js +3 -0
- package/src/lib/plugin-runtime/capabilities.js +50 -0
- package/src/lib/plugin-runtime/capability-consent.js +42 -35
- package/src/lib/plugin-runtime/hooks.js +36 -9
- package/src/lib/plugin-runtime/install.js +23 -7
- package/src/lib/plugin-runtime/lsp.js +4 -0
- package/src/lib/plugin-runtime/manifest.js +50 -2
- package/src/lib/plugin-runtime/mcp.js +30 -5
- package/src/lib/plugin-runtime/monitors.js +11 -0
- package/src/lib/plugin-runtime/plugin-options.js +94 -38
- package/src/lib/plugin-runtime/policy.js +29 -2
- package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
- package/src/lib/plugin-runtime/scopes.js +7 -3
- package/src/lib/plugin-runtime/trust.js +36 -31
- package/src/lib/process-execution-broker/credential-agent.js +114 -16
- package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
- package/src/lib/process-execution-broker/credential-transport.js +378 -0
- package/src/lib/process-execution-broker/index.js +408 -19
- package/src/lib/process-execution-broker/platform-sandbox.js +561 -21
- package/src/lib/process-execution-broker/windows-sandbox.ps1 +999 -0
- package/src/lib/project-mcp-trust.js +21 -26
- package/src/lib/protocol-replay.js +19 -12
- package/src/lib/settings-hooks.cjs +13 -0
- package/src/lib/skill-loader.js +83 -10
- package/src/lib/sync-credentials.js +5 -4
- package/src/lib/turn-binding.js +8 -6
- package/src/repl/agent-repl.js +113 -28
- package/src/repl/repl-turn-binding.js +5 -2
- package/src/runtime/agent-core.js +331 -26
- package/src/runtime/coding-agent-contract-shared.cjs +3 -3
- package/src/runtime/headless-runner.js +101 -4
- package/src/runtime/headless-stream.js +209 -23
- package/src/runtime/mcp-config.js +29 -17
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/workers/background-agent-worker.js +128 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DvySUzxJ.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-DkxCweb1.js +0 -1
- package/src/assets/web-panel/assets/Projects-ClomCrN0.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-pRgaWsUK.js +0 -4
- package/src/assets/web-panel/assets/chat-Bvl-ojg2.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CSVgo71Z.js +0 -1
- package/src/assets/web-panel/assets/index-DMIWh9fu.js +0 -1
- package/src/assets/web-panel/assets/index-ssTvV1l6.js +0 -1
|
@@ -56,6 +56,16 @@ const HTTP_REQUEST_TIMEOUT_MS = 30000;
|
|
|
56
56
|
* hard 30s regardless of its config — an inconsistency with HTTP servers.
|
|
57
57
|
*/
|
|
58
58
|
const STDIO_REQUEST_TIMEOUT_MS = 30000;
|
|
59
|
+
const MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
60
|
+
const URL_ELICITATION_REQUIRED = -32042;
|
|
61
|
+
|
|
62
|
+
function supportsUrlElicitationVersion(version) {
|
|
63
|
+
const normalized = String(version || "");
|
|
64
|
+
return (
|
|
65
|
+
/^\d{4}-\d{2}-\d{2}$/.test(normalized) &&
|
|
66
|
+
normalized >= MCP_PROTOCOL_VERSION
|
|
67
|
+
);
|
|
68
|
+
}
|
|
59
69
|
|
|
60
70
|
/**
|
|
61
71
|
* Infer the transport kind for a server config. Falls back to "stdio".
|
|
@@ -85,6 +95,75 @@ export function inferTransport(config) {
|
|
|
85
95
|
return "stdio";
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
function mcpRpcError(payload = {}) {
|
|
99
|
+
const error = new Error(payload.message || "Unknown MCP error");
|
|
100
|
+
if (payload.code !== undefined) error.code = payload.code;
|
|
101
|
+
if (payload.data !== undefined) error.data = payload.data;
|
|
102
|
+
return error;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function normalizeMcpElicitationRequest(
|
|
106
|
+
serverName,
|
|
107
|
+
requestId,
|
|
108
|
+
params = {},
|
|
109
|
+
) {
|
|
110
|
+
const mode = params.mode == null ? "form" : String(params.mode);
|
|
111
|
+
const message = String(params.message || "").trim();
|
|
112
|
+
if (!message) {
|
|
113
|
+
const error = new Error("MCP elicitation requires a non-empty message");
|
|
114
|
+
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
const base = {
|
|
118
|
+
...params,
|
|
119
|
+
mode,
|
|
120
|
+
message,
|
|
121
|
+
server: String(serverName),
|
|
122
|
+
requestId,
|
|
123
|
+
};
|
|
124
|
+
if (mode === "form") return base;
|
|
125
|
+
if (mode !== "url") {
|
|
126
|
+
const error = new Error(`Unsupported MCP elicitation mode: ${mode}`);
|
|
127
|
+
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const elicitationId = String(params.elicitationId || "").trim();
|
|
132
|
+
if (!elicitationId) {
|
|
133
|
+
const error = new Error(
|
|
134
|
+
"URL mode MCP elicitation requires elicitationId",
|
|
135
|
+
);
|
|
136
|
+
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
let target;
|
|
140
|
+
try {
|
|
141
|
+
target = new URL(String(params.url || ""));
|
|
142
|
+
} catch {
|
|
143
|
+
const error = new Error("URL mode MCP elicitation requires a valid URL");
|
|
144
|
+
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
if (
|
|
148
|
+
target.protocol !== "https:" ||
|
|
149
|
+
target.username ||
|
|
150
|
+
target.password ||
|
|
151
|
+
!target.hostname
|
|
152
|
+
) {
|
|
153
|
+
const error = new Error(
|
|
154
|
+
"URL mode MCP elicitation requires credential-free HTTPS",
|
|
155
|
+
);
|
|
156
|
+
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
...base,
|
|
161
|
+
elicitationId,
|
|
162
|
+
url: target.href,
|
|
163
|
+
urlHost: target.host,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
88
167
|
/** True for transports that talk over HTTP(S) — i.e. use fetch. */
|
|
89
168
|
export function isHttpTransport(transportKind) {
|
|
90
169
|
return (
|
|
@@ -182,6 +261,14 @@ export class MCPClient extends EventEmitter {
|
|
|
182
261
|
? Number(options.elicitationTimeoutMs)
|
|
183
262
|
: 180000;
|
|
184
263
|
this._pendingElicitations = new Map();
|
|
264
|
+
this._urlElicitations = new Map();
|
|
265
|
+
this._activeElicitations = 0;
|
|
266
|
+
this._maxConcurrentElicitations =
|
|
267
|
+
Number(options.maxConcurrentElicitations) > 0
|
|
268
|
+
? Math.min(128, Number(options.maxConcurrentElicitations))
|
|
269
|
+
: 32;
|
|
270
|
+
this._protocolVersion =
|
|
271
|
+
String(options.protocolVersion || MCP_PROTOCOL_VERSION);
|
|
185
272
|
this._runtimeProducer = options.eventRuntimeStore
|
|
186
273
|
? new EventRuntimeProducer({ store: options.eventRuntimeStore, emitter: this })
|
|
187
274
|
: null;
|
|
@@ -201,6 +288,11 @@ export class MCPClient extends EventEmitter {
|
|
|
201
288
|
if (Number(options.timeoutMs) > 0) {
|
|
202
289
|
this._elicitationTimeoutMs = Number(options.timeoutMs);
|
|
203
290
|
}
|
|
291
|
+
if (typeof handler === "function") {
|
|
292
|
+
for (const name of this.servers.keys()) {
|
|
293
|
+
this._ensureHttpMessageStream(name);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
204
296
|
}
|
|
205
297
|
|
|
206
298
|
/** Run an async agent turn with a session-scoped elicitation route. */
|
|
@@ -223,11 +315,15 @@ export class MCPClient extends EventEmitter {
|
|
|
223
315
|
const key = this._elicitationKey(serverName, requestId);
|
|
224
316
|
const pending = this._pendingElicitations.get(key);
|
|
225
317
|
if (!pending) return false;
|
|
226
|
-
|
|
318
|
+
const normalized = this._normalizeElicitationResponse(
|
|
319
|
+
response,
|
|
320
|
+
pending.request,
|
|
321
|
+
);
|
|
322
|
+
pending.resolve(normalized);
|
|
227
323
|
try {
|
|
228
324
|
this._runtimeProducer?.store.acknowledgeInbox(
|
|
229
325
|
`mcp-elicitation:${this._elicitationKey(serverName, requestId)}`,
|
|
230
|
-
{ response:
|
|
326
|
+
{ response: normalized },
|
|
231
327
|
);
|
|
232
328
|
} catch {}
|
|
233
329
|
return true;
|
|
@@ -244,27 +340,82 @@ export class MCPClient extends EventEmitter {
|
|
|
244
340
|
return `${String(serverName)}:${String(requestId)}`;
|
|
245
341
|
}
|
|
246
342
|
|
|
247
|
-
_normalizeElicitationResponse(response) {
|
|
343
|
+
_normalizeElicitationResponse(response, request = null) {
|
|
248
344
|
const action = String(response?.action || "decline").toLowerCase();
|
|
249
345
|
const safeAction = new Set(["accept", "decline", "cancel"]).has(action)
|
|
250
346
|
? action
|
|
251
347
|
: "decline";
|
|
252
348
|
const normalized = { action: safeAction };
|
|
253
|
-
if (
|
|
349
|
+
if (
|
|
350
|
+
safeAction === "accept" &&
|
|
351
|
+
request?.mode !== "url" &&
|
|
352
|
+
response?.content !== undefined
|
|
353
|
+
) {
|
|
254
354
|
normalized.content = response.content;
|
|
255
355
|
}
|
|
256
356
|
return normalized;
|
|
257
357
|
}
|
|
258
358
|
|
|
259
359
|
async _resolveElicitation(serverName, requestId, params = {}) {
|
|
360
|
+
if (this._activeElicitations >= this._maxConcurrentElicitations) {
|
|
361
|
+
const request = normalizeMcpElicitationRequest(
|
|
362
|
+
serverName,
|
|
363
|
+
requestId,
|
|
364
|
+
params,
|
|
365
|
+
);
|
|
366
|
+
this.emit("elicitation-deferred", {
|
|
367
|
+
...request,
|
|
368
|
+
reason: "capacity_exceeded",
|
|
369
|
+
wireAction: "decline",
|
|
370
|
+
});
|
|
371
|
+
return { action: "decline" };
|
|
372
|
+
}
|
|
373
|
+
this._activeElicitations += 1;
|
|
374
|
+
try {
|
|
375
|
+
return await this._resolveElicitationWithin(
|
|
376
|
+
serverName,
|
|
377
|
+
requestId,
|
|
378
|
+
params,
|
|
379
|
+
);
|
|
380
|
+
} finally {
|
|
381
|
+
this._activeElicitations -= 1;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async _resolveElicitationWithin(serverName, requestId, params = {}) {
|
|
260
386
|
const key = this._elicitationKey(serverName, requestId);
|
|
387
|
+
const request = normalizeMcpElicitationRequest(
|
|
388
|
+
serverName,
|
|
389
|
+
requestId,
|
|
390
|
+
params,
|
|
391
|
+
);
|
|
392
|
+
const negotiatedVersion =
|
|
393
|
+
this.servers.get(String(serverName))?.protocolVersion;
|
|
394
|
+
if (
|
|
395
|
+
request.mode === "url" &&
|
|
396
|
+
negotiatedVersion &&
|
|
397
|
+
!supportsUrlElicitationVersion(negotiatedVersion)
|
|
398
|
+
) {
|
|
399
|
+
const error = new Error(
|
|
400
|
+
`URL elicitation is unavailable under MCP ${negotiatedVersion}`,
|
|
401
|
+
);
|
|
402
|
+
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
if (request.mode === "url") this._rememberUrlElicitation(request);
|
|
261
406
|
try {
|
|
262
407
|
this._runtimeProducer?.publish(
|
|
263
|
-
{
|
|
408
|
+
{
|
|
409
|
+
type: "mcp_elicitation",
|
|
410
|
+
server: serverName,
|
|
411
|
+
requestId,
|
|
412
|
+
params: request,
|
|
413
|
+
},
|
|
264
414
|
{ origin: "mcp", id: `mcp-elicitation:${key}` },
|
|
265
415
|
);
|
|
266
416
|
} catch (error) {
|
|
267
417
|
this.emit("elicitation-error", { server: serverName, requestId, error });
|
|
418
|
+
this._settleUrlElicitation(request, { action: "cancel" });
|
|
268
419
|
return { action: "cancel" };
|
|
269
420
|
}
|
|
270
421
|
const contextSessionId = this._elicitationContext.getStore();
|
|
@@ -274,12 +425,17 @@ export class MCPClient extends EventEmitter {
|
|
|
274
425
|
if (handler) {
|
|
275
426
|
try {
|
|
276
427
|
const direct = await handler({
|
|
277
|
-
|
|
278
|
-
requestId,
|
|
279
|
-
...params,
|
|
428
|
+
...request,
|
|
280
429
|
});
|
|
281
|
-
if (
|
|
282
|
-
|
|
430
|
+
if (
|
|
431
|
+
direct !== undefined &&
|
|
432
|
+
String(direct?.action || "").toLowerCase() !== "defer"
|
|
433
|
+
) {
|
|
434
|
+
const response = this._normalizeElicitationResponse(
|
|
435
|
+
direct,
|
|
436
|
+
request,
|
|
437
|
+
);
|
|
438
|
+
this._settleUrlElicitation(request, response);
|
|
283
439
|
try {
|
|
284
440
|
this._runtimeProducer?.store.acknowledgeInbox(
|
|
285
441
|
`mcp-elicitation:${key}`,
|
|
@@ -294,6 +450,7 @@ export class MCPClient extends EventEmitter {
|
|
|
294
450
|
requestId,
|
|
295
451
|
error,
|
|
296
452
|
});
|
|
453
|
+
this._settleUrlElicitation(request, { action: "cancel" });
|
|
297
454
|
return { action: "cancel" };
|
|
298
455
|
}
|
|
299
456
|
}
|
|
@@ -308,12 +465,19 @@ export class MCPClient extends EventEmitter {
|
|
|
308
465
|
{ response: { action: "decline" } },
|
|
309
466
|
);
|
|
310
467
|
} catch {}
|
|
468
|
+
this._settleUrlElicitation(request, { action: "decline" });
|
|
469
|
+
this.emit("elicitation-deferred", {
|
|
470
|
+
...request,
|
|
471
|
+
reason: "no_interactive_host",
|
|
472
|
+
wireAction: "decline",
|
|
473
|
+
});
|
|
311
474
|
return { action: "decline" };
|
|
312
475
|
}
|
|
313
476
|
return new Promise((resolve) => {
|
|
314
477
|
const timeout = setTimeout(() => {
|
|
315
478
|
this._pendingElicitations.delete(key);
|
|
316
479
|
this.emit("elicitation-timeout", { server: serverName, requestId });
|
|
480
|
+
this._settleUrlElicitation(request, { action: "cancel" });
|
|
317
481
|
try {
|
|
318
482
|
this._runtimeProducer?.store.fail(
|
|
319
483
|
"inbox",
|
|
@@ -326,22 +490,128 @@ export class MCPClient extends EventEmitter {
|
|
|
326
490
|
}, this._elicitationTimeoutMs);
|
|
327
491
|
timeout.unref?.();
|
|
328
492
|
this._pendingElicitations.set(key, {
|
|
493
|
+
request,
|
|
329
494
|
resolve: (response) => {
|
|
330
495
|
clearTimeout(timeout);
|
|
331
496
|
this._pendingElicitations.delete(key);
|
|
332
|
-
|
|
497
|
+
const normalized = this._normalizeElicitationResponse(
|
|
498
|
+
response,
|
|
499
|
+
request,
|
|
500
|
+
);
|
|
501
|
+
this._settleUrlElicitation(request, normalized);
|
|
502
|
+
resolve(normalized);
|
|
333
503
|
},
|
|
334
504
|
});
|
|
335
505
|
this.emit("elicitation-request", {
|
|
336
|
-
|
|
337
|
-
requestId,
|
|
338
|
-
...params,
|
|
506
|
+
...request,
|
|
339
507
|
respond: (response) => this.respondElicitation(serverName, requestId, response),
|
|
340
508
|
cancel: () => this.cancelElicitation(serverName, requestId),
|
|
341
509
|
});
|
|
342
510
|
});
|
|
343
511
|
}
|
|
344
512
|
|
|
513
|
+
_rememberUrlElicitation(request) {
|
|
514
|
+
const id = request?.elicitationId;
|
|
515
|
+
if (!id) return;
|
|
516
|
+
const existing = this._urlElicitations.get(id);
|
|
517
|
+
if (existing && existing.server !== request.server) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
`MCP URL elicitation id collision across servers: ${id}`,
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
if (existing && existing.request?.url !== request.url) {
|
|
523
|
+
throw new Error(
|
|
524
|
+
`MCP URL elicitation id reused with a different URL: ${id}`,
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
if (!existing) {
|
|
528
|
+
this._urlElicitations.set(id, {
|
|
529
|
+
id,
|
|
530
|
+
server: request.server,
|
|
531
|
+
request,
|
|
532
|
+
status: "pending",
|
|
533
|
+
completionNotified: false,
|
|
534
|
+
waiters: new Set(),
|
|
535
|
+
});
|
|
536
|
+
while (this._urlElicitations.size > 1000) {
|
|
537
|
+
this._urlElicitations.delete(this._urlElicitations.keys().next().value);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
_settleUrlElicitation(request, response) {
|
|
543
|
+
if (request?.mode !== "url" || !request.elicitationId) return;
|
|
544
|
+
const entry = this._urlElicitations.get(request.elicitationId);
|
|
545
|
+
if (!entry || entry.status === "completed") return;
|
|
546
|
+
entry.status =
|
|
547
|
+
response?.action === "accept" ? "accepted" : response?.action || "decline";
|
|
548
|
+
if (entry.status === "accepted" && entry.completionNotified) {
|
|
549
|
+
this._completeUrlElicitation(entry);
|
|
550
|
+
}
|
|
551
|
+
if (entry.status !== "accepted") {
|
|
552
|
+
for (const waiter of entry.waiters) waiter(false);
|
|
553
|
+
entry.waiters.clear();
|
|
554
|
+
}
|
|
555
|
+
this.emit("elicitation-url-response", {
|
|
556
|
+
server: request.server,
|
|
557
|
+
elicitationId: request.elicitationId,
|
|
558
|
+
action: response?.action || "decline",
|
|
559
|
+
url: request.url,
|
|
560
|
+
urlHost: request.urlHost,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
_handleElicitationComplete(serverName, params = {}) {
|
|
565
|
+
const elicitationId = String(params.elicitationId || "");
|
|
566
|
+
const entry = this._urlElicitations.get(elicitationId);
|
|
567
|
+
if (!entry || entry.server !== String(serverName)) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
if (entry.status === "completed") return false;
|
|
571
|
+
if (entry.status === "pending") {
|
|
572
|
+
// A fast out-of-band flow can finish while the host is still returning
|
|
573
|
+
// its explicit consent result. Remember the notification, but do not
|
|
574
|
+
// mark it complete until that consent has resolved to `accept`.
|
|
575
|
+
entry.completionNotified = true;
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
if (entry.status !== "accepted") return false;
|
|
579
|
+
this._completeUrlElicitation(entry);
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
_completeUrlElicitation(entry) {
|
|
584
|
+
if (!entry || entry.status === "completed") return;
|
|
585
|
+
entry.status = "completed";
|
|
586
|
+
for (const waiter of entry.waiters) waiter(true);
|
|
587
|
+
entry.waiters.clear();
|
|
588
|
+
this.emit("elicitation-complete", {
|
|
589
|
+
server: entry.server,
|
|
590
|
+
elicitationId: entry.id,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
waitForElicitationCompletion(elicitationId, timeoutMs) {
|
|
595
|
+
const entry = this._urlElicitations.get(String(elicitationId));
|
|
596
|
+
if (!entry) return Promise.resolve(false);
|
|
597
|
+
if (entry.status === "completed") return Promise.resolve(true);
|
|
598
|
+
if (entry.status !== "accepted") return Promise.resolve(false);
|
|
599
|
+
const waitMs =
|
|
600
|
+
Number(timeoutMs) > 0 ? Number(timeoutMs) : this._elicitationTimeoutMs;
|
|
601
|
+
return new Promise((resolve) => {
|
|
602
|
+
let settled = false;
|
|
603
|
+
const finish = (value) => {
|
|
604
|
+
if (settled) return;
|
|
605
|
+
settled = true;
|
|
606
|
+
clearTimeout(timer);
|
|
607
|
+
entry.waiters.delete(finish);
|
|
608
|
+
resolve(value);
|
|
609
|
+
};
|
|
610
|
+
const timer = setTimeout(() => finish(false), waitMs);
|
|
611
|
+
entry.waiters.add(finish);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
345
615
|
/**
|
|
346
616
|
* Set (or clear) the agent session id advertised to stdio MCP servers. Only
|
|
347
617
|
* servers connected *after* this call see the new value; already-spawned
|
|
@@ -457,6 +727,8 @@ export class MCPClient extends EventEmitter {
|
|
|
457
727
|
httpUrl: null,
|
|
458
728
|
httpHeaders: {},
|
|
459
729
|
httpSessionId: null,
|
|
730
|
+
protocolVersion: null,
|
|
731
|
+
_httpMessageStream: null,
|
|
460
732
|
tools: [],
|
|
461
733
|
resources: [],
|
|
462
734
|
prompts: [],
|
|
@@ -501,6 +773,9 @@ export class MCPClient extends EventEmitter {
|
|
|
501
773
|
pluginId: config.pluginId,
|
|
502
774
|
pluginVersion: config.pluginVersion,
|
|
503
775
|
pluginSource: config.pluginSource,
|
|
776
|
+
...(config.sandboxPolicy
|
|
777
|
+
? { sandboxPolicy: config.sandboxPolicy }
|
|
778
|
+
: {}),
|
|
504
779
|
});
|
|
505
780
|
|
|
506
781
|
entry.process = proc;
|
|
@@ -574,11 +849,14 @@ export class MCPClient extends EventEmitter {
|
|
|
574
849
|
|
|
575
850
|
// Initialize MCP protocol (retried on transient network errors).
|
|
576
851
|
const initResult = await this._requestWithRetry(name, "initialize", {
|
|
577
|
-
protocolVersion:
|
|
852
|
+
protocolVersion: this._protocolVersion,
|
|
578
853
|
capabilities: {
|
|
579
854
|
tools: {},
|
|
580
855
|
resources: {},
|
|
581
856
|
prompts: {},
|
|
857
|
+
elicitation: supportsUrlElicitationVersion(this._protocolVersion)
|
|
858
|
+
? { form: {}, url: {} }
|
|
859
|
+
: { form: {} },
|
|
582
860
|
// Client-side roots capability: servers may request roots/list and
|
|
583
861
|
// subscribe to list_changed notifications (Claude-Code 2.1.203).
|
|
584
862
|
roots: { listChanged: true },
|
|
@@ -586,6 +864,9 @@ export class MCPClient extends EventEmitter {
|
|
|
586
864
|
clientInfo: { name: "chainlesschain-cli", version: "0.37.9" },
|
|
587
865
|
});
|
|
588
866
|
|
|
867
|
+
entry.protocolVersion = String(
|
|
868
|
+
initResult?.protocolVersion || this._protocolVersion,
|
|
869
|
+
);
|
|
589
870
|
// Send initialized notification
|
|
590
871
|
this._sendNotification(name, "notifications/initialized", {});
|
|
591
872
|
|
|
@@ -649,6 +930,12 @@ export class MCPClient extends EventEmitter {
|
|
|
649
930
|
tools: entry.tools.length,
|
|
650
931
|
toolsError: entry.toolsError,
|
|
651
932
|
});
|
|
933
|
+
if (
|
|
934
|
+
entry.httpUrl &&
|
|
935
|
+
(this._elicitationHandler || this._elicitationHandlers.size > 0)
|
|
936
|
+
) {
|
|
937
|
+
this._ensureHttpMessageStream(name);
|
|
938
|
+
}
|
|
652
939
|
return {
|
|
653
940
|
name,
|
|
654
941
|
state: entry.state,
|
|
@@ -700,12 +987,23 @@ export class MCPClient extends EventEmitter {
|
|
|
700
987
|
if (entry.process) {
|
|
701
988
|
entry.process.kill();
|
|
702
989
|
}
|
|
990
|
+
if (entry._httpMessageStream) {
|
|
991
|
+
entry._httpMessageStream.stopped = true;
|
|
992
|
+
entry._httpMessageStream.controller?.abort();
|
|
993
|
+
entry._httpMessageStream.wake?.();
|
|
994
|
+
entry._httpMessageStream = null;
|
|
995
|
+
}
|
|
703
996
|
// HTTP sessions: best-effort DELETE to free server-side state.
|
|
704
997
|
if (entry.httpUrl && entry.httpSessionId) {
|
|
705
998
|
try {
|
|
706
999
|
await _deps.fetch(entry.httpUrl, {
|
|
707
1000
|
method: "DELETE",
|
|
708
|
-
headers: {
|
|
1001
|
+
headers: {
|
|
1002
|
+
...(entry.httpHeaders || {}),
|
|
1003
|
+
"Mcp-Session-Id": entry.httpSessionId,
|
|
1004
|
+
"MCP-Protocol-Version":
|
|
1005
|
+
entry.protocolVersion || this._protocolVersion,
|
|
1006
|
+
},
|
|
709
1007
|
});
|
|
710
1008
|
} catch (_e) {
|
|
711
1009
|
// ignore — disconnect is best-effort
|
|
@@ -778,6 +1076,13 @@ export class MCPClient extends EventEmitter {
|
|
|
778
1076
|
try {
|
|
779
1077
|
return await this._callToolOnce(serverName, toolName, args);
|
|
780
1078
|
} catch (err) {
|
|
1079
|
+
if (
|
|
1080
|
+
err?.code === URL_ELICITATION_REQUIRED &&
|
|
1081
|
+
(await this._resolveRequiredUrlElicitations(serverName, err))
|
|
1082
|
+
) {
|
|
1083
|
+
// Retry exactly once after every out-of-band flow reports completion.
|
|
1084
|
+
return this._callToolOnce(serverName, toolName, args);
|
|
1085
|
+
}
|
|
781
1086
|
if (
|
|
782
1087
|
!this._reconnectors.has(serverName) ||
|
|
783
1088
|
!isLikelyConnectionError(err)
|
|
@@ -790,6 +1095,49 @@ export class MCPClient extends EventEmitter {
|
|
|
790
1095
|
}
|
|
791
1096
|
}
|
|
792
1097
|
|
|
1098
|
+
async _resolveRequiredUrlElicitations(serverName, error) {
|
|
1099
|
+
const requested = Array.isArray(error?.data?.elicitations)
|
|
1100
|
+
? error.data.elicitations
|
|
1101
|
+
: [];
|
|
1102
|
+
if (requested.length === 0 || requested.length > 16) return false;
|
|
1103
|
+
const normalized = [];
|
|
1104
|
+
const ids = new Set();
|
|
1105
|
+
for (const params of requested) {
|
|
1106
|
+
let request;
|
|
1107
|
+
try {
|
|
1108
|
+
request = normalizeMcpElicitationRequest(
|
|
1109
|
+
serverName,
|
|
1110
|
+
`url-required:${params?.elicitationId || normalized.length}`,
|
|
1111
|
+
params,
|
|
1112
|
+
);
|
|
1113
|
+
} catch {
|
|
1114
|
+
return false;
|
|
1115
|
+
}
|
|
1116
|
+
if (request.mode !== "url" || ids.has(request.elicitationId)) return false;
|
|
1117
|
+
ids.add(request.elicitationId);
|
|
1118
|
+
normalized.push(request);
|
|
1119
|
+
}
|
|
1120
|
+
const accepted = [];
|
|
1121
|
+
for (const request of normalized) {
|
|
1122
|
+
const response = await this._resolveElicitation(
|
|
1123
|
+
serverName,
|
|
1124
|
+
request.requestId,
|
|
1125
|
+
request,
|
|
1126
|
+
);
|
|
1127
|
+
if (response.action !== "accept") return false;
|
|
1128
|
+
accepted.push(request);
|
|
1129
|
+
}
|
|
1130
|
+
const completed = await Promise.all(
|
|
1131
|
+
accepted.map((request) =>
|
|
1132
|
+
this.waitForElicitationCompletion(
|
|
1133
|
+
request.elicitationId,
|
|
1134
|
+
this._elicitationTimeoutMs,
|
|
1135
|
+
),
|
|
1136
|
+
),
|
|
1137
|
+
);
|
|
1138
|
+
return completed.every(Boolean);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
793
1141
|
async _callToolOnce(serverName, toolName, args) {
|
|
794
1142
|
const entry = this.servers.get(serverName);
|
|
795
1143
|
if (!entry) throw new Error(`Server "${serverName}" not found`);
|
|
@@ -1019,6 +1367,113 @@ export class MCPClient extends EventEmitter {
|
|
|
1019
1367
|
}
|
|
1020
1368
|
}
|
|
1021
1369
|
|
|
1370
|
+
/**
|
|
1371
|
+
* Open the optional Streamable HTTP GET/SSE channel used for asynchronous
|
|
1372
|
+
* server requests and notifications. Interactive hosts enable it when they
|
|
1373
|
+
* install an elicitation handler; stdio already has a bidirectional stream.
|
|
1374
|
+
*/
|
|
1375
|
+
_ensureHttpMessageStream(serverName) {
|
|
1376
|
+
const entry = this.servers.get(serverName);
|
|
1377
|
+
if (
|
|
1378
|
+
!entry?.httpUrl ||
|
|
1379
|
+
entry.state !== ServerState.CONNECTED ||
|
|
1380
|
+
entry._httpMessageStream
|
|
1381
|
+
) {
|
|
1382
|
+
return false;
|
|
1383
|
+
}
|
|
1384
|
+
const stream = {
|
|
1385
|
+
stopped: false,
|
|
1386
|
+
controller: null,
|
|
1387
|
+
lastEventId: null,
|
|
1388
|
+
retryMs: 1000,
|
|
1389
|
+
wake: null,
|
|
1390
|
+
promise: null,
|
|
1391
|
+
};
|
|
1392
|
+
entry._httpMessageStream = stream;
|
|
1393
|
+
stream.promise = (async () => {
|
|
1394
|
+
try {
|
|
1395
|
+
while (
|
|
1396
|
+
!stream.stopped &&
|
|
1397
|
+
this.servers.get(serverName) === entry
|
|
1398
|
+
) {
|
|
1399
|
+
stream.controller =
|
|
1400
|
+
typeof AbortController === "function"
|
|
1401
|
+
? new AbortController()
|
|
1402
|
+
: null;
|
|
1403
|
+
const headers = {
|
|
1404
|
+
Accept: "text/event-stream",
|
|
1405
|
+
...(entry.httpHeaders || {}),
|
|
1406
|
+
...(entry.httpSessionId
|
|
1407
|
+
? { "Mcp-Session-Id": entry.httpSessionId }
|
|
1408
|
+
: {}),
|
|
1409
|
+
"MCP-Protocol-Version":
|
|
1410
|
+
entry.protocolVersion || this._protocolVersion,
|
|
1411
|
+
...(stream.lastEventId
|
|
1412
|
+
? { "Last-Event-ID": stream.lastEventId }
|
|
1413
|
+
: {}),
|
|
1414
|
+
};
|
|
1415
|
+
try {
|
|
1416
|
+
const response = await _deps.fetch(entry.httpUrl, {
|
|
1417
|
+
method: "GET",
|
|
1418
|
+
headers,
|
|
1419
|
+
...(stream.controller
|
|
1420
|
+
? { signal: stream.controller.signal }
|
|
1421
|
+
: {}),
|
|
1422
|
+
});
|
|
1423
|
+
if (response.status === 405) return;
|
|
1424
|
+
if (!response.ok) {
|
|
1425
|
+
throw new Error(
|
|
1426
|
+
`MCP HTTP message stream returned ${response.status}`,
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
const contentType = response.headers?.get
|
|
1430
|
+
? String(
|
|
1431
|
+
response.headers.get("content-type") || "",
|
|
1432
|
+
).toLowerCase()
|
|
1433
|
+
: "";
|
|
1434
|
+
if (!contentType.includes("text/event-stream")) return;
|
|
1435
|
+
const cap = Number.isFinite(entry.config?.maxBufferChars)
|
|
1436
|
+
? entry.config.maxBufferChars
|
|
1437
|
+
: MCP_MAX_BUFFER_CHARS;
|
|
1438
|
+
await _consumeSseMessageStream(response, {
|
|
1439
|
+
cap,
|
|
1440
|
+
stream,
|
|
1441
|
+
onMessage: (message) =>
|
|
1442
|
+
this._handleMessage(serverName, message),
|
|
1443
|
+
});
|
|
1444
|
+
} catch (error) {
|
|
1445
|
+
if (stream.stopped || stream.controller?.signal.aborted) return;
|
|
1446
|
+
this.emit("server-stream-error", {
|
|
1447
|
+
name: serverName,
|
|
1448
|
+
error: error?.message || String(error),
|
|
1449
|
+
});
|
|
1450
|
+
} finally {
|
|
1451
|
+
stream.controller = null;
|
|
1452
|
+
}
|
|
1453
|
+
if (stream.stopped) return;
|
|
1454
|
+
await new Promise((resolve) => {
|
|
1455
|
+
const delay = Math.max(
|
|
1456
|
+
50,
|
|
1457
|
+
Math.min(30000, Number(stream.retryMs) || 1000),
|
|
1458
|
+
);
|
|
1459
|
+
const timer = setTimeout(resolve, delay);
|
|
1460
|
+
stream.wake = () => {
|
|
1461
|
+
clearTimeout(timer);
|
|
1462
|
+
resolve();
|
|
1463
|
+
};
|
|
1464
|
+
});
|
|
1465
|
+
stream.wake = null;
|
|
1466
|
+
}
|
|
1467
|
+
} finally {
|
|
1468
|
+
if (entry._httpMessageStream === stream) {
|
|
1469
|
+
entry._httpMessageStream = null;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
})();
|
|
1473
|
+
stream.promise.catch(() => {});
|
|
1474
|
+
return true;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1022
1477
|
/**
|
|
1023
1478
|
* Send a JSON-RPC request over HTTP (Streamable HTTP per MCP spec).
|
|
1024
1479
|
* Accepts responses as either `application/json` or `text/event-stream`.
|
|
@@ -1040,6 +1495,10 @@ export class MCPClient extends EventEmitter {
|
|
|
1040
1495
|
if (entry.httpSessionId) {
|
|
1041
1496
|
headers["Mcp-Session-Id"] = entry.httpSessionId;
|
|
1042
1497
|
}
|
|
1498
|
+
if (method !== "initialize") {
|
|
1499
|
+
headers["MCP-Protocol-Version"] =
|
|
1500
|
+
entry.protocolVersion || this._protocolVersion;
|
|
1501
|
+
}
|
|
1043
1502
|
|
|
1044
1503
|
// Per-call timeout (parity with the 30s stdio timeout) so a hung or dead
|
|
1045
1504
|
// HTTP MCP server can't block the request forever. Servers flagged
|
|
@@ -1105,7 +1564,12 @@ export class MCPClient extends EventEmitter {
|
|
|
1105
1564
|
|
|
1106
1565
|
let envelope;
|
|
1107
1566
|
if (contentType.includes("text/event-stream")) {
|
|
1108
|
-
envelope = await _extractSseResponse(
|
|
1567
|
+
envelope = await _extractSseResponse(
|
|
1568
|
+
response,
|
|
1569
|
+
id,
|
|
1570
|
+
cap,
|
|
1571
|
+
(message) => this._handleMessage(serverName, message),
|
|
1572
|
+
);
|
|
1109
1573
|
} else {
|
|
1110
1574
|
const text = await _readBodyCapped(response, cap);
|
|
1111
1575
|
envelope = text ? JSON.parse(text) : null;
|
|
@@ -1115,7 +1579,7 @@ export class MCPClient extends EventEmitter {
|
|
|
1115
1579
|
throw new Error("Empty or invalid JSON-RPC response");
|
|
1116
1580
|
}
|
|
1117
1581
|
if (envelope.error) {
|
|
1118
|
-
throw
|
|
1582
|
+
throw mcpRpcError(envelope.error);
|
|
1119
1583
|
}
|
|
1120
1584
|
return envelope.result;
|
|
1121
1585
|
} catch (err) {
|
|
@@ -1143,6 +1607,8 @@ export class MCPClient extends EventEmitter {
|
|
|
1143
1607
|
if (entry.httpSessionId) {
|
|
1144
1608
|
headers["Mcp-Session-Id"] = entry.httpSessionId;
|
|
1145
1609
|
}
|
|
1610
|
+
headers["MCP-Protocol-Version"] =
|
|
1611
|
+
entry.protocolVersion || this._protocolVersion;
|
|
1146
1612
|
try {
|
|
1147
1613
|
const p = _deps.fetch(entry.httpUrl, {
|
|
1148
1614
|
method: "POST",
|
|
@@ -1220,7 +1686,7 @@ export class MCPClient extends EventEmitter {
|
|
|
1220
1686
|
entry._pending.delete(msg.id);
|
|
1221
1687
|
|
|
1222
1688
|
if (msg.error) {
|
|
1223
|
-
reject(
|
|
1689
|
+
reject(mcpRpcError(msg.error));
|
|
1224
1690
|
} else {
|
|
1225
1691
|
resolve(msg.result);
|
|
1226
1692
|
}
|
|
@@ -1248,6 +1714,8 @@ export class MCPClient extends EventEmitter {
|
|
|
1248
1714
|
this._refreshServerList(serverName, "tools");
|
|
1249
1715
|
} else if (msg.method === "notifications/resources/list_changed") {
|
|
1250
1716
|
this._refreshServerList(serverName, "resources");
|
|
1717
|
+
} else if (msg.method === "notifications/elicitation/complete") {
|
|
1718
|
+
this._handleElicitationComplete(serverName, msg.params || {});
|
|
1251
1719
|
}
|
|
1252
1720
|
this.emit("notification", {
|
|
1253
1721
|
server: serverName,
|
|
@@ -1315,7 +1783,12 @@ export class MCPClient extends EventEmitter {
|
|
|
1315
1783
|
if (method === "elicitation/create") {
|
|
1316
1784
|
this._resolveElicitation(serverName, id, msg.params || {})
|
|
1317
1785
|
.then((response) => this._sendResponse(serverName, id, response))
|
|
1318
|
-
.catch(() =>
|
|
1786
|
+
.catch((error) =>
|
|
1787
|
+
this._sendResponse(serverName, id, undefined, {
|
|
1788
|
+
code: -32602,
|
|
1789
|
+
message: error?.message || "invalid elicitation request",
|
|
1790
|
+
}),
|
|
1791
|
+
);
|
|
1319
1792
|
return;
|
|
1320
1793
|
}
|
|
1321
1794
|
this._sendResponse(serverName, id, undefined, {
|
|
@@ -1324,19 +1797,47 @@ export class MCPClient extends EventEmitter {
|
|
|
1324
1797
|
});
|
|
1325
1798
|
}
|
|
1326
1799
|
|
|
1327
|
-
/**
|
|
1328
|
-
* Write a JSON-RPC response back to a stdio server. HTTP transports cannot
|
|
1329
|
-
* deliver server-initiated requests to this client (no persistent SSE GET
|
|
1330
|
-
* stream is held), so there is never a response to send there.
|
|
1331
|
-
*/
|
|
1800
|
+
/** Write a JSON-RPC response back over stdio or Streamable HTTP POST. */
|
|
1332
1801
|
_sendResponse(serverName, id, result, error) {
|
|
1333
1802
|
const entry = this.servers.get(serverName);
|
|
1334
|
-
if (!entry
|
|
1335
|
-
const
|
|
1803
|
+
if (!entry) return;
|
|
1804
|
+
const envelope =
|
|
1336
1805
|
error !== undefined
|
|
1337
1806
|
? { jsonrpc: "2.0", id, error }
|
|
1338
|
-
: { jsonrpc: "2.0", id, result }
|
|
1339
|
-
)
|
|
1807
|
+
: { jsonrpc: "2.0", id, result };
|
|
1808
|
+
if (entry.httpUrl) {
|
|
1809
|
+
const headers = {
|
|
1810
|
+
"Content-Type": "application/json",
|
|
1811
|
+
Accept: "application/json, text/event-stream",
|
|
1812
|
+
...(entry.httpHeaders || {}),
|
|
1813
|
+
...(entry.httpSessionId
|
|
1814
|
+
? { "Mcp-Session-Id": entry.httpSessionId }
|
|
1815
|
+
: {}),
|
|
1816
|
+
"MCP-Protocol-Version":
|
|
1817
|
+
entry.protocolVersion || this._protocolVersion,
|
|
1818
|
+
};
|
|
1819
|
+
try {
|
|
1820
|
+
const pending = _deps.fetch(entry.httpUrl, {
|
|
1821
|
+
method: "POST",
|
|
1822
|
+
headers,
|
|
1823
|
+
body: JSON.stringify(envelope),
|
|
1824
|
+
});
|
|
1825
|
+
pending?.catch?.((cause) =>
|
|
1826
|
+
this.emit("server-stream-error", {
|
|
1827
|
+
name: serverName,
|
|
1828
|
+
error: cause?.message || String(cause),
|
|
1829
|
+
}),
|
|
1830
|
+
);
|
|
1831
|
+
} catch (cause) {
|
|
1832
|
+
this.emit("server-stream-error", {
|
|
1833
|
+
name: serverName,
|
|
1834
|
+
error: cause?.message || String(cause),
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
if (!entry.process) return;
|
|
1840
|
+
const message = JSON.stringify(envelope);
|
|
1340
1841
|
try {
|
|
1341
1842
|
entry.process.stdin.write(message + "\n");
|
|
1342
1843
|
} catch {
|
|
@@ -1408,30 +1909,104 @@ async function _readBodyCapped(response, cap) {
|
|
|
1408
1909
|
* `data:` chunks, comments, and non-JSON-RPC events. `cap` bounds the body
|
|
1409
1910
|
* size (see _readBodyCapped).
|
|
1410
1911
|
*/
|
|
1411
|
-
async function _extractSseResponse(
|
|
1912
|
+
async function _extractSseResponse(
|
|
1913
|
+
response,
|
|
1914
|
+
requestId,
|
|
1915
|
+
cap = 0,
|
|
1916
|
+
onMessage = null,
|
|
1917
|
+
) {
|
|
1412
1918
|
const text = await _readBodyCapped(response, cap);
|
|
1413
1919
|
|
|
1414
1920
|
// Split into events on blank line, parse each event's concatenated `data:` lines.
|
|
1415
1921
|
const events = text.split(/\r?\n\r?\n/);
|
|
1416
1922
|
for (const event of events) {
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1923
|
+
const parsed = _parseSseEvent(event);
|
|
1924
|
+
if (!parsed?.message) continue;
|
|
1925
|
+
const payload = parsed.message;
|
|
1926
|
+
if (payload.jsonrpc === "2.0" && payload.id === requestId) {
|
|
1927
|
+
return payload;
|
|
1928
|
+
}
|
|
1929
|
+
onMessage?.(payload);
|
|
1930
|
+
}
|
|
1931
|
+
throw new Error(`SSE stream ended without a response for id ${requestId}`);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
function _parseSseEvent(event) {
|
|
1935
|
+
const dataLines = [];
|
|
1936
|
+
let id = null;
|
|
1937
|
+
let retry = null;
|
|
1938
|
+
for (const line of String(event || "").split(/\r?\n/)) {
|
|
1939
|
+
if (line.startsWith(":")) continue;
|
|
1940
|
+
if (line.startsWith("data:")) {
|
|
1941
|
+
dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
1942
|
+
} else if (line.startsWith("id:")) {
|
|
1943
|
+
id = line.slice(3).replace(/^ /, "");
|
|
1944
|
+
} else if (line.startsWith("retry:")) {
|
|
1945
|
+
const value = Number(line.slice(6).trim());
|
|
1946
|
+
if (Number.isFinite(value) && value >= 0) retry = value;
|
|
1423
1947
|
}
|
|
1424
|
-
|
|
1948
|
+
}
|
|
1949
|
+
let message = null;
|
|
1950
|
+
if (dataLines.some((line) => line.length > 0)) {
|
|
1425
1951
|
try {
|
|
1426
|
-
const
|
|
1427
|
-
if (
|
|
1428
|
-
|
|
1952
|
+
const candidate = JSON.parse(dataLines.join("\n"));
|
|
1953
|
+
if (candidate && typeof candidate === "object") message = candidate;
|
|
1954
|
+
} catch {
|
|
1955
|
+
// Non-JSON SSE events are transport metadata, not MCP messages.
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
return { id, retry, message };
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
async function _consumeSseMessageStream(
|
|
1962
|
+
response,
|
|
1963
|
+
{ cap = 0, stream, onMessage },
|
|
1964
|
+
) {
|
|
1965
|
+
const dispatch = (rawEvent) => {
|
|
1966
|
+
const parsed = _parseSseEvent(rawEvent);
|
|
1967
|
+
if (parsed.id != null && parsed.id !== "") {
|
|
1968
|
+
stream.lastEventId = parsed.id;
|
|
1969
|
+
}
|
|
1970
|
+
if (parsed.retry != null) stream.retryMs = parsed.retry;
|
|
1971
|
+
if (parsed.message) onMessage(parsed.message);
|
|
1972
|
+
};
|
|
1973
|
+
|
|
1974
|
+
if (!response.body || typeof response.body.getReader !== "function") {
|
|
1975
|
+
const text = await _readBodyCapped(response, cap);
|
|
1976
|
+
for (const event of text.split(/\r?\n\r?\n/)) dispatch(event);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
const reader = response.body.getReader();
|
|
1981
|
+
const decoder = new TextDecoder("utf-8");
|
|
1982
|
+
let buffer = "";
|
|
1983
|
+
try {
|
|
1984
|
+
for (;;) {
|
|
1985
|
+
const { value, done } = await reader.read();
|
|
1986
|
+
if (done) break;
|
|
1987
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1988
|
+
if (cap > 0 && buffer.length > cap) {
|
|
1989
|
+
throw new Error(
|
|
1990
|
+
`MCP HTTP SSE event exceeded the ${cap}-char cap`,
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
let boundary;
|
|
1994
|
+
while ((boundary = buffer.search(/\r?\n\r?\n/)) >= 0) {
|
|
1995
|
+
const separator = buffer.match(/\r?\n\r?\n/)[0];
|
|
1996
|
+
const event = buffer.slice(0, boundary);
|
|
1997
|
+
buffer = buffer.slice(boundary + separator.length);
|
|
1998
|
+
dispatch(event);
|
|
1429
1999
|
}
|
|
1430
|
-
}
|
|
1431
|
-
|
|
2000
|
+
}
|
|
2001
|
+
buffer += decoder.decode();
|
|
2002
|
+
if (buffer.trim()) dispatch(buffer);
|
|
2003
|
+
} finally {
|
|
2004
|
+
try {
|
|
2005
|
+
reader.releaseLock?.();
|
|
2006
|
+
} catch {
|
|
2007
|
+
// Best-effort cleanup; abort/disconnect owns cancellation.
|
|
1432
2008
|
}
|
|
1433
2009
|
}
|
|
1434
|
-
throw new Error(`SSE stream ended without a response for id ${requestId}`);
|
|
1435
2010
|
}
|
|
1436
2011
|
|
|
1437
2012
|
/**
|