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
|
@@ -3,11 +3,30 @@
|
|
|
3
3
|
* response: frame parsing, tool-batch hand-off to the run registry, the
|
|
4
4
|
* close verdict and the bounded in-place restart.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
isCursorAuthError,
|
|
8
|
+
isCursorThrottleError,
|
|
9
|
+
isRetryableCursorStreamError,
|
|
10
|
+
withCursorFailureHint,
|
|
11
|
+
} from './cursor-wire-guards.mjs';
|
|
12
|
+
import { emitProviderRetryStage, markProviderRecoveryExhausted, retryAfterMsFromError } from './retry-classifier.mjs';
|
|
8
13
|
import { createFrameParser, cursorError, parseEndStream } from './cursor-wire-transport.mjs';
|
|
9
14
|
import { processServerMessage } from './cursor-wire-stream-messages.mjs';
|
|
10
15
|
|
|
16
|
+
// Resolves after `ms`, or early when the stream is cancelled.
|
|
17
|
+
function abortableSleep(ms, signal) {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
if (signal?.aborted) return resolve();
|
|
20
|
+
const done = () => {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
signal?.removeEventListener('abort', done);
|
|
23
|
+
resolve();
|
|
24
|
+
};
|
|
25
|
+
const timer = setTimeout(done, ms);
|
|
26
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
/**
|
|
12
31
|
* @param {object} ctx
|
|
13
32
|
* @param {{ bridge, heartbeat }} ctx.live the bridge currently feeding the stream (mutated on restart)
|
|
@@ -16,7 +35,10 @@ import { processServerMessage } from './cursor-wire-stream-messages.mjs';
|
|
|
16
35
|
*/
|
|
17
36
|
export function createBridgeController(ctx) {
|
|
18
37
|
const { live, state, filter, watchdog, sink, tuning, run, runs, restart, isCancelled } = ctx;
|
|
38
|
+
const { sleep = abortableSleep, cancelSignal = null, onStageChange = null } = ctx;
|
|
19
39
|
let retryCount = 0;
|
|
40
|
+
let throttleCount = 0;
|
|
41
|
+
let authCount = 0;
|
|
20
42
|
|
|
21
43
|
const finishToolBatch = () => {
|
|
22
44
|
if (state.closed || state.pending.length === 0) return;
|
|
@@ -35,37 +57,91 @@ export function createBridgeController(ctx) {
|
|
|
35
57
|
sink.finish('tool_calls');
|
|
36
58
|
};
|
|
37
59
|
|
|
60
|
+
// Exponential backoff with +-20% jitter for in-place retries.
|
|
61
|
+
const backoffMs = (attempt) => {
|
|
62
|
+
const ceiling = Math.min(tuning.retryMaxDelayMs, tuning.retryBaseDelayMs * 2 ** (attempt - 1));
|
|
63
|
+
return Math.floor(ceiling * (0.8 + Math.random() * 0.4));
|
|
64
|
+
};
|
|
65
|
+
|
|
38
66
|
const recoverOrFail = (error) => {
|
|
39
67
|
const retryable = isRetryableCursorStreamError(error);
|
|
68
|
+
// A throttle retries within the bounded throttle retry count; otherwise
|
|
69
|
+
// the error surfaces with retryAfterMs for the outer layer.
|
|
70
|
+
const throttled = !retryable && isCursorThrottleError(error);
|
|
71
|
+
// Rejected credentials get a forced token refresh before the retry.
|
|
72
|
+
const authFailure = !retryable && !throttled && isCursorAuthError(error);
|
|
73
|
+
// A throttle retry rotates the conversation and drops the checkpoint
|
|
74
|
+
// so unlike other recoveries it can only replay a run that
|
|
75
|
+
// has shown nothing; the rest may also continue from a checkpoint.
|
|
40
76
|
const recoveryEligible =
|
|
41
|
-
!isCancelled() &&
|
|
42
|
-
|
|
77
|
+
!isCancelled() &&
|
|
78
|
+
typeof restart === 'function' &&
|
|
79
|
+
(!state.visibleOutput || (!throttled && Boolean(run.conversation.checkpoint)));
|
|
80
|
+
const serverDelay = retryAfterMsFromError(error);
|
|
81
|
+
// Server advice is honored in full for every retryable kind; local
|
|
82
|
+
// backoff applies only when the server gave none.
|
|
83
|
+
const delayMs = Number.isFinite(serverDelay)
|
|
84
|
+
? serverDelay
|
|
85
|
+
: backoffMs(throttled ? throttleCount + 1 : retryCount + 1);
|
|
86
|
+
const throttleOk = throttled && throttleCount < tuning.maxThrottleRetries;
|
|
87
|
+
const authOk = authFailure && authCount < tuning.maxAuthRetries;
|
|
88
|
+
const canRetry =
|
|
89
|
+
retryCount < tuning.maxRetries && recoveryEligible && (retryable || throttleOk || authOk);
|
|
43
90
|
if (!canRetry) {
|
|
44
91
|
// A fresh Cursor run owns its bounded in-place retries. Mark
|
|
45
92
|
// exhaustion so the outer loop does not multiply that budget.
|
|
46
93
|
// Resumed tool-result streams have no restart closure; those
|
|
47
94
|
// intentionally fall through unmarked so the outer loop can
|
|
48
95
|
// rebuild from the committed assistant/tool-result history.
|
|
49
|
-
|
|
96
|
+
// A throttle that spent its bounded retries is final for the turn too.
|
|
97
|
+
if (
|
|
98
|
+
recoveryEligible &&
|
|
99
|
+
((retryable && retryCount >= tuning.maxRetries) ||
|
|
100
|
+
(throttled && throttleCount >= tuning.maxThrottleRetries))
|
|
101
|
+
) {
|
|
50
102
|
markProviderRecoveryExhausted(error, { owner: 'cursor-wire', attempts: retryCount + 1 });
|
|
51
103
|
}
|
|
52
|
-
sink.fail(error);
|
|
104
|
+
sink.fail(withCursorFailureHint(error));
|
|
53
105
|
return;
|
|
54
106
|
}
|
|
55
107
|
retryCount += 1;
|
|
108
|
+
if (throttled) throttleCount += 1;
|
|
109
|
+
if (authFailure) authCount += 1;
|
|
56
110
|
state.sawEnd = false;
|
|
57
111
|
state.batchBoundaryReady = false;
|
|
58
112
|
state.batchBoundaryChunkSeq = -1;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
113
|
+
// The wait is intentional, not stream silence; attach() re-arms on restart.
|
|
114
|
+
watchdog.pause();
|
|
115
|
+
emitProviderRetryStage(onStageChange, {
|
|
116
|
+
attempt: retryCount + 1,
|
|
117
|
+
maxAttempts: tuning.maxRetries + 1,
|
|
118
|
+
lastErr: error,
|
|
119
|
+
delayMs,
|
|
120
|
+
});
|
|
121
|
+
void sleep(delayMs, cancelSignal).then(async () => {
|
|
122
|
+
if (isCancelled() || state.closed) return;
|
|
123
|
+
try {
|
|
124
|
+
// restart() may refresh credentials first, so it can be async.
|
|
125
|
+
const next = await restart({
|
|
126
|
+
attempt: retryCount,
|
|
127
|
+
fromCheckpoint: !throttled && Boolean(run.conversation.checkpoint),
|
|
128
|
+
visibleOutput: state.visibleOutput,
|
|
129
|
+
reason: throttled ? 'throttle' : authFailure ? 'auth' : 'transient',
|
|
130
|
+
error,
|
|
131
|
+
});
|
|
132
|
+
if (isCancelled() || state.closed) {
|
|
133
|
+
clearInterval(next.heartbeat);
|
|
134
|
+
next.bridge.close();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
attach(next.bridge, next.heartbeat);
|
|
138
|
+
try {
|
|
139
|
+
onStageChange?.('streaming');
|
|
140
|
+
} catch {}
|
|
141
|
+
} catch (restartError) {
|
|
142
|
+
sink.fail(withCursorFailureHint(restartError));
|
|
143
|
+
}
|
|
144
|
+
});
|
|
69
145
|
};
|
|
70
146
|
|
|
71
147
|
/** The Connect end frame: an error closes, pending tools delimit, a finished turn completes. */
|
|
@@ -99,20 +175,23 @@ export function createBridgeController(ctx) {
|
|
|
99
175
|
closeError = frameError;
|
|
100
176
|
}
|
|
101
177
|
}
|
|
102
|
-
// Cursor commonly closes HTTP/2 immediately after turnEnded without
|
|
103
|
-
// a separate Connect end frame. The turn is already complete.
|
|
104
|
-
if (state.sawTurnEnded) {
|
|
105
|
-
sink.finish();
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
178
|
// Tool calls already emitted to the caller remain actionable even if
|
|
109
179
|
// the parked transport vanished. The next request rebuilds/resumes.
|
|
180
|
+
// Checked before turnEnded: a close that follows turnEnded only ends a
|
|
181
|
+
// completed turn when no exec is still waiting (turn ended
|
|
182
|
+
// with no MCP exec outstanding), never by dropping a tool call.
|
|
110
183
|
if (state.pending.length > 0) {
|
|
111
184
|
sink.finish('tool_calls');
|
|
112
185
|
return;
|
|
113
186
|
}
|
|
187
|
+
// Cursor commonly closes HTTP/2 immediately after turnEnded, with or
|
|
188
|
+
// without an end-stream error. The turn is already complete.
|
|
189
|
+
if (state.sawTurnEnded) {
|
|
190
|
+
sink.finish();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
114
193
|
if (!closeError && !state.sawEnd) {
|
|
115
|
-
closeError = cursorError('Cursor stream closed before its end frame', { code: '
|
|
194
|
+
closeError = cursorError('Cursor stream closed before its end frame', { code: 'incomplete_stream' });
|
|
116
195
|
}
|
|
117
196
|
if (closeError) recoverOrFail(closeError);
|
|
118
197
|
else sink.finish();
|
|
@@ -4,6 +4,7 @@ import crypto from 'node:crypto';
|
|
|
4
4
|
import http2 from 'node:http2';
|
|
5
5
|
import { MAX_CONNECT_FRAME_BYTES, createCursorByteQueue } from './cursor-wire-guards.mjs';
|
|
6
6
|
import { cursorClientVersion, warmCursorClientVersion } from './cursor-client-version.mjs';
|
|
7
|
+
import { retryAfterMsFromError } from './retry-classifier.mjs';
|
|
7
8
|
|
|
8
9
|
export { warmCursorClientVersion };
|
|
9
10
|
|
|
@@ -40,7 +41,66 @@ function rpcHeaders(accessToken, path, unary) {
|
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
// HTTP-equivalent status for every Connect error code.
|
|
45
|
+
const CONNECT_CODE_STATUS = Object.freeze({
|
|
46
|
+
canceled: 499,
|
|
47
|
+
unknown: 500,
|
|
48
|
+
invalid_argument: 400,
|
|
49
|
+
deadline_exceeded: 504,
|
|
50
|
+
not_found: 404,
|
|
51
|
+
already_exists: 409,
|
|
52
|
+
permission_denied: 403,
|
|
53
|
+
resource_exhausted: 429,
|
|
54
|
+
failed_precondition: 400,
|
|
55
|
+
aborted: 409,
|
|
56
|
+
out_of_range: 400,
|
|
57
|
+
unimplemented: 501,
|
|
58
|
+
internal: 500,
|
|
59
|
+
unavailable: 503,
|
|
60
|
+
data_loss: 500,
|
|
61
|
+
unauthenticated: 401,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Connect code for a plain HTTP failure that carried no Connect error body.
|
|
65
|
+
const HTTP_STATUS_CODE = Object.freeze({
|
|
66
|
+
401: 'unauthenticated',
|
|
67
|
+
403: 'permission_denied',
|
|
68
|
+
404: 'not_found',
|
|
69
|
+
429: 'resource_exhausted',
|
|
70
|
+
500: 'internal',
|
|
71
|
+
502: 'unavailable',
|
|
72
|
+
503: 'unavailable',
|
|
73
|
+
504: 'deadline_exceeded',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Only explicit wording about the tool catalog or request payload being too
|
|
77
|
+
// large counts; resource_exhausted otherwise stays a throttle.
|
|
78
|
+
const REQUEST_TOO_LARGE_RE =
|
|
79
|
+
/\b(?:tool\s+(?:catalog|definitions?|schemas?)|tools|payload|request(?:\s+body)?)\b[^.\n]{0,80}?\b(?:too\s+(?:large|big)|exceed(?:s|ed)?\b[^.\n]{0,30}?\b(?:size|limit|maximum))/i;
|
|
80
|
+
|
|
81
|
+
// Rate/quota wording wins over the size match: "request exceeded the rate
|
|
82
|
+
// limit" is a throttle, not an oversized request.
|
|
83
|
+
const THROTTLE_CUE_RE = /too\s+many\s+requests|quota|rate[\s_-]*limit|throttl/i;
|
|
84
|
+
|
|
85
|
+
function firstValue(value) {
|
|
86
|
+
return Array.isArray(value) ? value[0] : value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Lowercased string-valued header map without HTTP/2 pseudo-headers.
|
|
90
|
+
function plainHeaders(source) {
|
|
91
|
+
const headers = {};
|
|
92
|
+
for (const [key, value] of Object.entries(source || {})) {
|
|
93
|
+
const first = firstValue(value);
|
|
94
|
+
if (key.startsWith(':') || first == null || typeof first === 'object') continue;
|
|
95
|
+
headers[key.toLowerCase()] = String(first);
|
|
96
|
+
}
|
|
97
|
+
return headers;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function cursorError(
|
|
101
|
+
message,
|
|
102
|
+
{ status = 0, code = '', cursorCode = code, headers = null, retryAfter = null, reauthRequired = false, detail } = {}
|
|
103
|
+
) {
|
|
44
104
|
const error = new Error(message);
|
|
45
105
|
if (status) {
|
|
46
106
|
error.status = status;
|
|
@@ -48,17 +108,32 @@ export function cursorError(message, { status = 0, code = '', retryAfter = null
|
|
|
48
108
|
}
|
|
49
109
|
if (code) {
|
|
50
110
|
error.code = code;
|
|
51
|
-
error.cursorCode =
|
|
111
|
+
error.cursorCode = cursorCode;
|
|
52
112
|
}
|
|
113
|
+
if (detail != null) error.cursorDetail = detail;
|
|
114
|
+
if (reauthRequired) error.reauthRequired = true;
|
|
115
|
+
const errorHeaders = plainHeaders(headers);
|
|
53
116
|
if (retryAfter != null && retryAfter !== '') {
|
|
54
|
-
const headers = { 'retry-after': String(retryAfter) };
|
|
55
117
|
error.retryAfter = retryAfter;
|
|
56
|
-
|
|
57
|
-
|
|
118
|
+
errorHeaders['retry-after'] = String(retryAfter);
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(errorHeaders).length) {
|
|
121
|
+
error.headers = errorHeaders;
|
|
122
|
+
error.response = { status, headers: errorHeaders };
|
|
123
|
+
const retryAfterMs = retryAfterMsFromError(error);
|
|
124
|
+
if (Number.isFinite(retryAfterMs)) error.retryAfterMs = retryAfterMs;
|
|
58
125
|
}
|
|
59
126
|
return error;
|
|
60
127
|
}
|
|
61
128
|
|
|
129
|
+
function httpStatusError(status, headers) {
|
|
130
|
+
return cursorError(`Cursor request failed (${status})`, {
|
|
131
|
+
status,
|
|
132
|
+
code: HTTP_STATUS_CODE[status] || '',
|
|
133
|
+
headers,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
62
137
|
export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }) {
|
|
63
138
|
const session = http2.connect(url);
|
|
64
139
|
const request = session.request(rpcHeaders(accessToken, path, false));
|
|
@@ -66,7 +141,7 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
66
141
|
let closeHandler = null;
|
|
67
142
|
let closed = false;
|
|
68
143
|
let status = 0;
|
|
69
|
-
let
|
|
144
|
+
let responseHeaders = null;
|
|
70
145
|
let closeError = null;
|
|
71
146
|
let timeout = setTimeout(
|
|
72
147
|
() => close(cursorError('Cursor connection timed out', { code: 'connection_timeout' })),
|
|
@@ -97,8 +172,7 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
97
172
|
closed = true;
|
|
98
173
|
clearTimeout(timeout);
|
|
99
174
|
clearInterval(ping);
|
|
100
|
-
closeError =
|
|
101
|
-
error || (status >= 400 ? cursorError(`Cursor request failed (${status})`, { status, retryAfter }) : null);
|
|
175
|
+
closeError = error || (status >= 400 ? httpStatusError(status, responseHeaders) : null);
|
|
102
176
|
try {
|
|
103
177
|
request.close();
|
|
104
178
|
} catch {}
|
|
@@ -120,7 +194,7 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
120
194
|
|
|
121
195
|
request.on('response', (headers) => {
|
|
122
196
|
status = Number(headers[':status'] || 0);
|
|
123
|
-
|
|
197
|
+
responseHeaders = headers;
|
|
124
198
|
resetTimeout();
|
|
125
199
|
});
|
|
126
200
|
request.on('data', (chunk) => {
|
|
@@ -167,7 +241,7 @@ export async function callCursorUnary({ accessToken, path, body, url = API_URL,
|
|
|
167
241
|
const request = session.request(rpcHeaders(accessToken, path, true));
|
|
168
242
|
const chunks = [];
|
|
169
243
|
let status = 0;
|
|
170
|
-
let
|
|
244
|
+
let responseHeaders = null;
|
|
171
245
|
let settled = false;
|
|
172
246
|
const timeout = setTimeout(() => finish(new Error('Cursor request timed out')), timeoutMs);
|
|
173
247
|
const finish = (error = null) => {
|
|
@@ -181,12 +255,12 @@ export async function callCursorUnary({ accessToken, path, body, url = API_URL,
|
|
|
181
255
|
session.close();
|
|
182
256
|
} catch {}
|
|
183
257
|
if (error) reject(error);
|
|
184
|
-
else if (status >= 400) reject(
|
|
258
|
+
else if (status >= 400) reject(httpStatusError(status, responseHeaders));
|
|
185
259
|
else resolve(Buffer.concat(chunks));
|
|
186
260
|
};
|
|
187
261
|
request.on('response', (headers) => {
|
|
188
262
|
status = Number(headers[':status'] || 0);
|
|
189
|
-
|
|
263
|
+
responseHeaders = headers;
|
|
190
264
|
});
|
|
191
265
|
request.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
|
192
266
|
request.on('end', () => finish());
|
|
@@ -235,7 +309,7 @@ export function createFrameParser(onMessage, onEnd) {
|
|
|
235
309
|
};
|
|
236
310
|
parse.finish = () => {
|
|
237
311
|
if (pending.byteLength) {
|
|
238
|
-
throw cursorError('Cursor stream ended with a truncated frame', { code: '
|
|
312
|
+
throw cursorError('Cursor stream ended with a truncated frame', { code: 'incomplete_stream' });
|
|
239
313
|
}
|
|
240
314
|
};
|
|
241
315
|
parse.bufferedBytes = () => pending.byteLength;
|
|
@@ -246,26 +320,22 @@ export function parseEndStream(bytes) {
|
|
|
246
320
|
try {
|
|
247
321
|
const payload = JSON.parse(textDecoder.decode(bytes));
|
|
248
322
|
if (!payload?.error) return null;
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
retryAfter:
|
|
264
|
-
|
|
265
|
-
payload.error.retry_after ??
|
|
266
|
-
payload.metadata?.['retry-after'] ??
|
|
267
|
-
payload.metadata?.retryAfter ??
|
|
268
|
-
null,
|
|
323
|
+
const connectCode = String(payload.error.code || 'error').toLowerCase();
|
|
324
|
+
const detail = String(payload.error.message || 'request failed');
|
|
325
|
+
const tooLarge = connectCode === 'resource_exhausted' && !THROTTLE_CUE_RE.test(detail) && REQUEST_TOO_LARGE_RE.test(detail);
|
|
326
|
+
const retryAfter =
|
|
327
|
+
payload.error.retryAfter ??
|
|
328
|
+
payload.error.retry_after ??
|
|
329
|
+
payload.metadata?.['retry-after'] ??
|
|
330
|
+
payload.metadata?.retryAfter ??
|
|
331
|
+
null;
|
|
332
|
+
return cursorError(`Cursor ${connectCode}: ${detail}`, {
|
|
333
|
+
status: tooLarge ? 413 : CONNECT_CODE_STATUS[connectCode] || 0,
|
|
334
|
+
code: tooLarge ? 'request_too_large' : connectCode,
|
|
335
|
+
cursorCode: connectCode,
|
|
336
|
+
headers: payload.metadata,
|
|
337
|
+
retryAfter: firstValue(retryAfter),
|
|
338
|
+
detail,
|
|
269
339
|
});
|
|
270
340
|
} catch {
|
|
271
341
|
return cursorError('Cursor returned an invalid end-stream frame', { code: 'protocol_error' });
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from './cursor-wire-protobuf.mjs';
|
|
15
15
|
import {
|
|
16
16
|
assertCursorUserImages,
|
|
17
|
+
createCursorCredentialRefresher,
|
|
17
18
|
createCursorStreamWatchdog,
|
|
18
19
|
resolveCursorStreamTuning,
|
|
19
20
|
storeCursorBlob,
|
|
@@ -358,12 +359,16 @@ function createStreamResponse({
|
|
|
358
359
|
sessionId = '',
|
|
359
360
|
sawTurnEnded = false,
|
|
360
361
|
restart = null,
|
|
362
|
+
sleep = undefined,
|
|
363
|
+
onStageChange = null,
|
|
361
364
|
}) {
|
|
362
365
|
const id = newCompletionId();
|
|
363
366
|
// The bridge currently feeding the stream; a restart swaps it in place.
|
|
364
367
|
const live = { bridge, heartbeat };
|
|
365
368
|
let watchdog = null;
|
|
366
369
|
let cancelled = false;
|
|
370
|
+
// Cuts a pending retry backoff short when the caller cancels.
|
|
371
|
+
const cancelController = new AbortController();
|
|
367
372
|
const runs = { get: (runKey) => activeRuns.get(runKey), store: storeActiveRun, forget: forgetActiveRun };
|
|
368
373
|
const stream = new ReadableStream({
|
|
369
374
|
start(controller) {
|
|
@@ -395,12 +400,16 @@ function createStreamResponse({
|
|
|
395
400
|
run: { key, conversation, tools, cloudRule, modelParameters, maxMode, sessionId },
|
|
396
401
|
runs,
|
|
397
402
|
restart,
|
|
403
|
+
sleep,
|
|
404
|
+
onStageChange,
|
|
405
|
+
cancelSignal: cancelController.signal,
|
|
398
406
|
isCancelled: () => cancelled,
|
|
399
407
|
});
|
|
400
408
|
bridges.attach(bridge, heartbeat);
|
|
401
409
|
},
|
|
402
410
|
cancel(reason) {
|
|
403
411
|
cancelled = true;
|
|
412
|
+
cancelController.abort();
|
|
404
413
|
watchdog?.stop();
|
|
405
414
|
clearInterval(live.heartbeat);
|
|
406
415
|
const active = activeRuns.get(key);
|
|
@@ -459,7 +468,7 @@ function noUserMessageResponse() {
|
|
|
459
468
|
});
|
|
460
469
|
}
|
|
461
470
|
|
|
462
|
-
export async function handleChatCompletion(body, accessToken) {
|
|
471
|
+
export async function handleChatCompletion(body, accessToken, { onStageChange = null, getAccessToken = null } = {}) {
|
|
463
472
|
await warmCursorClientVersion();
|
|
464
473
|
const parsed = parseMessages(body.messages);
|
|
465
474
|
if (
|
|
@@ -504,6 +513,10 @@ export async function handleChatCompletion(body, accessToken) {
|
|
|
504
513
|
};
|
|
505
514
|
const requestBytes = buildRunRequest(runInput);
|
|
506
515
|
const { bridge, heartbeat } = startRun(accessToken, requestBytes);
|
|
516
|
+
// Every retry force-refreshes credentials and falls back to the previous
|
|
517
|
+
// token if the refresh fails. A rejected credential is only
|
|
518
|
+
// retried when the refresh produced a different token.
|
|
519
|
+
const credentials = createCursorCredentialRefresher(accessToken, getAccessToken);
|
|
507
520
|
return createStreamResponse({
|
|
508
521
|
bridge,
|
|
509
522
|
heartbeat,
|
|
@@ -515,11 +528,21 @@ export async function handleChatCompletion(body, accessToken) {
|
|
|
515
528
|
maxMode,
|
|
516
529
|
key,
|
|
517
530
|
sessionId,
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
531
|
+
onStageChange,
|
|
532
|
+
restart: async ({ fromCheckpoint, reason, error }) => {
|
|
533
|
+
if (reason === 'throttle') {
|
|
534
|
+
// Cursor can pin resource_exhausted to one conversation id (conversation
|
|
535
|
+
// rotation after a rate limit): start a fresh one from the full
|
|
536
|
+
// history instead of the checkpoint it refused.
|
|
537
|
+
conversation.id = crypto.randomUUID();
|
|
538
|
+
conversation.checkpoint = null;
|
|
539
|
+
}
|
|
540
|
+
const token = await credentials.refresh(reason, error);
|
|
541
|
+
let bytes = requestBytes;
|
|
542
|
+
if (fromCheckpoint) bytes = buildRunRequest({ ...runInput, userText: '', userImages: [] });
|
|
543
|
+
else if (reason === 'throttle') bytes = buildRunRequest(runInput);
|
|
544
|
+
return startRun(token, bytes);
|
|
545
|
+
},
|
|
523
546
|
});
|
|
524
547
|
}
|
|
525
548
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from './openai-compat-wire.mjs';
|
|
11
11
|
import { preparePdfTextForProvider } from './media-normalization.mjs';
|
|
12
12
|
import { consumeCompatChatCompletionStream } from './openai-compat-stream.mjs';
|
|
13
|
+
import { retryAfterMsFromError } from './retry-classifier.mjs';
|
|
13
14
|
import { ensureChatToolPairs } from './lib/wire-pairing.mjs';
|
|
14
15
|
import { cursorTokenExpiry, exchangeCursorToken, resolveCursorOAuthAccessToken } from './cursor-auth.mjs';
|
|
15
16
|
import {
|
|
@@ -62,14 +63,24 @@ function toCursorToolChoice(toolChoice) {
|
|
|
62
63
|
async function responseError(response, label) {
|
|
63
64
|
const text = await response.text().catch(() => '');
|
|
64
65
|
let message = text;
|
|
66
|
+
let code = '';
|
|
65
67
|
try {
|
|
66
68
|
const parsed = JSON.parse(text);
|
|
67
69
|
message = parsed?.error?.message || parsed?.message || text;
|
|
70
|
+
code = String(parsed?.error?.code || parsed?.code || '').toLowerCase();
|
|
68
71
|
} catch {
|
|
69
72
|
/* plain text */
|
|
70
73
|
}
|
|
71
74
|
const error = new Error(`${label} request failed (${response.status})${message ? `: ${message}` : ''}`);
|
|
72
75
|
error.status = response.status;
|
|
76
|
+
error.httpStatus = response.status;
|
|
77
|
+
if (code) error.code = code;
|
|
78
|
+
const headers = Object.fromEntries(response.headers?.entries?.() ?? []);
|
|
79
|
+
if (Object.keys(headers).length) {
|
|
80
|
+
error.headers = headers;
|
|
81
|
+
const retryAfterMs = retryAfterMsFromError(error);
|
|
82
|
+
if (Number.isFinite(retryAfterMs)) error.retryAfterMs = retryAfterMs;
|
|
83
|
+
}
|
|
73
84
|
throw error;
|
|
74
85
|
}
|
|
75
86
|
|
|
@@ -535,6 +546,9 @@ class CursorProviderBase {
|
|
|
535
546
|
nativePdf = false;
|
|
536
547
|
|
|
537
548
|
static inputExcludesCache = false;
|
|
549
|
+
// The in-place stream retry budget is the whole recovery: a spent budget
|
|
550
|
+
// surfaces instead of being re-run by the outer recovery cycles.
|
|
551
|
+
retryPolicy = Object.freeze({ recoveryCycles: false });
|
|
538
552
|
name;
|
|
539
553
|
config;
|
|
540
554
|
_runtimeOverride;
|
|
@@ -622,7 +636,10 @@ class CursorProviderBase {
|
|
|
622
636
|
sendOpts.onStageChange?.('requesting');
|
|
623
637
|
} catch {}
|
|
624
638
|
try {
|
|
625
|
-
const response = await runtime.handleChatCompletion(body, accessToken
|
|
639
|
+
const response = await runtime.handleChatCompletion(body, accessToken, {
|
|
640
|
+
onStageChange: sendOpts.onStageChange,
|
|
641
|
+
getAccessToken: (options) => this._accessToken({ signal, ...options }),
|
|
642
|
+
});
|
|
626
643
|
try {
|
|
627
644
|
sendOpts.onStageChange?.('streaming');
|
|
628
645
|
} catch {}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// runtime reuses the existing OpenAI-compat / Anthropic engines, constructed
|
|
5
5
|
// under the custom id so model routes and provider identity stay `custom-…`.
|
|
6
6
|
|
|
7
|
+
import { resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
|
|
7
8
|
import { effectiveCustomModelRows, normalizeCustomProviderConfig } from './custom-provider-config.mjs';
|
|
8
9
|
import { ANTHROPIC_VERSION } from './lib/anthropic-models.mjs';
|
|
9
10
|
import { enrichModels } from './model-catalog.mjs';
|
|
@@ -39,8 +40,11 @@ function withCustomModels(Base, discover) {
|
|
|
39
40
|
customConfig = null;
|
|
40
41
|
customDiscoveryError = null;
|
|
41
42
|
_customDiscovered = null;
|
|
42
|
-
_customApiKey = '';
|
|
43
43
|
_customKeyless = false;
|
|
44
|
+
// The key for discovery and error redaction, resolved per use like sends.
|
|
45
|
+
_customKey() {
|
|
46
|
+
return this._customKeyless ? '' : resolveProviderApiKey(this.name, this.config);
|
|
47
|
+
}
|
|
44
48
|
_customRows() {
|
|
45
49
|
return effectiveCustomModelRows(this.customConfig, this._customDiscovered || [], { providerId: this.name });
|
|
46
50
|
}
|
|
@@ -57,7 +61,7 @@ function withCustomModels(Base, discover) {
|
|
|
57
61
|
this.customDiscoveryError = null;
|
|
58
62
|
} catch (error) {
|
|
59
63
|
if (signal?.aborted) throw error;
|
|
60
|
-
failure = sanitizeDiscoveryError(error, this.
|
|
64
|
+
failure = sanitizeDiscoveryError(error, this._customKey());
|
|
61
65
|
this.customDiscoveryError = failure;
|
|
62
66
|
}
|
|
63
67
|
const rows = this._customRows();
|
|
@@ -82,7 +86,7 @@ async function discoverAnthropicModels(provider, signal) {
|
|
|
82
86
|
method: 'GET',
|
|
83
87
|
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
84
88
|
headers: {
|
|
85
|
-
...(provider._customKeyless ? {} : { 'x-api-key': provider.
|
|
89
|
+
...(provider._customKeyless ? {} : { 'x-api-key': provider._customKey() }),
|
|
86
90
|
'anthropic-version': ANTHROPIC_VERSION,
|
|
87
91
|
},
|
|
88
92
|
});
|
|
@@ -141,7 +145,6 @@ export async function createCustomProvider(id, config) {
|
|
|
141
145
|
provider = new Custom(providerId, engine);
|
|
142
146
|
}
|
|
143
147
|
provider.customConfig = cfg;
|
|
144
|
-
provider._customApiKey = apiKey;
|
|
145
148
|
provider._customKeyless = keyless;
|
|
146
149
|
provider._applyCustomRows(provider._customRows());
|
|
147
150
|
return provider;
|