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
|
@@ -7,17 +7,16 @@ import { noteErrorUsage } from './lib/note-error-usage.mjs';
|
|
|
7
7
|
import { retryReasonText, retryStatusLine } from '../../../shared/err-text.mjs';
|
|
8
8
|
import {
|
|
9
9
|
TERMINAL_EDGE_STATUSES,
|
|
10
|
-
headerValue,
|
|
11
10
|
boundedCauseChain,
|
|
12
11
|
classifyError,
|
|
13
|
-
isPermanentQuotaError,
|
|
14
12
|
isStaleKeepAliveError,
|
|
15
13
|
isToolInputCut,
|
|
16
14
|
retryAfterMsFromError,
|
|
17
15
|
} from './retry-classification.mjs';
|
|
16
|
+
import { DETERMINISTIC_KINDS, QUOTA_KINDS, errorHeaderValue, providerErrorKind } from './provider-error-kind.mjs';
|
|
17
|
+
import { adviceGatedRetry } from './retry-midstream.mjs';
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
AnthropicFallbackTriggeredError,
|
|
19
|
+
ModelFallbackTriggeredError,
|
|
21
20
|
DEFAULT_BACKOFF_MS,
|
|
22
21
|
DEFAULT_MAX_ATTEMPTS,
|
|
23
22
|
jitterDelayMs,
|
|
@@ -64,11 +63,16 @@ export function createStreamSafetyStamps() {
|
|
|
64
63
|
|
|
65
64
|
const providerRecoveryExhaustedErrors = new WeakSet();
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
// `terminal: true` records a provider VERDICT that recovery is over (a policy
|
|
67
|
+
// veto such as an unadvised advice-gated overload, or the repeated-overload
|
|
68
|
+
// stop), not merely a spent ladder: outer layers end recovery on it instead
|
|
69
|
+
// of opening another budget.
|
|
70
|
+
export function markProviderRecoveryExhausted(error, { owner = 'provider', attempts = null, terminal = false } = {}) {
|
|
68
71
|
if (!error || (typeof error !== 'object' && typeof error !== 'function')) return error;
|
|
69
72
|
providerRecoveryExhaustedErrors.add(error);
|
|
70
73
|
try {
|
|
71
74
|
error.providerRecoveryExhausted = true;
|
|
75
|
+
if (terminal) error.providerRecoveryTerminal = true;
|
|
72
76
|
if (owner) error.providerRecoveryOwner = String(owner);
|
|
73
77
|
const count = Number(attempts);
|
|
74
78
|
if (Number.isFinite(count) && count > 0) error.providerRecoveryAttempts = Math.floor(count);
|
|
@@ -85,18 +89,25 @@ export function isProviderRecoveryExhausted(error) {
|
|
|
85
89
|
);
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
/** True when a provider layer ruled that no further recovery may run (see markProviderRecoveryExhausted). */
|
|
93
|
+
export function isProviderRecoveryTerminal(error) {
|
|
94
|
+
if (!error || (typeof error !== 'object' && typeof error !== 'function')) return false;
|
|
95
|
+
return boundedCauseChain(error).some((candidate) => candidate?.providerRecoveryTerminal === true);
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
function throwIfRetryAborted(signal) {
|
|
89
99
|
if (!signal?.aborted) return;
|
|
90
100
|
const reason = signal.reason;
|
|
91
101
|
throw reason instanceof Error ? reason : new Error('withRetry: aborted');
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
104
|
+
// Before a retry: a stale keep-alive socket rebuilds the HTTP client to escape
|
|
105
|
+
// a poisoned keep-alive pool. A caller whose upstream poisons its HTTP/2 pool
|
|
106
|
+
// on server faults (`recycleDispatcherOn5xx`) also rebuilds it on a retried
|
|
107
|
+
// 5xx (edge 522/523/524 included). Never runs for a failure that is not retried.
|
|
108
|
+
function recycleDispatcherAfter(caught, status, opts) {
|
|
98
109
|
const poisoned =
|
|
99
|
-
isStaleKeepAliveError(caught) || (status >= 500 && status < 600
|
|
110
|
+
isStaleKeepAliveError(caught) || (opts.recycleDispatcherOn5xx === true && status >= 500 && status < 600);
|
|
100
111
|
if (!poisoned) return;
|
|
101
112
|
try {
|
|
102
113
|
recycleLlmDispatcher();
|
|
@@ -105,23 +116,71 @@ function recycleDispatcherAfter(caught, status) {
|
|
|
105
116
|
}
|
|
106
117
|
}
|
|
107
118
|
|
|
119
|
+
// Opt-in: without a fallback model, the request loop stops after
|
|
120
|
+
// `overloadFallbackAfter` overloads instead of spending the whole
|
|
121
|
+
// attempt budget. A provider verdict: no outer layer re-opens recovery.
|
|
122
|
+
function repeated529Error(caught, opts, maxAttempts) {
|
|
123
|
+
const error = new Error('Repeated 529 Overloaded errors', { cause: caught });
|
|
124
|
+
error.name = 'Repeated529Error';
|
|
125
|
+
error.repeated529 = true;
|
|
126
|
+
error.status = Number(caught?.status || caught?.httpStatus || 529) || 529;
|
|
127
|
+
error.httpStatus = error.status;
|
|
128
|
+
error.code = 'overloaded_error';
|
|
129
|
+
if (caught?.headers) error.headers = caught.headers;
|
|
130
|
+
return markProviderRecoveryExhausted(error, {
|
|
131
|
+
owner: opts.recoveryOwner || 'withRetry',
|
|
132
|
+
attempts: maxAttempts,
|
|
133
|
+
terminal: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Deterministic refusals of this request (auth, permission, policy, invalid
|
|
138
|
+
// request, context/size) end the loop before any status, header or overload
|
|
139
|
+
// rule may re-issue it. Quota kinds are judged separately below.
|
|
140
|
+
function deterministicRefusal(errorKind, kind, status, opts) {
|
|
141
|
+
if (!DETERMINISTIC_KINDS.has(errorKind) || QUOTA_KINDS.has(errorKind)) return false;
|
|
142
|
+
// Opt-in: a status-only 499 is retried (classifyError's retry499 verdict;
|
|
143
|
+
// a typed code on the 499 keeps it final).
|
|
144
|
+
return !(status === 499 && opts.retry499 === true && kind === 'transient');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The overload counter (`overloadFallbackAfter`) counts every overload of the
|
|
148
|
+
// send; intervening non-overload failures do not reset it.
|
|
149
|
+
function isOverloadFailure(status, errorKind) {
|
|
150
|
+
return status === 529 || errorKind === 'overloaded';
|
|
151
|
+
}
|
|
152
|
+
|
|
108
153
|
// Decides whether `caught` may be re-issued: returns the error to throw (the
|
|
109
154
|
// caught one, the exhausted-marked one on the last attempt, or the model
|
|
110
|
-
// fallback trigger) or null when the next attempt may run
|
|
111
|
-
|
|
155
|
+
// fallback trigger) or null when the next attempt may run — and only then
|
|
156
|
+
// rebuilds a poisoned HTTP pool for it.
|
|
157
|
+
function retryVeto(caught, context) {
|
|
158
|
+
const verdict = retryVerdict(caught, context);
|
|
159
|
+
if (verdict === null) {
|
|
160
|
+
const status = Number(caught?.httpStatus || caught?.status || caught?.response?.status || 0);
|
|
161
|
+
recycleDispatcherAfter(caught, status, context.opts);
|
|
162
|
+
}
|
|
163
|
+
return verdict;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function retryVerdict(caught, { opts, attempt, maxAttempts, state }) {
|
|
112
167
|
const lastAttempt = attempt === maxAttempts - 1;
|
|
168
|
+
// `finalOnExhaust`: the caller's ladder is the whole recovery, so a spent
|
|
169
|
+
// ladder is a provider-terminal verdict no outer layer re-opens.
|
|
113
170
|
const exhaustedError = () =>
|
|
114
|
-
maxAttempts > 1
|
|
171
|
+
maxAttempts > 1 || opts.finalOnExhaust === true
|
|
115
172
|
? markProviderRecoveryExhausted(caught, {
|
|
116
173
|
owner: opts.recoveryOwner || 'withRetry',
|
|
117
174
|
attempts: maxAttempts,
|
|
175
|
+
terminal: opts.finalOnExhaust === true,
|
|
118
176
|
})
|
|
119
177
|
: caught;
|
|
120
178
|
// A nested provider layer already spent its complete recovery budget.
|
|
121
179
|
// Never multiply that budget by replaying it from an outer withRetry.
|
|
122
180
|
if (isProviderRecoveryExhausted(caught)) return caught;
|
|
123
181
|
const status = Number(caught?.httpStatus || caught?.status || caught?.response?.status || 0);
|
|
124
|
-
const kind = classifyError(caught);
|
|
182
|
+
const kind = classifyError(caught, { retry499: opts.retry499 === true });
|
|
183
|
+
const errorKind = providerErrorKind(caught).kind;
|
|
125
184
|
// Hard replay boundary: a retry RE-ISSUES the request, so it is denied
|
|
126
185
|
// once visible output was relayed or a tool call was dispatched. Whether
|
|
127
186
|
// an eligible failure is actually retried remains the typed question
|
|
@@ -131,40 +190,67 @@ function retryVeto(caught, { opts, attempt, maxAttempts, state }) {
|
|
|
131
190
|
// split-call notice; re-issuing the identical request here would tend to be
|
|
132
191
|
// cut at the same place again.
|
|
133
192
|
if (isToolInputCut(caught)) return caught;
|
|
134
|
-
|
|
193
|
+
const overload = isOverloadFailure(status, errorKind);
|
|
194
|
+
// Deterministic refusals first: no status, header (x-should-retry:true
|
|
195
|
+
// included) or overload rule below may re-issue them.
|
|
196
|
+
if (deterministicRefusal(errorKind, kind, status, opts)) return caught;
|
|
197
|
+
// An origin-TLS edge failure (525/526) is final whatever header, advice or
|
|
198
|
+
// overload code accompanies it.
|
|
199
|
+
if (TERMINAL_EDGE_STATUSES.has(status)) return caught;
|
|
200
|
+
// An explicit quota / plan / billing refusal cannot recover by replaying
|
|
201
|
+
// the same request, whatever Retry-After the server attached. A 429 whose
|
|
202
|
+
// only quota evidence is header-derived (providerQuota / quotaExceeded) is
|
|
203
|
+
// still retried for non-subscriber callers (Enterprise/PAYG
|
|
204
|
+
// 429s); a typed quota/billing code never is.
|
|
205
|
+
if (QUOTA_KINDS.has(errorKind)) {
|
|
206
|
+
const typedQuota = QUOTA_KINDS.has(providerErrorKind(caught, { quotaFlags: false }).kind);
|
|
207
|
+
if (typedQuota || opts.retry429 === false || status !== 429) return caught;
|
|
208
|
+
}
|
|
135
209
|
// Background calls (titles and the like) never retry an overload: during a
|
|
136
210
|
// capacity cascade every retry multiplies gateway load, and nobody is
|
|
137
211
|
// waiting on the result.
|
|
138
212
|
if (status === 529 && opts.retry529 === false) return caught;
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// classification. Keep subscription OAuth 429 fail-fast ownership:
|
|
148
|
-
// retry429:false is the Max/Pro gate and must not wait for that window.
|
|
149
|
-
if (opts.provider === 'anthropic' && shouldRetryHeader === 'true' && !(status === 429 && opts.retry429 === false)) {
|
|
150
|
-
return lastAttempt ? exhaustedError() : null;
|
|
213
|
+
// A caller may opt into advice-gated overload / generic-429 retries. The
|
|
214
|
+
// denial is the provider's verdict: outer recovery layers honor it.
|
|
215
|
+
if (adviceGatedRetry(caught, opts) === false) {
|
|
216
|
+
return markProviderRecoveryExhausted(caught, {
|
|
217
|
+
owner: opts.recoveryOwner || 'withRetry',
|
|
218
|
+
attempts: attempt + 1,
|
|
219
|
+
terminal: true,
|
|
220
|
+
});
|
|
151
221
|
}
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
|
|
222
|
+
// Overload first: counted and retried ahead of the x-should-retry veto.
|
|
223
|
+
// The optional model fallback fires after `overloadFallbackAfter`
|
|
224
|
+
// overloads in the send (opt-in: providers pass fallbackModel only when
|
|
225
|
+
// the caller set one); without one, `stopAfterRepeated529` ends the loop
|
|
226
|
+
// there instead. The hard progress veto above runs first so neither can
|
|
227
|
+
// replay partial thinking/tool output. `overloadState` shares the count
|
|
228
|
+
// across a send.
|
|
229
|
+
if (overload) {
|
|
230
|
+
// Subscriber 429 fail-fast ownership also covers an overload-coded 429.
|
|
231
|
+
if (status === 429 && opts.retry429 === false) return caught;
|
|
157
232
|
state.consecutive529Errors += 1;
|
|
158
|
-
|
|
159
|
-
|
|
233
|
+
const limit = Number(opts.overloadFallbackAfter);
|
|
234
|
+
if (Number.isFinite(limit) && limit > 0 && state.consecutive529Errors >= limit) {
|
|
235
|
+
if (opts.fallbackModel && opts.fallbackModel !== opts.model) {
|
|
236
|
+
return new ModelFallbackTriggeredError(opts.model, opts.fallbackModel, caught);
|
|
237
|
+
}
|
|
238
|
+
if (opts.stopAfterRepeated529 === true) return repeated529Error(caught, opts, maxAttempts);
|
|
160
239
|
}
|
|
240
|
+
return lastAttempt ? exhaustedError() : null;
|
|
241
|
+
}
|
|
242
|
+
// x-should-retry:false is an explicit server veto on retrying and is
|
|
243
|
+
// honored as-is, ahead of status defaults including the 429 path.
|
|
244
|
+
const shouldRetryHeader = String(errorHeaderValue(caught, 'x-should-retry') || '').toLowerCase();
|
|
245
|
+
if (shouldRetryHeader === 'false') return caught;
|
|
246
|
+
// A transport that opts in (honorShouldRetryTrue) lets the non-standard
|
|
247
|
+
// positive override outrank ordinary status classification — except for
|
|
248
|
+
// subscribers (retry429:false), whose fail-fast ownership holds for every status.
|
|
249
|
+
if (opts.honorShouldRetryTrue === true && shouldRetryHeader === 'true' && opts.retry429 !== false) {
|
|
250
|
+
return lastAttempt ? exhaustedError() : null;
|
|
161
251
|
}
|
|
162
252
|
if (status === 429) {
|
|
163
253
|
if (opts.retry429 === false) return caught;
|
|
164
|
-
// A deterministic quota refusal cannot recover by replaying the same
|
|
165
|
-
// request. An explicit server retry window outranks message-text quota
|
|
166
|
-
// heuristics: RESOURCE_EXHAUSTED + Retry-After/RetryInfo is transient.
|
|
167
|
-
if (retryAfterMsFromError(caught) == null && isPermanentQuotaError(caught)) return caught;
|
|
168
254
|
// Retry only this request. Admission concurrency is fixed and is never
|
|
169
255
|
// reduced by rate limits. Output/tool stamps above remain a hard replay
|
|
170
256
|
// boundary.
|
|
@@ -184,8 +270,12 @@ function retryOptions(opts) {
|
|
|
184
270
|
perAttemptTimeoutMs: Number(opts.perAttemptTimeoutMs || 0),
|
|
185
271
|
perAttemptLabel: opts.perAttemptLabel || 'provider request',
|
|
186
272
|
retryJitterRatio: Number(opts.retryJitterRatio ?? PROVIDER_RETRY_JITTER_RATIO),
|
|
187
|
-
retryJitterMode:
|
|
273
|
+
retryJitterMode: ['positive', 'negative'].includes(opts.retryJitterMode) ? opts.retryJitterMode : 'symmetric',
|
|
188
274
|
sleepFn: typeof opts.sleepFn === 'function' ? opts.sleepFn : undefined,
|
|
275
|
+
// Opt-in: positive jitter on server advice, and advice as a
|
|
276
|
+
// floor under — never a replacement for — the current backoff step.
|
|
277
|
+
retryAfterJitterRatio: Math.max(0, Number(opts.retryAfterJitterRatio) || 0),
|
|
278
|
+
adviceAtLeastBackoff: opts.adviceAtLeastBackoff === true,
|
|
189
279
|
};
|
|
190
280
|
}
|
|
191
281
|
|
|
@@ -193,11 +283,17 @@ function retryOptions(opts) {
|
|
|
193
283
|
// the jittered backoff step. Retry-After is a server-mandated minimum. Do
|
|
194
284
|
// not cap, shorten, or jitter it; cancellation remains active throughout
|
|
195
285
|
// the full wait.
|
|
196
|
-
function retryWaitMs(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
286
|
+
function retryWaitMs(
|
|
287
|
+
attempt,
|
|
288
|
+
{ nextDelayMs, nextDelayReason },
|
|
289
|
+
{ backoffMs, retryJitterRatio, retryJitterMode, retryAfterJitterRatio = 0, adviceAtLeastBackoff = false }
|
|
290
|
+
) {
|
|
291
|
+
const step = backoffMs[Math.min(attempt, backoffMs.length - 1)] ?? 0;
|
|
292
|
+
if (nextDelayReason !== 'retry-after') return jitterDelayMs(nextDelayMs ?? step, retryJitterRatio, retryJitterMode);
|
|
293
|
+
let wait = Math.max(0, nextDelayMs ?? 0);
|
|
294
|
+
if (adviceAtLeastBackoff) wait = Math.max(wait, jitterDelayMs(step, retryJitterRatio, retryJitterMode));
|
|
295
|
+
// Positive-only jitter never shortens the advice.
|
|
296
|
+
return retryAfterJitterRatio > 0 ? jitterDelayMs(wait, retryAfterJitterRatio, 'positive') : wait;
|
|
201
297
|
}
|
|
202
298
|
|
|
203
299
|
/**
|
|
@@ -260,7 +356,14 @@ export async function withRetry(fn, opts = {}) {
|
|
|
260
356
|
let lastErr = null;
|
|
261
357
|
let nextDelayMs = null;
|
|
262
358
|
let nextDelayReason = null;
|
|
263
|
-
|
|
359
|
+
// `opts.overloadState` ({ consecutive529Errors }) lets one logical send share
|
|
360
|
+
// its overload count across every withRetry it runs (streaming, recovery
|
|
361
|
+
// cycles, non-streaming fallback), so the configured fallback still fires.
|
|
362
|
+
const shared = opts.overloadState && typeof opts.overloadState === 'object' ? opts.overloadState : null;
|
|
363
|
+
const state = shared || {};
|
|
364
|
+
if (!Number.isFinite(state.consecutive529Errors)) {
|
|
365
|
+
state.consecutive529Errors = Math.max(0, Number(opts.initialConsecutive529Errors) || 0);
|
|
366
|
+
}
|
|
264
367
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
265
368
|
throwIfRetryAborted(signal);
|
|
266
369
|
if (attempt > 0) {
|
|
@@ -11,6 +11,46 @@ import {
|
|
|
11
11
|
isTransientErrorCode,
|
|
12
12
|
typedStatusFrom,
|
|
13
13
|
} from './retry-classification.mjs';
|
|
14
|
+
import { DETERMINISTIC_KINDS, providerErrorKind } from './provider-error-kind.mjs';
|
|
15
|
+
|
|
16
|
+
// A 429 that is an overload or an ordinary rate limit is request-local: it is
|
|
17
|
+
// re-issued under the bounded mid-stream budget (the server's advice, when it
|
|
18
|
+
// sent one, sets the wait). Quota / plan / billing refusals never are.
|
|
19
|
+
// The advice-honoring mid-stream wait lives with the backoff schedule.
|
|
20
|
+
export { midstreamRetryDelayMs } from './retry-backoff.mjs';
|
|
21
|
+
|
|
22
|
+
// Per-provider parity, as policy options (default false = retry under the
|
|
23
|
+
// bounded budget with ordinary backoff):
|
|
24
|
+
// overloadRetryRequiresAdvice — an overload (server_is_overloaded,
|
|
25
|
+
// overloaded_error, 529, statusless overload event) retries ONLY when the
|
|
26
|
+
// server gave advice (Retry-After / retryAfterMs / RetryInfo).
|
|
27
|
+
// generic429RetryRequiresAdvice — a rate limit without a typed
|
|
28
|
+
// rate_limit_exceeded / slow_down code retries ONLY on advice.
|
|
29
|
+
// A typed rate_limit_exceeded / slow_down always retries (advice, else the
|
|
30
|
+
// delay its message names, else local backoff). Returns false when the
|
|
31
|
+
// policy denies the retry, null when it has no say.
|
|
32
|
+
const TYPED_RATE_LIMIT_CODES = new Set(['rate_limit_exceeded', 'slow_down']);
|
|
33
|
+
export function adviceGatedRetry(err, policy = {}) {
|
|
34
|
+
const { kind, code, retryAfterMs } = providerErrorKind(err);
|
|
35
|
+
const advised = retryAfterMs != null;
|
|
36
|
+
if (kind === 'overloaded' && policy.overloadRetryRequiresAdvice === true && !advised) return false;
|
|
37
|
+
if (
|
|
38
|
+
kind === 'rate_limited' &&
|
|
39
|
+
!TYPED_RATE_LIMIT_CODES.has(code) &&
|
|
40
|
+
policy.generic429RetryRequiresAdvice === true &&
|
|
41
|
+
!advised
|
|
42
|
+
) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function retryable429(err, policy) {
|
|
49
|
+
if (policy.retry429 === false) return false;
|
|
50
|
+
if (adviceGatedRetry(err, policy) === false) return false;
|
|
51
|
+
const { kind } = providerErrorKind(err);
|
|
52
|
+
return kind === 'overloaded' || kind === 'rate_limited';
|
|
53
|
+
}
|
|
14
54
|
|
|
15
55
|
// F) Retry-budget profiles as DATA. The numbers live ONLY here now.
|
|
16
56
|
// ws.*Retries (5) — one Responses stream retry budget.
|
|
@@ -88,19 +128,24 @@ function isWebSocketConnectionLimit(err) {
|
|
|
88
128
|
// bucket. Buckets need typed codes; the DEFAULT for an unrecognized (or
|
|
89
129
|
// absent) code is the bounded retry, with fatal refusal codes staying
|
|
90
130
|
// terminal.
|
|
91
|
-
function classifyResponseFailed(failed, attemptIndex, policy) {
|
|
131
|
+
function classifyResponseFailed(failed, attemptIndex, policy, err = null) {
|
|
92
132
|
const detail = failed?.response?.error || failed?.error || failed;
|
|
93
133
|
const failedStatus = typedStatusFrom(detail, failed?.response, failed);
|
|
134
|
+
// Deterministic codes outrank a numeric 5xx on the same payload.
|
|
135
|
+
const keys = [detail?.code, detail?.type]
|
|
136
|
+
.map((field) => (typeof field === 'string' ? field.trim().toLowerCase() : ''))
|
|
137
|
+
.filter(Boolean);
|
|
138
|
+
const codeClassifier = keys.map((key) => RESPONSE_FAILED_CODE_CLASSIFIERS.get(key)).find(Boolean) || null;
|
|
139
|
+
if (!codeClassifier && keys.some((key) => WIRE_ERROR_FATAL_CODES.has(key))) return null;
|
|
140
|
+
const subject = { responseFailed: failed, headers: err?.headers, retryAfterMs: err?.retryAfterMs };
|
|
141
|
+
if (DETERMINISTIC_KINDS.has(providerErrorKind(subject).kind)) return null;
|
|
142
|
+
// Statusless overload / generic rate-limit events follow the advice gate.
|
|
143
|
+
if (adviceGatedRetry(subject, policy) === false) return null;
|
|
94
144
|
if (failedStatus >= 500 && failedStatus < 600) {
|
|
95
145
|
if (TERMINAL_EDGE_STATUSES.has(failedStatus)) return null;
|
|
96
146
|
return _allowMidstream(`http_${failedStatus}`, attemptIndex, policy);
|
|
97
147
|
}
|
|
98
|
-
|
|
99
|
-
const key = typeof field === 'string' ? field.trim().toLowerCase() : '';
|
|
100
|
-
const classifier = RESPONSE_FAILED_CODE_CLASSIFIERS.get(key);
|
|
101
|
-
if (classifier) return _allowMidstream(classifier, attemptIndex, policy);
|
|
102
|
-
if (key && WIRE_ERROR_FATAL_CODES.has(key)) return null;
|
|
103
|
-
}
|
|
148
|
+
if (codeClassifier) return _allowMidstream(codeClassifier, attemptIndex, policy);
|
|
104
149
|
// A typed non-transient 4xx on the failure payload is a deterministic
|
|
105
150
|
// refusal even without a recognized code string.
|
|
106
151
|
if (failedStatus >= 400 && failedStatus < 500 && !TRANSIENT_STATUSES.has(failedStatus)) return null;
|
|
@@ -146,12 +191,19 @@ function _classifyMidstreamWs(err, state, attemptIndex, policy) {
|
|
|
146
191
|
// NOMINAL closes count too: a pooled socket the server retires between
|
|
147
192
|
// turns closes with 1000/1001/1005 and must reconnect fresh — treating
|
|
148
193
|
// that as terminal killed the turn instead of reissuing it.
|
|
149
|
-
|
|
194
|
+
// An EXPLICIT wire failure (response.failed / error event) before
|
|
195
|
+
// response.created exposed nothing either: it gets the same bounded WS
|
|
196
|
+
// retry budget, and its typed code decides below.
|
|
197
|
+
const failed = err?.responseFailed || state.responseFailedPayload;
|
|
198
|
+
if (!state.sawResponseCreated && !NON_TERMINAL_STREAM_CLOSE_CODES.has(closeCode) && !failed) return null;
|
|
150
199
|
if (state.userAbort) return null;
|
|
151
200
|
|
|
152
201
|
if (!err) return null;
|
|
153
202
|
const status = Number(err?.httpStatus || 0);
|
|
154
|
-
if (status === 401 || status === 403
|
|
203
|
+
if (status === 401 || status === 403) return null;
|
|
204
|
+
if (DETERMINISTIC_KINDS.has(providerErrorKind(err).kind)) return null;
|
|
205
|
+
if (status === 429) return retryable429(err, policy) ? _allowMidstream('http_429', attemptIndex, policy) : null;
|
|
206
|
+
if (status && adviceGatedRetry(err, policy) === false) return null;
|
|
155
207
|
if (status >= 500 && status < 600) {
|
|
156
208
|
if (TERMINAL_EDGE_STATUSES.has(status)) return null;
|
|
157
209
|
return _allowMidstream(`http_${status}`, attemptIndex, policy);
|
|
@@ -167,8 +219,7 @@ function _classifyMidstreamWs(err, state, attemptIndex, policy) {
|
|
|
167
219
|
return _allowMidstream(`ws_${closeCode}`, attemptIndex, policy);
|
|
168
220
|
}
|
|
169
221
|
|
|
170
|
-
|
|
171
|
-
if (failed) return classifyResponseFailed(failed, attemptIndex, policy);
|
|
222
|
+
if (failed) return classifyResponseFailed(failed, attemptIndex, policy, err);
|
|
172
223
|
return null;
|
|
173
224
|
}
|
|
174
225
|
|
|
@@ -181,8 +232,8 @@ const RESPONSE_FAILED_CODE_CLASSIFIERS = new Map([
|
|
|
181
232
|
['auth_expired', 'response_failed_auth_expired'],
|
|
182
233
|
['previous_response_not_found', 'previous_response_not_found'],
|
|
183
234
|
['websocket_connection_limit_reached', 'websocket_connection_limit'],
|
|
184
|
-
// A rate limit is retried after the server-advised wait
|
|
185
|
-
//
|
|
235
|
+
// A rate limit is retried after the server-advised wait; a usage limit
|
|
236
|
+
// stays a fatal code.
|
|
186
237
|
['rate_limit_exceeded', 'response_failed_rate_limited'],
|
|
187
238
|
]);
|
|
188
239
|
|
|
@@ -197,7 +248,10 @@ function _classifyMidstreamSse(err, state, attemptIndex, policy) {
|
|
|
197
248
|
if (!err) return null;
|
|
198
249
|
const status = Number(err?.httpStatus || err?.status || err?.response?.status || 0);
|
|
199
250
|
if (status === 401 || status === 403) return null;
|
|
200
|
-
if (
|
|
251
|
+
if (DETERMINISTIC_KINDS.has(providerErrorKind(err).kind)) return null;
|
|
252
|
+
if (status === 429) return retryable429(err, policy) ? 'http_429' : null;
|
|
253
|
+
// Opt-in: overloads retry only on server advice.
|
|
254
|
+
if (adviceGatedRetry(err, policy) === false) return null;
|
|
201
255
|
if (status >= 500 && status < 600) {
|
|
202
256
|
if (TERMINAL_EDGE_STATUSES.has(status)) return null;
|
|
203
257
|
return `http_${status}`;
|
|
@@ -322,13 +376,24 @@ export function shouldFallbackTransport(err, { signal, enabled = true } = {}) {
|
|
|
322
376
|
// returns null. HTTP 401 is reserved for auth recovery and 426 for immediate
|
|
323
377
|
// HTTPS fallback. The OpenAI OAuth caller opts out of 429 retries
|
|
324
378
|
// (retry429:false); all other callers retain the historical retryable
|
|
325
|
-
// UnexpectedStatus policy.
|
|
326
|
-
|
|
379
|
+
// UnexpectedStatus policy. overloadRetryRequiresAdvice /
|
|
380
|
+
// generic429RetryRequiresAdvice (default false) opt a caller into
|
|
381
|
+
// advice-gated overload / generic-429 retries.
|
|
382
|
+
export function classifyHandshakeError(
|
|
383
|
+
err,
|
|
384
|
+
{ retry429 = true, overloadRetryRequiresAdvice = false, generic429RetryRequiresAdvice = false } = {}
|
|
385
|
+
) {
|
|
327
386
|
if (!err) return null;
|
|
328
387
|
const code = err.code || '';
|
|
329
388
|
const status = Number(err.httpStatus || 0);
|
|
330
389
|
|
|
331
390
|
if (status === 401 || status === 426 || (status === 429 && !retry429)) return null;
|
|
391
|
+
// Every deterministic refusal (quota, billing, auth, permission, policy,
|
|
392
|
+
// invalid request, context/size) is final, whatever 5xx or advice it carries.
|
|
393
|
+
if (DETERMINISTIC_KINDS.has(providerErrorKind(err).kind)) return null;
|
|
394
|
+
if (status && adviceGatedRetry(err, { overloadRetryRequiresAdvice, generic429RetryRequiresAdvice }) === false) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
332
397
|
if (status > 0) {
|
|
333
398
|
// Typed transient handshake statuses only. A 403/404/4xx upgrade refusal
|
|
334
399
|
// is a deterministic decision: spending the retry budget on it cannot
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const TRANSPORT_SCOPE_DISPOSED: 'transport_scope_disposed';
|
|
2
|
+
export function forkTransportScope(sessionId: string, label?: string): string;
|
|
3
|
+
export function disposeTransportScope(
|
|
4
|
+
scope: string,
|
|
5
|
+
provider?: { releaseTransportScope?(scope: string): void } | null
|
|
6
|
+
): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// transport-scope.mjs — a send's transport state scope, apart from its
|
|
2
|
+
// session identity. Providers key their session-scoped transport and
|
|
3
|
+
// continuation state (pooled sockets and their previous_response_id anchor,
|
|
4
|
+
// Codex turn-state routing tokens, sticky HTTP fallback, Cursor runs and
|
|
5
|
+
// conversations) by `opts.transportScope || opts.sessionId`, while prompt
|
|
6
|
+
// cache routing (prompt_cache_key, handshake session id) stays on
|
|
7
|
+
// `opts.sessionId`. A request forked from a session (/btw) runs in a scope of
|
|
8
|
+
// its own: it shares the session's prompt cache but reads and writes none of
|
|
9
|
+
// its transport state, and its scope is disposed once it is answered.
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
|
|
12
|
+
/** The reason a scope's provider connections are closed when it is disposed. */
|
|
13
|
+
export const TRANSPORT_SCOPE_DISPOSED = 'transport_scope_disposed';
|
|
14
|
+
|
|
15
|
+
/** A fresh transport scope for one request forked from `sessionId`. */
|
|
16
|
+
export function forkTransportScope(sessionId, label = 'fork') {
|
|
17
|
+
return `${sessionId}#${label}:${randomUUID()}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Close every connection and drop every transport record of `scope`. */
|
|
21
|
+
export function disposeTransportScope(scope, provider) {
|
|
22
|
+
if (!scope) return;
|
|
23
|
+
try {
|
|
24
|
+
globalThis.__mixdogCloseProviderConnectionsForSession?.(scope, TRANSPORT_SCOPE_DISPOSED);
|
|
25
|
+
} finally {
|
|
26
|
+
provider?.releaseTransportScope?.(scope);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wire-gateway.mjs — an API-key gateway that serves one account on several
|
|
3
|
+
* wires (OpenRouter, DeepSeek). Chat Completions and Responses go through the
|
|
4
|
+
* OpenAI-compatible engine, Anthropic Messages through an Anthropic engine
|
|
5
|
+
* bound to the gateway; wire-route.mjs picks the wire per model.
|
|
6
|
+
*/
|
|
7
|
+
import { AnthropicProvider } from './anthropic.mjs';
|
|
8
|
+
import { OpenAICompatProvider, OPENAI_COMPAT_PRESETS } from './openai-compat.mjs';
|
|
9
|
+
import { originBoundFetch } from './lib/origin-bound-fetch.mjs';
|
|
10
|
+
import { withInclusiveUsage } from './lib/inclusive-usage.mjs';
|
|
11
|
+
import { getModelMetadataSync } from './model-catalog.mjs';
|
|
12
|
+
import { resolveWireRoute, sendOnRoute } from './wire-route.mjs';
|
|
13
|
+
|
|
14
|
+
// A Files API file-ID image anywhere in the history (tool results nest content).
|
|
15
|
+
function hasFileIdImage(value, depth = 0) {
|
|
16
|
+
if (!value || typeof value !== 'object' || depth > 6) return false;
|
|
17
|
+
if (Array.isArray(value)) return value.some((entry) => hasFileIdImage(entry, depth + 1));
|
|
18
|
+
const fileId = value.type === 'input_image' ? value.file_id : value.type === 'image' && value.source?.type === 'file' ? value.source.file_id : null;
|
|
19
|
+
if (typeof fileId === 'string' && fileId) return true;
|
|
20
|
+
return hasFileIdImage(value.content, depth + 1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The per-request capability options a gateway's Messages engine hands the
|
|
25
|
+
* shared Anthropic request builder (anthropic-api-request.mjs):
|
|
26
|
+
* - `modelMaxOutputTokens`: the gateway's own output ceiling for the model
|
|
27
|
+
* (`config.modelMaxOutputTokens(model)`),
|
|
28
|
+
* - `messagesEffortProjection`: the gateway's own effort projection
|
|
29
|
+
* (`config.messagesEffortProjection`), replacing the Claude rules,
|
|
30
|
+
* - `anthropic-beta` (`config.fileIdImageBeta`) only on a request that
|
|
31
|
+
* carries a file-ID image; every other beta stays suppressed.
|
|
32
|
+
*/
|
|
33
|
+
export function gatewayMessagesOpts(config, model, messages, opts) {
|
|
34
|
+
const out = { ...(opts || {}) };
|
|
35
|
+
const ceiling = Math.floor(Number(typeof config?.modelMaxOutputTokens === 'function' ? config.modelMaxOutputTokens(model) : 0));
|
|
36
|
+
if (ceiling > 0 && out.modelMaxOutputTokens === undefined) out.modelMaxOutputTokens = ceiling;
|
|
37
|
+
if (typeof config?.messagesEffortProjection === 'function') out.messagesEffortProjection = config.messagesEffortProjection;
|
|
38
|
+
if (config?.fileIdImageBeta && hasFileIdImage(messages)) {
|
|
39
|
+
out.requestHeaders = { 'anthropic-beta': config.fileIdImageBeta, ...(out.requestHeaders || {}) };
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Anthropic engine bound to a gateway: only the gateway key is sent — never
|
|
46
|
+
* an ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN from the environment — and only
|
|
47
|
+
* to the gateway origin. `config.authScheme: 'bearer'` sends the key as
|
|
48
|
+
* `Authorization: Bearer` (OpenRouter); the default is `x-api-key`. The client
|
|
49
|
+
* is rebuilt through here on every key change or reload.
|
|
50
|
+
* `config.capabilityModel(model)` maps the routing slug to the Claude id its
|
|
51
|
+
* capabilities are read from.
|
|
52
|
+
*/
|
|
53
|
+
export class GatewayMessagesEngine extends AnthropicProvider {
|
|
54
|
+
// Document capability of this gateway's Messages route, declared by the
|
|
55
|
+
// gateway config. `nativePdf`: PDFs ride as document blocks (default: lowered
|
|
56
|
+
// to text). `nativeDocuments`: any document block at all; unset leaves
|
|
57
|
+
// non-PDF documents as they are, an explicit false (DeepSeek) means every
|
|
58
|
+
// document variant is lowered to text or rejected.
|
|
59
|
+
nativePdf = this.config.nativePdf === true;
|
|
60
|
+
nativeDocuments = this.config.nativeDocuments;
|
|
61
|
+
_sendOnce(messages, model, tools, opts) {
|
|
62
|
+
return super._sendOnce(messages, model, tools, gatewayMessagesOpts(this.config, model, messages, opts));
|
|
63
|
+
}
|
|
64
|
+
_createClient(apiKey) {
|
|
65
|
+
const client = super._createClient(apiKey);
|
|
66
|
+
if (this.config.authScheme === 'bearer') {
|
|
67
|
+
client.apiKey = null;
|
|
68
|
+
client.authToken = apiKey || null;
|
|
69
|
+
} else {
|
|
70
|
+
client.authToken = null;
|
|
71
|
+
}
|
|
72
|
+
client.fetch = originBoundFetch(new URL(this.config.baseURL).origin, this.config.fetchImpl, this.name);
|
|
73
|
+
return client;
|
|
74
|
+
}
|
|
75
|
+
_capabilityModel(model) {
|
|
76
|
+
return (typeof this.config.capabilityModel === 'function' && this.config.capabilityModel(model)) || model;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class WireGatewayProvider {
|
|
81
|
+
// No document block on any gateway wire: a PDF is lowered to text.
|
|
82
|
+
nativePdf = false;
|
|
83
|
+
// Every wire reports inclusive input (Messages usage is converted).
|
|
84
|
+
static inputExcludesCache = false;
|
|
85
|
+
name;
|
|
86
|
+
config;
|
|
87
|
+
openai;
|
|
88
|
+
anthropic;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {string} name preset name (openai-compat-presets.mjs)
|
|
92
|
+
* @param {object} config provider config
|
|
93
|
+
* @param {object} messages the Messages engine: `{ baseURL, authScheme?, capabilityModel? }`
|
|
94
|
+
* @param {object} [compatConfig] extra OpenAI-compatible engine config
|
|
95
|
+
*/
|
|
96
|
+
constructor(name, config = {}, messages, compatConfig = {}) {
|
|
97
|
+
this.name = name;
|
|
98
|
+
this.config = config;
|
|
99
|
+
this.openai = new OpenAICompatProvider(name, { ...config, ...compatConfig });
|
|
100
|
+
this.anthropic = new GatewayMessagesEngine({
|
|
101
|
+
...config,
|
|
102
|
+
...messages,
|
|
103
|
+
name,
|
|
104
|
+
disableBetaHeaders: true,
|
|
105
|
+
modelMaxOutputTokens: messages.modelMaxOutputTokens ?? ((model) => this._modelMaxOutputTokens(model)),
|
|
106
|
+
extraHeaders: { ...(OPENAI_COMPAT_PRESETS[name]?.extraHeaders || {}), ...(config.extraHeaders || {}) },
|
|
107
|
+
});
|
|
108
|
+
// The Chat engine's retry profile stays the account's loop policy.
|
|
109
|
+
if (this.openai.retryPolicy) this.retryPolicy = this.openai.retryPolicy;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
resolveWireRoute(model, ctx = {}) {
|
|
113
|
+
return resolveWireRoute(this.name, model, { ...ctx, override: this.config?.modelWires?.[model] ?? null });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// The model's output ceiling: the gateway roster, else the shared catalog.
|
|
117
|
+
_modelMaxOutputTokens(model) {
|
|
118
|
+
const declared = Number(this.getCachedModelInfo(model)?.outputTokens ?? getModelMetadataSync(model, this.name)?.outputTokens);
|
|
119
|
+
return Number.isFinite(declared) && declared > 0 ? declared : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
send(messages, model, tools, sendOpts) {
|
|
123
|
+
const useModel = model || this.openai.defaultModel;
|
|
124
|
+
// The resolved route is the only wire authority: a caller's own wire
|
|
125
|
+
// switches never reach an engine, and one clear "unsupported" refusal
|
|
126
|
+
// moves the send to the next wire once (sendOnRoute).
|
|
127
|
+
return sendOnRoute(this, useModel, tools, sendOpts, messages, (wire, history, opts) => {
|
|
128
|
+
if (wire === 'messages') return withInclusiveUsage(() => this.anthropic.send(history, useModel, tools, opts));
|
|
129
|
+
if (wire === 'responses') return this.openai.send(history, model, tools, { ...opts, compatWireApi: 'responses' });
|
|
130
|
+
return this.openai.send(history, model, tools, opts);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
reloadApiKey() {
|
|
135
|
+
this.openai.reloadApiKey();
|
|
136
|
+
this.anthropic.reloadApiKey();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async listModels() {
|
|
140
|
+
return this.openai.listModels();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async isAvailable() {
|
|
144
|
+
return this.openai.isAvailable();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getCachedModelInfo(model) {
|
|
148
|
+
return this.openai.getCachedModelInfo(model);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** The Anthropic SDK base for a gateway's OpenAI base: the SDK appends `/v1/messages`. */
|
|
153
|
+
export function messagesBaseFromOpenAIBase(baseURL) {
|
|
154
|
+
return String(baseURL || '')
|
|
155
|
+
.replace(/\/+$/, '')
|
|
156
|
+
.replace(/\/v1$/i, '');
|
|
157
|
+
}
|