mixdog 1.3.4 → 1.4.0
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/LICENSES/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +17 -2
- package/README.md +11 -0
- package/package.json +3 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/gate-local.mjs +4 -2
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test.mjs +12 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +25 -1
- package/src/defaults/skills/browser-use/SKILL.md +77 -17
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +33 -9
- package/src/defaults/skills/setup/SKILL.md +29 -2
- package/src/defaults/skills/setup/references/actions.md +117 -28
- package/src/defaults/skills/setup/references/surfaces.md +77 -35
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/rules/lead/LEAD.md +18 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/config.mjs +34 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
- package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +135 -154
- package/src/runtime/channels/lib/schedules-db.mjs +83 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +19 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-hops.mjs +62 -2
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +51 -35
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +33 -2
- package/src/runtime/shared/connector-catalog.mjs +166 -8
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -14
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +231 -59
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +52 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +34 -1
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -14
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/runtime/shared/worktrees/test-support.mjs +9 -4
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +27 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +486 -179
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +561 -199
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/begin.mjs +3 -2
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +8 -1
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +927 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +35 -13
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/routine-home-turn.mjs +33 -6
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +73 -31
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +8 -4
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +28 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +187 -29
- package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +161 -157
- package/src/standalone/daemon.mjs +33 -2
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +10 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +21 -10
- package/src/tui/app/model-options.mjs +4 -27
- package/src/tui/dist/index.mjs +81 -65
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +23 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +30 -2
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +36 -3
- package/src/tui/session/turn.mjs +23 -2
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeMcpTransportUrl } from './client-config.mjs';
|
|
2
2
|
import { withAccountParams } from './oauth-account.mjs';
|
|
3
|
-
import { loginFailed, reauthenticationRequired } from './oauth-shared.mjs';
|
|
3
|
+
import { loginFailed, missingScopes, reauthenticationRequired, scopeNotGranted } from './oauth-shared.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The SDK's OAuth client provider over one connection's credential `cache`
|
|
@@ -60,7 +60,17 @@ export function createOAuthProvider({ cache, flow, lifetimeSignal, client = null
|
|
|
60
60
|
return creds.tokens;
|
|
61
61
|
},
|
|
62
62
|
saveTokens: async (value) => {
|
|
63
|
-
|
|
63
|
+
// The login's own deadline/cancel signal (oauth-login.mjs) when one runs.
|
|
64
|
+
const signal = flow.signal ?? lifetimeSignal;
|
|
65
|
+
signal.throwIfAborted();
|
|
66
|
+
// A browser sign-in of a client with its own scopes must be granted all
|
|
67
|
+
// of them (a granular consent screen lets the user uncheck some): nothing
|
|
68
|
+
// is stored, so the next connect asks again. A refresh is not checked.
|
|
69
|
+
if (flow.browserOpened && client?.scopes) {
|
|
70
|
+
const missing = missingScopes(client.scopes, value.scope);
|
|
71
|
+
if (missing.length) throw scopeNotGranted(missing);
|
|
72
|
+
}
|
|
73
|
+
const { tokens: priorTokens, identity: priorIdentity } = creds;
|
|
64
74
|
await save(() => {
|
|
65
75
|
creds.tokens = value;
|
|
66
76
|
// A browser sign-in may be another account: its identity replaces the
|
|
@@ -69,6 +79,15 @@ export function createOAuthProvider({ cache, flow, lifetimeSignal, client = null
|
|
|
69
79
|
if (flow.browserOpened) creds.identity = flow.identity ?? null;
|
|
70
80
|
else if (flow.identity) creds.identity = flow.identity;
|
|
71
81
|
});
|
|
82
|
+
// The login was reported failed while this write was outstanding: roll
|
|
83
|
+
// it back (same save path) so no tokens outlive the reported failure.
|
|
84
|
+
if (signal.aborted) {
|
|
85
|
+
await save(() => {
|
|
86
|
+
creds.tokens = priorTokens;
|
|
87
|
+
creds.identity = priorIdentity;
|
|
88
|
+
});
|
|
89
|
+
throw loginFailed();
|
|
90
|
+
}
|
|
72
91
|
},
|
|
73
92
|
saveCodeVerifier: (value) => {
|
|
74
93
|
flow.verifier = value;
|
|
@@ -5,5 +5,28 @@ export const reauthenticationRequired = () =>
|
|
|
5
5
|
});
|
|
6
6
|
export const loginFailed = () => new Error('MCP OAuth login failed, cancelled, or timed out');
|
|
7
7
|
|
|
8
|
+
// A sign-in whose token response lacks a requested scope (a provider's
|
|
9
|
+
// granular consent let the user leave a box unchecked). The message names only
|
|
10
|
+
// the scopes, never token data; callers match on the marker, which survives
|
|
11
|
+
// the wrapping transports and connect paths apply to the message.
|
|
12
|
+
export const SCOPE_NOT_GRANTED_MARKER = 'MCP OAuth scopeNotGranted';
|
|
13
|
+
export const scopeNotGranted = (missing) =>
|
|
14
|
+
Object.assign(new Error(`${SCOPE_NOT_GRANTED_MARKER}: access not granted for ${missing.join(' ')}`), {
|
|
15
|
+
code: 'scopeNotGranted',
|
|
16
|
+
missingScopes: [...missing],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// The basic identity scopes are never checked: Google answers a requested
|
|
20
|
+
// `email` with its userinfo.email URL (likewise `profile`).
|
|
21
|
+
const BASIC_SCOPES = new Set(['openid', 'email', 'profile']);
|
|
22
|
+
|
|
23
|
+
/** The requested non-basic scopes a token response's `scope` leaves out. A
|
|
24
|
+
* response without a `scope` field grants what was requested (RFC 6749). */
|
|
25
|
+
export function missingScopes(requested, granted) {
|
|
26
|
+
if (typeof granted !== 'string') return [];
|
|
27
|
+
const have = new Set(granted.split(/\s+/).filter(Boolean));
|
|
28
|
+
return (requested || []).filter((scope) => !BASIC_SCOPES.has(scope) && !have.has(scope));
|
|
29
|
+
}
|
|
30
|
+
|
|
8
31
|
// `signal`, also aborted by the caller's optional `other` signal.
|
|
9
32
|
export const anySignal = (signal, other) => AbortSignal.any(other ? [signal, other] : [signal]);
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import { createLoginFlow } from './oauth-login.mjs';
|
|
22
22
|
import { createOAuthProvider } from './oauth-provider.mjs';
|
|
23
23
|
import { createAuthorizedFetch } from './oauth-request.mjs';
|
|
24
|
-
import { REAUTHENTICATION_REQUIRED_MESSAGE, anySignal } from './oauth-shared.mjs';
|
|
24
|
+
import { REAUTHENTICATION_REQUIRED_MESSAGE, SCOPE_NOT_GRANTED_MARKER, anySignal } from './oauth-shared.mjs';
|
|
25
25
|
|
|
26
26
|
export { MCP_OAUTH_CALLBACK_MAX_BYTES, MCP_OAUTH_CALLBACK_NOT_PENDING, parsePastedMcpOAuthCallback };
|
|
27
27
|
|
|
@@ -40,6 +40,16 @@ export function isMcpOAuthReauthenticationRequired(error) {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** Whether a sign-in failed because the provider did not grant a requested
|
|
44
|
+
* scope (a real failure with its own message, not a plain "sign in"). */
|
|
45
|
+
export function isMcpOAuthScopeNotGranted(error) {
|
|
46
|
+
for (let current = error, depth = 0; current && depth < 5; current = current.cause, depth++) {
|
|
47
|
+
if (current.code === 'scopeNotGranted') return true;
|
|
48
|
+
if (String(current.message ?? current).includes(SCOPE_NOT_GRANTED_MARKER)) return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
// One keychain entry per exact server URL. Windows stores each account as a
|
|
44
54
|
// file name, so the account is a hash of the URL and nothing else.
|
|
45
55
|
export function mcpOAuthAccount(url) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ACCOUNT_PROVIDERS,
|
|
3
|
+
MAX_ACCOUNTS,
|
|
3
4
|
accountScopedSessionId,
|
|
4
5
|
readProviderAccountPool,
|
|
5
6
|
chooseProviderAccount,
|
|
@@ -9,11 +10,14 @@ import {
|
|
|
9
10
|
providerAccountExhaustedForModelOnly,
|
|
10
11
|
providerAccountPolicy,
|
|
11
12
|
providerAccountsResetAt,
|
|
13
|
+
providerAccountWithBlocks,
|
|
14
|
+
providerAccountBlockedUntil,
|
|
15
|
+
staleQuotaWindows,
|
|
12
16
|
} from '../../../shared/provider-accounts.mjs';
|
|
13
17
|
import { withProviderAccount, hasExplicitProviderAuthBinding } from '../../../shared/provider-auth-binding.mjs';
|
|
14
18
|
import { isExplicitUserAbortError, markProviderRecoveryExhausted } from './retry-classifier.mjs';
|
|
15
19
|
import { sleepWithAbort } from './retry-backoff.mjs';
|
|
16
|
-
import { QUOTA_KINDS, providerErrorKind } from './provider-error-kind.mjs';
|
|
20
|
+
import { QUOTA_KINDS, boundedCauseChain, providerErrorKind } from './provider-error-kind.mjs';
|
|
17
21
|
import { fetchOAuthUsageSnapshot, readCachedOAuthUsageSnapshot } from './oauth-usage.mjs';
|
|
18
22
|
|
|
19
23
|
// Only an explicit quota / plan / billing refusal advances the roster as
|
|
@@ -124,15 +128,18 @@ function accountsExhaustedError(useCredits) {
|
|
|
124
128
|
// evidence): a rate limit, advising the earliest cooldown end as the wait.
|
|
125
129
|
// The pool already spent its cooldown wait, so this is a terminal verdict for
|
|
126
130
|
// the loop, not an unclassified failure.
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
// A row's cooldown for `model` ends when every block holding that model lifts.
|
|
132
|
+
const coolingUntil = (row, model) => providerAccountBlockedUntil(row, model) ?? 0;
|
|
133
|
+
|
|
134
|
+
function earliestCooldownMs(rows, now, model) {
|
|
135
|
+
const ends = rows.map((row) => coolingUntil(row, model)).filter((until) => until > now);
|
|
129
136
|
return ends.length ? Math.min(...ends) - now : null;
|
|
130
137
|
}
|
|
131
138
|
|
|
132
|
-
function accountsCoolingDownError(rows, now) {
|
|
139
|
+
function accountsCoolingDownError(rows, now, model) {
|
|
133
140
|
const error = new Error('Every connected account is cooling down after a rate limit. Try again shortly.');
|
|
134
141
|
error.code = 'rate_limited';
|
|
135
|
-
const wait = earliestCooldownMs(rows, now);
|
|
142
|
+
const wait = earliestCooldownMs(rows, now, model);
|
|
136
143
|
if (wait != null) error.retryAfterMs = wait;
|
|
137
144
|
return markProviderRecoveryExhausted(error, { owner: 'account-pool', terminal: true });
|
|
138
145
|
}
|
|
@@ -258,6 +265,36 @@ function recordServingAccount(providerName, result, id, bindingId) {
|
|
|
258
265
|
};
|
|
259
266
|
}
|
|
260
267
|
|
|
268
|
+
// A block covers the quota bucket of the model that failed, which only a usage
|
|
269
|
+
// reading names. An account without a recorded reading is measured once before
|
|
270
|
+
// it is blocked — once per account for this process, whatever the outcome,
|
|
271
|
+
// through the ordinary cache (a fresh failed reading is not repeated) — and
|
|
272
|
+
// again only after a reading has recorded windows and they were dropped.
|
|
273
|
+
// Without a reading the block stays account-wide.
|
|
274
|
+
const measureAttempted = new Set();
|
|
275
|
+
|
|
276
|
+
async function measureOnce(providerName, row, account) {
|
|
277
|
+
const key = `${providerName}:${row.id}`;
|
|
278
|
+
if (row.usage?.windows?.length) {
|
|
279
|
+
measureAttempted.delete(key);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (measureAttempted.has(key)) return;
|
|
283
|
+
measureAttempted.add(key);
|
|
284
|
+
await fetchOAuthUsageSnapshot({ provider: providerName, accountId: row.id }, account(row.id), () => {}).catch(
|
|
285
|
+
() => null
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// The server's id of the limit a refusal names (a Codex model-specific limit),
|
|
290
|
+
// stamped on the error by the provider boundary.
|
|
291
|
+
function quotaLimitOf(error) {
|
|
292
|
+
for (const item of boundedCauseChain(error)) {
|
|
293
|
+
if (typeof item?.quotaLimit === 'string' && item.quotaLimit) return item.quotaLimit;
|
|
294
|
+
}
|
|
295
|
+
return '';
|
|
296
|
+
}
|
|
297
|
+
|
|
261
298
|
// Whether the failed account's quota window is exhausted, plus the server's
|
|
262
299
|
// retry delay. Anthropic and other subscriptions may use the same 429 code
|
|
263
300
|
// for burst throttling and exhausted subscription windows, so the native
|
|
@@ -272,7 +309,7 @@ async function accountQuotaExhausted(providerName, row, model, error, account) {
|
|
|
272
309
|
const policy = cooldownPolicyOf(account(row.id));
|
|
273
310
|
const floorMs = policy ? finiteMs(policy.floorMs, 0) : 0;
|
|
274
311
|
const blockUntil = (withReset, reason = 'unknown') => {
|
|
275
|
-
if (withReset && resetAt > now) return { until: resetAt, source: 'retry-after' };
|
|
312
|
+
if (withReset && resetAt > now) return { until: resetAt, source: 'retry-after', reset: true };
|
|
276
313
|
if (delay > 0) return { until: now + Math.max(delay, floorMs), source: 'retry-after' };
|
|
277
314
|
if (policy) {
|
|
278
315
|
const ms = policyCooldownMs(policy, `${providerName}:${row.id}`, reason);
|
|
@@ -298,10 +335,14 @@ async function accountQuotaExhausted(providerName, row, model, error, account) {
|
|
|
298
335
|
};
|
|
299
336
|
}
|
|
300
337
|
const typed = isAccountQuotaError(error);
|
|
301
|
-
if (typed)
|
|
338
|
+
if (typed) {
|
|
339
|
+
await measureOnce(providerName, row, account);
|
|
340
|
+
return { exhausted: true, delay, evidence: true, ...blockUntil(true, 'quota') };
|
|
341
|
+
}
|
|
302
342
|
// A policy with rotateOnRateLimit rotates on a plain rate limit too (the
|
|
303
343
|
// advice as its cooldown, else rateLimitedMs); never quota evidence.
|
|
304
344
|
if (policy?.rotateOnRateLimit === true && kind === 'rate_limited' && status === 429) {
|
|
345
|
+
await measureOnce(providerName, row, account);
|
|
305
346
|
return { exhausted: true, delay, evidence: false, ...blockUntil(false, 'rate_limited') };
|
|
306
347
|
}
|
|
307
348
|
let exhausted = status === 429 && delay > QUOTA_WINDOW_RETRY_AFTER_MS;
|
|
@@ -315,111 +356,185 @@ async function accountQuotaExhausted(providerName, row, model, error, account) {
|
|
|
315
356
|
const pool = readProviderAccountPool(providerName);
|
|
316
357
|
const entry = pool.accounts.find((item) => item.id === row.id);
|
|
317
358
|
const options = { useCredits: pool.useCredits === true };
|
|
318
|
-
evidence = providerAccountExhausted(
|
|
359
|
+
evidence = providerAccountExhausted(providerAccountWithBlocks(entry), Date.now(), model, options);
|
|
319
360
|
exhausted = exhausted || evidence || providerAccountExhausted(entry, Date.now(), model, options);
|
|
320
361
|
}
|
|
321
|
-
|
|
362
|
+
const reason = evidence ? 'quota' : 'unknown';
|
|
363
|
+
return { exhausted, delay, evidence, ...(exhausted ? blockUntil(evidence, reason) : {}) };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Record an exhausted verdict on the roster. An account-level block holds
|
|
367
|
+
// every model, so it carries no model or limit scope.
|
|
368
|
+
function blockRefusedAccount(providerName, accountId, model, error, verdict) {
|
|
369
|
+
const { evidence, until, source, reset, accountBlocked } = verdict;
|
|
370
|
+
return blockProviderAccount(providerName, accountId, until, accountBlocked ? '' : model, source, {
|
|
371
|
+
quota: evidence === true,
|
|
372
|
+
reset: reset === true,
|
|
373
|
+
limit: accountBlocked ? '' : quotaLimitOf(error),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* A refusal `error` that account `accountId` gave outside the pool's send (a
|
|
379
|
+
* cache warm, which never rotates): judged and recorded on the roster exactly
|
|
380
|
+
* as the send would, with `instance` as that account's provider. Resolves
|
|
381
|
+
* whether the account was blocked.
|
|
382
|
+
*/
|
|
383
|
+
export async function recordAccountRefusal(providerName, accountId, model, error, instance) {
|
|
384
|
+
const row = readProviderAccountPool(providerName).accounts.find((entry) => entry.id === accountId);
|
|
385
|
+
if (!row) return false;
|
|
386
|
+
const verdict = await accountQuotaExhausted(providerName, row, model, error, () => instance);
|
|
387
|
+
if (!verdict.exhausted) return false;
|
|
388
|
+
await blockRefusedAccount(providerName, row.id, model, error, verdict);
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Whether every account in `ids` (default: the roster) is exhausted with quota
|
|
393
|
+
// evidence: observed in this send (`evidenced`), or a full window.
|
|
394
|
+
function rosterExhaustion(providerName, model, evidenced, ids) {
|
|
395
|
+
const pool = readProviderAccountPool(providerName);
|
|
396
|
+
// Manual policy: the eligible set is the selected account alone.
|
|
397
|
+
const scope = ids || (providerAccountPolicy(pool) === 'manual' ? [pool.selectedId] : null);
|
|
398
|
+
const rows = scope ? pool.accounts.filter((entry) => scope.includes(entry.id)) : pool.accounts;
|
|
399
|
+
const opts = { useCredits: pool.useCredits === true };
|
|
400
|
+
const now = Date.now();
|
|
401
|
+
const all =
|
|
402
|
+
rows.length > 0 &&
|
|
403
|
+
rows.every((entry) =>
|
|
404
|
+
providerAccountExhausted(
|
|
405
|
+
evidenced.has(entry.id) ? entry : providerAccountWithBlocks(entry, (block) => block.blockedQuota === true),
|
|
406
|
+
now,
|
|
407
|
+
model,
|
|
408
|
+
opts
|
|
409
|
+
)
|
|
410
|
+
);
|
|
411
|
+
return { rows, opts, now, all };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Mark `error` only when every account in `ids` is exhausted with quota
|
|
415
|
+
// evidence (`rosterExhaustion`). Without an error of its own, the send reports
|
|
416
|
+
// quota exhaustion only on that evidence, and a rate-limit cooldown otherwise.
|
|
417
|
+
function exhaustedSendError(providerName, model, evidenced, error, ids) {
|
|
418
|
+
const { rows, opts, now, all } = rosterExhaustion(providerName, model, evidenced, ids);
|
|
419
|
+
if (all) {
|
|
420
|
+
return quotaExhausted(error || accountsExhaustedError(opts.useCredits), providerName, model, rows, opts);
|
|
421
|
+
}
|
|
422
|
+
return error || accountsCoolingDownError(rows, now, model);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// The provider's bound on the time one send spends waiting for cooldowns.
|
|
426
|
+
function cooldownWaitBoundMs(providerName, account, ids) {
|
|
427
|
+
const id = ids?.[0] || readProviderAccountPool(providerName).selectedId || 'default';
|
|
428
|
+
return finiteMs(cooldownPolicyOf(account(id))?.waitForCooldownMs, 0);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// A cooldown-only eligible set whose earliest cooldown ends inside the
|
|
432
|
+
// remaining wait budget is waited out; otherwise the send fails as before.
|
|
433
|
+
// `ledger` is the send's own record of the roster (see `poolSend`).
|
|
434
|
+
async function waitOutCooldownOrThrow(request, ledger, error, ids) {
|
|
435
|
+
const { providerName, model, account, options } = request;
|
|
436
|
+
const { rows, now, all } = rosterExhaustion(providerName, model, ledger.evidenced, ids);
|
|
437
|
+
const bound = cooldownWaitBoundMs(providerName, account, ids);
|
|
438
|
+
// Persisting a short cooldown can outlast its deadline. Release only
|
|
439
|
+
// attempts cooled by this send, and only under an enabled wait policy.
|
|
440
|
+
const expired = bound > 0 && rows.some((row) => ledger.cooled.has(row.id) && !(coolingUntil(row, model) > now));
|
|
441
|
+
let wait = null;
|
|
442
|
+
if (!all) wait = expired ? 0 : earliestCooldownMs(rows, now, model);
|
|
443
|
+
if (wait == null || ledger.waitedMs + wait > bound) {
|
|
444
|
+
throw exhaustedSendError(providerName, model, ledger.evidenced, error, ids);
|
|
445
|
+
}
|
|
446
|
+
options.onStageChange?.('reconnecting', {
|
|
447
|
+
message: `Rate limited — every account is cooling down; retrying in ${Math.ceil(wait / 1000)}s`,
|
|
448
|
+
reason: 'rate_limited',
|
|
449
|
+
waitMs: wait,
|
|
450
|
+
retryAt: now + wait,
|
|
451
|
+
});
|
|
452
|
+
// A zero-duration sleep does not check cancellation, including on the
|
|
453
|
+
// final allowed send when no next loop iteration will check it either.
|
|
454
|
+
options.signal?.throwIfAborted();
|
|
455
|
+
await sleepWithAbort(wait, options.signal);
|
|
456
|
+
ledger.waitedMs += wait;
|
|
457
|
+
const lifted = Date.now();
|
|
458
|
+
for (const row of readProviderAccountPool(providerName).accounts) {
|
|
459
|
+
if (ledger.cooled.has(row.id) && !(coolingUntil(row, model) > lifted)) {
|
|
460
|
+
ledger.cooled.delete(row.id);
|
|
461
|
+
ledger.attempted.delete(row.id);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// No roster: with credit use off, a stored reading showing the plan's window
|
|
467
|
+
// spent refuses before the implicit account sends.
|
|
468
|
+
function throwIfPlanQuotaSpent(providerName, pool, model) {
|
|
469
|
+
if (pool.useCredits !== true) {
|
|
470
|
+
const stored = readCachedOAuthUsageSnapshot({ provider: providerName, model: '' });
|
|
471
|
+
// A snapshot cached before per-model gating is stale evidence.
|
|
472
|
+
if (stored?.quotaWindows?.length && !staleQuotaWindows(providerName, stored.quotaWindows)) {
|
|
473
|
+
const row = { usage: { windows: stored.quotaWindows, checkedAt: stored.cachedAt } };
|
|
474
|
+
if (providerAccountExhausted(row, Date.now(), model)) {
|
|
475
|
+
throw quotaExhausted(accountsExhaustedError(false), providerName, model, [row]);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Headroom policy: a session's first pick, or its move off its account on a
|
|
482
|
+
// limit, becomes the selection; continuing on its account does not, so
|
|
483
|
+
// concurrent sessions never flip the selection back and forth. A move forced
|
|
484
|
+
// by a model-scoped limit serves this request only (`scoped`). Stamps the
|
|
485
|
+
// serving account on `result` and resolves whether the selection may commit.
|
|
486
|
+
function bindServedAccount(providerName, result, rowId, model, binding) {
|
|
487
|
+
const { policy, boundId, sessionKey, switched, refusalScoped } = binding;
|
|
488
|
+
const leftBound = Boolean(boundId) && rowId !== boundId;
|
|
489
|
+
const scoped = switched
|
|
490
|
+
? refusalScoped
|
|
491
|
+
: policy === 'headroom' && leftBound && accountExhaustedForModelOnly(providerName, boundId, model);
|
|
492
|
+
recordServingAccount(providerName, result, rowId, scoped && boundId ? boundId : rowId);
|
|
493
|
+
if (sessionKey && policy === 'headroom' && !scoped) rememberSessionAccount(sessionKey, rowId);
|
|
494
|
+
return { scoped, commit: policy !== 'headroom' || !boundId || leftBound };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// A failed attempt is never re-issued once output reached the caller or the
|
|
498
|
+
// attempt was unsafe to retry (`unsafe`, judged by the caller and read only
|
|
499
|
+
// when nothing was emitted).
|
|
500
|
+
function attemptUnretryable(bound, error, unsafe) {
|
|
501
|
+
return Boolean(bound.emitted() || unsafe() || error.liveTextEmitted || error.emittedToolCall);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function rotationReason(evidence, accountBlocked) {
|
|
505
|
+
if (evidence) return 'Quota exhausted';
|
|
506
|
+
if (accountBlocked) return 'Account blocked';
|
|
507
|
+
return 'Rate limited (cooldown)';
|
|
322
508
|
}
|
|
323
509
|
|
|
324
510
|
// The pool's send: walks the eligible accounts until one answers, blocking
|
|
325
511
|
// the ones whose quota window is exhausted along the way.
|
|
326
512
|
function poolSend(providerName, account) {
|
|
327
513
|
return async function send(messages, model, tools, options = {}) {
|
|
328
|
-
const
|
|
514
|
+
const request = { providerName, model, account, options };
|
|
515
|
+
// This send's record of the roster: the accounts it tried, those it found
|
|
516
|
+
// exhausted with quota evidence, those it blocked on a rate-limit
|
|
517
|
+
// cooldown, and the time it spent waiting for cooldowns.
|
|
518
|
+
const ledger = { attempted: new Set(), evidenced: new Set(), cooled: new Set(), waitedMs: 0 };
|
|
329
519
|
let lastError;
|
|
330
520
|
let probed = false;
|
|
331
|
-
|
|
521
|
+
let sends = 0;
|
|
332
522
|
const sessionKey = options.sessionId ? `${providerName}:${options.sessionId}` : null;
|
|
333
523
|
const boundId = sessionAccountId(providerName, sessionKey, messages, options);
|
|
334
524
|
// Whether this send already left an account, and whether the latest quota
|
|
335
525
|
// refusal held only for this model's family.
|
|
336
526
|
let switched = false;
|
|
337
527
|
let refusalScoped = false;
|
|
338
|
-
//
|
|
339
|
-
|
|
340
|
-
// Without an error of its own, the send reports quota exhaustion only on
|
|
341
|
-
// that evidence, and a rate-limit cooldown otherwise.
|
|
342
|
-
const exhaustion = (ids) => {
|
|
343
|
-
const pool = readProviderAccountPool(providerName);
|
|
344
|
-
// Manual policy: the eligible set is the selected account alone.
|
|
345
|
-
const scope = ids || (providerAccountPolicy(pool) === 'manual' ? [pool.selectedId] : null);
|
|
346
|
-
const rows = scope ? pool.accounts.filter((entry) => scope.includes(entry.id)) : pool.accounts;
|
|
347
|
-
const opts = { useCredits: pool.useCredits === true };
|
|
348
|
-
const now = Date.now();
|
|
349
|
-
const all =
|
|
350
|
-
rows.length > 0 &&
|
|
351
|
-
rows.every((entry) =>
|
|
352
|
-
providerAccountExhausted(
|
|
353
|
-
evidenced.has(entry.id) || entry.blockedQuota === true ? entry : { ...entry, blockedUntil: 0 },
|
|
354
|
-
now,
|
|
355
|
-
model,
|
|
356
|
-
opts
|
|
357
|
-
)
|
|
358
|
-
);
|
|
359
|
-
return { rows, opts, now, all };
|
|
360
|
-
};
|
|
361
|
-
const markExhausted = (error, ids) => {
|
|
362
|
-
const { rows, opts, now, all } = exhaustion(ids);
|
|
363
|
-
if (all) {
|
|
364
|
-
return quotaExhausted(error || accountsExhaustedError(opts.useCredits), providerName, model, rows, opts);
|
|
365
|
-
}
|
|
366
|
-
return error || accountsCoolingDownError(rows, now);
|
|
367
|
-
};
|
|
368
|
-
// Accounts this send blocked on a rate-limit cooldown, the time it spent
|
|
369
|
-
// waiting for cooldowns, and the provider's wait bound.
|
|
370
|
-
const cooled = new Set();
|
|
371
|
-
let waitedMs = 0;
|
|
372
|
-
let sends = 0;
|
|
373
|
-
const waitBoundMs = (ids) => {
|
|
374
|
-
const id = ids?.[0] || readProviderAccountPool(providerName).selectedId || 'default';
|
|
375
|
-
return finiteMs(cooldownPolicyOf(account(id))?.waitForCooldownMs, 0);
|
|
376
|
-
};
|
|
377
|
-
// A cooldown-only eligible set whose earliest cooldown ends inside the
|
|
378
|
-
// remaining wait budget is waited out; otherwise the send fails as before.
|
|
379
|
-
const waitOrThrow = async (error, ids) => {
|
|
380
|
-
const { rows, now, all } = exhaustion(ids);
|
|
381
|
-
const bound = waitBoundMs(ids);
|
|
382
|
-
// Persisting a short cooldown can outlast its deadline. Release only
|
|
383
|
-
// attempts cooled by this send, and only under an enabled wait policy.
|
|
384
|
-
const expired = bound > 0 && rows.some((row) => cooled.has(row.id) && !(Number(row.blockedUntil) > now));
|
|
385
|
-
const wait = all ? null : expired ? 0 : earliestCooldownMs(rows, now);
|
|
386
|
-
if (wait == null || waitedMs + wait > bound) throw markExhausted(error, ids);
|
|
387
|
-
options.onStageChange?.('reconnecting', {
|
|
388
|
-
message: `Rate limited — every account is cooling down; retrying in ${Math.ceil(wait / 1000)}s`,
|
|
389
|
-
reason: 'rate_limited',
|
|
390
|
-
waitMs: wait,
|
|
391
|
-
retryAt: now + wait,
|
|
392
|
-
});
|
|
393
|
-
// A zero-duration sleep does not check cancellation, including on the
|
|
394
|
-
// final allowed send when no next loop iteration will check it either.
|
|
395
|
-
options.signal?.throwIfAborted();
|
|
396
|
-
await sleepWithAbort(wait, options.signal);
|
|
397
|
-
waitedMs += wait;
|
|
398
|
-
const lifted = Date.now();
|
|
399
|
-
for (const row of readProviderAccountPool(providerName).accounts) {
|
|
400
|
-
if (cooled.has(row.id) && !(Number(row.blockedUntil) > lifted)) {
|
|
401
|
-
cooled.delete(row.id);
|
|
402
|
-
attempted.delete(row.id);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
while (attempted.size < 20 && sends < 20) {
|
|
528
|
+
// One try per connectable account at most (provider-accounts.mjs MAX_ACCOUNTS).
|
|
529
|
+
while (ledger.attempted.size < MAX_ACCOUNTS && sends < MAX_ACCOUNTS) {
|
|
407
530
|
options.signal?.throwIfAborted();
|
|
408
531
|
const pool = readProviderAccountPool(providerName);
|
|
409
532
|
if (!pool.accounts.length) {
|
|
410
|
-
|
|
411
|
-
const stored = readCachedOAuthUsageSnapshot({ provider: providerName, model: '' });
|
|
412
|
-
if (stored?.quotaWindows?.length) {
|
|
413
|
-
const row = { usage: { windows: stored.quotaWindows, checkedAt: stored.cachedAt } };
|
|
414
|
-
if (providerAccountExhausted(row, Date.now(), model)) {
|
|
415
|
-
throw quotaExhausted(accountsExhaustedError(false), providerName, model, [row]);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
533
|
+
throwIfPlanQuotaSpent(providerName, pool, model);
|
|
419
534
|
return account('default').send(messages, model, tools, options);
|
|
420
535
|
}
|
|
421
536
|
const policy = providerAccountPolicy(pool);
|
|
422
|
-
const row = chooseProviderAccount(pool, attempted, Date.now(), model, { sessionAccountId: boundId });
|
|
537
|
+
const row = chooseProviderAccount(pool, ledger.attempted, Date.now(), model, { sessionAccountId: boundId });
|
|
423
538
|
if (!row) {
|
|
424
539
|
// Once per send: a refusal built on a stale meter must not outlive the
|
|
425
540
|
// meter. Re-measuring can only widen the roster, so it is tried before
|
|
@@ -428,26 +543,22 @@ function poolSend(providerName, account) {
|
|
|
428
543
|
probed = true;
|
|
429
544
|
if (await probeRefusedAccounts(providerName, pool, model, account)) continue;
|
|
430
545
|
}
|
|
431
|
-
await
|
|
546
|
+
await waitOutCooldownOrThrow(request, ledger, lastError);
|
|
432
547
|
continue;
|
|
433
548
|
}
|
|
434
|
-
attempted.add(row.id);
|
|
549
|
+
ledger.attempted.add(row.id);
|
|
435
550
|
sends += 1;
|
|
436
551
|
const bound = boundAccountSend(providerName, row, messages, options);
|
|
437
552
|
try {
|
|
438
553
|
const result = await account(row.id).send(bound.history, model, tools, bound.opts);
|
|
439
554
|
quotaStrikes.delete(`${providerName}:${row.id}`);
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
: policy === 'headroom' && leftBound && accountExhaustedForModelOnly(providerName, boundId, model);
|
|
448
|
-
recordServingAccount(providerName, result, row.id, scoped && boundId ? boundId : row.id);
|
|
449
|
-
if (sessionKey && policy === 'headroom' && !scoped) rememberSessionAccount(sessionKey, row.id);
|
|
450
|
-
const commit = policy !== 'headroom' || !boundId || leftBound;
|
|
555
|
+
const { scoped, commit } = bindServedAccount(providerName, result, row.id, model, {
|
|
556
|
+
policy,
|
|
557
|
+
boundId,
|
|
558
|
+
sessionKey,
|
|
559
|
+
switched,
|
|
560
|
+
refusalScoped,
|
|
561
|
+
});
|
|
451
562
|
if (commit && (await commitFallbackAccount(providerName, pool, row, model, scoped, policy))) {
|
|
452
563
|
// Publish only a committed selection, never a speculative retry or
|
|
453
564
|
// one superseded by a manual switch. The existing progress channel
|
|
@@ -466,59 +577,41 @@ function poolSend(providerName, account) {
|
|
|
466
577
|
} catch (error) {
|
|
467
578
|
if (options.signal?.aborted) throw error;
|
|
468
579
|
if (error?.reauthRequired === true) {
|
|
469
|
-
if (
|
|
470
|
-
policy === 'manual' ||
|
|
471
|
-
bound.emitted() ||
|
|
472
|
-
error.unsafeToRetry ||
|
|
473
|
-
error.liveTextEmitted ||
|
|
474
|
-
error.emittedToolCall
|
|
475
|
-
)
|
|
476
|
-
throw error;
|
|
580
|
+
if (policy === 'manual' || attemptUnretryable(bound, error, () => error.unsafeToRetry)) throw error;
|
|
477
581
|
lastError = error;
|
|
478
582
|
switched = true;
|
|
479
583
|
refusalScoped = false;
|
|
480
584
|
options.onStageChange?.('reconnecting', { message: 'Sign-in expired — switching to the next account' });
|
|
481
585
|
continue;
|
|
482
586
|
}
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
row,
|
|
486
|
-
model,
|
|
487
|
-
error,
|
|
488
|
-
account
|
|
489
|
-
);
|
|
587
|
+
const verdict = await accountQuotaExhausted(providerName, row, model, error, account);
|
|
588
|
+
const { exhausted, evidence, accountBlocked } = verdict;
|
|
490
589
|
if (options.signal?.aborted || !exhausted) throw error;
|
|
491
|
-
if (evidence) evidenced.add(row.id);
|
|
492
|
-
else cooled.add(row.id);
|
|
493
|
-
await
|
|
590
|
+
if (evidence) ledger.evidenced.add(row.id);
|
|
591
|
+
else ledger.cooled.add(row.id);
|
|
592
|
+
await blockRefusedAccount(providerName, row.id, model, error, verdict);
|
|
494
593
|
// An account block is unsafe to re-issue on THAT account only; with no
|
|
495
594
|
// output emitted, the next account may serve the same request.
|
|
496
|
-
if (
|
|
497
|
-
bound.emitted() ||
|
|
498
|
-
(error.unsafeToRetry && accountBlocked !== true) ||
|
|
499
|
-
error.liveTextEmitted ||
|
|
500
|
-
error.emittedToolCall
|
|
501
|
-
)
|
|
502
|
-
throw error;
|
|
595
|
+
if (attemptUnretryable(bound, error, () => error.unsafeToRetry && accountBlocked !== true)) throw error;
|
|
503
596
|
// Manual policy: the eligible set is the selected account alone.
|
|
504
597
|
if (policy === 'manual') {
|
|
505
|
-
await
|
|
598
|
+
await waitOutCooldownOrThrow(request, ledger, error, [row.id]);
|
|
506
599
|
lastError = error;
|
|
507
600
|
continue;
|
|
508
601
|
}
|
|
509
602
|
lastError = error;
|
|
510
603
|
switched = true;
|
|
511
604
|
refusalScoped = accountExhaustedForModelOnly(providerName, row.id, model);
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
? 'Quota exhausted — switching to the next account'
|
|
515
|
-
: accountBlocked
|
|
516
|
-
? 'Account blocked — switching to the next account'
|
|
517
|
-
: 'Rate limited (cooldown) — switching to the next account',
|
|
518
|
-
});
|
|
605
|
+
const reason = rotationReason(evidence, accountBlocked);
|
|
606
|
+
options.onStageChange?.('reconnecting', { message: `${reason} — switching to the next account` });
|
|
519
607
|
}
|
|
520
608
|
}
|
|
521
|
-
throw
|
|
609
|
+
throw exhaustedSendError(
|
|
610
|
+
providerName,
|
|
611
|
+
model,
|
|
612
|
+
ledger.evidenced,
|
|
613
|
+
lastError || new Error('Provider account attempts exhausted.')
|
|
614
|
+
);
|
|
522
615
|
};
|
|
523
616
|
}
|
|
524
617
|
|