mixdog 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/package.json +1 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +13 -3
- package/src/defaults/skills/visualize/SKILL.md +12 -9
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config.mjs +10 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +122 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
- package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +447 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +41 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +34 -24
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/atomic-file.mjs +25 -4
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/err-text.mjs +25 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -52
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +124 -20
- package/src/runtime/shared/provider-api-key.mjs +12 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/turn-changes.mjs +447 -219
- package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
- package/src/runtime/shared/turn-snapshot.mjs +170 -48
- package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/contract.mjs +0 -24
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +61 -0
- package/src/session-runtime/boot/config.mjs +5 -2
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +35 -18
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/connect.mjs +83 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
- package/src/session-runtime/mcp-glue.mjs +25 -2
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +83 -15
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +9 -2
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +24 -5
- package/src/session-runtime/resource-mcp-api.mjs +198 -78
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/runtime-review-api.mjs +13 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +12 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-stored-session-views.mjs +11 -3
- package/src/standalone/daemon-telemetry.mjs +55 -0
- package/src/standalone/daemon.mjs +44 -11
- package/src/standalone/session-protocol.mjs +12 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
- package/src/standalone/session-service-api.mjs +5 -0
- package/src/standalone/session-service.mjs +1 -0
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +7 -0
- package/src/tui/app/create-app-pickers.mjs +4 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +13 -16
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-commands.mjs +7 -2
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +1323 -214
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +33 -24
- package/src/tui/session/session-api/extensions.mjs +26 -2
- package/src/tui/session/session-api/integrations.mjs +7 -0
- package/src/tui/session/session-api/lifecycle.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +12 -14
- package/src/tui/session/session-flow/drain.mjs +21 -5
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn.mjs +55 -15
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { noteErrorUsage } from './lib/note-error-usage.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
|
|
4
4
|
import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
|
|
5
5
|
import { appendAgentTrace } from '../agent-trace.mjs';
|
|
6
6
|
import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
|
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
_shouldFallbackXaiWsToHttp,
|
|
14
14
|
} from './openai-compat-xai.mjs';
|
|
15
15
|
import { withEffortRejectionFallback } from './effort-rejection.mjs';
|
|
16
|
+
import { applyCompatErrorContract } from './openai-compat-error-contract.mjs';
|
|
16
17
|
import { sendCompatResponses } from './openai-compat-responses.mjs';
|
|
18
|
+
import { COMPAT_RETRY_POLICY, COMPAT_RETRY_PROFILES, XAI_COMPAT_RETRY_POLICY } from './openai-compat-retry-profile.mjs';
|
|
17
19
|
import { sendCompatChat, recoverCompatNonStreaming } from './openai-compat-chat-send.mjs';
|
|
18
20
|
import { sendXaiResponses, sendXaiResponsesWebSocket } from './openai-compat-xai-send.mjs';
|
|
19
21
|
import {
|
|
@@ -69,8 +71,10 @@ export class OpenAICompatProvider {
|
|
|
69
71
|
constructor(name, config) {
|
|
70
72
|
const preset = PRESETS[name];
|
|
71
73
|
const baseURL = assertSafeBaseURL(config.baseURL || preset?.baseURL || 'http://localhost:8080/v1', name);
|
|
72
|
-
const apiKey = config
|
|
74
|
+
const apiKey = resolveProviderApiKey(name, config) || 'no-key';
|
|
73
75
|
this.name = name;
|
|
76
|
+
// A profiled provider's limits are final: no loop-level recovery cycles on top.
|
|
77
|
+
if (COMPAT_RETRY_PROFILES[name]) this.retryPolicy = name === 'xai' ? XAI_COMPAT_RETRY_POLICY : COMPAT_RETRY_POLICY;
|
|
74
78
|
this.config = config;
|
|
75
79
|
this.baseURL = baseURL;
|
|
76
80
|
this.apiKey = apiKey;
|
|
@@ -121,7 +125,7 @@ export class OpenAICompatProvider {
|
|
|
121
125
|
reloadApiKey() {
|
|
122
126
|
try {
|
|
123
127
|
const preset = PRESETS[this.name];
|
|
124
|
-
const newKey =
|
|
128
|
+
const newKey = reloadProviderApiKey(this.name, this.config);
|
|
125
129
|
const baseURL = assertSafeBaseURL(
|
|
126
130
|
this.config.baseURL || preset?.baseURL || 'http://localhost:8080/v1',
|
|
127
131
|
this.name
|
|
@@ -138,6 +142,12 @@ export class OpenAICompatProvider {
|
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
async send(messages, model, tools, sendOpts) {
|
|
145
|
+
// A keyless-constructed instance picks up the key once it is available.
|
|
146
|
+
const resolvedKey = resolveProviderApiKey(this.name, this.config) || 'no-key';
|
|
147
|
+
if (resolvedKey !== this.apiKey) {
|
|
148
|
+
this.apiKey = resolvedKey;
|
|
149
|
+
this.client = this._createClient(resolvedKey);
|
|
150
|
+
}
|
|
141
151
|
try {
|
|
142
152
|
return await this._doSend(messages, model, tools, sendOpts);
|
|
143
153
|
} catch (err) {
|
|
@@ -162,9 +172,19 @@ export class OpenAICompatProvider {
|
|
|
162
172
|
}
|
|
163
173
|
async _doSend(messages, model, tools, sendOpts) {
|
|
164
174
|
const useModel = model || this.defaultModel;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
try {
|
|
176
|
+
return await withEffortRejectionFallback(
|
|
177
|
+
this.name,
|
|
178
|
+
useModel,
|
|
179
|
+
sendOpts || {},
|
|
180
|
+
this.config?.reasoningEffort,
|
|
181
|
+
(opts) => this._sendWithOpts(messages, useModel, tools, opts)
|
|
182
|
+
);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
// Every provider built on this transport (custom, grok, muse, mimo,
|
|
185
|
+
// command-code, local) leaves through here with the boundary contract.
|
|
186
|
+
throw applyCompatErrorContract(err);
|
|
187
|
+
}
|
|
168
188
|
}
|
|
169
189
|
async _sendWithOpts(messages, useModel, tools, opts) {
|
|
170
190
|
// Re-warm a kept-alive socket to the provider origin before the turn so
|
|
@@ -1,33 +1,52 @@
|
|
|
1
1
|
import { getModelMetadataSync } from '../../../shared/llm/model-catalog.mjs';
|
|
2
2
|
|
|
3
|
-
// Public Responses contracts are independent of the OAuth backend.
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
3
|
+
// Public Responses contracts are independent of the OAuth backend. Every
|
|
4
|
+
// capability here comes from the model catalog (the service-tier price columns
|
|
5
|
+
// and the cache-write rate), so a model the catalog lists works without an
|
|
6
|
+
// edit. Only a model no catalog describes (cold install) falls back to the
|
|
7
|
+
// documented ids known before the catalogs carried these columns.
|
|
8
|
+
const SNAPSHOT_SUFFIX = /-\d{4}-\d{2}-\d{2}$/;
|
|
9
|
+
const PRE_CATALOG_GPT_56_PLUS =
|
|
7
10
|
/^(?:gpt-6-(?:astra|sol|luna)|gpt-6\.1-sol|gpt-5\.6-(?:sol|terra|luna))(?:-\d{4}-\d{2}-\d{2})?$/;
|
|
11
|
+
const PRE_CATALOG_EARLIER_FAST = /^gpt-5\.(?:5|4|4-mini)(?:-\d{4}|$)/;
|
|
12
|
+
const PRE_CATALOG_ULTRAFAST = /^(?:gpt-6-astra|gpt-6\.1-sol)(?:-\d{4}-\d{2}-\d{2})?$/;
|
|
13
|
+
const modelId = (model) => String(model?.id || model || '').trim();
|
|
8
14
|
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return
|
|
15
|
+
// The catalog metadata of one model. A dated snapshot reads as its undated
|
|
16
|
+
// alias when only the alias is listed.
|
|
17
|
+
function openAiCatalogMeta(model) {
|
|
18
|
+
const id = String(model?.id || model || '').trim();
|
|
19
|
+
for (const candidate of new Set([id, id.replace(SNAPSHOT_SUFFIX, '')])) {
|
|
20
|
+
const meta = candidate ? getModelMetadataSync(candidate, 'openai') : null;
|
|
21
|
+
if (meta) return meta;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
18
24
|
}
|
|
19
|
-
// Earlier models documented for Priority processing (now Fast mode).
|
|
20
|
-
const EARLIER_FAST_MODELS = /^gpt-5\.(?:5|4|4-mini)(?:-\d{4}|$)/;
|
|
21
25
|
|
|
22
26
|
export function openAiDirectSupportsFast(model) {
|
|
23
|
-
const
|
|
24
|
-
|
|
27
|
+
const meta = openAiCatalogMeta(model);
|
|
28
|
+
// The catalog's declaration (true or false) decides; a published Priority
|
|
29
|
+
// rate is the same declaration. A row silent on Fast keeps the documented ids.
|
|
30
|
+
if (typeof meta?.supportsFastMode === 'boolean') return meta.supportsFastMode;
|
|
31
|
+
if (meta?.fastPricing) return true;
|
|
32
|
+
const id = modelId(model);
|
|
33
|
+
return PRE_CATALOG_GPT_56_PLUS.test(id) || PRE_CATALOG_EARLIER_FAST.test(id);
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
// A published Ultrafast rate (the `_ultrafast` price columns) is the tier's
|
|
37
|
+
// declaration; a row silent on it keeps the documented ids.
|
|
29
38
|
export function openAiDirectSupportsUltrafast(model) {
|
|
30
|
-
|
|
39
|
+
if (openAiCatalogMeta(model)?.ultrafastPricing) return true;
|
|
40
|
+
return PRE_CATALOG_ULTRAFAST.test(modelId(model));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// OpenAI bills prompt-cache writes from GPT-5.6 on, and exactly those models
|
|
44
|
+
// take prompt_cache_options in place of prompt_cache_retention, so a published
|
|
45
|
+
// cache-write rate selects the new form.
|
|
46
|
+
// https://developers.openai.com/api/docs/guides/prompt-caching
|
|
47
|
+
function promptCacheForm(model) {
|
|
48
|
+
const writeRate = openAiCatalogMeta(model)?.cacheWriteCostPerM > 0;
|
|
49
|
+
return writeRate || PRE_CATALOG_GPT_56_PLUS.test(modelId(model)) ? 'options' : 'retention';
|
|
31
50
|
}
|
|
32
51
|
|
|
33
52
|
export function applyOpenAIDirectCachePolicy(body, model, storeResponses) {
|
|
@@ -37,10 +56,8 @@ export function applyOpenAIDirectCachePolicy(body, model, storeResponses) {
|
|
|
37
56
|
// Preserve the existing opt-out: no explicit cache-retention hint when
|
|
38
57
|
// response storage is disabled. The provider's default cache still applies.
|
|
39
58
|
if (!storeResponses) return body;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
body.prompt_cache_retention = '24h';
|
|
44
|
-
}
|
|
59
|
+
const form = promptCacheForm(model);
|
|
60
|
+
if (form === 'options') body.prompt_cache_options = { ttl: '30m' };
|
|
61
|
+
else if (form === 'retention') body.prompt_cache_retention = '24h';
|
|
45
62
|
return body;
|
|
46
63
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openai-error-contract.mjs — the one place the OpenAI provider boundary
|
|
3
|
+
* stamps the shared error contract onto a thrown error:
|
|
4
|
+
*
|
|
5
|
+
* status / httpStatus typed HTTP status
|
|
6
|
+
* code lowercased error code/type from the JSON body or wire event
|
|
7
|
+
* headers response headers (HTTP, WS handshake, nested error.headers)
|
|
8
|
+
* retryAfterMs server advice, uncapped (Retry-After / Retry-After-Ms)
|
|
9
|
+
* resetAt epoch ms from quota reset fields (resets_at, resets_in_seconds)
|
|
10
|
+
*
|
|
11
|
+
* Pure: no I/O. Message text is never parsed for status or code.
|
|
12
|
+
*/
|
|
13
|
+
import { retryAfterMsFromError, sleepWithAbort } from './retry-classifier.mjs';
|
|
14
|
+
|
|
15
|
+
const isObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
16
|
+
|
|
17
|
+
// Explicit quota / billing refusals: waiting never helps, so no retry layer
|
|
18
|
+
// may treat them as an ordinary rate limit.
|
|
19
|
+
const QUOTA_OR_BILLING_CODE_RE = /quota|billing|usage_limit|usage_not_included/;
|
|
20
|
+
|
|
21
|
+
export function isQuotaOrBillingCode(code) {
|
|
22
|
+
return QUOTA_OR_BILLING_CODE_RE.test(String(code || '').toLowerCase());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Parse an HTTP error body ONCE into its contract fields. `message` is the
|
|
27
|
+
* body's own message (untruncated); callers truncate for diagnostics.
|
|
28
|
+
*/
|
|
29
|
+
export function parseErrorBody(text) {
|
|
30
|
+
let json = null;
|
|
31
|
+
try {
|
|
32
|
+
json = JSON.parse(String(text || ''));
|
|
33
|
+
} catch {}
|
|
34
|
+
const error = isObject(json?.error) ? json.error : isObject(json) ? json : null;
|
|
35
|
+
const rawCode = error?.code ?? error?.type ?? (typeof json?.error === 'string' ? json.error : null);
|
|
36
|
+
const code = typeof rawCode === 'string' && rawCode.trim() ? rawCode.trim().toLowerCase() : '';
|
|
37
|
+
const message = typeof error?.message === 'string' ? error.message : '';
|
|
38
|
+
return { json, error, code, message };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function epochMsFrom(value) {
|
|
42
|
+
if (typeof value === 'string' && value.trim() && !Number.isFinite(Number(value))) {
|
|
43
|
+
const parsed = Date.parse(value);
|
|
44
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
45
|
+
}
|
|
46
|
+
const n = Number(value);
|
|
47
|
+
if (!Number.isFinite(n) || n <= 0) return 0;
|
|
48
|
+
// Epoch seconds (the wire form) vs epoch milliseconds.
|
|
49
|
+
return n < 1e12 ? Math.round(n * 1000) : Math.round(n);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Absolute quota reset time (epoch ms) from the first source carrying one; 0 when none. */
|
|
53
|
+
export function resetAtFrom(...sources) {
|
|
54
|
+
for (const source of sources) {
|
|
55
|
+
if (!isObject(source)) continue;
|
|
56
|
+
const absolute = epochMsFrom(source.resets_at ?? source.reset_at ?? source.resetsAt);
|
|
57
|
+
if (absolute) return absolute;
|
|
58
|
+
const inSeconds = Number(source.resets_in_seconds ?? source.reset_in_seconds);
|
|
59
|
+
if (Number.isFinite(inSeconds) && inSeconds > 0) return Date.now() + Math.round(inSeconds * 1000);
|
|
60
|
+
}
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── User-facing messages for typed wire errors ───────────────────────────────
|
|
65
|
+
const QUOTA_EXCEEDED_MESSAGE = 'Quota exceeded. Check your plan and billing details.';
|
|
66
|
+
const USAGE_NOT_INCLUDED_MESSAGE =
|
|
67
|
+
'To use Codex with your ChatGPT plan, upgrade to Plus: https://chatgpt.com/explore/plus.';
|
|
68
|
+
const SERVER_OVERLOADED_MESSAGE = 'Selected model is at capacity. Please try a different model.';
|
|
69
|
+
const WS_CONNECTION_LIMIT_MESSAGE =
|
|
70
|
+
'Responses websocket connection limit reached (60 minutes). Create a new websocket connection to continue.';
|
|
71
|
+
const PREVIOUS_RESPONSE_NOT_FOUND_MESSAGE = 'Previous response was not found. Retrying the full request.';
|
|
72
|
+
const QUOTA_EXCEEDED_CODES = new Set([
|
|
73
|
+
'insufficient_quota',
|
|
74
|
+
'credit_balance_exhausted',
|
|
75
|
+
'organization_spend_limit_exceeded',
|
|
76
|
+
'project_spend_limit_exceeded',
|
|
77
|
+
'organization_usage_limit_exceeded',
|
|
78
|
+
]);
|
|
79
|
+
// Wire codes whose own message is empty fall back to this copy.
|
|
80
|
+
const FALLBACK_MESSAGES = Object.freeze({
|
|
81
|
+
websocket_connection_limit_reached: WS_CONNECTION_LIMIT_MESSAGE,
|
|
82
|
+
previous_response_not_found: PREVIOUS_RESPONSE_NOT_FOUND_MESSAGE,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
function headerValue(headers, name) {
|
|
86
|
+
if (!headers || typeof headers !== 'object') return '';
|
|
87
|
+
if (typeof headers.get === 'function') return String(headers.get(name) ?? '').trim();
|
|
88
|
+
const lower = name.toLowerCase();
|
|
89
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
90
|
+
if (key.toLowerCase() === lower) return String(Array.isArray(value) ? value[0] : (value ?? '')).trim();
|
|
91
|
+
}
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ordinalSuffix(day) {
|
|
96
|
+
if (day >= 11 && day <= 13) return 'th';
|
|
97
|
+
return { 1: 'st', 2: 'nd', 3: 'rd' }[day % 10] || 'th';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Retry timestamp: "3:04 PM" today, else "Mar 5th, 2026 3:04 PM" (local time). */
|
|
101
|
+
export function formatRetryTimestamp(resetsAtMs, now = Date.now()) {
|
|
102
|
+
const reset = new Date(resetsAtMs);
|
|
103
|
+
const time = reset.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
|
|
104
|
+
if (reset.toDateString() === new Date(now).toDateString()) return time;
|
|
105
|
+
const month = reset.toLocaleDateString('en-US', { month: 'short' });
|
|
106
|
+
return `${month} ${reset.getDate()}${ordinalSuffix(reset.getDate())}, ${reset.getFullYear()} ${time}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const WORKSPACE_REACHED_MESSAGES = Object.freeze({
|
|
110
|
+
workspace_owner_credits_depleted: 'Your workspace is out of credits. Add credits to continue.',
|
|
111
|
+
workspace_member_credits_depleted:
|
|
112
|
+
'Your workspace is out of credits. Ask your workspace owner to refill in order to continue.',
|
|
113
|
+
workspace_owner_usage_limit_reached:
|
|
114
|
+
'You hit your spend cap set in your workspace. Increase your spend cap to continue.',
|
|
115
|
+
workspace_member_usage_limit_reached:
|
|
116
|
+
'You hit your spend cap set by the owner of your workspace. Ask an owner to increase your spend cap to continue.',
|
|
117
|
+
});
|
|
118
|
+
const PRO_PLANS = new Set(['pro', 'prolite', 'promax']);
|
|
119
|
+
const ADMIN_PLANS = new Set([
|
|
120
|
+
'team',
|
|
121
|
+
'self_serve_business_prolite',
|
|
122
|
+
'self_serve_business_usage_based',
|
|
123
|
+
'business',
|
|
124
|
+
'ent26',
|
|
125
|
+
'enterprise_cbp_automation',
|
|
126
|
+
'enterprise_cbp_usage_based',
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Usage-limit copy: per-limit name first, then workspace
|
|
131
|
+
* credits / spend cap, then the promo message, then the plan upgrade copy.
|
|
132
|
+
* @param {{planType?: string, resetsAtMs?: number, promo?: string, reachedType?: string, limitName?: string, now?: number}} f
|
|
133
|
+
*/
|
|
134
|
+
export function usageLimitMessage({ planType, resetsAtMs, promo, reachedType, limitName, now } = {}) {
|
|
135
|
+
const at = resetsAtMs ? formatRetryTimestamp(resetsAtMs, now) : '';
|
|
136
|
+
const suffix = at ? ` Try again at ${at}.` : ' Try again later.';
|
|
137
|
+
const suffixOr = at ? ` or try again at ${at}.` : ' or try again later.';
|
|
138
|
+
const name = String(limitName || '').trim();
|
|
139
|
+
if (name && !/^(codex|gpt-reserve)$/i.test(name)) {
|
|
140
|
+
return `You’ve hit your usage limit for ${name}. Switch to another model now,${suffixOr}`;
|
|
141
|
+
}
|
|
142
|
+
const workspace = WORKSPACE_REACHED_MESSAGES[String(reachedType || '').toLowerCase()];
|
|
143
|
+
if (workspace) return workspace;
|
|
144
|
+
if (promo) return `You’ve hit your usage limit. ${promo},${suffixOr}`;
|
|
145
|
+
const plan = String(planType || '').toLowerCase();
|
|
146
|
+
if (plan === 'plus') {
|
|
147
|
+
return `You’ve hit your usage limit. Upgrade to Pro (https://chatgpt.com/explore/pro), visit https://chatgpt.com/settings/usage to purchase more credits${suffixOr}`;
|
|
148
|
+
}
|
|
149
|
+
if (ADMIN_PLANS.has(plan)) {
|
|
150
|
+
return `You’ve hit your usage limit. To get more access now, send a request to your admin${suffixOr}`;
|
|
151
|
+
}
|
|
152
|
+
if (plan === 'free' || plan === 'go') {
|
|
153
|
+
return `You’ve hit your usage limit. Upgrade to Plus to continue using Codex (https://chatgpt.com/explore/plus),${suffixOr}`;
|
|
154
|
+
}
|
|
155
|
+
if (PRO_PLANS.has(plan)) {
|
|
156
|
+
return `You’ve hit your usage limit. Visit https://chatgpt.com/settings/usage to purchase more credits${suffixOr}`;
|
|
157
|
+
}
|
|
158
|
+
return `You’ve hit your usage limit.${suffix}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** The user-facing copy for a typed wire error, or '' when none applies. */
|
|
162
|
+
function typedWireErrorMessage({ code, type, error, headers, resetAt }) {
|
|
163
|
+
if (type === 'usage_limit_reached' || code === 'usage_limit_reached') {
|
|
164
|
+
const limitId = headerValue(headers, 'x-codex-active-limit').toLowerCase().replace(/_/g, '-');
|
|
165
|
+
return usageLimitMessage({
|
|
166
|
+
planType: error?.plan_type,
|
|
167
|
+
resetsAtMs: resetAt,
|
|
168
|
+
promo: headerValue(headers, 'x-codex-promo-message'),
|
|
169
|
+
reachedType: headerValue(headers, 'x-codex-rate-limit-reached-type'),
|
|
170
|
+
limitName: limitId ? headerValue(headers, `x-${limitId}-limit-name`) : '',
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (type === 'usage_not_included' || code === 'usage_not_included') return USAGE_NOT_INCLUDED_MESSAGE;
|
|
174
|
+
if (QUOTA_EXCEEDED_CODES.has(code) || type === 'insufficient_quota') return QUOTA_EXCEEDED_MESSAGE;
|
|
175
|
+
if (code === 'server_is_overloaded') return SERVER_OVERLOADED_MESSAGE;
|
|
176
|
+
const fallback = FALLBACK_MESSAGES[code];
|
|
177
|
+
return fallback && !String(error?.message || '').trim() ? fallback : '';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Swap the server's own text inside `err.message` for the mapped copy; the
|
|
181
|
+
// original stays on providerError. `userMessage` always carries the copy.
|
|
182
|
+
function applyTypedWireMessage(err, mapped, original) {
|
|
183
|
+
err.userMessage = mapped;
|
|
184
|
+
const current = String(err.message || '');
|
|
185
|
+
const raw = String(original || '');
|
|
186
|
+
for (const needle of [raw, raw.slice(0, 200)]) {
|
|
187
|
+
if (needle && current.endsWith(needle)) {
|
|
188
|
+
err.message = current.slice(0, current.length - needle.length) + mapped;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!raw && current) err.message = `${current}: ${mapped}`;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Stamp the contract onto `err`. Every field is optional input; only the
|
|
196
|
+
* ones present are written, and a field already on `err` is never replaced
|
|
197
|
+
* by an absent one.
|
|
198
|
+
*
|
|
199
|
+
* @param {Error} err
|
|
200
|
+
* @param {object} src
|
|
201
|
+
* @param {number} [src.status]
|
|
202
|
+
* @param {object} [src.headers] Headers or plain object
|
|
203
|
+
* @param {string} [src.code] already-lowercased code
|
|
204
|
+
* @param {object} [src.error] parsed error object (code/type/resets_at/headers)
|
|
205
|
+
* @param {object} [src.event] wire event the error was built from
|
|
206
|
+
*/
|
|
207
|
+
export function applyErrorContract(err, { status, headers, code, error, event } = {}) {
|
|
208
|
+
try {
|
|
209
|
+
const n = Number(status);
|
|
210
|
+
if (Number.isFinite(n) && n >= 100 && n <= 599) {
|
|
211
|
+
err.status = n;
|
|
212
|
+
err.httpStatus = n;
|
|
213
|
+
}
|
|
214
|
+
const wireHeaders = headers ?? error?.headers ?? event?.error?.headers ?? event?.response?.error?.headers ?? event?.headers;
|
|
215
|
+
if (wireHeaders && typeof wireHeaders === 'object') err.headers = wireHeaders;
|
|
216
|
+
const rawCode = code ?? error?.code ?? error?.type ?? null;
|
|
217
|
+
if (typeof rawCode === 'string' && rawCode.trim()) err.code = rawCode.trim().toLowerCase();
|
|
218
|
+
const resetAt = resetAtFrom(error, event?.error, event?.response?.error, event);
|
|
219
|
+
if (resetAt) err.resetAt = resetAt;
|
|
220
|
+
const retryAfterMs = retryAfterMsFromError(err);
|
|
221
|
+
if (retryAfterMs != null) {
|
|
222
|
+
err.retryAfterMs = retryAfterMs;
|
|
223
|
+
// Server advice is a deadline captured once, so a
|
|
224
|
+
// later transport switch cannot restart or skip it.
|
|
225
|
+
if (err.retryAfterAt == null) err.retryAfterAt = Date.now() + retryAfterMs;
|
|
226
|
+
}
|
|
227
|
+
const type = typeof error?.type === 'string' ? error.type.trim().toLowerCase() : '';
|
|
228
|
+
const mapped = typedWireErrorMessage({ code: err.code || '', type, error, headers: err.headers, resetAt: err.resetAt });
|
|
229
|
+
if (mapped) applyTypedWireMessage(err, mapped, error?.message);
|
|
230
|
+
} catch {}
|
|
231
|
+
return err;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* A response.incomplete with reason `content_filter` is a policy refusal:
|
|
236
|
+
* type it so no layer mistakes it for a transient or untyped failure. The
|
|
237
|
+
* wire event is deliberately NOT attached as `responseFailed` (that marker
|
|
238
|
+
* makes unknown codes default-retryable).
|
|
239
|
+
*/
|
|
240
|
+
export function markContentFilter(err, reason) {
|
|
241
|
+
if (String(reason || '').trim().toLowerCase() !== 'content_filter') return err;
|
|
242
|
+
err.code = 'content_filter';
|
|
243
|
+
err.providerErrorCode = 'content_filter';
|
|
244
|
+
return err;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The error for a non-2xx HTTP response whose body has been read as `text`.
|
|
249
|
+
* The message carries a bounded diagnostic; the structured fields come from
|
|
250
|
+
* the full parsed body.
|
|
251
|
+
*/
|
|
252
|
+
export function httpResponseError({ label, status, headers, text, initialResponse = false, diagnosticLimit = 200 }) {
|
|
253
|
+
const parsed = parseErrorBody(text);
|
|
254
|
+
const diagnostic = (parsed.message || String(text || '')).slice(0, diagnosticLimit);
|
|
255
|
+
const err = new Error(diagnostic ? `${label} ${status}: ${diagnostic}` : `${label} ${status}`);
|
|
256
|
+
if (initialResponse) err.initialResponseError = true;
|
|
257
|
+
if (parsed.error) err.providerError = parsed.error;
|
|
258
|
+
if (parsed.code) err.providerErrorCode = parsed.code;
|
|
259
|
+
return applyErrorContract(err, { status, headers, code: parsed.code, error: parsed.error, event: parsed.json });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// The route decides the HTTP/SSE label: the api-key `openai` provider is not OAuth.
|
|
263
|
+
export const httpLabel = (auth) => (auth?.type === 'openai-direct' ? 'OpenAI HTTP fallback' : 'OpenAI OAuth HTTP fallback');
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Changing transport must not bypass the server's retry deadline.
|
|
267
|
+
* Sleeps until the deadline the failed attempt captured (abortable).
|
|
268
|
+
*/
|
|
269
|
+
export async function waitForRetryDeadline(err, externalSignal, sleepFn) {
|
|
270
|
+
// retryAfterMsFromError reports what is left of the captured retryAfterAt.
|
|
271
|
+
const remaining = retryAfterMsFromError(err) ?? 0;
|
|
272
|
+
if (!(remaining > 0)) return;
|
|
273
|
+
await sleepWithAbort(remaining, externalSignal, sleepFn, 'OpenAI transport fallback wait aborted');
|
|
274
|
+
}
|
|
@@ -9,11 +9,17 @@
|
|
|
9
9
|
import zlib from 'node:zlib';
|
|
10
10
|
import { traceAgentFetch } from '../agent-trace.mjs';
|
|
11
11
|
import { PROVIDER_HTTP_RESPONSE_TIMEOUT_MS, createTimeoutSignal } from '../stall-policy.mjs';
|
|
12
|
+
import { CODEX_REQUEST_RETRY_BACKOFF_MS, CODEX_RETRY_JITTER_RATIO } from './openai-retry-policy.mjs';
|
|
12
13
|
import { classifyError, jitterDelayMs, retryAfterMsFromError, sleepWithAbort } from './retry-classifier.mjs';
|
|
14
|
+
import { emitProviderRetryStage } from './retry-executor.mjs';
|
|
15
|
+
import { adviceGatedRetry } from './retry-midstream.mjs';
|
|
16
|
+
import { TERMINAL_EDGE_STATUSES } from './retry-classification.mjs';
|
|
17
|
+
import { DETERMINISTIC_KINDS, providerErrorKind } from './provider-error-kind.mjs';
|
|
13
18
|
import { readStreamOutcome } from './lib/stream-outcome.mjs';
|
|
14
19
|
import { getLlmDispatcher, recycleLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
|
|
15
20
|
import { CODEX_RESPONSES_URL } from './openai-codex-endpoints.mjs';
|
|
16
21
|
import { captureCodexTurnState } from './openai-turn-state.mjs';
|
|
22
|
+
import { httpLabel, httpResponseError } from './openai-error-contract.mjs';
|
|
17
23
|
|
|
18
24
|
// Public OpenAI Responses API endpoint for the api-key `openai` provider.
|
|
19
25
|
// The openai-direct WS transport hits the same origin (openai-ws-pool
|
|
@@ -22,9 +28,28 @@ import { captureCodexTurnState } from './openai-turn-state.mjs';
|
|
|
22
28
|
// openai-oauth. Same Responses SSE wire format, only endpoint + auth differ.
|
|
23
29
|
const OPENAI_DIRECT_RESPONSES_URL = 'https://api.openai.com/v1/responses';
|
|
24
30
|
const CODEX_REQUEST_MAX_RETRIES = 4;
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
// Advice-gated retry for the OpenAI request loops (same options the shared
|
|
32
|
+
// withRetry takes): an overload and an untyped 429 retry only with server advice.
|
|
33
|
+
const OPENAI_REQUEST_RETRY_OPTS = Object.freeze({
|
|
34
|
+
overloadRetryRequiresAdvice: true,
|
|
35
|
+
generic429RetryRequiresAdvice: true,
|
|
36
|
+
backoffMs: CODEX_REQUEST_RETRY_BACKOFF_MS,
|
|
37
|
+
retryJitterRatio: CODEX_RETRY_JITTER_RATIO,
|
|
38
|
+
});
|
|
27
39
|
|
|
40
|
+
|
|
41
|
+
// Request-layer retry: 5xx and typed transport
|
|
42
|
+
// failures only. 408 and 429 are NOT retried here; they surface to the outer
|
|
43
|
+
// stream-retry layer, which owns their budget and honors Retry-After.
|
|
44
|
+
// A deterministic refusal (quota, billing, entitlement, policy, auth,
|
|
45
|
+
// permission, invalid request) is terminal whatever status carries it.
|
|
46
|
+
function isRetryableHttpFailure(err) {
|
|
47
|
+
const status = err.httpStatus;
|
|
48
|
+
if (!(status >= 500 && status <= 599) || TERMINAL_EDGE_STATUSES.has(status)) return false;
|
|
49
|
+
const { kind } = providerErrorKind(err);
|
|
50
|
+
if (DETERMINISTIC_KINDS.has(kind)) return false;
|
|
51
|
+
return adviceGatedRetry(err, OPENAI_REQUEST_RETRY_OPTS) !== false;
|
|
52
|
+
}
|
|
28
53
|
// Request-body zstd gate. Namespace import: on Node runtimes without zlib
|
|
29
54
|
// zstd bindings the named export would fail at module load, so the call site
|
|
30
55
|
// typeof-guards zlib.zstdCompressSync instead.
|
|
@@ -65,13 +90,15 @@ async function postWithRetries({
|
|
|
65
90
|
onStageChange,
|
|
66
91
|
_sleepFn,
|
|
67
92
|
}) {
|
|
93
|
+
const label = httpLabel(auth);
|
|
68
94
|
let bodyForSend = encodeRequestBody(auth, rawBytes);
|
|
69
95
|
let response;
|
|
96
|
+
let httpError = null;
|
|
70
97
|
for (let attempt = 0; attempt <= CODEX_REQUEST_MAX_RETRIES; attempt++) {
|
|
71
98
|
const headerTimeout = createTimeoutSignal(
|
|
72
99
|
totalTimeout.signal,
|
|
73
100
|
PROVIDER_HTTP_RESPONSE_TIMEOUT_MS,
|
|
74
|
-
|
|
101
|
+
`${label} initial response`
|
|
75
102
|
);
|
|
76
103
|
let requestError = null;
|
|
77
104
|
try {
|
|
@@ -113,7 +140,19 @@ async function postWithRetries({
|
|
|
113
140
|
attempt--;
|
|
114
141
|
continue;
|
|
115
142
|
}
|
|
116
|
-
|
|
143
|
+
// The error body is read exactly once, here, into the contract fields.
|
|
144
|
+
httpError = null;
|
|
145
|
+
if (response && !response.ok) {
|
|
146
|
+
const text = await response.text().catch(() => '');
|
|
147
|
+
httpError = httpResponseError({
|
|
148
|
+
label,
|
|
149
|
+
status: response.status,
|
|
150
|
+
headers: response.headers,
|
|
151
|
+
text,
|
|
152
|
+
initialResponse: true,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const retryableStatus = httpError !== null && isRetryableHttpFailure(httpError);
|
|
117
156
|
// Typed transient transport failures only (errno / SDK connection
|
|
118
157
|
// type). An unknown pre-response failure throws immediately instead of
|
|
119
158
|
// re-issuing the POST.
|
|
@@ -135,30 +174,36 @@ async function postWithRetries({
|
|
|
135
174
|
// Reissuing the POST is a REPLAY: allowed for a typed transient
|
|
136
175
|
// failure of the initial request, denied once the failure carries
|
|
137
176
|
// exposure evidence (relayed output / dispatched tool call).
|
|
138
|
-
const attemptFailure =
|
|
139
|
-
requestError ||
|
|
140
|
-
Object.assign(new Error(`OpenAI OAuth HTTP fallback ${response.status}`), {
|
|
141
|
-
httpStatus: response.status,
|
|
142
|
-
headers: response.headers,
|
|
143
|
-
initialResponseError: true,
|
|
144
|
-
});
|
|
177
|
+
const attemptFailure = requestError || httpError;
|
|
145
178
|
if (readStreamOutcome(attemptFailure).replaySafe !== true) {
|
|
146
|
-
if (response) await response.arrayBuffer().catch(() => {});
|
|
147
179
|
totalTimeout.cleanup();
|
|
148
180
|
throw attemptFailure;
|
|
149
181
|
}
|
|
150
|
-
// A non-success response has not exposed any streamed output. Drain
|
|
151
|
-
// its body before reissuing so the dispatcher can reuse the socket.
|
|
152
|
-
if (response) await response.arrayBuffer().catch(() => {});
|
|
153
182
|
// Server advice (Retry-After / Retry-After-Ms) replaces the local curve.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
183
|
+
// Uncapped for retryable errors; the attempt count is
|
|
184
|
+
// the bound, and the abort signal cancels the wait.
|
|
185
|
+
// The shared schedule carries a leading duplicate (withRetry convention).
|
|
186
|
+
const raw = OPENAI_REQUEST_RETRY_OPTS.backoffMs[attempt + 1];
|
|
187
|
+
const delayMs = retryAfterMsFromError(attemptFailure) ?? jitterDelayMs(raw, OPENAI_REQUEST_RETRY_OPTS.retryJitterRatio);
|
|
188
|
+
emitProviderRetryStage(onStageChange, {
|
|
189
|
+
attempt: attempt + 2,
|
|
190
|
+
maxAttempts: CODEX_REQUEST_MAX_RETRIES + 1,
|
|
191
|
+
lastErr: attemptFailure,
|
|
192
|
+
delayMs,
|
|
193
|
+
});
|
|
194
|
+
try {
|
|
195
|
+
await sleepWithAbort(
|
|
196
|
+
delayMs,
|
|
197
|
+
externalSignal || totalTimeout.signal,
|
|
198
|
+
_sleepFn,
|
|
199
|
+
`${label} request retry backoff aborted`
|
|
200
|
+
);
|
|
201
|
+
} catch (abortErr) {
|
|
202
|
+
totalTimeout.cleanup();
|
|
203
|
+
throw abortErr;
|
|
204
|
+
}
|
|
161
205
|
response = undefined;
|
|
206
|
+
httpError = null;
|
|
162
207
|
continue;
|
|
163
208
|
}
|
|
164
209
|
if (requestError) {
|
|
@@ -169,7 +214,7 @@ async function postWithRetries({
|
|
|
169
214
|
}
|
|
170
215
|
break;
|
|
171
216
|
}
|
|
172
|
-
return response;
|
|
217
|
+
return { response, httpError };
|
|
173
218
|
}
|
|
174
219
|
|
|
175
220
|
export async function openHttpSseResponse({
|
|
@@ -187,7 +232,7 @@ export async function openHttpSseResponse({
|
|
|
187
232
|
}) {
|
|
188
233
|
const fetchStartedAt = Date.now();
|
|
189
234
|
const responsesUrl = auth?.type === 'openai-direct' ? OPENAI_DIRECT_RESPONSES_URL : CODEX_RESPONSES_URL;
|
|
190
|
-
const response = await postWithRetries({
|
|
235
|
+
const { response, httpError } = await postWithRetries({
|
|
191
236
|
responsesUrl,
|
|
192
237
|
headers,
|
|
193
238
|
rawBytes: Buffer.from(JSON.stringify(body)),
|
|
@@ -208,14 +253,9 @@ export async function openHttpSseResponse({
|
|
|
208
253
|
transport: 'http',
|
|
209
254
|
});
|
|
210
255
|
|
|
211
|
-
if (
|
|
212
|
-
const text = await response.text().catch(() => '');
|
|
213
|
-
const err = new Error(`OpenAI OAuth HTTP fallback ${response.status}: ${text.slice(0, 200)}`);
|
|
214
|
-
err.httpStatus = response.status;
|
|
215
|
-
err.headers = response.headers;
|
|
216
|
-
err.initialResponseError = true;
|
|
256
|
+
if (httpError) {
|
|
217
257
|
totalTimeout.cleanup();
|
|
218
|
-
throw
|
|
258
|
+
throw httpError;
|
|
219
259
|
}
|
|
220
260
|
if (auth?.type !== 'openai-direct') {
|
|
221
261
|
const responseTurnState = response.headers?.get?.('x-codex-turn-state');
|
|
@@ -223,7 +263,7 @@ export async function openHttpSseResponse({
|
|
|
223
263
|
}
|
|
224
264
|
if (!response.body) {
|
|
225
265
|
totalTimeout.cleanup();
|
|
226
|
-
throw Object.assign(new Error(
|
|
266
|
+
throw Object.assign(new Error(`${httpLabel(auth)} returned no response body`), {
|
|
227
267
|
initialResponseError: true,
|
|
228
268
|
});
|
|
229
269
|
}
|