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
|
@@ -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 response (no finish reason or no text)
|
|
15
|
+
* is accepted as the model's answer. An empty answer that
|
|
16
|
+
* survives all 4 attempts throws a typed, provider-terminal
|
|
17
|
+
* invalid-stream error instead of returning nothing.
|
|
18
|
+
* long advice a server wait over 300s ends recovery on both phases
|
|
19
|
+
* (provider-terminal, no quota scope; the wait stays on
|
|
20
|
+
* the error for display).
|
|
21
|
+
*
|
|
22
|
+
* Replay stays gated by the stream-outcome contract: a stream that exposed
|
|
23
|
+
* text or dispatched a tool is never re-issued.
|
|
24
|
+
*/
|
|
25
|
+
import { readStreamOutcome } from './lib/stream-outcome.mjs';
|
|
26
|
+
import { DETERMINISTIC_KINDS, providerErrorKind, typedCodeKind } from './provider-error-kind.mjs';
|
|
27
|
+
import { midstreamRetryDelayMs } from './retry-backoff.mjs';
|
|
28
|
+
import { classifyError, markProviderRecoveryExhausted, sleepWithAbort, withRetry } from './retry-classifier.mjs';
|
|
29
|
+
|
|
30
|
+
export const GEMINI_REQUEST_RETRY = Object.freeze({
|
|
31
|
+
maxAttempts: 10,
|
|
32
|
+
// Wait before retry N reads index N (the leading 5s covers index 0 and 1):
|
|
33
|
+
// 5s, 10s, 20s, then the 30s cap.
|
|
34
|
+
backoffMs: Object.freeze([5000, 5000, 10_000, 20_000, 30_000]),
|
|
35
|
+
retryJitterRatio: 0.3,
|
|
36
|
+
// Server advice gets positive-only jitter and is never below the step.
|
|
37
|
+
retryAfterJitterRatio: 0.2,
|
|
38
|
+
adviceAtLeastBackoff: true,
|
|
39
|
+
retry499: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// A server wait longer than this is a lock-out, not a retry.
|
|
43
|
+
export const GEMINI_MAX_RETRYABLE_ADVICE_MS = 300_000;
|
|
44
|
+
|
|
45
|
+
function hasLongAdvice(err) {
|
|
46
|
+
const { kind, retryAfterMs } = providerErrorKind(err);
|
|
47
|
+
return retryAfterMs > GEMINI_MAX_RETRYABLE_ADVICE_MS && !DETERMINISTIC_KINDS.has(kind);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** True when the request's last turn is a tool result (functionResponse). */
|
|
51
|
+
export function geminiEndsWithFunctionResponse(contents) {
|
|
52
|
+
const last = Array.isArray(contents) ? contents[contents.length - 1] : null;
|
|
53
|
+
return Array.isArray(last?.parts) && last.parts.some((part) => part?.functionResponse);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const GEMINI_MIDSTREAM_MAX_ATTEMPTS = 4;
|
|
57
|
+
export const GEMINI_MIDSTREAM_INITIAL_DELAY_MS = 1000;
|
|
58
|
+
|
|
59
|
+
const midstreamStepMs = (streamAttempt) => GEMINI_MIDSTREAM_INITIAL_DELAY_MS * 2 ** streamAttempt;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Wait before re-issuing after a stream-phase failure, or null when it must
|
|
63
|
+
* surface. A server-advised wait on a retryable kind replaces the step.
|
|
64
|
+
*/
|
|
65
|
+
export function geminiMidstreamDelayMs(err, streamAttempt, signal = null) {
|
|
66
|
+
if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS || signal?.aborted) return null;
|
|
67
|
+
if (readStreamOutcome(err).replaySafe !== true) return null;
|
|
68
|
+
const { kind } = providerErrorKind(err);
|
|
69
|
+
if (hasLongAdvice(err)) return null;
|
|
70
|
+
// A status-only 499 (client closed request) is retried, as on the request
|
|
71
|
+
// ladder; a typed code on the 499 keeps it final.
|
|
72
|
+
const closed499 =
|
|
73
|
+
GEMINI_REQUEST_RETRY.retry499 && kind === 'invalid_request' && statusOf(err) === 499 && !typedCodeKind(err);
|
|
74
|
+
if (DETERMINISTIC_KINDS.has(kind) && !closed499) return null;
|
|
75
|
+
// 429 is request-local here: classifyError calls it permanent, but a rate
|
|
76
|
+
// limit that is not a spent quota is retried like any other stream fault.
|
|
77
|
+
if (!closed499 && classifyError(err) !== 'transient' && kind !== 'rate_limited') return null;
|
|
78
|
+
return midstreamRetryDelayMs(err, midstreamStepMs(streamAttempt));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const statusOf = (err) => Number(err?.httpStatus || err?.status || 0);
|
|
82
|
+
|
|
83
|
+
const INVALID_TOOL_CALL_FINISH = new Set(['MALFORMED_FUNCTION_CALL', 'UNEXPECTED_TOOL_CALL']);
|
|
84
|
+
// Empty-text finishes that are deterministic refusals or output-limit stops,
|
|
85
|
+
// owned by the policy / incomplete handling instead of a re-ask.
|
|
86
|
+
const EMPTY_TEXT_TERMINAL_FINISH = new Set(['MAX_TOKENS', 'SAFETY', 'RECITATION']);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Invalid stream: the reason a finished response is unusable
|
|
90
|
+
* ('MALFORMED_FUNCTION_CALL' | 'UNEXPECTED_TOOL_CALL' | 'NO_RESPONSE_TEXT'),
|
|
91
|
+
* or null. A response that carried a tool call, or whose text was already
|
|
92
|
+
* relayed, is never invalid (and never replayed).
|
|
93
|
+
*/
|
|
94
|
+
export function geminiInvalidStreamReason(response, textLeakGuard = null, { afterToolResult = false } = {}) {
|
|
95
|
+
const candidate = response?.candidates?.[0];
|
|
96
|
+
const parts = Array.isArray(candidate?.content?.parts) ? candidate.content.parts : [];
|
|
97
|
+
if (parts.some((part) => part?.functionCall)) return null;
|
|
98
|
+
if ((textLeakGuard?.getLeakedToolCalls?.() || []).length > 0) return null;
|
|
99
|
+
if ((textLeakGuard?.getRelayedText?.() || '').length > 0) return null;
|
|
100
|
+
const finish = String(candidate?.finishReason || '').replace(/^FINISH_REASON_/, '');
|
|
101
|
+
if (INVALID_TOOL_CALL_FINISH.has(finish)) return finish;
|
|
102
|
+
if (!finish || EMPTY_TEXT_TERMINAL_FINISH.has(finish)) 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
|
+
}
|
|
@@ -66,49 +66,6 @@ function splitGeminiToolContent(content) {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const GEMINI_FUNCTION_RESPONSE_MIME = /^(?:image\/(?:png|jpeg|webp)|application\/pdf|text\/plain)$/i;
|
|
70
|
-
|
|
71
|
-
function geminiFunctionCapabilities(model) {
|
|
72
|
-
const match = String(model || '')
|
|
73
|
-
.toLowerCase()
|
|
74
|
-
.match(/(?:^|\/)gemini(?:-live)?-(\d+)/);
|
|
75
|
-
const major = match ? Number.parseInt(match[1], 10) : null;
|
|
76
|
-
const modern = major == null || major >= 3;
|
|
77
|
-
return {
|
|
78
|
-
functionPartIds: modern,
|
|
79
|
-
multimodalFunctionResponse: modern,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function toGeminiFunctionResponseMedia(mediaParts) {
|
|
84
|
-
const parts = [];
|
|
85
|
-
const refs = [];
|
|
86
|
-
const external = [];
|
|
87
|
-
const omitted = [];
|
|
88
|
-
for (const media of mediaParts || []) {
|
|
89
|
-
const payload = media?.inlineData || media?.fileData;
|
|
90
|
-
const mimeType = String(payload?.mimeType || '').trim();
|
|
91
|
-
if (!GEMINI_FUNCTION_RESPONSE_MIME.test(mimeType)) {
|
|
92
|
-
if (mimeType) omitted.push(mimeType);
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
const displayName = `tool_media_${parts.length + 1}`;
|
|
96
|
-
if (media.inlineData) {
|
|
97
|
-
parts.push({ inlineData: { ...media.inlineData, mimeType, displayName } });
|
|
98
|
-
refs.push({ $ref: displayName });
|
|
99
|
-
} else {
|
|
100
|
-
// FunctionResponse.parts currently documents inlineData only.
|
|
101
|
-
// fileData is valid in ordinary Content parts but can 400 when
|
|
102
|
-
// nested here, so retain file-backed media as plain JSON metadata.
|
|
103
|
-
external.push({
|
|
104
|
-
mimeType,
|
|
105
|
-
fileUri: String(media.fileData?.fileUri || ''),
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return { parts, refs, external, omitted };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
69
|
/**
|
|
113
70
|
* Convert JSON Schema type string to Gemini SchemaType.
|
|
114
71
|
* Gemini SDK uses its own enum instead of plain strings.
|
|
@@ -647,27 +604,18 @@ export function toGeminiToolConfig(toolChoice) {
|
|
|
647
604
|
return undefined;
|
|
648
605
|
}
|
|
649
606
|
|
|
650
|
-
|
|
607
|
+
// One tool result: its functionResponse part, plus any media it carried as
|
|
608
|
+
// ordinary content parts. The ids pair a call with its response (the Claude
|
|
609
|
+
// models behind Antigravity require them) and are harmless for Gemini.
|
|
610
|
+
function toGeminiToolResult(message, toolNameByCallId) {
|
|
651
611
|
const functionName = toolNameByCallId?.get(message.toolCallId) || message.toolCallId || '';
|
|
652
612
|
const { response, mediaParts } = splitGeminiToolContent(message.content);
|
|
653
|
-
const media = toGeminiFunctionResponseMedia(mediaParts);
|
|
654
|
-
if (media.refs.length) response.media = media.refs;
|
|
655
|
-
if (media.external.length) response.externalMedia = media.external;
|
|
656
|
-
if (media.omitted.length) response.omittedMediaTypes = media.omitted;
|
|
657
613
|
const functionResponse = { name: functionName, response };
|
|
658
|
-
if (
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
if (capabilities.multimodalFunctionResponse && media.parts.length) {
|
|
662
|
-
functionResponse.parts = media.parts;
|
|
663
|
-
}
|
|
664
|
-
return {
|
|
665
|
-
content: { role: 'user', parts: [{ functionResponse }] },
|
|
666
|
-
trailingMediaParts: capabilities.multimodalFunctionResponse ? [] : media.parts,
|
|
667
|
-
};
|
|
614
|
+
if (typeof message.toolCallId === 'string' && message.toolCallId) functionResponse.id = message.toolCallId;
|
|
615
|
+
return { functionResponsePart: { functionResponse }, mediaParts };
|
|
668
616
|
}
|
|
669
617
|
|
|
670
|
-
function toGeminiContent(message
|
|
618
|
+
function toGeminiContent(message) {
|
|
671
619
|
if (!message || message.role === 'system') return null;
|
|
672
620
|
const orderedReplay =
|
|
673
621
|
message.role === 'assistant' ? providerReplayItems(message, ['gemini', 'antigravity']) : undefined;
|
|
@@ -689,7 +637,7 @@ function toGeminiContent(message, capabilities) {
|
|
|
689
637
|
functionCall: {
|
|
690
638
|
name: tc.name,
|
|
691
639
|
args: tc.arguments,
|
|
692
|
-
...(
|
|
640
|
+
...(typeof tc.id === 'string' && tc.id ? { id: tc.id } : {}),
|
|
693
641
|
},
|
|
694
642
|
};
|
|
695
643
|
if (
|
|
@@ -765,7 +713,6 @@ function geminiThoughtPartsFromMetadata(message) {
|
|
|
765
713
|
|
|
766
714
|
export function toGeminiContents(messages, model = '', { repairToolSignatures = true } = {}) {
|
|
767
715
|
const contents = [];
|
|
768
|
-
const capabilities = geminiFunctionCapabilities(model);
|
|
769
716
|
// Map synthetic toolCallId -> function name from prior assistant
|
|
770
717
|
// tool_calls so each functionResponse part carries the real function name.
|
|
771
718
|
const toolNameByCallId = new Map();
|
|
@@ -776,34 +723,26 @@ export function toGeminiContents(messages, model = '', { repairToolSignatures =
|
|
|
776
723
|
}
|
|
777
724
|
}
|
|
778
725
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
pendingToolMediaParts = [];
|
|
784
|
-
};
|
|
726
|
+
// Consecutive tool results share one user turn: every functionResponse part
|
|
727
|
+
// first (one per call, in call order), then the media the results carried as
|
|
728
|
+
// sibling inlineData/fileData parts.
|
|
729
|
+
let toolTurn = null;
|
|
785
730
|
for (const message of messages) {
|
|
786
731
|
if (message?.role === 'tool') {
|
|
787
|
-
const {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
Array.isArray(last.parts) &&
|
|
792
|
-
last.parts.length &&
|
|
793
|
-
last.parts.every((part) => part?.functionResponse)
|
|
794
|
-
) {
|
|
795
|
-
last.parts.push(...content.parts);
|
|
796
|
-
} else {
|
|
797
|
-
contents.push(content);
|
|
732
|
+
const { functionResponsePart, mediaParts } = toGeminiToolResult(message, toolNameByCallId);
|
|
733
|
+
if (!toolTurn) {
|
|
734
|
+
toolTurn = { content: { role: 'user', parts: [] }, responseCount: 0 };
|
|
735
|
+
contents.push(toolTurn.content);
|
|
798
736
|
}
|
|
799
|
-
|
|
737
|
+
toolTurn.content.parts.splice(toolTurn.responseCount, 0, functionResponsePart);
|
|
738
|
+
toolTurn.responseCount += 1;
|
|
739
|
+
toolTurn.content.parts.push(...mediaParts);
|
|
800
740
|
continue;
|
|
801
741
|
}
|
|
802
|
-
|
|
803
|
-
const content = toGeminiContent(message
|
|
742
|
+
toolTurn = null;
|
|
743
|
+
const content = toGeminiContent(message);
|
|
804
744
|
if (content) contents.push(content);
|
|
805
745
|
}
|
|
806
|
-
flushToolMedia();
|
|
807
746
|
return repairToolSignatures ? ensureGeminiToolCallSignatures(contents, model) : contents;
|
|
808
747
|
}
|
|
809
748
|
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
normalizeGeminiSdkError,
|
|
14
14
|
} from './gemini-stream.mjs';
|
|
15
15
|
import { geminiFailureUsage } from './gemini-response.mjs';
|
|
16
|
+
import { geminiEndsWithFunctionResponse } from './gemini-retry-policy.mjs';
|
|
16
17
|
|
|
17
18
|
export function geminiTextLeakGuardFor({ tools, callbacks }) {
|
|
18
19
|
return createGeminiTextLeakGuard({
|
|
@@ -71,7 +72,7 @@ function linkSdkRequestController(attemptSignal) {
|
|
|
71
72
|
return { controller, detachParent, clearConnectTimer };
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
export async function streamGeminiSdkAttempt(genModel, stream, attemptSignal) {
|
|
75
|
+
export async function streamGeminiSdkAttempt(genModel, stream, attemptSignal, onConnected = null) {
|
|
75
76
|
const { contents, callbacks } = stream;
|
|
76
77
|
const link = linkSdkRequestController(attemptSignal);
|
|
77
78
|
const { controller } = link;
|
|
@@ -89,6 +90,7 @@ export async function streamGeminiSdkAttempt(genModel, stream, attemptSignal) {
|
|
|
89
90
|
// the parent link attached so a later abort during streaming still
|
|
90
91
|
// reaches the request.
|
|
91
92
|
link.clearConnectTimer();
|
|
93
|
+
onConnected?.();
|
|
92
94
|
const textLeakGuard = geminiTextLeakGuardFor(stream);
|
|
93
95
|
const response = await consumeGeminiSdkStream(streamResult, {
|
|
94
96
|
signal: attemptSignal,
|
|
@@ -96,6 +98,7 @@ export async function streamGeminiSdkAttempt(genModel, stream, attemptSignal) {
|
|
|
96
98
|
onTextDelta: callbacks.onTextDelta,
|
|
97
99
|
textLeakGuard,
|
|
98
100
|
label: 'Gemini SDK streamGenerateContent',
|
|
101
|
+
acceptEmptyNoFinish: geminiEndsWithFunctionResponse(contents),
|
|
99
102
|
failureUsage: geminiFailureUsage(stream.opts, stream.cachedContent, stream.useModel),
|
|
100
103
|
cancelGeneration: (reason) => {
|
|
101
104
|
if (!controller.signal.aborted) controller.abort(reason);
|
|
@@ -26,6 +26,7 @@ import { createSdkStreamReader } from './gemini-sdk-stream/reader.mjs';
|
|
|
26
26
|
import { createRestStreamWatchdogs } from './gemini-rest-stream/watchdogs.mjs';
|
|
27
27
|
import { drainSseDataLines, sseDataPayload } from './gemini-rest-stream/sse-lines.mjs';
|
|
28
28
|
import { noteAbandonedUsage } from '../../../shared/llm/usage-context.mjs';
|
|
29
|
+
import { applyGeminiErrorContract, assertNoGeminiInBandError } from './gemini-error-contract.mjs';
|
|
29
30
|
|
|
30
31
|
export const GEMINI_FIRST_BYTE_TIMEOUT_MS = resolveTimeoutMs(
|
|
31
32
|
'MIXDOG_GEMINI_FIRST_BYTE_TIMEOUT_MS',
|
|
@@ -132,9 +133,16 @@ export function normalizeGeminiSdkError(err, label) {
|
|
|
132
133
|
const transport = geminiSdkTransportError(err, label);
|
|
133
134
|
if (transport) return transport;
|
|
134
135
|
const stamped = stampGeminiRpcError(err);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
if (isGeminiSdkStreamParseError(stamped)) {
|
|
137
|
+
return geminiStreamCorruptionError(`${label} corrupt SDK SSE JSON`, stamped);
|
|
138
|
+
}
|
|
139
|
+
if (!stamped || typeof stamped !== 'object') return stamped;
|
|
140
|
+
return applyGeminiErrorContract(stamped, {
|
|
141
|
+
details: stamped.details,
|
|
142
|
+
errorDetails: stamped.errorDetails,
|
|
143
|
+
googleStatus: stamped.geminiStatus,
|
|
144
|
+
message: stamped.message,
|
|
145
|
+
});
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
// CC-rule safety stamp for Gemini stream failures (provider-stall audit):
|
|
@@ -225,7 +233,10 @@ export function geminiChunkProgressKind(chunk) {
|
|
|
225
233
|
* (same shape as a non-streaming generateContent JSON body).
|
|
226
234
|
* Mirrors @google/generative-ai aggregateResponses().
|
|
227
235
|
*/
|
|
228
|
-
function aggregateGeminiStreamChunks(responses) {
|
|
236
|
+
function aggregateGeminiStreamChunks(responses, label = 'Gemini stream') {
|
|
237
|
+
// An error envelope anywhere in the stream — including after STOP — fails
|
|
238
|
+
// the turn instead of aggregating into a success.
|
|
239
|
+
for (const response of responses) assertNoGeminiInBandError(response, label);
|
|
229
240
|
const lastResponse = responses[responses.length - 1];
|
|
230
241
|
const aggregatedResponse = {
|
|
231
242
|
promptFeedback: lastResponse?.promptFeedback,
|
|
@@ -270,11 +281,28 @@ function aggregateGeminiStreamChunks(responses) {
|
|
|
270
281
|
return aggregatedResponse;
|
|
271
282
|
}
|
|
272
283
|
|
|
273
|
-
|
|
284
|
+
// A response with neither text nor a tool call (thoughts alone are not text).
|
|
285
|
+
function hasNoAnswerContent(aggregated) {
|
|
286
|
+
const parts = aggregated?.candidates?.[0]?.content?.parts;
|
|
287
|
+
return !(Array.isArray(parts) ? parts : []).some(
|
|
288
|
+
(part) => part?.functionCall || (part?.thought !== true && typeof part?.text === 'string' && part.text.trim())
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function assertGeminiStreamCompleted({
|
|
293
|
+
sawStreamChunk,
|
|
294
|
+
finishReason,
|
|
295
|
+
promptBlockReason,
|
|
296
|
+
label,
|
|
297
|
+
acceptEmptyNoFinish = false,
|
|
298
|
+
aggregated = null,
|
|
299
|
+
}) {
|
|
274
300
|
if (!sawStreamChunk) {
|
|
275
301
|
throw geminiTruncatedStreamError(`${label} truncated: empty stream`);
|
|
276
302
|
}
|
|
277
303
|
if (!finishReason && !promptBlockReason) {
|
|
304
|
+
// Right after a tool result the model may end with nothing at all.
|
|
305
|
+
if (acceptEmptyNoFinish && hasNoAnswerContent(aggregated)) return;
|
|
278
306
|
throw geminiTruncatedStreamError(`${label} truncated: no finishReason`);
|
|
279
307
|
}
|
|
280
308
|
}
|
|
@@ -436,7 +464,17 @@ function createLeakGuardFinalizer(textLeakGuard) {
|
|
|
436
464
|
// aggregation, leak guard, watchdogs — stays shared.
|
|
437
465
|
export async function consumeGeminiRestStreamResponse(
|
|
438
466
|
response,
|
|
439
|
-
{
|
|
467
|
+
{
|
|
468
|
+
signal,
|
|
469
|
+
onStreamDelta,
|
|
470
|
+
onTextDelta,
|
|
471
|
+
textLeakGuard,
|
|
472
|
+
label,
|
|
473
|
+
unwrapChunk = null,
|
|
474
|
+
onChunk = null,
|
|
475
|
+
failureUsage = null,
|
|
476
|
+
acceptEmptyNoFinish = false,
|
|
477
|
+
}
|
|
440
478
|
) {
|
|
441
479
|
const unwrap =
|
|
442
480
|
typeof unwrapChunk === 'function'
|
|
@@ -464,7 +502,10 @@ export async function consumeGeminiRestStreamResponse(
|
|
|
464
502
|
});
|
|
465
503
|
// One decoded chunk: collected for aggregation and relayed to every sink.
|
|
466
504
|
const relayChunk = (rawChunk) => {
|
|
505
|
+
// In-band errors stay top level even on a transport that nests payloads.
|
|
506
|
+
assertNoGeminiInBandError(rawChunk, label);
|
|
467
507
|
const parsed = unwrap(rawChunk);
|
|
508
|
+
assertNoGeminiInBandError(parsed, label);
|
|
468
509
|
if (!sawStreamChunk) {
|
|
469
510
|
sawStreamChunk = true;
|
|
470
511
|
watchdogs.clearFirstByte();
|
|
@@ -530,12 +571,13 @@ export async function consumeGeminiRestStreamResponse(
|
|
|
530
571
|
}
|
|
531
572
|
const tail = sseDataPayload(buffer.trim());
|
|
532
573
|
if (tail) {
|
|
574
|
+
let rawTail;
|
|
533
575
|
try {
|
|
534
|
-
|
|
535
|
-
relayChunk(rawTail);
|
|
576
|
+
[rawTail] = await parseProviderJsonBatch([tail]);
|
|
536
577
|
} catch (cause) {
|
|
537
578
|
throw geminiStreamCorruptionError(`${label} corrupt SSE tail JSON`, cause);
|
|
538
579
|
}
|
|
580
|
+
relayChunk(rawTail);
|
|
539
581
|
}
|
|
540
582
|
} catch (err) {
|
|
541
583
|
finalizeLeakGuard();
|
|
@@ -558,7 +600,7 @@ export async function consumeGeminiRestStreamResponse(
|
|
|
558
600
|
finalizeLeakGuard();
|
|
559
601
|
}
|
|
560
602
|
|
|
561
|
-
const aggregated = aggregateGeminiStreamChunks(allChunks);
|
|
603
|
+
const aggregated = aggregateGeminiStreamChunks(allChunks, label);
|
|
562
604
|
const finishReason = aggregated.candidates?.[0]?.finishReason || null;
|
|
563
605
|
const promptBlockReason = aggregated.promptFeedback?.blockReason || null;
|
|
564
606
|
// Truncation (no finishReason) after visible output must carry the same
|
|
@@ -566,7 +608,14 @@ export async function consumeGeminiRestStreamResponse(
|
|
|
566
608
|
// above, so stamp here too (review High: transient TRUNCATED_STREAM would
|
|
567
609
|
// otherwise replay/double-render live text via withRetry).
|
|
568
610
|
try {
|
|
569
|
-
assertGeminiStreamCompleted({
|
|
611
|
+
assertGeminiStreamCompleted({
|
|
612
|
+
sawStreamChunk,
|
|
613
|
+
finishReason,
|
|
614
|
+
promptBlockReason,
|
|
615
|
+
label,
|
|
616
|
+
acceptEmptyNoFinish,
|
|
617
|
+
aggregated,
|
|
618
|
+
});
|
|
570
619
|
} catch (err) {
|
|
571
620
|
throw stampGeminiStreamFailure(err, { relayedText, textLeakGuard, chunks: allChunks, failureUsage });
|
|
572
621
|
}
|
|
@@ -585,6 +634,7 @@ export async function consumeGeminiSdkStream(
|
|
|
585
634
|
firstByteTimeoutMs = GEMINI_FIRST_BYTE_TIMEOUT_MS,
|
|
586
635
|
cancellationGraceMs = 250,
|
|
587
636
|
failureUsage = null,
|
|
637
|
+
acceptEmptyNoFinish = false,
|
|
588
638
|
}
|
|
589
639
|
) {
|
|
590
640
|
let relayedText = '';
|
|
@@ -636,6 +686,7 @@ export async function consumeGeminiSdkStream(
|
|
|
636
686
|
if (signal?.aborted) throw abortError();
|
|
637
687
|
if (step.done) break;
|
|
638
688
|
reader.noteChunk();
|
|
689
|
+
assertNoGeminiInBandError(step.value, label);
|
|
639
690
|
if (step.value) collectedChunks.push(step.value);
|
|
640
691
|
try {
|
|
641
692
|
onStreamDelta?.(geminiChunkProgressKind(step.value));
|
|
@@ -652,7 +703,7 @@ export async function consumeGeminiSdkStream(
|
|
|
652
703
|
// locally; retain the SDK response path only for an empty collection.
|
|
653
704
|
let raw;
|
|
654
705
|
if (collectedChunks.length > 0) {
|
|
655
|
-
raw = aggregateGeminiStreamChunks(collectedChunks);
|
|
706
|
+
raw = aggregateGeminiStreamChunks(collectedChunks, label);
|
|
656
707
|
} else {
|
|
657
708
|
let response;
|
|
658
709
|
try {
|
|
@@ -661,10 +712,18 @@ export async function consumeGeminiSdkStream(
|
|
|
661
712
|
throw normalizeGeminiSdkError(err, label);
|
|
662
713
|
}
|
|
663
714
|
raw = response?.candidates ? response : response?.response || response;
|
|
715
|
+
assertNoGeminiInBandError(raw, label);
|
|
664
716
|
}
|
|
665
717
|
const finishReason = raw?.candidates?.[0]?.finishReason || null;
|
|
666
718
|
const promptBlockReason = raw?.promptFeedback?.blockReason || null;
|
|
667
|
-
assertGeminiStreamCompleted({
|
|
719
|
+
assertGeminiStreamCompleted({
|
|
720
|
+
sawStreamChunk: reader.sawStreamChunk,
|
|
721
|
+
finishReason,
|
|
722
|
+
promptBlockReason,
|
|
723
|
+
label,
|
|
724
|
+
acceptEmptyNoFinish,
|
|
725
|
+
aggregated: raw,
|
|
726
|
+
});
|
|
668
727
|
return raw;
|
|
669
728
|
} catch (err) {
|
|
670
729
|
reader?.clearFirstByte();
|
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
import { getModelMetadataSync } from '../../../shared/llm/model-catalog.mjs';
|
|
2
|
+
|
|
3
|
+
// The thinkingLevel values the Gemini wire accepts.
|
|
1
4
|
const LEVELS = ['minimal', 'low', 'medium', 'high'];
|
|
2
5
|
|
|
6
|
+
// The thinkingLevel values the catalogs declare for the model (provider live
|
|
7
|
+
// roster, then models.dev / LiteLLM rows, resolved by getModelMetadataSync),
|
|
8
|
+
// limited to what the Gemini wire accepts. null when no catalog declares a ladder.
|
|
9
|
+
function catalogThinkingLevels(model, provider) {
|
|
10
|
+
const bare = String(model || '').replace(/^models\//i, '');
|
|
11
|
+
const effort = getModelMetadataSync(bare, provider)?.reasoningOptions?.find((option) => option?.type === 'effort');
|
|
12
|
+
const declared = (Array.isArray(effort?.values) ? effort.values : [])
|
|
13
|
+
.map((value) => String(value).toLowerCase())
|
|
14
|
+
.filter((value) => LEVELS.includes(value));
|
|
15
|
+
return declared.length ? [...new Set(declared)] : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
3
18
|
// Only translate the public option names, not arbitrary effort-to-token ratios.
|
|
4
|
-
// Omitting settings preserves the provider's model default.
|
|
5
|
-
|
|
6
|
-
|
|
19
|
+
// Omitting settings preserves the provider's model default. A level is checked
|
|
20
|
+
// against the catalog's ladder when one is declared; otherwise the value is sent
|
|
21
|
+
// as given (within the wire vocabulary) and the server decides, and an explicit
|
|
22
|
+
// thinkingBudget is likewise passed through unvalidated beyond its wire type.
|
|
23
|
+
export function geminiThinkingConfig(model, opts = {}, { includeThoughts, provider = 'gemini' } = {}) {
|
|
7
24
|
const rawBudget = opts.thinkingBudget ?? opts.thinkingBudgetTokens;
|
|
8
25
|
const hasBudget = rawBudget !== undefined && rawBudget !== null && rawBudget !== '';
|
|
9
26
|
const rawLevel = opts.thinkingLevel ?? (hasBudget ? undefined : opts.effort);
|
|
@@ -15,14 +32,7 @@ export function geminiThinkingConfig(model, opts = {}, { includeThoughts } = {})
|
|
|
15
32
|
throw new TypeError('Choose thinkingLevel/effort or thinkingBudget, not both.');
|
|
16
33
|
}
|
|
17
34
|
if (level) {
|
|
18
|
-
|
|
19
|
-
throw new TypeError(`${model} uses thinkingBudget instead of thinkingLevel/effort.`);
|
|
20
|
-
}
|
|
21
|
-
let supported = LEVELS;
|
|
22
|
-
if (/^gemini-3(?:\.0)?-pro(?:-|$)/.test(id)) supported = ['low', 'high'];
|
|
23
|
-
else if (/^gemini-3\.1-pro(?:-|$)/.test(id) || /^gemini-3\.[78]-flash(?:-|$)/.test(id)) {
|
|
24
|
-
supported = ['low', 'medium', 'high'];
|
|
25
|
-
} else if (/^gemini-3\.1-flash-lite.*image/.test(id)) supported = ['minimal', 'high'];
|
|
35
|
+
const supported = catalogThinkingLevels(model, provider) ?? LEVELS;
|
|
26
36
|
if (!supported.includes(level)) {
|
|
27
37
|
throw new TypeError(`${model} supports thinking levels ${supported.join(', ')}; received ${level}.`);
|
|
28
38
|
}
|
|
@@ -33,15 +43,6 @@ export function geminiThinkingConfig(model, opts = {}, { includeThoughts } = {})
|
|
|
33
43
|
if (!Number.isInteger(budget) || budget < -1) {
|
|
34
44
|
throw new TypeError('thinkingBudget must be -1 (dynamic), 0, or a positive integer.');
|
|
35
45
|
}
|
|
36
|
-
if (/^gemini-2\.5-pro(?:-|$)/.test(id) && budget !== -1 && (budget < 128 || budget > 32768)) {
|
|
37
|
-
throw new TypeError(`${model} requires thinkingBudget -1 or 128..32768.`);
|
|
38
|
-
}
|
|
39
|
-
if (/^gemini-2\.5-flash(?:-|$)/.test(id) && budget > 24576) {
|
|
40
|
-
throw new TypeError(`${model} requires thinkingBudget at most 24576.`);
|
|
41
|
-
}
|
|
42
|
-
if (/^gemini-2\.5-flash-lite(?:-|$)/.test(id) && budget > 0 && budget < 512) {
|
|
43
|
-
throw new TypeError(`${model} requires thinkingBudget -1, 0, or 512..24576.`);
|
|
44
|
-
}
|
|
45
46
|
config.thinkingBudget = budget;
|
|
46
47
|
}
|
|
47
48
|
return Object.keys(config).length ? config : undefined;
|