mixdog 1.2.0 → 1.2.2
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 +7 -5
- package/package.json +1 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -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 +13 -3
- package/src/defaults/skills/visualize/SKILL.md +12 -9
- package/src/repl/slash-commands.mjs +16 -1
- 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-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config.mjs +10 -4
- 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 +122 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
- package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
- 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 +447 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- 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-response-normalization.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
- 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/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
- 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/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -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/messages.mjs +6 -2
- 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.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -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 +183 -374
- 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 +41 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- 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/builtin-tools.mjs +5 -5
- 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-range-index.mjs +173 -22
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
- 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/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/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/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -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/pg/process.mjs +34 -24
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/atomic-file.mjs +25 -4
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/err-text.mjs +25 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -52
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/model-display.mjs +13 -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 +124 -20
- package/src/runtime/shared/provider-api-key.mjs +12 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- 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-format.mjs +4 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/turn-changes.mjs +447 -219
- package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
- package/src/runtime/shared/turn-snapshot.mjs +170 -48
- package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/contract.mjs +0 -24
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +61 -0
- package/src/session-runtime/boot/config.mjs +5 -2
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +35 -18
- 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 +10 -5
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/connect.mjs +83 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
- package/src/session-runtime/mcp-glue.mjs +25 -2
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +83 -15
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +9 -2
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- 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/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +24 -5
- package/src/session-runtime/resource-mcp-api.mjs +198 -78
- 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/runtime-review-api.mjs +13 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- 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/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +12 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-stored-session-views.mjs +11 -3
- package/src/standalone/daemon-telemetry.mjs +55 -0
- package/src/standalone/daemon.mjs +44 -11
- package/src/standalone/session-protocol.mjs +12 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
- package/src/standalone/session-service-api.mjs +5 -0
- package/src/standalone/session-service.mjs +1 -0
- 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 +7 -0
- package/src/tui/app/create-app-pickers.mjs +4 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +13 -16
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-commands.mjs +7 -2
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- 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/StatusLine.jsx +2 -0
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +1323 -214
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +33 -24
- package/src/tui/session/session-api/extensions.mjs +26 -2
- package/src/tui/session/session-api/integrations.mjs +7 -0
- package/src/tui/session/session-api/lifecycle.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +12 -14
- package/src/tui/session/session-flow/drain.mjs +21 -5
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn.mjs +55 -15
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
// Plugin/project MCP server discovery + normalization, and skill-file counting.
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
closeSync,
|
|
5
|
-
existsSync,
|
|
6
|
-
fsyncSync,
|
|
7
|
-
openSync,
|
|
8
|
-
readFileSync,
|
|
9
|
-
readdirSync,
|
|
10
|
-
renameSync,
|
|
11
|
-
statSync,
|
|
12
|
-
unlinkSync,
|
|
13
|
-
writeFileSync,
|
|
14
|
-
} from 'node:fs';
|
|
15
|
-
import { isAbsolute, join } from 'node:path';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { join, relative, sep } from 'node:path';
|
|
16
4
|
import { clean } from './session-text.mjs';
|
|
17
|
-
import {
|
|
18
|
-
|
|
5
|
+
import {
|
|
6
|
+
PLUGIN_MANIFEST_FILES,
|
|
7
|
+
pluginManifest,
|
|
8
|
+
pluginSkillsRoots,
|
|
9
|
+
resolveContainedPluginPath,
|
|
10
|
+
} from '../../../shared/plugin-manifest.mjs';
|
|
19
11
|
import { isPlainObject } from '../../../shared/object.mjs';
|
|
20
12
|
|
|
21
13
|
const MCP_TRANSPORT_FIELDS = new Set([
|
|
@@ -50,105 +42,19 @@ export function mergeMcpServerConfig(existing, next) {
|
|
|
50
42
|
return { ...merged, ...next };
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
function readProjectMcpDocument(cwd, allowMissing = false) {
|
|
54
|
-
const path = join(cwd || '.', '.mcp.json');
|
|
55
|
-
if (!existsSync(path)) {
|
|
56
|
-
if (allowMissing) return { path, raw: { mcpServers: {} }, usesWrapper: true };
|
|
57
|
-
throw new Error(`MCP config file not found: ${path}`);
|
|
58
|
-
}
|
|
59
|
-
let raw;
|
|
60
|
-
try {
|
|
61
|
-
raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
62
|
-
} catch (error) {
|
|
63
|
-
throw new Error(`cannot read ${path}: ${error?.message || String(error)}`);
|
|
64
|
-
}
|
|
65
|
-
if (!isPlainObject(raw)) throw new Error(`unexpected .mcp.json shape at ${path}`);
|
|
66
|
-
if (Object.hasOwn(raw, 'mcpServers') && !isPlainObject(raw.mcpServers)) {
|
|
67
|
-
throw new Error(`unexpected .mcp.json shape at ${path}`);
|
|
68
|
-
}
|
|
69
|
-
const usesWrapper = isPlainObject(raw.mcpServers);
|
|
70
|
-
return { path, raw, usesWrapper };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function writeProjectMcpDocument(path, raw) {
|
|
74
|
-
let mode = 0o644;
|
|
75
|
-
try {
|
|
76
|
-
mode = statSync(path).mode;
|
|
77
|
-
} catch {
|
|
78
|
-
/* new file */
|
|
79
|
-
}
|
|
80
|
-
const tempPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
81
|
-
let fd = null;
|
|
82
|
-
try {
|
|
83
|
-
fd = openSync(tempPath, 'w', mode);
|
|
84
|
-
writeFileSync(fd, `${JSON.stringify(raw, null, 2)}\n`, 'utf8');
|
|
85
|
-
fsyncSync(fd);
|
|
86
|
-
closeSync(fd);
|
|
87
|
-
fd = null;
|
|
88
|
-
chmodSync(tempPath, mode);
|
|
89
|
-
renameSync(tempPath, path);
|
|
90
|
-
} catch (error) {
|
|
91
|
-
if (fd !== null) {
|
|
92
|
-
try {
|
|
93
|
-
closeSync(fd);
|
|
94
|
-
} catch {
|
|
95
|
-
/* best effort */
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
unlinkSync(tempPath);
|
|
100
|
-
} catch {
|
|
101
|
-
/* best effort */
|
|
102
|
-
}
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// The stored key for `key`: an exact own key first, else one that trims to it.
|
|
108
|
-
function mcpEntryKey(map, key) {
|
|
109
|
-
return Object.hasOwn(map, key) ? key : Object.keys(map).find((candidate) => String(candidate || '').trim() === key);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function readProjectMcpServerConfig(cwd, name) {
|
|
113
|
-
const { raw, usesWrapper } = readProjectMcpDocument(cwd);
|
|
114
|
-
const map = usesWrapper ? raw.mcpServers : raw;
|
|
115
|
-
const entryKey = mcpEntryKey(map, String(name || '').trim());
|
|
116
|
-
return entryKey && isPlainObject(map[entryKey]) ? { name: entryKey, config: { ...map[entryKey] } } : null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function saveProjectMcpServer(cwd, { originalName = '', name, config }) {
|
|
120
|
-
const { path, raw, usesWrapper } = readProjectMcpDocument(cwd, !originalName);
|
|
121
|
-
const map = usesWrapper ? raw.mcpServers : raw;
|
|
122
|
-
const target = String(name || '').trim();
|
|
123
|
-
const original = String(originalName || '').trim();
|
|
124
|
-
if (!target) throw new Error('MCP server name is required');
|
|
125
|
-
const entryKey = original ? mcpEntryKey(map, original) : null;
|
|
126
|
-
if (original && !entryKey) throw new Error(`MCP server not defined in ${path}: ${original}`);
|
|
127
|
-
if (target !== entryKey && Object.hasOwn(map, target)) {
|
|
128
|
-
throw new Error(`MCP server already exists in ${path}: ${target}`);
|
|
129
|
-
}
|
|
130
|
-
const existing = entryKey ? map[entryKey] : {};
|
|
131
|
-
if (entryKey && entryKey !== target) delete map[entryKey];
|
|
132
|
-
map[target] = mergeMcpServerConfig(existing, config);
|
|
133
|
-
writeProjectMcpDocument(path, raw);
|
|
134
|
-
return { name: target, source: 'project', path, config: { ...map[target] } };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
45
|
// Manifest + contained-path resolution live in the shared module so skill
|
|
138
46
|
// discovery and this status surface read the same roots.
|
|
139
47
|
export { pluginManifest, resolveContainedPluginPath };
|
|
140
48
|
|
|
141
|
-
function mcpServersMapFromJson(raw) {
|
|
142
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
|
|
143
|
-
if (isPlainObject(raw.mcpServers)) return raw.mcpServers;
|
|
144
|
-
return raw;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
49
|
function isSkillMdFile(name) {
|
|
148
50
|
return /^(SKILL|skill)\.md$/i.test(name);
|
|
149
51
|
}
|
|
150
52
|
|
|
151
|
-
const
|
|
53
|
+
const PLUGIN_MANIFEST_FORMATS = {
|
|
54
|
+
'.codex-plugin/plugin.json': 'codex',
|
|
55
|
+
'plugin.json': 'mixdog',
|
|
56
|
+
'.claude-plugin/plugin.json': 'claude',
|
|
57
|
+
};
|
|
152
58
|
|
|
153
59
|
export function countSkillFiles(root) {
|
|
154
60
|
let count = 0;
|
|
@@ -177,102 +83,283 @@ export function countSkillFiles(root) {
|
|
|
177
83
|
return count;
|
|
178
84
|
}
|
|
179
85
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
187
|
-
} else if (isPlainObject(mcp)) {
|
|
188
|
-
const keys = Object.keys(mcp).filter((k) => isPlainObject(mcp[k]));
|
|
189
|
-
if (keys.length) return { mcpScript: null, mcpInline: true };
|
|
86
|
+
const MCP_STDIO_TYPES = new Set(['stdio']);
|
|
87
|
+
const MCP_URL_TYPES = new Set(['http', 'streamable-http', 'sse', 'ws']);
|
|
88
|
+
|
|
89
|
+
function manifestFormat(root) {
|
|
90
|
+
for (const rel of PLUGIN_MANIFEST_FILES) {
|
|
91
|
+
const path = join(root, ...rel.split('/'));
|
|
92
|
+
if (existsSync(path)) return { path, format: PLUGIN_MANIFEST_FORMATS[rel] };
|
|
190
93
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Transport a declared server asks for, inferred the way the MCP client does
|
|
98
|
+
* when `type` is absent: `transport: "http"` or a `url` -> http, else stdio.
|
|
99
|
+
* Null for an unrecognized explicit type. */
|
|
100
|
+
export function declaredMcpTransport(config) {
|
|
101
|
+
if (config?.type !== undefined && config?.type !== null && config?.type !== '') {
|
|
102
|
+
if (typeof config.type !== 'string') return null;
|
|
103
|
+
const type = config.type.toLowerCase();
|
|
104
|
+
if (type === 'streamablehttp') return 'streamable-http';
|
|
105
|
+
return MCP_STDIO_TYPES.has(type) || MCP_URL_TYPES.has(type) ? type : null;
|
|
195
106
|
}
|
|
196
|
-
|
|
107
|
+
if (config?.transport === 'http') return 'http';
|
|
108
|
+
if (config?.command !== undefined) return 'stdio';
|
|
109
|
+
if (config?.url !== undefined) return 'http';
|
|
110
|
+
return 'stdio';
|
|
197
111
|
}
|
|
198
112
|
|
|
199
|
-
|
|
200
|
-
|
|
113
|
+
// One server entry -> a message when it cannot be launched, else null. Messages
|
|
114
|
+
// name fields and expected types only, never a configured value.
|
|
115
|
+
function mcpServerProblem(config) {
|
|
116
|
+
if (!isPlainObject(config)) return 'server entry must be an object';
|
|
117
|
+
const type = declaredMcpTransport(config);
|
|
118
|
+
if (!type) return '"type" must be one of stdio, http, streamable-http, sse, ws';
|
|
119
|
+
if (MCP_STDIO_TYPES.has(type)) {
|
|
120
|
+
return typeof config.command === 'string' && config.command.trim() ? null : 'stdio server requires "command"';
|
|
121
|
+
}
|
|
122
|
+
return typeof config.url === 'string' && config.url.trim() ? null : `${type} server requires "url"`;
|
|
201
123
|
}
|
|
202
124
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
|
|
125
|
+
// A JSON parse failure as a location only: engines may quote the offending
|
|
126
|
+
// source text, which can hold credentials.
|
|
127
|
+
function jsonErrorMessage(error) {
|
|
128
|
+
const text = String(error?.message || '');
|
|
129
|
+
const lineColumn = /line (\d+) column (\d+)/.exec(text);
|
|
130
|
+
if (lineColumn) return `invalid JSON at line ${lineColumn[1]} column ${lineColumn[2]}`;
|
|
131
|
+
const position = /position (\d+)/.exec(text);
|
|
132
|
+
if (position) return `invalid JSON at position ${position[1]}`;
|
|
133
|
+
return /end of (JSON|data) input/i.test(text) ? 'invalid JSON: unexpected end of input' : 'invalid JSON';
|
|
210
134
|
}
|
|
211
135
|
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
136
|
+
function readJsonFile(path, errors) {
|
|
137
|
+
let text;
|
|
138
|
+
try {
|
|
139
|
+
text = readFileSync(path, 'utf8');
|
|
140
|
+
} catch (error) {
|
|
141
|
+
errors.push({ source: path, message: `cannot read file${error?.code ? ` (${error.code})` : ''}` });
|
|
142
|
+
return { ok: false };
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
return { ok: true, value: JSON.parse(text) };
|
|
146
|
+
} catch (error) {
|
|
147
|
+
errors.push({ source: path, message: jsonErrorMessage(error) });
|
|
148
|
+
return { ok: false };
|
|
149
|
+
}
|
|
215
150
|
}
|
|
216
151
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
for (const [k, v] of Object.entries(value)) out[k] = substitutePluginRootTokensDeep(v, root);
|
|
223
|
-
return out;
|
|
152
|
+
// A `{ mcpServers: {...} }` wrapper or a bare server map -> the server map.
|
|
153
|
+
function mcpServerMap(doc, source, errors, where = '') {
|
|
154
|
+
if (!isPlainObject(doc)) {
|
|
155
|
+
errors.push({ source, message: `${where}MCP declaration must be an object` });
|
|
156
|
+
return null;
|
|
224
157
|
}
|
|
225
|
-
return
|
|
158
|
+
if (!Object.hasOwn(doc, 'mcpServers')) return doc;
|
|
159
|
+
if (!isPlainObject(doc.mcpServers)) {
|
|
160
|
+
errors.push({ source, message: `${where}"mcpServers" must be an object` });
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return doc.mcpServers;
|
|
226
164
|
}
|
|
227
165
|
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
} else {
|
|
234
|
-
out.cwd = root;
|
|
166
|
+
function collectMcpServers(map, source, into, errors) {
|
|
167
|
+
for (const [key, config] of Object.entries(map)) {
|
|
168
|
+
const problem = mcpServerProblem(config);
|
|
169
|
+
if (problem) errors.push({ source, key, message: problem });
|
|
170
|
+
else into.set(key, { key, config, source });
|
|
235
171
|
}
|
|
236
|
-
return out;
|
|
237
172
|
}
|
|
238
173
|
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
if (!
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
174
|
+
function loadMcpJsonFile(path, into, errors) {
|
|
175
|
+
const read = readJsonFile(path, errors);
|
|
176
|
+
if (!read.ok) return;
|
|
177
|
+
const map = mcpServerMap(read.value, path, errors);
|
|
178
|
+
if (map) collectMcpServers(map, path, into, errors);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// `where` locates the manifest field (e.g. `mcpServers[1]: `); the declared
|
|
182
|
+
// path itself is never echoed.
|
|
183
|
+
function loadDeclaredPath(root, rel, manifestPath, into, errors, touched, where) {
|
|
184
|
+
if (typeof rel !== 'string' || !rel.trim()) {
|
|
185
|
+
errors.push({ source: manifestPath, message: `${where}MCP path must be a non-empty string` });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const abs = resolveContainedPluginPath(root, rel);
|
|
189
|
+
if (!abs || rel.split(/[\\/]/).includes('..')) {
|
|
190
|
+
errors.push({ source: manifestPath, message: `${where}MCP path must be relative and stay inside the plugin root` });
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
touched.add(abs);
|
|
194
|
+
if (!existsSync(abs)) {
|
|
195
|
+
errors.push({ source: manifestPath, message: `${where}MCP file not found` });
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
loadMcpJsonFile(abs, into, errors);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function readPluginMcpDeclarations(root, touched) {
|
|
202
|
+
const errors = [];
|
|
203
|
+
const into = new Map();
|
|
204
|
+
for (const rel of PLUGIN_MANIFEST_FILES) touched.add(join(root, ...rel.split('/')));
|
|
205
|
+
const found = manifestFormat(root);
|
|
206
|
+
const mcpJson = join(root, '.mcp.json');
|
|
207
|
+
touched.add(mcpJson);
|
|
208
|
+
const hasMcpJson = existsSync(mcpJson);
|
|
209
|
+
let format = 'none';
|
|
210
|
+
if (found) format = found.format;
|
|
211
|
+
else if (hasMcpJson) format = 'claude';
|
|
212
|
+
|
|
213
|
+
if (hasMcpJson) loadMcpJsonFile(mcpJson, into, errors);
|
|
214
|
+
|
|
215
|
+
let declaredRef;
|
|
216
|
+
if (found) {
|
|
217
|
+
const read = readJsonFile(found.path, errors);
|
|
218
|
+
const declared = read.ok && isPlainObject(read.value) ? read.value.mcpServers : undefined;
|
|
219
|
+
declaredRef = declared;
|
|
220
|
+
if (declared === undefined) {
|
|
221
|
+
/* nothing declared */
|
|
222
|
+
} else if (typeof declared === 'string') {
|
|
223
|
+
loadDeclaredPath(root, declared, found.path, into, errors, touched, '"mcpServers": ');
|
|
224
|
+
} else if (Array.isArray(declared)) {
|
|
225
|
+
// Members merge in order: a path to a JSON file, or an inline server
|
|
226
|
+
// map (bare or `{ mcpServers }`-wrapped).
|
|
227
|
+
declared.forEach((entry, index) => {
|
|
228
|
+
const where = `"mcpServers"[${index}]: `;
|
|
229
|
+
if (isPlainObject(entry)) {
|
|
230
|
+
const map = mcpServerMap(entry, found.path, errors, where);
|
|
231
|
+
if (map) collectMcpServers(map, found.path, into, errors);
|
|
232
|
+
} else {
|
|
233
|
+
loadDeclaredPath(root, entry, found.path, into, errors, touched, where);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
} else if (isPlainObject(declared)) {
|
|
237
|
+
const map = mcpServerMap(declared, found.path, errors);
|
|
238
|
+
if (map) collectMcpServers(map, found.path, into, errors);
|
|
239
|
+
} else {
|
|
240
|
+
errors.push({ source: found.path, message: '"mcpServers" must be a path, a list, or an object' });
|
|
247
241
|
}
|
|
248
|
-
const keys = Object.keys(rawServers).filter((k) => isPlainObject(rawServers[k]));
|
|
249
|
-
if (!keys.length) throw new Error('plugin.json has no mcpServers');
|
|
250
|
-
return { rawServers, mcpRoot: root };
|
|
251
242
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
243
|
+
const declaredPath = typeof declaredRef === 'string' ? declaredRef : null;
|
|
244
|
+
return { format, servers: [...into.values()], errors, declaredPath };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Parsed declarations per plugin root, valid while every file the parse looked
|
|
248
|
+
// at (present or absent) still has the same identity, size and timestamps.
|
|
249
|
+
const DECLARATION_CACHE_MAX = 256;
|
|
250
|
+
const declarationCache = new Map();
|
|
251
|
+
const declarationCacheStats = { hits: 0, misses: 0 };
|
|
252
|
+
|
|
253
|
+
function fileStamp(path) {
|
|
254
|
+
try {
|
|
255
|
+
const st = statSync(path, { bigint: true });
|
|
256
|
+
return `${st.ino}:${st.size}:${st.mtimeNs}:${st.ctimeNs}`;
|
|
257
|
+
} catch {
|
|
258
|
+
return '-';
|
|
256
259
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function copyDeclarations(result) {
|
|
263
|
+
return {
|
|
264
|
+
format: result.format,
|
|
265
|
+
servers: result.servers.map((server) => ({ ...server, config: structuredClone(server.config) })),
|
|
266
|
+
errors: result.errors.map((error) => ({ ...error })),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** What a plugin declares for MCP, read raw: `<root>/.mcp.json` first, then the
|
|
271
|
+
* manifest `mcpServers` (a path, an inline object, or a list mixing both).
|
|
272
|
+
* Later sources override earlier ones by key. Never throws for plugin
|
|
273
|
+
* content; errors carry locations and field names, never values. Unchanged
|
|
274
|
+
* declaration files are not parsed again. */
|
|
275
|
+
export function loadPluginMcpDeclarations(root) {
|
|
276
|
+
return copyDeclarations(cachedDeclarations(root));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function cachedDeclarations(root) {
|
|
280
|
+
const key = String(root || '');
|
|
281
|
+
const cached = declarationCache.get(key);
|
|
282
|
+
if (cached?.signature.every(([path, stamp]) => fileStamp(path) === stamp)) {
|
|
283
|
+
declarationCacheStats.hits += 1;
|
|
284
|
+
return cached.result;
|
|
285
|
+
}
|
|
286
|
+
declarationCacheStats.misses += 1;
|
|
287
|
+
// Stamps are taken before reading, so a write racing the parse invalidates.
|
|
288
|
+
const before = new Map();
|
|
289
|
+
const result = readPluginMcpDeclarations(root, {
|
|
290
|
+
add(path) {
|
|
291
|
+
if (!before.has(path)) before.set(path, fileStamp(path));
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
declarationCache.delete(key);
|
|
295
|
+
declarationCache.set(key, { signature: [...before], result });
|
|
296
|
+
if (declarationCache.size > DECLARATION_CACHE_MAX) declarationCache.delete(declarationCache.keys().next().value);
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Test seam: cache hit/miss counters. */
|
|
301
|
+
export function pluginMcpDeclarationCacheStats() {
|
|
302
|
+
return { ...declarationCacheStats };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Adapter: where the declarations live, in the shape older callers expect.
|
|
306
|
+
export function discoverPluginMcp(root) {
|
|
307
|
+
const { servers, declaredPath } = cachedDeclarations(root);
|
|
308
|
+
if (!servers.length) return { mcpScript: null, mcpInline: false };
|
|
309
|
+
const inline = servers.some((s) => PLUGIN_MANIFEST_FILES.some((rel) => s.source === join(root, ...rel.split('/'))));
|
|
310
|
+
if (inline) return { mcpScript: null, mcpInline: true };
|
|
311
|
+
if (declaredPath?.trim()) {
|
|
312
|
+
const abs = resolveContainedPluginPath(root, declaredPath);
|
|
313
|
+
if (servers.some((s) => s.source === abs)) return { mcpScript: declaredPath.trim(), mcpInline: false };
|
|
260
314
|
}
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
return {
|
|
315
|
+
const mcpJson = join(root, '.mcp.json');
|
|
316
|
+
const declaredFile = servers.find((s) => s.source !== mcpJson) || servers[0];
|
|
317
|
+
return { mcpScript: relative(root, declaredFile.source).split(sep).join('/'), mcpInline: false };
|
|
264
318
|
}
|
|
265
319
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
320
|
+
// User intent for plugin-declared servers, stored apart from the declarations
|
|
321
|
+
// as `pluginMcp[pluginId][declaredKey]`: `enabled: false`, a kept `alias`
|
|
322
|
+
// name, and connection overrides layered over the declaration.
|
|
323
|
+
export const PLUGIN_MCP_OVERRIDE_MAP_FIELDS = Object.freeze(['env', 'headers', 'env_http_headers']);
|
|
324
|
+
export const PLUGIN_MCP_OVERRIDE_VALUE_FIELDS = Object.freeze([
|
|
325
|
+
'command',
|
|
326
|
+
'args',
|
|
327
|
+
'url',
|
|
328
|
+
'bearer_token_env_var',
|
|
329
|
+
'env_vars',
|
|
330
|
+
...MCP_TIMEOUT_FIELD_GROUPS.flat(),
|
|
331
|
+
]);
|
|
332
|
+
|
|
333
|
+
function normalizePluginMcpIntent(value) {
|
|
334
|
+
if (!isPlainObject(value)) return null;
|
|
335
|
+
const out = {};
|
|
336
|
+
// `enabled: true` is kept too: it turns on a server declared `enabled: false`.
|
|
337
|
+
if (typeof value.enabled === 'boolean') out.enabled = value.enabled;
|
|
338
|
+
if (typeof value.alias === 'string' && value.alias.trim()) out.alias = value.alias.trim();
|
|
339
|
+
for (const field of PLUGIN_MCP_OVERRIDE_MAP_FIELDS) {
|
|
340
|
+
if (!isPlainObject(value[field])) continue;
|
|
341
|
+
const map = Object.fromEntries(Object.entries(value[field]).filter(([, v]) => v !== null && v !== undefined));
|
|
342
|
+
if (Object.keys(map).length) out[field] = map;
|
|
343
|
+
}
|
|
344
|
+
for (const field of PLUGIN_MCP_OVERRIDE_VALUE_FIELDS) {
|
|
345
|
+
if (value[field] !== undefined && value[field] !== null) out[field] = value[field];
|
|
346
|
+
}
|
|
347
|
+
return Object.keys(out).length ? out : null;
|
|
270
348
|
}
|
|
271
349
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
350
|
+
/** `{ [pluginId]: { [declaredKey]: intent } }` with empty records dropped. */
|
|
351
|
+
export function normalizePluginMcpIntents(value) {
|
|
352
|
+
const out = {};
|
|
353
|
+
if (!isPlainObject(value)) return out;
|
|
354
|
+
for (const [pluginId, servers] of Object.entries(value)) {
|
|
355
|
+
const id = clean(pluginId);
|
|
356
|
+
if (!id || !isPlainObject(servers)) continue;
|
|
357
|
+
const kept = {};
|
|
358
|
+
for (const [key, intent] of Object.entries(servers)) {
|
|
359
|
+
const normalized = normalizePluginMcpIntent(intent);
|
|
360
|
+
if (key && normalized) kept[key] = normalized;
|
|
361
|
+
}
|
|
362
|
+
if (Object.keys(kept).length) out[id] = kept;
|
|
363
|
+
}
|
|
364
|
+
return out;
|
|
278
365
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// definitions out of the eager prefix; selection never changes permission policy.
|
|
3
3
|
import { deferredCatalogUnion, selectDeferredTools, toolSearchNativePayload } from './tool-catalog.mjs';
|
|
4
4
|
import { preDispatchDenyForSession } from '../session/loop/pre-dispatch-deny.mjs';
|
|
5
|
+
import { parseMcpToolName } from '../../../shared/tool-primitives.mjs';
|
|
5
6
|
|
|
6
7
|
export function loadSkillToolDependencies(envelope, session, mode) {
|
|
7
8
|
if (!envelope?.__toolEnvelope) return envelope;
|
|
@@ -15,7 +16,9 @@ export function loadSkillToolDependencies(envelope, session, mode) {
|
|
|
15
16
|
for (const { type, value } of dependencies) {
|
|
16
17
|
if (type === 'tool') names.add(value);
|
|
17
18
|
else if (type === 'mcp') {
|
|
18
|
-
const matches = [...catalog.
|
|
19
|
+
const matches = [...catalog.values()]
|
|
20
|
+
.filter((tool) => parseMcpToolName(tool)?.server === value)
|
|
21
|
+
.map((tool) => tool.name);
|
|
19
22
|
if (matches.length)
|
|
20
23
|
matches.forEach((name) => {
|
|
21
24
|
names.add(name);
|
|
@@ -145,11 +145,32 @@ export function sortedCatalogByMeasuredUsage(catalog) {
|
|
|
145
145
|
.map((entry) => entry.tool);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
const IMMUTABLE_SURFACE_TOOLS = new WeakSet();
|
|
149
|
+
|
|
150
|
+
function deepFreeze(value) {
|
|
151
|
+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
152
|
+
Object.freeze(value);
|
|
153
|
+
for (const key of Object.keys(value)) deepFreeze(value[key]);
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
|
|
148
158
|
export function activeToolForSurface(tool) {
|
|
149
159
|
if (!tool || typeof tool !== 'object') return tool;
|
|
160
|
+
if (IMMUTABLE_SURFACE_TOOLS.has(tool)) return tool;
|
|
150
161
|
return JSON.parse(JSON.stringify(tool));
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
// Clone once into a deep-frozen definition that activeToolForSurface passes
|
|
165
|
+
// through without re-cloning. Safe to share: it can never be mutated in place.
|
|
166
|
+
export function immutableToolForSurface(tool) {
|
|
167
|
+
if (!tool || typeof tool !== 'object') return tool;
|
|
168
|
+
if (IMMUTABLE_SURFACE_TOOLS.has(tool)) return tool;
|
|
169
|
+
const frozen = deepFreeze(JSON.parse(JSON.stringify(tool)));
|
|
170
|
+
IMMUTABLE_SURFACE_TOOLS.add(frozen);
|
|
171
|
+
return frozen;
|
|
172
|
+
}
|
|
173
|
+
|
|
153
174
|
export function deferredProviderMode(provider) {
|
|
154
175
|
const p = clean(provider).toLowerCase();
|
|
155
176
|
if (p === 'gemini') return 'manifest';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { clean } from './session-text.mjs';
|
|
5
5
|
import { mergePendingDeferredToolDelta } from './deferred-tool-delta.mjs';
|
|
6
6
|
import { applyInitialDeferredToolManifestToBp2, stripDeferredToolManifestBlock } from '../context/collect.mjs';
|
|
7
|
-
import { getMcpServerInstructionsMap, getMcpTools } from '../mcp/client.mjs';
|
|
7
|
+
import { getMcpServerInstructionsMap, getMcpTools, getMcpToolGeneration } from '../mcp/client.mjs';
|
|
8
8
|
import { filterMcpToolsForSession } from './extension-scopes.mjs';
|
|
9
9
|
import { isDeferredToolAvailable } from './deferred-tool-availability.mjs';
|
|
10
10
|
import { isResponsesFreeformTool, toResponsesCustomTool } from '../providers/custom-tool-wire.mjs';
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
routeToolRank,
|
|
24
24
|
sortedCatalogByMeasuredUsage,
|
|
25
25
|
activeToolForSurface,
|
|
26
|
+
immutableToolForSurface,
|
|
26
27
|
deferredProviderMode,
|
|
27
28
|
nativeProviderFamily,
|
|
28
29
|
} from './tool-catalog-schema.mjs';
|
|
@@ -464,6 +465,56 @@ export function refreshInitialDeferredMcpSurface(session, liveMcpTools) {
|
|
|
464
465
|
*/
|
|
465
466
|
export function reconcileDeferredMcpToolCatalog(session, liveMcpTools) {
|
|
466
467
|
if (!session || !Array.isArray(session.tools)) return null;
|
|
468
|
+
const cached = RECONCILE_STATE.get(session);
|
|
469
|
+
if (cached && reconcileStateMatches(cached, reconcileStateOf(session, liveMcpTools))) return null;
|
|
470
|
+
const result = reconcileDeferredMcpToolCatalogUncached(session, liveMcpTools);
|
|
471
|
+
RECONCILE_STATE.set(session, reconcileStateOf(session, liveMcpTools));
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Last reconciled inputs per session. A matching state means the surface that
|
|
476
|
+
// reconciliation would rebuild is identical, so it is skipped outright.
|
|
477
|
+
const RECONCILE_STATE = new WeakMap();
|
|
478
|
+
const LIVE_CLONES = new WeakMap();
|
|
479
|
+
|
|
480
|
+
function liveToolClone(tool) {
|
|
481
|
+
if (!tool || typeof tool !== 'object') return tool;
|
|
482
|
+
const generation = getMcpToolGeneration();
|
|
483
|
+
const hit = LIVE_CLONES.get(tool);
|
|
484
|
+
if (hit && hit.generation === generation) return hit.clone;
|
|
485
|
+
const clone = immutableToolForSurface(tool);
|
|
486
|
+
LIVE_CLONES.set(tool, { generation, clone });
|
|
487
|
+
return clone;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function reconcileStateOf(session, liveMcpTools) {
|
|
491
|
+
return {
|
|
492
|
+
generation: getMcpToolGeneration(),
|
|
493
|
+
live: Array.isArray(liveMcpTools) ? liveMcpTools.slice() : [],
|
|
494
|
+
tools: session.tools.slice(),
|
|
495
|
+
lateCatalog: session.deferredLateToolCatalog,
|
|
496
|
+
scope: clean(session.mcpScopeId),
|
|
497
|
+
cwd: clean(session.cwd),
|
|
498
|
+
mode: clean(session.deferredSurfaceMode),
|
|
499
|
+
providerMode: clean(session.deferredProviderMode),
|
|
500
|
+
provider: clean(session.provider),
|
|
501
|
+
model: clean(session.model),
|
|
502
|
+
disallowed: JSON.stringify(session.disallowedTools || []),
|
|
503
|
+
callable: JSON.stringify(parseToolSelection(session.deferredCallableTools)),
|
|
504
|
+
discovered: JSON.stringify(parseToolSelection(session.deferredDiscoveredTools)),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function reconcileStateMatches(a, b) {
|
|
509
|
+
for (const key of Object.keys(a)) {
|
|
510
|
+
if (key === 'live' || key === 'tools') {
|
|
511
|
+
if (a[key].length !== b[key].length || a[key].some((tool, i) => tool !== b[key][i])) return false;
|
|
512
|
+
} else if (a[key] !== b[key]) return false;
|
|
513
|
+
}
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function reconcileDeferredMcpToolCatalogUncached(session, liveMcpTools) {
|
|
467
518
|
const live = filterDisallowedTools(Array.isArray(liveMcpTools) ? liveMcpTools : [], session.disallowedTools);
|
|
468
519
|
const hadSnapshot = Array.isArray(session.deferredMcpToolNames);
|
|
469
520
|
const previousNames = new Set(
|
|
@@ -482,7 +533,7 @@ export function reconcileDeferredMcpToolCatalog(session, liveMcpTools) {
|
|
|
482
533
|
}
|
|
483
534
|
for (const tool of live) {
|
|
484
535
|
const name = clean(tool?.name);
|
|
485
|
-
if (name && isMcpToolName(name)) byName.set(name,
|
|
536
|
+
if (name && isMcpToolName(name)) byName.set(name, liveToolClone(tool));
|
|
486
537
|
}
|
|
487
538
|
const catalog = sortedCatalogByMeasuredUsage([...byName.values()]);
|
|
488
539
|
const next = catalog.filter((tool) => session.deferredSurfaceMode !== 'readonly' || isReadonlySelectable(tool));
|
|
@@ -505,7 +556,7 @@ export function reconcileDeferredMcpToolCatalog(session, liveMcpTools) {
|
|
|
505
556
|
for (const tool of live) {
|
|
506
557
|
const name = clean(tool?.name);
|
|
507
558
|
if (!name || !isMcpToolName(name) || liveMcpByName.has(name)) continue;
|
|
508
|
-
liveMcpByName.set(name,
|
|
559
|
+
liveMcpByName.set(name, liveToolClone(tool));
|
|
509
560
|
}
|
|
510
561
|
|
|
511
562
|
// Rebuild the LATE pool only (boot catalog stays frozen). It holds live MCP
|