mixdog 1.1.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/package.json +2 -1
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +10 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +18 -5
- package/src/defaults/skills/visualize/SKILL.md +160 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/help.mjs +2 -0
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +19 -5
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
- package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/shared/atomic-file.mjs +33 -6
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -130
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +125 -21
- package/src/runtime/shared/provider-api-key.mjs +33 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/tool-surface.mjs +7 -0
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +90 -0
- package/src/session-runtime/boot/config.mjs +7 -2
- package/src/session-runtime/boot/facade.mjs +21 -0
- package/src/session-runtime/boot/infrastructure.mjs +30 -18
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +4 -0
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +68 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/mcp-glue.mjs +22 -2
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +18 -3
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/resource-extension-sync.mjs +45 -7
- package/src/session-runtime/resource-mcp-api.mjs +121 -84
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +23 -5
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +15 -4
- package/src/standalone/daemon.mjs +179 -8
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +11 -0
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +21 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +6 -0
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +37 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +839 -207
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +4 -2
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +17 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +9 -0
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +27 -3
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +56 -10
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
|
|
4
4
|
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
5
5
|
import { basename, dirname, join } from 'node:path';
|
|
6
|
-
import { maxMtimeRecursive } from '../cache-mtime.mjs';
|
|
6
|
+
import { maxMtimeRecursive, mtimeSignatureRecursive } from '../cache-mtime.mjs';
|
|
7
7
|
import { mixdogHome, resolvePluginData, mixdogRoot } from '../../../shared/plugin-paths.mjs';
|
|
8
|
-
import { pluginSkillsRoots } from '../../../shared/plugin-manifest.mjs';
|
|
8
|
+
import { PLUGIN_MANIFEST_FILES, pluginSkillsRoots } from '../../../shared/plugin-manifest.mjs';
|
|
9
9
|
import {
|
|
10
10
|
parseSkillDocument,
|
|
11
11
|
SKILL_TRIGGER_LISTING_MAX,
|
|
12
12
|
SKILL_TRIGGER_LISTING_MIN,
|
|
13
13
|
} from '../../../shared/skill-document.mjs';
|
|
14
14
|
import { readSkillToolDependencies, skillToolDependenciesRoot } from '../../../shared/skill-tool-dependencies.mjs';
|
|
15
|
+
import { SKILL_LAUNCHER_COMMAND } from '../../../shared/skill-script-runtime.mjs';
|
|
15
16
|
import { loadConfig, normalizeSkillsConfig } from '../config.mjs';
|
|
16
17
|
import { builtinFeatureActive, withGrandfatheredBuiltins } from '../runtime-core/builtin-features.mjs';
|
|
17
18
|
import { SKILL_TOOL } from '../runtime-core/tool-defs.mjs';
|
|
@@ -88,6 +89,32 @@ function pluginSkillDirs() {
|
|
|
88
89
|
return dirs;
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
/** Paths whose own mtime (no recursion) gates the skill list for enabled
|
|
93
|
+
* registered plugins: every manifest file, plus the nearest existing ancestor
|
|
94
|
+
* of each candidate skills root (so a skills dir created later is noticed). */
|
|
95
|
+
function pluginShallowWatchPaths() {
|
|
96
|
+
const registryPath = pluginRegistryPath();
|
|
97
|
+
let registry;
|
|
98
|
+
try {
|
|
99
|
+
registry = JSON.parse(readFileSync(registryPath, 'utf-8'));
|
|
100
|
+
} catch {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const paths = [];
|
|
104
|
+
for (const entry of Array.isArray(registry?.plugins) ? registry.plugins : []) {
|
|
105
|
+
if (entry?.enabled === false) continue;
|
|
106
|
+
const root = entry && typeof entry.root === 'string' ? entry.root : null;
|
|
107
|
+
if (!root || !existsSync(root)) continue;
|
|
108
|
+
for (const rel of PLUGIN_MANIFEST_FILES) paths.push(join(root, ...rel.split('/')));
|
|
109
|
+
for (const skillsDir of pluginSkillsRoots(root)) {
|
|
110
|
+
let dir = skillsDir;
|
|
111
|
+
while (!existsSync(dir) && dirname(dir) !== dir) dir = dirname(dir);
|
|
112
|
+
paths.push(dir);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return paths;
|
|
116
|
+
}
|
|
117
|
+
|
|
91
118
|
export function readSafe(path) {
|
|
92
119
|
try {
|
|
93
120
|
if (!existsSync(path)) return null;
|
|
@@ -216,24 +243,39 @@ function followsBuiltinFeature(skill) {
|
|
|
216
243
|
return skill?.source === 'builtin' && Array.isArray(skill.requires) && skill.requires.length > 0;
|
|
217
244
|
}
|
|
218
245
|
|
|
219
|
-
function findCachedSkillByKey(key) {
|
|
220
|
-
return collectSkillsCached(null).find((entry) => normalizeSkillNameKey(entry.name) === key);
|
|
246
|
+
function findCachedSkillByKey(key, skills = null) {
|
|
247
|
+
return (skills || collectSkillsCached(null)).find((entry) => normalizeSkillNameKey(entry.name) === key);
|
|
221
248
|
}
|
|
222
249
|
|
|
223
|
-
|
|
250
|
+
/** `skills`: optional snapshot from one collectSkillsCached() call, reused so a
|
|
251
|
+
* batch of per-skill checks scans the disk once. */
|
|
252
|
+
export function isSkillDisabled(name, config = null, skills = null) {
|
|
224
253
|
const n = normalizeSkillNameKey(name);
|
|
225
254
|
if (!n) return false;
|
|
226
|
-
const skill = findCachedSkillByKey(n);
|
|
255
|
+
const skill = findCachedSkillByKey(n, skills);
|
|
227
256
|
if (!followsBuiltinFeature(skill) && getDisabledSkillNameSet(config).has(n)) return true;
|
|
228
257
|
return Boolean(skill && missingFeature(skill, featureConfig(config)));
|
|
229
258
|
}
|
|
230
259
|
|
|
231
260
|
/** The built-in feature a skill needs that is not active, or null. */
|
|
232
|
-
export function skillMissingFeature(name, config = null) {
|
|
233
|
-
const skill = findCachedSkillByKey(normalizeSkillNameKey(name));
|
|
261
|
+
export function skillMissingFeature(name, config = null, skills = null) {
|
|
262
|
+
const skill = findCachedSkillByKey(normalizeSkillNameKey(name), skills);
|
|
234
263
|
return skill ? missingFeature(skill, featureConfig(config)) : null;
|
|
235
264
|
}
|
|
236
265
|
|
|
266
|
+
/** True when an installed skill exists but its (or its plugin's) project scope
|
|
267
|
+
* excludes `cwd` — the same scope rule filterSkillsExcludingDisabled applies. */
|
|
268
|
+
export function skillOutsideScope(name, cwd, config = null, skills = null) {
|
|
269
|
+
const skill = findCachedSkillByKey(normalizeSkillNameKey(name), skills);
|
|
270
|
+
if (!skill) return false;
|
|
271
|
+
return !skillAllowedForCwd(extensionScopesFromConfig(featureConfig(config)), skill, cwd);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** The Skill tool result for a skill skillOutsideScope refuses. */
|
|
275
|
+
export function skillOutsideScopeError(name) {
|
|
276
|
+
return `Error: skill "${name}" is not available in this project (its project scope excludes the current folder)`;
|
|
277
|
+
}
|
|
278
|
+
|
|
237
279
|
/**
|
|
238
280
|
* Skills a session may see: not switched off, feature available, and — when
|
|
239
281
|
* `cwd` is given — inside the skill's (or its plugin's) project scope. Without
|
|
@@ -260,8 +302,6 @@ export function collectPromptSkillsCached(cwd, config = null) {
|
|
|
260
302
|
}
|
|
261
303
|
// --- Global skill cache (mtime-based) ---
|
|
262
304
|
const _skillsCache = new Map();
|
|
263
|
-
const _mtimeCache = new Map();
|
|
264
|
-
const _MTIME_TTL_MS = 2000;
|
|
265
305
|
|
|
266
306
|
export function collectSkillsCached(cwd) {
|
|
267
307
|
if (skillsDisabled()) return [];
|
|
@@ -273,32 +313,41 @@ export function collectSkillsCached(cwd) {
|
|
|
273
313
|
skillsDirs.push(...pluginSkillDirs().map(({ dir }) => dir), ...builtinSkillDirs());
|
|
274
314
|
// registry.json itself gates plugin add/remove: removal deletes the
|
|
275
315
|
// plugin's skills dir (so no dir mtime advances), but saveRegistry()
|
|
276
|
-
// always rewrites this file.
|
|
316
|
+
// always rewrites this file. mtimeSignatureRecursive stats plain files directly.
|
|
277
317
|
const registryPath = pluginRegistryPath();
|
|
278
318
|
if (registryPath) skillsDirs.push(registryPath);
|
|
279
|
-
|
|
319
|
+
// Exact, un-timed gate: stat every call. The shallow watch list covers
|
|
320
|
+
// manifest edits and skill roots that do not exist yet.
|
|
321
|
+
const signature = `${mtimeSignatureRecursive(skillsDirs)}|${mtimeSignatureRecursive(pluginShallowWatchPaths(), 0)}`;
|
|
280
322
|
const entry = _skillsCache.get(key);
|
|
281
|
-
if (entry && entry.
|
|
323
|
+
if (entry && entry.signature === signature) {
|
|
282
324
|
return entry.value;
|
|
283
325
|
}
|
|
284
326
|
const skills = collectSkills(cwd);
|
|
285
|
-
_skillsCache.set(key, { value: skills,
|
|
327
|
+
_skillsCache.set(key, { value: skills, signature });
|
|
286
328
|
capMapSize(_skillsCache);
|
|
287
329
|
return skills;
|
|
288
330
|
}
|
|
289
331
|
export function invalidateSkillsCache(cwd) {
|
|
290
332
|
void cwd;
|
|
291
333
|
_skillsCache.clear();
|
|
292
|
-
_mtimeCache.clear();
|
|
293
334
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Directory the model hands to external processes for a skill. A packaged
|
|
338
|
+
* desktop runtime reads built-in skills from inside `runtime.asar`, which only
|
|
339
|
+
* Electron can open; the build ships them as real files beside the archive
|
|
340
|
+
* under `runtime.asar.unpacked`, so a built-in skill's directory points there.
|
|
341
|
+
* Every other source keeps its own on-disk directory.
|
|
342
|
+
*/
|
|
343
|
+
export function skillExecutionDir(dir, source) {
|
|
344
|
+
if (source !== 'builtin') return dir;
|
|
345
|
+
return String(dir).replace(/([\\/])runtime\.asar(?=[\\/]|$)/, '$1runtime.asar.unpacked');
|
|
298
346
|
}
|
|
299
347
|
|
|
300
348
|
/**
|
|
301
|
-
* Load full skill content plus its on-disk directory (for base-dir + ${MIXDOG_SKILL_DIR}
|
|
349
|
+
* Load full skill content plus its on-disk directory (for base-dir + ${MIXDOG_SKILL_DIR};
|
|
350
|
+
* ${MIXDOG_SKILL_NODE} is resolved at render time in buildSkillResultEnvelope).
|
|
302
351
|
*/
|
|
303
352
|
export function loadSkillResource(name, cwd) {
|
|
304
353
|
const skillName = String(name || '').trim();
|
|
@@ -311,7 +360,7 @@ export function loadSkillResource(name, cwd) {
|
|
|
311
360
|
try {
|
|
312
361
|
return {
|
|
313
362
|
content: parseSkillDocument(content).body,
|
|
314
|
-
dir: dirname(skill.filePath),
|
|
363
|
+
dir: skillExecutionDir(dirname(skill.filePath), skill.source),
|
|
315
364
|
filePath: skill.filePath,
|
|
316
365
|
source: skill.source || 'global',
|
|
317
366
|
toolDependencies: skill.toolDependencies || [],
|
|
@@ -335,6 +384,10 @@ export function buildSkillResultEnvelope(name, content, skillDir) {
|
|
|
335
384
|
const escapedName = escapeSkillXmlText(name);
|
|
336
385
|
let body = String(content == null ? '' : content);
|
|
337
386
|
let dirLine = '';
|
|
387
|
+
// `${MIXDOG_SKILL_NODE}` runs a bundled script on this daemon's own runtime
|
|
388
|
+
// (plain node cannot load runtime internals from a packaged archive). It is
|
|
389
|
+
// one shell-neutral command name that model shells find on their PATH.
|
|
390
|
+
body = body.replace(/\$\{MIXDOG_SKILL_NODE\}/g, SKILL_LAUNCHER_COMMAND);
|
|
338
391
|
if (skillDir) {
|
|
339
392
|
const normDir = String(skillDir).replace(/\\/g, '/');
|
|
340
393
|
body = body.replace(/\$\{MIXDOG_SKILL_DIR\}/g, normDir);
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* kind, URL policy, env expansion, stdio environment, HTTP headers, timeouts,
|
|
4
4
|
* and the log scrubbing that keeps credentials out of connection messages.
|
|
5
5
|
*/
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { rememberMcpToolIdentity } from '../../../shared/tool-primitives.mjs';
|
|
6
8
|
|
|
7
9
|
/** Known auto-detect targets: the discovery-advert service name and the MCP
|
|
8
10
|
* endpoint path on its port.
|
|
@@ -15,8 +17,16 @@ export const AUTO_DETECT_PORTS = {
|
|
|
15
17
|
'mixdog-memory': { discovery: 'memory', endpoint: '/mcp' },
|
|
16
18
|
};
|
|
17
19
|
const DEFAULT_MCP_CALL_TIMEOUT_MS = 120000;
|
|
18
|
-
// Per-server STARTUP handshake budget (connect + listTools):
|
|
19
|
-
|
|
20
|
+
// Per-server STARTUP handshake budget (connect + paginated listTools): 30s.
|
|
21
|
+
// Cold npx/uvx wrappers routinely need well over 10s to install and boot.
|
|
22
|
+
const DEFAULT_MCP_STARTUP_TIMEOUT_MS = 30000;
|
|
23
|
+
// Hard ceiling for a request that keeps reporting progress (each progress
|
|
24
|
+
// notification resets the per-call timeout, this caps the total): 30min.
|
|
25
|
+
const DEFAULT_MCP_MAX_TOTAL_TIMEOUT_MS = 1_800_000;
|
|
26
|
+
// Largest delay setTimeout honours; anything larger fires after 1ms. Used as
|
|
27
|
+
// the SDK request timeout when our own call timeout is disabled, so the SDK's
|
|
28
|
+
// built-in 60s default never applies.
|
|
29
|
+
export const MCP_MAX_TIMER_MS = 2_147_483_647;
|
|
20
30
|
|
|
21
31
|
function isLoopbackMcpHost(hostname) {
|
|
22
32
|
const host = String(hostname || '')
|
|
@@ -100,30 +110,106 @@ export function scrubMcpConnectionMessage(value, cfg = {}) {
|
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
/**
|
|
103
|
-
* Expand `${VAR}` and `${
|
|
104
|
-
* provided env map (defaults to process.env). Recurses into
|
|
105
|
-
*
|
|
113
|
+
* Expand `${VAR}`, `${env:VAR}` and `${VAR:-default}` references in string
|
|
114
|
+
* values using the provided env map (defaults to process.env). Recurses into
|
|
115
|
+
* arrays/objects. The default (possibly empty) applies only when the variable
|
|
116
|
+
* is unset, and runs to the first `}`. An unset variable without a default
|
|
117
|
+
* expands to an empty string and, when `missing` (a Set) is given, its name is
|
|
118
|
+
* recorded there. No shell execution.
|
|
106
119
|
*/
|
|
107
|
-
export function expandEnvVars(value, env = process.env) {
|
|
120
|
+
export function expandEnvVars(value, env = process.env, missing) {
|
|
108
121
|
if (typeof value === 'string') {
|
|
109
|
-
return value.replace(/\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)
|
|
122
|
+
return value.replace(/\$\{(?:env:)?([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (_m, name, fallback) => {
|
|
110
123
|
const v = env?.[name];
|
|
111
|
-
|
|
124
|
+
if (v != null) return String(v);
|
|
125
|
+
if (fallback !== undefined) return fallback;
|
|
126
|
+
missing?.add(name);
|
|
127
|
+
return '';
|
|
112
128
|
});
|
|
113
129
|
}
|
|
114
130
|
if (Array.isArray(value)) {
|
|
115
|
-
return value.map((v) => expandEnvVars(v, env));
|
|
131
|
+
return value.map((v) => expandEnvVars(v, env, missing));
|
|
116
132
|
}
|
|
117
133
|
if (value && typeof value === 'object') {
|
|
118
134
|
const out = {};
|
|
119
135
|
for (const [k, v] of Object.entries(value)) {
|
|
120
|
-
out[k] = expandEnvVars(v, env);
|
|
136
|
+
out[k] = expandEnvVars(v, env, missing);
|
|
121
137
|
}
|
|
122
138
|
return out;
|
|
123
139
|
}
|
|
124
140
|
return value;
|
|
125
141
|
}
|
|
126
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Names (deduplicated, first-seen order) of variables a server config needs
|
|
145
|
+
* that are unset: `${VAR}` references without a default in command, args,
|
|
146
|
+
* env, url, headers and cwd, plus the variables named directly by
|
|
147
|
+
* `bearer_token_env_var`, `env_http_headers` values and `env_vars`. Names
|
|
148
|
+
* only — never values.
|
|
149
|
+
*/
|
|
150
|
+
export function findMissingMcpEnvVars(cfg = {}, env = process.env) {
|
|
151
|
+
const missing = new Set();
|
|
152
|
+
expandEnvVars([cfg.command, cfg.args, cfg.env, cfg.url, cfg.headers, cfg.cwd], env, missing);
|
|
153
|
+
const named = [
|
|
154
|
+
cfg.bearer_token_env_var,
|
|
155
|
+
...(cfg.env_http_headers && typeof cfg.env_http_headers === 'object' && !Array.isArray(cfg.env_http_headers)
|
|
156
|
+
? Object.values(cfg.env_http_headers)
|
|
157
|
+
: []),
|
|
158
|
+
...(Array.isArray(cfg.env_vars) ? cfg.env_vars : []),
|
|
159
|
+
];
|
|
160
|
+
for (const value of named) {
|
|
161
|
+
const name = typeof value === 'string' ? value.trim() : '';
|
|
162
|
+
if (name && envValue(env, name) == null) missing.add(name);
|
|
163
|
+
}
|
|
164
|
+
return [...missing];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Windows variable names are case-insensitive.
|
|
168
|
+
function envValue(env, name) {
|
|
169
|
+
if (process.platform !== 'win32') return env?.[name];
|
|
170
|
+
const actual = Object.keys(env || {}).find((key) => key.toLowerCase() === name.toLowerCase());
|
|
171
|
+
return actual ? env[actual] : undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Provider tool names: at most 64 characters of [A-Za-z0-9_-] (the strictest
|
|
175
|
+
// limit among the providers Mixdog talks to).
|
|
176
|
+
const PROVIDER_TOOL_NAME_MAX = 64;
|
|
177
|
+
const PROVIDER_TOOL_NAME_RE = /^[A-Za-z0-9_-]+$/;
|
|
178
|
+
const KEPT_SERVER_PART_MAX = 40;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* `mcp__<server>__<tool>` when that already fits provider limits (unchanged
|
|
182
|
+
* for every name that fits today). Otherwise a deterministic short form:
|
|
183
|
+
* the server part kept when it is safe (sanitized and cut short when not),
|
|
184
|
+
* the tool part sanitized and cut, and a hash of the full name appended so
|
|
185
|
+
* distinct tools never meet. Callers keep the original tool name alongside
|
|
186
|
+
* to dispatch the call.
|
|
187
|
+
*/
|
|
188
|
+
export function providerSafeMcpToolName(serverName, toolName) {
|
|
189
|
+
const server = String(serverName || '');
|
|
190
|
+
const tool = String(toolName || '');
|
|
191
|
+
const full = `mcp__${server}__${tool}`;
|
|
192
|
+
if (full.length <= PROVIDER_TOOL_NAME_MAX && PROVIDER_TOOL_NAME_RE.test(full)) return full;
|
|
193
|
+
const hash = createHash('sha1').update(full).digest('hex').slice(0, 8);
|
|
194
|
+
const serverPart =
|
|
195
|
+
/^[A-Za-z0-9-]+(?:_[A-Za-z0-9-]+)*$/.test(server) && server.length <= KEPT_SERVER_PART_MAX
|
|
196
|
+
? server
|
|
197
|
+
: server
|
|
198
|
+
.replace(/[^A-Za-z0-9-]+/g, '-')
|
|
199
|
+
.replace(/^-+|-+$/g, '')
|
|
200
|
+
.slice(0, 24)
|
|
201
|
+
.replace(/-+$/, '') || 'server';
|
|
202
|
+
const room = PROVIDER_TOOL_NAME_MAX - `mcp__${serverPart}__`.length - hash.length - 1;
|
|
203
|
+
const toolPart = tool
|
|
204
|
+
.replace(/[^A-Za-z0-9_-]+/g, '_')
|
|
205
|
+
.slice(0, Math.max(0, room))
|
|
206
|
+
.replace(/_+$/, '');
|
|
207
|
+
const safe = `mcp__${serverPart}__${toolPart ? `${toolPart}_` : ''}${hash}`;
|
|
208
|
+
// Name-only consumers in this process (display, guards) resolve it back.
|
|
209
|
+
rememberMcpToolIdentity(safe, server, tool);
|
|
210
|
+
return safe;
|
|
211
|
+
}
|
|
212
|
+
|
|
127
213
|
const DEFAULT_STDIO_ENV_KEYS =
|
|
128
214
|
process.platform === 'win32'
|
|
129
215
|
? [
|
|
@@ -204,8 +290,9 @@ export function resolveMcpTransportKind(cfg) {
|
|
|
204
290
|
// (e.g. a busy editor) must not stall a tool call indefinitely. Genuinely
|
|
205
291
|
// long-running tools can raise/disable it via MIXDOG_MCP_CALL_TIMEOUT_MS or a
|
|
206
292
|
// per-server timeoutMs/callTimeoutMs config value (0/off/none/false disables).
|
|
207
|
-
// On expiry
|
|
208
|
-
//
|
|
293
|
+
// On expiry only the timed-out request is cancelled (notifications/cancelled);
|
|
294
|
+
// the server and its other in-flight calls stay up, and the timed-out call is
|
|
295
|
+
// never retried automatically (avoids side-effect dupes).
|
|
209
296
|
export function resolveMcpCallTimeoutMs(cfg = {}, env = process.env) {
|
|
210
297
|
const raw =
|
|
211
298
|
cfg?.timeoutMs ?? cfg?.timeout_ms ?? cfg?.callTimeoutMs ?? cfg?.call_timeout_ms ?? env?.MIXDOG_MCP_CALL_TIMEOUT_MS;
|
|
@@ -213,11 +300,42 @@ export function resolveMcpCallTimeoutMs(cfg = {}, env = process.env) {
|
|
|
213
300
|
if (typeof raw === 'string' && /^(0|off|none|false)$/i.test(raw.trim())) return 0;
|
|
214
301
|
const parsed = Number(raw);
|
|
215
302
|
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_MCP_CALL_TIMEOUT_MS;
|
|
216
|
-
return Math.round(parsed);
|
|
303
|
+
return Math.min(Math.round(parsed), MCP_MAX_TIMER_MS);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Total cap for a call whose server keeps reporting progress. Default 30min.
|
|
307
|
+
// Per-server override: maxTotalTimeoutMs / max_total_timeout_ms. Global env:
|
|
308
|
+
// MIXDOG_MCP_MAX_TOTAL_TIMEOUT_MS. 0/off/none/false removes the cap.
|
|
309
|
+
export function resolveMcpMaxTotalTimeoutMs(cfg = {}, env = process.env) {
|
|
310
|
+
const raw = cfg?.maxTotalTimeoutMs ?? cfg?.max_total_timeout_ms ?? env?.MIXDOG_MCP_MAX_TOTAL_TIMEOUT_MS;
|
|
311
|
+
if (raw == null || raw === '' || raw === false) return DEFAULT_MCP_MAX_TOTAL_TIMEOUT_MS;
|
|
312
|
+
if (raw === 0 || (typeof raw === 'string' && /^(0|off|none|false)$/i.test(raw.trim()))) return 0;
|
|
313
|
+
const parsed = Number(raw);
|
|
314
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_MCP_MAX_TOTAL_TIMEOUT_MS;
|
|
315
|
+
return Math.min(Math.round(parsed), MCP_MAX_TIMER_MS);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The effective limits of one MCP request:
|
|
320
|
+
* - callTimeoutMs: our per-call timeout (0 = disabled), reset on progress;
|
|
321
|
+
* - sdkTimeoutMs: what the SDK gets as `timeout` — the call timeout, or the
|
|
322
|
+
* largest timer when disabled so the SDK's 60s default never applies;
|
|
323
|
+
* - maxTotalTimeoutMs: the progress-reset ceiling (never below the call
|
|
324
|
+
* timeout), or undefined when the call is unbounded or the cap is off.
|
|
325
|
+
*/
|
|
326
|
+
export function resolveMcpRequestTimeouts(cfg = {}, env = process.env) {
|
|
327
|
+
const callTimeoutMs = resolveMcpCallTimeoutMs(cfg, env);
|
|
328
|
+
if (!(callTimeoutMs > 0)) return { callTimeoutMs: 0, sdkTimeoutMs: MCP_MAX_TIMER_MS, maxTotalTimeoutMs: undefined };
|
|
329
|
+
const cap = resolveMcpMaxTotalTimeoutMs(cfg, env);
|
|
330
|
+
return {
|
|
331
|
+
callTimeoutMs,
|
|
332
|
+
sdkTimeoutMs: callTimeoutMs,
|
|
333
|
+
maxTotalTimeoutMs: cap > 0 ? Math.max(cap, callTimeoutMs) : undefined,
|
|
334
|
+
};
|
|
217
335
|
}
|
|
218
336
|
|
|
219
337
|
// MCP per-server STARTUP timeout: bounds the connect + listTools handshake so a
|
|
220
|
-
// slow or hung server can't stall boot or the first turn. Default
|
|
338
|
+
// slow or hung server can't stall boot or the first turn. Default 30s.
|
|
221
339
|
// Per-server override: startupTimeoutMs / startupTimeoutSec. Global
|
|
222
340
|
// env: MIXDOG_MCP_STARTUP_TIMEOUT_MS. A value of 0/off/none/false disables it.
|
|
223
341
|
export function resolveMcpStartupTimeoutMs(cfg = {}, env = process.env) {
|
|
@@ -235,5 +353,5 @@ export function resolveMcpStartupTimeoutMs(cfg = {}, env = process.env) {
|
|
|
235
353
|
if (raw === 0 || (typeof raw === 'string' && /^(0|off|none|false)$/i.test(raw.trim()))) return 0;
|
|
236
354
|
const parsed = Number(raw) * scale;
|
|
237
355
|
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_MCP_STARTUP_TIMEOUT_MS;
|
|
238
|
-
return Math.round(parsed);
|
|
356
|
+
return Math.min(Math.round(parsed), MCP_MAX_TIMER_MS);
|
|
239
357
|
}
|
|
@@ -4,14 +4,44 @@
|
|
|
4
4
|
* and a discovery advert that refused the connection is distrusted.
|
|
5
5
|
*/
|
|
6
6
|
import { markServiceUnreachable, isConnRefuseError } from '../../../shared/service-discovery.mjs';
|
|
7
|
-
import { resolveMcpStartupTimeoutMs } from './client-config.mjs';
|
|
7
|
+
import { MCP_MAX_TIMER_MS, resolveMcpStartupTimeoutMs } from './client-config.mjs';
|
|
8
|
+
import { appendMcpStderrTail } from './client-transport.mjs';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
// tools/list pagination bounds: a server that never stops handing out cursors
|
|
11
|
+
// (or repeats one) cannot loop the client forever.
|
|
12
|
+
const MAX_MCP_TOOL_PAGES = 100;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Every tool of a server: follows `nextCursor` until it is exhausted, repeats,
|
|
16
|
+
* or MAX_MCP_TOOL_PAGES is reached. A page with an invalid shape is returned
|
|
17
|
+
* as-is so the caller's shape validation reports it.
|
|
18
|
+
* The SDK rebuilds its output validators from each page alone, so after a
|
|
19
|
+
* paginated listing they are rebuilt once from every page's tools.
|
|
20
|
+
*/
|
|
21
|
+
export async function listAllMcpTools(client, requestOptions = undefined) {
|
|
22
|
+
const tools = [];
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
let cursor = null;
|
|
25
|
+
let pages = 0;
|
|
26
|
+
for (; pages < MAX_MCP_TOOL_PAGES; pages += 1) {
|
|
27
|
+
const result = await client.listTools(cursor ? { cursor } : undefined, requestOptions);
|
|
28
|
+
if (!result || !Array.isArray(result.tools)) return result;
|
|
29
|
+
tools.push(...result.tools);
|
|
30
|
+
const next = typeof result.nextCursor === 'string' && result.nextCursor ? result.nextCursor : null;
|
|
31
|
+
if (!next || seen.has(next)) break;
|
|
32
|
+
seen.add(next);
|
|
33
|
+
cursor = next;
|
|
34
|
+
}
|
|
35
|
+
if (pages > 0) client.cacheToolMetadata?.(tools);
|
|
36
|
+
return { tools };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function performHandshake(client, transport, requestOptions) {
|
|
40
|
+
await client.connect(transport, requestOptions);
|
|
11
41
|
const instructionsRaw = typeof client.getInstructions === 'function' ? client.getInstructions() : undefined;
|
|
12
42
|
const instructions = typeof instructionsRaw === 'string' ? instructionsRaw.trim() : '';
|
|
13
43
|
const capabilities = client.getServerCapabilities?.() || {};
|
|
14
|
-
const toolsResult = capabilities.tools ? await client
|
|
44
|
+
const toolsResult = capabilities.tools ? await listAllMcpTools(client, requestOptions) : { tools: [] };
|
|
15
45
|
return { instructions, toolsResult, capabilities };
|
|
16
46
|
}
|
|
17
47
|
|
|
@@ -28,12 +58,24 @@ function startupTimeoutError(name, startupTimeoutMs) {
|
|
|
28
58
|
* boot or the first turn. On expiry the pending transport/child is torn down
|
|
29
59
|
* (nothing leaks) and this server fails like any other connect failure — the
|
|
30
60
|
* parallel Promise.allSettled means other servers are unaffected.
|
|
31
|
-
*
|
|
61
|
+
* The SDK's per-request timeout follows the same budget (its own 60s default
|
|
62
|
+
* would otherwise cut a longer or disabled budget short). Failures carry the
|
|
63
|
+
* server's stderr tail. Returns { instructions, toolsResult, capabilities }.
|
|
32
64
|
*/
|
|
33
|
-
export async function runBoundedHandshake({
|
|
65
|
+
export async function runBoundedHandshake({
|
|
66
|
+
name,
|
|
67
|
+
cfg,
|
|
68
|
+
client,
|
|
69
|
+
transport,
|
|
70
|
+
autoDetectAdvert,
|
|
71
|
+
closeServer,
|
|
72
|
+
stderrTail = () => '',
|
|
73
|
+
}) {
|
|
34
74
|
const startupTimeoutMs = resolveMcpStartupTimeoutMs(cfg);
|
|
35
75
|
const guard = { timer: null, timedOut: false };
|
|
36
|
-
const handshake = performHandshake(client, transport
|
|
76
|
+
const handshake = performHandshake(client, transport, {
|
|
77
|
+
timeout: startupTimeoutMs > 0 ? startupTimeoutMs : MCP_MAX_TIMER_MS,
|
|
78
|
+
});
|
|
37
79
|
try {
|
|
38
80
|
if (startupTimeoutMs <= 0) return await handshake;
|
|
39
81
|
const deadline = new Promise((_, rej) => {
|
|
@@ -63,7 +105,12 @@ export async function runBoundedHandshake({ name, cfg, client, transport, autoDe
|
|
|
63
105
|
// Never let the late handshake settle into an unhandled rejection.
|
|
64
106
|
handshake.catch(() => {});
|
|
65
107
|
}
|
|
66
|
-
|
|
108
|
+
const tail = stderrTail();
|
|
109
|
+
if (tail && err instanceof Error) {
|
|
110
|
+
err.message = appendMcpStderrTail(err.message, tail);
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
throw tail ? new Error(appendMcpStderrTail(String(err), tail)) : err;
|
|
67
114
|
} finally {
|
|
68
115
|
if (guard.timer) clearTimeout(guard.timer);
|
|
69
116
|
}
|
|
@@ -7,6 +7,7 @@ import { readServicePort } from '../../../shared/service-discovery.mjs';
|
|
|
7
7
|
import {
|
|
8
8
|
AUTO_DETECT_PORTS,
|
|
9
9
|
expandEnvVars,
|
|
10
|
+
findMissingMcpEnvVars,
|
|
10
11
|
mcpUrlForLog,
|
|
11
12
|
normalizeMcpTransportUrl,
|
|
12
13
|
resolveMcpHttpHeaders,
|
|
@@ -32,19 +33,83 @@ function resolveAutoDetectUrl(name, cfg) {
|
|
|
32
33
|
return { url: `http://127.0.0.1:${port}${spec.endpoint}`, advert: { service: spec.discovery, port } };
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
// Bounded tail of a stdio server's stderr, kept for failure messages (a server
|
|
37
|
+
// that dies at startup or mid-session usually says why on stderr).
|
|
38
|
+
const MCP_STDERR_TAIL_CHARS = 4096;
|
|
39
|
+
const noStderrTail = () => '';
|
|
40
|
+
|
|
41
|
+
/** Append a server's stderr tail to a failure message (unchanged when empty). */
|
|
42
|
+
export function appendMcpStderrTail(message, tail) {
|
|
43
|
+
const text = String(tail || '').trim();
|
|
44
|
+
return text ? `${message}\n--- server stderr (tail) ---\n${text}` : String(message);
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
function requestInitFor(cfg) {
|
|
36
48
|
const headers = resolveMcpHttpHeaders(cfg);
|
|
37
49
|
return headers && Object.keys(headers).length > 0 ? { requestInit: { headers } } : undefined;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
// The SDK's WebSocket transport opens its socket without a way to set request
|
|
53
|
+
// headers. This subclass changes only socket creation: Node's WebSocket takes
|
|
54
|
+
// `{ protocols, headers }`; the guard and event wiring match the stock start().
|
|
55
|
+
function createHeaderWebSocketTransport(Base, messageSchema, url, headers) {
|
|
56
|
+
return new (class extends Base {
|
|
57
|
+
start() {
|
|
58
|
+
if (this._socket) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
'WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
this._socket = new globalThis.WebSocket(this._url, { protocols: 'mcp', headers });
|
|
65
|
+
this._socket.onerror = (event) => {
|
|
66
|
+
const error = 'error' in event ? event.error : new Error(`WebSocket error: ${JSON.stringify(event)}`);
|
|
67
|
+
reject(error);
|
|
68
|
+
this.onerror?.(error);
|
|
69
|
+
};
|
|
70
|
+
this._socket.onopen = () => resolve();
|
|
71
|
+
this._socket.onclose = () => this.onclose?.();
|
|
72
|
+
this._socket.onmessage = (event) => {
|
|
73
|
+
let message;
|
|
74
|
+
try {
|
|
75
|
+
message = messageSchema.parse(JSON.parse(event.data));
|
|
76
|
+
} catch (error) {
|
|
77
|
+
this.onerror?.(error);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.onmessage?.(message);
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
})(url);
|
|
85
|
+
}
|
|
86
|
+
/** Returns { transport, autoDetectAdvert, stderrTail, warnings } — stderrTail()
|
|
87
|
+
* is the last ~4 KB of a stdio server's (scrubbed) stderr, '' for remote
|
|
88
|
+
* transports. `strictEnv` (the caller passes it for plugin-declared servers)
|
|
89
|
+
* makes unset variables without a default fail the server; otherwise they
|
|
90
|
+
* expand to empty and are reported in `warnings` (names only). */
|
|
91
|
+
export function createMcpTransport({ name, cfg, sdk, log, strictEnv }) {
|
|
42
92
|
const { StdioClientTransport, StreamableHTTPClientTransport, SSEClientTransport, WebSocketClientTransport } = sdk;
|
|
43
93
|
const kind = resolveMcpTransportKind(cfg);
|
|
94
|
+
const warnings = [];
|
|
95
|
+
if (kind !== 'autoDetect') {
|
|
96
|
+
const missing = findMissingMcpEnvVars(cfg);
|
|
97
|
+
if (missing.length) {
|
|
98
|
+
const message = `missing environment variable(s): ${missing.join(', ')}`;
|
|
99
|
+
if (strictEnv === true) throw new Error(message);
|
|
100
|
+
warnings.push(message);
|
|
101
|
+
log(`[mcp-client] "${name}": ${message}\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
44
104
|
if (kind === 'autoDetect') {
|
|
45
105
|
const { url, advert } = resolveAutoDetectUrl(name, cfg);
|
|
46
106
|
log(`[mcp-client] Connecting "${name}" via autoDetect HTTP: ${url}\n`);
|
|
47
|
-
return {
|
|
107
|
+
return {
|
|
108
|
+
transport: new StreamableHTTPClientTransport(new URL(url)),
|
|
109
|
+
autoDetectAdvert: advert,
|
|
110
|
+
stderrTail: noStderrTail,
|
|
111
|
+
warnings,
|
|
112
|
+
};
|
|
48
113
|
}
|
|
49
114
|
if (kind === 'http' || kind === 'sse') {
|
|
50
115
|
const Transport = kind === 'http' ? StreamableHTTPClientTransport : SSEClientTransport;
|
|
@@ -54,26 +119,39 @@ export function createMcpTransport({ name, cfg, sdk, log }) {
|
|
|
54
119
|
return {
|
|
55
120
|
transport: opts ? new Transport(new URL(url), opts) : new Transport(new URL(url)),
|
|
56
121
|
autoDetectAdvert: null,
|
|
122
|
+
stderrTail: noStderrTail,
|
|
123
|
+
warnings,
|
|
57
124
|
};
|
|
58
125
|
}
|
|
59
126
|
if (kind === 'ws') {
|
|
60
|
-
// WebSocketClientTransport ctor takes only a URL; headers are ignored.
|
|
61
127
|
const url = normalizeMcpTransportUrl(expandEnvVars(String(cfg.url ?? '')), 'ws');
|
|
128
|
+
const opts = requestInitFor(cfg);
|
|
62
129
|
log(`[mcp-client] Connecting "${name}" via WebSocket: ${mcpUrlForLog(url)}\n`);
|
|
63
|
-
|
|
130
|
+
const transport = opts
|
|
131
|
+
? createHeaderWebSocketTransport(
|
|
132
|
+
WebSocketClientTransport,
|
|
133
|
+
sdk.JSONRPCMessageSchema,
|
|
134
|
+
new URL(url),
|
|
135
|
+
opts.requestInit.headers
|
|
136
|
+
)
|
|
137
|
+
: new WebSocketClientTransport(new URL(url));
|
|
138
|
+
return { transport, autoDetectAdvert: null, stderrTail: noStderrTail, warnings };
|
|
64
139
|
}
|
|
65
140
|
if (kind === 'stdio') {
|
|
66
141
|
const transport = new StdioClientTransport({
|
|
67
142
|
command: expandEnvVars(String(cfg.command ?? '')),
|
|
68
143
|
args: Array.isArray(cfg.args) ? expandEnvVars(cfg.args) : cfg.args,
|
|
69
|
-
cwd: cfg.cwd,
|
|
144
|
+
cwd: cfg.cwd == null ? cfg.cwd : expandEnvVars(String(cfg.cwd)),
|
|
70
145
|
env: resolveMcpStdioEnvironment(cfg),
|
|
71
146
|
stderr: cfg.stderr ?? 'pipe',
|
|
72
147
|
});
|
|
148
|
+
let tail = '';
|
|
73
149
|
transport.stderr?.on?.('data', (chunk) => {
|
|
74
|
-
|
|
150
|
+
const text = scrubMcpConnectionMessage(chunk, cfg);
|
|
151
|
+
tail = (tail + text).slice(-MCP_STDERR_TAIL_CHARS);
|
|
152
|
+
log(`[mcp:${name}:stderr] ${text}`);
|
|
75
153
|
});
|
|
76
|
-
return { transport, autoDetectAdvert: null };
|
|
154
|
+
return { transport, autoDetectAdvert: null, stderrTail: () => tail.trim(), warnings };
|
|
77
155
|
}
|
|
78
156
|
throw new Error(
|
|
79
157
|
`Invalid config for "${name}": need autoDetect, type (stdio/http/sse/ws), url (http), or command (stdio)`
|