mixdog 1.2.2 → 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/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -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/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- 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/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
- 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 +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
- 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 +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
- 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/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/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 +121 -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 +5 -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-error-contract.mjs +1 -1
- 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 +42 -24
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
- 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.mjs +53 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
- 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 +25 -4
- 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 +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
- 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-ws-delta.mjs +7 -16
- 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-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.mjs +11 -3
- 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 +147 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
- 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/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- 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 +3 -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 +63 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
- 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 +4 -1
- 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 +16 -0
- 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/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
- 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.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 +13 -0
- 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/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-summary-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -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/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- 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 +31 -26
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
- 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 +1 -1
- 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/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 +6 -0
- 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-effort.mjs +6 -0
- 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/loopback-listener.mjs +9 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/review-diff.mjs +52 -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-segments.mjs +28 -4
- 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 +268 -100
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
- package/src/runtime/shared/turn-snapshot.mjs +857 -683
- package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
- 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 -0
- 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/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 +13 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +6 -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 +12 -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 +72 -7
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +17 -5
- package/src/session-runtime/model-route/set-route.mjs +2 -2
- 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/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +8 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- 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/tag-registry.mjs +15 -1
- 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.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/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-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 +19 -0
- package/src/standalone/daemon.mjs +61 -1
- 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 +7 -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/session-calls/session-action.mjs +18 -3
- package/src/standalone/session-service-api.mjs +6 -1
- 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/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +2 -0
- 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 +13 -3
- 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/question-panel-model.mjs +156 -0
- package/src/tui/dist/index.mjs +2413 -1465
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/session-api/commands.mjs +10 -0
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/integrations.mjs +9 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +2 -1
- package/src/tui/session/session-api/settings.mjs +6 -0
- package/src/tui/session/session-api.mjs +1 -0
- 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 +12 -5
- package/src/workflows/default/WORKFLOW.md +3 -1
|
@@ -9,7 +9,7 @@ export const CUSTOM_PROVIDER_PROTOCOLS = Object.freeze(['openai-chat', 'openai-r
|
|
|
9
9
|
export const CUSTOM_PROVIDER_SCHEMA_VERSION = 2;
|
|
10
10
|
// Ranked weakest → strongest.
|
|
11
11
|
export const CUSTOM_REASONING_LEVELS = CUSTOM_EFFORT_LEVELS;
|
|
12
|
-
export const CUSTOM_REASONING_WIRE_FORMATS = Object.freeze(['standard', 'deepseek']);
|
|
12
|
+
export const CUSTOM_REASONING_WIRE_FORMATS = Object.freeze(['standard', 'deepseek', 'together', 'vllm']);
|
|
13
13
|
export const CUSTOM_PROVIDER_AUTH_MODES = Object.freeze(['key', 'none']);
|
|
14
14
|
export const CUSTOM_MODEL_OVERRIDE_LIMIT = 500;
|
|
15
15
|
|
|
@@ -74,10 +74,22 @@ const V2_TOP_LEVEL_KEYS = new Set([
|
|
|
74
74
|
'auth',
|
|
75
75
|
'discovery',
|
|
76
76
|
'modelOverrides',
|
|
77
|
+
'wires',
|
|
78
|
+
'modelWires',
|
|
77
79
|
'id',
|
|
78
80
|
'apiKey',
|
|
81
|
+
'apiKeyEnv',
|
|
79
82
|
'preconnect',
|
|
80
83
|
]);
|
|
84
|
+
export const CUSTOM_API_KEY_ENV_RE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
85
|
+
|
|
86
|
+
// Env var name that supplies the key, or '' when unset (undefined / null).
|
|
87
|
+
function normalizeApiKeyEnv(raw) {
|
|
88
|
+
if (raw === undefined || raw === null) return '';
|
|
89
|
+
if (typeof raw !== 'string' || !CUSTOM_API_KEY_ENV_RE.test(raw))
|
|
90
|
+
fail('apiKeyEnv must be a valid environment variable name');
|
|
91
|
+
return raw;
|
|
92
|
+
}
|
|
81
93
|
const MAX_BASE_URL = 2048;
|
|
82
94
|
|
|
83
95
|
function onlyKnownKeys(value, allowed, label) {
|
|
@@ -229,6 +241,49 @@ function normalizeOverrides(raw, protocol, legacy) {
|
|
|
229
241
|
return out;
|
|
230
242
|
}
|
|
231
243
|
|
|
244
|
+
// The wire each OpenAI-compatible protocol speaks (wire-route.mjs WIRES).
|
|
245
|
+
export const CUSTOM_PROTOCOL_WIRES = Object.freeze({
|
|
246
|
+
'openai-chat': 'chat',
|
|
247
|
+
'openai-responses': 'responses',
|
|
248
|
+
anthropic: 'messages',
|
|
249
|
+
});
|
|
250
|
+
// Wires a user-defined OpenAI-compatible endpoint can be declared to serve.
|
|
251
|
+
export const CUSTOM_PROVIDER_WIRES = Object.freeze(['chat', 'responses', 'messages']);
|
|
252
|
+
|
|
253
|
+
// `wires`: the wires the endpoint serves (the protocol's own wire included);
|
|
254
|
+
// absent = the protocol's wire only. Anthropic-protocol providers speak one wire.
|
|
255
|
+
function normalizeWires(raw, protocol) {
|
|
256
|
+
if (raw === undefined) return null;
|
|
257
|
+
if (protocol === 'anthropic') fail("wires requires an 'openai-chat' or 'openai-responses' protocol");
|
|
258
|
+
if (!Array.isArray(raw) || raw.length === 0) fail('wires must be a non-empty array');
|
|
259
|
+
const seen = new Set();
|
|
260
|
+
for (const wire of raw) {
|
|
261
|
+
if (!CUSTOM_PROVIDER_WIRES.includes(wire)) fail(`wires has unsupported wire: ${String(wire)}`);
|
|
262
|
+
if (seen.has(wire)) fail(`wires has duplicate wire: ${wire}`);
|
|
263
|
+
seen.add(wire);
|
|
264
|
+
}
|
|
265
|
+
if (!seen.has(CUSTOM_PROTOCOL_WIRES[protocol]))
|
|
266
|
+
fail(`wires must include the protocol wire: ${CUSTOM_PROTOCOL_WIRES[protocol]}`);
|
|
267
|
+
return CUSTOM_PROVIDER_WIRES.filter((wire) => seen.has(wire));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// `modelWires`: per-model wire override, each one of the served wires.
|
|
271
|
+
function normalizeModelWires(raw, protocol, wires) {
|
|
272
|
+
if (raw === undefined) return {};
|
|
273
|
+
if (protocol === 'anthropic') fail("modelWires requires an 'openai-chat' or 'openai-responses' protocol");
|
|
274
|
+
if (!isPlainObject(raw)) fail('modelWires must be an object');
|
|
275
|
+
const keys = Object.keys(raw);
|
|
276
|
+
if (keys.length > CUSTOM_MODEL_OVERRIDE_LIMIT) fail(`at most ${CUSTOM_MODEL_OVERRIDE_LIMIT} model wires are allowed`);
|
|
277
|
+
const served = wires || [CUSTOM_PROTOCOL_WIRES[protocol]];
|
|
278
|
+
const out = {};
|
|
279
|
+
for (const key of keys) {
|
|
280
|
+
const id = modelIdKey(key, 'modelWires key');
|
|
281
|
+
if (!served.includes(raw[key])) fail(`modelWires[${id}] must be one of the served wires: ${served.join(', ')}`);
|
|
282
|
+
out[id] = raw[key];
|
|
283
|
+
}
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
|
|
232
287
|
// Legacy (pre-v2) `models: [{ id, name?, contextWindow?, maxOutputTokens? }]`
|
|
233
288
|
// become manual, visible overrides; their presence hides newly discovered ids.
|
|
234
289
|
function migrateLegacy(input) {
|
|
@@ -268,6 +323,8 @@ export function normalizeCustomProviderConfig(input) {
|
|
|
268
323
|
// v2 fields without the version would be silently dropped by migration.
|
|
269
324
|
if (input.modelOverrides !== undefined || input.discovery !== undefined)
|
|
270
325
|
fail('modelOverrides and discovery require schemaVersion 2');
|
|
326
|
+
if (input.wires !== undefined || input.modelWires !== undefined)
|
|
327
|
+
fail('wires and modelWires require schemaVersion 2');
|
|
271
328
|
source = migrateLegacy(input);
|
|
272
329
|
} else if (Number.isInteger(version) && version > CUSTOM_PROVIDER_SCHEMA_VERSION)
|
|
273
330
|
fail(`unsupported schemaVersion ${version}; update mixdog to use this provider`);
|
|
@@ -293,10 +350,13 @@ export function normalizeCustomProviderConfig(input) {
|
|
|
293
350
|
if (!CUSTOM_PROVIDER_PROTOCOLS.includes(protocol)) fail(`unsupported protocol: ${protocol}`);
|
|
294
351
|
const auth = source.auth === undefined || source.auth === null ? 'key' : text(source.auth);
|
|
295
352
|
if (!CUSTOM_PROVIDER_AUTH_MODES.includes(auth)) fail(`invalid auth: ${String(source.auth)}`);
|
|
353
|
+
const apiKeyEnv = normalizeApiKeyEnv(source.apiKeyEnv);
|
|
296
354
|
const discovery = source.discovery === undefined ? {} : source.discovery;
|
|
297
355
|
if (!isPlainObject(discovery)) fail('discovery must be an object');
|
|
298
356
|
onlyKnownKeys(discovery, DISCOVERY_KEYS, 'discovery');
|
|
299
357
|
const newModelsHidden = optionalBoolean(discovery.newModelsHidden, 'discovery.newModelsHidden') ?? false;
|
|
358
|
+
const wires = normalizeWires(source.wires, protocol);
|
|
359
|
+
const modelWires = normalizeModelWires(source.modelWires, protocol, wires);
|
|
300
360
|
return {
|
|
301
361
|
type: 'custom',
|
|
302
362
|
schemaVersion: CUSTOM_PROVIDER_SCHEMA_VERSION,
|
|
@@ -305,8 +365,11 @@ export function normalizeCustomProviderConfig(input) {
|
|
|
305
365
|
baseURL: normalizeBaseURL(source.baseURL),
|
|
306
366
|
enabled: source.enabled !== false,
|
|
307
367
|
auth,
|
|
368
|
+
...(apiKeyEnv && auth === 'key' ? { apiKeyEnv } : {}),
|
|
308
369
|
discovery: { newModelsHidden },
|
|
309
370
|
modelOverrides: normalizeOverrides(source.modelOverrides, protocol, legacy),
|
|
371
|
+
...(wires ? { wires } : {}),
|
|
372
|
+
...(Object.keys(modelWires).length ? { modelWires } : {}),
|
|
310
373
|
};
|
|
311
374
|
}
|
|
312
375
|
|
|
@@ -5,7 +5,19 @@
|
|
|
5
5
|
// under the custom id so model routes and provider identity stay `custom-…`.
|
|
6
6
|
|
|
7
7
|
import { resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
CUSTOM_PROTOCOL_WIRES,
|
|
10
|
+
effectiveCustomModelRows,
|
|
11
|
+
normalizeCustomProviderConfig,
|
|
12
|
+
} from './custom-provider-config.mjs';
|
|
13
|
+
import {
|
|
14
|
+
customWireProfile,
|
|
15
|
+
providerOffersWire,
|
|
16
|
+
registerWireProfile,
|
|
17
|
+
resolveWireRoute,
|
|
18
|
+
sendOnRoute,
|
|
19
|
+
} from './wire-route.mjs';
|
|
20
|
+
import { withInclusiveUsage } from './lib/inclusive-usage.mjs';
|
|
9
21
|
import { ANTHROPIC_VERSION } from './lib/anthropic-models.mjs';
|
|
10
22
|
import { enrichModels } from './model-catalog.mjs';
|
|
11
23
|
import { fetchCompatModelItems, listCompatModels } from './openai-compat-models.mjs';
|
|
@@ -45,6 +57,22 @@ function withCustomModels(Base, discover) {
|
|
|
45
57
|
_customKey() {
|
|
46
58
|
return this._customKeyless ? '' : resolveProviderApiKey(this.name, this.config);
|
|
47
59
|
}
|
|
60
|
+
// Env mode with the variable unset: fail before any request is sent.
|
|
61
|
+
_assertCustomKey() {
|
|
62
|
+
const name = this.customConfig?.apiKeyEnv;
|
|
63
|
+
if (name && !this._customKeyless && !resolveProviderApiKey(this.name, this.config))
|
|
64
|
+
throw new Error(`API key environment variable ${name} is not set`);
|
|
65
|
+
}
|
|
66
|
+
// A 401 reload must not fall back to the stale key once the variable is
|
|
67
|
+
// gone; with a new value the base reload picks it up for the retry.
|
|
68
|
+
reloadApiKey() {
|
|
69
|
+
this._assertCustomKey();
|
|
70
|
+
return super.reloadApiKey();
|
|
71
|
+
}
|
|
72
|
+
async send(...args) {
|
|
73
|
+
this._assertCustomKey();
|
|
74
|
+
return super.send(...args);
|
|
75
|
+
}
|
|
48
76
|
_customRows() {
|
|
49
77
|
return effectiveCustomModelRows(this.customConfig, this._customDiscovered || [], { providerId: this.name });
|
|
50
78
|
}
|
|
@@ -57,6 +85,7 @@ function withCustomModels(Base, discover) {
|
|
|
57
85
|
async listModels({ signal = null, throwOnError = false } = {}) {
|
|
58
86
|
let failure = null;
|
|
59
87
|
try {
|
|
88
|
+
this._assertCustomKey();
|
|
60
89
|
this._customDiscovered = await discover(this, signal);
|
|
61
90
|
this.customDiscoveryError = null;
|
|
62
91
|
} catch (error) {
|
|
@@ -112,10 +141,14 @@ export async function createCustomProvider(id, config) {
|
|
|
112
141
|
// `auth: 'none'` is authoritative: a supplied/injected key is ignored and
|
|
113
142
|
// `keyless` makes the engine omit every credential header.
|
|
114
143
|
const keyless = cfg.auth === 'none';
|
|
115
|
-
|
|
144
|
+
// Env mode: the named variable is the only key source.
|
|
145
|
+
let apiKey = '';
|
|
146
|
+
if (cfg.apiKeyEnv) apiKey = process.env[cfg.apiKeyEnv] || '';
|
|
147
|
+
else if (!keyless) apiKey = config.apiKey || '';
|
|
116
148
|
const engine = {
|
|
117
149
|
baseURL: cfg.baseURL,
|
|
118
150
|
apiKey: apiKey || undefined,
|
|
151
|
+
...(cfg.apiKeyEnv ? { apiKeyEnv: cfg.apiKeyEnv } : {}),
|
|
119
152
|
...(keyless ? { keyless: true } : {}),
|
|
120
153
|
...(config.preconnect === false ? { preconnect: false } : {}),
|
|
121
154
|
};
|
|
@@ -129,14 +162,41 @@ export async function createCustomProvider(id, config) {
|
|
|
129
162
|
provider.nativePdf = false;
|
|
130
163
|
} else {
|
|
131
164
|
const { OpenAICompatProvider } = await import('./openai-compat.mjs');
|
|
132
|
-
|
|
165
|
+
// The wires the endpoint is declared to serve (default: the protocol's
|
|
166
|
+
// own) and the per-model `modelWires` override pick each model's wire
|
|
167
|
+
// through the shared wire-route table, pinned per session like a gateway.
|
|
168
|
+
registerWireProfile(
|
|
169
|
+
providerId,
|
|
170
|
+
customWireProfile({ protocolWire: CUSTOM_PROTOCOL_WIRES[cfg.protocol], wires: cfg.wires })
|
|
171
|
+
);
|
|
172
|
+
let messagesEngine = null;
|
|
173
|
+
if (providerOffersWire(providerId, 'messages')) {
|
|
174
|
+
const { AnthropicProvider } = await import('./anthropic.mjs');
|
|
175
|
+
// The Anthropic SDK appends /v1/messages itself; the endpoint's /v1 base maps to it.
|
|
176
|
+
messagesEngine = new AnthropicProvider({ ...engine, name: providerId, baseURL: cfg.baseURL.replace(/\/v1$/i, '') });
|
|
177
|
+
messagesEngine.nativePdf = false;
|
|
178
|
+
}
|
|
133
179
|
class CompatBase extends OpenAICompatProvider {
|
|
180
|
+
messagesEngine = messagesEngine;
|
|
134
181
|
_fetchModelItems({ signal = null } = {}) {
|
|
135
182
|
return fetchCompatModelItems(this, { signal, keyless: this._customKeyless });
|
|
136
183
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
184
|
+
resolveWireRoute(model, ctx = {}) {
|
|
185
|
+
return resolveWireRoute(this.name, model, { ...ctx, override: cfg.modelWires?.[model] ?? null });
|
|
186
|
+
}
|
|
187
|
+
send(messages, model, tools, sendOpts) {
|
|
188
|
+
const useModel = model || this.defaultModel;
|
|
189
|
+
// The resolved route is the only wire authority: a caller's own wire
|
|
190
|
+
// switches never reach the engine. A custom provider declares the
|
|
191
|
+
// wires its endpoint serves, so a clear "unsupported" refusal moves
|
|
192
|
+
// the send to another declared wire once (sendOnRoute).
|
|
193
|
+
return sendOnRoute(this, useModel, tools, sendOpts, messages, (wire, history, opts) => {
|
|
194
|
+
if (wire === 'messages' && this.messagesEngine) {
|
|
195
|
+
return withInclusiveUsage(() => this.messagesEngine.send(history, useModel, tools, opts));
|
|
196
|
+
}
|
|
197
|
+
if (wire === 'responses') return super.send(history, model, tools, { ...opts, compatWireApi: 'responses' });
|
|
198
|
+
return super.send(history, model, tools, opts);
|
|
199
|
+
});
|
|
140
200
|
}
|
|
141
201
|
}
|
|
142
202
|
const Custom = withCustomModels(CompatBase, (self, signal) =>
|
|
@@ -144,6 +204,10 @@ export async function createCustomProvider(id, config) {
|
|
|
144
204
|
);
|
|
145
205
|
provider = new Custom(providerId, engine);
|
|
146
206
|
}
|
|
207
|
+
// The bound Messages engine reads the parent's model rows, so a routed
|
|
208
|
+
// Messages request carries the same row-driven thinking/effort as the
|
|
209
|
+
// Anthropic protocol.
|
|
210
|
+
if (provider.messagesEngine) provider.messagesEngine.getCachedModelInfo = (model) => provider.getCachedModelInfo(model);
|
|
147
211
|
provider.customConfig = cfg;
|
|
148
212
|
provider._customKeyless = keyless;
|
|
149
213
|
provider._applyCustomRows(provider._customRows());
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// effort to a supported level and serializes it for the provider protocol.
|
|
5
5
|
// First-party providers never route through here.
|
|
6
6
|
|
|
7
|
+
import { isTogetherBaseURL } from './openai-compat-reasoning-field.mjs';
|
|
8
|
+
|
|
7
9
|
export const CUSTOM_EFFORT_LEVELS = Object.freeze(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
8
10
|
const RANK = new Map(CUSTOM_EFFORT_LEVELS.map((level, index) => [level, index]));
|
|
9
11
|
|
|
@@ -63,16 +65,30 @@ export function snapCustomEffort(levels, effort) {
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
const EMPTY = Object.freeze({ level: null, fields: {}, thinking: null, replay: false });
|
|
68
|
+
// Chat-only extensions on top of the standard `reasoning_effort`.
|
|
69
|
+
const CHAT_ONLY_WIRE_FORMATS = new Set(['deepseek', 'together', 'vllm']);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The effective wire format of a row: its declared format, else `together`
|
|
73
|
+
* when the provider's own base URL is Together AI (whose hybrid models toggle
|
|
74
|
+
* reasoning with a `reasoning.enabled` flag), else `standard`.
|
|
75
|
+
*/
|
|
76
|
+
export function customWireFormat(model, baseURL) {
|
|
77
|
+
const declared = model?.customReasoning?.wireFormat;
|
|
78
|
+
if (CHAT_ONLY_WIRE_FORMATS.has(declared)) return declared;
|
|
79
|
+
return isTogetherBaseURL(baseURL) ? 'together' : 'standard';
|
|
80
|
+
}
|
|
66
81
|
|
|
67
82
|
/**
|
|
68
|
-
* @param {{ protocol: 'openai-chat'|'openai-responses'|'anthropic', model: object|null, effort: string|null|undefined }} input
|
|
83
|
+
* @param {{ protocol: 'openai-chat'|'openai-responses'|'anthropic', model: object|null, effort: string|null|undefined, baseURL?: string }} input
|
|
69
84
|
* @returns {{ level: string|null, fields: object, thinking: 'on'|'off'|null, replay: boolean }}
|
|
70
85
|
*/
|
|
71
|
-
export function resolveCustomReasoning({ protocol, model, effort } = {}) {
|
|
86
|
+
export function resolveCustomReasoning({ protocol, model, effort, baseURL } = {}) {
|
|
72
87
|
const custom = model?.customReasoning || {};
|
|
73
|
-
const wireFormat =
|
|
74
|
-
//
|
|
75
|
-
|
|
88
|
+
const wireFormat = protocol === 'openai-chat' ? customWireFormat(model, baseURL) : 'standard';
|
|
89
|
+
// The `thinking` / `reasoning.enabled` / chat_template_kwargs extensions
|
|
90
|
+
// exist only on the Chat wire.
|
|
91
|
+
if (CHAT_ONLY_WIRE_FORMATS.has(custom.wireFormat) && protocol !== 'openai-chat') return { ...EMPTY, fields: {} };
|
|
76
92
|
const level = snapCustomEffort(customEffortLevels(model), effort);
|
|
77
93
|
let thinking = null;
|
|
78
94
|
if (level !== null) thinking = level === 'none' ? 'off' : 'on';
|
|
@@ -86,6 +102,18 @@ export function resolveCustomReasoning({ protocol, model, effort } = {}) {
|
|
|
86
102
|
level === 'none'
|
|
87
103
|
? { thinking: { type: 'disabled' } }
|
|
88
104
|
: { thinking: { type: 'enabled' }, reasoning_effort: value };
|
|
105
|
+
} else if (wireFormat === 'together') {
|
|
106
|
+
// Together: `reasoning.enabled` toggles hybrid models (those that can
|
|
107
|
+
// be turned off); `reasoning_effort` sets the depth of the others.
|
|
108
|
+
if (level === 'none') fields = { reasoning: { enabled: false } };
|
|
109
|
+
else {
|
|
110
|
+
fields = { reasoning_effort: value };
|
|
111
|
+
if (customEffortLevels(model)?.includes('none')) fields.reasoning = { enabled: true };
|
|
112
|
+
}
|
|
113
|
+
} else if (wireFormat === 'vllm') {
|
|
114
|
+
// vLLM switches thinking through the chat template.
|
|
115
|
+
fields = { chat_template_kwargs: { enable_thinking: level !== 'none' } };
|
|
116
|
+
if (level !== 'none') fields.reasoning_effort = value;
|
|
89
117
|
} else {
|
|
90
118
|
fields = { reasoning_effort: value };
|
|
91
119
|
}
|
|
@@ -101,7 +129,6 @@ export function resolveCustomReasoning({ protocol, model, effort } = {}) {
|
|
|
101
129
|
// An explicit replay flag wins over the catalog's reasoning_content field.
|
|
102
130
|
const replayFlag =
|
|
103
131
|
typeof custom.replay === 'boolean' ? custom.replay : model?.reasoningContentField === 'reasoning_content';
|
|
104
|
-
const replay =
|
|
105
|
-
protocol === 'openai-chat' && replayFlag && thinking !== 'off' && !/reasoner/i.test(String(model?.id || ''));
|
|
132
|
+
const replay = protocol === 'openai-chat' && replayFlag && thinking !== 'off';
|
|
106
133
|
return { level, fields, thinking, replay };
|
|
107
134
|
}
|
|
@@ -42,17 +42,43 @@ function customToolArgumentsFromInput(name, input) {
|
|
|
42
42
|
return { input: text };
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Responses calls of a tool loaded from a declared namespace carry
|
|
46
|
+
// `namespace` beside `name`. Mixdog declares no namespaces today, so this is
|
|
47
|
+
// parse-side preservation only: a call that carries one keeps it on its
|
|
48
|
+
// record (and in replay), and its dispatch dedupe identity is qualified, so it
|
|
49
|
+
// is never conflated with a same-named top-level tool.
|
|
50
|
+
export function toolCallNamespace(item) {
|
|
51
|
+
return typeof item?.namespace === 'string' && item.namespace ? item.namespace : '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Copy the source item's namespace onto a call record (mutates; returns it). */
|
|
55
|
+
export function withToolCallNamespace(call, source) {
|
|
56
|
+
const namespace = toolCallNamespace(source);
|
|
57
|
+
if (call && namespace && !call.namespace) call.namespace = namespace;
|
|
58
|
+
return call;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Dispatch dedupe identity: the namespace-qualified tool name. */
|
|
62
|
+
export function qualifiedToolCallName(call) {
|
|
63
|
+
const namespace = toolCallNamespace(call);
|
|
64
|
+
const name = call?.name || '';
|
|
65
|
+
return namespace ? `${namespace}.${name}` : name;
|
|
66
|
+
}
|
|
67
|
+
|
|
45
68
|
export function customToolCallFromResponseItem(item) {
|
|
46
69
|
if (item?.type !== 'custom_tool_call') return null;
|
|
47
70
|
const id = item.call_id || item.id || '';
|
|
48
71
|
const name = item.name || '';
|
|
49
72
|
if (!id || !name) return null;
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
return withToolCallNamespace(
|
|
74
|
+
{
|
|
75
|
+
id,
|
|
76
|
+
name,
|
|
77
|
+
arguments: customToolArgumentsFromInput(name, item.input ?? ''),
|
|
78
|
+
nativeType: 'custom_tool_call',
|
|
79
|
+
},
|
|
80
|
+
item
|
|
81
|
+
);
|
|
56
82
|
}
|
|
57
83
|
|
|
58
84
|
export function isCustomToolCallRecord(call) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek: Chat Completions at https://api.deepseek.com plus the Anthropic
|
|
3
|
+
* Messages endpoint at https://api.deepseek.com/anthropic (the SDK appends
|
|
4
|
+
* /v1/messages). Per DeepSeek's Anthropic API docs that endpoint carries
|
|
5
|
+
* thinking and tool_use/tool_result blocks and ignores cache_control and
|
|
6
|
+
* anthropic-beta; agent/tool sessions use it, tool-less one-shot requests stay
|
|
7
|
+
* on Chat (wire-route.mjs). DeepSeek has no Responses endpoint.
|
|
8
|
+
*/
|
|
9
|
+
import { WireGatewayProvider, messagesBaseFromOpenAIBase } from './wire-gateway.mjs';
|
|
10
|
+
import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
|
|
11
|
+
|
|
12
|
+
export function deepSeekMessagesBaseURL(configuredBaseURL) {
|
|
13
|
+
return `${messagesBaseFromOpenAIBase(configuredBaseURL || OPENAI_COMPAT_PRESETS.deepseek.baseURL)}/anthropic`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Requested effort → output_config.effort (thinking_mode docs: minimal/low →
|
|
17
|
+
// low, medium/high/xhigh → high, max/ultra → max).
|
|
18
|
+
const DEEPSEEK_EFFORT = Object.freeze({
|
|
19
|
+
minimal: 'low',
|
|
20
|
+
low: 'low',
|
|
21
|
+
medium: 'high',
|
|
22
|
+
high: 'high',
|
|
23
|
+
xhigh: 'high',
|
|
24
|
+
max: 'max',
|
|
25
|
+
ultra: 'max',
|
|
26
|
+
});
|
|
27
|
+
const DEEPSEEK_OFF = new Set(['none', 'off', 'disabled']);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* DeepSeek's own effort contract on its Messages endpoint: thinking is on by
|
|
31
|
+
* default and `budget_tokens` is ignored, so an effort goes in
|
|
32
|
+
* `output_config.effort`; off is `thinking:{type:'disabled'}` together with the
|
|
33
|
+
* documented Anthropic-format toggle `reasoning:{effort:'none'}`. No effort
|
|
34
|
+
* leaves the default (thinking on, effort high). Always handles the request.
|
|
35
|
+
*/
|
|
36
|
+
export function deepSeekEffortProjection(body, { effort } = {}) {
|
|
37
|
+
const level = String(effort ?? '')
|
|
38
|
+
.trim()
|
|
39
|
+
.toLowerCase();
|
|
40
|
+
if (DEEPSEEK_OFF.has(level)) {
|
|
41
|
+
body.thinking = { type: 'disabled' };
|
|
42
|
+
body.reasoning = { effort: 'none' };
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
const mapped = DEEPSEEK_EFFORT[level];
|
|
46
|
+
if (mapped) {
|
|
47
|
+
const existing = body.output_config && typeof body.output_config === 'object' ? body.output_config : {};
|
|
48
|
+
body.output_config = { ...existing, effort: mapped };
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class DeepSeekProvider extends WireGatewayProvider {
|
|
54
|
+
constructor(config = {}) {
|
|
55
|
+
super('deepseek', config, {
|
|
56
|
+
baseURL: deepSeekMessagesBaseURL(config.baseURL),
|
|
57
|
+
// The endpoint has no document block of any kind (PDFs included).
|
|
58
|
+
nativePdf: false,
|
|
59
|
+
nativeDocuments: false,
|
|
60
|
+
// disable_parallel_tool_use is ignored on every tool_choice.
|
|
61
|
+
supportsDisableParallelToolUse: false,
|
|
62
|
+
// Image `source.type: file` needs the Files API beta, on those requests only.
|
|
63
|
+
fileIdImageBeta: 'files-api-2025-04-14',
|
|
64
|
+
messagesEffortProjection: deepSeekEffortProjection,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -183,7 +183,9 @@ export function autoEffortModel(providerName, providerImpl, model) {
|
|
|
183
183
|
// list from falling back to a family ladder.
|
|
184
184
|
return { id: model, reasoningLevels: anthropicEffortLevelsFor(model), customReasoning: true };
|
|
185
185
|
}
|
|
186
|
-
|
|
186
|
+
// Every other route reads the provider's own cached model row, the source
|
|
187
|
+
// route resolution (lookupModelMeta) uses; a bare id has no levels and
|
|
188
|
+
// skips the judge.
|
|
187
189
|
return { ...(providerImpl?.getCachedModelInfo?.(model) || {}), id: model };
|
|
188
190
|
}
|
|
189
191
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { appendAgentTrace } from '../agent-trace.mjs';
|
|
15
15
|
import {
|
|
16
16
|
_estimateGeminiCacheTokens,
|
|
17
|
-
|
|
17
|
+
geminiExplicitCacheMinTokens,
|
|
18
18
|
_geminiCachePrefixCount,
|
|
19
19
|
_geminiCachePrefixContents,
|
|
20
20
|
_geminiCachePrefixHash,
|
|
@@ -146,6 +146,12 @@ export function awaitSharedCreate(task, signal) {
|
|
|
146
146
|
// role='user' or 'model'; generateContent uses role='function' for
|
|
147
147
|
// tool_result turns, so that is collapsed to 'user' (functionResponse parts
|
|
148
148
|
// remain inside).
|
|
149
|
+
// The model id without its `models/` resource prefix; every URL and body that
|
|
150
|
+
// adds the prefix itself starts from this.
|
|
151
|
+
export function geminiBareModelId(model) {
|
|
152
|
+
return String(model || '').replace(/^models\//i, '');
|
|
153
|
+
}
|
|
154
|
+
|
|
149
155
|
export function geminiCacheCreateBody({
|
|
150
156
|
model,
|
|
151
157
|
ttlSeconds,
|
|
@@ -157,7 +163,7 @@ export function geminiCacheCreateBody({
|
|
|
157
163
|
}) {
|
|
158
164
|
const cachePrefixContents = _geminiCachePrefixContents(contents, cachePrefixContentCount);
|
|
159
165
|
const body = {
|
|
160
|
-
model: `models/${model}`,
|
|
166
|
+
model: `models/${geminiBareModelId(model)}`,
|
|
161
167
|
ttl: `${ttlSeconds}s`,
|
|
162
168
|
};
|
|
163
169
|
if (systemInstruction) {
|
|
@@ -252,9 +258,10 @@ export function dropRejectedGeminiCache(cachedContent, opts) {
|
|
|
252
258
|
// the model-specific minimum. Skip the POST entirely when the estimate
|
|
253
259
|
// is under threshold so we don't spam 400 responses turn-after-turn.
|
|
254
260
|
export function geminiPrefixBelowMinimum({ model, systemInstruction, geminiTools, contents, opts, currentIter }) {
|
|
255
|
-
|
|
261
|
+
// null: the model is excluded from explicit caching, so never create one.
|
|
262
|
+
const minTokens = geminiExplicitCacheMinTokens(model);
|
|
256
263
|
const estimatedTokens = _estimateGeminiCacheTokens(systemInstruction, geminiTools, contents);
|
|
257
|
-
if (estimatedTokens >= minTokens) return false;
|
|
264
|
+
if (minTokens !== null && estimatedTokens >= minTokens) return false;
|
|
258
265
|
traceGeminiCache(opts, currentIter, 'gemini_cache_skip', {
|
|
259
266
|
reason: 'prefix_below_min',
|
|
260
267
|
estimatedTokens,
|
|
@@ -66,17 +66,28 @@ export function _estimateGeminiCacheTokens(systemInstruction, geminiTools, conte
|
|
|
66
66
|
return Math.ceil(chars / 4);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
69
|
+
// Explicit-cache eligibility and minimum prefix size per model, from the
|
|
70
|
+
// caching docs: explicit caching works on Gemini 3 and 2.5 and is not supported
|
|
71
|
+
// from Gemini 4 onward; documented minimums are 4096 for Gemini 3.x and 2048
|
|
72
|
+
// for 2.5 Flash/Pro. Other ids keep the conservative 4096 floor (skipping a
|
|
73
|
+
// small prefix only falls back to the implicit cache, whereas a floor that is
|
|
74
|
+
// too low would POST a create the server rejects every turn).
|
|
74
75
|
const GEMINI_CACHE_MIN_TOKENS = 4096;
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
// Minimum prefix tokens for an explicit cache, or null when the docs exclude
|
|
78
|
+
// the model from explicit caching.
|
|
79
|
+
export function geminiExplicitCacheMinTokens(model) {
|
|
80
|
+
const bare = String(model || '').replace(/^models\//i, '').toLowerCase();
|
|
81
|
+
const major = /^gemini-(\d+)/.exec(bare);
|
|
82
|
+
if (major && Number(major[1]) >= 4) return null;
|
|
83
|
+
if (/^gemini-2\.5-(?:pro|flash)(?!-lite)(?:-|$)/.test(bare)) return 2048;
|
|
77
84
|
return GEMINI_CACHE_MIN_TOKENS;
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
export function _geminiCacheMinTokens(model) {
|
|
88
|
+
return geminiExplicitCacheMinTokens(model) ?? GEMINI_CACHE_MIN_TOKENS;
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
export function _geminiCachePrefixCount(contents) {
|
|
81
92
|
return Array.isArray(contents) && contents.length > 1 ? contents.length - 1 : 0;
|
|
82
93
|
}
|
|
@@ -128,8 +139,10 @@ export function markGeminiExplicitCacheRefused(model, err) {
|
|
|
128
139
|
return true;
|
|
129
140
|
}
|
|
130
141
|
|
|
142
|
+
// True when explicit cache creates must not be attempted: the docs exclude the
|
|
143
|
+
// model, or the server already refused it.
|
|
131
144
|
export function geminiExplicitCacheRefused(model) {
|
|
132
|
-
return geminiExplicitCacheRefusedModels.has(String(model || ''));
|
|
145
|
+
return geminiExplicitCacheMinTokens(model) === null || geminiExplicitCacheRefusedModels.has(String(model || ''));
|
|
133
146
|
}
|
|
134
147
|
|
|
135
148
|
export function _geminiCredentialFingerprint(apiKey) {
|
|
@@ -245,7 +258,11 @@ export function _resolveGeminiCacheUsage({ usageMetadata, cachedContent, provide
|
|
|
245
258
|
firstFinite(usageMetadata?.thoughtsTokenCount, usageMetadata?.thoughts_token_count);
|
|
246
259
|
const hasExplicitPromptTokens =
|
|
247
260
|
Object.hasOwn(usageMetadata || {}, 'promptTokenCount') || Object.hasOwn(usageMetadata || {}, 'prompt_token_count');
|
|
248
|
-
|
|
261
|
+
// toolUsePromptTokenCount (tool-result prompt tokens) is billed as input and
|
|
262
|
+
// reported separately from promptTokenCount; cached tokens stay a subset of
|
|
263
|
+
// the prompt portion.
|
|
264
|
+
const toolUseTokens = firstFinite(usageMetadata?.toolUsePromptTokenCount, usageMetadata?.tool_use_prompt_token_count);
|
|
265
|
+
const inputTokens = hasExplicitPromptTokens ? promptTokens + toolUseTokens : Math.max(0, totalTokens - outputTokens);
|
|
249
266
|
const reportedCachedTokens = firstFinite(
|
|
250
267
|
// generateContent UsageMetadata field.
|
|
251
268
|
usageMetadata?.cachedContentTokenCount,
|
|
@@ -180,12 +180,94 @@ export function assertNoGeminiInBandError(chunk, label) {
|
|
|
180
180
|
if (err) throw err;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
const
|
|
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
|
+
}
|
|
184
228
|
|
|
185
229
|
/** Policy-refusal code for a finishReason, or null for any other reason. */
|
|
186
230
|
export function geminiPolicyFinishCode(finishReason) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
});
|
|
191
273
|
}
|