mixdog 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/package.json +2 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- 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/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
- 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/providers/account-pool.mjs +188 -29
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -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 +48 -37
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
- 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 +102 -45
- 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-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
- 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 +88 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
- 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 +103 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -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-request-body.mjs +41 -5
- 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 +369 -112
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
- 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/grok-oauth.mjs +69 -19
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- 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-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- 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 +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
- 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 +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
- 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/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -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 +7 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- 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-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- 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-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -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/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- 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 +1 -97
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- 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-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +65 -50
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +2 -2
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +31 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -40
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +670 -274
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
- package/src/runtime/shared/turn-snapshot.mjs +986 -690
- package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -24
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +19 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +12 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +73 -8
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +46 -14
- package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
- package/src/session-runtime/mcp-glue.mjs +3 -0
- 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 +85 -17
- 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/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- 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 +4 -0
- package/src/session-runtime/resource-mcp-api.mjs +98 -2
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +21 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- 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/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- 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/tag-registry.mjs +15 -1
- 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-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +74 -0
- package/src/standalone/daemon.mjs +64 -8
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +14 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -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 +18 -3
- package/src/standalone/session-service-api.mjs +11 -1
- package/src/standalone/session-service.mjs +1 -0
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +6 -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.mjs +2 -1
- package/src/tui/app/model-options.mjs +11 -16
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +20 -5
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- 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/dist/index.mjs +3221 -1500
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +43 -24
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/extensions.mjs +22 -0
- package/src/tui/session/session-api/integrations.mjs +16 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +7 -2
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +11 -14
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/session-flow/drain.mjs +7 -1
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +46 -16
- 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/workflows/default/WORKFLOW.md +3 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import keychain from '../../../../lib/keychain-cjs.cjs';
|
|
4
|
+
import { openInBrowser } from '../../../shared/open-url.mjs';
|
|
5
|
+
import { normalizeMcpTransportUrl } from './client-config.mjs';
|
|
6
|
+
import {
|
|
7
|
+
MCP_OAUTH_CALLBACK_MAX_BYTES,
|
|
8
|
+
MCP_OAUTH_CALLBACK_NOT_PENDING,
|
|
9
|
+
parsePastedMcpOAuthCallback,
|
|
10
|
+
} from './oauth-callback.mjs';
|
|
11
|
+
import {
|
|
12
|
+
TOKENS_FLAG,
|
|
13
|
+
createCredentialCache,
|
|
14
|
+
credentialFile,
|
|
15
|
+
readStoredCredentials,
|
|
16
|
+
withCredentialLock,
|
|
17
|
+
writeRevisionMarker,
|
|
18
|
+
} from './oauth-credentials.mjs';
|
|
19
|
+
import { createLoginFlow } from './oauth-login.mjs';
|
|
20
|
+
import { createOAuthProvider } from './oauth-provider.mjs';
|
|
21
|
+
import { createAuthorizedFetch } from './oauth-request.mjs';
|
|
22
|
+
import { REAUTHENTICATION_REQUIRED_MESSAGE, anySignal } from './oauth-shared.mjs';
|
|
23
|
+
|
|
24
|
+
export { MCP_OAUTH_CALLBACK_MAX_BYTES, MCP_OAUTH_CALLBACK_NOT_PENDING, parsePastedMcpOAuthCallback };
|
|
25
|
+
|
|
26
|
+
export const MCP_OAUTH_TIMEOUT_MS = 5 * 60_000;
|
|
27
|
+
|
|
28
|
+
const scopes = new WeakMap();
|
|
29
|
+
|
|
30
|
+
/** Whether a connect failed only because the server needs an (explicit) OAuth
|
|
31
|
+
* sign-in. Transports may wrap the error (SSE re-raises it as an event
|
|
32
|
+
* message), so the message and cause chain count too. */
|
|
33
|
+
export function isMcpOAuthReauthenticationRequired(error) {
|
|
34
|
+
for (let current = error, depth = 0; current && depth < 5; current = current.cause, depth++) {
|
|
35
|
+
if (current.code === 'reauthenticationRequired') return true;
|
|
36
|
+
if (String(current.message ?? current).includes(REAUTHENTICATION_REQUIRED_MESSAGE)) return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// One keychain entry per exact server URL. Windows stores each account as a
|
|
42
|
+
// file name, so the account is a hash of the URL and nothing else.
|
|
43
|
+
export function mcpOAuthAccount(url) {
|
|
44
|
+
return `mcp-oauth.${createHash('sha256').update(url).digest('hex')}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Whether OAuth tokens are stored for one server URL, read from the
|
|
48
|
+
* non-secret revision marker without touching the keychain. */
|
|
49
|
+
export function hasStoredMcpOAuthTokens(url) {
|
|
50
|
+
try {
|
|
51
|
+
const marker = readFileSync(credentialFile(mcpOAuthAccount(normalizeMcpTransportUrl(url)), 'rev'), 'utf8');
|
|
52
|
+
return marker.endsWith(TOKENS_FLAG);
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Log out of one MCP server URL: delete its stored tokens and client
|
|
59
|
+
* registration and drop them from live connections, which then need an
|
|
60
|
+
* explicit reconnect to log in again. */
|
|
61
|
+
export async function clearMcpOAuth({ url, credentialStore = keychain }) {
|
|
62
|
+
url = normalizeMcpTransportUrl(url);
|
|
63
|
+
const account = mcpOAuthAccount(url);
|
|
64
|
+
await withCredentialLock(account, async () => {
|
|
65
|
+
await writeRevisionMarker(account, null);
|
|
66
|
+
if ((await readStoredCredentials(credentialStore, account)) != null)
|
|
67
|
+
await credentialStore.deleteSecretAsync(account);
|
|
68
|
+
});
|
|
69
|
+
scopes.get(credentialStore)?.get(url)?.oauth.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The keychain is the only durable store. Exact URLs, including query strings,
|
|
73
|
+
// partition clients and tokens even when servers share an authorization issuer.
|
|
74
|
+
export function createMcpOAuth({
|
|
75
|
+
url,
|
|
76
|
+
credentialStore = keychain,
|
|
77
|
+
interactive = false,
|
|
78
|
+
isTransportEndpoint = () => false,
|
|
79
|
+
serverName,
|
|
80
|
+
...options
|
|
81
|
+
}) {
|
|
82
|
+
url = normalizeMcpTransportUrl(url);
|
|
83
|
+
let entries = scopes.get(credentialStore);
|
|
84
|
+
if (!entries) {
|
|
85
|
+
entries = new Map();
|
|
86
|
+
scopes.set(credentialStore, entries);
|
|
87
|
+
}
|
|
88
|
+
let entry = entries.get(url);
|
|
89
|
+
if (!entry) {
|
|
90
|
+
entry = { oauth: createSharedOAuth({ url, credentialStore, ...options }), users: 0 };
|
|
91
|
+
entries.set(url, entry);
|
|
92
|
+
}
|
|
93
|
+
entry.users++;
|
|
94
|
+
const lifetime = new AbortController();
|
|
95
|
+
let onStart = () => {};
|
|
96
|
+
let loginAllowed = interactive === true;
|
|
97
|
+
let loginStarted = false;
|
|
98
|
+
let loginCompleted = false;
|
|
99
|
+
return {
|
|
100
|
+
provider: entry.oauth.provider,
|
|
101
|
+
signal: lifetime.signal,
|
|
102
|
+
set onStart(value) {
|
|
103
|
+
onStart = value;
|
|
104
|
+
},
|
|
105
|
+
/** Whether this connection was opened by an explicit sign-in. */
|
|
106
|
+
get interactive() {
|
|
107
|
+
return interactive === true;
|
|
108
|
+
},
|
|
109
|
+
/** Whether this connection started an interactive (browser) login. */
|
|
110
|
+
get loginStarted() {
|
|
111
|
+
return loginStarted;
|
|
112
|
+
},
|
|
113
|
+
/** Whether that login obtained tokens (the sign-in itself completed). */
|
|
114
|
+
get loginCompleted() {
|
|
115
|
+
return loginCompleted;
|
|
116
|
+
},
|
|
117
|
+
finishConnect() {
|
|
118
|
+
loginAllowed = false;
|
|
119
|
+
},
|
|
120
|
+
/** Every OAuth token this connection held, for scrubbing errors and results. */
|
|
121
|
+
secrets: () => entry.oauth.secrets(),
|
|
122
|
+
/** Answer the browser login this connection started with the redirect URL
|
|
123
|
+
* pasted from the browser, exactly as the loopback callback would. */
|
|
124
|
+
completeCallback(pasted) {
|
|
125
|
+
if (!loginStarted || loginCompleted || lifetime.signal.aborted) throw new Error(MCP_OAUTH_CALLBACK_NOT_PENDING);
|
|
126
|
+
entry.oauth.completeCallback(pasted);
|
|
127
|
+
},
|
|
128
|
+
async fetch(input, init = {}) {
|
|
129
|
+
const signal = anySignal(lifetime.signal, init.signal);
|
|
130
|
+
let startedHere = false;
|
|
131
|
+
return entry.oauth.fetch(
|
|
132
|
+
input,
|
|
133
|
+
{ ...init, signal },
|
|
134
|
+
() => {
|
|
135
|
+
if (!loginAllowed) return false;
|
|
136
|
+
loginAllowed = false;
|
|
137
|
+
loginStarted = startedHere = true;
|
|
138
|
+
onStart();
|
|
139
|
+
return true;
|
|
140
|
+
},
|
|
141
|
+
options.openBrowser,
|
|
142
|
+
isTransportEndpoint,
|
|
143
|
+
serverName,
|
|
144
|
+
// The login this request started stored tokens (a cancel, denial,
|
|
145
|
+
// timeout or failed exchange never gets here); the retry after it may
|
|
146
|
+
// still fail on its own.
|
|
147
|
+
() => {
|
|
148
|
+
if (startedHere) loginCompleted = true;
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
close() {
|
|
153
|
+
if (lifetime.signal.aborted) return;
|
|
154
|
+
lifetime.abort();
|
|
155
|
+
if (--entry.users === 0) {
|
|
156
|
+
entry.oauth.close();
|
|
157
|
+
entries.delete(url);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function createSharedOAuth({
|
|
164
|
+
url,
|
|
165
|
+
credentialStore,
|
|
166
|
+
openBrowser = openInBrowser,
|
|
167
|
+
fetchImpl = fetch,
|
|
168
|
+
timeoutMs = MCP_OAUTH_TIMEOUT_MS,
|
|
169
|
+
}) {
|
|
170
|
+
const lifetime = new AbortController();
|
|
171
|
+
const cache = createCredentialCache({ url, credentialStore, account: mcpOAuthAccount(url) });
|
|
172
|
+
const { creds } = cache;
|
|
173
|
+
const flow = {
|
|
174
|
+
listener: undefined,
|
|
175
|
+
state: undefined,
|
|
176
|
+
verifier: undefined,
|
|
177
|
+
allowBrowser: false,
|
|
178
|
+
browserOpened: false,
|
|
179
|
+
launchBrowser: openBrowser,
|
|
180
|
+
finishCallback: undefined,
|
|
181
|
+
};
|
|
182
|
+
const provider = createOAuthProvider({ cache, flow, lifetimeSignal: lifetime.signal });
|
|
183
|
+
const { login, stopListening, closeListener, answerCallback } = createLoginFlow({
|
|
184
|
+
url,
|
|
185
|
+
cache,
|
|
186
|
+
flow,
|
|
187
|
+
provider,
|
|
188
|
+
lifetimeSignal: lifetime.signal,
|
|
189
|
+
openBrowser,
|
|
190
|
+
fetchImpl,
|
|
191
|
+
timeoutMs,
|
|
192
|
+
});
|
|
193
|
+
const requests = createAuthorizedFetch({
|
|
194
|
+
url,
|
|
195
|
+
cache,
|
|
196
|
+
login,
|
|
197
|
+
fetchImpl,
|
|
198
|
+
lifetimeSignal: lifetime.signal,
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
provider,
|
|
202
|
+
signal: lifetime.signal,
|
|
203
|
+
fetch: requests.fetch,
|
|
204
|
+
secrets: () => [...cache.held],
|
|
205
|
+
/** Answer the pending browser login with a pasted redirect URL: the same
|
|
206
|
+
* state check, PKCE exchange and persistence as the loopback callback,
|
|
207
|
+
* whose listener closes. An invalid paste leaves the login pending. */
|
|
208
|
+
completeCallback(pasted) {
|
|
209
|
+
if (!flow.finishCallback || !flow.browserOpened || !flow.state || !creds.redirectUrl) {
|
|
210
|
+
throw new Error(MCP_OAUTH_CALLBACK_NOT_PENDING);
|
|
211
|
+
}
|
|
212
|
+
const { code } = parsePastedMcpOAuthCallback(pasted, { redirectUrl: creds.redirectUrl, state: flow.state });
|
|
213
|
+
stopListening();
|
|
214
|
+
// A denial (no code) fails the login without echoing its description.
|
|
215
|
+
answerCallback(code);
|
|
216
|
+
},
|
|
217
|
+
/** Forget in-memory credentials (after a log-out) and cancel a login in
|
|
218
|
+
* progress; the next request re-reads the keychain. */
|
|
219
|
+
clear() {
|
|
220
|
+
requests.cancelLogin();
|
|
221
|
+
cache.clear();
|
|
222
|
+
},
|
|
223
|
+
close() {
|
|
224
|
+
lifetime.abort();
|
|
225
|
+
flow.finishCallback?.();
|
|
226
|
+
closeListener();
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -11,17 +11,60 @@ import {
|
|
|
11
11
|
providerAccountsResetAt,
|
|
12
12
|
} from '../../../shared/provider-accounts.mjs';
|
|
13
13
|
import { withProviderAccount, hasExplicitProviderAuthBinding } from '../../../shared/provider-auth-binding.mjs';
|
|
14
|
-
import {
|
|
14
|
+
import { isExplicitUserAbortError, markProviderRecoveryExhausted } from './retry-classifier.mjs';
|
|
15
|
+
import { sleepWithAbort } from './retry-backoff.mjs';
|
|
16
|
+
import { QUOTA_KINDS, providerErrorKind } from './provider-error-kind.mjs';
|
|
15
17
|
import { fetchOAuthUsageSnapshot, readCachedOAuthUsageSnapshot } from './oauth-usage.mjs';
|
|
16
18
|
|
|
17
|
-
// Only an explicit quota refusal advances the roster
|
|
18
|
-
// refusal, policy denial or transport
|
|
19
|
+
// Only an explicit quota / plan / billing refusal advances the roster as
|
|
20
|
+
// quota; a generic 429, overload, auth refusal, policy denial or transport
|
|
21
|
+
// failure retains its existing recovery.
|
|
19
22
|
export function isAccountQuotaError(error) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (isExplicitUserAbortError(error)) return false;
|
|
24
|
+
const { kind, status } = providerErrorKind(error);
|
|
25
|
+
if (status === 401 || status === 403) return false;
|
|
26
|
+
return QUOTA_KINDS.has(kind);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Kinds that say nothing about the account: the server (or the network) is
|
|
30
|
+
// temporarily unable to serve (or has no capacity in the requested service
|
|
31
|
+
// tier). They never block or rotate an account.
|
|
32
|
+
const SERVER_SIDE_KINDS = new Set(['overloaded', 'transient', 'service_tier_unavailable']);
|
|
33
|
+
|
|
34
|
+
// Provider-supplied roster cooldowns (`accountCooldownPolicy` on the provider
|
|
35
|
+
// object), used when the server named no delay:
|
|
36
|
+
// { floorMs, rateLimitedMs, quotaStepsMs, unknownMs, rotateOnRateLimit,
|
|
37
|
+
// waitForCooldownMs, blockedMs }
|
|
38
|
+
// blockedMs: cooldown for an `accountBlocked` refusal, which then rotates.
|
|
39
|
+
// quotaStepsMs escalates per consecutive quota refusal of one account (reset
|
|
40
|
+
// by a served answer). Absent → the inferred five minutes.
|
|
41
|
+
// rotateOnRateLimit rotates on a plain 429 rate limit; its advice, when
|
|
42
|
+
// present, becomes that account's cooldown.
|
|
43
|
+
// waitForCooldownMs: when every eligible account is only cooling down (no
|
|
44
|
+
// quota evidence), the send waits — cancellable, reported as reconnect
|
|
45
|
+
// progress — for the earliest cooldown while the send's total wait stays
|
|
46
|
+
// within this bound; beyond it the cooldown error surfaces. Absent → no wait.
|
|
47
|
+
const INFERRED_COOLDOWN_MS = 5 * 60_000;
|
|
48
|
+
const quotaStrikes = new Map();
|
|
49
|
+
|
|
50
|
+
function cooldownPolicyOf(instance) {
|
|
51
|
+
const policy = instance?.accountCooldownPolicy;
|
|
52
|
+
return policy && typeof policy === 'object' ? policy : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function finiteMs(value, fallback) {
|
|
56
|
+
const n = Number(value);
|
|
57
|
+
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function policyCooldownMs(policy, strikeKey, reason) {
|
|
61
|
+
if (reason === 'rate_limited') return finiteMs(policy.rateLimitedMs, INFERRED_COOLDOWN_MS);
|
|
62
|
+
if (reason === 'quota' && Array.isArray(policy.quotaStepsMs) && policy.quotaStepsMs.length) {
|
|
63
|
+
const strikes = quotaStrikes.get(strikeKey) || 0;
|
|
64
|
+
quotaStrikes.set(strikeKey, strikes + 1);
|
|
65
|
+
return finiteMs(policy.quotaStepsMs[Math.min(strikes, policy.quotaStepsMs.length - 1)], INFERRED_COOLDOWN_MS);
|
|
66
|
+
}
|
|
67
|
+
return finiteMs(policy.unknownMs, INFERRED_COOLDOWN_MS);
|
|
25
68
|
}
|
|
26
69
|
|
|
27
70
|
// Burst throttles reopen within seconds. A 429 whose Retry-After points
|
|
@@ -77,6 +120,23 @@ function accountsExhaustedError(useCredits) {
|
|
|
77
120
|
return error;
|
|
78
121
|
}
|
|
79
122
|
|
|
123
|
+
// Every eligible account is only cooling down after a rate limit (no quota
|
|
124
|
+
// evidence): a rate limit, advising the earliest cooldown end as the wait.
|
|
125
|
+
// The pool already spent its cooldown wait, so this is a terminal verdict for
|
|
126
|
+
// the loop, not an unclassified failure.
|
|
127
|
+
function earliestCooldownMs(rows, now) {
|
|
128
|
+
const ends = rows.map((row) => Number(row.blockedUntil)).filter((until) => Number.isFinite(until) && until > now);
|
|
129
|
+
return ends.length ? Math.min(...ends) - now : null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function accountsCoolingDownError(rows, now) {
|
|
133
|
+
const error = new Error('Every connected account is cooling down after a rate limit. Try again shortly.');
|
|
134
|
+
error.code = 'rate_limited';
|
|
135
|
+
const wait = earliestCooldownMs(rows, now);
|
|
136
|
+
if (wait != null) error.retryAfterMs = wait;
|
|
137
|
+
return markProviderRecoveryExhausted(error, { owner: 'account-pool', terminal: true });
|
|
138
|
+
}
|
|
139
|
+
|
|
80
140
|
// Structured proof that the send ended because no account can serve `model`
|
|
81
141
|
// (not merely because a generic 429 or a stopped loop surfaced an error).
|
|
82
142
|
function quotaExhausted(error, providerName, model, rows, options = {}) {
|
|
@@ -200,15 +260,53 @@ function recordServingAccount(providerName, result, id, bindingId) {
|
|
|
200
260
|
// retry delay. Anthropic and other subscriptions may use the same 429 code
|
|
201
261
|
// for burst throttling and exhausted subscription windows, so the native
|
|
202
262
|
// usage endpoint decides rather than a human-readable message.
|
|
263
|
+
// `until` is when the block lifts: the real quota reset when the provider
|
|
264
|
+
// named one, else the advised delay, else an inferred five minutes. A long
|
|
265
|
+
// Retry-After without quota evidence is a rate-limit cooldown: it rotates
|
|
266
|
+
// this send, blocks no longer than advised and is never quota evidence.
|
|
203
267
|
async function accountQuotaExhausted(providerName, row, model, error, account) {
|
|
204
|
-
const
|
|
205
|
-
const
|
|
268
|
+
const { kind, status, retryAfterMs: delay, resetAt } = providerErrorKind(error);
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
const policy = cooldownPolicyOf(account(row.id));
|
|
271
|
+
const floorMs = policy ? finiteMs(policy.floorMs, 0) : 0;
|
|
272
|
+
const blockUntil = (withReset, reason = 'unknown') => {
|
|
273
|
+
if (withReset && resetAt > now) return { until: resetAt, source: 'retry-after' };
|
|
274
|
+
if (delay > 0) return { until: now + Math.max(delay, floorMs), source: 'retry-after' };
|
|
275
|
+
if (policy) {
|
|
276
|
+
const ms = policyCooldownMs(policy, `${providerName}:${row.id}`, reason);
|
|
277
|
+
return { until: now + Math.max(ms, floorMs), source: 'inferred' };
|
|
278
|
+
}
|
|
279
|
+
return { until: now + INFERRED_COOLDOWN_MS, source: 'inferred' };
|
|
280
|
+
};
|
|
281
|
+
if (SERVER_SIDE_KINDS.has(kind) || isExplicitUserAbortError(error)) {
|
|
282
|
+
return { exhausted: false, delay, evidence: false, ...blockUntil(false) };
|
|
283
|
+
}
|
|
284
|
+
// An account-level block (`accountBlocked`, e.g. the account needs
|
|
285
|
+
// verification): with a policy `blockedMs` that account cools for it and the
|
|
286
|
+
// roster rotates; never quota evidence. Without one it surfaces as before.
|
|
287
|
+
const blockedMs = policy ? finiteMs(policy.blockedMs, 0) : 0;
|
|
288
|
+
if (error?.accountBlocked === true && blockedMs > 0) {
|
|
289
|
+
return {
|
|
290
|
+
exhausted: true,
|
|
291
|
+
accountBlocked: true,
|
|
292
|
+
delay,
|
|
293
|
+
evidence: false,
|
|
294
|
+
until: now + Math.max(blockedMs, floorMs),
|
|
295
|
+
source: 'inferred',
|
|
296
|
+
};
|
|
297
|
+
}
|
|
206
298
|
const typed = isAccountQuotaError(error);
|
|
207
|
-
|
|
299
|
+
if (typed) return { exhausted: true, delay, evidence: true, ...blockUntil(true, 'quota') };
|
|
300
|
+
// A policy with rotateOnRateLimit rotates on a plain rate limit too (the
|
|
301
|
+
// advice as its cooldown, else rateLimitedMs); never quota evidence.
|
|
302
|
+
if (policy?.rotateOnRateLimit === true && kind === 'rate_limited' && status === 429) {
|
|
303
|
+
return { exhausted: true, delay, evidence: false, ...blockUntil(false, 'rate_limited') };
|
|
304
|
+
}
|
|
305
|
+
let exhausted = status === 429 && delay > QUOTA_WINDOW_RETRY_AFTER_MS;
|
|
208
306
|
// Proof of a spent quota: a typed quota refusal or an exhausted usage window
|
|
209
307
|
// (a long Retry-After alone only rotates the roster, it proves nothing).
|
|
210
|
-
let evidence =
|
|
211
|
-
if (
|
|
308
|
+
let evidence = false;
|
|
309
|
+
if (status === 429) {
|
|
212
310
|
await fetchOAuthUsageSnapshot({ provider: providerName, accountId: row.id }, account(row.id), () => {}, {
|
|
213
311
|
force: true,
|
|
214
312
|
});
|
|
@@ -218,7 +316,7 @@ async function accountQuotaExhausted(providerName, row, model, error, account) {
|
|
|
218
316
|
evidence = providerAccountExhausted({ ...entry, blockedUntil: 0 }, Date.now(), model, options);
|
|
219
317
|
exhausted = exhausted || evidence || providerAccountExhausted(entry, Date.now(), model, options);
|
|
220
318
|
}
|
|
221
|
-
return { exhausted, delay, evidence };
|
|
319
|
+
return { exhausted, delay, evidence, ...(exhausted ? blockUntil(evidence, evidence ? 'quota' : 'unknown') : {}) };
|
|
222
320
|
}
|
|
223
321
|
|
|
224
322
|
// The pool's send: walks the eligible accounts until one answers, blocking
|
|
@@ -237,7 +335,9 @@ function poolSend(providerName, account) {
|
|
|
237
335
|
let refusalScoped = false;
|
|
238
336
|
// Mark `error` only when every account in `ids` (default: the roster) is
|
|
239
337
|
// exhausted with quota evidence: observed in this send, or a full window.
|
|
240
|
-
|
|
338
|
+
// Without an error of its own, the send reports quota exhaustion only on
|
|
339
|
+
// that evidence, and a rate-limit cooldown otherwise.
|
|
340
|
+
const exhaustion = (ids) => {
|
|
241
341
|
const pool = readProviderAccountPool(providerName);
|
|
242
342
|
// Manual policy: the eligible set is the selected account alone.
|
|
243
343
|
const scope = ids || (providerAccountPolicy(pool) === 'manual' ? [pool.selectedId] : null);
|
|
@@ -254,9 +354,47 @@ function poolSend(providerName, account) {
|
|
|
254
354
|
opts
|
|
255
355
|
)
|
|
256
356
|
);
|
|
257
|
-
return
|
|
357
|
+
return { rows, opts, now, all };
|
|
358
|
+
};
|
|
359
|
+
const markExhausted = (error, ids) => {
|
|
360
|
+
const { rows, opts, now, all } = exhaustion(ids);
|
|
361
|
+
if (all) {
|
|
362
|
+
return quotaExhausted(error || accountsExhaustedError(opts.useCredits), providerName, model, rows, opts);
|
|
363
|
+
}
|
|
364
|
+
return error || accountsCoolingDownError(rows, now);
|
|
365
|
+
};
|
|
366
|
+
// Accounts this send blocked on a rate-limit cooldown, the time it spent
|
|
367
|
+
// waiting for cooldowns, and the provider's wait bound.
|
|
368
|
+
const cooled = new Set();
|
|
369
|
+
let waitedMs = 0;
|
|
370
|
+
let sends = 0;
|
|
371
|
+
const waitBoundMs = (ids) => {
|
|
372
|
+
const id = ids?.[0] || readProviderAccountPool(providerName).selectedId || 'default';
|
|
373
|
+
return finiteMs(cooldownPolicyOf(account(id))?.waitForCooldownMs, 0);
|
|
374
|
+
};
|
|
375
|
+
// A cooldown-only eligible set whose earliest cooldown ends inside the
|
|
376
|
+
// remaining wait budget is waited out; otherwise the send fails as before.
|
|
377
|
+
const waitOrThrow = async (error, ids) => {
|
|
378
|
+
const { rows, now, all } = exhaustion(ids);
|
|
379
|
+
const wait = all ? null : earliestCooldownMs(rows, now);
|
|
380
|
+
if (wait == null || waitedMs + wait > waitBoundMs(ids)) throw markExhausted(error, ids);
|
|
381
|
+
options.onStageChange?.('reconnecting', {
|
|
382
|
+
message: `Rate limited — every account is cooling down; retrying in ${Math.ceil(wait / 1000)}s`,
|
|
383
|
+
reason: 'rate_limited',
|
|
384
|
+
waitMs: wait,
|
|
385
|
+
retryAt: now + wait,
|
|
386
|
+
});
|
|
387
|
+
await sleepWithAbort(wait, options.signal);
|
|
388
|
+
waitedMs += wait;
|
|
389
|
+
const lifted = Date.now();
|
|
390
|
+
for (const row of readProviderAccountPool(providerName).accounts) {
|
|
391
|
+
if (cooled.has(row.id) && !(Number(row.blockedUntil) > lifted)) {
|
|
392
|
+
cooled.delete(row.id);
|
|
393
|
+
attempted.delete(row.id);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
258
396
|
};
|
|
259
|
-
while (attempted.size < 20) {
|
|
397
|
+
while (attempted.size < 20 && sends < 20) {
|
|
260
398
|
options.signal?.throwIfAborted();
|
|
261
399
|
const pool = readProviderAccountPool(providerName);
|
|
262
400
|
if (!pool.accounts.length) {
|
|
@@ -281,12 +419,15 @@ function poolSend(providerName, account) {
|
|
|
281
419
|
probed = true;
|
|
282
420
|
if (await probeRefusedAccounts(providerName, pool, model, account)) continue;
|
|
283
421
|
}
|
|
284
|
-
|
|
422
|
+
await waitOrThrow(lastError);
|
|
423
|
+
continue;
|
|
285
424
|
}
|
|
286
425
|
attempted.add(row.id);
|
|
426
|
+
sends += 1;
|
|
287
427
|
const bound = boundAccountSend(providerName, row, messages, options);
|
|
288
428
|
try {
|
|
289
429
|
const result = await account(row.id).send(bound.history, model, tools, bound.opts);
|
|
430
|
+
quotaStrikes.delete(`${providerName}:${row.id}`);
|
|
290
431
|
// Headroom policy: a session's first pick, or its move off its account
|
|
291
432
|
// on a limit, becomes the selection; continuing on its account does
|
|
292
433
|
// not, so concurrent sessions never flip the selection back and forth.
|
|
@@ -330,24 +471,42 @@ function poolSend(providerName, account) {
|
|
|
330
471
|
options.onStageChange?.('reconnecting', { message: 'Sign-in expired — switching to the next account' });
|
|
331
472
|
continue;
|
|
332
473
|
}
|
|
333
|
-
const { exhausted,
|
|
334
|
-
if (options.signal?.aborted || !exhausted) throw error;
|
|
335
|
-
if (evidence) evidenced.add(row.id);
|
|
336
|
-
await blockProviderAccount(
|
|
474
|
+
const { exhausted, evidence, until, source, accountBlocked } = await accountQuotaExhausted(
|
|
337
475
|
providerName,
|
|
338
|
-
row
|
|
339
|
-
Date.now() + (delay > 0 ? delay : 5 * 60_000),
|
|
476
|
+
row,
|
|
340
477
|
model,
|
|
341
|
-
|
|
342
|
-
|
|
478
|
+
error,
|
|
479
|
+
account
|
|
343
480
|
);
|
|
344
|
-
if (
|
|
481
|
+
if (options.signal?.aborted || !exhausted) throw error;
|
|
482
|
+
if (evidence) evidenced.add(row.id);
|
|
483
|
+
else cooled.add(row.id);
|
|
484
|
+
await blockProviderAccount(providerName, row.id, until, model, source, { quota: evidence === true });
|
|
485
|
+
// An account block is unsafe to re-issue on THAT account only; with no
|
|
486
|
+
// output emitted, the next account may serve the same request.
|
|
487
|
+
if (
|
|
488
|
+
bound.emitted() ||
|
|
489
|
+
(error.unsafeToRetry && accountBlocked !== true) ||
|
|
490
|
+
error.liveTextEmitted ||
|
|
491
|
+
error.emittedToolCall
|
|
492
|
+
)
|
|
493
|
+
throw error;
|
|
345
494
|
// Manual policy: the eligible set is the selected account alone.
|
|
346
|
-
if (policy === 'manual')
|
|
495
|
+
if (policy === 'manual') {
|
|
496
|
+
await waitOrThrow(error, [row.id]);
|
|
497
|
+
lastError = error;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
347
500
|
lastError = error;
|
|
348
501
|
switched = true;
|
|
349
502
|
refusalScoped = accountExhaustedForModelOnly(providerName, row.id, model);
|
|
350
|
-
options.onStageChange?.('reconnecting', {
|
|
503
|
+
options.onStageChange?.('reconnecting', {
|
|
504
|
+
message: evidence
|
|
505
|
+
? 'Quota exhausted — switching to the next account'
|
|
506
|
+
: accountBlocked
|
|
507
|
+
? 'Account blocked — switching to the next account'
|
|
508
|
+
: 'Rate limited (cooldown) — switching to the next account',
|
|
509
|
+
});
|
|
351
510
|
}
|
|
352
511
|
}
|
|
353
512
|
throw markExhausted(lastError || new Error('Provider account attempts exhausted.'));
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { PROVIDER_NONSTREAM_TOTAL_TIMEOUT_MS, createTimeoutSignal } from '../stall-policy.mjs';
|
|
10
10
|
import { AnthropicFallbackTriggeredError, markProviderRecoveryExhausted } from './retry-classifier.mjs';
|
|
11
11
|
import { normalizeAnthropicNonStreamingResponse } from './lib/anthropic-request-utils.mjs';
|
|
12
|
+
import { capForNonStreaming } from './anthropic-max-tokens-recovery.mjs';
|
|
12
13
|
import { createAnthropicRecoveryGuards } from './anthropic-recovery-guards.mjs';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -44,7 +45,7 @@ export function createAnthropicApiRecovery({
|
|
|
44
45
|
try {
|
|
45
46
|
onStageChange?.('requesting', { transport: 'non-streaming-fallback' });
|
|
46
47
|
} catch {}
|
|
47
|
-
const nonStreamingParams = { ...params, stream: false };
|
|
48
|
+
const nonStreamingParams = capForNonStreaming({ ...params, stream: false });
|
|
48
49
|
const timeoutMs =
|
|
49
50
|
Number(opts._nonStreamingTimeoutMs) > 0
|
|
50
51
|
? Number(opts._nonStreamingTimeoutMs)
|
|
@@ -16,10 +16,12 @@ import { fastModeAvailable } from './anthropic-fast-mode.mjs';
|
|
|
16
16
|
import { applyAnthropicEffortToBody, shouldIncludeEffortBeta } from './anthropic-effort.mjs';
|
|
17
17
|
import { isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
|
|
18
18
|
import {
|
|
19
|
+
anthropicThinkingActive,
|
|
19
20
|
applyAnthropicCacheMarkers,
|
|
20
21
|
clampAnthropicThinkingBudget as clampThinkingBudgetTokens,
|
|
21
22
|
resolveAnthropicCacheTtls as resolveCacheTtls,
|
|
22
23
|
resolveAnthropicMessageCacheSlots,
|
|
24
|
+
sharedCachePrefixBoundary,
|
|
23
25
|
toAnthropicToolChoice,
|
|
24
26
|
} from './lib/anthropic-request-utils.mjs';
|
|
25
27
|
import {
|
|
@@ -54,17 +56,21 @@ export async function buildAnthropicApiRequest({
|
|
|
54
56
|
opts,
|
|
55
57
|
fastModeLatched,
|
|
56
58
|
modelInfo = null,
|
|
59
|
+
// The Claude id a gateway slug stands for (OpenRouter `anthropic/claude-opus-4.8`
|
|
60
|
+
// → `claude-opus-4.8`): the output cap and the thinking/effort shape are read
|
|
61
|
+
// from it, while the wire still names `useModel`.
|
|
62
|
+
capabilityModel = useModel,
|
|
63
|
+
// A gateway-supplied output limit for the model; wins over the Anthropic catalog.
|
|
64
|
+
modelMaxOutputTokens = opts?.modelMaxOutputTokens ?? null,
|
|
57
65
|
}) {
|
|
58
66
|
// A user-defined provider's reasoning comes from its own model row only:
|
|
59
67
|
// the global by-model-id capability index and the Claude-id ladders are
|
|
60
68
|
// first-party knowledge and must not leak onto (or from) a custom endpoint.
|
|
61
69
|
const isCustom = isCustomProviderName(name);
|
|
62
|
-
const maxTokens = resolveMaxTokens(
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
disableBetaHeaders: config?.disableBetaHeaders,
|
|
67
|
-
});
|
|
70
|
+
const maxTokens = resolveMaxTokens(capabilityModel, { modelMaxOutputTokens });
|
|
71
|
+
const effortOptions = { ...config, ...opts, disableBetaHeaders: config?.disableBetaHeaders };
|
|
72
|
+
const gatewayEffort = typeof config?.messagesEffort === 'function' ? config.messagesEffort(opts) : null;
|
|
73
|
+
const effortProjection = projectEffortConfiguration(messages, name, useModel, effortOptions);
|
|
68
74
|
const ttls = resolveCacheTtls(opts);
|
|
69
75
|
|
|
70
76
|
const systemMsgs = messages.filter((m) => m.role === 'system');
|
|
@@ -73,20 +79,29 @@ export async function buildAnthropicApiRequest({
|
|
|
73
79
|
// (cacheTier:'tier3') at ttls.tier3 — each its own system content block.
|
|
74
80
|
const systemBlocks = buildSystemBlocks(systemMsgs, ttls.system, ttls.tier3);
|
|
75
81
|
|
|
76
|
-
// Message-tail cache budget (4-BP layout + ANTHROPIC_MSG_SLOTS) is
|
|
77
|
-
// shared with anthropic-oauth — see resolveAnthropicMessageCacheSlots.
|
|
78
|
-
const messageCacheSlots = resolveAnthropicMessageCacheSlots(systemBlocks, ttls);
|
|
79
82
|
// Tools are resolved BEFORE the messages: the lowering needs the final
|
|
80
83
|
// tool list to drop tool_reference blocks whose tool no longer ships
|
|
81
84
|
// in this request (otherwise the API rejects the whole turn).
|
|
82
|
-
const requestTools = requestAnthropicTools(tools, chatMsgs, opts);
|
|
85
|
+
const requestTools = requestAnthropicTools(tools, chatMsgs, opts, name);
|
|
86
|
+
// Message-tail cache budget (4-BP layout + ANTHROPIC_MSG_SLOTS) is
|
|
87
|
+
// shared with anthropic-oauth — see resolveAnthropicMessageCacheSlots.
|
|
88
|
+
const messageCacheSlots = resolveAnthropicMessageCacheSlots(systemBlocks, ttls, { tools: requestTools });
|
|
83
89
|
// Build → sanitize (once, inside toAnthropicMessages) → mark. Markers
|
|
84
90
|
// are applied to the FINAL sanitized array by invariant, so block
|
|
85
91
|
// drops / inserts / reorders performed by the sanitizer can never move
|
|
86
92
|
// or delete a marked block. NEVER sanitize again after this.
|
|
93
|
+
const lowerHistory = (history, projection) =>
|
|
94
|
+
lowerAnthropicEffortHistory(
|
|
95
|
+
history.filter((m) => m.role !== 'system'),
|
|
96
|
+
(segment) => toAnthropicMessages(segment, requestTools, name),
|
|
97
|
+
projection
|
|
98
|
+
);
|
|
87
99
|
const anthropicMessages = applyAnthropicCacheMarkers(
|
|
88
|
-
|
|
89
|
-
messageCacheSlots
|
|
100
|
+
lowerHistory(chatMsgs, effortProjection),
|
|
101
|
+
messageCacheSlots,
|
|
102
|
+
sharedCachePrefixBoundary(messages, opts, (prefix) =>
|
|
103
|
+
lowerHistory(prefix, projectEffortConfiguration(prefix, name, useModel, effortOptions))
|
|
104
|
+
)
|
|
90
105
|
);
|
|
91
106
|
|
|
92
107
|
const params = {
|
|
@@ -108,10 +123,9 @@ export async function buildAnthropicApiRequest({
|
|
|
108
123
|
// tool_choice only when tools are actually present (Anthropic rejects
|
|
109
124
|
// tool_choice without tools). 'none' rides the hard-cap final turn to
|
|
110
125
|
// forbid tool USE while keeping the tools prefix stable for cache reuse.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
126
|
+
// The key is reserved here (wire key order) and resolved once thinking is
|
|
127
|
+
// known: forced any/tool is invalid while thinking is enabled.
|
|
128
|
+
if (params.tools) params.tool_choice = undefined;
|
|
115
129
|
const hasDeferredTools = Array.isArray(params.tools) && params.tools.some((tool) => tool?.defer_loading === true);
|
|
116
130
|
// Known tool names for the shared parseSSEStream leaked-tool-call guard
|
|
117
131
|
// (same guard fixes both Anthropic providers). Recovered leaked calls
|
|
@@ -129,16 +143,30 @@ export async function buildAnthropicApiRequest({
|
|
|
129
143
|
});
|
|
130
144
|
if (fields.thinking) params.thinking = fields.thinking;
|
|
131
145
|
if (fields.output_config) params.output_config = { ...(params.output_config || {}), ...fields.output_config };
|
|
132
|
-
} else {
|
|
146
|
+
} else if (gatewayEffort !== 'none') {
|
|
147
|
+
// A gateway's own reasoning control (`messagesEffort`) replaces the turn
|
|
148
|
+
// effort; 'none' sends no thinking at all.
|
|
149
|
+
const turnOpts = effortProjection ? { ...opts, effort: effortProjection.initialEffort } : opts;
|
|
133
150
|
applyAnthropicEffortToBody(params, {
|
|
134
|
-
model:
|
|
135
|
-
opts:
|
|
151
|
+
model: capabilityModel,
|
|
152
|
+
opts: gatewayEffort ? { ...turnOpts, effort: gatewayEffort } : turnOpts,
|
|
136
153
|
maxTokens,
|
|
137
154
|
clampThinkingBudgetTokens,
|
|
138
155
|
logTag: name,
|
|
139
156
|
progressUpdates: config?.disableBetaHeaders !== true,
|
|
140
157
|
});
|
|
141
158
|
}
|
|
159
|
+
if (params.tools) {
|
|
160
|
+
const toolChoice = toAnthropicToolChoice(opts.toolChoice, {
|
|
161
|
+
thinking: anthropicThinkingActive(params),
|
|
162
|
+
disableParallelToolUse: opts.disableParallelToolUse === true,
|
|
163
|
+
// A gateway that cannot honour disable_parallel_tool_use declares so.
|
|
164
|
+
supportsDisableParallelToolUse: config?.supportsDisableParallelToolUse !== false,
|
|
165
|
+
onThinkingConflict: opts.toolChoiceThinkingConflict,
|
|
166
|
+
});
|
|
167
|
+
if (toolChoice) params.tool_choice = toolChoice;
|
|
168
|
+
else delete params.tool_choice;
|
|
169
|
+
}
|
|
142
170
|
// Fast mode → speed: "fast" on models Anthropic marks as speed-capable.
|
|
143
171
|
// Suppressed while the fast capacity pool is cooling down.
|
|
144
172
|
let fastLatched = fastModeLatched;
|
|
@@ -169,7 +197,7 @@ export async function buildAnthropicApiRequest({
|
|
|
169
197
|
fastMode: fastLatched,
|
|
170
198
|
toolSearch: hasDeferredTools,
|
|
171
199
|
// Custom: the beta follows the resolver's output, not the model id.
|
|
172
|
-
effort: isCustom ? Boolean(params.output_config?.effort) : shouldIncludeEffortBeta(
|
|
200
|
+
effort: isCustom ? Boolean(params.output_config?.effort) : shouldIncludeEffortBeta(capabilityModel, opts),
|
|
173
201
|
serverFallback: params.fallbacks === 'default',
|
|
174
202
|
thinkingDisplayUpdates: params.thinking?.display === 'updates',
|
|
175
203
|
}),
|