@gajae-code/coding-agent 0.17.2 → 0.17.5
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 +267 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/github-release.d.ts +17 -0
- package/dist/types/cli/public-command-entry.d.ts +56 -0
- package/dist/types/cli/public-command-errors.d.ts +126 -0
- package/dist/types/cli/public-command-evidence.d.ts +99 -0
- package/dist/types/cli/public-command-help.d.ts +65 -0
- package/dist/types/cli/public-command-registry.d.ts +69 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/cli/skills-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +4 -50
- package/dist/types/commands/sdk.d.ts +20 -37
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/commands/skills.d.ts +9 -0
- package/dist/types/config/autorouting-tier-map.d.ts +1049 -3
- package/dist/types/config/file-lock.d.ts +13 -3
- package/dist/types/config/model-profile-activation.d.ts +45 -1
- package/dist/types/config/model-registry.d.ts +14 -3
- package/dist/types/config/model-resolver.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +11 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
- package/dist/types/daemon/operator-contract.d.ts +5 -1
- package/dist/types/eval/js/worker-protocol.d.ts +2 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +12 -0
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +11 -2
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +14 -1
- package/dist/types/harness-control-plane/control-endpoint.d.ts +4 -1
- package/dist/types/harness-control-plane/finalize.d.ts +14 -3
- package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/parse.d.ts +9 -1
- package/dist/types/main.d.ts +23 -1
- package/dist/types/modes/acp/acp-agent.d.ts +55 -1
- package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
- package/dist/types/modes/components/session-selector.d.ts +5 -2
- package/dist/types/modes/components/user-message-selector.d.ts +5 -2
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
- package/dist/types/modes/types.d.ts +2 -2
- package/dist/types/runtime/process-lifecycle.d.ts +2 -0
- package/dist/types/runtime-mcp/loader.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +69 -6
- package/dist/types/runtime-mcp/pool.d.ts +17 -0
- package/dist/types/runtime-mcp/redaction.d.ts +20 -0
- package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
- package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
- package/dist/types/sdk/broker/broker.d.ts +17 -4
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/identity.d.ts +5 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
- package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
- package/dist/types/sdk/broker/managed-task-dag.d.ts +1335 -0
- package/dist/types/sdk/broker/managed-task-verification.d.ts +37 -0
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/session-index.d.ts +22 -3
- package/dist/types/sdk/broker/spawn-authority.d.ts +12 -0
- package/dist/types/sdk/broker/startup-failure.d.ts +4 -0
- package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
- package/dist/types/sdk/bus/index.d.ts +19 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
- package/dist/types/sdk/cli/master-cli.d.ts +6 -10
- package/dist/types/sdk/cli/rows.d.ts +19 -1
- package/dist/types/sdk/cli/session-cli.d.ts +22 -5
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/guides/cli.d.ts +3 -4
- package/dist/types/sdk/guides/verify.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +9 -0
- package/dist/types/sdk/host/session-runtime.d.ts +31 -0
- package/dist/types/sdk/host/types.d.ts +10 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +85 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +37 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/session.d.ts +3 -3
- package/dist/types/sdk/startup-capability.d.ts +2 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +48 -4
- package/dist/types/sdk/transport/stdio.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +175 -23
- package/dist/types/session/fallback-chain-controller.d.ts +6 -6
- package/dist/types/session/fold-coordinator.d.ts +17 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
- package/dist/types/session/session-manager.d.ts +64 -1
- package/dist/types/session/session-work-lease.d.ts +14 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
- package/dist/types/setup/provider-onboarding.d.ts +81 -1
- package/dist/types/task/types.d.ts +2 -0
- package/dist/types/tools/bash-interactive.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +37 -11
- package/dist/types/tools/fetch.d.ts +10 -1
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/steer-fold.d.ts +31 -0
- package/dist/types/tools/subagent.d.ts +8 -1
- package/dist/types/utils/git.d.ts +28 -2
- package/dist/types/utils/mupdf-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf.d.ts +4 -0
- package/dist/types/utils/session-title-generation.d.ts +6 -0
- package/dist/types/utils/title-generator.d.ts +11 -2
- package/dist/types/web/scrapers/types.d.ts +3 -0
- package/package.json +18 -10
- package/scripts/build-binary.ts +9 -3
- package/scripts/compile-args.ts +0 -9
- package/scripts/embed-mupdf.ts +52 -0
- package/scripts/generate-sdk-operation-inventory.ts +31 -1
- package/scripts/vendor-markit.ts +196 -0
- package/src/async/job-manager.ts +22 -1
- package/src/cli/daemon-cli.ts +53 -18
- package/src/cli/github-release.ts +87 -5
- package/src/cli/mcp-cli.ts +38 -4
- package/src/cli/public-command-entry.ts +450 -0
- package/src/cli/public-command-errors.ts +581 -0
- package/src/cli/public-command-evidence.ts +717 -0
- package/src/cli/public-command-help.ts +360 -0
- package/src/cli/public-command-registry.ts +541 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +10 -1
- package/src/cli/skills-cli.ts +46 -2
- package/src/cli/stats-cli.ts +11 -1
- package/src/cli.ts +61 -1
- package/src/commands/daemon.ts +65 -69
- package/src/commands/sdk.ts +175 -117
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +543 -3
- package/src/config/file-lock.ts +206 -65
- package/src/config/model-discovery-manager.ts +4 -4
- package/src/config/model-profile-activation.ts +417 -66
- package/src/config/model-registry.ts +254 -36
- package/src/config/model-resolver.ts +6 -1
- package/src/config/settings-schema.ts +26 -2
- package/src/coordinator-mcp/policy.ts +10 -5
- package/src/coordinator-mcp/question-gate-codec.ts +17 -0
- package/src/coordinator-mcp/question-state.ts +179 -9
- package/src/coordinator-mcp/server.ts +521 -87
- package/src/coordinator-mcp/session-reaper.ts +6 -4
- package/src/crash/record-loader.ts +6 -2
- package/src/daemon/operator-contract.ts +26 -12
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/context-manager.ts +8 -8
- package/src/eval/js/worker-core.ts +9 -4
- package/src/eval/js/worker-protocol.ts +2 -0
- package/src/exec/bash-executor.ts +3 -0
- package/src/extensibility/extensions/runner.ts +52 -4
- package/src/extensibility/extensions/types.ts +10 -0
- package/src/extensibility/gjc-plugins/registry.ts +196 -37
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +46 -8
- package/src/extensibility/runtime-skill-discovery.ts +61 -3
- package/src/extensibility/shared-events.ts +3 -1
- package/src/gjc-runtime/session-state-lock.ts +165 -24
- package/src/gjc-runtime/state-writer.ts +317 -6
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/harness-control-plane/control-endpoint.ts +24 -6
- package/src/harness-control-plane/finalize.ts +312 -25
- package/src/harness-control-plane/owner.ts +323 -89
- package/src/internal-urls/docs-index.generated.ts +23 -20
- package/src/internal-urls/embedded-protocol.ts +83 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/parse.ts +21 -1
- package/src/internal-urls/router.ts +4 -2
- package/src/main.ts +192 -74
- package/src/modes/acp/acp-agent.ts +973 -56
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/gjc-bundle-settings.ts +21 -1
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/tool-status-header.ts +72 -6
- package/src/modes/components/user-message-selector.ts +57 -15
- package/src/modes/controllers/command-controller.ts +58 -32
- package/src/modes/controllers/extension-ui-controller.ts +33 -7
- package/src/modes/controllers/input-controller.ts +4 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +161 -15
- package/src/modes/interactive-mode.ts +21 -7
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
- package/src/modes/types.ts +2 -2
- package/src/prompts/agent-fragments/browser-aside.md +1 -0
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/read.md +2 -0
- package/src/runtime/network-prewarm-service.test.ts +36 -1
- package/src/runtime/network-prewarm-service.ts +28 -3
- package/src/runtime/process-lifecycle.ts +22 -11
- package/src/runtime-mcp/config.ts +16 -1
- package/src/runtime-mcp/loader.ts +6 -3
- package/src/runtime-mcp/manager.ts +845 -109
- package/src/runtime-mcp/pool.test.ts +31 -0
- package/src/runtime-mcp/pool.ts +304 -28
- package/src/runtime-mcp/redaction.ts +42 -0
- package/src/runtime-mcp/startup-policy.ts +7 -0
- package/src/runtime-mcp/tool-bridge.ts +71 -0
- package/src/runtime-mcp/transports/http.ts +108 -25
- package/src/sdk/acp/adapter.ts +27 -8
- package/src/sdk/broker/broker.ts +1431 -198
- package/src/sdk/broker/ensure.ts +21 -3
- package/src/sdk/broker/identity.ts +22 -1
- package/src/sdk/broker/lifecycle-ledger.ts +95 -31
- package/src/sdk/broker/lifecycle.ts +690 -141
- package/src/sdk/broker/managed-task-dag.ts +1927 -0
- package/src/sdk/broker/managed-task-verification.ts +642 -0
- package/src/sdk/broker/runtime.ts +1 -1
- package/src/sdk/broker/session-index.ts +97 -4
- package/src/sdk/broker/spawn-authority.ts +175 -14
- package/src/sdk/broker/startup-failure.ts +80 -1
- package/src/sdk/broker/stderr-drainer.ts +59 -0
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/index.ts +707 -81
- package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -3
- package/src/sdk/cli/master-cli.ts +47 -36
- package/src/sdk/cli/rows.ts +30 -7
- package/src/sdk/cli/session-cli.ts +705 -184
- package/src/sdk/client/client.ts +18 -0
- package/src/sdk/guides/catalog.ts +2 -2
- package/src/sdk/guides/cli.ts +20 -62
- package/src/sdk/guides/verify.ts +14 -0
- package/src/sdk/host/host.ts +49 -5
- package/src/sdk/host/session-runtime.test.ts +184 -2
- package/src/sdk/host/session-runtime.ts +322 -101
- package/src/sdk/host/types.ts +12 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
- package/src/sdk/host/websocket-transport.ts +40 -6
- package/src/sdk/index.ts +11 -0
- package/src/sdk/lifecycle/service.ts +2 -0
- package/src/sdk/prompt-deadline-flush.ts +356 -0
- package/src/sdk/prompt-deadline-manager.ts +133 -1
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/session-router.ts +9 -1
- package/src/sdk/session.ts +763 -116
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +397 -42
- package/src/sdk/transport/socket.ts +12 -11
- package/src/sdk/transport/stdio.ts +7 -3
- package/src/session/agent-session.ts +2472 -407
- package/src/session/fallback-chain-controller.ts +6 -7
- package/src/session/fold-coordinator.ts +24 -5
- package/src/session/internal/managed-session-scope.ts +68 -18
- package/src/session/internal/managed-session-storage.ts +334 -12
- package/src/session/session-manager.ts +382 -44
- package/src/session/session-work-lease.ts +34 -0
- package/src/setup/model-onboarding-guidance.ts +1 -1
- package/src/setup/provider-onboarding.ts +397 -24
- package/src/setup/provider-presets.json +2 -1
- package/src/slash-commands/builtin-registry.ts +259 -15
- package/src/task/executor.ts +46 -17
- package/src/task/index.ts +14 -2
- package/src/task/types.ts +2 -0
- package/src/tools/bash-interactive.ts +2 -0
- package/src/tools/bash.ts +118 -78
- package/src/tools/fetch.ts +140 -25
- package/src/tools/job.ts +151 -25
- package/src/tools/output-meta.ts +15 -2
- package/src/tools/path-utils.ts +27 -3
- package/src/tools/read.ts +3 -3
- package/src/tools/steer-fold.ts +107 -0
- package/src/tools/subagent-render.ts +24 -1
- package/src/tools/subagent.ts +27 -1
- package/src/tools/tool-catalog.generated.ts +29 -2
- package/src/tools/tool-errors.ts +4 -0
- package/src/utils/git.ts +53 -3
- package/src/utils/markit.ts +55 -41
- package/src/utils/mupdf-assets.d.ts +4 -0
- package/src/utils/mupdf-embedded.ts +4 -0
- package/src/utils/mupdf.ts +136 -0
- package/src/utils/session-title-generation.ts +29 -0
- package/src/utils/title-generator.ts +46 -2
- package/src/web/scrapers/types.ts +24 -3
- package/vendor/markit-ai/LICENSE +21 -0
- package/vendor/markit-ai/MANIFEST.json +412 -0
- package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/config.js +139 -0
- package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
- package/vendor/markit-ai/dist/commands/convert.js +113 -0
- package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/formats.js +92 -0
- package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/init.js +29 -0
- package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/onboard.js +61 -0
- package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/plugin.js +58 -0
- package/vendor/markit-ai/dist/config.d.ts +26 -0
- package/vendor/markit-ai/dist/config.js +42 -0
- package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/audio.js +104 -0
- package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/csv.js +83 -0
- package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/docx.js +57 -0
- package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/epub.js +128 -0
- package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
- package/vendor/markit-ai/dist/converters/github.js +148 -0
- package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/html.js +30 -0
- package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/image.js +124 -0
- package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
- package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
- package/vendor/markit-ai/dist/converters/iwork.js +391 -0
- package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/json.js +21 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
- package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
- package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
- package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
- package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
- package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
- package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
- package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/pptx.js +289 -0
- package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
- package/vendor/markit-ai/dist/converters/rss.js +138 -0
- package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
- package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
- package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/xml.js +18 -0
- package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/yaml.js +17 -0
- package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/zip.js +57 -0
- package/vendor/markit-ai/dist/index.d.ts +30 -0
- package/vendor/markit-ai/dist/index.js +26 -0
- package/vendor/markit-ai/dist/main.d.ts +2 -0
- package/vendor/markit-ai/dist/main.js +191 -0
- package/vendor/markit-ai/dist/markit.d.ts +45 -0
- package/vendor/markit-ai/dist/markit.js +267 -0
- package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
- package/vendor/markit-ai/dist/plugins/api.js +42 -0
- package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
- package/vendor/markit-ai/dist/plugins/index.js +3 -0
- package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/installer.js +176 -0
- package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
- package/vendor/markit-ai/dist/plugins/loader.js +61 -0
- package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/types.js +1 -0
- package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
- package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
- package/vendor/markit-ai/dist/providers/index.js +56 -0
- package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/openai.js +76 -0
- package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
- package/vendor/markit-ai/dist/providers/types.js +1 -0
- package/vendor/markit-ai/dist/types.d.ts +36 -0
- package/vendor/markit-ai/dist/types.js +1 -0
- package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
- package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
- package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
- package/vendor/markit-ai/dist/utils/output.js +31 -0
- package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
- package/vendor/markit-ai/dist/utils/turndown.js +70 -0
- package/vendor/markit-ai/package.json +74 -0
- package/vendor/markit-ai.patch +49 -0
package/src/sdk/session.ts
CHANGED
|
@@ -59,6 +59,7 @@ import { loadCapability, reset as resetCapabilities } from "../capability";
|
|
|
59
59
|
import { type Rule, ruleCapability, setActiveRules } from "../capability/rule";
|
|
60
60
|
import type { SourceMeta } from "../capability/types";
|
|
61
61
|
import { AUTOROUTING_INACTIVE_WARNING } from "../config/autorouting-contract";
|
|
62
|
+
import { ModelProfileCredentialError, resolveMissingSessionModelRecovery } from "../config/model-profile-activation";
|
|
62
63
|
import { resolveModelProfileName } from "../config/model-profile-contract";
|
|
63
64
|
import { resolveProfileBindings } from "../config/model-profiles";
|
|
64
65
|
import { kNoAuth, ModelRegistry } from "../config/model-registry";
|
|
@@ -66,9 +67,11 @@ import {
|
|
|
66
67
|
formatModelString,
|
|
67
68
|
parseModelPattern,
|
|
68
69
|
parseModelString,
|
|
70
|
+
refreshMissingQualifiedModelProviders,
|
|
69
71
|
resolveAllowedModels,
|
|
70
72
|
resolveModelChainWithAuth,
|
|
71
73
|
resolveModelRoleValue,
|
|
74
|
+
resolveStartupModelRefreshSelectors,
|
|
72
75
|
type ScopedModelSelection,
|
|
73
76
|
} from "../config/model-resolver";
|
|
74
77
|
import { normalizeModelSelectorValue } from "../config/model-selector-value";
|
|
@@ -115,9 +118,11 @@ import { resolveCurrentPhaseForParent } from "../extensibility/gjc-plugins/injec
|
|
|
115
118
|
import { currentActivationFingerprint } from "../extensibility/gjc-plugins/lifecycle";
|
|
116
119
|
import {
|
|
117
120
|
buildPluginMcpConfigs,
|
|
121
|
+
type GjcPluginMcpServerProvenance,
|
|
118
122
|
getGjcPluginToolDeclarations,
|
|
119
123
|
loadAlwaysOnPluginTools,
|
|
120
124
|
renderAlwaysOnSystemAppendices,
|
|
125
|
+
safePluginMcpDiagnostic,
|
|
121
126
|
} from "../extensibility/gjc-plugins/runtime-adapters";
|
|
122
127
|
import {
|
|
123
128
|
GjcRuntimeFindingAccumulator,
|
|
@@ -144,8 +149,16 @@ import {
|
|
|
144
149
|
createOptionalRuntimeServices,
|
|
145
150
|
type OptionalRuntimeServicesOverrides,
|
|
146
151
|
} from "../runtime/optional-runtime-services";
|
|
147
|
-
import {
|
|
152
|
+
import {
|
|
153
|
+
DeferredMCPTool,
|
|
154
|
+
isMCPStartupTimeoutError,
|
|
155
|
+
loadAllMCPConfigs,
|
|
156
|
+
MCPManager,
|
|
157
|
+
type MCPToolCache,
|
|
158
|
+
resolveMCPToolCache,
|
|
159
|
+
} from "../runtime-mcp";
|
|
148
160
|
import type { MCPLoadResult } from "../runtime-mcp/manager";
|
|
161
|
+
import { MCP_STARTUP_WAIT_GRACE_MS } from "../runtime-mcp/startup-policy";
|
|
149
162
|
import type { MCPServerConfig } from "../runtime-mcp/types";
|
|
150
163
|
import {
|
|
151
164
|
getNotificationConfig,
|
|
@@ -608,9 +621,9 @@ export interface CreateAgentSessionOptions {
|
|
|
608
621
|
inheritedAsyncJobManager?: AsyncJobManager;
|
|
609
622
|
/**
|
|
610
623
|
* W6b: the parent's scope-held MCP facade, handed to a canonical sub-session so
|
|
611
|
-
* it can inherit
|
|
612
|
-
*
|
|
613
|
-
*
|
|
624
|
+
* it can inherit MCP tools without owning the manager. Replaces the removed
|
|
625
|
+
* `MCPManager.instance()` inheritance path; the sub-session subscribes to catalog
|
|
626
|
+
* changes but never connects or disposes this manager.
|
|
614
627
|
*/
|
|
615
628
|
inheritedMcpManager?: import("../runtime-mcp/manager").MCPManager;
|
|
616
629
|
|
|
@@ -1247,6 +1260,29 @@ const DEFERRED_MCP_CONFIG_STARTUP_ERROR = "MCP tools could not be loaded.";
|
|
|
1247
1260
|
const MAX_EXACT_MCP_TOOL_NAME_LENGTH = 100;
|
|
1248
1261
|
const pluginMcpManagerServers = new WeakMap<MCPManager, ReadonlySet<string>>();
|
|
1249
1262
|
const conventionalMcpManagerServers = new WeakMap<MCPManager, ReadonlySet<string>>();
|
|
1263
|
+
const failedPluginMcpManagerServers = new WeakMap<MCPManager, ReadonlySet<string>>();
|
|
1264
|
+
const pluginMcpCleanupFailures = new WeakSet<MCPManager>();
|
|
1265
|
+
|
|
1266
|
+
function sameMcpToolSnapshot(first: readonly CustomTool[], second: readonly CustomTool[]): boolean {
|
|
1267
|
+
return first.length === second.length && first.every((tool, index) => tool === second[index]);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function classifyInheritedMcpToolNames(
|
|
1271
|
+
manager: MCPManager,
|
|
1272
|
+
tools: readonly CustomTool[],
|
|
1273
|
+
): { pluginMcpToolNames: string[]; conventionalMcpToolNames: string[] } {
|
|
1274
|
+
const pluginServers = pluginMcpManagerServers.get(manager);
|
|
1275
|
+
const conventionalServers = conventionalMcpManagerServers.get(manager);
|
|
1276
|
+
const pluginMcpToolNames: string[] = [];
|
|
1277
|
+
const conventionalMcpToolNames: string[] = [];
|
|
1278
|
+
for (const tool of tools) {
|
|
1279
|
+
const serverName = tool.mcpServerName;
|
|
1280
|
+
if (serverName === undefined) continue;
|
|
1281
|
+
if (pluginServers?.has(serverName)) pluginMcpToolNames.push(tool.name);
|
|
1282
|
+
else if (conventionalServers?.has(serverName)) conventionalMcpToolNames.push(tool.name);
|
|
1283
|
+
}
|
|
1284
|
+
return { pluginMcpToolNames, conventionalMcpToolNames };
|
|
1285
|
+
}
|
|
1250
1286
|
|
|
1251
1287
|
class ExactMcpToolNameCollisionError extends Error {
|
|
1252
1288
|
constructor(toolNames: Iterable<string>) {
|
|
@@ -1261,23 +1297,43 @@ class ExactMcpToolNameCollisionError extends Error {
|
|
|
1261
1297
|
class McpManagerCleanupError extends Error {
|
|
1262
1298
|
readonly code = "MCP_MANAGER_CLEANUP_FAILED";
|
|
1263
1299
|
constructor(cause: unknown) {
|
|
1264
|
-
super(`Owned MCP manager cleanup failed: ${
|
|
1300
|
+
super(`Owned MCP manager cleanup failed: ${safePluginMcpDiagnostic(cause)}`, { cause });
|
|
1265
1301
|
this.name = "McpManagerCleanupError";
|
|
1266
1302
|
}
|
|
1267
1303
|
}
|
|
1268
1304
|
|
|
1305
|
+
class McpStartupCleanupRetryError extends Error {
|
|
1306
|
+
readonly primaryError: unknown;
|
|
1307
|
+
constructor(primaryError: unknown) {
|
|
1308
|
+
super("Owned MCP manager cleanup failed during startup", { cause: primaryError });
|
|
1309
|
+
this.name = "McpStartupCleanupRetryError";
|
|
1310
|
+
this.primaryError = primaryError;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1269
1314
|
class McpManagerCleanupDiagnosticError extends Error {
|
|
1270
1315
|
readonly code = "MCP_MANAGER_CLEANUP_FAILED";
|
|
1271
1316
|
readonly primaryError: unknown;
|
|
1272
1317
|
readonly cleanupDiagnostic: { code: "MCP_MANAGER_CLEANUP_FAILED"; cause: unknown };
|
|
1273
1318
|
constructor(primaryError: unknown, cleanupError: unknown) {
|
|
1274
|
-
super(
|
|
1319
|
+
super(safePluginMcpDiagnostic(primaryError), { cause: primaryError });
|
|
1275
1320
|
this.name = "McpManagerCleanupDiagnosticError";
|
|
1276
1321
|
this.primaryError = primaryError;
|
|
1277
1322
|
this.cleanupDiagnostic = { code: "MCP_MANAGER_CLEANUP_FAILED", cause: cleanupError };
|
|
1278
1323
|
}
|
|
1279
1324
|
}
|
|
1280
1325
|
|
|
1326
|
+
class McpPluginStartupCleanupDiagnosticError extends Error {
|
|
1327
|
+
readonly primaryError: unknown;
|
|
1328
|
+
readonly cleanupDiagnostic: { code: "MCP_MANAGER_CLEANUP_FAILED"; cause: unknown };
|
|
1329
|
+
constructor(primaryError: unknown, cleanupError: unknown) {
|
|
1330
|
+
super("GJC plugin MCP startup and cleanup failed");
|
|
1331
|
+
this.name = "McpPluginStartupCleanupDiagnosticError";
|
|
1332
|
+
this.primaryError = primaryError;
|
|
1333
|
+
this.cleanupDiagnostic = { code: "MCP_MANAGER_CLEANUP_FAILED", cause: cleanupError };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1281
1337
|
function safeIsInstanceOf<T extends object>(value: unknown, ctor: abstract new (...args: any[]) => T): boolean {
|
|
1282
1338
|
try {
|
|
1283
1339
|
return value instanceof ctor;
|
|
@@ -1300,8 +1356,8 @@ function safeCleanupDiagnosticForLog(value: unknown): { code: string; cause: str
|
|
|
1300
1356
|
const code = safeReadProperty(value, "code");
|
|
1301
1357
|
const nestedCause = safeReadProperty(value, "cause");
|
|
1302
1358
|
return {
|
|
1303
|
-
code: typeof code === "string" ? code : "MCP_MANAGER_CLEANUP_FAILED",
|
|
1304
|
-
cause:
|
|
1359
|
+
code: typeof code === "string" ? safePluginMcpDiagnostic(code) : "MCP_MANAGER_CLEANUP_FAILED",
|
|
1360
|
+
cause: safePluginMcpDiagnostic(nestedCause === undefined ? value : nestedCause),
|
|
1305
1361
|
};
|
|
1306
1362
|
}
|
|
1307
1363
|
function safeReadCleanupDiagnostic(value: unknown): unknown {
|
|
@@ -1519,7 +1575,33 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1519
1575
|
let asyncJobManagerOwned = false;
|
|
1520
1576
|
let asyncJobManagerAdmitted = false;
|
|
1521
1577
|
let priorAsyncJobManager: AsyncJobManager | undefined;
|
|
1522
|
-
|
|
1578
|
+
type OwnedMcpManagerCleanup = () => Promise<void>;
|
|
1579
|
+
let cleanupOwnedMcpManager: OwnedMcpManagerCleanup | undefined;
|
|
1580
|
+
let cleanupOwnedMcpManagerOwner: MCPManager | undefined;
|
|
1581
|
+
const registerOwnedMcpManagerCleanup = (owned: MCPManager): OwnedMcpManagerCleanup => {
|
|
1582
|
+
let cleanupAttempt: Promise<void> | undefined;
|
|
1583
|
+
const cleanup: OwnedMcpManagerCleanup = () => {
|
|
1584
|
+
if (cleanupAttempt) return cleanupAttempt;
|
|
1585
|
+
cleanupAttempt = Promise.resolve()
|
|
1586
|
+
.then(() => owned.disconnectAll())
|
|
1587
|
+
.then(
|
|
1588
|
+
() => {
|
|
1589
|
+
if (cleanupOwnedMcpManager === cleanup) {
|
|
1590
|
+
cleanupOwnedMcpManager = undefined;
|
|
1591
|
+
cleanupOwnedMcpManagerOwner = undefined;
|
|
1592
|
+
}
|
|
1593
|
+
},
|
|
1594
|
+
error => {
|
|
1595
|
+
cleanupAttempt = undefined;
|
|
1596
|
+
throw error;
|
|
1597
|
+
},
|
|
1598
|
+
);
|
|
1599
|
+
return cleanupAttempt;
|
|
1600
|
+
};
|
|
1601
|
+
cleanupOwnedMcpManager = cleanup;
|
|
1602
|
+
cleanupOwnedMcpManagerOwner = owned;
|
|
1603
|
+
return cleanup;
|
|
1604
|
+
};
|
|
1523
1605
|
const agentRegistry = options.agentRegistry ?? AgentRegistry.global();
|
|
1524
1606
|
const resolvedAgentId = options.agentId ?? options.parentTaskPrefix ?? MAIN_AGENT_ID;
|
|
1525
1607
|
const resolvedAgentDisplayName = options.agentDisplayName ?? (isCanonicalSubSession ? "sub" : "main");
|
|
@@ -1557,6 +1639,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1557
1639
|
unsubscribeCredentialDisabled = undefined;
|
|
1558
1640
|
unsubscribe?.();
|
|
1559
1641
|
};
|
|
1642
|
+
let inheritedMcpToolsPublisher: ((tools: readonly CustomTool[]) => void) | undefined;
|
|
1643
|
+
let inheritedMcpToolsUnsubscribe: (() => void) | undefined;
|
|
1644
|
+
const stopInheritedMcpToolsSubscription = (): void => {
|
|
1645
|
+
inheritedMcpToolsPublisher = undefined;
|
|
1646
|
+
const unsubscribe = inheritedMcpToolsUnsubscribe;
|
|
1647
|
+
inheritedMcpToolsUnsubscribe = undefined;
|
|
1648
|
+
unsubscribe?.();
|
|
1649
|
+
};
|
|
1560
1650
|
|
|
1561
1651
|
try {
|
|
1562
1652
|
// Subscribe before any getApiKey() call so startup model probes can't fire a
|
|
@@ -1903,6 +1993,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1903
1993
|
);
|
|
1904
1994
|
}
|
|
1905
1995
|
if (!options.modelRegistry && !attemptedStartupCacheAdmission) {
|
|
1996
|
+
// Match the CLI's awaited startup discovery: the background refresh below
|
|
1997
|
+
// is not a readiness barrier, so resolving a fresh configured default first
|
|
1998
|
+
// can reject a model that the provider would discover moments later.
|
|
1999
|
+
const startupModelSelectors = resolveStartupModelRefreshSelectors(
|
|
2000
|
+
{
|
|
2001
|
+
model: options.modelPattern ?? (options.model ? formatModelString(options.model) : undefined),
|
|
2002
|
+
credential: options.credentialSelector ? "session" : undefined,
|
|
2003
|
+
resume: hasExistingSession,
|
|
2004
|
+
hasStartupProfile: Boolean(options.activeModelProfile || settings.get("modelProfile.default")),
|
|
2005
|
+
},
|
|
2006
|
+
settings,
|
|
2007
|
+
);
|
|
2008
|
+
await refreshMissingQualifiedModelProviders(startupModelSelectors, modelRegistry, credentialSessionId);
|
|
1906
2009
|
modelRegistry.refreshInBackground("online-if-uncached", credentialSessionId);
|
|
1907
2010
|
}
|
|
1908
2011
|
|
|
@@ -1968,9 +2071,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1968
2071
|
const persistedProfileOwnsDefault = acceptedPersistedProfileName
|
|
1969
2072
|
? resolveProfileBindings(persistedProfiles.get(acceptedPersistedProfileName)!).defaultSelector !== undefined
|
|
1970
2073
|
: false;
|
|
1971
|
-
|
|
2074
|
+
let startupActiveModelProfile =
|
|
1972
2075
|
acceptedInheritedProfileName ??
|
|
1973
2076
|
(!hasExplicitModel && acceptedPersistedProfileName ? acceptedPersistedProfileName : undefined);
|
|
2077
|
+
let savedDefaultWasUnresolved = false;
|
|
2078
|
+
let deferredMissingSessionRecovery = false;
|
|
2079
|
+
let retainedRecoveryBindingsAfterLateRestore = false;
|
|
2080
|
+
let recoveredSessionDefault:
|
|
2081
|
+
| {
|
|
2082
|
+
entries: string[];
|
|
2083
|
+
profileName: string;
|
|
2084
|
+
activeIndex: number;
|
|
2085
|
+
thinkingLevel?: ThinkingLevel;
|
|
2086
|
+
explicitThinkingLevel: boolean;
|
|
2087
|
+
skips: Array<{ selector: string; reason: string }>;
|
|
2088
|
+
}
|
|
2089
|
+
| undefined;
|
|
1974
2090
|
// If session has data, restore its configured default chain rather than the
|
|
1975
2091
|
// scalar runtime model, which may be a stale fallback from the prior run.
|
|
1976
2092
|
if (!hasExplicitModel && !model && hasExistingSession && defaultModelEntries.length > 0) {
|
|
@@ -1994,6 +2110,33 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1994
2110
|
if (model && preferredCredentialProvider && model.provider !== preferredCredentialProvider) {
|
|
1995
2111
|
model = undefined;
|
|
1996
2112
|
}
|
|
2113
|
+
savedDefaultWasUnresolved = !model;
|
|
2114
|
+
if (!model && resumeModelBehavior !== "useCurrentDefault") {
|
|
2115
|
+
try {
|
|
2116
|
+
const recovery = await resolveMissingSessionModelRecovery({
|
|
2117
|
+
modelRegistry,
|
|
2118
|
+
settings,
|
|
2119
|
+
defaultEntries: defaultModelEntries,
|
|
2120
|
+
skips: restoredDefaultResolution.skips,
|
|
2121
|
+
savedDefault: existingSession.models.default,
|
|
2122
|
+
credentialSessionId,
|
|
2123
|
+
...(persistedProfileOwnsDefault ? { aliasIntent: "preset-equivalent" as const } : {}),
|
|
2124
|
+
});
|
|
2125
|
+
deferredMissingSessionRecovery = true;
|
|
2126
|
+
if (
|
|
2127
|
+
recovery?.model &&
|
|
2128
|
+
(!preferredCredentialProvider || recovery.model.provider === preferredCredentialProvider)
|
|
2129
|
+
) {
|
|
2130
|
+
model = recovery.model;
|
|
2131
|
+
recoveredSessionDefault = recovery;
|
|
2132
|
+
startupActiveModelProfile = undefined;
|
|
2133
|
+
modelFallbackMessage =
|
|
2134
|
+
"Saved session model is no longer registered; restored the durable default preset instead.";
|
|
2135
|
+
}
|
|
2136
|
+
} catch {
|
|
2137
|
+
deferredMissingSessionRecovery = true;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
1997
2140
|
if (!model) modelFallbackMessage = `Could not restore model ${defaultModelEntries.join(" -> ")}`;
|
|
1998
2141
|
});
|
|
1999
2142
|
}
|
|
@@ -2029,6 +2172,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2029
2172
|
thinkingLevel = restoredThinkingLevel;
|
|
2030
2173
|
}
|
|
2031
2174
|
|
|
2175
|
+
if (
|
|
2176
|
+
thinkingLevel === undefined &&
|
|
2177
|
+
(restoredThinkingLevel === undefined || restoredThinkingLevel === ThinkingLevel.Inherit) &&
|
|
2178
|
+
recoveredSessionDefault?.explicitThinkingLevel
|
|
2179
|
+
) {
|
|
2180
|
+
thinkingLevel = recoveredSessionDefault.thinkingLevel;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2032
2183
|
if (thinkingLevel === undefined && !hasExplicitModel && defaultRoleSpec.explicitThinkingLevel) {
|
|
2033
2184
|
thinkingLevel = defaultRoleSpec.thinkingLevel;
|
|
2034
2185
|
}
|
|
@@ -2308,11 +2459,18 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2308
2459
|
};
|
|
2309
2460
|
let mcpManager: MCPManager | undefined = options.mcpManager;
|
|
2310
2461
|
let ownsMcpManager = false;
|
|
2462
|
+
let ownedMcpToolCache: MCPToolCache | null | undefined;
|
|
2463
|
+
const getOwnedMcpToolCache = async (): Promise<MCPToolCache | null> => {
|
|
2464
|
+
if (ownedMcpToolCache === undefined)
|
|
2465
|
+
ownedMcpToolCache = await resolveMCPToolCache(undefined, getAgentDbPath(agentDir));
|
|
2466
|
+
return ownedMcpToolCache;
|
|
2467
|
+
};
|
|
2311
2468
|
const cwdCapturingToolNames: string[] = [];
|
|
2312
2469
|
const ownedConventionalMcpServerNames = new Set<string>();
|
|
2313
|
-
|
|
2314
|
-
let
|
|
2315
|
-
let
|
|
2470
|
+
const cachedConventionalMcpServerNames = new Set<string>();
|
|
2471
|
+
let ownedMcpManagerToolNames: string[] = [];
|
|
2472
|
+
let ownedMcpManagerTools: readonly CustomTool[] = [];
|
|
2473
|
+
let publishOwnedMcpTools = false;
|
|
2316
2474
|
const notificationDebounceTimers = new Map<string, Timer>();
|
|
2317
2475
|
const wireMcpManagerCallbacks = (manager: MCPManager): void => {
|
|
2318
2476
|
manager.setOnPromptsChanged(serverName => {
|
|
@@ -2367,6 +2525,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2367
2525
|
if (previousManager) await previousManager.disconnectAll().catch(() => {});
|
|
2368
2526
|
pluginMcpToolNames.length = 0;
|
|
2369
2527
|
conventionalMcpToolNames.length = 0;
|
|
2528
|
+
ownedConventionalMcpServerNames.clear();
|
|
2529
|
+
cachedConventionalMcpServerNames.clear();
|
|
2530
|
+
ownedMcpManagerToolNames = [];
|
|
2531
|
+
ownedMcpManagerTools = [];
|
|
2532
|
+
publishOwnedMcpTools = false;
|
|
2370
2533
|
let nextManager: MCPManager | undefined;
|
|
2371
2534
|
try {
|
|
2372
2535
|
const loaded = await loadAllMCPConfigs(to, {
|
|
@@ -2390,11 +2553,51 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2390
2553
|
]),
|
|
2391
2554
|
),
|
|
2392
2555
|
};
|
|
2556
|
+
const conventionalCacheServerNames = new Set(
|
|
2557
|
+
Object.keys(loaded.configs).filter(name => !pluginNames.has(name)),
|
|
2558
|
+
);
|
|
2559
|
+
for (const name of conventionalCacheServerNames) ownedConventionalMcpServerNames.add(name);
|
|
2560
|
+
publishOwnedMcpTools = Object.keys(mergedConfigs).length > 0;
|
|
2393
2561
|
if (Object.keys(mergedConfigs).length > 0) {
|
|
2394
|
-
nextManager = new MCPManager(
|
|
2562
|
+
nextManager = new MCPManager(
|
|
2563
|
+
to,
|
|
2564
|
+
conventionalCacheServerNames.size > 0 ? await getOwnedMcpToolCache() : null,
|
|
2565
|
+
{
|
|
2566
|
+
sharedPoolIdleMs: settings.get("mcp.sharedPoolIdleMs"),
|
|
2567
|
+
toolCacheServerNames: conventionalCacheServerNames,
|
|
2568
|
+
},
|
|
2569
|
+
);
|
|
2395
2570
|
nextManager.setAuthStorage(authStorage);
|
|
2396
2571
|
wireMcpManagerCallbacks(nextManager);
|
|
2397
2572
|
const result = await nextManager.connectServers(mergedConfigs, mergedSources as never);
|
|
2573
|
+
const connectedPluginNames = new Set(result.connectedServers.filter(name => pluginNames.has(name)));
|
|
2574
|
+
pluginMcpManagerServers.set(nextManager, new Set(pluginNames));
|
|
2575
|
+
conventionalMcpManagerServers.set(nextManager, conventionalCacheServerNames);
|
|
2576
|
+
failedPluginMcpManagerServers.set(nextManager, new Set());
|
|
2577
|
+
for (const tool of result.tools) {
|
|
2578
|
+
const serverName = tool.mcpServerName;
|
|
2579
|
+
if (
|
|
2580
|
+
tool instanceof DeferredMCPTool &&
|
|
2581
|
+
serverName &&
|
|
2582
|
+
conventionalCacheServerNames.has(serverName)
|
|
2583
|
+
) {
|
|
2584
|
+
cachedConventionalMcpServerNames.add(serverName);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
ownedMcpManagerTools = result.tools as CustomTool[];
|
|
2588
|
+
ownedMcpManagerToolNames = result.tools.map(tool => tool.name);
|
|
2589
|
+
const unsettledConventionalNames = [...conventionalCacheServerNames].filter(
|
|
2590
|
+
name =>
|
|
2591
|
+
nextManager?.getConnectionStatus(name) === "connecting" ||
|
|
2592
|
+
cachedConventionalMcpServerNames.has(name),
|
|
2593
|
+
);
|
|
2594
|
+
if (pluginNames.size > 0) {
|
|
2595
|
+
if (connectedPluginNames.size > 0 && unsettledConventionalNames.length === 0) {
|
|
2596
|
+
nextManager.sealConnectionSet();
|
|
2597
|
+
} else {
|
|
2598
|
+
nextManager.freezeConnectionSet();
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2398
2601
|
nextCustomTools.push(...(result.tools as CustomTool[]));
|
|
2399
2602
|
nextCwdCapturing.push(...result.tools.map(tool => tool.name));
|
|
2400
2603
|
for (const tool of result.tools) {
|
|
@@ -2412,14 +2615,18 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2412
2615
|
mcpManager = nextManager;
|
|
2413
2616
|
ownsMcpManager = Boolean(nextManager);
|
|
2414
2617
|
await session.replaceOwnedMcpManager(nextManager);
|
|
2415
|
-
await session.refreshMCPTools((nextManager?.getTools() ?? []) as CustomTool[]
|
|
2618
|
+
await session.refreshMCPTools((nextManager?.getTools() ?? []) as CustomTool[], {
|
|
2619
|
+
mandatoryMCPToolNames: pluginMcpToolNames,
|
|
2620
|
+
});
|
|
2416
2621
|
}
|
|
2417
2622
|
cwdCapturingToolNames.length = 0;
|
|
2418
2623
|
cwdCapturingToolNames.push(...nextCwdCapturing);
|
|
2419
2624
|
await session.replaceNamedCustomTools(
|
|
2420
2625
|
previousCwdCapturing.filter(name => !nextCustomTools.some(tool => tool.name === name)),
|
|
2421
2626
|
nextCustomTools,
|
|
2627
|
+
{ mandatoryMCPToolNames: pluginMcpToolNames },
|
|
2422
2628
|
);
|
|
2629
|
+
wireOwnedMcpToolSync();
|
|
2423
2630
|
};
|
|
2424
2631
|
|
|
2425
2632
|
/**
|
|
@@ -2982,10 +3189,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2982
3189
|
const exactMcpToolNames: string[] = [];
|
|
2983
3190
|
const pluginMcpToolNames: string[] = [];
|
|
2984
3191
|
const conventionalMcpToolNames: string[] = [];
|
|
3192
|
+
let inheritedMcpManager: MCPManager | undefined;
|
|
3193
|
+
let inheritedMcpManagerToolNames: string[] = [];
|
|
3194
|
+
let inheritedMcpToolsSnapshot: readonly CustomTool[] = [];
|
|
3195
|
+
let pendingInheritedMcpToolsSnapshot: readonly CustomTool[] | undefined;
|
|
2985
3196
|
let deferredExactMcpConfig: { manager: MCPManager; configPath: string } | undefined;
|
|
2986
3197
|
let deferredConventionalMcp:
|
|
2987
3198
|
| {
|
|
2988
3199
|
manager: MCPManager;
|
|
3200
|
+
cleanup: OwnedMcpManagerCleanup;
|
|
2989
3201
|
configs: Record<string, MCPServerConfig>;
|
|
2990
3202
|
sources: Record<string, SourceMeta>;
|
|
2991
3203
|
conventionalConfigs: Record<string, MCPServerConfig>;
|
|
@@ -3105,74 +3317,179 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3105
3317
|
mergedSources: Record<string, SourceMeta>,
|
|
3106
3318
|
conventionalConfigs: Record<string, MCPServerConfig>,
|
|
3107
3319
|
pluginNames: ReadonlySet<string>,
|
|
3320
|
+
pluginMcpProvenance: ReadonlyMap<string, GjcPluginMcpServerProvenance>,
|
|
3321
|
+
cleanupOwned: OwnedMcpManagerCleanup,
|
|
3108
3322
|
): Promise<MCPLoadResult> => {
|
|
3109
3323
|
let result: MCPLoadResult;
|
|
3110
3324
|
try {
|
|
3111
3325
|
result = await owned.connectServers(mergedConfigs, mergedSources as never);
|
|
3112
3326
|
} catch (error) {
|
|
3113
|
-
if (
|
|
3327
|
+
if (
|
|
3328
|
+
safeIsInstanceOf(error, McpManagerCleanupError) ||
|
|
3329
|
+
safeIsInstanceOf(error, McpManagerCleanupDiagnosticError) ||
|
|
3330
|
+
safeReadCleanupDiagnostic(error) !== undefined
|
|
3331
|
+
)
|
|
3332
|
+
throw error;
|
|
3114
3333
|
// Avoid leaking partially-started server processes on failure.
|
|
3334
|
+
const retryAfterStartupCleanupFailure = options.enableMCP === false && !options.deferMcpConfigStartup;
|
|
3115
3335
|
let cleanupError: unknown;
|
|
3336
|
+
let cleanupFailed = false;
|
|
3116
3337
|
try {
|
|
3117
|
-
await
|
|
3338
|
+
await cleanupOwned();
|
|
3118
3339
|
} catch (disconnectError) {
|
|
3340
|
+
cleanupFailed = true;
|
|
3119
3341
|
cleanupError = disconnectError;
|
|
3120
|
-
} finally {
|
|
3121
|
-
cleanupOwnedMcpManager = undefined;
|
|
3122
3342
|
}
|
|
3123
|
-
if (
|
|
3343
|
+
if (cleanupFailed) {
|
|
3344
|
+
if (retryAfterStartupCleanupFailure) throw new McpStartupCleanupRetryError(error);
|
|
3345
|
+
if (pluginNames.size > 0) {
|
|
3346
|
+
throw new McpPluginStartupCleanupDiagnosticError(error, cleanupError);
|
|
3347
|
+
}
|
|
3348
|
+
throw attachMcpCleanupDiagnostic(error, cleanupError);
|
|
3349
|
+
}
|
|
3124
3350
|
throw error;
|
|
3125
3351
|
}
|
|
3352
|
+
const conventionalServerNames = new Set(
|
|
3353
|
+
Object.keys(conventionalConfigs).filter(name => !pluginNames.has(name)),
|
|
3354
|
+
);
|
|
3355
|
+
for (const tool of result.tools) {
|
|
3356
|
+
if (tool instanceof DeferredMCPTool && conventionalServerNames.has(tool.mcpServerName)) {
|
|
3357
|
+
cachedConventionalMcpServerNames.add(tool.mcpServerName);
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3126
3360
|
for (const [server, err] of result.errors) {
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3361
|
+
const provenance = pluginMcpProvenance.get(server);
|
|
3362
|
+
if (provenance) {
|
|
3363
|
+
gjcFindings.add({
|
|
3364
|
+
identity: provenance.identity,
|
|
3365
|
+
surfaceId: provenance.surfaceId,
|
|
3366
|
+
code: "runtime_mismatch",
|
|
3367
|
+
message: `MCP server connection failed: ${safePluginMcpDiagnostic(err)}`,
|
|
3368
|
+
decision: "error",
|
|
3369
|
+
provenance: {
|
|
3370
|
+
source: "plugin-bundle",
|
|
3371
|
+
plugin: provenance.identity.name,
|
|
3372
|
+
scope: provenance.identity.scope,
|
|
3373
|
+
},
|
|
3374
|
+
});
|
|
3375
|
+
}
|
|
3376
|
+
if (provenance) {
|
|
3377
|
+
logger.error("GJC plugin MCP connection failed", {
|
|
3378
|
+
path: `mcp:${server}`,
|
|
3379
|
+
error: safePluginMcpDiagnostic(err),
|
|
3380
|
+
});
|
|
3381
|
+
} else if (!isMCPStartupTimeoutError(err)) {
|
|
3382
|
+
logger.warn("MCP server connection failed", {
|
|
3383
|
+
path: `mcp:${server}`,
|
|
3384
|
+
// MCPLoadResult stores failures as strings, so their original
|
|
3385
|
+
// type is unavailable here; use the manager classifier's safe fallback.
|
|
3386
|
+
error: "transport-error",
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
const failedPluginNames = new Set([...result.errors.keys()].filter(name => pluginMcpProvenance.has(name)));
|
|
3391
|
+
const connectedPluginNames = new Set(
|
|
3392
|
+
result.connectedServers.filter(name => pluginNames.has(name) && !failedPluginNames.has(name)),
|
|
3393
|
+
);
|
|
3394
|
+
const trustedPluginServerNames = new Set(
|
|
3395
|
+
[...pluginMcpProvenance.keys()].filter(name => !failedPluginNames.has(name)),
|
|
3396
|
+
);
|
|
3397
|
+
const connectedTrustedPluginNames = new Set(
|
|
3398
|
+
[...connectedPluginNames].filter(name => trustedPluginServerNames.has(name)),
|
|
3399
|
+
);
|
|
3400
|
+
const unsettledPluginNames = [...pluginNames].filter(
|
|
3401
|
+
name =>
|
|
3402
|
+
!failedPluginNames.has(name) &&
|
|
3403
|
+
!connectedPluginNames.has(name) &&
|
|
3404
|
+
owned.getConnectionStatus(name) !== "disconnected",
|
|
3405
|
+
);
|
|
3406
|
+
let pluginCleanupFailed = false;
|
|
3407
|
+
for (const server of unsettledPluginNames) {
|
|
3408
|
+
const provenance = pluginMcpProvenance.get(server);
|
|
3409
|
+
if (!provenance) continue;
|
|
3410
|
+
let message =
|
|
3411
|
+
"MCP server did not produce a complete startup result within its declared startup window; its tools are unavailable for this session";
|
|
3412
|
+
let cleanupDiagnostic: string | undefined;
|
|
3413
|
+
failedPluginNames.add(server);
|
|
3414
|
+
try {
|
|
3415
|
+
await owned.disconnectServer(server);
|
|
3416
|
+
} catch (error) {
|
|
3417
|
+
pluginCleanupFailed = true;
|
|
3418
|
+
cleanupDiagnostic = safePluginMcpDiagnostic(error);
|
|
3419
|
+
}
|
|
3420
|
+
if (owned.getConnectionStatus(server) !== "disconnected") {
|
|
3421
|
+
pluginCleanupFailed = true;
|
|
3422
|
+
cleanupDiagnostic ??= "server remained connected or connecting after disconnect";
|
|
3423
|
+
}
|
|
3424
|
+
if (cleanupDiagnostic !== undefined) message += `; cleanup diagnostic: ${cleanupDiagnostic}`;
|
|
3425
|
+
gjcFindings.add({
|
|
3426
|
+
identity: provenance.identity,
|
|
3427
|
+
surfaceId: provenance.surfaceId,
|
|
3428
|
+
code: "runtime_mismatch",
|
|
3429
|
+
message,
|
|
3430
|
+
decision: "error",
|
|
3431
|
+
provenance: {
|
|
3432
|
+
source: "plugin-bundle",
|
|
3433
|
+
plugin: provenance.identity.name,
|
|
3434
|
+
scope: provenance.identity.scope,
|
|
3435
|
+
},
|
|
3436
|
+
});
|
|
3437
|
+
logger.error("GJC plugin MCP startup did not settle", {
|
|
3134
3438
|
path: `mcp:${server}`,
|
|
3135
|
-
error:
|
|
3439
|
+
error: message,
|
|
3440
|
+
...(cleanupDiagnostic === undefined ? {} : { cleanupDiagnostic }),
|
|
3136
3441
|
});
|
|
3137
3442
|
}
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3443
|
+
failedPluginMcpManagerServers.set(owned, new Set(failedPluginNames));
|
|
3444
|
+
if (pluginCleanupFailed) pluginMcpCleanupFailures.add(owned);
|
|
3445
|
+
const successfulTools = result.tools.filter(
|
|
3446
|
+
tool => tool.mcpServerName === undefined || !failedPluginNames.has(tool.mcpServerName),
|
|
3447
|
+
);
|
|
3448
|
+
// Only a connection still in its startup window or a cached fallback
|
|
3449
|
+
// needs the mixed manager to remain mutable. Successfully connected
|
|
3450
|
+
// ordinary tools do not delay the synchronous seal; cached fallbacks keep
|
|
3451
|
+
// their reconnect path available while they remain published.
|
|
3452
|
+
const unsettledConventionalNames = [...conventionalServerNames].filter(
|
|
3453
|
+
name => owned.getConnectionStatus(name) === "connecting" || cachedConventionalMcpServerNames.has(name),
|
|
3145
3454
|
);
|
|
3146
|
-
const retainOwnedManager =
|
|
3455
|
+
const retainOwnedManager =
|
|
3456
|
+
result.connectedServers.length > 0 ||
|
|
3457
|
+
unsettledConventionalNames.length > 0 ||
|
|
3458
|
+
successfulTools.length > 0 ||
|
|
3459
|
+
pluginCleanupFailed;
|
|
3147
3460
|
if (retainOwnedManager) {
|
|
3148
3461
|
mcpManager = owned;
|
|
3149
3462
|
ownsMcpManager = true;
|
|
3150
|
-
customTools.push(...(
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3463
|
+
customTools.push(...(successfulTools as CustomTool[]));
|
|
3464
|
+
ownedMcpManagerTools = successfulTools as CustomTool[];
|
|
3465
|
+
ownedMcpManagerToolNames = successfulTools.map(tool => tool.name);
|
|
3466
|
+
cwdCapturingToolNames.push(...successfulTools.map(tool => tool.name));
|
|
3467
|
+
for (const name of conventionalServerNames) ownedConventionalMcpServerNames.add(name);
|
|
3468
|
+
pluginMcpManagerServers.set(owned, trustedPluginServerNames);
|
|
3469
|
+
conventionalMcpManagerServers.set(owned, conventionalServerNames);
|
|
3470
|
+
for (const tool of successfulTools) {
|
|
3156
3471
|
const serverName = tool.mcpServerName;
|
|
3157
3472
|
if (serverName === undefined) continue;
|
|
3158
|
-
if (
|
|
3473
|
+
if (connectedTrustedPluginNames.has(serverName)) pluginMcpToolNames.push(tool.name);
|
|
3159
3474
|
else {
|
|
3160
3475
|
conventionalMcpToolNames.push(tool.name);
|
|
3161
|
-
ownedConventionalMcpToolNames.push(tool.name);
|
|
3162
3476
|
}
|
|
3163
3477
|
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
//
|
|
3167
|
-
//
|
|
3168
|
-
|
|
3169
|
-
|
|
3478
|
+
publishOwnedMcpTools = true;
|
|
3479
|
+
// Keep plugin config authority fixed even while a conventional cached
|
|
3480
|
+
// fallback needs reconnects; a full reload would disconnect and lose the
|
|
3481
|
+
// plugin-bundle configs because native discovery does not reload them.
|
|
3482
|
+
if (pluginNames.size > 0) {
|
|
3483
|
+
if (connectedTrustedPluginNames.size > 0 && unsettledConventionalNames.length === 0) {
|
|
3484
|
+
owned.sealConnectionSet();
|
|
3485
|
+
} else {
|
|
3486
|
+
owned.freezeConnectionSet();
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3170
3489
|
} else {
|
|
3171
3490
|
try {
|
|
3172
|
-
await
|
|
3173
|
-
cleanupOwnedMcpManager = undefined;
|
|
3491
|
+
await cleanupOwned();
|
|
3174
3492
|
} catch (cleanupError) {
|
|
3175
|
-
cleanupOwnedMcpManager = undefined;
|
|
3176
3493
|
throw new McpManagerCleanupError(cleanupError);
|
|
3177
3494
|
}
|
|
3178
3495
|
}
|
|
@@ -3189,7 +3506,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3189
3506
|
owned.setAuthStorage(authStorage);
|
|
3190
3507
|
mcpManager = owned;
|
|
3191
3508
|
ownsMcpManager = true;
|
|
3192
|
-
|
|
3509
|
+
registerOwnedMcpManagerCleanup(owned);
|
|
3193
3510
|
if (options.deferMcpConfigStartup) {
|
|
3194
3511
|
deferredExactMcpConfig = { manager: owned, configPath: explicitMcpConfigPath };
|
|
3195
3512
|
} else {
|
|
@@ -3257,7 +3574,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3257
3574
|
// tools are surfaced as always-on tools rather than gated behind MCP
|
|
3258
3575
|
// selection.
|
|
3259
3576
|
try {
|
|
3260
|
-
const { configs, quarantine } = await buildPluginMcpConfigs({ cwd });
|
|
3577
|
+
const { configs, quarantine, serverProvenance: pluginMcpProvenance } = await buildPluginMcpConfigs({ cwd });
|
|
3261
3578
|
for (const q of quarantine) {
|
|
3262
3579
|
gjcFindings.add({
|
|
3263
3580
|
identity: q.identity,
|
|
@@ -3284,9 +3601,30 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3284
3601
|
// own bypass of the same mismatch at the manager boundary).
|
|
3285
3602
|
const mergedSourceMetas = mergedSources as Record<string, SourceMeta>;
|
|
3286
3603
|
if (Object.keys(mergedConfigs).length > 0) {
|
|
3287
|
-
const
|
|
3604
|
+
const conventionalCacheServerNames = new Set(
|
|
3605
|
+
Object.keys(conventionalConfigs).filter(name => !pluginNames.has(name)),
|
|
3606
|
+
);
|
|
3607
|
+
const maxPluginMcpTimeoutMs = Object.values(configs).reduce((maximum, config) => {
|
|
3608
|
+
const timeout = config.timeout;
|
|
3609
|
+
return typeof timeout === "number" && Number.isFinite(timeout) && timeout > 0
|
|
3610
|
+
? Math.max(maximum, timeout)
|
|
3611
|
+
: maximum;
|
|
3612
|
+
}, 0);
|
|
3613
|
+
const maxPluginMcpStartupWaitMs =
|
|
3614
|
+
maxPluginMcpTimeoutMs > 0 ? maxPluginMcpTimeoutMs + MCP_STARTUP_WAIT_GRACE_MS : undefined;
|
|
3615
|
+
const owned = new MCPManager(
|
|
3616
|
+
cwd,
|
|
3617
|
+
conventionalCacheServerNames.size > 0 ? await getOwnedMcpToolCache() : null,
|
|
3618
|
+
{
|
|
3619
|
+
sharedPoolIdleMs: settings.get("mcp.sharedPoolIdleMs"),
|
|
3620
|
+
toolCacheServerNames: conventionalCacheServerNames,
|
|
3621
|
+
...(maxPluginMcpStartupWaitMs === undefined
|
|
3622
|
+
? {}
|
|
3623
|
+
: { maxStartupTimeoutMs: maxPluginMcpStartupWaitMs }),
|
|
3624
|
+
},
|
|
3625
|
+
);
|
|
3288
3626
|
owned.setAuthStorage(authStorage);
|
|
3289
|
-
|
|
3627
|
+
const cleanupOwned = registerOwnedMcpManagerCleanup(owned);
|
|
3290
3628
|
// Interactive launches may paint before MCP connects; the deferred
|
|
3291
3629
|
// starter reconnects this manager after first paint and a startup
|
|
3292
3630
|
// turn barrier keeps the first prompt from racing tool registration.
|
|
@@ -3296,6 +3634,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3296
3634
|
if (options.deferMcpConfigStartup && pluginNames.size === 0) {
|
|
3297
3635
|
deferredConventionalMcp = {
|
|
3298
3636
|
manager: owned,
|
|
3637
|
+
cleanup: cleanupOwned,
|
|
3299
3638
|
configs: mergedConfigs,
|
|
3300
3639
|
sources: mergedSourceMetas,
|
|
3301
3640
|
conventionalConfigs,
|
|
@@ -3312,37 +3651,60 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3312
3651
|
mergedSourceMetas,
|
|
3313
3652
|
conventionalConfigs,
|
|
3314
3653
|
pluginNames,
|
|
3654
|
+
pluginMcpProvenance,
|
|
3655
|
+
cleanupOwned,
|
|
3315
3656
|
);
|
|
3316
3657
|
}
|
|
3317
3658
|
}
|
|
3318
3659
|
} catch (error) {
|
|
3319
|
-
if (safeIsInstanceOf(error,
|
|
3660
|
+
if (safeIsInstanceOf(error, McpStartupCleanupRetryError)) {
|
|
3661
|
+
throw (error as McpStartupCleanupRetryError).primaryError;
|
|
3662
|
+
}
|
|
3663
|
+
const pluginStartupCleanupFailure = safeIsInstanceOf(error, McpPluginStartupCleanupDiagnosticError)
|
|
3664
|
+
? (error as McpPluginStartupCleanupDiagnosticError)
|
|
3665
|
+
: undefined;
|
|
3666
|
+
if (
|
|
3667
|
+
!pluginStartupCleanupFailure &&
|
|
3668
|
+
(safeIsInstanceOf(error, McpManagerCleanupError) ||
|
|
3669
|
+
safeIsInstanceOf(error, McpManagerCleanupDiagnosticError) ||
|
|
3670
|
+
safeReadCleanupDiagnostic(error) !== undefined)
|
|
3671
|
+
)
|
|
3672
|
+
throw error;
|
|
3320
3673
|
gjcProducersComplete = false;
|
|
3321
|
-
const cleanupDiagnostic =
|
|
3674
|
+
const cleanupDiagnostic =
|
|
3675
|
+
pluginStartupCleanupFailure?.cleanupDiagnostic ?? safeReadCleanupDiagnostic(error);
|
|
3322
3676
|
logger.warn("Failed to wire GJC plugin MCP servers", {
|
|
3323
|
-
error:
|
|
3677
|
+
error: pluginStartupCleanupFailure
|
|
3678
|
+
? safePluginMcpDiagnostic(pluginStartupCleanupFailure.primaryError)
|
|
3679
|
+
: safeErrorForLog(error),
|
|
3324
3680
|
cleanupDiagnostic: safeCleanupDiagnosticForLog(cleanupDiagnostic),
|
|
3325
3681
|
});
|
|
3326
3682
|
}
|
|
3327
3683
|
} else if (isCanonicalSubSession) {
|
|
3328
|
-
// Subagents inherit the parent's
|
|
3329
|
-
//
|
|
3330
|
-
//
|
|
3331
|
-
// scope; process-global state is never consulted for MCP routing.
|
|
3684
|
+
// Subagents inherit the parent's MCP tools without owning the manager.
|
|
3685
|
+
// They subscribe to catalog changes but never connect or dispose it; the
|
|
3686
|
+
// facade is carried explicitly by the parent scope.
|
|
3332
3687
|
const inherited = mcpManager ?? options.inheritedMcpManager;
|
|
3333
3688
|
if (inherited) {
|
|
3334
3689
|
try {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3690
|
+
inheritedMcpManager = inherited;
|
|
3691
|
+
inheritedMcpToolsUnsubscribe = inherited.subscribeToToolsChanged(tools => {
|
|
3692
|
+
const snapshot = tools.map(tool => tool as CustomTool);
|
|
3693
|
+
if (inheritedMcpToolsPublisher) inheritedMcpToolsPublisher(snapshot);
|
|
3694
|
+
else pendingInheritedMcpToolsSnapshot = snapshot;
|
|
3695
|
+
});
|
|
3696
|
+
const inheritedTools = inherited.getTools() as CustomTool[];
|
|
3697
|
+
inheritedMcpToolsSnapshot = inheritedTools;
|
|
3698
|
+
inheritedMcpManagerToolNames = inheritedTools.map(tool => tool.name);
|
|
3699
|
+
customTools.push(...inheritedTools);
|
|
3700
|
+
const classifiedTools = classifyInheritedMcpToolNames(inherited, inheritedTools);
|
|
3701
|
+
pluginMcpToolNames.push(...classifiedTools.pluginMcpToolNames);
|
|
3702
|
+
conventionalMcpToolNames.push(...classifiedTools.conventionalMcpToolNames);
|
|
3345
3703
|
} catch (error) {
|
|
3704
|
+
stopInheritedMcpToolsSubscription();
|
|
3705
|
+
inheritedMcpManager = undefined;
|
|
3706
|
+
inheritedMcpToolsSnapshot = [];
|
|
3707
|
+
pendingInheritedMcpToolsSnapshot = undefined;
|
|
3346
3708
|
logger.warn("Failed to inherit MCP tools in subagent", { error: safeErrorForLog(error) });
|
|
3347
3709
|
}
|
|
3348
3710
|
}
|
|
@@ -3495,6 +3857,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3495
3857
|
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
3496
3858
|
return session.abortPromptAndWait(handle, seamOptions);
|
|
3497
3859
|
},
|
|
3860
|
+
pendingToolExecutions: handle => {
|
|
3861
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
3862
|
+
return session.pendingToolExecutions(handle);
|
|
3863
|
+
},
|
|
3498
3864
|
},
|
|
3499
3865
|
ensureProviderDaemon: options.ensureNotificationProviderDaemon,
|
|
3500
3866
|
runBtwTurn: async (question, signal) => {
|
|
@@ -3585,6 +3951,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3585
3951
|
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
3586
3952
|
return session.abortPromptAndWait(handle, seamOptions);
|
|
3587
3953
|
},
|
|
3954
|
+
pendingToolExecutions: handle => {
|
|
3955
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
3956
|
+
return session.pendingToolExecutions(handle);
|
|
3957
|
+
},
|
|
3588
3958
|
},
|
|
3589
3959
|
});
|
|
3590
3960
|
}
|
|
@@ -3703,6 +4073,103 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3703
4073
|
extensionsResult.runtime.pendingProviderRegistrations = [];
|
|
3704
4074
|
}
|
|
3705
4075
|
|
|
4076
|
+
// A startup extension can register the saved model after the initial
|
|
4077
|
+
// recovery lookup. Reconsider the saved chain against that completed
|
|
4078
|
+
// catalog before retaining a durable runtime fallback.
|
|
4079
|
+
if (savedDefaultWasUnresolved && defaultModelEntries.length > 0) {
|
|
4080
|
+
const restoredAfterExtensions = await resolveModelChainWithAuth(
|
|
4081
|
+
defaultModelEntries,
|
|
4082
|
+
modelRegistry,
|
|
4083
|
+
settings,
|
|
4084
|
+
credentialSessionId,
|
|
4085
|
+
{
|
|
4086
|
+
managedFallback: defaultModelEntries.length > 1,
|
|
4087
|
+
canonicalSessionId: providerSessionId,
|
|
4088
|
+
...(persistedProfileOwnsDefault ? { aliasIntent: "preset-equivalent" as const } : {}),
|
|
4089
|
+
},
|
|
4090
|
+
);
|
|
4091
|
+
if (
|
|
4092
|
+
restoredAfterExtensions.model &&
|
|
4093
|
+
(!preferredCredentialProvider || restoredAfterExtensions.model.provider === preferredCredentialProvider)
|
|
4094
|
+
) {
|
|
4095
|
+
model = restoredAfterExtensions.model;
|
|
4096
|
+
retainedRecoveryBindingsAfterLateRestore = savedDefaultWasUnresolved;
|
|
4097
|
+
if (options.thinkingLevel !== undefined) {
|
|
4098
|
+
thinkingLevel = resolveThinkingLevelForModel(model, options.thinkingLevel);
|
|
4099
|
+
} else if (restoredThinkingLevel !== undefined && restoredThinkingLevel !== ThinkingLevel.Inherit) {
|
|
4100
|
+
thinkingLevel = resolveThinkingLevelForModel(model, restoredThinkingLevel);
|
|
4101
|
+
} else {
|
|
4102
|
+
thinkingLevel = restoredAfterExtensions.explicitThinkingLevel
|
|
4103
|
+
? restoredAfterExtensions.thinkingLevel
|
|
4104
|
+
: undefined;
|
|
4105
|
+
if (thinkingLevel === undefined && !hasExplicitModel && defaultRoleSpec.explicitThinkingLevel)
|
|
4106
|
+
thinkingLevel = defaultRoleSpec.thinkingLevel;
|
|
4107
|
+
if (thinkingLevel === undefined && hasExplicitDefaultThinkingLevel)
|
|
4108
|
+
thinkingLevel = settings.get("defaultThinkingLevel");
|
|
4109
|
+
if (thinkingLevel === undefined && model.thinking?.defaultLevel !== undefined)
|
|
4110
|
+
thinkingLevel = model.thinking.defaultLevel;
|
|
4111
|
+
if (thinkingLevel === undefined) thinkingLevel = settings.get("defaultThinkingLevel");
|
|
4112
|
+
thinkingLevel = resolveThinkingLevelForModel(model, thinkingLevel);
|
|
4113
|
+
}
|
|
4114
|
+
recoveredSessionDefault = undefined;
|
|
4115
|
+
startupActiveModelProfile =
|
|
4116
|
+
acceptedInheritedProfileName ??
|
|
4117
|
+
(!hasExplicitModel && acceptedPersistedProfileName ? acceptedPersistedProfileName : undefined);
|
|
4118
|
+
modelFallbackMessage = undefined;
|
|
4119
|
+
} else if (deferredMissingSessionRecovery) {
|
|
4120
|
+
const hadProvisionalRecovery = recoveredSessionDefault !== undefined;
|
|
4121
|
+
let lateRecoveryAccepted = false;
|
|
4122
|
+
try {
|
|
4123
|
+
const recovery = await resolveMissingSessionModelRecovery({
|
|
4124
|
+
modelRegistry,
|
|
4125
|
+
settings,
|
|
4126
|
+
defaultEntries: defaultModelEntries,
|
|
4127
|
+
skips: restoredAfterExtensions.skips,
|
|
4128
|
+
savedDefault: existingSession.models.default,
|
|
4129
|
+
credentialSessionId,
|
|
4130
|
+
...(persistedProfileOwnsDefault ? { aliasIntent: "preset-equivalent" as const } : {}),
|
|
4131
|
+
});
|
|
4132
|
+
if (
|
|
4133
|
+
recovery?.model &&
|
|
4134
|
+
(!preferredCredentialProvider || recovery.model.provider === preferredCredentialProvider)
|
|
4135
|
+
) {
|
|
4136
|
+
model = recovery.model;
|
|
4137
|
+
if (options.thinkingLevel !== undefined) {
|
|
4138
|
+
thinkingLevel = resolveThinkingLevelForModel(model, options.thinkingLevel);
|
|
4139
|
+
} else if (restoredThinkingLevel !== undefined && restoredThinkingLevel !== ThinkingLevel.Inherit) {
|
|
4140
|
+
thinkingLevel = resolveThinkingLevelForModel(model, restoredThinkingLevel);
|
|
4141
|
+
} else {
|
|
4142
|
+
const recoveredLevel = recovery.explicitThinkingLevel
|
|
4143
|
+
? recovery.thinkingLevel
|
|
4144
|
+
: defaultRoleSpec.explicitThinkingLevel
|
|
4145
|
+
? defaultRoleSpec.thinkingLevel
|
|
4146
|
+
: hasExplicitDefaultThinkingLevel
|
|
4147
|
+
? settings.get("defaultThinkingLevel")
|
|
4148
|
+
: (model.thinking?.defaultLevel ?? settings.get("defaultThinkingLevel"));
|
|
4149
|
+
thinkingLevel = resolveThinkingLevelForModel(model, recoveredLevel);
|
|
4150
|
+
}
|
|
4151
|
+
recoveredSessionDefault = recovery;
|
|
4152
|
+
startupActiveModelProfile = undefined;
|
|
4153
|
+
modelFallbackMessage =
|
|
4154
|
+
"Saved session model is no longer registered; restored the durable default preset instead.";
|
|
4155
|
+
lateRecoveryAccepted = true;
|
|
4156
|
+
}
|
|
4157
|
+
} catch (error) {
|
|
4158
|
+
if (!(error instanceof ModelProfileCredentialError)) throw error;
|
|
4159
|
+
}
|
|
4160
|
+
// A completed extension catalog can make the saved selector registered
|
|
4161
|
+
// again without making it callable. Do not retain the provisional durable
|
|
4162
|
+
// fallback in that case: the saved model remains authoritative for the
|
|
4163
|
+
// normal unavailable-model path below.
|
|
4164
|
+
if (!lateRecoveryAccepted && hadProvisionalRecovery) {
|
|
4165
|
+
model = undefined;
|
|
4166
|
+
recoveredSessionDefault = undefined;
|
|
4167
|
+
startupActiveModelProfile = undefined;
|
|
4168
|
+
modelFallbackMessage = `Could not restore model ${defaultModelEntries.join(" -> ")}`;
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
|
|
3706
4173
|
let startupCredentialModelRejected = false;
|
|
3707
4174
|
if (startupModelReference && (startupCredentialSelector || startupCredentialProviderMismatch)) {
|
|
3708
4175
|
const validated =
|
|
@@ -4790,12 +5257,25 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4790
5257
|
providerSessionState: options.providerSessionState,
|
|
4791
5258
|
});
|
|
4792
5259
|
session.setActiveModelProfile(startupActiveModelProfile);
|
|
5260
|
+
if (retainedRecoveryBindingsAfterLateRestore) session.markStartupRecoveryBindingsRequired();
|
|
5261
|
+
if (recoveredSessionDefault)
|
|
5262
|
+
session.installRecoveredDefaultFallbackChain(
|
|
5263
|
+
recoveredSessionDefault.entries,
|
|
5264
|
+
recoveredSessionDefault.profileName,
|
|
5265
|
+
recoveredSessionDefault.activeIndex,
|
|
5266
|
+
recoveredSessionDefault.skips,
|
|
5267
|
+
);
|
|
4793
5268
|
session.configWarnings.push(...contextFileWarnings);
|
|
4794
5269
|
// Determined once, here, where settings are already available. Keep the
|
|
4795
5270
|
// durable warning for interactive and print consumers; ACP delivery is
|
|
4796
5271
|
// carried by the host replay ring through the internal runtime seam above.
|
|
4797
5272
|
if (autoroutingInactive) session.configWarnings.push(AUTOROUTING_INACTIVE_WARNING);
|
|
4798
5273
|
hasSession = true;
|
|
5274
|
+
const cleanupOwnedManager = cleanupOwnedMcpManager;
|
|
5275
|
+
const sessionOwnedMcpManager = ownsMcpManager ? mcpManager : undefined;
|
|
5276
|
+
if (cleanupOwnedManager && cleanupOwnedMcpManagerOwner !== sessionOwnedMcpManager) {
|
|
5277
|
+
session.registerToolSessionCleanup(cleanupOwnedManager);
|
|
5278
|
+
}
|
|
4799
5279
|
if (masterModeContext) {
|
|
4800
5280
|
// One scoped, no-probe peer snapshot immediately before the FIRST accepted
|
|
4801
5281
|
// provider request; see createMasterPeerSnapshotContributor for the
|
|
@@ -4977,59 +5457,166 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
4977
5457
|
// createAgentSession rather than surfacing as an unhandled rejection.
|
|
4978
5458
|
if (!options.deferMemoryBackendStartup) await startMemoryBackend();
|
|
4979
5459
|
|
|
4980
|
-
// Wire
|
|
4981
|
-
//
|
|
4982
|
-
//
|
|
4983
|
-
//
|
|
4984
|
-
const
|
|
4985
|
-
|
|
4986
|
-
|
|
5460
|
+
// Wire late catalog publications from every owned manager into the live
|
|
5461
|
+
// session. Plugin-only managers also change after startup through
|
|
5462
|
+
// tools/list_changed; mixed managers additionally publish cached fallbacks.
|
|
5463
|
+
// Shared by eager startup and the deferred starter.
|
|
5464
|
+
const wireOwnedMcpToolSync = (initialTools?: readonly CustomTool[]): void => {
|
|
5465
|
+
const manager = mcpManager;
|
|
5466
|
+
if (!manager || !publishOwnedMcpTools || manager.isToolsOnly()) return;
|
|
5467
|
+
// Late MCP connections can publish near-simultaneously.
|
|
4987
5468
|
// Serialize the swaps so an older snapshot cannot interleave with a
|
|
4988
5469
|
// newer one inside replaceNamedCustomTools and leave a stale list.
|
|
4989
5470
|
// Each link swallows (and logs) its own failure so one bad
|
|
4990
5471
|
// publication cannot kill the chain for every later one.
|
|
4991
|
-
let
|
|
4992
|
-
|
|
4993
|
-
|
|
5472
|
+
let ownedMcpToolsSync: Promise<void> = Promise.resolve();
|
|
5473
|
+
let acceptingOwnedMcpTools = true;
|
|
5474
|
+
const syncOwnedMcpTools = (tools: readonly CustomTool[]): Promise<void> => {
|
|
5475
|
+
if (!acceptingOwnedMcpTools) return ownedMcpToolsSync;
|
|
5476
|
+
ownedMcpToolsSync = ownedMcpToolsSync
|
|
4994
5477
|
.then(async () => {
|
|
4995
|
-
if (session.isDisposed) return;
|
|
4996
|
-
const
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5478
|
+
if (session.isDisposed || mcpManager !== manager) return;
|
|
5479
|
+
const pluginServerNames = new Set(pluginMcpManagerServers.get(manager));
|
|
5480
|
+
const conventionalServerNames = new Set(conventionalMcpManagerServers.get(manager));
|
|
5481
|
+
// Plugin authority is fixed from validated bundle provenance when
|
|
5482
|
+
// this manager is created. SourceMeta can be caller-supplied later.
|
|
5483
|
+
for (const serverName of manager.getAllServerNames()) {
|
|
5484
|
+
if (!pluginServerNames.has(serverName)) conventionalServerNames.add(serverName);
|
|
5485
|
+
}
|
|
5486
|
+
for (const tool of tools) {
|
|
5487
|
+
const serverName = tool.mcpServerName;
|
|
5488
|
+
if (!serverName) continue;
|
|
5489
|
+
if (!pluginServerNames.has(serverName)) conventionalServerNames.add(serverName);
|
|
5490
|
+
}
|
|
5491
|
+
conventionalMcpManagerServers.set(manager, conventionalServerNames);
|
|
5492
|
+
const failedPluginServers = failedPluginMcpManagerServers.get(manager);
|
|
5493
|
+
const nextCachedConventionalMcpServerNames = new Set(cachedConventionalMcpServerNames);
|
|
5494
|
+
for (const serverName of nextCachedConventionalMcpServerNames) {
|
|
5495
|
+
if (!tools.some(tool => tool.mcpServerName === serverName)) {
|
|
5496
|
+
nextCachedConventionalMcpServerNames.delete(serverName);
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
for (const tool of tools) {
|
|
5500
|
+
if (
|
|
5501
|
+
tool instanceof DeferredMCPTool &&
|
|
5502
|
+
tool.mcpServerName &&
|
|
5503
|
+
conventionalServerNames.has(tool.mcpServerName)
|
|
5504
|
+
) {
|
|
5505
|
+
nextCachedConventionalMcpServerNames.add(tool.mcpServerName);
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
const nextTools = tools.filter(tool => {
|
|
5509
|
+
const serverName = tool.mcpServerName;
|
|
5510
|
+
if (!serverName) return false;
|
|
5511
|
+
if (
|
|
5512
|
+
failedPluginServers?.has(serverName) &&
|
|
5513
|
+
manager.getConnectionStatus(serverName) !== "connected"
|
|
5514
|
+
) {
|
|
5515
|
+
return false;
|
|
5516
|
+
}
|
|
5517
|
+
return pluginServerNames.has(serverName) || conventionalServerNames.has(serverName);
|
|
5518
|
+
});
|
|
5519
|
+
const previousNames = ownedMcpManagerToolNames;
|
|
5520
|
+
const nextToolNames = nextTools.map(tool => tool.name);
|
|
5001
5521
|
const previousSet = new Set(previousNames);
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5522
|
+
const nextPluginToolNames = nextTools
|
|
5523
|
+
.filter(tool => tool.mcpServerName && pluginServerNames.has(tool.mcpServerName))
|
|
5524
|
+
.map(tool => tool.name);
|
|
5525
|
+
const nextConventionalToolNames = nextTools
|
|
5526
|
+
.filter(tool => tool.mcpServerName && conventionalServerNames.has(tool.mcpServerName))
|
|
5527
|
+
.map(tool => tool.name);
|
|
5528
|
+
const nextMandatoryMcpToolNames = [
|
|
5529
|
+
...pluginMcpToolNames.filter(name => !previousSet.has(name)),
|
|
5530
|
+
...nextPluginToolNames,
|
|
5531
|
+
];
|
|
5532
|
+
const nextConventionalMcpToolNames = [
|
|
5533
|
+
...conventionalMcpToolNames.filter(name => !previousSet.has(name)),
|
|
5534
|
+
...nextConventionalToolNames,
|
|
5535
|
+
];
|
|
5536
|
+
const nextCwdCapturingToolNames = [
|
|
5005
5537
|
...cwdCapturingToolNames.filter(name => !previousSet.has(name)),
|
|
5006
|
-
...
|
|
5538
|
+
...nextToolNames,
|
|
5539
|
+
];
|
|
5540
|
+
const nextOwnedPluginServersConnected = [...pluginServerNames].some(
|
|
5541
|
+
name => manager.getConnectionStatus(name) === "connected",
|
|
5542
|
+
);
|
|
5543
|
+
const nextOwnedConventionalMcpServerNames = new Set(conventionalServerNames);
|
|
5544
|
+
const hasPublishedCachedTool = nextTools.some(
|
|
5545
|
+
tool =>
|
|
5546
|
+
tool.mcpServerName !== undefined &&
|
|
5547
|
+
nextCachedConventionalMcpServerNames.has(tool.mcpServerName),
|
|
5007
5548
|
);
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5549
|
+
try {
|
|
5550
|
+
await session.replaceNamedCustomTools(previousNames, nextTools, {
|
|
5551
|
+
mandatoryMCPToolNames: nextMandatoryMcpToolNames,
|
|
5552
|
+
activateNewTools: false,
|
|
5553
|
+
});
|
|
5554
|
+
} catch (error) {
|
|
5555
|
+
ownedMcpManagerToolNames = [...new Set([...previousNames, ...nextToolNames])];
|
|
5556
|
+
throw error;
|
|
5557
|
+
}
|
|
5558
|
+
ownedMcpManagerToolNames = nextToolNames;
|
|
5559
|
+
ownedMcpManagerTools = nextTools;
|
|
5560
|
+
pluginMcpToolNames.splice(0, pluginMcpToolNames.length, ...nextMandatoryMcpToolNames);
|
|
5561
|
+
conventionalMcpToolNames.splice(0, conventionalMcpToolNames.length, ...nextConventionalMcpToolNames);
|
|
5562
|
+
cwdCapturingToolNames.splice(0, cwdCapturingToolNames.length, ...nextCwdCapturingToolNames);
|
|
5563
|
+
ownedConventionalMcpServerNames.clear();
|
|
5564
|
+
for (const name of nextOwnedConventionalMcpServerNames) ownedConventionalMcpServerNames.add(name);
|
|
5565
|
+
cachedConventionalMcpServerNames.clear();
|
|
5566
|
+
for (const name of nextCachedConventionalMcpServerNames) cachedConventionalMcpServerNames.add(name);
|
|
5567
|
+
// Mixed plugin + conventional sessions defer the seal while a
|
|
5568
|
+
// conventional server is connecting. Cached fallback servers retain
|
|
5569
|
+
// their reconnect path while any of their tools remain published,
|
|
5570
|
+
// including after reconnect replaces DeferredMCPTool with MCPTool.
|
|
5571
|
+
// Derive that hold from this snapshot so removing/replacing the tools
|
|
5572
|
+
// restores the fixed-connection plugin contract.
|
|
5013
5573
|
if (
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
!
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5574
|
+
nextOwnedPluginServersConnected &&
|
|
5575
|
+
!manager.isConnectionSetSealed() &&
|
|
5576
|
+
![...nextOwnedConventionalMcpServerNames].some(
|
|
5577
|
+
name => manager.getConnectionStatus(name) === "connecting",
|
|
5578
|
+
) &&
|
|
5579
|
+
!hasPublishedCachedTool
|
|
5020
5580
|
) {
|
|
5021
|
-
|
|
5581
|
+
manager.sealConnectionSet();
|
|
5022
5582
|
}
|
|
5023
5583
|
})
|
|
5024
5584
|
.catch(error => {
|
|
5025
|
-
logger.warn("Failed to publish
|
|
5585
|
+
logger.warn("Failed to publish owned MCP tools", { error: safeErrorForLog(error) });
|
|
5026
5586
|
});
|
|
5027
|
-
return
|
|
5587
|
+
return ownedMcpToolsSync;
|
|
5028
5588
|
};
|
|
5029
|
-
|
|
5030
|
-
|
|
5589
|
+
session.registerToolSessionCleanup(async () => {
|
|
5590
|
+
acceptingOwnedMcpTools = false;
|
|
5591
|
+
await ownedMcpToolsSync;
|
|
5031
5592
|
});
|
|
5032
|
-
|
|
5593
|
+
manager.setOnToolsChanged(tools => {
|
|
5594
|
+
void syncOwnedMcpTools(tools);
|
|
5595
|
+
});
|
|
5596
|
+
const catalog = manager.getToolCatalogSnapshot();
|
|
5597
|
+
if (initialTools !== undefined) {
|
|
5598
|
+
// Deferred startup already registered its captured result. Re-read
|
|
5599
|
+
// after that await so a publication during registration is not lost.
|
|
5600
|
+
const currentTools =
|
|
5601
|
+
catalog.publication === "published" ? catalog.tools.map(tool => tool as CustomTool) : initialTools;
|
|
5602
|
+
void syncOwnedMcpTools(currentTools);
|
|
5603
|
+
} else if (catalog.publication === "published") {
|
|
5604
|
+
const failedPluginServers = failedPluginMcpManagerServers.get(manager);
|
|
5605
|
+
const catalogTools = catalog.tools
|
|
5606
|
+
.filter(tool => {
|
|
5607
|
+
const serverName = tool.mcpServerName;
|
|
5608
|
+
return (
|
|
5609
|
+
serverName !== undefined &&
|
|
5610
|
+
(!failedPluginServers?.has(serverName) || manager.getConnectionStatus(serverName) === "connected")
|
|
5611
|
+
);
|
|
5612
|
+
})
|
|
5613
|
+
.map(tool => tool as CustomTool);
|
|
5614
|
+
if (!sameMcpToolSnapshot(catalogTools, ownedMcpManagerTools)) {
|
|
5615
|
+
void syncOwnedMcpTools(catalog.tools.map(tool => tool as CustomTool));
|
|
5616
|
+
}
|
|
5617
|
+
} else if (!pluginMcpCleanupFailures.has(manager)) {
|
|
5618
|
+
void syncOwnedMcpTools(ownedMcpManagerTools);
|
|
5619
|
+
}
|
|
5033
5620
|
};
|
|
5034
5621
|
const wireOwnedMcpManagerLifecycle = (): void => {
|
|
5035
5622
|
if (!mcpManager) return;
|
|
@@ -5040,20 +5627,66 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
5040
5627
|
postmortem.register("mcp-notification-cleanup", clearDebounceTimers);
|
|
5041
5628
|
wireMcpManagerCallbacks(mcpManager);
|
|
5042
5629
|
};
|
|
5630
|
+
const wireInheritedMcpToolSync = async (): Promise<void> => {
|
|
5631
|
+
const manager = inheritedMcpManager;
|
|
5632
|
+
if (!manager) return;
|
|
5633
|
+
let inheritedMcpToolsSync: Promise<void> = Promise.resolve();
|
|
5634
|
+
const syncInheritedTools = (tools: readonly CustomTool[]): Promise<void> => {
|
|
5635
|
+
const snapshot = [...tools];
|
|
5636
|
+
inheritedMcpToolsSync = inheritedMcpToolsSync
|
|
5637
|
+
.then(async () => {
|
|
5638
|
+
if (session.isDisposed) return;
|
|
5639
|
+
const previousNames = inheritedMcpManagerToolNames;
|
|
5640
|
+
const nextNames = snapshot.map(tool => tool.name);
|
|
5641
|
+
const classifiedTools = classifyInheritedMcpToolNames(manager, snapshot);
|
|
5642
|
+
try {
|
|
5643
|
+
await session.replaceNamedCustomTools(previousNames, snapshot, {
|
|
5644
|
+
mandatoryMCPToolNames: classifiedTools.pluginMcpToolNames,
|
|
5645
|
+
activateNewTools: false,
|
|
5646
|
+
});
|
|
5647
|
+
} catch (error) {
|
|
5648
|
+
inheritedMcpManagerToolNames = [...new Set([...previousNames, ...nextNames])];
|
|
5649
|
+
throw error;
|
|
5650
|
+
}
|
|
5651
|
+
inheritedMcpManagerToolNames = nextNames;
|
|
5652
|
+
pluginMcpToolNames.splice(0, pluginMcpToolNames.length, ...classifiedTools.pluginMcpToolNames);
|
|
5653
|
+
conventionalMcpToolNames.splice(
|
|
5654
|
+
0,
|
|
5655
|
+
conventionalMcpToolNames.length,
|
|
5656
|
+
...classifiedTools.conventionalMcpToolNames,
|
|
5657
|
+
);
|
|
5658
|
+
})
|
|
5659
|
+
.catch(error => {
|
|
5660
|
+
logger.warn("Failed to publish inherited MCP tools", { error: safeErrorForLog(error) });
|
|
5661
|
+
});
|
|
5662
|
+
return inheritedMcpToolsSync;
|
|
5663
|
+
};
|
|
5664
|
+
inheritedMcpToolsPublisher = tools => {
|
|
5665
|
+
void syncInheritedTools(tools);
|
|
5666
|
+
};
|
|
5667
|
+
session.registerToolSessionCleanup(async () => {
|
|
5668
|
+
stopInheritedMcpToolsSubscription();
|
|
5669
|
+
await inheritedMcpToolsSync;
|
|
5670
|
+
});
|
|
5671
|
+
const initialSnapshot = pendingInheritedMcpToolsSnapshot ?? inheritedMcpToolsSnapshot;
|
|
5672
|
+
pendingInheritedMcpToolsSnapshot = undefined;
|
|
5673
|
+
await syncInheritedTools(initialSnapshot);
|
|
5674
|
+
};
|
|
5043
5675
|
// Exact-config managers do not receive reactive callbacks; their tools are
|
|
5044
5676
|
// registered once in the session-owned catalog. A pending conventional
|
|
5045
5677
|
// deferral wires its own callbacks from the starter after it connects, so
|
|
5046
5678
|
// nothing is wired here yet.
|
|
5047
5679
|
if (mcpManager && !options.mcpManager && explicitMcpConfigPath === undefined && !deferredConventionalMcp) {
|
|
5048
|
-
if (
|
|
5049
|
-
|
|
5680
|
+
if (publishOwnedMcpTools) {
|
|
5681
|
+
wireOwnedMcpToolSync();
|
|
5050
5682
|
} else if (!ownsMcpManager) {
|
|
5051
5683
|
mcpManager.setOnToolsChanged(tools => {
|
|
5052
|
-
void session.refreshMCPTools(tools);
|
|
5684
|
+
void session.refreshMCPTools(tools.map(tool => tool as CustomTool));
|
|
5053
5685
|
});
|
|
5054
5686
|
}
|
|
5055
5687
|
wireOwnedMcpManagerLifecycle();
|
|
5056
5688
|
}
|
|
5689
|
+
await wireInheritedMcpToolSync();
|
|
5057
5690
|
|
|
5058
5691
|
// Constructor-time workflow-gate tool restoration is deferred by one
|
|
5059
5692
|
// microtask (the ToolSession closure needs `session` assigned). Await it
|
|
@@ -5084,7 +5717,18 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
5084
5717
|
let hasErrors = false;
|
|
5085
5718
|
if (exact) {
|
|
5086
5719
|
const result = await exact.manager.discoverAndConnect({ configPath: exact.configPath });
|
|
5087
|
-
|
|
5720
|
+
// A slash control may commit while the deferred startup flight is
|
|
5721
|
+
// loading. Re-read the manager's fenced publication rather than
|
|
5722
|
+
// replaying the stale startup snapshot into the session registry. A
|
|
5723
|
+
// deferred manager can still be waiting to publish its first catalog,
|
|
5724
|
+
// though; only that explicit unpublished state may use the discovery
|
|
5725
|
+
// result. Empty published/fenced catalogs are authoritative and must
|
|
5726
|
+
// not replay stale tools across an exact control.
|
|
5727
|
+
const catalog = exact.manager.getToolCatalogSnapshot();
|
|
5728
|
+
const resultTools =
|
|
5729
|
+
catalog.publication === "unpublished"
|
|
5730
|
+
? (result.tools as CustomTool[])
|
|
5731
|
+
: (catalog.tools as CustomTool[]);
|
|
5088
5732
|
const toolNames = resultTools.map(tool => tool.name);
|
|
5089
5733
|
const collidingToolNames = findDeferredExactMcpToolNameCollisions(
|
|
5090
5734
|
toolNames,
|
|
@@ -5114,6 +5758,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
5114
5758
|
conventional.sources,
|
|
5115
5759
|
conventional.conventionalConfigs,
|
|
5116
5760
|
new Set<string>(),
|
|
5761
|
+
new Map<string, GjcPluginMcpServerProvenance>(),
|
|
5762
|
+
conventional.cleanup,
|
|
5117
5763
|
);
|
|
5118
5764
|
const resultTools = result.tools as CustomTool[];
|
|
5119
5765
|
if (!cancelled && !session.isDisposed) {
|
|
@@ -5123,7 +5769,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
5123
5769
|
// before this registration would therefore drop a persisted conventional
|
|
5124
5770
|
// MCP selection and persist the empty set.
|
|
5125
5771
|
await session.refreshMCPTools(resultTools);
|
|
5126
|
-
|
|
5772
|
+
wireOwnedMcpToolSync(resultTools);
|
|
5127
5773
|
wireOwnedMcpManagerLifecycle();
|
|
5128
5774
|
if (
|
|
5129
5775
|
!session.isDisposed &&
|
|
@@ -5181,6 +5827,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
5181
5827
|
} catch (error) {
|
|
5182
5828
|
// Release the subscription if the throw happened after install but before the
|
|
5183
5829
|
// dispose-wrap took ownership.
|
|
5830
|
+
stopInheritedMcpToolsSubscription();
|
|
5184
5831
|
releaseCredentialDisabledSubscription();
|
|
5185
5832
|
let cleanupDiagnostic: unknown;
|
|
5186
5833
|
try {
|