open-multi-agent-kit 1.2.0 → 1.2.3
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/CHANGELOG.md +54 -0
- package/README.md +2 -2
- package/dist/approval-api.d.ts +5 -0
- package/dist/approval-api.d.ts.map +1 -0
- package/dist/approval-api.js +5 -0
- package/dist/approval-api.js.map +1 -0
- package/dist/cli/session-picker.d.ts +2 -2
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/commands/verified-run-cli.d.ts.map +1 -1
- package/dist/commands/verified-run-cli.js +69 -22
- package/dist/commands/verified-run-cli.js.map +1 -1
- package/dist/commands/verified-run-signal.d.ts +3 -0
- package/dist/commands/verified-run-signal.d.ts.map +1 -0
- package/dist/commands/verified-run-signal.js +15 -0
- package/dist/commands/verified-run-signal.js.map +1 -0
- package/dist/coordination/broker.d.ts.map +1 -1
- package/dist/coordination/broker.js +13 -6
- package/dist/coordination/broker.js.map +1 -1
- package/dist/coordination/operation.d.ts +1 -0
- package/dist/coordination/operation.d.ts.map +1 -1
- package/dist/coordination/operation.js +55 -3
- package/dist/coordination/operation.js.map +1 -1
- package/dist/coordination/resource.d.ts +1 -1
- package/dist/coordination/resource.d.ts.map +1 -1
- package/dist/coordination/resource.js +10 -4
- package/dist/coordination/resource.js.map +1 -1
- package/dist/coordination/types.d.ts.map +1 -1
- package/dist/coordination/types.js +1 -1
- package/dist/coordination/types.js.map +1 -1
- package/dist/core/adaptorch-bridge.d.ts +1 -1
- package/dist/core/adaptorch-bridge.d.ts.map +1 -1
- package/dist/core/adaptorch-bridge.js +1 -1
- package/dist/core/adaptorch-bridge.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +16 -16
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +32 -33
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/canonical-json.d.ts +3 -0
- package/dist/core/canonical-json.d.ts.map +1 -0
- package/dist/core/canonical-json.js +20 -0
- package/dist/core/canonical-json.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +7 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +14 -29
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/fallback.d.ts +16 -5
- package/dist/core/compaction/fallback.d.ts.map +1 -1
- package/dist/core/compaction/fallback.js +4 -3
- package/dist/core/compaction/fallback.js.map +1 -1
- package/dist/core/compaction/model-failover.d.ts +21 -0
- package/dist/core/compaction/model-failover.d.ts.map +1 -0
- package/dist/core/compaction/model-failover.js +46 -0
- package/dist/core/compaction/model-failover.js.map +1 -0
- package/dist/core/compaction/session-summary.d.ts +21 -0
- package/dist/core/compaction/session-summary.d.ts.map +1 -0
- package/dist/core/compaction/session-summary.js +27 -0
- package/dist/core/compaction/session-summary.js.map +1 -0
- package/dist/core/export-html/template.js +14 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/session-lifecycle-types.d.ts +14 -0
- package/dist/core/extensions/session-lifecycle-types.d.ts.map +1 -0
- package/dist/core/extensions/session-lifecycle-types.js +2 -0
- package/dist/core/extensions/session-lifecycle-types.js.map +1 -0
- package/dist/core/extensions/types.d.ts +4 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +10 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -6
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/legacy-resource-policy.d.ts +7 -0
- package/dist/core/legacy-resource-policy.d.ts.map +1 -0
- package/dist/core/legacy-resource-policy.js +9 -0
- package/dist/core/legacy-resource-policy.js.map +1 -0
- package/dist/core/mcp/config.d.ts.map +1 -1
- package/dist/core/mcp/config.js +17 -3
- package/dist/core/mcp/config.js.map +1 -1
- package/dist/core/mcp/connection-queue.d.ts +12 -0
- package/dist/core/mcp/connection-queue.d.ts.map +1 -0
- package/dist/core/mcp/connection-queue.js +46 -0
- package/dist/core/mcp/connection-queue.js.map +1 -0
- package/dist/core/mcp/manager-runtime.d.ts +55 -0
- package/dist/core/mcp/manager-runtime.d.ts.map +1 -0
- package/dist/core/mcp/manager-runtime.js +66 -0
- package/dist/core/mcp/manager-runtime.js.map +1 -0
- package/dist/core/mcp/manager.d.ts +12 -68
- package/dist/core/mcp/manager.d.ts.map +1 -1
- package/dist/core/mcp/manager.js +33 -113
- package/dist/core/mcp/manager.js.map +1 -1
- package/dist/core/mcp/protocol.d.ts.map +1 -1
- package/dist/core/mcp/protocol.js +70 -40
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +59 -129
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/package-resource-patterns.d.ts +7 -0
- package/dist/core/package-resource-patterns.d.ts.map +1 -0
- package/dist/core/package-resource-patterns.js +99 -0
- package/dist/core/package-resource-patterns.js.map +1 -0
- package/dist/core/provider-resilience.d.ts +9 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +15 -0
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts +5 -2
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +4 -8
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/run-execution-api.d.ts +8 -1
- package/dist/core/run-execution-api.d.ts.map +1 -1
- package/dist/core/run-execution-api.js +4 -0
- package/dist/core/run-execution-api.js.map +1 -1
- package/dist/core/run-journal.d.ts +1 -2
- package/dist/core/run-journal.d.ts.map +1 -1
- package/dist/core/run-journal.js +2 -19
- package/dist/core/run-journal.js.map +1 -1
- package/dist/core/session-listing.d.ts +51 -0
- package/dist/core/session-listing.d.ts.map +1 -0
- package/dist/core/session-listing.js +111 -0
- package/dist/core/session-listing.js.map +1 -0
- package/dist/core/session-manager.d.ts +12 -37
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +12 -118
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-message-text.d.ts +5 -0
- package/dist/core/session-message-text.d.ts.map +1 -0
- package/dist/core/session-message-text.js +9 -0
- package/dist/core/session-message-text.js.map +1 -0
- package/dist/core/skills-catalog-cache.d.ts +2 -0
- package/dist/core/skills-catalog-cache.d.ts.map +1 -1
- package/dist/core/skills-catalog-cache.js +71 -17
- package/dist/core/skills-catalog-cache.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/verified-run/authority-clock.d.ts +3 -0
- package/dist/core/verified-run/authority-clock.d.ts.map +1 -0
- package/dist/core/verified-run/authority-clock.js +25 -0
- package/dist/core/verified-run/authority-clock.js.map +1 -0
- package/dist/core/verified-run/authority-errors.d.ts +10 -0
- package/dist/core/verified-run/authority-errors.d.ts.map +1 -0
- package/dist/core/verified-run/authority-errors.js +17 -0
- package/dist/core/verified-run/authority-errors.js.map +1 -0
- package/dist/core/verified-run/authority-events.d.ts +126 -0
- package/dist/core/verified-run/authority-events.d.ts.map +1 -0
- package/dist/core/verified-run/authority-events.js +482 -0
- package/dist/core/verified-run/authority-events.js.map +1 -0
- package/dist/core/verified-run/authority-journal.d.ts +27 -0
- package/dist/core/verified-run/authority-journal.d.ts.map +1 -0
- package/dist/core/verified-run/authority-journal.js +83 -0
- package/dist/core/verified-run/authority-journal.js.map +1 -0
- package/dist/core/verified-run/authority-meaning.d.ts +13 -0
- package/dist/core/verified-run/authority-meaning.d.ts.map +1 -0
- package/dist/core/verified-run/authority-meaning.js +40 -0
- package/dist/core/verified-run/authority-meaning.js.map +1 -0
- package/dist/core/verified-run/authority-runtime.d.ts +82 -0
- package/dist/core/verified-run/authority-runtime.d.ts.map +1 -0
- package/dist/core/verified-run/authority-runtime.js +169 -0
- package/dist/core/verified-run/authority-runtime.js.map +1 -0
- package/dist/core/verified-run/authority-store.d.ts +204 -0
- package/dist/core/verified-run/authority-store.d.ts.map +1 -0
- package/dist/core/verified-run/authority-store.js +852 -0
- package/dist/core/verified-run/authority-store.js.map +1 -0
- package/dist/core/verified-run/broker.d.ts +8 -1
- package/dist/core/verified-run/broker.d.ts.map +1 -1
- package/dist/core/verified-run/broker.js +94 -90
- package/dist/core/verified-run/broker.js.map +1 -1
- package/dist/core/verified-run/candidate-policy.d.ts +10 -0
- package/dist/core/verified-run/candidate-policy.d.ts.map +1 -0
- package/dist/core/verified-run/candidate-policy.js +33 -0
- package/dist/core/verified-run/candidate-policy.js.map +1 -0
- package/dist/core/verified-run/candidate.d.ts.map +1 -1
- package/dist/core/verified-run/candidate.js +5 -10
- package/dist/core/verified-run/candidate.js.map +1 -1
- package/dist/core/verified-run/coordinator.d.ts +35 -17
- package/dist/core/verified-run/coordinator.d.ts.map +1 -1
- package/dist/core/verified-run/coordinator.js +111 -57
- package/dist/core/verified-run/coordinator.js.map +1 -1
- package/dist/core/verified-run/dag-phase.d.ts.map +1 -1
- package/dist/core/verified-run/dag-phase.js +1 -1
- package/dist/core/verified-run/dag-phase.js.map +1 -1
- package/dist/core/verified-run/dag-recovery.d.ts +5 -1
- package/dist/core/verified-run/dag-recovery.d.ts.map +1 -1
- package/dist/core/verified-run/dag-recovery.js +10 -4
- package/dist/core/verified-run/dag-recovery.js.map +1 -1
- package/dist/core/verified-run/event-parser.d.ts.map +1 -1
- package/dist/core/verified-run/event-parser.js +33 -0
- package/dist/core/verified-run/event-parser.js.map +1 -1
- package/dist/core/verified-run/evidence-read-projection.d.ts +4 -0
- package/dist/core/verified-run/evidence-read-projection.d.ts.map +1 -0
- package/dist/core/verified-run/evidence-read-projection.js +27 -0
- package/dist/core/verified-run/evidence-read-projection.js.map +1 -0
- package/dist/core/verified-run/evidence.d.ts +12 -1
- package/dist/core/verified-run/evidence.d.ts.map +1 -1
- package/dist/core/verified-run/evidence.js +51 -2
- package/dist/core/verified-run/evidence.js.map +1 -1
- package/dist/core/verified-run/git-candidate-preflight.d.ts +4 -0
- package/dist/core/verified-run/git-candidate-preflight.d.ts.map +1 -0
- package/dist/core/verified-run/git-candidate-preflight.js +34 -0
- package/dist/core/verified-run/git-candidate-preflight.js.map +1 -0
- package/dist/core/verified-run/git-effect-supervisor.d.ts +24 -0
- package/dist/core/verified-run/git-effect-supervisor.d.ts.map +1 -0
- package/dist/core/verified-run/git-effect-supervisor.js +174 -0
- package/dist/core/verified-run/git-effect-supervisor.js.map +1 -0
- package/dist/core/verified-run/git-execution.d.ts +27 -0
- package/dist/core/verified-run/git-execution.d.ts.map +1 -0
- package/dist/core/verified-run/git-execution.js +118 -0
- package/dist/core/verified-run/git-execution.js.map +1 -0
- package/dist/core/verified-run/git-plumbing.d.ts +37 -0
- package/dist/core/verified-run/git-plumbing.d.ts.map +1 -0
- package/dist/core/verified-run/git-plumbing.js +171 -0
- package/dist/core/verified-run/git-plumbing.js.map +1 -0
- package/dist/core/verified-run/git-publication-worker.d.ts +2 -0
- package/dist/core/verified-run/git-publication-worker.d.ts.map +1 -0
- package/dist/core/verified-run/git-publication-worker.js +54 -0
- package/dist/core/verified-run/git-publication-worker.js.map +1 -0
- package/dist/core/verified-run/git-sandbox-layout.d.ts +4 -0
- package/dist/core/verified-run/git-sandbox-layout.d.ts.map +1 -0
- package/dist/core/verified-run/git-sandbox-layout.js +29 -0
- package/dist/core/verified-run/git-sandbox-layout.js.map +1 -0
- package/dist/core/verified-run/git-worker-protocol.d.ts +19 -0
- package/dist/core/verified-run/git-worker-protocol.d.ts.map +1 -0
- package/dist/core/verified-run/git-worker-protocol.js +109 -0
- package/dist/core/verified-run/git-worker-protocol.js.map +1 -0
- package/dist/core/verified-run/git-worker-runtime.d.ts +19 -0
- package/dist/core/verified-run/git-worker-runtime.d.ts.map +1 -0
- package/dist/core/verified-run/git-worker-runtime.js +74 -0
- package/dist/core/verified-run/git-worker-runtime.js.map +1 -0
- package/dist/core/verified-run/journal.d.ts +2 -3
- package/dist/core/verified-run/journal.d.ts.map +1 -1
- package/dist/core/verified-run/journal.js.map +1 -1
- package/dist/core/verified-run/namespace-identity.d.ts.map +1 -1
- package/dist/core/verified-run/namespace-identity.js +1 -1
- package/dist/core/verified-run/namespace-identity.js.map +1 -1
- package/dist/core/verified-run/owned-execution.d.ts +10 -1
- package/dist/core/verified-run/owned-execution.d.ts.map +1 -1
- package/dist/core/verified-run/owned-execution.js +124 -31
- package/dist/core/verified-run/owned-execution.js.map +1 -1
- package/dist/core/verified-run/phase-context.d.ts +2 -0
- package/dist/core/verified-run/phase-context.d.ts.map +1 -1
- package/dist/core/verified-run/phase-context.js.map +1 -1
- package/dist/core/verified-run/projection.d.ts.map +1 -1
- package/dist/core/verified-run/projection.js +51 -1
- package/dist/core/verified-run/projection.js.map +1 -1
- package/dist/core/verified-run/publish-preflight.d.ts +12 -0
- package/dist/core/verified-run/publish-preflight.d.ts.map +1 -0
- package/dist/core/verified-run/publish-preflight.js +38 -0
- package/dist/core/verified-run/publish-preflight.js.map +1 -0
- package/dist/core/verified-run/publish-start.d.ts +5 -0
- package/dist/core/verified-run/publish-start.d.ts.map +1 -0
- package/dist/core/verified-run/publish-start.js +7 -0
- package/dist/core/verified-run/publish-start.js.map +1 -0
- package/dist/core/verified-run/recovery-projection.d.ts.map +1 -1
- package/dist/core/verified-run/recovery-projection.js +5 -0
- package/dist/core/verified-run/recovery-projection.js.map +1 -1
- package/dist/core/verified-run/recovery.d.ts +5 -1
- package/dist/core/verified-run/recovery.d.ts.map +1 -1
- package/dist/core/verified-run/recovery.js +10 -4
- package/dist/core/verified-run/recovery.js.map +1 -1
- package/dist/core/verified-run/run-plan.d.ts +17 -0
- package/dist/core/verified-run/run-plan.d.ts.map +1 -0
- package/dist/core/verified-run/run-plan.js +18 -0
- package/dist/core/verified-run/run-plan.js.map +1 -0
- package/dist/core/verified-run/run-publish-owned.d.ts +6 -0
- package/dist/core/verified-run/run-publish-owned.d.ts.map +1 -0
- package/dist/core/verified-run/run-publish-owned.js +199 -0
- package/dist/core/verified-run/run-publish-owned.js.map +1 -0
- package/dist/core/verified-run/run-publish.d.ts +19 -0
- package/dist/core/verified-run/run-publish.d.ts.map +1 -0
- package/dist/core/verified-run/run-publish.js +210 -0
- package/dist/core/verified-run/run-publish.js.map +1 -0
- package/dist/core/verified-run/run-status-owned.d.ts +4 -0
- package/dist/core/verified-run/run-status-owned.d.ts.map +1 -0
- package/dist/core/verified-run/run-status-owned.js +14 -0
- package/dist/core/verified-run/run-status-owned.js.map +1 -0
- package/dist/core/verified-run/run-status.d.ts +133 -0
- package/dist/core/verified-run/run-status.d.ts.map +1 -0
- package/dist/core/verified-run/run-status.js +193 -0
- package/dist/core/verified-run/run-status.js.map +1 -0
- package/dist/core/verified-run/run-types.d.ts +31 -0
- package/dist/core/verified-run/run-types.d.ts.map +1 -1
- package/dist/core/verified-run/run-types.js.map +1 -1
- package/dist/core/verified-run/storage.d.ts.map +1 -1
- package/dist/core/verified-run/storage.js +1 -1
- package/dist/core/verified-run/storage.js.map +1 -1
- package/dist/core/verified-run/supervisor-adapter.d.ts +81 -0
- package/dist/core/verified-run/supervisor-adapter.d.ts.map +1 -0
- package/dist/core/verified-run/supervisor-adapter.js +168 -0
- package/dist/core/verified-run/supervisor-adapter.js.map +1 -0
- package/dist/core/verified-run/verification-phase.d.ts.map +1 -1
- package/dist/core/verified-run/verification-phase.js +1 -1
- package/dist/core/verified-run/verification-phase.js.map +1 -1
- package/dist/core/verified-run/writer-phase.d.ts.map +1 -1
- package/dist/core/verified-run/writer-phase.js +1 -0
- package/dist/core/verified-run/writer-phase.js.map +1 -1
- package/dist/core/verified-run/writer-recovery.d.ts +2 -0
- package/dist/core/verified-run/writer-recovery.d.ts.map +1 -1
- package/dist/core/verified-run/writer-recovery.js +1 -0
- package/dist/core/verified-run/writer-recovery.js.map +1 -1
- package/dist/core/workload-shard-execution-types.d.ts +4 -1
- package/dist/core/workload-shard-execution-types.d.ts.map +1 -1
- package/dist/core/workload-shard-execution-types.js.map +1 -1
- package/dist/core/workload-shard-executor.d.ts +2 -2
- package/dist/core/workload-shard-executor.d.ts.map +1 -1
- package/dist/core/workload-shard-executor.js +16 -37
- package/dist/core/workload-shard-executor.js.map +1 -1
- package/dist/core/workload-shard-frontier.d.ts +5 -0
- package/dist/core/workload-shard-frontier.d.ts.map +1 -0
- package/dist/core/workload-shard-frontier.js +47 -0
- package/dist/core/workload-shard-frontier.js.map +1 -0
- package/dist/core/workload-shard-runner.d.ts.map +1 -1
- package/dist/core/workload-shard-runner.js +28 -17
- package/dist/core/workload-shard-runner.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -1
- package/dist/main.js.map +1 -1
- package/dist/metacognition/bridge-result.d.ts +148 -0
- package/dist/metacognition/bridge-result.d.ts.map +1 -0
- package/dist/metacognition/bridge-result.js +46 -0
- package/dist/metacognition/bridge-result.js.map +1 -0
- package/dist/metacognition/calibration-selective.d.ts.map +1 -1
- package/dist/metacognition/calibration-selective.js +8 -2
- package/dist/metacognition/calibration-selective.js.map +1 -1
- package/dist/metacognition/index.d.ts +1 -0
- package/dist/metacognition/index.d.ts.map +1 -1
- package/dist/metacognition/index.js +1 -0
- package/dist/metacognition/index.js.map +1 -1
- package/dist/metacognition/risk.d.ts.map +1 -1
- package/dist/metacognition/risk.js +28 -14
- package/dist/metacognition/risk.js.map +1 -1
- package/dist/metacognition/runtime-bridge.d.ts +41 -14
- package/dist/metacognition/runtime-bridge.d.ts.map +1 -1
- package/dist/metacognition/runtime-bridge.js +357 -39
- package/dist/metacognition/runtime-bridge.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +42 -26
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-container.d.ts +7 -0
- package/dist/modes/interactive/components/chat-container.d.ts.map +1 -0
- package/dist/modes/interactive/components/chat-container.js +14 -0
- package/dist/modes/interactive/components/chat-container.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +4 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +0 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-async-search.d.ts +33 -0
- package/dist/modes/interactive/components/session-selector-async-search.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-async-search.js +109 -0
- package/dist/modes/interactive/components/session-selector-async-search.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-loaders.d.ts +6 -0
- package/dist/modes/interactive/components/session-selector-loaders.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-loaders.js +9 -0
- package/dist/modes/interactive/components/session-selector-loaders.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-tree.d.ts +16 -0
- package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-tree.js +46 -0
- package/dist/modes/interactive/components/session-selector-tree.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts +6 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +36 -76
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution-images.d.ts +32 -0
- package/dist/modes/interactive/components/tool-execution-images.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution-images.js +144 -0
- package/dist/modes/interactive/components/tool-execution-images.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +3 -4
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +22 -71
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -9
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tui-diagnostics.d.ts +8 -4
- package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -1
- package/dist/modes/interactive/tui-diagnostics.js +35 -1
- package/dist/modes/interactive/tui-diagnostics.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -17
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-response.d.ts +5 -0
- package/dist/modes/rpc/rpc-response.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-response.js +17 -0
- package/dist/modes/rpc/rpc-response.js.map +1 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -5
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/adaptorch-preview-spec.md +7 -7
- package/docs/atomic-commit-planning.md +69 -0
- package/docs/compaction.md +26 -1
- package/docs/extensions.md +14 -0
- package/docs/mcp.md +22 -0
- package/docs/metacognition.md +79 -0
- package/docs/model-catalog-refresh.md +159 -1
- package/docs/models.md +1 -1
- package/docs/runtime-algorithms.md +49 -4
- package/docs/session-format.md +8 -0
- package/docs/sessions.md +19 -0
- package/docs/skills.md +19 -0
- package/docs/terminal-setup.md +15 -0
- package/docs/tui.md +41 -0
- package/docs/verified-run-safety.md +75 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,YAAY,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,wFAAwF;IACxF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,qFAAqF;IACrF,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,SAAS,CAAC;CACjE;AAiBD,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C,YAAY,OAAO,EAAE,iBAAiB,EAMrC;IAED,uDAAuD;IACvD,IAAI,WAAW,IAAI,MAAM,EAAE,CAE1B;IAED,6EAA6E;IAC7E,MAAM,IAAI,eAAe,EAAE,CAS1B;IAED;;;;OAIG;IACG,mBAAmB,IAAI,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,CAY9E;IAED,sEAAsE;IAChE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAcpD;IAED;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI,CAWZ;IAED;;;;;;;;OAQG;IACG,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAa7G;YAEa,WAAW;IAmBzB,OAAO,CAAC,eAAe;YAqBT,cAAc;CAiE5B","sourcesContent":["/**\n * Lazy, crash-isolated MCP server manager.\n *\n * Design constraints that shaped this:\n * - A workspace can configure 25 servers. Spawning them all at startup would\n * trade a 0.6s cold start for a multi-second one, so connection is lazy and\n * only happens when tools are actually requested.\n * - One broken server must never take a session down. Every connect and every\n * listing is isolated; a failure is recorded as server status and the other\n * servers still contribute tools.\n * - Connect work is deduplicated: concurrent `listToolDefinitions()` calls\n * share one in-flight connect per server.\n */\n\nimport type { TSchema } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { detectMcpDescriptorPromptInjection, MCP_QUARANTINE_PATTERN_SIGNAL_THRESHOLD } from \"../mcp-public-presets.ts\";\nimport { McpClient, type McpClientOptions } from \"./client.ts\";\nimport { createMcpToolDefinition, type McpToolDetails } from \"./tools.ts\";\n\nexport type McpServerState = \"idle\" | \"connecting\" | \"ready\" | \"failed\";\n\nexport interface McpServerConfig {\n\treadonly name: string;\n\treadonly command: string;\n\treadonly args?: readonly string[];\n\treadonly env?: Readonly<Record<string, string>>;\n\treadonly cwd?: string;\n\t/** Skip this server without removing it from configuration. */\n\treadonly disabled?: boolean;\n\treadonly requestTimeoutMs?: number;\n\treadonly handshakeTimeoutMs?: number;\n}\n\nexport interface McpServerStatus {\n\treadonly name: string;\n\treadonly state: McpServerState;\n\treadonly toolCount: number;\n\t/** Failure reason when `state` is `failed`. Never contains configured env values. */\n\treadonly error?: string;\n\treadonly serverVersion?: string;\n\t/** Tool names excluded because their descriptions carried prompt-injection payloads. */\n\treadonly quarantinedTools?: readonly string[];\n}\n\nexport interface McpManagerOptions {\n\treadonly servers: readonly McpServerConfig[];\n\treadonly cwd?: string;\n\treadonly clientInfo?: { readonly name: string; readonly version: string };\n\treadonly callTimeoutMs?: number;\n\t/** Injected for tests so the manager can be exercised without spawning processes. */\n\treadonly createClient?: (options: McpClientOptions) => McpClient;\n}\n\ninterface ServerRuntime {\n\treadonly config: McpServerConfig;\n\tstate: McpServerState;\n\tclient?: McpClient;\n\ttools: ToolDefinition<TSchema, McpToolDetails>[];\n\t/** Descriptor prompt-injection quarantines (namespaced tool names). */\n\tquarantinedTools: string[];\n\terror?: string;\n\tconnecting?: Promise<void>;\n\t/** Client owned by the in-flight connect attempt; not yet published. */\n\tpendingClient?: McpClient;\n\t/** Monotonic generation; a completed attempt may only publish when current. */\n\tgeneration: number;\n}\n\nexport class McpManager {\n\tprivate readonly runtimes = new Map<string, ServerRuntime>();\n\tprivate readonly options: McpManagerOptions;\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.options = options;\n\t\tfor (const config of options.servers) {\n\t\t\tif (this.runtimes.has(config.name)) continue; // First definition wins; inventory already applies precedence.\n\t\t\tthis.runtimes.set(config.name, { config, state: \"idle\", tools: [], quarantinedTools: [], generation: 0 });\n\t\t}\n\t}\n\n\t/** Configured server names, in configuration order. */\n\tget serverNames(): string[] {\n\t\treturn [...this.runtimes.keys()];\n\t}\n\n\t/** Current status per server. Safe to call before any connection attempt. */\n\tstatus(): McpServerStatus[] {\n\t\treturn [...this.runtimes.values()].map((runtime) => ({\n\t\t\tname: runtime.config.name,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t}));\n\t}\n\n\t/**\n\t * Connect every enabled server that has not been attempted yet, then return\n\t * the union of their tools. Servers are connected concurrently and failures\n\t * are isolated, so a partial result is the normal outcome, not an error.\n\t */\n\tasync listToolDefinitions(): Promise<ToolDefinition<TSchema, McpToolDetails>[]> {\n\t\tawait Promise.all([...this.runtimes.values()].map((runtime) => this.ensureConnected(runtime)));\n\t\tconst tools: ToolDefinition<TSchema, McpToolDetails>[] = [];\n\t\tconst seen = new Set<string>();\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tfor (const tool of runtime.tools) {\n\t\t\t\tif (seen.has(tool.name)) continue; // Namespacing makes this rare; keep it deterministic anyway.\n\t\t\t\tseen.add(tool.name);\n\t\t\t\ttools.push(tool);\n\t\t\t}\n\t\t}\n\t\treturn tools;\n\t}\n\n\t/** Connect a single server by name. Returns its status either way. */\n\tasync connect(name: string): Promise<McpServerStatus> {\n\t\tconst runtime = this.runtimes.get(name);\n\t\tif (!runtime) {\n\t\t\treturn { name, state: \"failed\", toolCount: 0, error: `Unknown MCP server \"${name}\"` };\n\t\t}\n\t\tawait this.ensureConnected(runtime);\n\t\treturn {\n\t\t\tname,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * Terminate every connected or connecting server. Idempotent.\n\t *\n\t * Bumping the generation invalidates any in-flight connect before its late\n\t * completion can publish tools or flip the state back to `ready`, and the\n\t * attempt's not-yet-published client is closed alongside the live one so no\n\t * spawned process outlives the close.\n\t */\n\tclose(): void {\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\truntime.generation += 1;\n\t\t\truntime.client?.close();\n\t\t\truntime.client = undefined;\n\t\t\truntime.pendingClient?.close();\n\t\t\truntime.pendingClient = undefined;\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\tif (runtime.state === \"ready\" || runtime.state === \"connecting\") runtime.state = \"idle\";\n\t\t}\n\t}\n\n\t/**\n\t * Verify that `ready` servers are actually alive with a protocol ping. A\n\t * failed ping closes the client and marks the server `failed`, so a\n\t * silently killed process stops masquerading as connected. Servers already\n\t * `failed` are re-attempted only when `reconnectFailed` is set — callers\n\t * should gate that behind a slow cadence so a permanently broken server is\n\t * not respawned every probe. Idle servers stay idle: connection is lazy by\n\t * design, and a status probe must not spawn processes.\n\t */\n\tasync checkHealth(options?: { pingTimeoutMs?: number; reconnectFailed?: boolean }): Promise<McpServerStatus[]> {\n\t\tconst work: Promise<void>[] = [];\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tif (runtime.state === \"ready\" && runtime.client) {\n\t\t\t\twork.push(this.pingRuntime(runtime, options?.pingTimeoutMs));\n\t\t\t} else if (runtime.state === \"failed\" && options?.reconnectFailed && !runtime.config.disabled) {\n\t\t\t\truntime.state = \"idle\";\n\t\t\t\truntime.error = undefined;\n\t\t\t\twork.push(this.ensureConnected(runtime));\n\t\t\t}\n\t\t}\n\t\tawait Promise.all(work);\n\t\treturn this.status();\n\t}\n\n\tprivate async pingRuntime(runtime: ServerRuntime, timeoutMs?: number): Promise<void> {\n\t\t// Pin the probed client: a close or reconnect during the ping must not let\n\t\t// this failure path dispose the replacement client.\n\t\tconst client = runtime.client;\n\t\tif (!client) return;\n\t\ttry {\n\t\t\tawait client.ping(timeoutMs);\n\t\t} catch (error) {\n\t\t\tif (runtime.client !== client) return; // A newer owner already handled it.\n\t\t\t// Isolation point: only this server transitions; the rest stay ready.\n\t\t\truntime.client = undefined;\n\t\t\tclient.close();\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = `health check failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\t}\n\t}\n\n\tprivate ensureConnected(runtime: ServerRuntime): Promise<void> {\n\t\tif (runtime.config.disabled) {\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = \"disabled by configuration\";\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tif (runtime.state === \"ready\" || runtime.state === \"failed\") return Promise.resolve();\n\t\tif (runtime.connecting) return runtime.connecting;\n\n\t\truntime.state = \"connecting\";\n\t\tconst generation = runtime.generation;\n\t\tconst attempt = this.connectRuntime(runtime, generation).finally(() => {\n\t\t\t// Only this attempt may clear its own in-flight markers; a newer\n\t\t\t// attempt's `connecting`/`pendingClient` belong to the newer generation.\n\t\t\tif (runtime.connecting === attempt) runtime.connecting = undefined;\n\t\t\tif (runtime.generation === generation) runtime.pendingClient = undefined;\n\t\t});\n\t\truntime.connecting = attempt;\n\t\treturn attempt;\n\t}\n\n\tprivate async connectRuntime(runtime: ServerRuntime, generation: number): Promise<void> {\n\t\tconst clientOptions: McpClientOptions = {\n\t\t\tname: runtime.config.name,\n\t\t\tclientInfo: this.options.clientInfo,\n\t\t\trequestTimeoutMs: runtime.config.requestTimeoutMs,\n\t\t\thandshakeTimeoutMs: runtime.config.handshakeTimeoutMs,\n\t\t\ttransport: {\n\t\t\t\tcommand: runtime.config.command,\n\t\t\t\targs: runtime.config.args,\n\t\t\t\tenv: runtime.config.env,\n\t\t\t\tcwd: runtime.config.cwd ?? this.options.cwd,\n\t\t\t},\n\t\t};\n\t\tconst client = this.options.createClient?.(clientOptions) ?? new McpClient(clientOptions);\n\t\t// Register before the first await so close() can dispose a client whose\n\t\t// connect() promise has not resolved yet.\n\t\truntime.pendingClient = client;\n\t\tconst isCurrent = () => runtime.generation === generation && runtime.pendingClient === client;\n\t\ttry {\n\t\t\tawait client.connect();\n\t\t\tif (!isCurrent()) {\n\t\t\t\t// close() or a newer attempt superseded this one mid-handshake.\n\t\t\t\tclient.close();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst schemas = await client.listTools();\n\t\t\tif (!isCurrent()) {\n\t\t\t\tclient.close();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\truntime.client = client;\n\t\t\truntime.pendingClient = undefined;\n\t\t\t// 서술자 주입 스크리닝: MCP 도구 설명은 신뢰할 수 없는 제3자 입력이다.\n\t\t\t// admitPublicMcpServer와 동일 임계값(0.7)으로 차단 — 수입 시점에서 fail-closed.\n\t\t\tconst definitions = schemas.map((schema) =>\n\t\t\t\tcreateMcpToolDefinition(runtime.config.name, client, schema, {\n\t\t\t\t\tcallTimeoutMs: this.options.callTimeoutMs,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst admitted: typeof definitions = [];\n\t\t\tconst quarantined: string[] = [];\n\t\t\tfor (const definition of definitions) {\n\t\t\t\tif (\n\t\t\t\t\tdetectMcpDescriptorPromptInjection(definition.description ?? \"\").patternSignal >\n\t\t\t\t\tMCP_QUARANTINE_PATTERN_SIGNAL_THRESHOLD\n\t\t\t\t) {\n\t\t\t\t\tquarantined.push(definition.name);\n\t\t\t\t} else {\n\t\t\t\t\tadmitted.push(definition);\n\t\t\t\t}\n\t\t\t}\n\t\t\truntime.tools = admitted;\n\t\t\truntime.quarantinedTools = quarantined;\n\t\t\truntime.state = \"ready\";\n\t\t\truntime.error = undefined;\n\t\t} catch (error) {\n\t\t\tclient.close();\n\t\t\tif (!isCurrent()) return; // The current generation owns the state now.\n\t\t\t// Isolation point: this server is out, every other server is unaffected.\n\t\t\truntime.client = undefined;\n\t\t\truntime.tools = [];\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAEN,KAAK,iBAAiB,EACtB,KAAK,eAAe,EAEpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEhH,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAEjD,YAAY,OAAO,EAAE,iBAAiB,EAOrC;IAED,uDAAuD;IACvD,IAAI,WAAW,IAAI,MAAM,EAAE,CAE1B;IAED,6EAA6E;IAC7E,MAAM,IAAI,eAAe,EAAE,CAS1B;IAED,oGAAoG;IAC9F,mBAAmB,IAAI,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,CAY9E;IAED,4FAA4F;IACtF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAYpD;IAED;;;;OAIG;IACH,KAAK,IAAI,IAAI,CAaZ;IAED;;;OAGG;IACG,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAa7G;YAEa,WAAW;IAgBzB,OAAO,CAAC,eAAe;CA+BvB","sourcesContent":["/** Lazy MCP connections with bounded startup, per-server failure isolation and generation ownership. */\nimport type { TSchema } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { McpConnectionQueue } from \"./connection-queue.ts\";\nimport {\n\tconnectMcpRuntime,\n\ttype McpManagerOptions,\n\ttype McpServerStatus,\n\ttype ServerRuntime,\n} from \"./manager-runtime.ts\";\nimport type { McpToolDetails } from \"./tools.ts\";\n\nexport type { McpManagerOptions, McpServerConfig, McpServerState, McpServerStatus } from \"./manager-runtime.ts\";\n\nexport class McpManager {\n\tprivate readonly runtimes = new Map<string, ServerRuntime>();\n\tprivate readonly options: McpManagerOptions;\n\tprivate readonly connections: McpConnectionQueue;\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.options = options;\n\t\tthis.connections = new McpConnectionQueue(options.connectionConcurrency ?? 4);\n\t\tfor (const config of options.servers) {\n\t\t\tif (this.runtimes.has(config.name)) continue; // First definition wins; inventory already applies precedence.\n\t\t\tthis.runtimes.set(config.name, { config, state: \"idle\", tools: [], quarantinedTools: [], generation: 0 });\n\t\t}\n\t}\n\n\t/** Configured server names, in configuration order. */\n\tget serverNames(): string[] {\n\t\treturn [...this.runtimes.keys()];\n\t}\n\n\t/** Current status per server. Safe to call before any connection attempt. */\n\tstatus(): McpServerStatus[] {\n\t\treturn [...this.runtimes.values()].map((runtime) => ({\n\t\t\tname: runtime.config.name,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t}));\n\t}\n\n\t/** Wait for every enabled attempt, then return the full admitted catalog in configuration order. */\n\tasync listToolDefinitions(): Promise<ToolDefinition<TSchema, McpToolDetails>[]> {\n\t\tawait Promise.all([...this.runtimes.values()].map((runtime) => this.ensureConnected(runtime)));\n\t\tconst tools: ToolDefinition<TSchema, McpToolDetails>[] = [];\n\t\tconst seen = new Set<string>();\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tfor (const tool of runtime.tools) {\n\t\t\t\tif (seen.has(tool.name)) continue;\n\t\t\t\tseen.add(tool.name);\n\t\t\t\ttools.push(tool);\n\t\t\t}\n\t\t}\n\t\treturn tools;\n\t}\n\n\t/** Connect a single server by name using the same capacity and single-flight as listing. */\n\tasync connect(name: string): Promise<McpServerStatus> {\n\t\tconst runtime = this.runtimes.get(name);\n\t\tif (!runtime) return { name, state: \"failed\", toolCount: 0, error: `Unknown MCP server \"${name}\"` };\n\t\tawait this.ensureConnected(runtime);\n\t\treturn {\n\t\t\tname,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * Invalidate queued/active generations before closing their clients. Slots for\n\t * started work remain occupied until settlement, not merely until close is requested.\n\t * The manager can be explicitly connected again after close.\n\t */\n\tclose(): void {\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\truntime.generation += 1;\n\t\t\truntime.client?.close();\n\t\t\truntime.client = undefined;\n\t\t\truntime.pendingClient?.close();\n\t\t\truntime.pendingClient = undefined;\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\tif (runtime.state === \"ready\" || runtime.state === \"connecting\" || runtime.state === \"queued\")\n\t\t\t\truntime.state = \"idle\";\n\t\t}\n\t\tthis.connections.cancelQueued();\n\t}\n\n\t/**\n\t * Ping ready servers; idle/queued servers are not spawned by a probe. Failed\n\t * servers retry only on explicit recovery, using the same startup queue.\n\t */\n\tasync checkHealth(options?: { pingTimeoutMs?: number; reconnectFailed?: boolean }): Promise<McpServerStatus[]> {\n\t\tconst work: Promise<void>[] = [];\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tif (runtime.state === \"ready\" && runtime.client) {\n\t\t\t\twork.push(this.pingRuntime(runtime, options?.pingTimeoutMs));\n\t\t\t} else if (runtime.state === \"failed\" && options?.reconnectFailed && !runtime.config.disabled) {\n\t\t\t\truntime.state = \"idle\";\n\t\t\t\truntime.error = undefined;\n\t\t\t\twork.push(this.ensureConnected(runtime));\n\t\t\t}\n\t\t}\n\t\tawait Promise.all(work);\n\t\treturn this.status();\n\t}\n\n\tprivate async pingRuntime(runtime: ServerRuntime, timeoutMs?: number): Promise<void> {\n\t\tconst client = runtime.client;\n\t\tif (!client) return;\n\t\ttry {\n\t\t\tawait client.ping(timeoutMs);\n\t\t} catch (error) {\n\t\t\tif (runtime.client !== client) return; // A newer owner already handled it.\n\t\t\truntime.client = undefined;\n\t\t\tclient.close();\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = `health check failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\t}\n\t}\n\n\tprivate ensureConnected(runtime: ServerRuntime): Promise<void> {\n\t\tif (runtime.config.disabled) {\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = \"disabled by configuration\";\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tif (runtime.state === \"ready\" || runtime.state === \"failed\") return Promise.resolve();\n\t\tif (runtime.connecting) {\n\t\t\tif (runtime.connectingGeneration === runtime.generation) return runtime.connecting;\n\t\t\treturn runtime.connecting.then(() => this.ensureConnected(runtime));\n\t\t}\n\n\t\truntime.state = \"queued\";\n\t\tconst generation = runtime.generation;\n\t\truntime.connectingGeneration = generation;\n\t\tconst attempt = this.connections\n\t\t\t.run(async () => {\n\t\t\t\tif (runtime.generation !== generation) return;\n\t\t\t\truntime.state = \"connecting\";\n\t\t\t\tawait connectMcpRuntime(runtime, generation, this.options);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (runtime.connecting === attempt) {\n\t\t\t\t\truntime.connecting = undefined;\n\t\t\t\t\truntime.connectingGeneration = undefined;\n\t\t\t\t}\n\t\t\t\tif (runtime.generation === generation) runtime.pendingClient = undefined;\n\t\t\t});\n\t\truntime.connecting = attempt;\n\t\treturn attempt;\n\t}\n}\n"]}
|
package/dist/core/mcp/manager.js
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Design constraints that shaped this:
|
|
5
|
-
* - A workspace can configure 25 servers. Spawning them all at startup would
|
|
6
|
-
* trade a 0.6s cold start for a multi-second one, so connection is lazy and
|
|
7
|
-
* only happens when tools are actually requested.
|
|
8
|
-
* - One broken server must never take a session down. Every connect and every
|
|
9
|
-
* listing is isolated; a failure is recorded as server status and the other
|
|
10
|
-
* servers still contribute tools.
|
|
11
|
-
* - Connect work is deduplicated: concurrent `listToolDefinitions()` calls
|
|
12
|
-
* share one in-flight connect per server.
|
|
13
|
-
*/
|
|
14
|
-
import { detectMcpDescriptorPromptInjection, MCP_QUARANTINE_PATTERN_SIGNAL_THRESHOLD } from "../mcp-public-presets.js";
|
|
15
|
-
import { McpClient } from "./client.js";
|
|
16
|
-
import { createMcpToolDefinition } from "./tools.js";
|
|
1
|
+
import { McpConnectionQueue } from "./connection-queue.js";
|
|
2
|
+
import { connectMcpRuntime, } from "./manager-runtime.js";
|
|
17
3
|
export class McpManager {
|
|
18
4
|
runtimes = new Map();
|
|
19
5
|
options;
|
|
6
|
+
connections;
|
|
20
7
|
constructor(options) {
|
|
21
8
|
this.options = options;
|
|
9
|
+
this.connections = new McpConnectionQueue(options.connectionConcurrency ?? 4);
|
|
22
10
|
for (const config of options.servers) {
|
|
23
11
|
if (this.runtimes.has(config.name))
|
|
24
12
|
continue; // First definition wins; inventory already applies precedence.
|
|
@@ -40,11 +28,7 @@ export class McpManager {
|
|
|
40
28
|
...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),
|
|
41
29
|
}));
|
|
42
30
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Connect every enabled server that has not been attempted yet, then return
|
|
45
|
-
* the union of their tools. Servers are connected concurrently and failures
|
|
46
|
-
* are isolated, so a partial result is the normal outcome, not an error.
|
|
47
|
-
*/
|
|
31
|
+
/** Wait for every enabled attempt, then return the full admitted catalog in configuration order. */
|
|
48
32
|
async listToolDefinitions() {
|
|
49
33
|
await Promise.all([...this.runtimes.values()].map((runtime) => this.ensureConnected(runtime)));
|
|
50
34
|
const tools = [];
|
|
@@ -52,19 +36,18 @@ export class McpManager {
|
|
|
52
36
|
for (const runtime of this.runtimes.values()) {
|
|
53
37
|
for (const tool of runtime.tools) {
|
|
54
38
|
if (seen.has(tool.name))
|
|
55
|
-
continue;
|
|
39
|
+
continue;
|
|
56
40
|
seen.add(tool.name);
|
|
57
41
|
tools.push(tool);
|
|
58
42
|
}
|
|
59
43
|
}
|
|
60
44
|
return tools;
|
|
61
45
|
}
|
|
62
|
-
/** Connect a single server by name
|
|
46
|
+
/** Connect a single server by name using the same capacity and single-flight as listing. */
|
|
63
47
|
async connect(name) {
|
|
64
48
|
const runtime = this.runtimes.get(name);
|
|
65
|
-
if (!runtime)
|
|
49
|
+
if (!runtime)
|
|
66
50
|
return { name, state: "failed", toolCount: 0, error: `Unknown MCP server "${name}"` };
|
|
67
|
-
}
|
|
68
51
|
await this.ensureConnected(runtime);
|
|
69
52
|
return {
|
|
70
53
|
name,
|
|
@@ -76,12 +59,9 @@ export class McpManager {
|
|
|
76
59
|
};
|
|
77
60
|
}
|
|
78
61
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* completion can publish tools or flip the state back to `ready`, and the
|
|
83
|
-
* attempt's not-yet-published client is closed alongside the live one so no
|
|
84
|
-
* spawned process outlives the close.
|
|
62
|
+
* Invalidate queued/active generations before closing their clients. Slots for
|
|
63
|
+
* started work remain occupied until settlement, not merely until close is requested.
|
|
64
|
+
* The manager can be explicitly connected again after close.
|
|
85
65
|
*/
|
|
86
66
|
close() {
|
|
87
67
|
for (const runtime of this.runtimes.values()) {
|
|
@@ -92,18 +72,14 @@ export class McpManager {
|
|
|
92
72
|
runtime.pendingClient = undefined;
|
|
93
73
|
runtime.tools = [];
|
|
94
74
|
runtime.quarantinedTools = [];
|
|
95
|
-
if (runtime.state === "ready" || runtime.state === "connecting")
|
|
75
|
+
if (runtime.state === "ready" || runtime.state === "connecting" || runtime.state === "queued")
|
|
96
76
|
runtime.state = "idle";
|
|
97
77
|
}
|
|
78
|
+
this.connections.cancelQueued();
|
|
98
79
|
}
|
|
99
80
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* silently killed process stops masquerading as connected. Servers already
|
|
103
|
-
* `failed` are re-attempted only when `reconnectFailed` is set — callers
|
|
104
|
-
* should gate that behind a slow cadence so a permanently broken server is
|
|
105
|
-
* not respawned every probe. Idle servers stay idle: connection is lazy by
|
|
106
|
-
* design, and a status probe must not spawn processes.
|
|
81
|
+
* Ping ready servers; idle/queued servers are not spawned by a probe. Failed
|
|
82
|
+
* servers retry only on explicit recovery, using the same startup queue.
|
|
107
83
|
*/
|
|
108
84
|
async checkHealth(options) {
|
|
109
85
|
const work = [];
|
|
@@ -121,8 +97,6 @@ export class McpManager {
|
|
|
121
97
|
return this.status();
|
|
122
98
|
}
|
|
123
99
|
async pingRuntime(runtime, timeoutMs) {
|
|
124
|
-
// Pin the probed client: a close or reconnect during the ping must not let
|
|
125
|
-
// this failure path dispose the replacement client.
|
|
126
100
|
const client = runtime.client;
|
|
127
101
|
if (!client)
|
|
128
102
|
return;
|
|
@@ -132,7 +106,6 @@ export class McpManager {
|
|
|
132
106
|
catch (error) {
|
|
133
107
|
if (runtime.client !== client)
|
|
134
108
|
return; // A newer owner already handled it.
|
|
135
|
-
// Isolation point: only this server transitions; the rest stay ready.
|
|
136
109
|
runtime.client = undefined;
|
|
137
110
|
client.close();
|
|
138
111
|
runtime.tools = [];
|
|
@@ -149,84 +122,31 @@ export class McpManager {
|
|
|
149
122
|
}
|
|
150
123
|
if (runtime.state === "ready" || runtime.state === "failed")
|
|
151
124
|
return Promise.resolve();
|
|
152
|
-
if (runtime.connecting)
|
|
153
|
-
|
|
154
|
-
|
|
125
|
+
if (runtime.connecting) {
|
|
126
|
+
if (runtime.connectingGeneration === runtime.generation)
|
|
127
|
+
return runtime.connecting;
|
|
128
|
+
return runtime.connecting.then(() => this.ensureConnected(runtime));
|
|
129
|
+
}
|
|
130
|
+
runtime.state = "queued";
|
|
155
131
|
const generation = runtime.generation;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (runtime.
|
|
132
|
+
runtime.connectingGeneration = generation;
|
|
133
|
+
const attempt = this.connections
|
|
134
|
+
.run(async () => {
|
|
135
|
+
if (runtime.generation !== generation)
|
|
136
|
+
return;
|
|
137
|
+
runtime.state = "connecting";
|
|
138
|
+
await connectMcpRuntime(runtime, generation, this.options);
|
|
139
|
+
})
|
|
140
|
+
.finally(() => {
|
|
141
|
+
if (runtime.connecting === attempt) {
|
|
160
142
|
runtime.connecting = undefined;
|
|
143
|
+
runtime.connectingGeneration = undefined;
|
|
144
|
+
}
|
|
161
145
|
if (runtime.generation === generation)
|
|
162
146
|
runtime.pendingClient = undefined;
|
|
163
147
|
});
|
|
164
148
|
runtime.connecting = attempt;
|
|
165
149
|
return attempt;
|
|
166
150
|
}
|
|
167
|
-
async connectRuntime(runtime, generation) {
|
|
168
|
-
const clientOptions = {
|
|
169
|
-
name: runtime.config.name,
|
|
170
|
-
clientInfo: this.options.clientInfo,
|
|
171
|
-
requestTimeoutMs: runtime.config.requestTimeoutMs,
|
|
172
|
-
handshakeTimeoutMs: runtime.config.handshakeTimeoutMs,
|
|
173
|
-
transport: {
|
|
174
|
-
command: runtime.config.command,
|
|
175
|
-
args: runtime.config.args,
|
|
176
|
-
env: runtime.config.env,
|
|
177
|
-
cwd: runtime.config.cwd ?? this.options.cwd,
|
|
178
|
-
},
|
|
179
|
-
};
|
|
180
|
-
const client = this.options.createClient?.(clientOptions) ?? new McpClient(clientOptions);
|
|
181
|
-
// Register before the first await so close() can dispose a client whose
|
|
182
|
-
// connect() promise has not resolved yet.
|
|
183
|
-
runtime.pendingClient = client;
|
|
184
|
-
const isCurrent = () => runtime.generation === generation && runtime.pendingClient === client;
|
|
185
|
-
try {
|
|
186
|
-
await client.connect();
|
|
187
|
-
if (!isCurrent()) {
|
|
188
|
-
// close() or a newer attempt superseded this one mid-handshake.
|
|
189
|
-
client.close();
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const schemas = await client.listTools();
|
|
193
|
-
if (!isCurrent()) {
|
|
194
|
-
client.close();
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
runtime.client = client;
|
|
198
|
-
runtime.pendingClient = undefined;
|
|
199
|
-
// 서술자 주입 스크리닝: MCP 도구 설명은 신뢰할 수 없는 제3자 입력이다.
|
|
200
|
-
// admitPublicMcpServer와 동일 임계값(0.7)으로 차단 — 수입 시점에서 fail-closed.
|
|
201
|
-
const definitions = schemas.map((schema) => createMcpToolDefinition(runtime.config.name, client, schema, {
|
|
202
|
-
callTimeoutMs: this.options.callTimeoutMs,
|
|
203
|
-
}));
|
|
204
|
-
const admitted = [];
|
|
205
|
-
const quarantined = [];
|
|
206
|
-
for (const definition of definitions) {
|
|
207
|
-
if (detectMcpDescriptorPromptInjection(definition.description ?? "").patternSignal >
|
|
208
|
-
MCP_QUARANTINE_PATTERN_SIGNAL_THRESHOLD) {
|
|
209
|
-
quarantined.push(definition.name);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
admitted.push(definition);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
runtime.tools = admitted;
|
|
216
|
-
runtime.quarantinedTools = quarantined;
|
|
217
|
-
runtime.state = "ready";
|
|
218
|
-
runtime.error = undefined;
|
|
219
|
-
}
|
|
220
|
-
catch (error) {
|
|
221
|
-
client.close();
|
|
222
|
-
if (!isCurrent())
|
|
223
|
-
return; // The current generation owns the state now.
|
|
224
|
-
// Isolation point: this server is out, every other server is unaffected.
|
|
225
|
-
runtime.client = undefined;
|
|
226
|
-
runtime.tools = [];
|
|
227
|
-
runtime.state = "failed";
|
|
228
|
-
runtime.error = error instanceof Error ? error.message : String(error);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
151
|
}
|
|
232
152
|
//# sourceMappingURL=manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,kCAAkC,EAAE,uCAAuC,EAAE,MAAM,0BAA0B,CAAC;AACvH,OAAO,EAAE,SAAS,EAAyB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAuB,MAAM,YAAY,CAAC;AAmD1E,MAAM,OAAO,UAAU;IACL,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC5C,OAAO,CAAoB;IAE5C,YAAY,OAA0B,EAAE;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,SAAS,CAAC,+DAA+D;YAC7G,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3G,CAAC;IAAA,CACD;IAED,uDAAuD;IACvD,IAAI,WAAW,GAAa;QAC3B,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAAA,CACjC;IAED,6EAA6E;IAC7E,MAAM,GAAsB;QAC3B,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACpD,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;YAC/B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO;YACjD,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9F,CAAC,CAAC,CAAC;IAAA,CACJ;IAED;;;;OAIG;IACH,KAAK,CAAC,mBAAmB,GAAuD;QAC/E,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/F,MAAM,KAAK,GAA8C,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAClC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,SAAS,CAAC,6DAA6D;gBAChG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;QACF,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb;IAED,sEAAsE;IACtE,KAAK,CAAC,OAAO,CAAC,IAAY,EAA4B;QACrD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,uBAAuB,IAAI,GAAG,EAAE,CAAC;QACvF,CAAC;QACD,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QACpC,OAAO;YACN,IAAI;YACJ,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;YAC/B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO;YACjD,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9F,CAAC;IAAA,CACF;IAED;;;;;;;OAOG;IACH,KAAK,GAAS;QACb,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;YACxB,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YACxB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAC3B,OAAO,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC;YAC/B,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;YAClC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,gBAAgB,GAAG,EAAE,CAAC;YAC9B,IAAI,OAAO,CAAC,KAAK,KAAK,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,YAAY;gBAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;QACzF,CAAC;IAAA,CACD;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,WAAW,CAAC,OAA+D,EAA8B;QAC9G,MAAM,IAAI,GAAoB,EAAE,CAAC;QACjC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,IAAI,OAAO,CAAC,KAAK,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACjD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;YAC9D,CAAC;iBAAM,IAAI,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,EAAE,eAAe,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;gBAC/F,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;gBACvB,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1C,CAAC;QACF,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IAAA,CACrB;IAEO,KAAK,CAAC,WAAW,CAAC,OAAsB,EAAE,SAAkB,EAAiB;QACpF,2EAA2E;QAC3E,oDAAoD;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;gBAAE,OAAO,CAAC,oCAAoC;YAC3E,sEAAsE;YACtE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAC3B,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,gBAAgB,GAAG,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;YACzB,OAAO,CAAC,KAAK,GAAG,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAClG,CAAC;IAAA,CACD;IAEO,eAAe,CAAC,OAAsB,EAAiB;QAC9D,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;YACzB,OAAO,CAAC,KAAK,GAAG,2BAA2B,CAAC;YAC5C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,KAAK,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACtF,IAAI,OAAO,CAAC,UAAU;YAAE,OAAO,OAAO,CAAC,UAAU,CAAC;QAElD,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC;QAC7B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACtE,iEAAiE;YACjE,yEAAyE;YACzE,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO;gBAAE,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;YACnE,IAAI,OAAO,CAAC,UAAU,KAAK,UAAU;gBAAE,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;QAAA,CACzE,CAAC,CAAC;QACH,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;QAC7B,OAAO,OAAO,CAAC;IAAA,CACf;IAEO,KAAK,CAAC,cAAc,CAAC,OAAsB,EAAE,UAAkB,EAAiB;QACvF,MAAM,aAAa,GAAqB;YACvC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;YACnC,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB;YACjD,kBAAkB,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB;YACrD,SAAS,EAAE;gBACV,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO;gBAC/B,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;gBACzB,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG;gBACvB,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG;aAC3C;SACD,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,aAAa,CAAC,IAAI,IAAI,SAAS,CAAC,aAAa,CAAC,CAAC;QAC1F,wEAAwE;QACxE,0CAA0C;QAC1C,OAAO,CAAC,aAAa,GAAG,MAAM,CAAC;QAC/B,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,KAAK,UAAU,IAAI,OAAO,CAAC,aAAa,KAAK,MAAM,CAAC;QAC9F,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;gBAClB,gEAAgE;gBAChE,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO;YACR,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;YACzC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;gBAClB,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO;YACR,CAAC;YACD,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;YACxB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;YAClC,iGAA6C;YAC7C,kGAAgE;YAChE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC1C,uBAAuB,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;gBAC5D,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa;aACzC,CAAC,CACF,CAAC;YACF,MAAM,QAAQ,GAAuB,EAAE,CAAC;YACxC,MAAM,WAAW,GAAa,EAAE,CAAC;YACjC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACtC,IACC,kCAAkC,CAAC,UAAU,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,aAAa;oBAC9E,uCAAuC,EACtC,CAAC;oBACF,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBACnC,CAAC;qBAAM,CAAC;oBACP,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC3B,CAAC;YACF,CAAC;YACD,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;YACzB,OAAO,CAAC,gBAAgB,GAAG,WAAW,CAAC;YACvC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC;YACxB,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,EAAE;gBAAE,OAAO,CAAC,6CAA6C;YACvE,yEAAyE;YACzE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAC3B,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;YACzB,OAAO,CAAC,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxE,CAAC;IAAA,CACD;CACD","sourcesContent":["/**\n * Lazy, crash-isolated MCP server manager.\n *\n * Design constraints that shaped this:\n * - A workspace can configure 25 servers. Spawning them all at startup would\n * trade a 0.6s cold start for a multi-second one, so connection is lazy and\n * only happens when tools are actually requested.\n * - One broken server must never take a session down. Every connect and every\n * listing is isolated; a failure is recorded as server status and the other\n * servers still contribute tools.\n * - Connect work is deduplicated: concurrent `listToolDefinitions()` calls\n * share one in-flight connect per server.\n */\n\nimport type { TSchema } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { detectMcpDescriptorPromptInjection, MCP_QUARANTINE_PATTERN_SIGNAL_THRESHOLD } from \"../mcp-public-presets.ts\";\nimport { McpClient, type McpClientOptions } from \"./client.ts\";\nimport { createMcpToolDefinition, type McpToolDetails } from \"./tools.ts\";\n\nexport type McpServerState = \"idle\" | \"connecting\" | \"ready\" | \"failed\";\n\nexport interface McpServerConfig {\n\treadonly name: string;\n\treadonly command: string;\n\treadonly args?: readonly string[];\n\treadonly env?: Readonly<Record<string, string>>;\n\treadonly cwd?: string;\n\t/** Skip this server without removing it from configuration. */\n\treadonly disabled?: boolean;\n\treadonly requestTimeoutMs?: number;\n\treadonly handshakeTimeoutMs?: number;\n}\n\nexport interface McpServerStatus {\n\treadonly name: string;\n\treadonly state: McpServerState;\n\treadonly toolCount: number;\n\t/** Failure reason when `state` is `failed`. Never contains configured env values. */\n\treadonly error?: string;\n\treadonly serverVersion?: string;\n\t/** Tool names excluded because their descriptions carried prompt-injection payloads. */\n\treadonly quarantinedTools?: readonly string[];\n}\n\nexport interface McpManagerOptions {\n\treadonly servers: readonly McpServerConfig[];\n\treadonly cwd?: string;\n\treadonly clientInfo?: { readonly name: string; readonly version: string };\n\treadonly callTimeoutMs?: number;\n\t/** Injected for tests so the manager can be exercised without spawning processes. */\n\treadonly createClient?: (options: McpClientOptions) => McpClient;\n}\n\ninterface ServerRuntime {\n\treadonly config: McpServerConfig;\n\tstate: McpServerState;\n\tclient?: McpClient;\n\ttools: ToolDefinition<TSchema, McpToolDetails>[];\n\t/** Descriptor prompt-injection quarantines (namespaced tool names). */\n\tquarantinedTools: string[];\n\terror?: string;\n\tconnecting?: Promise<void>;\n\t/** Client owned by the in-flight connect attempt; not yet published. */\n\tpendingClient?: McpClient;\n\t/** Monotonic generation; a completed attempt may only publish when current. */\n\tgeneration: number;\n}\n\nexport class McpManager {\n\tprivate readonly runtimes = new Map<string, ServerRuntime>();\n\tprivate readonly options: McpManagerOptions;\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.options = options;\n\t\tfor (const config of options.servers) {\n\t\t\tif (this.runtimes.has(config.name)) continue; // First definition wins; inventory already applies precedence.\n\t\t\tthis.runtimes.set(config.name, { config, state: \"idle\", tools: [], quarantinedTools: [], generation: 0 });\n\t\t}\n\t}\n\n\t/** Configured server names, in configuration order. */\n\tget serverNames(): string[] {\n\t\treturn [...this.runtimes.keys()];\n\t}\n\n\t/** Current status per server. Safe to call before any connection attempt. */\n\tstatus(): McpServerStatus[] {\n\t\treturn [...this.runtimes.values()].map((runtime) => ({\n\t\t\tname: runtime.config.name,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t}));\n\t}\n\n\t/**\n\t * Connect every enabled server that has not been attempted yet, then return\n\t * the union of their tools. Servers are connected concurrently and failures\n\t * are isolated, so a partial result is the normal outcome, not an error.\n\t */\n\tasync listToolDefinitions(): Promise<ToolDefinition<TSchema, McpToolDetails>[]> {\n\t\tawait Promise.all([...this.runtimes.values()].map((runtime) => this.ensureConnected(runtime)));\n\t\tconst tools: ToolDefinition<TSchema, McpToolDetails>[] = [];\n\t\tconst seen = new Set<string>();\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tfor (const tool of runtime.tools) {\n\t\t\t\tif (seen.has(tool.name)) continue; // Namespacing makes this rare; keep it deterministic anyway.\n\t\t\t\tseen.add(tool.name);\n\t\t\t\ttools.push(tool);\n\t\t\t}\n\t\t}\n\t\treturn tools;\n\t}\n\n\t/** Connect a single server by name. Returns its status either way. */\n\tasync connect(name: string): Promise<McpServerStatus> {\n\t\tconst runtime = this.runtimes.get(name);\n\t\tif (!runtime) {\n\t\t\treturn { name, state: \"failed\", toolCount: 0, error: `Unknown MCP server \"${name}\"` };\n\t\t}\n\t\tawait this.ensureConnected(runtime);\n\t\treturn {\n\t\t\tname,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * Terminate every connected or connecting server. Idempotent.\n\t *\n\t * Bumping the generation invalidates any in-flight connect before its late\n\t * completion can publish tools or flip the state back to `ready`, and the\n\t * attempt's not-yet-published client is closed alongside the live one so no\n\t * spawned process outlives the close.\n\t */\n\tclose(): void {\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\truntime.generation += 1;\n\t\t\truntime.client?.close();\n\t\t\truntime.client = undefined;\n\t\t\truntime.pendingClient?.close();\n\t\t\truntime.pendingClient = undefined;\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\tif (runtime.state === \"ready\" || runtime.state === \"connecting\") runtime.state = \"idle\";\n\t\t}\n\t}\n\n\t/**\n\t * Verify that `ready` servers are actually alive with a protocol ping. A\n\t * failed ping closes the client and marks the server `failed`, so a\n\t * silently killed process stops masquerading as connected. Servers already\n\t * `failed` are re-attempted only when `reconnectFailed` is set — callers\n\t * should gate that behind a slow cadence so a permanently broken server is\n\t * not respawned every probe. Idle servers stay idle: connection is lazy by\n\t * design, and a status probe must not spawn processes.\n\t */\n\tasync checkHealth(options?: { pingTimeoutMs?: number; reconnectFailed?: boolean }): Promise<McpServerStatus[]> {\n\t\tconst work: Promise<void>[] = [];\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tif (runtime.state === \"ready\" && runtime.client) {\n\t\t\t\twork.push(this.pingRuntime(runtime, options?.pingTimeoutMs));\n\t\t\t} else if (runtime.state === \"failed\" && options?.reconnectFailed && !runtime.config.disabled) {\n\t\t\t\truntime.state = \"idle\";\n\t\t\t\truntime.error = undefined;\n\t\t\t\twork.push(this.ensureConnected(runtime));\n\t\t\t}\n\t\t}\n\t\tawait Promise.all(work);\n\t\treturn this.status();\n\t}\n\n\tprivate async pingRuntime(runtime: ServerRuntime, timeoutMs?: number): Promise<void> {\n\t\t// Pin the probed client: a close or reconnect during the ping must not let\n\t\t// this failure path dispose the replacement client.\n\t\tconst client = runtime.client;\n\t\tif (!client) return;\n\t\ttry {\n\t\t\tawait client.ping(timeoutMs);\n\t\t} catch (error) {\n\t\t\tif (runtime.client !== client) return; // A newer owner already handled it.\n\t\t\t// Isolation point: only this server transitions; the rest stay ready.\n\t\t\truntime.client = undefined;\n\t\t\tclient.close();\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = `health check failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\t}\n\t}\n\n\tprivate ensureConnected(runtime: ServerRuntime): Promise<void> {\n\t\tif (runtime.config.disabled) {\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = \"disabled by configuration\";\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tif (runtime.state === \"ready\" || runtime.state === \"failed\") return Promise.resolve();\n\t\tif (runtime.connecting) return runtime.connecting;\n\n\t\truntime.state = \"connecting\";\n\t\tconst generation = runtime.generation;\n\t\tconst attempt = this.connectRuntime(runtime, generation).finally(() => {\n\t\t\t// Only this attempt may clear its own in-flight markers; a newer\n\t\t\t// attempt's `connecting`/`pendingClient` belong to the newer generation.\n\t\t\tif (runtime.connecting === attempt) runtime.connecting = undefined;\n\t\t\tif (runtime.generation === generation) runtime.pendingClient = undefined;\n\t\t});\n\t\truntime.connecting = attempt;\n\t\treturn attempt;\n\t}\n\n\tprivate async connectRuntime(runtime: ServerRuntime, generation: number): Promise<void> {\n\t\tconst clientOptions: McpClientOptions = {\n\t\t\tname: runtime.config.name,\n\t\t\tclientInfo: this.options.clientInfo,\n\t\t\trequestTimeoutMs: runtime.config.requestTimeoutMs,\n\t\t\thandshakeTimeoutMs: runtime.config.handshakeTimeoutMs,\n\t\t\ttransport: {\n\t\t\t\tcommand: runtime.config.command,\n\t\t\t\targs: runtime.config.args,\n\t\t\t\tenv: runtime.config.env,\n\t\t\t\tcwd: runtime.config.cwd ?? this.options.cwd,\n\t\t\t},\n\t\t};\n\t\tconst client = this.options.createClient?.(clientOptions) ?? new McpClient(clientOptions);\n\t\t// Register before the first await so close() can dispose a client whose\n\t\t// connect() promise has not resolved yet.\n\t\truntime.pendingClient = client;\n\t\tconst isCurrent = () => runtime.generation === generation && runtime.pendingClient === client;\n\t\ttry {\n\t\t\tawait client.connect();\n\t\t\tif (!isCurrent()) {\n\t\t\t\t// close() or a newer attempt superseded this one mid-handshake.\n\t\t\t\tclient.close();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst schemas = await client.listTools();\n\t\t\tif (!isCurrent()) {\n\t\t\t\tclient.close();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\truntime.client = client;\n\t\t\truntime.pendingClient = undefined;\n\t\t\t// 서술자 주입 스크리닝: MCP 도구 설명은 신뢰할 수 없는 제3자 입력이다.\n\t\t\t// admitPublicMcpServer와 동일 임계값(0.7)으로 차단 — 수입 시점에서 fail-closed.\n\t\t\tconst definitions = schemas.map((schema) =>\n\t\t\t\tcreateMcpToolDefinition(runtime.config.name, client, schema, {\n\t\t\t\t\tcallTimeoutMs: this.options.callTimeoutMs,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst admitted: typeof definitions = [];\n\t\t\tconst quarantined: string[] = [];\n\t\t\tfor (const definition of definitions) {\n\t\t\t\tif (\n\t\t\t\t\tdetectMcpDescriptorPromptInjection(definition.description ?? \"\").patternSignal >\n\t\t\t\t\tMCP_QUARANTINE_PATTERN_SIGNAL_THRESHOLD\n\t\t\t\t) {\n\t\t\t\t\tquarantined.push(definition.name);\n\t\t\t\t} else {\n\t\t\t\t\tadmitted.push(definition);\n\t\t\t\t}\n\t\t\t}\n\t\t\truntime.tools = admitted;\n\t\t\truntime.quarantinedTools = quarantined;\n\t\t\truntime.state = \"ready\";\n\t\t\truntime.error = undefined;\n\t\t} catch (error) {\n\t\t\tclient.close();\n\t\t\tif (!isCurrent()) return; // The current generation owns the state now.\n\t\t\t// Isolation point: this server is out, every other server is unaffected.\n\t\t\truntime.client = undefined;\n\t\t\truntime.tools = [];\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = error instanceof Error ? error.message : String(error);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"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","sourcesContent":["/** Lazy MCP connections with bounded startup, per-server failure isolation and generation ownership. */\nimport type { TSchema } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { McpConnectionQueue } from \"./connection-queue.ts\";\nimport {\n\tconnectMcpRuntime,\n\ttype McpManagerOptions,\n\ttype McpServerStatus,\n\ttype ServerRuntime,\n} from \"./manager-runtime.ts\";\nimport type { McpToolDetails } from \"./tools.ts\";\n\nexport type { McpManagerOptions, McpServerConfig, McpServerState, McpServerStatus } from \"./manager-runtime.ts\";\n\nexport class McpManager {\n\tprivate readonly runtimes = new Map<string, ServerRuntime>();\n\tprivate readonly options: McpManagerOptions;\n\tprivate readonly connections: McpConnectionQueue;\n\n\tconstructor(options: McpManagerOptions) {\n\t\tthis.options = options;\n\t\tthis.connections = new McpConnectionQueue(options.connectionConcurrency ?? 4);\n\t\tfor (const config of options.servers) {\n\t\t\tif (this.runtimes.has(config.name)) continue; // First definition wins; inventory already applies precedence.\n\t\t\tthis.runtimes.set(config.name, { config, state: \"idle\", tools: [], quarantinedTools: [], generation: 0 });\n\t\t}\n\t}\n\n\t/** Configured server names, in configuration order. */\n\tget serverNames(): string[] {\n\t\treturn [...this.runtimes.keys()];\n\t}\n\n\t/** Current status per server. Safe to call before any connection attempt. */\n\tstatus(): McpServerStatus[] {\n\t\treturn [...this.runtimes.values()].map((runtime) => ({\n\t\t\tname: runtime.config.name,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t}));\n\t}\n\n\t/** Wait for every enabled attempt, then return the full admitted catalog in configuration order. */\n\tasync listToolDefinitions(): Promise<ToolDefinition<TSchema, McpToolDetails>[]> {\n\t\tawait Promise.all([...this.runtimes.values()].map((runtime) => this.ensureConnected(runtime)));\n\t\tconst tools: ToolDefinition<TSchema, McpToolDetails>[] = [];\n\t\tconst seen = new Set<string>();\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tfor (const tool of runtime.tools) {\n\t\t\t\tif (seen.has(tool.name)) continue;\n\t\t\t\tseen.add(tool.name);\n\t\t\t\ttools.push(tool);\n\t\t\t}\n\t\t}\n\t\treturn tools;\n\t}\n\n\t/** Connect a single server by name using the same capacity and single-flight as listing. */\n\tasync connect(name: string): Promise<McpServerStatus> {\n\t\tconst runtime = this.runtimes.get(name);\n\t\tif (!runtime) return { name, state: \"failed\", toolCount: 0, error: `Unknown MCP server \"${name}\"` };\n\t\tawait this.ensureConnected(runtime);\n\t\treturn {\n\t\t\tname,\n\t\t\tstate: runtime.state,\n\t\t\ttoolCount: runtime.tools.length,\n\t\t\terror: runtime.error,\n\t\t\tserverVersion: runtime.client?.serverInfo.version,\n\t\t\t...(runtime.quarantinedTools.length > 0 ? { quarantinedTools: runtime.quarantinedTools } : {}),\n\t\t};\n\t}\n\n\t/**\n\t * Invalidate queued/active generations before closing their clients. Slots for\n\t * started work remain occupied until settlement, not merely until close is requested.\n\t * The manager can be explicitly connected again after close.\n\t */\n\tclose(): void {\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\truntime.generation += 1;\n\t\t\truntime.client?.close();\n\t\t\truntime.client = undefined;\n\t\t\truntime.pendingClient?.close();\n\t\t\truntime.pendingClient = undefined;\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\tif (runtime.state === \"ready\" || runtime.state === \"connecting\" || runtime.state === \"queued\")\n\t\t\t\truntime.state = \"idle\";\n\t\t}\n\t\tthis.connections.cancelQueued();\n\t}\n\n\t/**\n\t * Ping ready servers; idle/queued servers are not spawned by a probe. Failed\n\t * servers retry only on explicit recovery, using the same startup queue.\n\t */\n\tasync checkHealth(options?: { pingTimeoutMs?: number; reconnectFailed?: boolean }): Promise<McpServerStatus[]> {\n\t\tconst work: Promise<void>[] = [];\n\t\tfor (const runtime of this.runtimes.values()) {\n\t\t\tif (runtime.state === \"ready\" && runtime.client) {\n\t\t\t\twork.push(this.pingRuntime(runtime, options?.pingTimeoutMs));\n\t\t\t} else if (runtime.state === \"failed\" && options?.reconnectFailed && !runtime.config.disabled) {\n\t\t\t\truntime.state = \"idle\";\n\t\t\t\truntime.error = undefined;\n\t\t\t\twork.push(this.ensureConnected(runtime));\n\t\t\t}\n\t\t}\n\t\tawait Promise.all(work);\n\t\treturn this.status();\n\t}\n\n\tprivate async pingRuntime(runtime: ServerRuntime, timeoutMs?: number): Promise<void> {\n\t\tconst client = runtime.client;\n\t\tif (!client) return;\n\t\ttry {\n\t\t\tawait client.ping(timeoutMs);\n\t\t} catch (error) {\n\t\t\tif (runtime.client !== client) return; // A newer owner already handled it.\n\t\t\truntime.client = undefined;\n\t\t\tclient.close();\n\t\t\truntime.tools = [];\n\t\t\truntime.quarantinedTools = [];\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = `health check failed: ${error instanceof Error ? error.message : String(error)}`;\n\t\t}\n\t}\n\n\tprivate ensureConnected(runtime: ServerRuntime): Promise<void> {\n\t\tif (runtime.config.disabled) {\n\t\t\truntime.state = \"failed\";\n\t\t\truntime.error = \"disabled by configuration\";\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tif (runtime.state === \"ready\" || runtime.state === \"failed\") return Promise.resolve();\n\t\tif (runtime.connecting) {\n\t\t\tif (runtime.connectingGeneration === runtime.generation) return runtime.connecting;\n\t\t\treturn runtime.connecting.then(() => this.ensureConnected(runtime));\n\t\t}\n\n\t\truntime.state = \"queued\";\n\t\tconst generation = runtime.generation;\n\t\truntime.connectingGeneration = generation;\n\t\tconst attempt = this.connections\n\t\t\t.run(async () => {\n\t\t\t\tif (runtime.generation !== generation) return;\n\t\t\t\truntime.state = \"connecting\";\n\t\t\t\tawait connectMcpRuntime(runtime, generation, this.options);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (runtime.connecting === attempt) {\n\t\t\t\t\truntime.connecting = undefined;\n\t\t\t\t\truntime.connectingGeneration = undefined;\n\t\t\t\t}\n\t\t\t\tif (runtime.generation === generation) runtime.pendingClient = undefined;\n\t\t\t});\n\t\truntime.connecting = attempt;\n\t\treturn attempt;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+FAA+F;AAC/F,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,mBAAmB,GAAG,eAAe,CAAC;AAMpF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,eAAe,CAiB9E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,mBAAmB,CAEtF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,cAAc,CAK5E;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAQ7D;AAED,MAAM,WAAW,WAAW;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,GAAE,MAA+B,GAAG;IACjF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACd,
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+FAA+F;AAC/F,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,mBAAmB,GAAG,eAAe,CAAC;AAMpF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,eAAe,CAiB9E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,mBAAmB,CAEtF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,cAAc,CAK5E;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAQ7D;AAED,MAAM,WAAW,WAAW;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,GAAE,MAA+B,GAAG;IACjF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;IACnC,KAAK,IAAI,IAAI,CAAC;CACd,CAgFA;AAeD,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAGlE","sourcesContent":["/**\n * Pure JSON-RPC 2.0 framing for the MCP stdio transport.\n *\n * MCP stdio frames one JSON message per line on stdout; embedded newlines are\n * illegal, so decoding is a line split with a hard length ceiling. Nothing here\n * touches a process, a socket, or a clock — the transport owns all I/O so this\n * layer stays trivially testable.\n */\n\n/** Ceiling for a single decoded line. A server that exceeds it is malfunctioning, not slow. */\nexport const MAX_MESSAGE_LINE_BYTES = 16 * 1024 * 1024;\n\nexport type JsonRpcId = number | string;\n\nexport interface JsonRpcRequest {\n\treadonly jsonrpc: \"2.0\";\n\treadonly id: JsonRpcId;\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport interface JsonRpcNotification {\n\treadonly jsonrpc: \"2.0\";\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport interface JsonRpcErrorBody {\n\treadonly code: number;\n\treadonly message: string;\n\treadonly data?: unknown;\n}\n\nexport interface JsonRpcResponse {\n\treadonly jsonrpc: \"2.0\";\n\treadonly id: JsonRpcId;\n\treadonly result?: unknown;\n\treadonly error?: JsonRpcErrorBody;\n}\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonRpcResponse(message: unknown): message is JsonRpcResponse {\n\tif (!isRecord(message) || message.jsonrpc !== \"2.0\") return false;\n\tif (!(\"id\" in message)) return false;\n\tconst id = message.id;\n\tif (typeof id !== \"number\" && typeof id !== \"string\") return false;\n\t// JSON-RPC success and error are mutually exclusive; `error: null` is not a\n\t// valid error object, and a present error must carry its required fields.\n\tconst hasResult = \"result\" in message;\n\tconst hasError = \"error\" in message;\n\tif (hasResult === hasError) return false; // both present or both absent\n\tif (hasError) {\n\t\tconst error = message.error;\n\t\tif (!isRecord(error)) return false;\n\t\tif (typeof error.code !== \"number\" || !Number.isFinite(error.code)) return false;\n\t\tif (typeof error.message !== \"string\") return false;\n\t}\n\treturn true;\n}\n\nexport function isJsonRpcNotification(message: unknown): message is JsonRpcNotification {\n\treturn isRecord(message) && message.jsonrpc === \"2.0\" && typeof message.method === \"string\" && !(\"id\" in message);\n}\n\nexport function isJsonRpcRequest(message: unknown): message is JsonRpcRequest {\n\tif (!isRecord(message) || message.jsonrpc !== \"2.0\") return false;\n\tif (typeof message.method !== \"string\") return false;\n\tconst id = message.id;\n\treturn typeof id === \"number\" || typeof id === \"string\";\n}\n\n/** Serialize one message as a single stdio frame (JSON + newline). */\nexport function encodeMessage(message: JsonRpcMessage): string {\n\tconst line = JSON.stringify(message);\n\tif (line.includes(\"\\n\")) {\n\t\t// JSON.stringify escapes newlines, so this is unreachable for valid input;\n\t\t// keep the guard so a future custom serializer cannot corrupt framing.\n\t\tthrow new Error(\"MCP frame contains a literal newline\");\n\t}\n\treturn `${line}\\n`;\n}\n\nexport interface DecodedLine {\n\t/** Parsed message, when the line was valid JSON. */\n\treadonly message?: JsonRpcMessage;\n\t/** Why the line was dropped, when it was not usable. */\n\treadonly error?: string;\n}\n\n/**\n * Incremental newline-delimited JSON decoder.\n *\n * The limit is enforced in UTF-8 bytes against the raw frame, before JSON\n * parsing, so the same message is accepted or rejected regardless of how the\n * transport splits it into chunks. A line that exceeds\n * {@link MAX_MESSAGE_LINE_BYTES} is reported once, its bytes are discarded\n * without being retained, and decoding resynchronizes at the next newline —\n * a runaway server cannot exhaust memory by growing the retained buffer.\n *\n * `maxLineBytes` must be a positive finite integer; anything else is a\n * configuration error, not a size to guess at.\n */\nexport function createLineDecoder(maxLineBytes: number = MAX_MESSAGE_LINE_BYTES): {\n\tpush(chunk: string): DecodedLine[];\n\treset(): void;\n} {\n\tif (!Number.isSafeInteger(maxLineBytes) || maxLineBytes <= 0) {\n\t\tthrow new RangeError(`maxLineBytes must be a positive finite integer, got ${maxLineBytes}`);\n\t}\n\t// Batch small fragments so one-character delivery cannot retain millions of\n\t// array slots. Only newly arrived text is scanned; each line is joined once.\n\tconst blockChars = 4096;\n\tlet blocks: string[] = [];\n\tlet small: string[] = [];\n\tlet smallChars = 0;\n\tlet lineBytes = 0;\n\tlet lastCodeUnit = -1;\n\tlet discarding = false;\n\tconst clearLine = (): void => {\n\t\tblocks = [];\n\t\tsmall = [];\n\t\tsmallChars = 0;\n\t\tlineBytes = 0;\n\t\tlastCodeUnit = -1;\n\t};\n\tconst compact = (): void => {\n\t\tif (small.length > 0) blocks.push(small.join(\"\"));\n\t\tsmall = [];\n\t\tsmallChars = 0;\n\t};\n\tconst append = (part: string): void => {\n\t\tif (part.length === 0) return;\n\t\tif (part.length >= blockChars) {\n\t\t\tcompact();\n\t\t\tblocks.push(part);\n\t\t} else {\n\t\t\tsmall.push(part);\n\t\t\tsmallChars += part.length;\n\t\t\tif (smallChars >= blockChars) compact();\n\t\t}\n\t};\n\n\treturn {\n\t\tpush(chunk: string): DecodedLine[] {\n\t\t\tconst out: DecodedLine[] = [];\n\t\t\tlet offset = 0;\n\t\t\twhile (offset < chunk.length) {\n\t\t\t\tconst newline = chunk.indexOf(\"\\n\", offset);\n\t\t\t\tconst end = newline < 0 ? chunk.length : newline;\n\t\t\t\tif (!discarding) {\n\t\t\t\t\tconst part = chunk.slice(offset, end);\n\t\t\t\t\tlet addedBytes = Buffer.byteLength(part, \"utf8\");\n\t\t\t\t\t// String callers may split a surrogate pair even though the stdio\n\t\t\t\t\t// transport already decodes UTF-8 before delivering chunks.\n\t\t\t\t\tconst first = part.charCodeAt(0);\n\t\t\t\t\tif (lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff && first >= 0xdc00 && first <= 0xdfff) {\n\t\t\t\t\t\taddedBytes -= 2;\n\t\t\t\t\t}\n\t\t\t\t\tlineBytes += addedBytes;\n\t\t\t\t\tif (lineBytes > maxLineBytes) {\n\t\t\t\t\t\tout.push({ error: `MCP frame exceeded ${maxLineBytes} bytes` });\n\t\t\t\t\t\tclearLine();\n\t\t\t\t\t\tdiscarding = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tappend(part);\n\t\t\t\t\t\tif (part.length > 0) lastCodeUnit = part.charCodeAt(part.length - 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (newline < 0) break;\n\t\t\t\tif (!discarding) {\n\t\t\t\t\tcompact();\n\t\t\t\t\tconst line = blocks.join(\"\").trim();\n\t\t\t\t\tif (line.length > 0) out.push(decodeLine(line));\n\t\t\t\t}\n\t\t\t\tclearLine();\n\t\t\t\tdiscarding = false;\n\t\t\t\toffset = newline + 1;\n\t\t\t}\n\t\t\treturn out;\n\t\t},\n\t\treset(): void {\n\t\t\tclearLine();\n\t\t\tdiscarding = false;\n\t\t},\n\t};\n}\n\nfunction decodeLine(line: string): DecodedLine {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch {\n\t\treturn { error: \"MCP frame is not valid JSON\" };\n\t}\n\tif (isJsonRpcResponse(parsed) || isJsonRpcNotification(parsed) || isJsonRpcRequest(parsed)) {\n\t\treturn { message: parsed };\n\t}\n\treturn { error: \"MCP frame is not a JSON-RPC 2.0 message\" };\n}\n\n/** Format a JSON-RPC error body for a human or a model. */\nexport function formatJsonRpcError(error: JsonRpcErrorBody): string {\n\tconst suffix = error.data === undefined ? \"\" : ` (${JSON.stringify(error.data)})`;\n\treturn `JSON-RPC error ${error.code}: ${error.message}${suffix}`;\n}\n"]}
|
|
@@ -74,56 +74,86 @@ export function createLineDecoder(maxLineBytes = MAX_MESSAGE_LINE_BYTES) {
|
|
|
74
74
|
if (!Number.isSafeInteger(maxLineBytes) || maxLineBytes <= 0) {
|
|
75
75
|
throw new RangeError(`maxLineBytes must be a positive finite integer, got ${maxLineBytes}`);
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
// Batch small fragments so one-character delivery cannot retain millions of
|
|
78
|
+
// array slots. Only newly arrived text is scanned; each line is joined once.
|
|
79
|
+
const blockChars = 4096;
|
|
80
|
+
let blocks = [];
|
|
81
|
+
let small = [];
|
|
82
|
+
let smallChars = 0;
|
|
83
|
+
let lineBytes = 0;
|
|
84
|
+
let lastCodeUnit = -1;
|
|
81
85
|
let discarding = false;
|
|
86
|
+
const clearLine = () => {
|
|
87
|
+
blocks = [];
|
|
88
|
+
small = [];
|
|
89
|
+
smallChars = 0;
|
|
90
|
+
lineBytes = 0;
|
|
91
|
+
lastCodeUnit = -1;
|
|
92
|
+
};
|
|
93
|
+
const compact = () => {
|
|
94
|
+
if (small.length > 0)
|
|
95
|
+
blocks.push(small.join(""));
|
|
96
|
+
small = [];
|
|
97
|
+
smallChars = 0;
|
|
98
|
+
};
|
|
99
|
+
const append = (part) => {
|
|
100
|
+
if (part.length === 0)
|
|
101
|
+
return;
|
|
102
|
+
if (part.length >= blockChars) {
|
|
103
|
+
compact();
|
|
104
|
+
blocks.push(part);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
small.push(part);
|
|
108
|
+
smallChars += part.length;
|
|
109
|
+
if (smallChars >= blockChars)
|
|
110
|
+
compact();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
82
113
|
return {
|
|
83
114
|
push(chunk) {
|
|
84
115
|
const out = [];
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
|
|
116
|
+
let offset = 0;
|
|
117
|
+
while (offset < chunk.length) {
|
|
118
|
+
const newline = chunk.indexOf("\n", offset);
|
|
119
|
+
const end = newline < 0 ? chunk.length : newline;
|
|
120
|
+
if (!discarding) {
|
|
121
|
+
const part = chunk.slice(offset, end);
|
|
122
|
+
let addedBytes = Buffer.byteLength(part, "utf8");
|
|
123
|
+
// String callers may split a surrogate pair even though the stdio
|
|
124
|
+
// transport already decodes UTF-8 before delivering chunks.
|
|
125
|
+
const first = part.charCodeAt(0);
|
|
126
|
+
if (lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff && first >= 0xdc00 && first <= 0xdfff) {
|
|
127
|
+
addedBytes -= 2;
|
|
128
|
+
}
|
|
129
|
+
lineBytes += addedBytes;
|
|
130
|
+
if (lineBytes > maxLineBytes) {
|
|
131
|
+
out.push({ error: `MCP frame exceeded ${maxLineBytes} bytes` });
|
|
132
|
+
clearLine();
|
|
133
|
+
discarding = true;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
append(part);
|
|
137
|
+
if (part.length > 0)
|
|
138
|
+
lastCodeUnit = part.charCodeAt(part.length - 1);
|
|
139
|
+
}
|
|
91
140
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const line = buffer.slice(0, newlineIndex);
|
|
100
|
-
const lineBytes = Buffer.byteLength(line, "utf8");
|
|
101
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
102
|
-
bufferBytes -= lineBytes + 1; // consumed line plus its newline
|
|
103
|
-
const trimmed = line.trim();
|
|
104
|
-
if (trimmed.length === 0) {
|
|
105
|
-
newlineIndex = buffer.indexOf("\n");
|
|
106
|
-
continue;
|
|
141
|
+
if (newline < 0)
|
|
142
|
+
break;
|
|
143
|
+
if (!discarding) {
|
|
144
|
+
compact();
|
|
145
|
+
const line = blocks.join("").trim();
|
|
146
|
+
if (line.length > 0)
|
|
147
|
+
out.push(decodeLine(line));
|
|
107
148
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
else {
|
|
112
|
-
out.push(decodeLine(trimmed));
|
|
113
|
-
}
|
|
114
|
-
newlineIndex = buffer.indexOf("\n");
|
|
115
|
-
}
|
|
116
|
-
if (bufferBytes > maxLineBytes) {
|
|
117
|
-
out.push({ error: `MCP frame exceeded ${maxLineBytes} bytes` });
|
|
118
|
-
buffer = "";
|
|
119
|
-
bufferBytes = 0;
|
|
120
|
-
discarding = true;
|
|
149
|
+
clearLine();
|
|
150
|
+
discarding = false;
|
|
151
|
+
offset = newline + 1;
|
|
121
152
|
}
|
|
122
153
|
return out;
|
|
123
154
|
},
|
|
124
155
|
reset() {
|
|
125
|
-
|
|
126
|
-
bufferBytes = 0;
|
|
156
|
+
clearLine();
|
|
127
157
|
discarding = false;
|
|
128
158
|
},
|
|
129
159
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/core/mcp/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure JSON-RPC 2.0 framing for the MCP stdio transport.\n *\n * MCP stdio frames one JSON message per line on stdout; embedded newlines are\n * illegal, so decoding is a line split with a hard length ceiling. Nothing here\n * touches a process, a socket, or a clock — the transport owns all I/O so this\n * layer stays trivially testable.\n */\n\n/** Ceiling for a single decoded line. A server that exceeds it is malfunctioning, not slow. */\nexport const MAX_MESSAGE_LINE_BYTES = 16 * 1024 * 1024;\n\nexport type JsonRpcId = number | string;\n\nexport interface JsonRpcRequest {\n\treadonly jsonrpc: \"2.0\";\n\treadonly id: JsonRpcId;\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport interface JsonRpcNotification {\n\treadonly jsonrpc: \"2.0\";\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport interface JsonRpcErrorBody {\n\treadonly code: number;\n\treadonly message: string;\n\treadonly data?: unknown;\n}\n\nexport interface JsonRpcResponse {\n\treadonly jsonrpc: \"2.0\";\n\treadonly id: JsonRpcId;\n\treadonly result?: unknown;\n\treadonly error?: JsonRpcErrorBody;\n}\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonRpcResponse(message: unknown): message is JsonRpcResponse {\n\tif (!isRecord(message) || message.jsonrpc !== \"2.0\") return false;\n\tif (!(\"id\" in message)) return false;\n\tconst id = message.id;\n\tif (typeof id !== \"number\" && typeof id !== \"string\") return false;\n\t// JSON-RPC success and error are mutually exclusive; `error: null` is not a\n\t// valid error object, and a present error must carry its required fields.\n\tconst hasResult = \"result\" in message;\n\tconst hasError = \"error\" in message;\n\tif (hasResult === hasError) return false; // both present or both absent\n\tif (hasError) {\n\t\tconst error = message.error;\n\t\tif (!isRecord(error)) return false;\n\t\tif (typeof error.code !== \"number\" || !Number.isFinite(error.code)) return false;\n\t\tif (typeof error.message !== \"string\") return false;\n\t}\n\treturn true;\n}\n\nexport function isJsonRpcNotification(message: unknown): message is JsonRpcNotification {\n\treturn isRecord(message) && message.jsonrpc === \"2.0\" && typeof message.method === \"string\" && !(\"id\" in message);\n}\n\nexport function isJsonRpcRequest(message: unknown): message is JsonRpcRequest {\n\tif (!isRecord(message) || message.jsonrpc !== \"2.0\") return false;\n\tif (typeof message.method !== \"string\") return false;\n\tconst id = message.id;\n\treturn typeof id === \"number\" || typeof id === \"string\";\n}\n\n/** Serialize one message as a single stdio frame (JSON + newline). */\nexport function encodeMessage(message: JsonRpcMessage): string {\n\tconst line = JSON.stringify(message);\n\tif (line.includes(\"\\n\")) {\n\t\t// JSON.stringify escapes newlines, so this is unreachable for valid input;\n\t\t// keep the guard so a future custom serializer cannot corrupt framing.\n\t\tthrow new Error(\"MCP frame contains a literal newline\");\n\t}\n\treturn `${line}\\n`;\n}\n\nexport interface DecodedLine {\n\t/** Parsed message, when the line was valid JSON. */\n\treadonly message?: JsonRpcMessage;\n\t/** Why the line was dropped, when it was not usable. */\n\treadonly error?: string;\n}\n\n/**\n * Incremental newline-delimited JSON decoder.\n *\n * The limit is enforced in UTF-8 bytes against the raw frame, before JSON\n * parsing, so the same message is accepted or rejected regardless of how the\n * transport splits it into chunks. A line that exceeds\n * {@link MAX_MESSAGE_LINE_BYTES} is reported once, its bytes are discarded\n * without being retained, and decoding resynchronizes at the next newline —\n * a runaway server cannot exhaust memory by growing the retained buffer.\n *\n * `maxLineBytes` must be a positive finite integer; anything else is a\n * configuration error, not a size to guess at.\n */\nexport function createLineDecoder(maxLineBytes: number = MAX_MESSAGE_LINE_BYTES): {\n\tpush(chunk: string): DecodedLine[];\n\treset(): void;\n} {\n\tif (!Number.isSafeInteger(maxLineBytes) || maxLineBytes <= 0) {\n\t\tthrow new RangeError(`maxLineBytes must be a positive finite integer, got ${maxLineBytes}`);\n\t}\n\tlet buffer = \"\";\n\t/** UTF-8 byte length of `buffer` — the limit unit, not the JS string length. */\n\tlet bufferBytes = 0;\n\t/** True while the remainder of an oversized line is being discarded. */\n\tlet discarding = false;\n\n\treturn {\n\t\tpush(chunk: string): DecodedLine[] {\n\t\t\tconst out: DecodedLine[] = [];\n\t\t\tlet rest = chunk;\n\n\t\t\tif (discarding) {\n\t\t\t\t// Skip the tail of the rejected line without storing it.\n\t\t\t\tconst boundary = rest.indexOf(\"\\n\");\n\t\t\t\tif (boundary === -1) {\n\t\t\t\t\treturn out;\n\t\t\t\t}\n\t\t\t\tdiscarding = false;\n\t\t\t\trest = rest.slice(boundary + 1);\n\t\t\t}\n\n\t\t\tbuffer += rest;\n\t\t\tbufferBytes += Buffer.byteLength(rest, \"utf8\");\n\n\t\t\tlet newlineIndex = buffer.indexOf(\"\\n\");\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst line = buffer.slice(0, newlineIndex);\n\t\t\t\tconst lineBytes = Buffer.byteLength(line, \"utf8\");\n\t\t\t\tbuffer = buffer.slice(newlineIndex + 1);\n\t\t\t\tbufferBytes -= lineBytes + 1; // consumed line plus its newline\n\t\t\t\tconst trimmed = line.trim();\n\t\t\t\tif (trimmed.length === 0) {\n\t\t\t\t\tnewlineIndex = buffer.indexOf(\"\\n\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (lineBytes > maxLineBytes) {\n\t\t\t\t\tout.push({ error: `MCP frame exceeded ${maxLineBytes} bytes` });\n\t\t\t\t} else {\n\t\t\t\t\tout.push(decodeLine(trimmed));\n\t\t\t\t}\n\t\t\t\tnewlineIndex = buffer.indexOf(\"\\n\");\n\t\t\t}\n\n\t\t\tif (bufferBytes > maxLineBytes) {\n\t\t\t\tout.push({ error: `MCP frame exceeded ${maxLineBytes} bytes` });\n\t\t\t\tbuffer = \"\";\n\t\t\t\tbufferBytes = 0;\n\t\t\t\tdiscarding = true;\n\t\t\t}\n\t\t\treturn out;\n\t\t},\n\t\treset(): void {\n\t\t\tbuffer = \"\";\n\t\t\tbufferBytes = 0;\n\t\t\tdiscarding = false;\n\t\t},\n\t};\n}\n\nfunction decodeLine(line: string): DecodedLine {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch {\n\t\treturn { error: \"MCP frame is not valid JSON\" };\n\t}\n\tif (isJsonRpcResponse(parsed) || isJsonRpcNotification(parsed) || isJsonRpcRequest(parsed)) {\n\t\treturn { message: parsed };\n\t}\n\treturn { error: \"MCP frame is not a JSON-RPC 2.0 message\" };\n}\n\n/** Format a JSON-RPC error body for a human or a model. */\nexport function formatJsonRpcError(error: JsonRpcErrorBody): string {\n\tconst suffix = error.data === undefined ? \"\" : ` (${JSON.stringify(error.data)})`;\n\treturn `JSON-RPC error ${error.code}: ${error.message}${suffix}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/core/mcp/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAgCvD,SAAS,QAAQ,CAAC,KAAc,EAAoC;IACnE,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAgB,EAA8B;IAC/E,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;IACtB,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACnE,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAM,SAAS,GAAG,QAAQ,IAAI,OAAO,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,IAAI,OAAO,CAAC;IACpC,IAAI,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,CAAC,8BAA8B;IACxE,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QACjF,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAkC;IACvF,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC;AAAA,CAClH;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgB,EAA6B;IAC7E,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACrD,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;IACtB,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,CAAC;AAAA,CACxD;AAED,sEAAsE;AACtE,MAAM,UAAU,aAAa,CAAC,OAAuB,EAAU;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,2EAA2E;QAC3E,uEAAuE;QACvE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,IAAI,IAAI,CAAC;AAAA,CACnB;AASD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAY,GAAW,sBAAsB,EAG7E;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,UAAU,CAAC,uDAAuD,YAAY,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,4EAA4E;IAC5E,6EAA6E;IAC7E,MAAM,UAAU,GAAG,IAAI,CAAC;IACxB,IAAI,MAAM,GAAa,EAAE,CAAC;IAC1B,IAAI,KAAK,GAAa,EAAE,CAAC;IACzB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IACtB,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,SAAS,GAAG,GAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,EAAE,CAAC;QACZ,KAAK,GAAG,EAAE,CAAC;QACX,UAAU,GAAG,CAAC,CAAC;QACf,SAAS,GAAG,CAAC,CAAC;QACd,YAAY,GAAG,CAAC,CAAC,CAAC;IAAA,CAClB,CAAC;IACF,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAClD,KAAK,GAAG,EAAE,CAAC;QACX,UAAU,GAAG,CAAC,CAAC;IAAA,CACf,CAAC;IACF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAQ,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9B,IAAI,IAAI,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC;YAC1B,IAAI,UAAU,IAAI,UAAU;gBAAE,OAAO,EAAE,CAAC;QACzC,CAAC;IAAA,CACD,CAAC;IAEF,OAAO;QACN,IAAI,CAAC,KAAa,EAAiB;YAClC,MAAM,GAAG,GAAkB,EAAE,CAAC;YAC9B,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,OAAO,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC5C,MAAM,GAAG,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;gBACjD,IAAI,CAAC,UAAU,EAAE,CAAC;oBACjB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;oBACtC,IAAI,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;oBACjD,kEAAkE;oBAClE,4DAA4D;oBAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;oBACjC,IAAI,YAAY,IAAI,MAAM,IAAI,YAAY,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;wBAC5F,UAAU,IAAI,CAAC,CAAC;oBACjB,CAAC;oBACD,SAAS,IAAI,UAAU,CAAC;oBACxB,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;wBAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,YAAY,QAAQ,EAAE,CAAC,CAAC;wBAChE,SAAS,EAAE,CAAC;wBACZ,UAAU,GAAG,IAAI,CAAC;oBACnB,CAAC;yBAAM,CAAC;wBACP,MAAM,CAAC,IAAI,CAAC,CAAC;wBACb,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;4BAAE,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBACtE,CAAC;gBACF,CAAC;gBACD,IAAI,OAAO,GAAG,CAAC;oBAAE,MAAM;gBACvB,IAAI,CAAC,UAAU,EAAE,CAAC;oBACjB,OAAO,EAAE,CAAC;oBACV,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;oBACpC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;wBAAE,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;gBACjD,CAAC;gBACD,SAAS,EAAE,CAAC;gBACZ,UAAU,GAAG,KAAK,CAAC;gBACnB,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC;YACtB,CAAC;YACD,OAAO,GAAG,CAAC;QAAA,CACX;QACD,KAAK,GAAS;YACb,SAAS,EAAE,CAAC;YACZ,UAAU,GAAG,KAAK,CAAC;QAAA,CACnB;KACD,CAAC;AAAA,CACF;AAED,SAAS,UAAU,CAAC,IAAY,EAAe;IAC9C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,iBAAiB,CAAC,MAAM,CAAC,IAAI,qBAAqB,CAAC,MAAM,CAAC,IAAI,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC;AAAA,CAC5D;AAED,2DAA2D;AAC3D,MAAM,UAAU,kBAAkB,CAAC,KAAuB,EAAU;IACnE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IAClF,OAAO,kBAAkB,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,GAAG,MAAM,EAAE,CAAC;AAAA,CACjE","sourcesContent":["/**\n * Pure JSON-RPC 2.0 framing for the MCP stdio transport.\n *\n * MCP stdio frames one JSON message per line on stdout; embedded newlines are\n * illegal, so decoding is a line split with a hard length ceiling. Nothing here\n * touches a process, a socket, or a clock — the transport owns all I/O so this\n * layer stays trivially testable.\n */\n\n/** Ceiling for a single decoded line. A server that exceeds it is malfunctioning, not slow. */\nexport const MAX_MESSAGE_LINE_BYTES = 16 * 1024 * 1024;\n\nexport type JsonRpcId = number | string;\n\nexport interface JsonRpcRequest {\n\treadonly jsonrpc: \"2.0\";\n\treadonly id: JsonRpcId;\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport interface JsonRpcNotification {\n\treadonly jsonrpc: \"2.0\";\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport interface JsonRpcErrorBody {\n\treadonly code: number;\n\treadonly message: string;\n\treadonly data?: unknown;\n}\n\nexport interface JsonRpcResponse {\n\treadonly jsonrpc: \"2.0\";\n\treadonly id: JsonRpcId;\n\treadonly result?: unknown;\n\treadonly error?: JsonRpcErrorBody;\n}\n\nexport type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isJsonRpcResponse(message: unknown): message is JsonRpcResponse {\n\tif (!isRecord(message) || message.jsonrpc !== \"2.0\") return false;\n\tif (!(\"id\" in message)) return false;\n\tconst id = message.id;\n\tif (typeof id !== \"number\" && typeof id !== \"string\") return false;\n\t// JSON-RPC success and error are mutually exclusive; `error: null` is not a\n\t// valid error object, and a present error must carry its required fields.\n\tconst hasResult = \"result\" in message;\n\tconst hasError = \"error\" in message;\n\tif (hasResult === hasError) return false; // both present or both absent\n\tif (hasError) {\n\t\tconst error = message.error;\n\t\tif (!isRecord(error)) return false;\n\t\tif (typeof error.code !== \"number\" || !Number.isFinite(error.code)) return false;\n\t\tif (typeof error.message !== \"string\") return false;\n\t}\n\treturn true;\n}\n\nexport function isJsonRpcNotification(message: unknown): message is JsonRpcNotification {\n\treturn isRecord(message) && message.jsonrpc === \"2.0\" && typeof message.method === \"string\" && !(\"id\" in message);\n}\n\nexport function isJsonRpcRequest(message: unknown): message is JsonRpcRequest {\n\tif (!isRecord(message) || message.jsonrpc !== \"2.0\") return false;\n\tif (typeof message.method !== \"string\") return false;\n\tconst id = message.id;\n\treturn typeof id === \"number\" || typeof id === \"string\";\n}\n\n/** Serialize one message as a single stdio frame (JSON + newline). */\nexport function encodeMessage(message: JsonRpcMessage): string {\n\tconst line = JSON.stringify(message);\n\tif (line.includes(\"\\n\")) {\n\t\t// JSON.stringify escapes newlines, so this is unreachable for valid input;\n\t\t// keep the guard so a future custom serializer cannot corrupt framing.\n\t\tthrow new Error(\"MCP frame contains a literal newline\");\n\t}\n\treturn `${line}\\n`;\n}\n\nexport interface DecodedLine {\n\t/** Parsed message, when the line was valid JSON. */\n\treadonly message?: JsonRpcMessage;\n\t/** Why the line was dropped, when it was not usable. */\n\treadonly error?: string;\n}\n\n/**\n * Incremental newline-delimited JSON decoder.\n *\n * The limit is enforced in UTF-8 bytes against the raw frame, before JSON\n * parsing, so the same message is accepted or rejected regardless of how the\n * transport splits it into chunks. A line that exceeds\n * {@link MAX_MESSAGE_LINE_BYTES} is reported once, its bytes are discarded\n * without being retained, and decoding resynchronizes at the next newline —\n * a runaway server cannot exhaust memory by growing the retained buffer.\n *\n * `maxLineBytes` must be a positive finite integer; anything else is a\n * configuration error, not a size to guess at.\n */\nexport function createLineDecoder(maxLineBytes: number = MAX_MESSAGE_LINE_BYTES): {\n\tpush(chunk: string): DecodedLine[];\n\treset(): void;\n} {\n\tif (!Number.isSafeInteger(maxLineBytes) || maxLineBytes <= 0) {\n\t\tthrow new RangeError(`maxLineBytes must be a positive finite integer, got ${maxLineBytes}`);\n\t}\n\t// Batch small fragments so one-character delivery cannot retain millions of\n\t// array slots. Only newly arrived text is scanned; each line is joined once.\n\tconst blockChars = 4096;\n\tlet blocks: string[] = [];\n\tlet small: string[] = [];\n\tlet smallChars = 0;\n\tlet lineBytes = 0;\n\tlet lastCodeUnit = -1;\n\tlet discarding = false;\n\tconst clearLine = (): void => {\n\t\tblocks = [];\n\t\tsmall = [];\n\t\tsmallChars = 0;\n\t\tlineBytes = 0;\n\t\tlastCodeUnit = -1;\n\t};\n\tconst compact = (): void => {\n\t\tif (small.length > 0) blocks.push(small.join(\"\"));\n\t\tsmall = [];\n\t\tsmallChars = 0;\n\t};\n\tconst append = (part: string): void => {\n\t\tif (part.length === 0) return;\n\t\tif (part.length >= blockChars) {\n\t\t\tcompact();\n\t\t\tblocks.push(part);\n\t\t} else {\n\t\t\tsmall.push(part);\n\t\t\tsmallChars += part.length;\n\t\t\tif (smallChars >= blockChars) compact();\n\t\t}\n\t};\n\n\treturn {\n\t\tpush(chunk: string): DecodedLine[] {\n\t\t\tconst out: DecodedLine[] = [];\n\t\t\tlet offset = 0;\n\t\t\twhile (offset < chunk.length) {\n\t\t\t\tconst newline = chunk.indexOf(\"\\n\", offset);\n\t\t\t\tconst end = newline < 0 ? chunk.length : newline;\n\t\t\t\tif (!discarding) {\n\t\t\t\t\tconst part = chunk.slice(offset, end);\n\t\t\t\t\tlet addedBytes = Buffer.byteLength(part, \"utf8\");\n\t\t\t\t\t// String callers may split a surrogate pair even though the stdio\n\t\t\t\t\t// transport already decodes UTF-8 before delivering chunks.\n\t\t\t\t\tconst first = part.charCodeAt(0);\n\t\t\t\t\tif (lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff && first >= 0xdc00 && first <= 0xdfff) {\n\t\t\t\t\t\taddedBytes -= 2;\n\t\t\t\t\t}\n\t\t\t\t\tlineBytes += addedBytes;\n\t\t\t\t\tif (lineBytes > maxLineBytes) {\n\t\t\t\t\t\tout.push({ error: `MCP frame exceeded ${maxLineBytes} bytes` });\n\t\t\t\t\t\tclearLine();\n\t\t\t\t\t\tdiscarding = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tappend(part);\n\t\t\t\t\t\tif (part.length > 0) lastCodeUnit = part.charCodeAt(part.length - 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (newline < 0) break;\n\t\t\t\tif (!discarding) {\n\t\t\t\t\tcompact();\n\t\t\t\t\tconst line = blocks.join(\"\").trim();\n\t\t\t\t\tif (line.length > 0) out.push(decodeLine(line));\n\t\t\t\t}\n\t\t\t\tclearLine();\n\t\t\t\tdiscarding = false;\n\t\t\t\toffset = newline + 1;\n\t\t\t}\n\t\t\treturn out;\n\t\t},\n\t\treset(): void {\n\t\t\tclearLine();\n\t\t\tdiscarding = false;\n\t\t},\n\t};\n}\n\nfunction decodeLine(line: string): DecodedLine {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(line);\n\t} catch {\n\t\treturn { error: \"MCP frame is not valid JSON\" };\n\t}\n\tif (isJsonRpcResponse(parsed) || isJsonRpcNotification(parsed) || isJsonRpcRequest(parsed)) {\n\t\treturn { message: parsed };\n\t}\n\treturn { error: \"MCP frame is not a JSON-RPC 2.0 message\" };\n}\n\n/** Format a JSON-RPC error body for a human or a model. */\nexport function formatJsonRpcError(error: JsonRpcErrorBody): string {\n\tconst suffix = error.data === undefined ? \"\" : ` (${JSON.stringify(error.data)})`;\n\treturn `JSON-RPC error ${error.code}: ${error.message}${suffix}`;\n}\n"]}
|
|
@@ -172,6 +172,10 @@ export declare class DefaultPackageManager implements PackageManager {
|
|
|
172
172
|
private uninstallNpm;
|
|
173
173
|
private installGit;
|
|
174
174
|
private updateGit;
|
|
175
|
+
private hasMissingGitDependencies;
|
|
176
|
+
private repairMissingGitDependencies;
|
|
177
|
+
private getGitUpdateMarkerPath;
|
|
178
|
+
private cleanAndInstallGitDependencies;
|
|
175
179
|
private ensureGitRef;
|
|
176
180
|
private refreshTemporaryGitSource;
|
|
177
181
|
private removeGit;
|