@prestyj/cli 5.10.0 → 5.12.0
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/README.md +3 -4
- package/assets/skills/compliance-guard/SKILL.md +251 -0
- package/assets/skills/compliance-guard/references/artifacts.md +186 -0
- package/assets/skills/compliance-guard/references/eu-uk.md +151 -0
- package/assets/skills/compliance-guard/references/exposure-triage.md +161 -0
- package/assets/skills/compliance-guard/references/lawsuit-vectors.md +141 -0
- package/assets/skills/compliance-guard/references/provenance.md +42 -0
- package/assets/skills/compliance-guard/references/sector-gates.md +173 -0
- package/assets/skills/compliance-guard/references/security-baseline.md +130 -0
- package/assets/skills/compliance-guard/references/trigger-map.md +163 -0
- package/assets/skills/compliance-guard/references/us.md +164 -0
- package/dist/app-sidecar-auth-broadcast.test.d.ts +2 -0
- package/dist/app-sidecar-auth-broadcast.test.d.ts.map +1 -0
- package/dist/app-sidecar-auth-broadcast.test.js +348 -0
- package/dist/app-sidecar-auth-broadcast.test.js.map +1 -0
- package/dist/app-sidecar-sessions.d.ts +5 -2
- package/dist/app-sidecar-sessions.d.ts.map +1 -1
- package/dist/app-sidecar-sessions.js +34 -8
- package/dist/app-sidecar-sessions.js.map +1 -1
- package/dist/app-sidecar-sessions.test.js +96 -1
- package/dist/app-sidecar-sessions.test.js.map +1 -1
- package/dist/app-sidecar.js +616 -173
- package/dist/app-sidecar.js.map +1 -1
- package/dist/chat-agents/jiwa.test.js +1 -1
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +33 -36
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/command-routing.d.ts +1 -1
- package/dist/cli/command-routing.d.ts.map +1 -1
- package/dist/cli/command-routing.js +1 -0
- package/dist/cli/command-routing.js.map +1 -1
- package/dist/cli/command-routing.test.js +1 -0
- package/dist/cli/command-routing.test.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +179 -48
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +18 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -76
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +30 -13
- package/dist/config.test.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +62 -0
- package/dist/core/agent-notifications.d.ts.map +1 -0
- package/dist/core/agent-notifications.js +92 -0
- package/dist/core/agent-notifications.js.map +1 -0
- package/dist/core/agent-notifications.test.d.ts +2 -0
- package/dist/core/agent-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-notifications.test.js +97 -0
- package/dist/core/agent-notifications.test.js.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts +2 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.js +201 -0
- package/dist/core/agent-session-checkpoint-flush.test.js.map +1 -0
- package/dist/core/agent-session-compaction.test.js +323 -6
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-crash-durability.test.d.ts +2 -0
- package/dist/core/agent-session-crash-durability.test.d.ts.map +1 -0
- package/dist/core/agent-session-crash-durability.test.js +102 -0
- package/dist/core/agent-session-crash-durability.test.js.map +1 -0
- package/dist/core/agent-session-marker-anchors.test.js +181 -2
- package/dist/core/agent-session-marker-anchors.test.js.map +1 -1
- package/dist/core/agent-session-model-switch.test.d.ts +2 -0
- package/dist/core/agent-session-model-switch.test.d.ts.map +1 -0
- package/dist/core/agent-session-model-switch.test.js +161 -0
- package/dist/core/agent-session-model-switch.test.js.map +1 -0
- package/dist/core/agent-session-notifications.test.d.ts +2 -0
- package/dist/core/agent-session-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-session-notifications.test.js +114 -0
- package/dist/core/agent-session-notifications.test.js.map +1 -0
- package/dist/core/agent-session-process-gate.test.d.ts +2 -0
- package/dist/core/agent-session-process-gate.test.d.ts.map +1 -0
- package/dist/core/agent-session-process-gate.test.js +98 -0
- package/dist/core/agent-session-process-gate.test.js.map +1 -0
- package/dist/core/agent-session-queue.test.js +65 -0
- package/dist/core/agent-session-queue.test.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.js +80 -23
- package/dist/core/agent-session-review-coverage.test.js.map +1 -1
- package/dist/core/agent-session-slash-restore.test.d.ts +2 -0
- package/dist/core/agent-session-slash-restore.test.d.ts.map +1 -0
- package/dist/core/agent-session-slash-restore.test.js +121 -0
- package/dist/core/agent-session-slash-restore.test.js.map +1 -0
- package/dist/core/agent-session-tool-tiers.test.d.ts +2 -0
- package/dist/core/agent-session-tool-tiers.test.d.ts.map +1 -0
- package/dist/core/agent-session-tool-tiers.test.js +138 -0
- package/dist/core/agent-session-tool-tiers.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +257 -13
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1118 -241
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agents.d.ts +29 -1
- package/dist/core/agents.d.ts.map +1 -1
- package/dist/core/agents.js +47 -97
- package/dist/core/agents.js.map +1 -1
- package/dist/core/agents.test.js +87 -7
- package/dist/core/agents.test.js.map +1 -1
- package/dist/core/api-benchmark.d.ts +3 -3
- package/dist/core/api-benchmark.js +7 -7
- package/dist/core/auth-providers.d.ts +37 -1
- package/dist/core/auth-providers.d.ts.map +1 -1
- package/dist/core/auth-providers.js +83 -3
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/auto-update.test.js +1 -1
- package/dist/core/autopilot-gate.d.ts +2 -2
- package/dist/core/autopilot-gate.js +1 -1
- package/dist/core/bash-spawn-benchmark.js +6 -6
- package/dist/core/bundled-agents.d.ts +3 -0
- package/dist/core/bundled-agents.d.ts.map +1 -0
- package/dist/core/bundled-agents.js +252 -0
- package/dist/core/bundled-agents.js.map +1 -0
- package/dist/core/cache-warm-benchmark.d.ts +2 -2
- package/dist/core/cache-warm-benchmark.js +5 -5
- package/dist/core/code-retrieval-chunkers.d.ts +27 -0
- package/dist/core/code-retrieval-chunkers.d.ts.map +1 -0
- package/dist/core/code-retrieval-chunkers.js +264 -0
- package/dist/core/code-retrieval-chunkers.js.map +1 -0
- package/dist/core/code-retrieval-chunkers.test.d.ts +2 -0
- package/dist/core/code-retrieval-chunkers.test.d.ts.map +1 -0
- package/dist/core/code-retrieval-chunkers.test.js +203 -0
- package/dist/core/code-retrieval-chunkers.test.js.map +1 -0
- package/dist/core/code-retrieval.d.ts +6 -0
- package/dist/core/code-retrieval.d.ts.map +1 -1
- package/dist/core/code-retrieval.js +38 -1
- package/dist/core/code-retrieval.js.map +1 -1
- package/dist/core/compaction/active-context.test.js +21 -0
- package/dist/core/compaction/active-context.test.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +108 -8
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +423 -119
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +360 -8
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/policy.d.ts +17 -0
- package/dist/core/compaction/policy.d.ts.map +1 -0
- package/dist/core/compaction/policy.js +22 -0
- package/dist/core/compaction/policy.js.map +1 -0
- package/dist/core/compaction/policy.test.d.ts +2 -0
- package/dist/core/compaction/policy.test.d.ts.map +1 -0
- package/dist/core/compaction/policy.test.js +35 -0
- package/dist/core/compaction/policy.test.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.d.ts +33 -0
- package/dist/core/compaction/query-aware-selector.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.js +202 -0
- package/dist/core/compaction/query-aware-selector.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts +2 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.js +96 -0
- package/dist/core/compaction/query-aware-selector.test.js.map +1 -0
- package/dist/core/compaction/tool-result-pruner.d.ts +24 -11
- package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -1
- package/dist/core/compaction/tool-result-pruner.js +98 -26
- package/dist/core/compaction/tool-result-pruner.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.test.js +178 -6
- package/dist/core/compaction/tool-result-pruner.test.js.map +1 -1
- package/dist/core/encode-cwd.test.js +1 -1
- package/dist/core/event-bus.d.ts +23 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +10 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +28 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugin-bundles.d.ts +23 -0
- package/dist/core/extensions/plugin-bundles.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.js +228 -0
- package/dist/core/extensions/plugin-bundles.js.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts +2 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.js +110 -0
- package/dist/core/extensions/plugin-bundles.test.js.map +1 -0
- package/dist/core/fast-apply-benchmark.d.ts +2 -2
- package/dist/core/fast-apply-benchmark.js +6 -6
- package/dist/core/foreign-session-import.d.ts +120 -0
- package/dist/core/foreign-session-import.d.ts.map +1 -0
- package/dist/core/foreign-session-import.js +598 -0
- package/dist/core/foreign-session-import.js.map +1 -0
- package/dist/core/foreign-session-import.test.d.ts +2 -0
- package/dist/core/foreign-session-import.test.d.ts.map +1 -0
- package/dist/core/foreign-session-import.test.js +220 -0
- package/dist/core/foreign-session-import.test.js.map +1 -0
- package/dist/core/foreign-transcript-blocks.d.ts +51 -0
- package/dist/core/foreign-transcript-blocks.d.ts.map +1 -0
- package/dist/core/foreign-transcript-blocks.js +151 -0
- package/dist/core/foreign-transcript-blocks.js.map +1 -0
- package/dist/core/goal-lifecycle-smoke.test.js +2 -2
- package/dist/core/goal-store.js +1 -1
- package/dist/core/goal-store.test.js +2 -2
- package/dist/core/goal-verifier.test.js +2 -2
- package/dist/core/goal-worker-dev-server-lifecycle.test.js +1 -1
- package/dist/core/goal-worker.test.js +2 -2
- package/dist/core/hashline-edit-benchmark.d.ts +2 -2
- package/dist/core/hashline-edit-benchmark.js +7 -7
- package/dist/core/ideal-review.d.ts +20 -1
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +37 -2
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +99 -3
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/local-endpoint-store.test.js +1 -1
- package/dist/core/logger.d.ts +1 -1
- package/dist/core/logger.js +2 -2
- package/dist/core/logger.js.map +1 -1
- package/dist/core/loop-breaker.d.ts.map +1 -1
- package/dist/core/loop-breaker.js +1 -0
- package/dist/core/loop-breaker.js.map +1 -1
- package/dist/core/lsp/client-navigation.test.d.ts +2 -0
- package/dist/core/lsp/client-navigation.test.d.ts.map +1 -0
- package/dist/core/lsp/client-navigation.test.js +135 -0
- package/dist/core/lsp/client-navigation.test.js.map +1 -0
- package/dist/core/lsp/client.d.ts +84 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +190 -3
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/integration.test.js +2 -2
- package/dist/core/lsp/manager-navigation.test.d.ts +2 -0
- package/dist/core/lsp/manager-navigation.test.d.ts.map +1 -0
- package/dist/core/lsp/manager-navigation.test.js +121 -0
- package/dist/core/lsp/manager-navigation.test.js.map +1 -0
- package/dist/core/lsp/manager.d.ts +81 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +183 -64
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +18 -0
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/lsp/pool.d.ts +113 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +255 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/pool.test.d.ts +2 -0
- package/dist/core/lsp/pool.test.d.ts.map +1 -0
- package/dist/core/lsp/pool.test.js +286 -0
- package/dist/core/lsp/pool.test.js.map +1 -0
- package/dist/core/lsp/servers.d.ts.map +1 -1
- package/dist/core/lsp/servers.js +43 -3
- package/dist/core/lsp/servers.js.map +1 -1
- package/dist/core/lsp/servers.test.d.ts +2 -0
- package/dist/core/lsp/servers.test.d.ts.map +1 -0
- package/dist/core/lsp/servers.test.js +67 -0
- package/dist/core/lsp/servers.test.js.map +1 -0
- package/dist/core/lsp/windows.test.js +6 -18
- package/dist/core/lsp/windows.test.js.map +1 -1
- package/dist/core/mcp/catalog-cache.d.ts +63 -0
- package/dist/core/mcp/catalog-cache.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.js +163 -0
- package/dist/core/mcp/catalog-cache.js.map +1 -0
- package/dist/core/mcp/catalog-cache.test.d.ts +2 -0
- package/dist/core/mcp/catalog-cache.test.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.test.js +99 -0
- package/dist/core/mcp/catalog-cache.test.js.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts +2 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.js +231 -0
- package/dist/core/mcp/client-session-recovery.test.js.map +1 -0
- package/dist/core/mcp/client.d.ts +158 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +478 -45
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/content.d.ts +19 -0
- package/dist/core/mcp/content.d.ts.map +1 -0
- package/dist/core/mcp/content.js +172 -0
- package/dist/core/mcp/content.js.map +1 -0
- package/dist/core/mcp/content.test.d.ts +2 -0
- package/dist/core/mcp/content.test.d.ts.map +1 -0
- package/dist/core/mcp/content.test.js +135 -0
- package/dist/core/mcp/content.test.js.map +1 -0
- package/dist/core/mcp/deferred-catalog.d.ts +1 -1
- package/dist/core/mcp/deferred-catalog.js +1 -1
- package/dist/core/mcp/elicitation-bridge.d.ts +41 -0
- package/dist/core/mcp/elicitation-bridge.d.ts.map +1 -0
- package/dist/core/mcp/elicitation-bridge.js +52 -0
- package/dist/core/mcp/elicitation-bridge.js.map +1 -0
- package/dist/core/mcp/elicitation.test.d.ts +2 -0
- package/dist/core/mcp/elicitation.test.d.ts.map +1 -0
- package/dist/core/mcp/elicitation.test.js +158 -0
- package/dist/core/mcp/elicitation.test.js.map +1 -0
- package/dist/core/mcp/index.d.ts +5 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +2 -0
- package/dist/core/mcp/index.js.map +1 -1
- package/dist/core/mcp/legacy-negotiation.test.d.ts +2 -0
- package/dist/core/mcp/legacy-negotiation.test.d.ts.map +1 -0
- package/dist/core/mcp/legacy-negotiation.test.js +80 -0
- package/dist/core/mcp/legacy-negotiation.test.js.map +1 -0
- package/dist/core/mcp/oauth-provider.d.ts +1 -2
- package/dist/core/mcp/oauth-provider.d.ts.map +1 -1
- package/dist/core/mcp/oauth-provider.js +6 -2
- package/dist/core/mcp/oauth-provider.js.map +1 -1
- package/dist/core/mcp/oauth-store.d.ts +1 -1
- package/dist/core/mcp/oauth-store.d.ts.map +1 -1
- package/dist/core/mcp/oauth-store.js.map +1 -1
- package/dist/core/mcp/oauth-store.test.js +2 -2
- package/dist/core/mcp/resolve-stdio.test.js +5 -5
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts +2 -0
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts.map +1 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js +138 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js.map +1 -0
- package/dist/core/mcp/shared-pool.d.ts +158 -0
- package/dist/core/mcp/shared-pool.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.js +235 -0
- package/dist/core/mcp/shared-pool.js.map +1 -0
- package/dist/core/mcp/shared-pool.test.d.ts +2 -0
- package/dist/core/mcp/shared-pool.test.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.test.js +425 -0
- package/dist/core/mcp/shared-pool.test.js.map +1 -0
- package/dist/core/mcp/store.test.js +5 -5
- package/dist/core/mcp/types.d.ts +12 -0
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/mcp/when-connected.test.d.ts +2 -0
- package/dist/core/mcp/when-connected.test.d.ts.map +1 -0
- package/dist/core/mcp/when-connected.test.js +46 -0
- package/dist/core/mcp/when-connected.test.js.map +1 -0
- package/dist/core/nolan-context.d.ts.map +1 -1
- package/dist/core/nolan-context.js +8 -0
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-context.test.js +40 -0
- package/dist/core/nolan-context.test.js.map +1 -1
- package/dist/core/nolan-model.test.js +1 -1
- package/dist/core/nolan-prompt.js +8 -5
- package/dist/core/nolan-prompt.js.map +1 -1
- package/dist/core/nolan-prompt.test.js +5 -0
- package/dist/core/nolan-prompt.test.js.map +1 -1
- package/dist/core/oauth/xai.d.ts +2 -0
- package/dist/core/oauth/xai.d.ts.map +1 -0
- package/dist/core/oauth/xai.js +4 -0
- package/dist/core/oauth/xai.js.map +1 -0
- package/dist/core/persistent-shell.d.ts +3 -2
- package/dist/core/persistent-shell.d.ts.map +1 -1
- package/dist/core/persistent-shell.js +11 -4
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/persistent-shell.test.js +6 -6
- package/dist/core/process-gate.d.ts +45 -0
- package/dist/core/process-gate.d.ts.map +1 -0
- package/dist/core/process-gate.js +79 -0
- package/dist/core/process-gate.js.map +1 -0
- package/dist/core/process-gate.test.d.ts +2 -0
- package/dist/core/process-gate.test.d.ts.map +1 -0
- package/dist/core/process-gate.test.js +87 -0
- package/dist/core/process-gate.test.js.map +1 -0
- package/dist/core/process-manager-dev-server-repro.test.js +51 -12
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
- package/dist/core/process-manager-notifications.test.d.ts +2 -0
- package/dist/core/process-manager-notifications.test.d.ts.map +1 -0
- package/dist/core/process-manager-notifications.test.js +227 -0
- package/dist/core/process-manager-notifications.test.js.map +1 -0
- package/dist/core/process-manager.d.ts +72 -1
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +247 -4
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/progress/git-xp.test.js +2 -2
- package/dist/core/progress/ranks.d.ts +14 -3
- package/dist/core/progress/ranks.d.ts.map +1 -1
- package/dist/core/progress/ranks.js +181 -12
- package/dist/core/progress/ranks.js.map +1 -1
- package/dist/core/progress/ranks.test.js +48 -7
- package/dist/core/progress/ranks.test.js.map +1 -1
- package/dist/core/progress/rebuild.test.js +1 -1
- package/dist/core/progress/store.test.js +1 -1
- package/dist/core/progress/types.d.ts +2 -1
- package/dist/core/progress/types.d.ts.map +1 -1
- package/dist/core/project-discovery.d.ts +31 -6
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +349 -79
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-discovery.test.js +128 -4
- package/dist/core/project-discovery.test.js.map +1 -1
- package/dist/core/prompt-commands.js +3 -3
- package/dist/core/radio.d.ts +1 -1
- package/dist/core/radio.d.ts.map +1 -1
- package/dist/core/radio.js +90 -6
- package/dist/core/radio.js.map +1 -1
- package/dist/core/radio.test.js +15 -2
- package/dist/core/radio.test.js.map +1 -1
- package/dist/core/regrounding.d.ts.map +1 -1
- package/dist/core/regrounding.js +1 -0
- package/dist/core/regrounding.js.map +1 -1
- package/dist/core/run-claim.d.ts +37 -0
- package/dist/core/run-claim.d.ts.map +1 -0
- package/dist/core/run-claim.js +46 -0
- package/dist/core/run-claim.js.map +1 -0
- package/dist/core/run-claim.test.d.ts +2 -0
- package/dist/core/run-claim.test.d.ts.map +1 -0
- package/dist/core/run-claim.test.js +107 -0
- package/dist/core/run-claim.test.js.map +1 -0
- package/dist/core/run-journal.test.d.ts +2 -0
- package/dist/core/run-journal.test.d.ts.map +1 -0
- package/dist/core/run-journal.test.js +195 -0
- package/dist/core/run-journal.test.js.map +1 -0
- package/dist/core/run-lifecycle.d.ts +22 -3
- package/dist/core/run-lifecycle.d.ts.map +1 -1
- package/dist/core/run-lifecycle.js +12 -3
- package/dist/core/run-lifecycle.js.map +1 -1
- package/dist/core/sandbox-domains.d.ts +14 -0
- package/dist/core/sandbox-domains.d.ts.map +1 -0
- package/dist/core/sandbox-domains.js +76 -0
- package/dist/core/sandbox-domains.js.map +1 -0
- package/dist/core/sandbox-feedback.d.ts +22 -0
- package/dist/core/sandbox-feedback.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.js +69 -0
- package/dist/core/sandbox-feedback.js.map +1 -0
- package/dist/core/sandbox-feedback.test.d.ts +2 -0
- package/dist/core/sandbox-feedback.test.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.test.js +32 -0
- package/dist/core/sandbox-feedback.test.js.map +1 -0
- package/dist/core/sandbox.d.ts +91 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +254 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/sandbox.test.d.ts +2 -0
- package/dist/core/sandbox.test.d.ts.map +1 -0
- package/dist/core/sandbox.test.js +157 -0
- package/dist/core/sandbox.test.js.map +1 -0
- package/dist/core/semantic-search-benchmark.d.ts +2 -2
- package/dist/core/semantic-search-benchmark.js +5 -5
- package/dist/core/session-compaction.d.ts +6 -0
- package/dist/core/session-compaction.d.ts.map +1 -1
- package/dist/core/session-compaction.js +11 -1
- package/dist/core/session-compaction.js.map +1 -1
- package/dist/core/session-compaction.test.js +19 -2
- package/dist/core/session-compaction.test.js.map +1 -1
- package/dist/core/session-export.d.ts.map +1 -1
- package/dist/core/session-export.js +12 -4
- package/dist/core/session-export.js.map +1 -1
- package/dist/core/session-history.d.ts +95 -6
- package/dist/core/session-history.d.ts.map +1 -1
- package/dist/core/session-history.js +261 -17
- package/dist/core/session-history.js.map +1 -1
- package/dist/core/session-history.test.js +173 -2
- package/dist/core/session-history.test.js.map +1 -1
- package/dist/core/session-manager.d.ts +223 -6
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +579 -74
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +154 -10
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/session-notification-visibility.test.d.ts +2 -0
- package/dist/core/session-notification-visibility.test.d.ts.map +1 -0
- package/dist/core/session-notification-visibility.test.js +113 -0
- package/dist/core/session-notification-visibility.test.js.map +1 -0
- package/dist/core/session-preview.d.ts +5 -4
- package/dist/core/session-preview.d.ts.map +1 -1
- package/dist/core/session-preview.js +12 -5
- package/dist/core/session-preview.js.map +1 -1
- package/dist/core/session-restore-display.test.js +25 -0
- package/dist/core/session-restore-display.test.js.map +1 -1
- package/dist/core/session-storage-fd-leak.test.d.ts +2 -0
- package/dist/core/session-storage-fd-leak.test.d.ts.map +1 -0
- package/dist/core/session-storage-fd-leak.test.js +116 -0
- package/dist/core/session-storage-fd-leak.test.js.map +1 -0
- package/dist/core/session-storage.d.ts +22 -2
- package/dist/core/session-storage.d.ts.map +1 -1
- package/dist/core/session-storage.js +72 -9
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/session-storage.test.js +8 -3
- package/dist/core/session-storage.test.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +36 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shell-path.js +1 -1
- package/dist/core/shell.d.ts +1 -1
- package/dist/core/shell.js +2 -2
- package/dist/core/shell.test.js +2 -2
- package/dist/core/shutdown.d.ts +71 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +144 -0
- package/dist/core/shutdown.js.map +1 -0
- package/dist/core/shutdown.test.d.ts +2 -0
- package/dist/core/shutdown.test.d.ts.map +1 -0
- package/dist/core/shutdown.test.js +160 -0
- package/dist/core/shutdown.test.js.map +1 -0
- package/dist/core/sidecar-error-reporter.js +1 -1
- package/dist/core/sidecar-error-reporter.test.js +2 -2
- package/dist/core/skills-routing.test.js +96 -0
- package/dist/core/skills-routing.test.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +3 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/steering.d.ts +12 -0
- package/dist/core/steering.d.ts.map +1 -1
- package/dist/core/steering.js +16 -0
- package/dist/core/steering.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +22 -0
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +84 -13
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +57 -15
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.test.js +3 -3
- package/dist/core/task-store.js +1 -1
- package/dist/core/verification-evidence.d.ts +17 -0
- package/dist/core/verification-evidence.d.ts.map +1 -0
- package/dist/core/verification-evidence.js +283 -0
- package/dist/core/verification-evidence.js.map +1 -0
- package/dist/core/verification-evidence.test.d.ts +2 -0
- package/dist/core/verification-evidence.test.d.ts.map +1 -0
- package/dist/core/verification-evidence.test.js +103 -0
- package/dist/core/verification-evidence.test.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +17 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/acp-mode.d.ts +122 -0
- package/dist/modes/acp-mode.d.ts.map +1 -0
- package/dist/modes/acp-mode.js +1366 -0
- package/dist/modes/acp-mode.js.map +1 -0
- package/dist/modes/acp-mode.test.d.ts +2 -0
- package/dist/modes/acp-mode.test.d.ts.map +1 -0
- package/dist/modes/acp-mode.test.js +1065 -0
- package/dist/modes/acp-mode.test.js.map +1 -0
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +15 -11
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/json-mode.d.ts +36 -0
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +39 -0
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/json-mode.test.d.ts +2 -0
- package/dist/modes/json-mode.test.d.ts.map +1 -0
- package/dist/modes/json-mode.test.js +84 -0
- package/dist/modes/json-mode.test.js.map +1 -0
- package/dist/modes/serve-mode.d.ts +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +14 -9
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +8 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +91 -11
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.test.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.test.d.ts.map +1 -0
- package/dist/modes/subagent-worker-mode.test.js +52 -0
- package/dist/modes/subagent-worker-mode.test.js.map +1 -0
- package/dist/session.d.ts +29 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +41 -0
- package/dist/session.js.map +1 -1
- package/dist/session.test.js +1 -1
- package/dist/system-prompt.d.ts +37 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +123 -11
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +90 -11
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/system-prompt.tiering.test.d.ts +2 -0
- package/dist/system-prompt.tiering.test.d.ts.map +1 -0
- package/dist/system-prompt.tiering.test.js +105 -0
- package/dist/system-prompt.tiering.test.js.map +1 -0
- package/dist/tools/bash.d.ts +2 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +55 -16
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/code-nav.d.ts +30 -0
- package/dist/tools/code-nav.d.ts.map +1 -0
- package/dist/tools/code-nav.js +320 -0
- package/dist/tools/code-nav.js.map +1 -0
- package/dist/tools/code-nav.test.d.ts +2 -0
- package/dist/tools/code-nav.test.d.ts.map +1 -0
- package/dist/tools/code-nav.test.js +136 -0
- package/dist/tools/code-nav.test.js.map +1 -0
- package/dist/tools/edit.test.js +1 -1
- package/dist/tools/generate-image.d.ts +1 -1
- package/dist/tools/goals.d.ts +4 -4
- package/dist/tools/goals.test.js +2 -2
- package/dist/tools/grep-parity.test.d.ts +2 -0
- package/dist/tools/grep-parity.test.d.ts.map +1 -0
- package/dist/tools/grep-parity.test.js +92 -0
- package/dist/tools/grep-parity.test.js.map +1 -0
- package/dist/tools/grep-recall.test.d.ts +2 -0
- package/dist/tools/grep-recall.test.d.ts.map +1 -0
- package/dist/tools/grep-recall.test.js +114 -0
- package/dist/tools/grep-recall.test.js.map +1 -0
- package/dist/tools/grep-spawn.test.d.ts +2 -0
- package/dist/tools/grep-spawn.test.d.ts.map +1 -0
- package/dist/tools/grep-spawn.test.js +66 -0
- package/dist/tools/grep-spawn.test.js.map +1 -0
- package/dist/tools/grep.d.ts +13 -3
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +342 -41
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.js +17 -14
- package/dist/tools/grep.test.js.map +1 -1
- package/dist/tools/index.d.ts +20 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +28 -18
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.d.ts +2 -0
- package/dist/tools/index.test.d.ts.map +1 -0
- package/dist/tools/index.test.js +55 -0
- package/dist/tools/index.test.js.map +1 -0
- package/dist/tools/plan-mode.test.js +1 -1
- package/dist/tools/prompt-hints.d.ts +9 -0
- package/dist/tools/prompt-hints.d.ts.map +1 -1
- package/dist/tools/prompt-hints.js +61 -8
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read-only-bash.d.ts +3 -0
- package/dist/tools/read-only-bash.d.ts.map +1 -1
- package/dist/tools/read-only-bash.js +7 -9
- package/dist/tools/read-only-bash.js.map +1 -1
- package/dist/tools/safe-env.js +1 -1
- package/dist/tools/search-code.d.ts.map +1 -1
- package/dist/tools/search-code.js +7 -6
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/search-code.test.js +8 -2
- package/dist/tools/search-code.test.js.map +1 -1
- package/dist/tools/skill.js +2 -1
- package/dist/tools/skill.js.map +1 -1
- package/dist/tools/subagent-control.d.ts.map +1 -1
- package/dist/tools/subagent-control.js +25 -6
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent-control.test.js +34 -1
- package/dist/tools/subagent-control.test.js.map +1 -1
- package/dist/tools/subagent-shared.d.ts +26 -1
- package/dist/tools/subagent-shared.d.ts.map +1 -1
- package/dist/tools/subagent-shared.js +40 -7
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent-shared.test.js +43 -9
- package/dist/tools/subagent-shared.test.js.map +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +54 -16
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.js +68 -3
- package/dist/tools/subagent.test.js.map +1 -1
- package/dist/tools/task-output.d.ts.map +1 -1
- package/dist/tools/task-output.js +3 -1
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/task-send.test.js +19 -10
- package/dist/tools/task-send.test.js.map +1 -1
- package/dist/tools/tool-search.d.ts +12 -1
- package/dist/tools/tool-search.d.ts.map +1 -1
- package/dist/tools/tool-search.js +24 -4
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-search.test.d.ts +2 -0
- package/dist/tools/tool-search.test.d.ts.map +1 -0
- package/dist/tools/tool-search.test.js +69 -0
- package/dist/tools/tool-search.test.js.map +1 -0
- package/dist/tools/tool-tiers.d.ts +42 -0
- package/dist/tools/tool-tiers.d.ts.map +1 -0
- package/dist/tools/tool-tiers.js +78 -0
- package/dist/tools/tool-tiers.js.map +1 -0
- package/dist/tools/tool-tiers.test.d.ts +2 -0
- package/dist/tools/tool-tiers.test.d.ts.map +1 -0
- package/dist/tools/tool-tiers.test.js +48 -0
- package/dist/tools/tool-tiers.test.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +5 -2
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/write.test.js +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +27 -1
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/Banner.d.ts.map +1 -1
- package/dist/ui/components/Banner.js +24 -16
- package/dist/ui/components/Banner.js.map +1 -1
- package/dist/ui/components/ToolExecution.js +42 -34
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/force-color.d.ts +2 -0
- package/dist/ui/components/force-color.d.ts.map +1 -0
- package/dist/ui/components/force-color.js +11 -0
- package/dist/ui/components/force-color.js.map +1 -0
- package/dist/ui/components/themed-output.test.d.ts +2 -0
- package/dist/ui/components/themed-output.test.d.ts.map +1 -0
- package/dist/ui/components/themed-output.test.js +111 -0
- package/dist/ui/components/themed-output.test.js.map +1 -0
- package/dist/ui/error-item.d.ts +1 -1
- package/dist/ui/error-item.js +3 -3
- package/dist/ui/error-item.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +3 -0
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +45 -6
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/login.js +1 -1
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +1 -1
- package/dist/ui/render.js +6 -6
- package/dist/ui/theme/dark-ansi.json +6 -0
- package/dist/ui/theme/dark-daltonized.json +6 -0
- package/dist/ui/theme/dark.json +6 -0
- package/dist/ui/theme/light-ansi.json +6 -0
- package/dist/ui/theme/light-daltonized.json +6 -0
- package/dist/ui/theme/light.json +6 -0
- package/dist/ui/theme/theme.d.ts +6 -0
- package/dist/ui/theme/theme.d.ts.map +1 -1
- package/dist/ui/tui-terminal-recorder.test.js +1 -1
- package/dist/utils/git.test.js +3 -3
- package/dist/utils/github.test.js +3 -3
- package/dist/utils/github.test.js.map +1 -1
- package/dist/utils/image.d.ts +26 -3
- package/dist/utils/image.d.ts.map +1 -1
- package/dist/utils/image.js +83 -14
- package/dist/utils/image.js.map +1 -1
- package/dist/utils/image.test.d.ts +2 -0
- package/dist/utils/image.test.d.ts.map +1 -0
- package/dist/utils/image.test.js +139 -0
- package/dist/utils/image.test.js.map +1 -0
- package/package.json +7 -6
package/dist/core/mcp/client.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/
|
|
3
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
-
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
1
|
+
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
2
|
+
import { Client, OAuthError, OAuthErrorCode, ProtocolError, SdkError, SdkErrorCode, SdkHttpError, StreamableHTTPClientTransport, SSEClientTransport, UnauthorizedError, } from "@modelcontextprotocol/client";
|
|
6
3
|
import { z } from "zod";
|
|
7
4
|
import http from "node:http";
|
|
8
5
|
import os from "node:os";
|
|
@@ -10,9 +7,222 @@ import { log } from "../logger.js";
|
|
|
10
7
|
import { McpOAuthProvider, MCP_OAUTH_CALLBACK_PORT, MCP_OAUTH_CALLBACK_PATH, } from "./oauth-provider.js";
|
|
11
8
|
import { McpOAuthStore } from "./oauth-store.js";
|
|
12
9
|
import { isLocalhost, alternateLoopback, isNetworkError } from "./loopback.js";
|
|
10
|
+
import { toToolResult } from "./content.js";
|
|
13
11
|
import { resolveStdioCommand } from "./resolve-stdio.js";
|
|
12
|
+
import { McpCatalogCache } from "./catalog-cache.js";
|
|
13
|
+
import { isShareableServer, sharedMcpPool, } from "./shared-pool.js";
|
|
14
14
|
export class MCPClientManager {
|
|
15
15
|
servers = [];
|
|
16
|
+
/**
|
|
17
|
+
* Per-server connection settlement, so a caller holding a cached-only tool
|
|
18
|
+
* can wait for the live client instead of failing or hanging forever.
|
|
19
|
+
*/
|
|
20
|
+
connections = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* In-flight session rebuilds, keyed by server name. N tool calls to the same
|
|
23
|
+
* server all 404 at once when its session expires; without coalescing each
|
|
24
|
+
* would spawn its own reconnect and they would race to replace each other in
|
|
25
|
+
* `this.servers`. Sharing one promise means exactly one reconnect per outage.
|
|
26
|
+
*/
|
|
27
|
+
reconnecting = new Map();
|
|
28
|
+
/**
|
|
29
|
+
* Claims on process-wide shared connections, keyed by server name. Held so
|
|
30
|
+
* `dispose` can hand them back: the pooled child only exits once the LAST
|
|
31
|
+
* manager holding it releases, so leaking a claim here would pin the process
|
|
32
|
+
* for the daemon's lifetime — the exact bug sharing is meant to fix.
|
|
33
|
+
*/
|
|
34
|
+
sharedHandles = new Map();
|
|
35
|
+
catalogCache;
|
|
36
|
+
/**
|
|
37
|
+
* Opt into the 2026-07-28 revision. Off by default: `mode: "auto"` probes with
|
|
38
|
+
* `server/discover` before falling back to `initialize`, and a legacy stdio
|
|
39
|
+
* server that ignores pre-`initialize` requests pays the full probe timeout.
|
|
40
|
+
*/
|
|
41
|
+
modernProtocol;
|
|
42
|
+
/**
|
|
43
|
+
* Host callback for server-initiated `elicitation/create`. Absent means we
|
|
44
|
+
* declare no elicitation capability at all — which is protocol-legal and is
|
|
45
|
+
* how the CLI runs, since it has nowhere to render the form.
|
|
46
|
+
*/
|
|
47
|
+
onElicit;
|
|
48
|
+
/**
|
|
49
|
+
* Pool backing `shared: true` servers. Defaults to the daemon-wide singleton;
|
|
50
|
+
* injectable so tests can exercise sharing without touching global state.
|
|
51
|
+
*/
|
|
52
|
+
pool;
|
|
53
|
+
/**
|
|
54
|
+
* Fired when a connected server's transport closes on its own — a crashed or
|
|
55
|
+
* exited child, not our own `dispose`. The pool uses it to drop a dead
|
|
56
|
+
* connection so the next session rebuilds instead of inheriting a corpse.
|
|
57
|
+
*/
|
|
58
|
+
onServerClosed;
|
|
59
|
+
/** Set while `dispose` runs, so our own teardown is not reported as a death. */
|
|
60
|
+
disposing = false;
|
|
61
|
+
constructor(opts = {}) {
|
|
62
|
+
this.catalogCache = opts.catalogCache ?? new McpCatalogCache();
|
|
63
|
+
this.modernProtocol = opts.modernProtocol ?? false;
|
|
64
|
+
this.onElicit = opts.onElicit;
|
|
65
|
+
this.pool = opts.sharedPool ?? sharedMcpPool;
|
|
66
|
+
this.onServerClosed = opts.onServerClosed;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a `Client` for one server, declaring elicitation support and wiring
|
|
70
|
+
* the handler when the host can actually prompt the user.
|
|
71
|
+
*
|
|
72
|
+
* Form mode only. URL mode would have us open a browser tab in the middle of
|
|
73
|
+
* a tool call with no user gesture behind it — the SDK rejects URL-mode
|
|
74
|
+
* requests for us when the `url` capability is undeclared.
|
|
75
|
+
*/
|
|
76
|
+
createClient(serverName, negotiate) {
|
|
77
|
+
const handler = this.onElicit;
|
|
78
|
+
if (!handler)
|
|
79
|
+
return new Client({ name: "ezcoder", version: "1.0.0" }, negotiate);
|
|
80
|
+
const client = new Client({ name: "ezcoder", version: "1.0.0" }, { ...negotiate, capabilities: { elicitation: { form: {} } } });
|
|
81
|
+
client.setRequestHandler("elicitation/create", async (request) => {
|
|
82
|
+
const params = request.params;
|
|
83
|
+
if (params.mode === "url") {
|
|
84
|
+
// Unreachable while `url` stays undeclared, but a server that ignores
|
|
85
|
+
// capabilities must get a clean decline rather than a thrown handler.
|
|
86
|
+
return { action: "decline" };
|
|
87
|
+
}
|
|
88
|
+
return handler({
|
|
89
|
+
server: serverName,
|
|
90
|
+
message: params.message,
|
|
91
|
+
requestedSchema: params.requestedSchema,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
return client;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Version-negotiation options for a real (non-probe) connect. `probe()` is
|
|
98
|
+
* spawn-per-invocation and deliberately never negotiates: a legacy stdio
|
|
99
|
+
* server that ignores the discovery request would stall it for the whole
|
|
100
|
+
* probe timeout, turning "validate this server" into a 30s hang.
|
|
101
|
+
*
|
|
102
|
+
* The probe timeout is transport-aware because the SDK's timeout VERDICT is:
|
|
103
|
+
*
|
|
104
|
+
* - **stdio** — silence on a local pipe means a legacy server, and the SDK
|
|
105
|
+
* falls back to `initialize`. The probe runs on a disposable sibling
|
|
106
|
+
* process, so the fallback is clean. Waiting the full connect timeout to
|
|
107
|
+
* reach a conclusion we can draw in seconds is pure dead time, so cap it
|
|
108
|
+
* short.
|
|
109
|
+
* - **HTTP** — silence means an outage, and the SDK REJECTS the connect
|
|
110
|
+
* rather than falling back. A short timeout would therefore turn a slow
|
|
111
|
+
* cold start into a hard connection failure, so inherit the full connect
|
|
112
|
+
* timeout and let the normal timeout handling deal with a real outage.
|
|
113
|
+
*/
|
|
114
|
+
negotiationOptions(transport) {
|
|
115
|
+
if (!this.modernProtocol)
|
|
116
|
+
return {};
|
|
117
|
+
return {
|
|
118
|
+
versionNegotiation: {
|
|
119
|
+
mode: "auto",
|
|
120
|
+
...(transport === "stdio" ? { probe: { timeoutMs: STDIO_PROBE_TIMEOUT_MS } } : {}),
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Claim the pooled connection for a shared server and adopt its tools.
|
|
126
|
+
*
|
|
127
|
+
* Shaped like `connectServer` (resolve tools / throw on failure) so the
|
|
128
|
+
* caller's `allSettled` handling is identical for both kinds. The pool
|
|
129
|
+
* reports failure in-band, so it is rethrown here with the message the pool
|
|
130
|
+
* already formatted.
|
|
131
|
+
*/
|
|
132
|
+
async connectShared(config) {
|
|
133
|
+
const handle = await this.pool.acquire(config,
|
|
134
|
+
// The pooled connection is just another manager with sharing switched
|
|
135
|
+
// off, so it takes the normal connect path instead of recursing into the
|
|
136
|
+
// pool. Its elicit handler is the pool's dispatcher, NOT this session's:
|
|
137
|
+
// the connection is shared, so each request has to be routed to whichever
|
|
138
|
+
// session actually provoked it.
|
|
139
|
+
(opts) => {
|
|
140
|
+
const manager = new MCPClientManager({
|
|
141
|
+
catalogCache: opts.catalogCache,
|
|
142
|
+
modernProtocol: opts.modernProtocol,
|
|
143
|
+
onElicit: opts.onElicit,
|
|
144
|
+
sharedPool: this.pool,
|
|
145
|
+
// Let the pool retire this connection if its server exits.
|
|
146
|
+
onServerClosed: opts.onClosed,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
connect: async (target) => {
|
|
150
|
+
const [result] = await manager.connectAllDetailed([{ ...target, shared: false }]);
|
|
151
|
+
return (result ?? {
|
|
152
|
+
name: target.name,
|
|
153
|
+
ok: false,
|
|
154
|
+
toolCount: 0,
|
|
155
|
+
tools: [],
|
|
156
|
+
error: "connection produced no result",
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
dispose: () => manager.dispose(),
|
|
160
|
+
};
|
|
161
|
+
}, {
|
|
162
|
+
catalogCache: this.catalogCache,
|
|
163
|
+
modernProtocol: this.modernProtocol,
|
|
164
|
+
onElicit: this.onElicit,
|
|
165
|
+
});
|
|
166
|
+
if (!handle.result.ok) {
|
|
167
|
+
// Nothing was claimed on a failed connect, but releasing is idempotent
|
|
168
|
+
// and keeps this path honest if that ever changes.
|
|
169
|
+
await handle.release();
|
|
170
|
+
throw new Error(handle.result.error ?? "connection failed");
|
|
171
|
+
}
|
|
172
|
+
this.sharedHandles.set(config.name, handle);
|
|
173
|
+
// Wrap each pooled tool so a call from THIS session is attributable while
|
|
174
|
+
// it runs. The pool needs that to answer "which window asked?" when the
|
|
175
|
+
// server elicits mid-call; without it a shared server could only ever
|
|
176
|
+
// cancel its prompts.
|
|
177
|
+
return handle.result.tools.map((tool) => ({
|
|
178
|
+
...tool,
|
|
179
|
+
execute: async (args, context) => {
|
|
180
|
+
const endCall = handle.beginCall();
|
|
181
|
+
try {
|
|
182
|
+
return await tool.execute(args, context);
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
endCall();
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
/** Get-or-create the settlement record for one server name. */
|
|
191
|
+
connectionSlot(name) {
|
|
192
|
+
const existing = this.connections.get(name);
|
|
193
|
+
if (existing)
|
|
194
|
+
return existing;
|
|
195
|
+
let settle;
|
|
196
|
+
const promise = new Promise((resolve) => {
|
|
197
|
+
settle = resolve;
|
|
198
|
+
});
|
|
199
|
+
const slot = { promise, settle };
|
|
200
|
+
this.connections.set(name, slot);
|
|
201
|
+
return slot;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resolve once a server's connection attempt has settled. Returns `ok:false`
|
|
205
|
+
* with the failure reason when that server could not connect, and times out
|
|
206
|
+
* rather than hanging when no attempt is ever made (e.g. a server that was
|
|
207
|
+
* removed from the config since the catalog cache was written).
|
|
208
|
+
*/
|
|
209
|
+
async whenConnected(name, timeoutMs = 30_000) {
|
|
210
|
+
if (this.servers.some((server) => server.name === name))
|
|
211
|
+
return { ok: true };
|
|
212
|
+
const slot = this.connectionSlot(name);
|
|
213
|
+
let timer;
|
|
214
|
+
const timeout = new Promise((resolve) => {
|
|
215
|
+
timer = setTimeout(() => resolve({ ok: false, error: `timed out after ${timeoutMs}ms` }), timeoutMs);
|
|
216
|
+
timer.unref?.();
|
|
217
|
+
});
|
|
218
|
+
try {
|
|
219
|
+
return await Promise.race([slot.promise, timeout]);
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
if (timer)
|
|
223
|
+
clearTimeout(timer);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
16
226
|
async connectAll(configs) {
|
|
17
227
|
const results = await this.connectAllDetailed(configs);
|
|
18
228
|
return results.flatMap((r) => r.tools);
|
|
@@ -21,12 +231,25 @@ export class MCPClientManager {
|
|
|
21
231
|
* Connect every enabled server and return one result per server (success →
|
|
22
232
|
* ok + toolCount; failure → ok:false with a human-readable error string).
|
|
23
233
|
* Keeps successfully connected servers in `this.servers`.
|
|
234
|
+
*
|
|
235
|
+
* Shareable servers — stdio, unless opted out with `shared: false` — are not
|
|
236
|
+
* connected here at all: they are claimed from the process-wide pool, so every
|
|
237
|
+
* session in the daemon multiplexes over one child process instead of spawning
|
|
238
|
+
* its own. The split is invisible to callers — results and tools come back in
|
|
239
|
+
* the same shape, `whenConnected` settles for both kinds, and `dispose`
|
|
240
|
+
* releases the pooled claims — so sharing applies wherever a manager is used
|
|
241
|
+
* (sessions, CLI, subagents) without each call site opting in.
|
|
24
242
|
*/
|
|
25
243
|
async connectAllDetailed(configs) {
|
|
26
244
|
const enabled = configs.filter((c) => c.enabled !== false);
|
|
27
245
|
if (enabled.length === 0)
|
|
28
246
|
return [];
|
|
29
|
-
|
|
247
|
+
// Claim a settlement slot for every server up front, so a `whenConnected`
|
|
248
|
+
// caller that arrives before a slow server's turn still waits on the right
|
|
249
|
+
// promise instead of creating a second one.
|
|
250
|
+
for (const config of enabled)
|
|
251
|
+
this.connectionSlot(config.name);
|
|
252
|
+
const settled = await Promise.allSettled(enabled.map((c) => (isShareableServer(c) ? this.connectShared(c) : this.connectServer(c))));
|
|
30
253
|
const results = settled.map((result, i) => {
|
|
31
254
|
const name = enabled[i].name;
|
|
32
255
|
if (result.status === "fulfilled") {
|
|
@@ -37,6 +260,10 @@ export class MCPClientManager {
|
|
|
37
260
|
log("WARN", "mcp", `Failed to connect to MCP server "${name}"`, { error });
|
|
38
261
|
return { name, ok: false, toolCount: 0, tools: [], error, requiresAuth };
|
|
39
262
|
});
|
|
263
|
+
// Release anyone waiting on a cached-only tool from these servers.
|
|
264
|
+
for (const result of results) {
|
|
265
|
+
this.connectionSlot(result.name).settle(result.ok ? { ok: true } : { ok: false, error: result.error ?? "connection failed" });
|
|
266
|
+
}
|
|
40
267
|
const connected = results.filter((r) => r.ok).length;
|
|
41
268
|
const toolCount = results.reduce((sum, r) => sum + r.toolCount, 0);
|
|
42
269
|
log("INFO", "mcp", `Connected ${connected} MCP server(s), ${toolCount} tool(s)`);
|
|
@@ -49,7 +276,7 @@ export class MCPClientManager {
|
|
|
49
276
|
*/
|
|
50
277
|
async probe(config) {
|
|
51
278
|
try {
|
|
52
|
-
const tools = await this.connectServer(config);
|
|
279
|
+
const tools = await this.connectServer(config, { probe: true });
|
|
53
280
|
const server = this.servers.find((s) => s.name === config.name);
|
|
54
281
|
if (server) {
|
|
55
282
|
this.servers = this.servers.filter((s) => s !== server);
|
|
@@ -88,6 +315,9 @@ export class MCPClientManager {
|
|
|
88
315
|
// Fresh PKCE/state for this attempt so a previous half-finished login can't
|
|
89
316
|
// poison the exchange.
|
|
90
317
|
await store.patch(config.name, { codeVerifier: undefined, state: undefined });
|
|
318
|
+
// The whole callback query string, not just `code`: v2's finishAuth reads
|
|
319
|
+
// `iss` from it and validates the issuer (RFC 9207) before redeeming the
|
|
320
|
+
// code, which is the defense against an authorization-server mix-up.
|
|
91
321
|
let codeResolve;
|
|
92
322
|
let codeReject;
|
|
93
323
|
const codePromise = new Promise((resolve, reject) => {
|
|
@@ -129,7 +359,7 @@ export class MCPClientManager {
|
|
|
129
359
|
return;
|
|
130
360
|
}
|
|
131
361
|
res.end("<html><body><h1>Login successful!</h1><p>You can close this tab and return to EZ Coder.</p></body></html>");
|
|
132
|
-
codeResolve?.(
|
|
362
|
+
codeResolve?.(reqUrl.searchParams);
|
|
133
363
|
});
|
|
134
364
|
try {
|
|
135
365
|
await new Promise((resolve, reject) => {
|
|
@@ -167,9 +397,9 @@ export class MCPClientManager {
|
|
|
167
397
|
if (!isUnauthorized(err))
|
|
168
398
|
throw err;
|
|
169
399
|
}
|
|
170
|
-
const
|
|
400
|
+
const callbackParams = await codePromise;
|
|
171
401
|
// Exchange the code for tokens (persisted via provider.saveTokens).
|
|
172
|
-
await loginTransport.finishAuth(
|
|
402
|
+
await loginTransport.finishAuth(callbackParams);
|
|
173
403
|
await loginClient.close().catch(() => { });
|
|
174
404
|
// Verify the authorized connection on a fresh transport + list tools.
|
|
175
405
|
const verifyTransport = new StreamableHTTPClientTransport(url, {
|
|
@@ -192,8 +422,20 @@ export class MCPClientManager {
|
|
|
192
422
|
await store.patch(config.name, { codeVerifier: undefined, state: undefined });
|
|
193
423
|
}
|
|
194
424
|
}
|
|
195
|
-
async connectServer(config) {
|
|
425
|
+
async connectServer(config, opts = {}) {
|
|
196
426
|
const timeout = config.timeout ?? 30_000;
|
|
427
|
+
// `command` is what actually selects the stdio path further down.
|
|
428
|
+
const negotiate = opts.probe
|
|
429
|
+
? {}
|
|
430
|
+
: this.negotiationOptions(config.command ? "stdio" : "http");
|
|
431
|
+
// A server we already know speaks the 2025 handshake skips the discovery
|
|
432
|
+
// probe entirely. Only the legacy verdict is reusable from an era string:
|
|
433
|
+
// adopting a 'modern' verdict needs the full DiscoverResult, so those still
|
|
434
|
+
// probe (a modern server answers immediately, so that costs nothing).
|
|
435
|
+
const prior = "versionNegotiation" in negotiate && (await this.cachedEra(config)) === "legacy"
|
|
436
|
+
? { kind: "legacy" }
|
|
437
|
+
: undefined;
|
|
438
|
+
const connectOptions = { timeout, ...(prior ? { prior } : {}) };
|
|
197
439
|
let client;
|
|
198
440
|
let transport;
|
|
199
441
|
if (config.command) {
|
|
@@ -226,9 +468,9 @@ export class MCPClientManager {
|
|
|
226
468
|
transport.stderr?.on("data", (chunk) => {
|
|
227
469
|
stderrChunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
228
470
|
});
|
|
229
|
-
client =
|
|
471
|
+
client = this.createClient(config.name, negotiate);
|
|
230
472
|
try {
|
|
231
|
-
await client.connect(transport,
|
|
473
|
+
await client.connect(transport, connectOptions);
|
|
232
474
|
}
|
|
233
475
|
catch (err) {
|
|
234
476
|
const stderr = stderrChunks.join("").slice(-4000);
|
|
@@ -244,7 +486,7 @@ export class MCPClientManager {
|
|
|
244
486
|
const url = new URL(config.url);
|
|
245
487
|
const isLocal = isLocalhost(url);
|
|
246
488
|
try {
|
|
247
|
-
const r = await this.connectHttp(url, config, isLocal,
|
|
489
|
+
const r = await this.connectHttp(url, config, isLocal, negotiate, connectOptions);
|
|
248
490
|
client = r.client;
|
|
249
491
|
transport = r.transport;
|
|
250
492
|
}
|
|
@@ -261,13 +503,40 @@ export class MCPClientManager {
|
|
|
261
503
|
});
|
|
262
504
|
const altUrl = new URL(url);
|
|
263
505
|
altUrl.hostname = alt;
|
|
264
|
-
const r = await this.connectHttp(altUrl, config, isLocal,
|
|
506
|
+
const r = await this.connectHttp(altUrl, config, isLocal, negotiate, connectOptions);
|
|
265
507
|
client = r.client;
|
|
266
508
|
transport = r.transport;
|
|
267
509
|
}
|
|
268
510
|
}
|
|
269
|
-
this.servers.push({ name: config.name, client, transport, lastCallTime: 0 });
|
|
511
|
+
this.servers.push({ name: config.name, client, transport, lastCallTime: 0, config });
|
|
512
|
+
// Report an unexpected close. A stdio child that crashes has no recovery
|
|
513
|
+
// path (`canRecoverSession` excludes stdio deliberately: respawning one
|
|
514
|
+
// mid-call would be a surprise), so the only safe response is to let the
|
|
515
|
+
// owner drop this connection and rebuild on the next use. That matters most
|
|
516
|
+
// for a POOLED connection, where a corpse would otherwise be handed to every
|
|
517
|
+
// session in the daemon, including ones that connect later.
|
|
518
|
+
client.onclose = () => {
|
|
519
|
+
if (this.disposing)
|
|
520
|
+
return;
|
|
521
|
+
log("WARN", "mcp", `MCP server "${config.name}" closed unexpectedly`);
|
|
522
|
+
this.onServerClosed?.(config.name);
|
|
523
|
+
};
|
|
270
524
|
const { tools } = await client.listTools(undefined, { timeout });
|
|
525
|
+
// Persist the live tool list so the NEXT cold start can answer tool_search
|
|
526
|
+
// before this server has finished connecting. Awaited (a small serialized
|
|
527
|
+
// JSON write) so the entry is on disk before the connect is reported done —
|
|
528
|
+
// otherwise a session that starts right after would still see a cold cache.
|
|
529
|
+
// A failed write must never fail the connect.
|
|
530
|
+
try {
|
|
531
|
+
await this.catalogCache.save(config, tools.map((tool) => ({
|
|
532
|
+
name: `mcp__${config.name}__${tool.name}`,
|
|
533
|
+
description: tool.description ?? "",
|
|
534
|
+
rawInputSchema: tool.inputSchema,
|
|
535
|
+
})), this.negotiatedEra(client));
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
// Cache is an optimization; a connected server is still fully usable.
|
|
539
|
+
}
|
|
271
540
|
return tools.map((tool) => {
|
|
272
541
|
const toolName = `mcp__${config.name}__${tool.name}`;
|
|
273
542
|
return {
|
|
@@ -275,7 +544,7 @@ export class MCPClientManager {
|
|
|
275
544
|
description: tool.description ?? "",
|
|
276
545
|
parameters: z.record(z.string(), z.unknown()),
|
|
277
546
|
rawInputSchema: tool.inputSchema,
|
|
278
|
-
execute: async (args) => {
|
|
547
|
+
execute: async (args, context) => {
|
|
279
548
|
const server = this.servers.find((s) => s.name === config.name);
|
|
280
549
|
if (server) {
|
|
281
550
|
const elapsed = Date.now() - server.lastCallTime;
|
|
@@ -285,23 +554,37 @@ export class MCPClientManager {
|
|
|
285
554
|
}
|
|
286
555
|
server.lastCallTime = Date.now();
|
|
287
556
|
}
|
|
288
|
-
|
|
289
|
-
|
|
557
|
+
// Resolve the client from `this.servers` on every attempt rather than
|
|
558
|
+
// closing over the one from connect time: a session rebuild swaps the
|
|
559
|
+
// entry, and a stale capture would keep calling the dead client.
|
|
560
|
+
const liveClient = () => this.servers.find((s) => s.name === config.name)?.client ?? client;
|
|
561
|
+
// The client the attempt actually ran against, so a failure can be
|
|
562
|
+
// attributed to "my client was replaced" vs "the server hung up".
|
|
563
|
+
let attemptClient = liveClient();
|
|
564
|
+
const callOnce = async () => {
|
|
565
|
+
attemptClient = liveClient();
|
|
566
|
+
const result = await attemptClient.callTool({ name: tool.name, arguments: args }, { timeout: config.timeout ?? 60_000 });
|
|
290
567
|
if (!("content" in result) || !Array.isArray(result.content)) {
|
|
291
568
|
return "(empty response)";
|
|
292
569
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
"text" in item &&
|
|
298
|
-
typeof item.text === "string") {
|
|
299
|
-
texts.push(item.text);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
return texts.join("\n") || "(empty response)";
|
|
570
|
+
return toToolResult(result.content, toolName);
|
|
571
|
+
};
|
|
572
|
+
try {
|
|
573
|
+
return await callOnce();
|
|
303
574
|
}
|
|
304
575
|
catch (err) {
|
|
576
|
+
// An expired HTTP session is recoverable exactly once: rebuild the
|
|
577
|
+
// connection and replay the call. A second failure is a real error.
|
|
578
|
+
if (this.canRecoverSession(config, err, attemptClient, context?.signal)) {
|
|
579
|
+
try {
|
|
580
|
+
await this.reconnectServer(config);
|
|
581
|
+
if (!context?.signal?.aborted)
|
|
582
|
+
return await callOnce();
|
|
583
|
+
}
|
|
584
|
+
catch (retryErr) {
|
|
585
|
+
return `MCP tool error: ${formatConnectError(retryErr)}`;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
305
588
|
const msg = err instanceof Error ? err.message : String(err);
|
|
306
589
|
if (msg.includes("Too Many R") || msg.includes("429")) {
|
|
307
590
|
return "Rate limited — too many requests. Wait a moment before searching again.";
|
|
@@ -312,6 +595,66 @@ export class MCPClientManager {
|
|
|
312
595
|
};
|
|
313
596
|
});
|
|
314
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Is this failure a recoverable expired HTTP session?
|
|
600
|
+
*
|
|
601
|
+
* Stdio servers are excluded: they have no HTTP session to expire, so a 404
|
|
602
|
+
* from one means something else entirely and respawning the child process
|
|
603
|
+
* mid-call would be a surprise. An already-aborted call is excluded too —
|
|
604
|
+
* reconnecting to replay work the user cancelled resurrects it.
|
|
605
|
+
*/
|
|
606
|
+
canRecoverSession(config, err, attemptClient, signal) {
|
|
607
|
+
if (config.command)
|
|
608
|
+
return false;
|
|
609
|
+
if (signal?.aborted)
|
|
610
|
+
return false;
|
|
611
|
+
if (isSessionExpired(err))
|
|
612
|
+
return true;
|
|
613
|
+
// A sibling call that hit the same expired session may already have torn
|
|
614
|
+
// the transport down underneath us, which surfaces as a generic "connection
|
|
615
|
+
// closed" rather than the 404. Recover only when the client we called has
|
|
616
|
+
// genuinely been retired — otherwise this is a real disconnect and a retry
|
|
617
|
+
// would just fail again.
|
|
618
|
+
const current = this.servers.find((s) => s.name === config.name)?.client;
|
|
619
|
+
return current !== attemptClient && this.isConnectionClosed(err);
|
|
620
|
+
}
|
|
621
|
+
/** A local "the transport went away mid-request" failure, as the SDK reports it. */
|
|
622
|
+
isConnectionClosed(reason) {
|
|
623
|
+
return SdkError.isInstance(reason) && reason.code === SdkErrorCode.ConnectionClosed;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Rebuild one server's connection from its stored config, replacing the dead
|
|
627
|
+
* entry in `this.servers`. Concurrent callers share a single rebuild.
|
|
628
|
+
*
|
|
629
|
+
* Rejects if the reconnect itself fails, so the caller reports a real error
|
|
630
|
+
* rather than retrying against a client that was never replaced.
|
|
631
|
+
*/
|
|
632
|
+
async reconnectServer(config) {
|
|
633
|
+
const inFlight = this.reconnecting.get(config.name);
|
|
634
|
+
if (inFlight)
|
|
635
|
+
return inFlight;
|
|
636
|
+
const attempt = (async () => {
|
|
637
|
+
log("INFO", "mcp", `MCP session expired for "${config.name}", reconnecting`);
|
|
638
|
+
const dead = this.servers.find((s) => s.name === config.name);
|
|
639
|
+
if (dead) {
|
|
640
|
+
this.servers = this.servers.filter((s) => s !== dead);
|
|
641
|
+
try {
|
|
642
|
+
await dead.client.close();
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
// The session is already gone server-side; a failed close is expected.
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
await this.connectServer(config);
|
|
649
|
+
})();
|
|
650
|
+
this.reconnecting.set(config.name, attempt);
|
|
651
|
+
try {
|
|
652
|
+
await attempt;
|
|
653
|
+
}
|
|
654
|
+
finally {
|
|
655
|
+
this.reconnecting.delete(config.name);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
315
658
|
/**
|
|
316
659
|
* Connect a single HTTP (Streamable HTTP or SSE) server and return the live
|
|
317
660
|
* client + transport. Transport selection:
|
|
@@ -322,7 +665,7 @@ export class MCPClientManager {
|
|
|
322
665
|
* needs OAuth and attaching it there is dead weight that can misdiagnose a
|
|
323
666
|
* protocol mismatch as a login requirement.
|
|
324
667
|
*/
|
|
325
|
-
async connectHttp(url, config, isLocal,
|
|
668
|
+
async connectHttp(url, config, isLocal, negotiate, connectOptions) {
|
|
326
669
|
const reqInit = config.headers ? { headers: config.headers } : undefined;
|
|
327
670
|
const authProvider = isLocal ? undefined : new McpOAuthProvider({ serverName: config.name });
|
|
328
671
|
const sseTransport = () => new SSEClientTransport(url, {
|
|
@@ -332,8 +675,8 @@ export class MCPClientManager {
|
|
|
332
675
|
});
|
|
333
676
|
if (config.transport === "sse") {
|
|
334
677
|
const transport = sseTransport();
|
|
335
|
-
const client =
|
|
336
|
-
await client.connect(transport,
|
|
678
|
+
const client = this.createClient(config.name, negotiate);
|
|
679
|
+
await client.connect(transport, connectOptions);
|
|
337
680
|
return { client, transport };
|
|
338
681
|
}
|
|
339
682
|
try {
|
|
@@ -341,8 +684,8 @@ export class MCPClientManager {
|
|
|
341
684
|
requestInit: reqInit,
|
|
342
685
|
authProvider,
|
|
343
686
|
});
|
|
344
|
-
const client =
|
|
345
|
-
await client.connect(transport,
|
|
687
|
+
const client = this.createClient(config.name, negotiate);
|
|
688
|
+
await client.connect(transport, connectOptions);
|
|
346
689
|
return { client, transport };
|
|
347
690
|
}
|
|
348
691
|
catch (streamableErr) {
|
|
@@ -356,12 +699,40 @@ export class MCPClientManager {
|
|
|
356
699
|
error: String(streamableErr),
|
|
357
700
|
});
|
|
358
701
|
const transport = sseTransport();
|
|
359
|
-
const client =
|
|
360
|
-
await client.connect(transport,
|
|
702
|
+
const client = this.createClient(config.name, negotiate);
|
|
703
|
+
await client.connect(transport, connectOptions);
|
|
361
704
|
return { client, transport };
|
|
362
705
|
}
|
|
363
706
|
}
|
|
707
|
+
/**
|
|
708
|
+
* Protocol era actually negotiated with a server, as the SDK reports it.
|
|
709
|
+
* Undefined before a connect completes; with negotiation off the SDK always
|
|
710
|
+
* settles on `legacy` (the 2025 `initialize` handshake).
|
|
711
|
+
*/
|
|
712
|
+
negotiatedEra(client) {
|
|
713
|
+
return client.getProtocolEra() ?? "legacy";
|
|
714
|
+
}
|
|
715
|
+
/** Previously negotiated era for this exact server config, if still cached. */
|
|
716
|
+
async cachedEra(config) {
|
|
717
|
+
try {
|
|
718
|
+
return await this.catalogCache.protocolEraFor(config);
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
return undefined;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
364
724
|
async dispose() {
|
|
725
|
+
// Our own teardown closes transports too; flag it so those closes are not
|
|
726
|
+
// reported as unexpected deaths.
|
|
727
|
+
this.disposing = true;
|
|
728
|
+
this.connections.clear();
|
|
729
|
+
this.reconnecting.clear();
|
|
730
|
+
// Hand back pooled claims first. These are references, not connections —
|
|
731
|
+
// the shared child survives if another session still holds it, and exits
|
|
732
|
+
// when this was the last claim.
|
|
733
|
+
const handles = [...this.sharedHandles.values()];
|
|
734
|
+
this.sharedHandles.clear();
|
|
735
|
+
await Promise.all(handles.map((handle) => handle.release()));
|
|
365
736
|
for (const server of this.servers) {
|
|
366
737
|
try {
|
|
367
738
|
await server.client.close();
|
|
@@ -374,25 +745,81 @@ export class MCPClientManager {
|
|
|
374
745
|
}
|
|
375
746
|
}
|
|
376
747
|
/**
|
|
377
|
-
* Turn a thrown connection error into a short human-readable string.
|
|
378
|
-
*
|
|
748
|
+
* Turn a thrown connection error into a short human-readable string.
|
|
749
|
+
*
|
|
750
|
+
* v2 splits the old `McpError` into `ProtocolError` (a JSON-RPC error that
|
|
751
|
+
* crossed the wire) and `SdkError` (a purely local failure: timeout, closed
|
|
752
|
+
* connection, HTTP status). Matching the typed classes gives far better wording
|
|
753
|
+
* than sniffing message text, which survives only as the fallback for errors
|
|
754
|
+
* thrown before the SDK's machinery engages (raw fetch failures).
|
|
379
755
|
*/
|
|
380
756
|
function formatConnectError(reason) {
|
|
757
|
+
if (SdkHttpError.isInstance(reason)) {
|
|
758
|
+
if (reason.status === 429)
|
|
759
|
+
return "Rate limited (429) — try again in a moment.";
|
|
760
|
+
const statusText = reason.statusText ? ` ${reason.statusText}` : "";
|
|
761
|
+
return `Server responded with HTTP ${reason.status}${statusText}.`;
|
|
762
|
+
}
|
|
763
|
+
if (SdkError.isInstance(reason)) {
|
|
764
|
+
if (reason.code === SdkErrorCode.RequestTimeout) {
|
|
765
|
+
return "Timed out waiting for the server to respond.";
|
|
766
|
+
}
|
|
767
|
+
if (reason.code === SdkErrorCode.ConnectionClosed) {
|
|
768
|
+
return "Connection closed before the server finished starting up.";
|
|
769
|
+
}
|
|
770
|
+
return reason.message;
|
|
771
|
+
}
|
|
772
|
+
if (OAuthError.isInstance(reason))
|
|
773
|
+
return `OAuth error (${reason.code}): ${reason.message}`;
|
|
774
|
+
if (ProtocolError.isInstance(reason))
|
|
775
|
+
return reason.message;
|
|
381
776
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
382
777
|
if (msg.includes("Too Many R") || msg.includes("429")) {
|
|
383
778
|
return "Rate limited (429) — try again in a moment.";
|
|
384
779
|
}
|
|
385
780
|
return msg;
|
|
386
781
|
}
|
|
782
|
+
/**
|
|
783
|
+
* How long to wait for a stdio server to answer `server/discover` before
|
|
784
|
+
* concluding it is a 2025-era server. A local child that has not replied in
|
|
785
|
+
* this window is not going to; the SDK then falls back to `initialize`.
|
|
786
|
+
*/
|
|
787
|
+
const STDIO_PROBE_TIMEOUT_MS = 2_500;
|
|
788
|
+
/** OAuth failures that mean the saved credentials can no longer be used. */
|
|
789
|
+
const REAUTH_OAUTH_CODES = [
|
|
790
|
+
OAuthErrorCode.InvalidToken,
|
|
791
|
+
OAuthErrorCode.InvalidGrant,
|
|
792
|
+
OAuthErrorCode.InvalidClient,
|
|
793
|
+
OAuthErrorCode.AccessDenied,
|
|
794
|
+
];
|
|
795
|
+
/**
|
|
796
|
+
* Whether an error means the remote server has dropped our HTTP session.
|
|
797
|
+
*
|
|
798
|
+
* `StreamableHTTPClientTransport.send` handles 401/403 (it re-runs auth) but
|
|
799
|
+
* returns a bare `SdkHttpError{status:404}` when the `Mcp-Session-Id` we are
|
|
800
|
+
* carrying is no longer known to the server — which is exactly what happens
|
|
801
|
+
* after a server restart or an idle-session sweep on a long desktop session.
|
|
802
|
+
* The SDK has no recovery for it, so we rebuild the connection ourselves.
|
|
803
|
+
*/
|
|
804
|
+
function isSessionExpired(reason) {
|
|
805
|
+
return SdkHttpError.isInstance(reason) && reason.status === 404;
|
|
806
|
+
}
|
|
387
807
|
/**
|
|
388
808
|
* Whether a connect error means the server needs OAuth login. The SDK throws
|
|
389
|
-
* its typed `UnauthorizedError` when auth is required,
|
|
390
|
-
*
|
|
391
|
-
*
|
|
809
|
+
* its typed `UnauthorizedError` when auth is required, a `SdkHttpError` for a
|
|
810
|
+
* bare 401, and an `OAuthError` when the token endpoint rejects the saved
|
|
811
|
+
* credentials. A raw 401 from before the auth machinery engages can still
|
|
812
|
+
* surface as a plain message — so the string check remains as a fallback.
|
|
392
813
|
*/
|
|
393
814
|
function isUnauthorized(reason) {
|
|
394
|
-
if (reason
|
|
815
|
+
if (UnauthorizedError.isInstance(reason))
|
|
395
816
|
return true;
|
|
817
|
+
if (SdkHttpError.isInstance(reason))
|
|
818
|
+
return reason.status === 401;
|
|
819
|
+
// Token/grant/client codes mean "log in again"; the rest (server_error,
|
|
820
|
+
// temporarily_unavailable, …) are transient, not an auth gap.
|
|
821
|
+
if (OAuthError.isInstance(reason))
|
|
822
|
+
return REAUTH_OAUTH_CODES.includes(reason.code);
|
|
396
823
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
397
824
|
return (msg.includes("Unauthorized") ||
|
|
398
825
|
msg.includes("401") ||
|
|
@@ -410,12 +837,18 @@ function escapeHtml(value) {
|
|
|
410
837
|
* Create a custom fetch wrapper that injects extra headers into every request.
|
|
411
838
|
* Used for SSEClientTransport's eventSourceInit to pass auth headers
|
|
412
839
|
* on the initial SSE GET connection (which doesn't use requestInit).
|
|
840
|
+
*
|
|
841
|
+
* The incoming headers may be a Web Standard `Headers` instance (v2 uses it
|
|
842
|
+
* throughout) or a plain object. Spreading a `Headers` yields `{}`, silently
|
|
843
|
+
* dropping the SDK's own auth header — so normalize through `Headers`, which
|
|
844
|
+
* accepts both shapes, and merge with `set()` rather than bracket access.
|
|
413
845
|
*/
|
|
414
846
|
function createHeaderFetch(extraHeaders) {
|
|
415
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
416
847
|
return (url, init) => {
|
|
417
|
-
const
|
|
418
|
-
|
|
848
|
+
const headers = new Headers(init?.headers);
|
|
849
|
+
for (const [name, value] of Object.entries(extraHeaders))
|
|
850
|
+
headers.set(name, value);
|
|
851
|
+
return fetch(url, { ...init, headers });
|
|
419
852
|
};
|
|
420
853
|
}
|
|
421
854
|
//# sourceMappingURL=client.js.map
|