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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Code provider (commandcode.ai Provider API).
|
|
3
|
+
*
|
|
4
|
+
* Credentials are a Studio-issued API key (command-code-oauth-tokens.mjs).
|
|
5
|
+
* Each model is served on the endpoint its /models row declares: Chat
|
|
6
|
+
* Completions through the OpenAI-compatible engine, Messages-only models
|
|
7
|
+
* (Claude) through the Anthropic engine. Both clients are pinned to the
|
|
8
|
+
* Command Code API host. The key never expires and has no refresh grant: a
|
|
9
|
+
* 401 means the user must sign in again.
|
|
10
|
+
*/
|
|
11
|
+
import { noteErrorUsage } from './lib/note-error-usage.mjs';
|
|
12
|
+
import { fileMtimeMs, normalizeExpiresAtMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
|
|
13
|
+
import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
|
|
14
|
+
import { COMPAT_RETRY_POLICY } from './openai-compat-retry-profile.mjs';
|
|
15
|
+
import { listCompatModels } from './openai-compat-models.mjs';
|
|
16
|
+
import { AnthropicProvider } from './anthropic.mjs';
|
|
17
|
+
import { toInclusiveOpenCodeGoUsage as toInclusiveUsage } from './opencode-go.mjs';
|
|
18
|
+
import { sanitizeToolPairs } from '../session/context-utils.mjs';
|
|
19
|
+
import { preparePdfTextForProvider } from './media-normalization.mjs';
|
|
20
|
+
import { currentUsageContext } from '../../../shared/llm/usage-context.mjs';
|
|
21
|
+
import { makeModelCache } from './model-cache.mjs';
|
|
22
|
+
import {
|
|
23
|
+
API_ORIGIN,
|
|
24
|
+
CREDITS_URL,
|
|
25
|
+
PROVIDER_BASE_URL,
|
|
26
|
+
PROVIDER_ID,
|
|
27
|
+
commandCodeReauthError,
|
|
28
|
+
getOwnTokenPath,
|
|
29
|
+
loadTokens,
|
|
30
|
+
requestCommandCodeJson,
|
|
31
|
+
} from './command-code-oauth-tokens.mjs';
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
hasCommandCodeOAuthCredentials,
|
|
35
|
+
describeCommandCodeOAuthCredentials,
|
|
36
|
+
forgetCommandCodeOAuthCredentials,
|
|
37
|
+
} from './command-code-oauth-tokens.mjs';
|
|
38
|
+
export { beginOAuthLogin, loginOAuth } from './command-code-oauth-login.mjs';
|
|
39
|
+
|
|
40
|
+
export const DEFAULT_MODEL = 'deepseek/deepseek-v4-flash';
|
|
41
|
+
const CHAT_ENDPOINT = '/chat/completions';
|
|
42
|
+
const MESSAGES_ENDPOINT = '/messages';
|
|
43
|
+
// The Anthropic SDK appends /v1/messages itself.
|
|
44
|
+
const MESSAGES_BASE_URL = `${API_ORIGIN}/provider`;
|
|
45
|
+
const FIVE_HOURS_MS = 5 * 60 * 60 * 1000;
|
|
46
|
+
const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
47
|
+
|
|
48
|
+
const isUnauthorized = (err) => Number(err?.httpStatus || err?.status) === 401;
|
|
49
|
+
|
|
50
|
+
// The discovered roster (what /models returned) is persisted so the shared
|
|
51
|
+
// sync lookup sees what listModels found.
|
|
52
|
+
const commandCodeModelCache = makeModelCache({
|
|
53
|
+
fileName: 'command-code-oauth-models.json',
|
|
54
|
+
ttlMs: 24 * 60 * 60 * 1000,
|
|
55
|
+
version: 1,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The wire a /models row is served on: 'chat' when it declares Chat
|
|
60
|
+
* Completions or declares no endpoints at all, 'messages' when it declares
|
|
61
|
+
* only Messages, null when it declares neither.
|
|
62
|
+
*/
|
|
63
|
+
export function modelWire(item) {
|
|
64
|
+
if (typeof item?.id !== 'string' || !item.id) return null;
|
|
65
|
+
const endpoints = item.supported_endpoints;
|
|
66
|
+
if (!Array.isArray(endpoints)) return 'chat';
|
|
67
|
+
const declared = endpoints.map((endpoint) => String(endpoint).replace(/^\/?(?:provider\/)?(?:v1\/)?/, '/'));
|
|
68
|
+
if (declared.includes(CHAT_ENDPOINT)) return 'chat';
|
|
69
|
+
if (declared.includes(MESSAGES_ENDPOINT)) return 'messages';
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Every request either engine makes goes through this fetch: anything outside
|
|
74
|
+
// the Command Code API origin is refused before the key leaves the process,
|
|
75
|
+
// and redirects are never followed (any 3xx is an error).
|
|
76
|
+
function apiOnlyFetch(fetchImpl) {
|
|
77
|
+
return async (input, init) => {
|
|
78
|
+
const url = new URL(String(input?.url ?? input));
|
|
79
|
+
if (url.origin !== API_ORIGIN) {
|
|
80
|
+
throw new Error(`[command-code-oauth] refusing to send the API key to ${url.host}`);
|
|
81
|
+
}
|
|
82
|
+
const res = await (fetchImpl || globalThis.fetch)(input, { ...init, redirect: 'manual' });
|
|
83
|
+
if (res.status >= 300 && res.status < 400) {
|
|
84
|
+
throw new Error(`[command-code-oauth] refusing to follow a redirect from ${url.host}`);
|
|
85
|
+
}
|
|
86
|
+
return res;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Anthropic engine bound to this credential: only the Command Code key is
|
|
91
|
+
// sent (never an ANTHROPIC_AUTH_TOKEN from the environment) and only to the
|
|
92
|
+
// Command Code API host.
|
|
93
|
+
class CommandCodeMessagesEngine extends AnthropicProvider {
|
|
94
|
+
nativePdf = false;
|
|
95
|
+
_createClient(apiKey) {
|
|
96
|
+
const client = super._createClient(apiKey);
|
|
97
|
+
client.authToken = null;
|
|
98
|
+
client.fetch = apiOnlyFetch(this.config.fetchImpl);
|
|
99
|
+
return client;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const normalizeUsageError = (err) => {
|
|
104
|
+
if (err && typeof err === 'object') {
|
|
105
|
+
if (err.usage) err.usage = toInclusiveUsage(err.usage);
|
|
106
|
+
if (err.partialUsage) err.partialUsage = toInclusiveUsage(err.partialUsage);
|
|
107
|
+
}
|
|
108
|
+
return err;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
function rosterRow(item) {
|
|
112
|
+
const contextWindow = Number(item.context_window ?? item.max_context_window ?? item.context_length ?? 0);
|
|
113
|
+
const outputTokens = Number(item.max_output_tokens ?? item.output_tokens ?? 0);
|
|
114
|
+
const name = [item.display_name, item.name].find((v) => typeof v === 'string' && v.trim());
|
|
115
|
+
return {
|
|
116
|
+
id: item.id,
|
|
117
|
+
provider: PROVIDER_ID,
|
|
118
|
+
...(name ? { name: name.trim() } : {}),
|
|
119
|
+
...(contextWindow > 0 ? { contextWindow } : {}),
|
|
120
|
+
...(outputTokens > 0 ? { outputTokens } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function chooseDefaultModel(ids) {
|
|
125
|
+
const list = ids || [];
|
|
126
|
+
return list.includes(DEFAULT_MODEL) ? DEFAULT_MODEL : list[0] || DEFAULT_MODEL;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Strip the key from an error (message plus any echoed gateway body).
|
|
130
|
+
function scrubErrorSecrets(err, apiKey) {
|
|
131
|
+
if (!err || typeof err !== 'object' || !apiKey) return err;
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
const walk = (obj, depth) => {
|
|
134
|
+
if (!obj || typeof obj !== 'object' || seen.has(obj) || depth > 4) return;
|
|
135
|
+
seen.add(obj);
|
|
136
|
+
for (const k of Object.keys(obj)) {
|
|
137
|
+
const v = obj[k];
|
|
138
|
+
if (typeof v === 'string') {
|
|
139
|
+
if (v.includes(apiKey)) obj[k] = scrubOAuthSecrets(v, [apiKey]);
|
|
140
|
+
} else walk(v, depth + 1);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
if (typeof err.message === 'string') err.message = scrubOAuthSecrets(err.message, [apiKey]);
|
|
144
|
+
walk(err.error, 0);
|
|
145
|
+
walk(err.body, 0);
|
|
146
|
+
return err;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Redact where the transport first surfaces errors (request open and stream
|
|
150
|
+
// iteration), so the shared retry reporter never logs the key.
|
|
151
|
+
function redactClientErrors(client, apiKey) {
|
|
152
|
+
const completions = client?.chat?.completions;
|
|
153
|
+
if (!completions || !apiKey || completions.create?.commandCodeRedacting === apiKey) return;
|
|
154
|
+
const create = completions.create.bind(completions);
|
|
155
|
+
const wrapped = async (...args) => {
|
|
156
|
+
try {
|
|
157
|
+
const stream = await create(...args);
|
|
158
|
+
if (stream && typeof stream[Symbol.asyncIterator] === 'function') {
|
|
159
|
+
const iterate = stream[Symbol.asyncIterator].bind(stream);
|
|
160
|
+
stream[Symbol.asyncIterator] = () => {
|
|
161
|
+
const it = iterate();
|
|
162
|
+
return {
|
|
163
|
+
next: async (...a) => {
|
|
164
|
+
try {
|
|
165
|
+
return await it.next(...a);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
throw scrubErrorSecrets(err, apiKey);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
return: (...a) => it.return?.(...a),
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return stream;
|
|
175
|
+
} catch (err) {
|
|
176
|
+
throw scrubErrorSecrets(err, apiKey);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
wrapped.commandCodeRedacting = apiKey;
|
|
180
|
+
completions.create = wrapped;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Same for the Anthropic client's streaming request (read through
|
|
184
|
+
// `.asResponse()`); every other failure is scrubbed by _shapeError.
|
|
185
|
+
function redactMessagesErrors(client, apiKey) {
|
|
186
|
+
const messages = client?.messages;
|
|
187
|
+
if (!messages || !apiKey || messages.create?.commandCodeRedacting === apiKey) return;
|
|
188
|
+
const create = messages.create.bind(messages);
|
|
189
|
+
const wrapped = (...args) => {
|
|
190
|
+
const request = create(...args);
|
|
191
|
+
const asResponse = request?.asResponse?.bind(request);
|
|
192
|
+
if (asResponse) {
|
|
193
|
+
request.asResponse = () =>
|
|
194
|
+
asResponse().catch((err) => {
|
|
195
|
+
throw scrubErrorSecrets(err, apiKey);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return request;
|
|
199
|
+
};
|
|
200
|
+
wrapped.commandCodeRedacting = apiKey;
|
|
201
|
+
messages.create = wrapped;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function usageStatus(usedFraction) {
|
|
205
|
+
if (usedFraction >= 1) return 'exhausted';
|
|
206
|
+
if (usedFraction >= 0.9) return 'warning';
|
|
207
|
+
return 'ok';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function creditWindow({ id, label, longLabel, durationMs }, entry) {
|
|
211
|
+
const cap = Number(entry?.cap);
|
|
212
|
+
const used = Number(entry?.used);
|
|
213
|
+
if (!entry || !Number.isFinite(cap) || cap <= 0 || !Number.isFinite(used) || used < 0) return null;
|
|
214
|
+
const usedPct = Math.min(100, (used / cap) * 100);
|
|
215
|
+
const usedFraction = usedPct / 100;
|
|
216
|
+
const resetAt = normalizeExpiresAtMs(entry.resetAt);
|
|
217
|
+
return {
|
|
218
|
+
id,
|
|
219
|
+
label,
|
|
220
|
+
longLabel,
|
|
221
|
+
source: 'command-code-credits',
|
|
222
|
+
usedPct: Math.round(usedPct * 100) / 100,
|
|
223
|
+
usedFraction,
|
|
224
|
+
remainingFraction: 1 - usedFraction,
|
|
225
|
+
status: usageStatus(usedFraction),
|
|
226
|
+
durationMs,
|
|
227
|
+
...(resetAt ? { resetAt } : {}),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Map `/alpha/billing/credits` windowLimits onto the shared quota-window shape. */
|
|
232
|
+
export function quotaWindowsFromCredits(body) {
|
|
233
|
+
const limits = (body?.data ?? body)?.windowLimits;
|
|
234
|
+
return [
|
|
235
|
+
creditWindow({ id: '5h', label: '5H', longLabel: '5 Hours', durationMs: FIVE_HOURS_MS }, limits?.fiveHour),
|
|
236
|
+
creditWindow({ id: '1w', label: '7D', longLabel: 'Weekly', durationMs: WEEK_MS }, limits?.weekly),
|
|
237
|
+
].filter(Boolean);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export class CommandCodeOAuthProvider {
|
|
241
|
+
// No document block on this wire: a PDF is lowered to text before the request is built.
|
|
242
|
+
nativePdf = false;
|
|
243
|
+
// OpenAI-compatible usage: prompt_tokens includes cached. See registry.mjs.
|
|
244
|
+
static inputExcludesCache = false;
|
|
245
|
+
name = PROVIDER_ID;
|
|
246
|
+
// The retry profile's limits are final: no loop-level recovery cycles on top.
|
|
247
|
+
retryPolicy = COMPAT_RETRY_POLICY;
|
|
248
|
+
config;
|
|
249
|
+
tokens = null;
|
|
250
|
+
_inner = null;
|
|
251
|
+
_innerKey = null;
|
|
252
|
+
_defaultModel = null;
|
|
253
|
+
_defaultModelKey = null;
|
|
254
|
+
_messages = null;
|
|
255
|
+
_messagesKey = null;
|
|
256
|
+
// Model id → wire ('chat' | 'messages') from the roster fetched with `_wiresKey`.
|
|
257
|
+
_wires = new Map();
|
|
258
|
+
_wiresKey = null;
|
|
259
|
+
|
|
260
|
+
constructor(config) {
|
|
261
|
+
this.config = config || {};
|
|
262
|
+
this.tokens = loadTokens();
|
|
263
|
+
try {
|
|
264
|
+
preloadOpenAICompatRuntime();
|
|
265
|
+
} catch {
|
|
266
|
+
/* authoritative send surfaces load failures */
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
ensureAuth() {
|
|
271
|
+
// Re-read when the token file changed (sign-in from another process).
|
|
272
|
+
const mtime = fileMtimeMs(getOwnTokenPath());
|
|
273
|
+
if (!mtime) {
|
|
274
|
+
// File gone (signed out / forgotten): drop every cached credential.
|
|
275
|
+
this.tokens = null;
|
|
276
|
+
this._inner = null;
|
|
277
|
+
this._innerKey = null;
|
|
278
|
+
this._messages = null;
|
|
279
|
+
this._messagesKey = null;
|
|
280
|
+
}
|
|
281
|
+
if (!this.tokens || mtime > (this.tokens.mtimeMs || 0)) this.tokens = loadTokens() || this.tokens;
|
|
282
|
+
if (!this.tokens?.apiKey) {
|
|
283
|
+
throw new Error('[command-code-oauth] credentials not found — open /providers in mixdog to sign in first');
|
|
284
|
+
}
|
|
285
|
+
return this.tokens;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
_ensureInner() {
|
|
289
|
+
const { apiKey } = this.ensureAuth();
|
|
290
|
+
if (this._inner && this._innerKey === apiKey) return this._inner;
|
|
291
|
+
const { fetchImpl, baseURL: _ignored, ...config } = this.config;
|
|
292
|
+
const inner = new OpenAICompatProvider(PROVIDER_ID, {
|
|
293
|
+
...config,
|
|
294
|
+
apiKey,
|
|
295
|
+
// Fixed: the key is only ever sent to the Command Code API host.
|
|
296
|
+
baseURL: PROVIDER_BASE_URL,
|
|
297
|
+
// Explicit nulls keep OPENAI_ORG_ID / OPENAI_PROJECT_ID out of the requests.
|
|
298
|
+
clientOptions: { organization: null, project: null, fetch: apiOnlyFetch(fetchImpl) },
|
|
299
|
+
extraHeaders: { Accept: 'application/json' },
|
|
300
|
+
});
|
|
301
|
+
inner._fetchModelItems = ({ signal } = {}) => this._fetchRoster(apiKey, signal);
|
|
302
|
+
redactClientErrors(inner.client, apiKey);
|
|
303
|
+
this._inner = inner;
|
|
304
|
+
this._innerKey = apiKey;
|
|
305
|
+
return inner;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// The Anthropic engine for Messages-only models, on the same key and host.
|
|
309
|
+
_ensureMessages(apiKey) {
|
|
310
|
+
if (this._messages && this._messagesKey === apiKey) return this._messages;
|
|
311
|
+
const { baseURL: _ignored, ...config } = this.config;
|
|
312
|
+
const engine = new CommandCodeMessagesEngine({
|
|
313
|
+
...config,
|
|
314
|
+
name: PROVIDER_ID,
|
|
315
|
+
apiKey,
|
|
316
|
+
baseURL: MESSAGES_BASE_URL,
|
|
317
|
+
disableBetaHeaders: true,
|
|
318
|
+
// x-api-key from the SDK plus the bearer form of the same key.
|
|
319
|
+
extraHeaders: { Authorization: `Bearer ${apiKey}` },
|
|
320
|
+
});
|
|
321
|
+
redactMessagesErrors(engine.client, apiKey);
|
|
322
|
+
this._messages = engine;
|
|
323
|
+
this._messagesKey = apiKey;
|
|
324
|
+
return engine;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Servable rows from /models; only the latest fetch with the still-active
|
|
328
|
+
// key publishes the roster and the per-model wire map.
|
|
329
|
+
async _fetchRoster(apiKey, signal = null) {
|
|
330
|
+
const publish = commandCodeModelCache.beginSave(() => loadTokens()?.apiKey === apiKey);
|
|
331
|
+
const body = await requestCommandCodeJson(`${PROVIDER_BASE_URL}/models`, apiKey, {
|
|
332
|
+
fetchImpl: this.config.fetchImpl,
|
|
333
|
+
signal,
|
|
334
|
+
});
|
|
335
|
+
let list = [];
|
|
336
|
+
if (Array.isArray(body?.data)) list = body.data;
|
|
337
|
+
else if (Array.isArray(body)) list = body;
|
|
338
|
+
const items = list.filter((item) => modelWire(item));
|
|
339
|
+
publish(items.map(rosterRow));
|
|
340
|
+
if (this.tokens?.apiKey === apiKey) {
|
|
341
|
+
this._wires = new Map(items.map((item) => [item.id, modelWire(item)]));
|
|
342
|
+
this._wiresKey = apiKey;
|
|
343
|
+
}
|
|
344
|
+
return items;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// The wire for a model, from this credential's roster (fetched once when
|
|
348
|
+
// missing). A model the roster does not know, or a roster that cannot be
|
|
349
|
+
// read right now, stays on Chat Completions; a rejected key is surfaced.
|
|
350
|
+
async _wireFor(inner, model) {
|
|
351
|
+
if (this._wiresKey !== inner.apiKey) {
|
|
352
|
+
try {
|
|
353
|
+
await listCompatModels(inner, { throwOnError: true });
|
|
354
|
+
} catch (err) {
|
|
355
|
+
if (isUnauthorized(err)) throw err;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return (this._wiresKey === inner.apiKey && this._wires.get(model)) || 'chat';
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// A rejected key needs a fresh sign-in; the gateway's own detail stays attached.
|
|
362
|
+
_shapeError(err, apiKey) {
|
|
363
|
+
noteErrorUsage(err);
|
|
364
|
+
scrubErrorSecrets(err, apiKey);
|
|
365
|
+
if (isUnauthorized(err) && !err?.reauthRequired) return commandCodeReauthError(String(err?.message || ''));
|
|
366
|
+
return err;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async _pickDefaultModel(inner) {
|
|
370
|
+
if (this._defaultModel && this._defaultModelKey === this._innerKey) return this._defaultModel;
|
|
371
|
+
const rows = await listCompatModels(inner, { throwOnError: true });
|
|
372
|
+
this._defaultModel = chooseDefaultModel(rows.map((row) => row.id));
|
|
373
|
+
this._defaultModelKey = this._innerKey;
|
|
374
|
+
return this._defaultModel;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async send(messages, model, tools, sendOpts) {
|
|
378
|
+
let inner;
|
|
379
|
+
try {
|
|
380
|
+
inner = this._ensureInner();
|
|
381
|
+
} catch (err) {
|
|
382
|
+
throw this._shapeError(err, this.tokens?.apiKey);
|
|
383
|
+
}
|
|
384
|
+
const apiKey = this._innerKey;
|
|
385
|
+
// The model's declared endpoint picks the wire; a caller cannot move a
|
|
386
|
+
// model to Responses.
|
|
387
|
+
const { compatWireApi: _wire, responsesTransport: _transport, ...opts } = sendOpts || {};
|
|
388
|
+
try {
|
|
389
|
+
const useModel = model || (await this._pickDefaultModel(inner));
|
|
390
|
+
if ((await this._wireFor(inner, useModel)) === 'messages') {
|
|
391
|
+
return await this._sendMessages(apiKey, messages, useModel, tools, opts);
|
|
392
|
+
}
|
|
393
|
+
// _doSend directly: the compat wrapper's 401 handler reloads a static
|
|
394
|
+
// config key, which does not apply to this credential.
|
|
395
|
+
return await inner._doSend(messages, useModel, tools, opts);
|
|
396
|
+
} catch (err) {
|
|
397
|
+
throw this._shapeError(err, apiKey);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Messages wire. Same request preparation as the Anthropic engine's own
|
|
402
|
+
// send, but straight to _doSend: its 401 handler re-reads a static config
|
|
403
|
+
// key and replays, which does not apply to this credential. Anthropic usage
|
|
404
|
+
// excludes cached input; it is reported in this provider's inclusive form.
|
|
405
|
+
async _sendMessages(apiKey, messages, model, tools, opts) {
|
|
406
|
+
const engine = this._ensureMessages(apiKey);
|
|
407
|
+
const prepared = await preparePdfTextForProvider(sanitizeToolPairs(messages), engine);
|
|
408
|
+
const identity = currentUsageContext();
|
|
409
|
+
if (identity) identity.normalizeAbandonedUsage = toInclusiveUsage;
|
|
410
|
+
try {
|
|
411
|
+
const result = await engine._doSend(prepared, model, tools, opts);
|
|
412
|
+
return result?.usage ? { ...result, usage: toInclusiveUsage(result.usage) } : result;
|
|
413
|
+
} catch (err) {
|
|
414
|
+
throw normalizeUsageError(err);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async listModels() {
|
|
419
|
+
let inner;
|
|
420
|
+
try {
|
|
421
|
+
inner = this._ensureInner();
|
|
422
|
+
return await listCompatModels(inner, { throwOnError: true });
|
|
423
|
+
} catch (err) {
|
|
424
|
+
throw this._shapeError(err, this.tokens?.apiKey);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** 5-hour and weekly credit windows for this account's key. */
|
|
429
|
+
async getUsageSnapshot() {
|
|
430
|
+
const { apiKey, userId } = this.ensureAuth();
|
|
431
|
+
const body = await requestCommandCodeJson(CREDITS_URL, apiKey, { fetchImpl: this.config.fetchImpl });
|
|
432
|
+
const quotaWindows = quotaWindowsFromCredits(body);
|
|
433
|
+
if (!quotaWindows.length) return null;
|
|
434
|
+
return {
|
|
435
|
+
provider: PROVIDER_ID,
|
|
436
|
+
model: null,
|
|
437
|
+
source: 'command-code-credits',
|
|
438
|
+
fetchedAt: Date.now(),
|
|
439
|
+
quotaWindows,
|
|
440
|
+
...(userId ? { accountId: userId } : {}),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async isAvailable() {
|
|
445
|
+
return loadTokens() !== null;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
@@ -8,6 +8,7 @@ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
|
|
|
8
8
|
import { openInBrowser } from '../../../shared/open-url.mjs';
|
|
9
9
|
import { createOAuthPkce } from './lib/oauth-pkce.mjs';
|
|
10
10
|
import { accountIdentityFields, normalizeAccountIdentity } from './lib/oauth-token-utils.mjs';
|
|
11
|
+
import { retryAfterMsFromError } from './retry-classifier.mjs';
|
|
11
12
|
|
|
12
13
|
const LOGIN_URL = 'https://cursor.com/loginDeepControl';
|
|
13
14
|
const POLL_URL = 'https://api2.cursor.sh/auth/poll';
|
|
@@ -17,6 +18,23 @@ const REFRESH_SKEW_MS = 5 * 60_000;
|
|
|
17
18
|
// abort signal, which must never govern work every other waiter depends on.
|
|
18
19
|
const REFRESH_TIMEOUT_MS = 30_000;
|
|
19
20
|
const refreshesInFlight = new Map();
|
|
21
|
+
export const REAUTH_GUIDANCE = 'Open /providers in Mixdog to sign in again.';
|
|
22
|
+
// The refresh credential itself was rejected.
|
|
23
|
+
const PERMANENT_REFRESH_STATUS = new Set([400, 401, 403]);
|
|
24
|
+
const EXCHANGE_STATUS_CODE = Object.freeze({
|
|
25
|
+
400: 'invalid_argument',
|
|
26
|
+
401: 'unauthenticated',
|
|
27
|
+
403: 'permission_denied',
|
|
28
|
+
429: 'resource_exhausted',
|
|
29
|
+
500: 'internal',
|
|
30
|
+
502: 'unavailable',
|
|
31
|
+
503: 'unavailable',
|
|
32
|
+
504: 'deadline_exceeded',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
function reauthError(message) {
|
|
36
|
+
return Object.assign(new Error(message), { reauthRequired: true });
|
|
37
|
+
}
|
|
20
38
|
|
|
21
39
|
// Wait on the shared refresh while still honouring THIS caller's abort signal.
|
|
22
40
|
// Aborting only ends this caller's wait; the exchange keeps running for the
|
|
@@ -197,8 +215,25 @@ export async function exchangeCursorToken(token, { fetchFn = fetch, signal } = {
|
|
|
197
215
|
});
|
|
198
216
|
if (!response.ok) {
|
|
199
217
|
const detail = await response.text().catch(() => '');
|
|
200
|
-
|
|
218
|
+
// Only a credential verdict is permanent; throttles, 5xx and network
|
|
219
|
+
// failures leave the refresh token valid and stay retryable upstream.
|
|
220
|
+
const reauthRequired = PERMANENT_REFRESH_STATUS.has(response.status);
|
|
221
|
+
const error = new Error(
|
|
222
|
+
`Cursor token exchange failed (${response.status})${detail ? `: ${detail}` : ''}${
|
|
223
|
+
reauthRequired ? `. ${REAUTH_GUIDANCE}` : ''
|
|
224
|
+
}`
|
|
225
|
+
);
|
|
201
226
|
error.status = response.status;
|
|
227
|
+
error.httpStatus = response.status;
|
|
228
|
+
const code = EXCHANGE_STATUS_CODE[response.status];
|
|
229
|
+
if (code) error.code = code;
|
|
230
|
+
const headers = Object.fromEntries(response.headers?.entries?.() ?? []);
|
|
231
|
+
if (Object.keys(headers).length) {
|
|
232
|
+
error.headers = headers;
|
|
233
|
+
const retryAfterMs = retryAfterMsFromError(error);
|
|
234
|
+
if (Number.isFinite(retryAfterMs)) error.retryAfterMs = retryAfterMs;
|
|
235
|
+
}
|
|
236
|
+
if (reauthRequired) error.reauthRequired = true;
|
|
202
237
|
throw error;
|
|
203
238
|
}
|
|
204
239
|
const data = await response.json();
|
|
@@ -277,9 +312,7 @@ export async function resolveCursorOAuthAccessToken({ forceRefresh = false, fetc
|
|
|
277
312
|
if (!forceRefresh && !expiring) return tokens.access_token;
|
|
278
313
|
if (!tokens.refresh_token) {
|
|
279
314
|
if (!tokens.expires_at || tokens.expires_at > Date.now()) return tokens.access_token;
|
|
280
|
-
throw
|
|
281
|
-
'Cursor access token expired and has no refresh token. Open /providers in Mixdog to sign in again.'
|
|
282
|
-
);
|
|
315
|
+
throw reauthError(`Cursor access token expired and has no refresh token. ${REAUTH_GUIDANCE}`);
|
|
283
316
|
}
|
|
284
317
|
const refreshPath = credentialsPath();
|
|
285
318
|
let refreshInFlight = refreshesInFlight.get(refreshPath);
|
|
@@ -296,7 +329,7 @@ export async function resolveCursorOAuthAccessToken({ forceRefresh = false, fetc
|
|
|
296
329
|
return latest;
|
|
297
330
|
}
|
|
298
331
|
if (!latest.refresh_token) {
|
|
299
|
-
throw
|
|
332
|
+
throw reauthError(`Cursor OAuth refresh token is unavailable. ${REAUTH_GUIDANCE}`);
|
|
300
333
|
}
|
|
301
334
|
// Deliberately NOT the caller's signal: this exchange is shared by
|
|
302
335
|
// every concurrent waiter, so letting the first caller's abort
|
|
@@ -4,7 +4,7 @@ import { createRemoteVersionSource } from './npm-cli-version.mjs';
|
|
|
4
4
|
// documented version endpoint; the official installer script embeds the
|
|
5
5
|
// current build (…/lab/<build>/…), parsed best-effort. Any format change or
|
|
6
6
|
// failure falls back to the floor.
|
|
7
|
-
export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.10.
|
|
7
|
+
export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.10.14-0f9edde';
|
|
8
8
|
const BUILD_PATTERN = /downloads\.cursor\.com\/lab\/(\d{4}\.\d{2}\.\d{2}-[0-9a-f]{6,40})\//;
|
|
9
9
|
export const CURSOR_INSTALL_URL = 'https://cursor.com/install';
|
|
10
10
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { mergeFlatSchemaProperty } from './lib/flat-schema-merge.mjs';
|
|
2
|
+
import { REAUTH_GUIDANCE } from './cursor-auth.mjs';
|
|
3
|
+
import { isTransientErrorCode } from './provider-error-kind.mjs';
|
|
2
4
|
|
|
3
5
|
const SCHEMA_ANNOTATIONS = new Set([
|
|
4
6
|
'$comment',
|
|
@@ -32,6 +34,15 @@ export function resolveCursorStreamTuning(env = process.env) {
|
|
|
32
34
|
idleTimeoutMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_IDLE_TIMEOUT_MS, 180_000),
|
|
33
35
|
parkTimeoutMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_PARK_TIMEOUT_MS, 45_000, { min: 1_000 }),
|
|
34
36
|
maxRetries: boundedInteger(env.MIXDOG_CURSOR_STREAM_IDLE_MAX_RETRIES, 5, { max: 10 }),
|
|
37
|
+
// Transport retry: 250ms base, 2s ceiling.
|
|
38
|
+
retryBaseDelayMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_RETRY_BASE_MS, 250, { max: 60_000 }),
|
|
39
|
+
retryMaxDelayMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_RETRY_MAX_MS, 2_000, { max: 120_000 }),
|
|
40
|
+
// A rate limit gets one recovery (min(1, maxRetries)).
|
|
41
|
+
maxThrottleRetries: boundedInteger(env.MIXDOG_CURSOR_THROTTLE_MAX_RETRIES, 1, { max: 5 }),
|
|
42
|
+
// One credential-refresh retry; the outer provider layer adds none for a
|
|
43
|
+
// stream that already ran, and re-sending a rejected credential only
|
|
44
|
+
// hammers the token endpoint.
|
|
45
|
+
maxAuthRetries: boundedInteger(env.MIXDOG_CURSOR_AUTH_MAX_RETRIES, 1, { max: 5 }),
|
|
35
46
|
};
|
|
36
47
|
}
|
|
37
48
|
|
|
@@ -221,6 +232,13 @@ export function createCursorStreamWatchdog({ idleTimeoutMs, parkTimeoutMs, onTim
|
|
|
221
232
|
if (kind === 'work') arm(idleTimeoutMs, 'idle');
|
|
222
233
|
else if (kind === 'park') arm(parkTimeoutMs, 'park');
|
|
223
234
|
},
|
|
235
|
+
// Suspend both deadlines during an intentional retry wait; the next
|
|
236
|
+
// start()/progress('work'|'park') re-arms them, so only real stream
|
|
237
|
+
// silence is ever counted.
|
|
238
|
+
pause() {
|
|
239
|
+
if (timer) clearTimeout(timer);
|
|
240
|
+
timer = null;
|
|
241
|
+
},
|
|
224
242
|
stop() {
|
|
225
243
|
stopped = true;
|
|
226
244
|
if (timer) clearTimeout(timer);
|
|
@@ -251,8 +269,85 @@ export function cursorInteractionProgress(update) {
|
|
|
251
269
|
return 'none';
|
|
252
270
|
}
|
|
253
271
|
|
|
272
|
+
// Codes that describe a transient server verdict or a recognized transport
|
|
273
|
+
// truncation. Everything else (protocol/decoding errors, 4xx verdicts,
|
|
274
|
+
// unknown failures) is deterministic and is never replayed in place.
|
|
275
|
+
const TRANSIENT_STREAM_CODES = new Set([
|
|
276
|
+
'unavailable',
|
|
277
|
+
'internal',
|
|
278
|
+
'deadline_exceeded',
|
|
279
|
+
'stream_aborted',
|
|
280
|
+
'goaway',
|
|
281
|
+
'connection_timeout',
|
|
282
|
+
'stream_idle_timeout',
|
|
283
|
+
'stream_park_timeout',
|
|
284
|
+
'incomplete_stream',
|
|
285
|
+
'ERR_HTTP2_STREAM_CANCEL',
|
|
286
|
+
'ERR_HTTP2_GOAWAY_SESSION',
|
|
287
|
+
]);
|
|
288
|
+
|
|
289
|
+
// Protocol-mismatch check: Cursor wraps a wire mismatch in `internal`, and
|
|
290
|
+
// re-sending a request the schema cannot decode only burns the retry budget.
|
|
291
|
+
// Tested on the server's own detail, before the transient code is trusted.
|
|
292
|
+
const PROTOCOL_DRIFT_RE = /\b(protocol|protobuf|wire drift|invalid argument|failed_precondition)\b/i;
|
|
293
|
+
|
|
294
|
+
export function isCursorProtocolDrift(error) {
|
|
295
|
+
if (error?.code === 'protocol_error') return true;
|
|
296
|
+
return PROTOCOL_DRIFT_RE.test(String(error?.cursorDetail ?? ''));
|
|
297
|
+
}
|
|
298
|
+
|
|
254
299
|
export function isRetryableCursorStreamError(error) {
|
|
255
|
-
const status = Number(error?.httpStatus || error?.status || 0);
|
|
256
300
|
if (error?.name === 'AbortError') return false;
|
|
257
|
-
|
|
301
|
+
const code = String(error?.code || '');
|
|
302
|
+
if (!TRANSIENT_STREAM_CODES.has(code) && !isTransientErrorCode(code)) return false;
|
|
303
|
+
return !isCursorProtocolDrift(error);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Rejected credentials: worth one forced token refresh before giving up.
|
|
307
|
+
export function isCursorAuthError(error) {
|
|
308
|
+
if (error?.name === 'AbortError') return false;
|
|
309
|
+
return error?.code === 'unauthenticated' || error?.code === 'permission_denied';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Credentials for in-place restarts. Every retry force-refreshes and keeps the
|
|
313
|
+
// previous token if the refresh fails; a rejected credential is
|
|
314
|
+
// only retried when the refresh produced a different token.
|
|
315
|
+
export function createCursorCredentialRefresher(initialToken, getAccessToken) {
|
|
316
|
+
let current = initialToken;
|
|
317
|
+
return {
|
|
318
|
+
async refresh(reason, error) {
|
|
319
|
+
let next = current;
|
|
320
|
+
if (getAccessToken) {
|
|
321
|
+
try {
|
|
322
|
+
next = (await getAccessToken({ forceRefresh: true })) || current;
|
|
323
|
+
} catch {
|
|
324
|
+
/* keep the previous token */
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (reason === 'auth' && next === current) throw error;
|
|
328
|
+
current = next;
|
|
329
|
+
return current;
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Final-failure guidance appended once to the original Cursor detail
|
|
335
|
+
// (transport-failure hint).
|
|
336
|
+
export function withCursorFailureHint(error) {
|
|
337
|
+
if (!(error instanceof Error) || error.cursorHintApplied) return error;
|
|
338
|
+
const hint = isCursorAuthError(error)
|
|
339
|
+
? REAUTH_GUIDANCE
|
|
340
|
+
: isCursorProtocolDrift(error)
|
|
341
|
+
? 'Cursor may have changed its wire protocol; update Mixdog or set MIXDOG_CURSOR_CLIENT_VERSION if this persists.'
|
|
342
|
+
: '';
|
|
343
|
+
if (!hint) return error;
|
|
344
|
+
error.cursorHintApplied = true;
|
|
345
|
+
error.message = `${error.message} ${hint}`;
|
|
346
|
+
return error;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// resource_exhausted / 429 that is a throttle, not an oversized request.
|
|
350
|
+
export function isCursorThrottleError(error) {
|
|
351
|
+
if (error?.name === 'AbortError' || error?.code === 'request_too_large') return false;
|
|
352
|
+
return error?.code === 'resource_exhausted' || Number(error?.httpStatus || error?.status || 0) === 429;
|
|
258
353
|
}
|