mixdog 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/package.json +2 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +65 -50
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +2 -2
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +31 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -40
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +670 -274
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
- package/src/runtime/shared/turn-snapshot.mjs +986 -690
- package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -24
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +19 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +12 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +73 -8
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +46 -14
- package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
- package/src/session-runtime/mcp-glue.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +85 -17
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +4 -0
- package/src/session-runtime/resource-mcp-api.mjs +98 -2
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +21 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +74 -0
- package/src/standalone/daemon.mjs +64 -8
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +14 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
- package/src/standalone/session-service-api.mjs +11 -1
- package/src/standalone/session-service.mjs +1 -0
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +6 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +11 -16
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +20 -5
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/dist/index.mjs +3221 -1500
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +43 -24
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/extensions.mjs +22 -0
- package/src/tui/session/session-api/integrations.mjs +16 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +7 -2
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +11 -14
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/session-flow/drain.mjs +7 -1
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +46 -16
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/workflows/default/WORKFLOW.md +3 -1
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gemini-error-contract.mjs — Google RPC error details → the provider error
|
|
3
|
+
* contract shared by Gemini (REST + SDK) and Antigravity:
|
|
4
|
+
* status, code (lowercased), retryAfterMs (uncapped), resetAt (epoch ms),
|
|
5
|
+
* quotaScope ('minute' | 'day' | undefined).
|
|
6
|
+
*
|
|
7
|
+
* Details come from `error.details` (REST body) or `errorDetails` (SDK
|
|
8
|
+
* GoogleGenerativeAIFetchError). Entries are google.rpc.RetryInfo,
|
|
9
|
+
* QuotaFailure and ErrorInfo.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const DURATION_RE = /^(?:\d+(?:\.\d+)?(?:ms|s|m|h))+$/;
|
|
13
|
+
const DURATION_PART_RE = /(\d+(?:\.\d+)?)(ms|s|m|h)/g;
|
|
14
|
+
const DURATION_UNIT_MS = { ms: 1, s: 1000, m: 60_000, h: 3_600_000 };
|
|
15
|
+
const BARE_SECONDS_RE = /^\d+(?:\.\d+)?$/;
|
|
16
|
+
|
|
17
|
+
// Google error classification: a per-minute quota with no RetryInfo is
|
|
18
|
+
// retried after one quota window.
|
|
19
|
+
export const GEMINI_PER_MINUTE_RETRY_MS = 60_000;
|
|
20
|
+
|
|
21
|
+
// "Please retry in 34.07s" (Gemini API) / "reset after 2h1m1s" (Cloud Code).
|
|
22
|
+
const MESSAGE_DELAY_RE = /(?:please retry in|reset after)\s+((?:\d+(?:\.\d+)?(?:ms|h|m|s))+)/i;
|
|
23
|
+
const LIMIT_ZERO_RE = /limit:\s*0(?!\d|\.\d)/i;
|
|
24
|
+
const CAPACITY_MESSAGE_RE = /exhausted your capacity|capacity exceeded|model_capacity_exhausted/i;
|
|
25
|
+
// ErrorInfo reasons that name a spent quota with no window of its own.
|
|
26
|
+
const TERMINAL_REASONS = new Set(['quota_exhausted', 'insufficient_g1_credits_balance']);
|
|
27
|
+
const CAPACITY_REASONS = new Set(['model_capacity_exhausted', 'model_capacity_exceeded']);
|
|
28
|
+
|
|
29
|
+
const GOOGLE_STATUS_HTTP = {
|
|
30
|
+
INVALID_ARGUMENT: 400,
|
|
31
|
+
UNAUTHENTICATED: 401,
|
|
32
|
+
PERMISSION_DENIED: 403,
|
|
33
|
+
NOT_FOUND: 404,
|
|
34
|
+
RESOURCE_EXHAUSTED: 429,
|
|
35
|
+
INTERNAL: 500,
|
|
36
|
+
UNAVAILABLE: 503,
|
|
37
|
+
DEADLINE_EXCEEDED: 504,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function detailType(entry) {
|
|
41
|
+
return typeof entry?.['@type'] === 'string' ? entry['@type'] : '';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function durationMs(value) {
|
|
45
|
+
if (typeof value === 'string') {
|
|
46
|
+
const text = value.trim();
|
|
47
|
+
if (BARE_SECONDS_RE.test(text)) return Math.ceil(Number(text) * 1000);
|
|
48
|
+
if (!DURATION_RE.test(text)) return null;
|
|
49
|
+
let total = 0;
|
|
50
|
+
for (const [, amount, unit] of text.matchAll(DURATION_PART_RE)) total += Number(amount) * DURATION_UNIT_MS[unit];
|
|
51
|
+
return Math.ceil(total);
|
|
52
|
+
}
|
|
53
|
+
if (value && typeof value === 'object') {
|
|
54
|
+
const ms = Number(value.seconds || 0) * 1000 + Number(value.nanos || 0) / 1_000_000;
|
|
55
|
+
return Number.isFinite(ms) && ms >= 0 ? Math.ceil(ms) : null;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// A zero wait is no advice: the caller's normal backoff (or the per-minute
|
|
61
|
+
// window) applies instead of an immediate re-issue.
|
|
62
|
+
function adviceMs(value) {
|
|
63
|
+
const ms = durationMs(value);
|
|
64
|
+
return ms > 0 ? ms : undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function scopeOf(text) {
|
|
68
|
+
if (/per[\s_-]?day|daily/i.test(text)) return 'day';
|
|
69
|
+
if (/per[\s_-]?minute/i.test(text)) return 'minute';
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* `quotaScope` is 'minute' / 'day' from the typed quota ids only (never from
|
|
75
|
+
* the length of the server's wait); 'plan' marks a spent
|
|
76
|
+
* quota with no window (ErrorInfo QUOTA_EXHAUSTED, or a QuotaFailure
|
|
77
|
+
* violation with a zero limit; message prose alone never counts). Capacity
|
|
78
|
+
* (MODEL_CAPACITY_EXHAUSTED / "exhausted your capacity") is an overload of the
|
|
79
|
+
* shared pool: code `model_capacity_exhausted`, never a quota scope.
|
|
80
|
+
* @param {{ details?: unknown, errorDetails?: unknown, googleStatus?: string, message?: string }} input
|
|
81
|
+
* @returns {{ code?: string, retryAfterMs?: number, resetAt?: number, quotaScope?: 'minute'|'day'|'plan' }}
|
|
82
|
+
*/
|
|
83
|
+
export function geminiErrorContract({ details, errorDetails, googleStatus, message } = {}) {
|
|
84
|
+
const list = [details, errorDetails].flatMap((entry) => (Array.isArray(entry) ? entry : []));
|
|
85
|
+
let reason = '';
|
|
86
|
+
let retryAfterMs;
|
|
87
|
+
let resetAt;
|
|
88
|
+
const scopes = new Set();
|
|
89
|
+
let zeroLimit = false;
|
|
90
|
+
for (const entry of list) {
|
|
91
|
+
const type = detailType(entry);
|
|
92
|
+
if (type.endsWith('.RetryInfo') || entry?.retryDelay !== undefined) {
|
|
93
|
+
retryAfterMs ??= adviceMs(entry.retryDelay);
|
|
94
|
+
}
|
|
95
|
+
if (type.endsWith('.QuotaFailure') || Array.isArray(entry?.violations)) {
|
|
96
|
+
for (const violation of entry.violations || []) {
|
|
97
|
+
const scope = scopeOf(String(violation?.quotaId || ''));
|
|
98
|
+
if (scope) scopes.add(scope);
|
|
99
|
+
// A spent allowance is named by the typed violation, not by prose.
|
|
100
|
+
if (String(violation?.quotaValue ?? '').trim() === '0' || LIMIT_ZERO_RE.test(String(violation?.description || ''))) {
|
|
101
|
+
zeroLimit = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (type.endsWith('.ErrorInfo') || typeof entry?.reason === 'string') {
|
|
106
|
+
if (!reason && typeof entry.reason === 'string') reason = entry.reason.trim().toLowerCase();
|
|
107
|
+
const metadata = entry.metadata && typeof entry.metadata === 'object' ? entry.metadata : {};
|
|
108
|
+
retryAfterMs ??= adviceMs(metadata.quotaResetDelay);
|
|
109
|
+
const stamp = Date.parse(String(metadata.quotaResetTimeStamp || ''));
|
|
110
|
+
if (resetAt === undefined && Number.isFinite(stamp)) resetAt = stamp;
|
|
111
|
+
const scope = scopeOf(
|
|
112
|
+
[metadata.quotaId, metadata.quota_limit, metadata.quota_metric, entry.reason].filter(Boolean).join(' ')
|
|
113
|
+
);
|
|
114
|
+
if (scope) scopes.add(scope);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const text = typeof message === 'string' ? message : '';
|
|
118
|
+
// The message names capacity only when nothing typed said otherwise (a
|
|
119
|
+
// reason, a quota id or RetryInfo outranks it, as in classifyGoogleError).
|
|
120
|
+
const capacity =
|
|
121
|
+
CAPACITY_REASONS.has(reason) ||
|
|
122
|
+
(!reason && scopes.size === 0 && retryAfterMs === undefined && CAPACITY_MESSAGE_RE.test(text));
|
|
123
|
+
// A per-day violation outlasts any per-minute one reported with it.
|
|
124
|
+
let quotaScope = scopes.has('day') ? 'day' : scopes.has('minute') ? 'minute' : undefined;
|
|
125
|
+
if (retryAfterMs === undefined) {
|
|
126
|
+
const named = MESSAGE_DELAY_RE.exec(text);
|
|
127
|
+
if (named) retryAfterMs = adviceMs(named[1]);
|
|
128
|
+
}
|
|
129
|
+
// Per-minute quota with no RetryInfo: wait one quota window.
|
|
130
|
+
if (retryAfterMs === undefined && quotaScope === 'minute' && !capacity) retryAfterMs = GEMINI_PER_MINUTE_RETRY_MS;
|
|
131
|
+
if (resetAt === undefined && retryAfterMs !== undefined) resetAt = Date.now() + retryAfterMs;
|
|
132
|
+
if (!capacity && !quotaScope && (TERMINAL_REASONS.has(reason) || zeroLimit)) quotaScope = 'plan';
|
|
133
|
+
const status = typeof googleStatus === 'string' ? googleStatus.trim().toLowerCase() : '';
|
|
134
|
+
const code = capacity ? 'model_capacity_exhausted' : reason || status;
|
|
135
|
+
return {
|
|
136
|
+
...(code ? { code } : {}),
|
|
137
|
+
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
138
|
+
...(resetAt !== undefined ? { resetAt } : {}),
|
|
139
|
+
...(quotaScope ? { quotaScope } : {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Copy the contract fields onto `err`; an already-typed `code` is kept. */
|
|
144
|
+
export function applyGeminiErrorContract(err, input) {
|
|
145
|
+
const { code, ...rest } = geminiErrorContract(input);
|
|
146
|
+
Object.assign(err, rest);
|
|
147
|
+
if (code && !(typeof err.code === 'string' && err.code)) err.code = code;
|
|
148
|
+
return err;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The `{ error: {...} }` envelope a Gemini/Cloud Code stream carries in-band
|
|
153
|
+
* instead of an HTTP error status. Returns the typed error, or null when the
|
|
154
|
+
* chunk is an ordinary payload.
|
|
155
|
+
*/
|
|
156
|
+
export function geminiInBandError(chunk, label) {
|
|
157
|
+
const detail = chunk && typeof chunk === 'object' ? chunk.error : null;
|
|
158
|
+
if (!detail || (typeof detail !== 'object' && typeof detail !== 'string')) return null;
|
|
159
|
+
const body = typeof detail === 'object' ? detail : { message: detail };
|
|
160
|
+
const googleStatus = typeof body.status === 'string' ? body.status : '';
|
|
161
|
+
const status = Number.isFinite(Number(body.code)) && Number(body.code) > 0
|
|
162
|
+
? Number(body.code)
|
|
163
|
+
: GOOGLE_STATUS_HTTP[googleStatus.toUpperCase()];
|
|
164
|
+
const err = new Error(`${label} in-band error${status ? ` ${status}` : ''}: ${String(body.message || googleStatus || 'unknown').slice(0, 300)}`);
|
|
165
|
+
err.name = 'GeminiInBandError';
|
|
166
|
+
if (status) {
|
|
167
|
+
err.status = status;
|
|
168
|
+
err.httpStatus = status;
|
|
169
|
+
}
|
|
170
|
+
if (googleStatus) err.geminiStatus = googleStatus.toUpperCase();
|
|
171
|
+
err.error = body;
|
|
172
|
+
const details = body.details ?? body.errorDetails;
|
|
173
|
+
if (Array.isArray(details)) err.details = details;
|
|
174
|
+
return applyGeminiErrorContract(err, { details, googleStatus, message: body.message });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Throw the typed in-band error when `chunk` is an error envelope. */
|
|
178
|
+
export function assertNoGeminiInBandError(chunk, label) {
|
|
179
|
+
const err = geminiInBandError(chunk, label);
|
|
180
|
+
if (err) throw err;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const FINISH_KINDS = new Map(
|
|
184
|
+
Object.entries({
|
|
185
|
+
complete: ['STOP'],
|
|
186
|
+
output_limit: ['MAX_TOKENS'],
|
|
187
|
+
// Content refused by policy: deterministic, never re-asked.
|
|
188
|
+
policy: [
|
|
189
|
+
'SAFETY',
|
|
190
|
+
'BLOCKLIST',
|
|
191
|
+
'PROHIBITED_CONTENT',
|
|
192
|
+
'SPII',
|
|
193
|
+
'IMAGE_SAFETY',
|
|
194
|
+
'IMAGE_PROHIBITED_CONTENT',
|
|
195
|
+
'ESCALATION',
|
|
196
|
+
'PUP_LIMITED_DISABLED',
|
|
197
|
+
],
|
|
198
|
+
language: ['LANGUAGE'],
|
|
199
|
+
recitation: ['RECITATION', 'IMAGE_RECITATION'],
|
|
200
|
+
// The model produced no usable call: the same request is re-asked.
|
|
201
|
+
invalid_tool_call: ['MALFORMED_FUNCTION_CALL', 'UNEXPECTED_TOOL_CALL', 'MALFORMED_RESPONSE'],
|
|
202
|
+
// Tool-call terminals a re-ask cannot fix.
|
|
203
|
+
tool_call: ['TOO_MANY_TOOL_CALLS', 'MISSING_THOUGHT_SIGNATURE'],
|
|
204
|
+
continuation: ['CONTINUATION'],
|
|
205
|
+
other: ['OTHER', 'IMAGE_OTHER', 'NO_IMAGE'],
|
|
206
|
+
}).flatMap(([kind, reasons]) => reasons.map((reason) => [reason, kind]))
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
/** Finish (or `PROMPT_`-prefixed block) reason without wire prefixes. */
|
|
210
|
+
export function geminiNormalizeFinishReason(finishReason) {
|
|
211
|
+
return String(finishReason || '')
|
|
212
|
+
.replace(/^FINISH_REASON_/, '')
|
|
213
|
+
.replace(/^PROMPT_/, '')
|
|
214
|
+
.replace(/^BLOCK_REASON_/, '');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Kind of a documented finishReason / blockReason: 'none' (absent or
|
|
219
|
+
* UNSPECIFIED: the stream is unfinished), 'complete', 'output_limit',
|
|
220
|
+
* 'policy', 'language', 'recitation', 'invalid_tool_call', 'tool_call',
|
|
221
|
+
* 'continuation' or 'other' (OTHER and any undocumented future reason).
|
|
222
|
+
*/
|
|
223
|
+
export function geminiFinishReasonKind(finishReason) {
|
|
224
|
+
const reason = geminiNormalizeFinishReason(finishReason);
|
|
225
|
+
if (!reason || reason === 'UNSPECIFIED') return 'none';
|
|
226
|
+
return FINISH_KINDS.get(reason) || 'other';
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Policy-refusal code for a finishReason, or null for any other reason. */
|
|
230
|
+
export function geminiPolicyFinishCode(finishReason) {
|
|
231
|
+
switch (geminiFinishReasonKind(finishReason)) {
|
|
232
|
+
case 'policy':
|
|
233
|
+
return 'safety';
|
|
234
|
+
case 'recitation':
|
|
235
|
+
return 'recitation';
|
|
236
|
+
case 'language':
|
|
237
|
+
return 'content_filter';
|
|
238
|
+
default:
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Evidence a terminal response carries beyond its finishReason: candidate and
|
|
245
|
+
* prompt safety ratings, the finish message, the prompt block reason and any
|
|
246
|
+
* continuation token. Only present fields are returned.
|
|
247
|
+
*/
|
|
248
|
+
export function geminiFinishErrorFields(response) {
|
|
249
|
+
const candidate = response?.candidates?.[0];
|
|
250
|
+
const feedback = response?.promptFeedback;
|
|
251
|
+
const fields = {
|
|
252
|
+
finishMessage: candidate?.finishMessage,
|
|
253
|
+
safetyRatings: candidate?.safetyRatings,
|
|
254
|
+
blockReason: feedback?.blockReason,
|
|
255
|
+
promptSafetyRatings: feedback?.safetyRatings,
|
|
256
|
+
continuationToken: candidate?.continuationToken,
|
|
257
|
+
};
|
|
258
|
+
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Typed result for a CONTINUATION finish, which this client cannot resume
|
|
263
|
+
* (Gemini direct only). Keeps the original error's partial turn and the
|
|
264
|
+
* token so a caller can continue it out of band.
|
|
265
|
+
*/
|
|
266
|
+
export function geminiContinuationUnsupportedError(incomplete, response) {
|
|
267
|
+
return Object.assign(incomplete, geminiFinishErrorFields(response), {
|
|
268
|
+
name: 'GeminiContinuationUnsupportedError',
|
|
269
|
+
code: 'CONTINUATION_UNSUPPORTED',
|
|
270
|
+
continuationUnsupported: true,
|
|
271
|
+
message: 'Gemini response stopped with finishReason=CONTINUATION; continuation is not supported by this client',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// True for an id in the Gemini wire family ("gemini-4-argon", "models/gemini-3.1-pro",
|
|
2
|
+
// "gemini-pro-agent"). It names the family only, never a generation: it separates
|
|
3
|
+
// Gemini from the other model families an Antigravity gateway serves (Claude,
|
|
4
|
+
// GPT-OSS), which reject Gemini-only request fields outright.
|
|
5
|
+
const GEMINI_FAMILY = /(?:^|\/)gemini-/i;
|
|
6
|
+
|
|
7
|
+
export function isGeminiFamilyModel(model) {
|
|
8
|
+
return GEMINI_FAMILY.test(String(model || ''));
|
|
9
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { isGeminiFamilyModel } from './gemini-family.mjs';
|
|
2
|
+
|
|
1
3
|
const SYNTHETIC_TOOL_SIGNATURE = 'skip_thought_signature_validator';
|
|
2
4
|
|
|
3
|
-
// Gemini
|
|
4
|
-
// Foreign/interrupted histories may not have
|
|
5
|
-
//
|
|
5
|
+
// Gemini models that validate thought signatures check the first functionCall
|
|
6
|
+
// in every step of the current turn. Foreign/interrupted histories may not have
|
|
7
|
+
// a native signature for that call. Preserve real signatures and unsigned
|
|
8
|
+
// siblings of a signed parallel batch. The validator-bypass token is a Gemini
|
|
9
|
+
// wire token: it is only fabricated for the Gemini family, because the other
|
|
10
|
+
// families an Antigravity gateway serves reject it.
|
|
6
11
|
export function ensureGeminiToolCallSignatures(contents, model) {
|
|
7
|
-
if (
|
|
12
|
+
if (!isGeminiFamilyModel(model)) return contents;
|
|
8
13
|
let activeTurnStart = 0;
|
|
9
14
|
for (let index = contents.length - 1; index >= 0; index -= 1) {
|
|
10
15
|
const content = contents[index];
|
|
@@ -12,6 +12,30 @@ import { geminiThinkingConfig } from './gemini-thinking.mjs';
|
|
|
12
12
|
import { toGeminiContents, toGeminiRequestTools, toGeminiToolConfig } from './gemini-schema.mjs';
|
|
13
13
|
|
|
14
14
|
// Request pieces shared by the cached REST path and the SDK path.
|
|
15
|
+
// Caller-supplied generation controls the wire carries in generationConfig.
|
|
16
|
+
// maxOutputTokens counts thought tokens too (see the thinking docs). Anything
|
|
17
|
+
// invalid is rejected here instead of being dropped. (safetySettings is a
|
|
18
|
+
// request-level field, forwarded separately.)
|
|
19
|
+
function geminiGenerationControls(opts) {
|
|
20
|
+
const controls = {};
|
|
21
|
+
const maxRaw = opts.maxOutputTokens ?? opts.max_output_tokens;
|
|
22
|
+
if (maxRaw !== undefined && maxRaw !== null) {
|
|
23
|
+
const max = Number(maxRaw);
|
|
24
|
+
if (!Number.isInteger(max) || max <= 0) {
|
|
25
|
+
throw new TypeError('maxOutputTokens must be a positive integer (it includes thinking tokens).');
|
|
26
|
+
}
|
|
27
|
+
controls.maxOutputTokens = max;
|
|
28
|
+
}
|
|
29
|
+
if (opts.temperature !== undefined && opts.temperature !== null) {
|
|
30
|
+
const temperature = Number(opts.temperature);
|
|
31
|
+
if (!Number.isFinite(temperature) || temperature < 0 || temperature > 2) {
|
|
32
|
+
throw new TypeError('temperature must be a number between 0 and 2.');
|
|
33
|
+
}
|
|
34
|
+
controls.temperature = temperature;
|
|
35
|
+
}
|
|
36
|
+
return controls;
|
|
37
|
+
}
|
|
38
|
+
|
|
15
39
|
export function buildGeminiRequest(messages, useModel, tools, opts) {
|
|
16
40
|
// Gemini returns thought summaries only when the request asks for them.
|
|
17
41
|
// Without this the reasoning channel stays empty for the whole turn and
|
|
@@ -19,7 +43,11 @@ export function buildGeminiRequest(messages, useModel, tools, opts) {
|
|
|
19
43
|
// round reads as another preamble. On by default for every model; an
|
|
20
44
|
// explicit opts.includeThoughts still wins.
|
|
21
45
|
const thinkingConfig = geminiThinkingConfig(useModel, opts, { includeThoughts: true });
|
|
22
|
-
const
|
|
46
|
+
const controls = geminiGenerationControls(opts);
|
|
47
|
+
const generationConfig =
|
|
48
|
+
thinkingConfig || Object.keys(controls).length
|
|
49
|
+
? { ...(thinkingConfig ? { thinkingConfig } : {}), ...controls }
|
|
50
|
+
: undefined;
|
|
23
51
|
const systemInstruction =
|
|
24
52
|
messages
|
|
25
53
|
.filter((m) => m.role === 'system')
|
|
@@ -29,17 +57,24 @@ export function buildGeminiRequest(messages, useModel, tools, opts) {
|
|
|
29
57
|
const contents = toGeminiContents(chatMsgs, useModel);
|
|
30
58
|
if (!contents.length) throw new Error('No messages to send');
|
|
31
59
|
|
|
32
|
-
const requestTools = toGeminiRequestTools(tools, opts.nativeTools);
|
|
60
|
+
const requestTools = toGeminiRequestTools(tools, opts.nativeTools, { onToolExcluded: opts.onToolExcluded });
|
|
33
61
|
const geminiTools = requestTools.length ? requestTools : undefined;
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
// Tool config only accompanies function declarations that were kept.
|
|
63
|
+
const toolConfig = requestTools.some((entry) => entry.functionDeclarations?.length)
|
|
64
|
+
? toGeminiToolConfig(opts.toolChoice)
|
|
65
|
+
: undefined;
|
|
66
|
+
if (opts.safetySettings !== undefined && opts.safetySettings !== null && !Array.isArray(opts.safetySettings)) {
|
|
67
|
+
throw new TypeError('safetySettings must be an array of { category, threshold } entries.');
|
|
68
|
+
}
|
|
69
|
+
const safetySettings = opts.safetySettings?.length ? opts.safetySettings : undefined;
|
|
70
|
+
return { generationConfig, systemInstruction, contents, geminiTools, toolConfig, safetySettings };
|
|
36
71
|
}
|
|
37
72
|
|
|
38
73
|
// The generateContent body for a cached prefix. The cache carries the
|
|
39
74
|
// recorded prefix; every uncached tail turn is sent, not just the last
|
|
40
75
|
// message, so reused cachedContents preserve full conversation context
|
|
41
76
|
// between periodic refreshes.
|
|
42
|
-
export function geminiCachedRestBody({ opts, contents, cachedContent, generationConfig }) {
|
|
77
|
+
export function geminiCachedRestBody({ opts, contents, cachedContent, generationConfig, safetySettings }) {
|
|
43
78
|
const cachedPrefixContentCount = Number.isFinite(Number(opts.providerState?.gemini?.cachePrefixContentCount))
|
|
44
79
|
? Math.max(0, Math.min(contents.length, Math.trunc(Number(opts.providerState.gemini.cachePrefixContentCount))))
|
|
45
80
|
: 0;
|
|
@@ -48,5 +83,6 @@ export function geminiCachedRestBody({ opts, contents, cachedContent, generation
|
|
|
48
83
|
contents: deltaContents.length ? deltaContents : contents.slice(-1),
|
|
49
84
|
cachedContent,
|
|
50
85
|
...(generationConfig ? { generationConfig } : {}),
|
|
86
|
+
...(safetySettings ? { safetySettings } : {}),
|
|
51
87
|
};
|
|
52
88
|
}
|
|
@@ -14,6 +14,7 @@ import { createProviderReplay } from './lib/provider-replay.mjs';
|
|
|
14
14
|
import { collectGeminiGroundingSources, parseGeminiTextPartMetadata, parseToolCalls } from './gemini-schema.mjs';
|
|
15
15
|
import { _resolveGeminiCacheUsage } from './gemini-cache.mjs';
|
|
16
16
|
import { traceGeminiCache } from './gemini-cache-policy.mjs';
|
|
17
|
+
import { geminiPolicyFinishCode } from './gemini-error-contract.mjs';
|
|
17
18
|
|
|
18
19
|
// Candidate text and tool calls after the text-leak guard: leaked
|
|
19
20
|
// (text-embedded) tool calls are appended to the native ones and empty the
|
|
@@ -64,7 +65,8 @@ export function geminiIncompleteError(response, parsed, useModel, usage = null)
|
|
|
64
65
|
if (!finishReason || normalizedFinishReason === 'STOP') return null;
|
|
65
66
|
return Object.assign(new Error(`Gemini response incomplete: finishReason=${finishReason}`), {
|
|
66
67
|
name: 'ProviderIncompleteError',
|
|
67
|
-
|
|
68
|
+
// Safety/recitation are policy refusals the core presents as such.
|
|
69
|
+
code: geminiPolicyFinishCode(finishReason) || 'PROVIDER_INCOMPLETE',
|
|
68
70
|
providerIncomplete: true,
|
|
69
71
|
finishReason,
|
|
70
72
|
partialContent: parsed.content,
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gemini-retry-policy.mjs — Gemini API retry constants and the two-phase
|
|
3
|
+
* attempt loop (request backoff and stream retry):
|
|
4
|
+
*
|
|
5
|
+
* connection phase up to 10 attempts, 5s initial delay doubling to a 30s
|
|
6
|
+
* cap, ±30% jitter (a server-advised wait replaces it);
|
|
7
|
+
* stream phase a failure after the response opened (a stream that ends
|
|
8
|
+
* with no finish reason is one: the transport raises it as
|
|
9
|
+
* a truncated stream), or a response the model left
|
|
10
|
+
* unusable (malformed / unexpected tool call, empty text),
|
|
11
|
+
* re-issues the whole request up to 4 attempts total at
|
|
12
|
+
* 1s / 2s / 4s. A response that finished with no finish
|
|
13
|
+
* reason is never judged here, and right after a tool
|
|
14
|
+
* result an empty STOP answer is accepted as the model's
|
|
15
|
+
* answer (an empty stream with no finish reason is still
|
|
16
|
+
* unfinished). An empty answer that
|
|
17
|
+
* survives all 4 attempts throws a typed, provider-terminal
|
|
18
|
+
* invalid-stream error instead of returning nothing.
|
|
19
|
+
* long advice a server wait over 300s ends recovery on both phases
|
|
20
|
+
* (provider-terminal, no quota scope; the wait stays on
|
|
21
|
+
* the error for display).
|
|
22
|
+
*
|
|
23
|
+
* Replay stays gated by the stream-outcome contract: a stream that exposed
|
|
24
|
+
* text or dispatched a tool is never re-issued.
|
|
25
|
+
*/
|
|
26
|
+
import { readStreamOutcome } from './lib/stream-outcome.mjs';
|
|
27
|
+
import { DETERMINISTIC_KINDS, providerErrorKind, typedCodeKind } from './provider-error-kind.mjs';
|
|
28
|
+
import { midstreamRetryDelayMs } from './retry-backoff.mjs';
|
|
29
|
+
import { geminiFinishReasonKind, geminiNormalizeFinishReason } from './gemini-error-contract.mjs';
|
|
30
|
+
import { classifyError, markProviderRecoveryExhausted, sleepWithAbort, withRetry } from './retry-classifier.mjs';
|
|
31
|
+
|
|
32
|
+
export const GEMINI_REQUEST_RETRY = Object.freeze({
|
|
33
|
+
maxAttempts: 10,
|
|
34
|
+
// Wait before retry N reads index N (the leading 5s covers index 0 and 1):
|
|
35
|
+
// 5s, 10s, 20s, then the 30s cap.
|
|
36
|
+
backoffMs: Object.freeze([5000, 5000, 10_000, 20_000, 30_000]),
|
|
37
|
+
retryJitterRatio: 0.3,
|
|
38
|
+
// Server advice gets positive-only jitter and is never below the step.
|
|
39
|
+
retryAfterJitterRatio: 0.2,
|
|
40
|
+
adviceAtLeastBackoff: true,
|
|
41
|
+
retry499: true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// A server wait longer than this is a lock-out, not a retry.
|
|
45
|
+
export const GEMINI_MAX_RETRYABLE_ADVICE_MS = 300_000;
|
|
46
|
+
|
|
47
|
+
function hasLongAdvice(err) {
|
|
48
|
+
const { kind, retryAfterMs } = providerErrorKind(err);
|
|
49
|
+
return retryAfterMs > GEMINI_MAX_RETRYABLE_ADVICE_MS && !DETERMINISTIC_KINDS.has(kind);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** True when the request's last turn is a tool result (functionResponse). */
|
|
53
|
+
export function geminiEndsWithFunctionResponse(contents) {
|
|
54
|
+
const last = Array.isArray(contents) ? contents[contents.length - 1] : null;
|
|
55
|
+
return Array.isArray(last?.parts) && last.parts.some((part) => part?.functionResponse);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const GEMINI_MIDSTREAM_MAX_ATTEMPTS = 4;
|
|
59
|
+
export const GEMINI_MIDSTREAM_INITIAL_DELAY_MS = 1000;
|
|
60
|
+
|
|
61
|
+
const midstreamStepMs = (streamAttempt) => GEMINI_MIDSTREAM_INITIAL_DELAY_MS * 2 ** streamAttempt;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Wait before re-issuing after a stream-phase failure, or null when it must
|
|
65
|
+
* surface. A server-advised wait on a retryable kind replaces the step.
|
|
66
|
+
*/
|
|
67
|
+
export function geminiMidstreamDelayMs(err, streamAttempt, signal = null) {
|
|
68
|
+
if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS || signal?.aborted) return null;
|
|
69
|
+
if (readStreamOutcome(err).replaySafe !== true) return null;
|
|
70
|
+
const { kind } = providerErrorKind(err);
|
|
71
|
+
if (hasLongAdvice(err)) return null;
|
|
72
|
+
// A status-only 499 (client closed request) is retried, as on the request
|
|
73
|
+
// ladder; a typed code on the 499 keeps it final.
|
|
74
|
+
const closed499 =
|
|
75
|
+
GEMINI_REQUEST_RETRY.retry499 && kind === 'invalid_request' && statusOf(err) === 499 && !typedCodeKind(err);
|
|
76
|
+
if (DETERMINISTIC_KINDS.has(kind) && !closed499) return null;
|
|
77
|
+
// 429 is request-local here: classifyError calls it permanent, but a rate
|
|
78
|
+
// limit that is not a spent quota is retried like any other stream fault.
|
|
79
|
+
if (!closed499 && classifyError(err) !== 'transient' && kind !== 'rate_limited') return null;
|
|
80
|
+
return midstreamRetryDelayMs(err, midstreamStepMs(streamAttempt));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const statusOf = (err) => Number(err?.httpStatus || err?.status || 0);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Invalid stream: the reason a finished response is unusable
|
|
87
|
+
* ('MALFORMED_FUNCTION_CALL' | 'UNEXPECTED_TOOL_CALL' | 'MALFORMED_RESPONSE' |
|
|
88
|
+
* 'NO_RESPONSE_TEXT'), or null. A response that carried a tool call, or whose
|
|
89
|
+
* text was already relayed, is never invalid (and never replayed). Only a
|
|
90
|
+
* STOP with no text is "no text"; every other documented finish (policy,
|
|
91
|
+
* output limit, continuation, OTHER, ...) is owned by the incomplete handling.
|
|
92
|
+
*/
|
|
93
|
+
export function geminiInvalidStreamReason(response, textLeakGuard = null, { afterToolResult = false } = {}) {
|
|
94
|
+
const candidate = response?.candidates?.[0];
|
|
95
|
+
const parts = Array.isArray(candidate?.content?.parts) ? candidate.content.parts : [];
|
|
96
|
+
if (parts.some((part) => part?.functionCall)) return null;
|
|
97
|
+
if ((textLeakGuard?.getLeakedToolCalls?.() || []).length > 0) return null;
|
|
98
|
+
if ((textLeakGuard?.getRelayedText?.() || '').length > 0) return null;
|
|
99
|
+
const finish = geminiNormalizeFinishReason(candidate?.finishReason);
|
|
100
|
+
const kind = geminiFinishReasonKind(finish);
|
|
101
|
+
if (kind === 'invalid_tool_call') return finish;
|
|
102
|
+
if (kind !== 'complete') return null;
|
|
103
|
+
// Right after a tool result the model may legitimately answer with nothing.
|
|
104
|
+
if (afterToolResult) return null;
|
|
105
|
+
const text = parts
|
|
106
|
+
.filter((part) => part?.thought !== true && typeof part?.text === 'string')
|
|
107
|
+
.map((part) => part.text)
|
|
108
|
+
.join('')
|
|
109
|
+
.replace(/[\u200B-\u200D\uFEFF\u200E\u200F]/g, '')
|
|
110
|
+
.trim();
|
|
111
|
+
return text ? null : 'NO_RESPONSE_TEXT';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Run `run` under the Gemini ladder. `run({ signal, connected })` performs one
|
|
116
|
+
* open + consume and calls `connected()` once the response opened; it resolves
|
|
117
|
+
* `{ response, textLeakGuard }`.
|
|
118
|
+
*/
|
|
119
|
+
export async function runGeminiStreamAttempts({ run, signal, onRetry, sleepFn, afterToolResult = false }) {
|
|
120
|
+
for (let streamAttempt = 0; ; streamAttempt++) {
|
|
121
|
+
let connected = false;
|
|
122
|
+
let result;
|
|
123
|
+
try {
|
|
124
|
+
result = await withRetry(
|
|
125
|
+
async ({ signal: attemptSignal }) => {
|
|
126
|
+
try {
|
|
127
|
+
return await run({
|
|
128
|
+
signal: attemptSignal,
|
|
129
|
+
connected: () => {
|
|
130
|
+
connected = true;
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
} catch (err) {
|
|
134
|
+
// A server wait over the limit ends recovery on either phase.
|
|
135
|
+
if (hasLongAdvice(err)) {
|
|
136
|
+
throw markProviderRecoveryExhausted(err, {
|
|
137
|
+
owner: connected ? 'gemini-stream' : 'gemini-connection',
|
|
138
|
+
terminal: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// The connection ladder is spent on opening; a stream-phase
|
|
142
|
+
// failure belongs to the stream ladder below.
|
|
143
|
+
throw connected ? markProviderRecoveryExhausted(err, { owner: 'gemini-connection' }) : err;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{ ...GEMINI_REQUEST_RETRY, finalOnExhaust: true, signal, sleepFn, onRetry }
|
|
147
|
+
);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
const waitMs = connected ? geminiMidstreamDelayMs(err, streamAttempt, signal) : null;
|
|
150
|
+
if (waitMs == null) throw err;
|
|
151
|
+
onRetry?.({ attempt: streamAttempt + 1, maxAttempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS, lastErr: err, delayMs: waitMs });
|
|
152
|
+
await sleepWithAbort(waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const invalid = geminiInvalidStreamReason(result.response, result.textLeakGuard, { afterToolResult });
|
|
156
|
+
if (!invalid || signal?.aborted) return result;
|
|
157
|
+
const unusable = Object.assign(new Error(`Gemini stream unusable: ${invalid}`), {
|
|
158
|
+
name: 'InvalidStreamError',
|
|
159
|
+
code: invalid.toLowerCase(),
|
|
160
|
+
});
|
|
161
|
+
if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS) {
|
|
162
|
+
// A spent ladder on an empty answer is a failure, not an answer; a
|
|
163
|
+
// malformed / unexpected tool call keeps its finish-reason error.
|
|
164
|
+
if (invalid !== 'NO_RESPONSE_TEXT') return result;
|
|
165
|
+
throw markProviderRecoveryExhausted(unusable, {
|
|
166
|
+
owner: 'gemini-stream',
|
|
167
|
+
attempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
|
|
168
|
+
terminal: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const waitMs = midstreamStepMs(streamAttempt);
|
|
172
|
+
onRetry?.({
|
|
173
|
+
attempt: streamAttempt + 1,
|
|
174
|
+
maxAttempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
|
|
175
|
+
lastErr: unusable,
|
|
176
|
+
delayMs: waitMs,
|
|
177
|
+
});
|
|
178
|
+
await sleepWithAbort(waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
|
|
179
|
+
}
|
|
180
|
+
}
|