mixdog 1.3.5 → 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/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +4 -2
- 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 +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +17 -1
- package/src/defaults/skills/browser-use/SKILL.md +59 -11
- 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 +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- 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/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- 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 +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +181 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- 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 +145 -66
- 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-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- 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 +2 -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/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 +26 -0
- 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/runtime-core/ask-user-tool-defs.mjs +12 -7
- 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/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.mjs +125 -30
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
- 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 +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- 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 +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -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 +3 -2
- 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 +12 -3
- 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-execute.mjs +133 -90
- 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/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/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.mjs +36 -9
- 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-rows.mjs +8 -6
- 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/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/transcript-restore/restore.mjs +18 -1
- 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/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -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 +10 -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/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/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- 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/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/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/usage-rollup.mjs +1 -1
- 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 +7 -1
- 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 +10 -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-result-summary.mjs +1 -14
- 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 +448 -185
- package/src/session-runtime/agent-task-sessions.mjs +329 -180
- package/src/session-runtime/boot/apis.mjs +9 -0
- 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 +6 -1
- 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/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 +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- 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 +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- 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-owner.mjs +40 -9
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- 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 +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- 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 +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +16 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- 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 +124 -188
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- 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-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/dist/index.mjs +58 -64
- 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 +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- 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 +17 -1
- 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 +19 -2
- package/src/tui/session/turn.mjs +1 -0
- 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
|
@@ -2,10 +2,11 @@ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/pro
|
|
|
2
2
|
import { sanitizeToolPairs } from '../session/context-utils.mjs';
|
|
3
3
|
import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
|
|
4
4
|
import {
|
|
5
|
-
announceAnthropicModelFallback,
|
|
6
5
|
createAnthropicOverloadTracker,
|
|
7
6
|
isTerminalOverloadModel,
|
|
7
|
+
takeAnthropicModelFallback,
|
|
8
8
|
} from './anthropic-overload-fallback.mjs';
|
|
9
|
+
import { removeAbortListener } from './anthropic-oauth-request/abort-wiring.mjs';
|
|
9
10
|
import { readStreamOutcome } from './lib/stream-outcome.mjs';
|
|
10
11
|
import { createPassthroughSignal } from '../stall-policy.mjs';
|
|
11
12
|
import { createAbortController } from '../../../shared/abort-controller.mjs';
|
|
@@ -18,6 +19,7 @@ import { ANTHROPIC_EFFORT_LEVELS, withEffortRejectionFallback } from './effort-r
|
|
|
18
19
|
import { armFirstByteWatchdog, createAnthropicApiTransport } from './anthropic-api-transport.mjs';
|
|
19
20
|
import { createAnthropicApiRecovery } from './anthropic-api-recovery.mjs';
|
|
20
21
|
import { buildAnthropicTurnResult } from './anthropic-turn-result.mjs';
|
|
22
|
+
import { sendAnthropicCacheWarm } from './anthropic-cache-warm.mjs';
|
|
21
23
|
import {
|
|
22
24
|
assertAnthropicStreamNotEmpty,
|
|
23
25
|
createAnthropicMidState,
|
|
@@ -38,6 +40,29 @@ import { streamCallbacks } from './lib/send-callbacks.mjs';
|
|
|
38
40
|
import { preparePdfTextForProvider } from './media-normalization.mjs';
|
|
39
41
|
export { _test, _toAnthropicMessagesForTest } from './anthropic-messages.mjs';
|
|
40
42
|
|
|
43
|
+
// One stream attempt's controller, aborted with the caller's reason when the
|
|
44
|
+
// send's signal aborts. An already-aborted send fails before the attempt.
|
|
45
|
+
function linkAttemptAbort(totalSignal) {
|
|
46
|
+
const streamController = createAbortController();
|
|
47
|
+
let cancelHandler = null;
|
|
48
|
+
|
|
49
|
+
if (totalSignal) {
|
|
50
|
+
if (totalSignal.aborted) {
|
|
51
|
+
const reason = totalSignal.reason;
|
|
52
|
+
throw reason instanceof Error ? reason : new Error('Anthropic request aborted');
|
|
53
|
+
}
|
|
54
|
+
cancelHandler = () => {
|
|
55
|
+
try {
|
|
56
|
+
streamController.abort(totalSignal.reason);
|
|
57
|
+
} catch {
|
|
58
|
+
// best-effort propagation of caller cancellation
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
totalSignal.addEventListener('abort', cancelHandler, { once: true });
|
|
62
|
+
}
|
|
63
|
+
return { streamController, cancelHandler };
|
|
64
|
+
}
|
|
65
|
+
|
|
41
66
|
export class AnthropicProvider {
|
|
42
67
|
// Takes a PDF as a native document block (decided by this adapter's wire protocol).
|
|
43
68
|
nativePdf = true;
|
|
@@ -143,6 +168,38 @@ export class AnthropicProvider {
|
|
|
143
168
|
throw err;
|
|
144
169
|
}
|
|
145
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Cache-warm mode (anthropic-cache-warm.mjs): the request `send` would
|
|
173
|
+
* build for these arguments, sent once on the retry-free client — no
|
|
174
|
+
* key reload, fallback model or re-send — and cut at the first content
|
|
175
|
+
* block. `onUsage` receives the reported usage as it arrives.
|
|
176
|
+
*/
|
|
177
|
+
async sendCacheWarm(messages, model, tools, opts = {}, { onUsage, accountId = '' } = {}) {
|
|
178
|
+
const prepared = await preparePdfTextForProvider(sanitizeToolPairs(messages), this);
|
|
179
|
+
const { params, requestHeaders } = await buildAnthropicApiRequest({
|
|
180
|
+
name: this.name,
|
|
181
|
+
config: this.config,
|
|
182
|
+
messages: prepared,
|
|
183
|
+
useModel: model,
|
|
184
|
+
tools,
|
|
185
|
+
opts,
|
|
186
|
+
fastModeLatched: this.fastModeBetaHeaderLatched,
|
|
187
|
+
modelInfo: isCustomProviderName(this.name) ? this.getCachedModelInfo?.(model) || null : null,
|
|
188
|
+
capabilityModel: this._capabilityModel(model),
|
|
189
|
+
});
|
|
190
|
+
return sendAnthropicCacheWarm({
|
|
191
|
+
providerName: this.name,
|
|
192
|
+
instance: this,
|
|
193
|
+
accountId,
|
|
194
|
+
model,
|
|
195
|
+
opts,
|
|
196
|
+
onUsage,
|
|
197
|
+
post: (signal) =>
|
|
198
|
+
this._currentClient()
|
|
199
|
+
.messages.create(params, { signal, ...(requestHeaders ? { headers: requestHeaders } : {}) })
|
|
200
|
+
.asResponse(),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
146
203
|
// The `anthropic` API-key provider and every custom `anthropic`-protocol
|
|
147
204
|
// provider (a subclass of this one) share this send path.
|
|
148
205
|
async _doSend(messages, model, tools, sendOpts) {
|
|
@@ -188,89 +245,20 @@ export class AnthropicProvider {
|
|
|
188
245
|
const totalTimeout = createPassthroughSignal(externalSignal);
|
|
189
246
|
const totalSignal = totalTimeout.signal;
|
|
190
247
|
|
|
191
|
-
const
|
|
192
|
-
if (!handler) return;
|
|
193
|
-
try {
|
|
194
|
-
totalSignal.removeEventListener('abort', handler);
|
|
195
|
-
} catch {}
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
// Request-loop 529s and SSE overload events share one counter; at the
|
|
199
|
-
// limit the configured fallback model takes over the whole send.
|
|
200
|
-
const overload = createAnthropicOverloadTracker({
|
|
201
|
-
model: useModel,
|
|
202
|
-
fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
|
|
203
|
-
terminalAtLimit: !isCustomProviderName(this.name) && isTerminalOverloadModel(useModel),
|
|
204
|
-
});
|
|
205
|
-
// The SDK calls and their initial-response retry policy
|
|
206
|
-
// (anthropic-api-transport.mjs).
|
|
207
|
-
const transport = createAnthropicApiTransport({
|
|
208
|
-
client: this._currentClient(),
|
|
209
|
-
label: this.name,
|
|
248
|
+
const { transport, buildTurnResult, recovery, midstream } = this._createSendRecovery({
|
|
210
249
|
opts,
|
|
211
250
|
useModel,
|
|
212
251
|
params,
|
|
213
252
|
requestHeaders,
|
|
214
253
|
totalSignal,
|
|
215
|
-
overload,
|
|
216
|
-
});
|
|
217
|
-
// Usage accounting + caller-visible projection, shared by the streaming
|
|
218
|
-
// success path and the non-streaming fallback (anthropic-turn-result.mjs).
|
|
219
|
-
const buildTurnResult = (parseResult) =>
|
|
220
|
-
buildAnthropicTurnResult(parseResult, { provider: this.name, useModel, opts });
|
|
221
|
-
// Non-streaming re-issue of a dead stream, behind the transport-recovery
|
|
222
|
-
// budget and the exposed-output retraction handshake
|
|
223
|
-
// (anthropic-api-recovery.mjs).
|
|
224
|
-
const recovery = createAnthropicApiRecovery({
|
|
225
|
-
label: this.name,
|
|
226
|
-
opts,
|
|
227
|
-
useModel,
|
|
228
|
-
params,
|
|
229
|
-
totalSignal,
|
|
230
|
-
transport,
|
|
231
|
-
buildTurnResult,
|
|
232
254
|
onStageChange,
|
|
233
255
|
onTextReset,
|
|
234
256
|
});
|
|
235
|
-
// Bounded mid-stream retries for transient stream loss; jittered backoff
|
|
236
|
-
// between attempts (anthropic-midstream-recovery.mjs, shared with
|
|
237
|
-
// anthropic-oauth).
|
|
238
|
-
const midstream = createAnthropicMidstreamRecovery({
|
|
239
|
-
label: this.name,
|
|
240
|
-
displayName: 'Anthropic',
|
|
241
|
-
outcomeProvider: 'anthropic',
|
|
242
|
-
midstreamOwner: `${this.name}-midstream`,
|
|
243
|
-
unreachableMessage: 'Anthropic mid-stream retry: unreachable',
|
|
244
|
-
// The transport's withRetry already exhausted the full request-level
|
|
245
|
-
// budget for a non-OK initial response, so it must not earn an
|
|
246
|
-
// additional SSE retry budget here.
|
|
247
|
-
initialResponseErrorTerminal: true,
|
|
248
|
-
maxRetries: ANTHROPIC_MAX_MIDSTREAM_RETRIES,
|
|
249
|
-
totalSignal,
|
|
250
|
-
recovery,
|
|
251
|
-
onStageChange,
|
|
252
|
-
retry529: opts.retry529 !== false,
|
|
253
|
-
overload,
|
|
254
|
-
});
|
|
255
257
|
|
|
256
258
|
let streamingFallbackTried = false;
|
|
257
259
|
try {
|
|
258
260
|
for (let attemptIndex = 0; attemptIndex <= ANTHROPIC_MAX_MIDSTREAM_RETRIES; attemptIndex++) {
|
|
259
|
-
const streamController =
|
|
260
|
-
let cancelHandler = null;
|
|
261
|
-
|
|
262
|
-
if (totalSignal) {
|
|
263
|
-
if (totalSignal.aborted) {
|
|
264
|
-
const reason = totalSignal.reason;
|
|
265
|
-
throw reason instanceof Error ? reason : new Error('Anthropic request aborted');
|
|
266
|
-
}
|
|
267
|
-
cancelHandler = () => {
|
|
268
|
-
try {
|
|
269
|
-
streamController.abort(totalSignal.reason);
|
|
270
|
-
} catch {}
|
|
271
|
-
};
|
|
272
|
-
totalSignal.addEventListener('abort', cancelHandler, { once: true });
|
|
273
|
-
}
|
|
261
|
+
const { streamController, cancelHandler } = linkAttemptAbort(totalSignal);
|
|
274
262
|
|
|
275
263
|
const midState = createAnthropicMidState(attemptIndex);
|
|
276
264
|
|
|
@@ -280,13 +268,17 @@ export class AnthropicProvider {
|
|
|
280
268
|
try {
|
|
281
269
|
try {
|
|
282
270
|
onStageChange?.('requesting');
|
|
283
|
-
} catch {
|
|
271
|
+
} catch {
|
|
272
|
+
// the stage observer is advisory
|
|
273
|
+
}
|
|
284
274
|
|
|
285
275
|
response = await transport.requestStreamingResponse();
|
|
286
276
|
|
|
287
277
|
try {
|
|
288
278
|
onStageChange?.('streaming');
|
|
289
|
-
} catch {
|
|
279
|
+
} catch {
|
|
280
|
+
// the stage observer is advisory
|
|
281
|
+
}
|
|
290
282
|
|
|
291
283
|
firstByte = armFirstByteWatchdog(streamController, midState);
|
|
292
284
|
|
|
@@ -307,7 +299,9 @@ export class AnthropicProvider {
|
|
|
307
299
|
);
|
|
308
300
|
try {
|
|
309
301
|
streamController.abort?.('Anthropic SSE complete');
|
|
310
|
-
} catch {
|
|
302
|
+
} catch {
|
|
303
|
+
// the stream is complete; the controller may already be aborted
|
|
304
|
+
}
|
|
311
305
|
|
|
312
306
|
firstByte.cleanup();
|
|
313
307
|
assertAnthropicStreamNotEmpty(midState, parseResult, 'Anthropic');
|
|
@@ -336,26 +330,80 @@ export class AnthropicProvider {
|
|
|
336
330
|
return decision.value;
|
|
337
331
|
} finally {
|
|
338
332
|
firstByte?.cleanup();
|
|
339
|
-
|
|
333
|
+
removeAbortListener(totalSignal, cancelHandler);
|
|
340
334
|
}
|
|
341
335
|
}
|
|
342
336
|
throw midstream.exhaustedError();
|
|
343
337
|
} catch (err) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
// operation, not just the first request.
|
|
347
|
-
if (!(err instanceof AnthropicFallbackTriggeredError)) throw err;
|
|
348
|
-
process.stderr.write(`[${this.name}] ${err.message}\n`);
|
|
349
|
-
announceAnthropicModelFallback(onStageChange, useModel, err.fallbackModel);
|
|
350
|
-
return await this._doSend(messages, err.fallbackModel, tools, {
|
|
351
|
-
...opts,
|
|
352
|
-
fallbackModel: undefined,
|
|
353
|
-
_fallbackTriggered: true,
|
|
354
|
-
});
|
|
338
|
+
const fallbackOpts = takeAnthropicModelFallback(err, { tag: this.name, model: useModel, opts, onStageChange });
|
|
339
|
+
return await this._doSend(messages, err.fallbackModel, tools, fallbackOpts);
|
|
355
340
|
} finally {
|
|
356
341
|
totalTimeout.cleanup();
|
|
357
342
|
}
|
|
358
343
|
}
|
|
344
|
+
// The recovery pipeline of one send over its fixed request: the shared
|
|
345
|
+
// overload counter, the transport, the turn-result projection, the
|
|
346
|
+
// non-streaming recovery and the bounded mid-stream retries.
|
|
347
|
+
_createSendRecovery({ opts, useModel, params, requestHeaders, totalSignal, onStageChange, onTextReset }) {
|
|
348
|
+
// Request-loop 529s and SSE overload events share one counter; at the
|
|
349
|
+
// limit the configured fallback model takes over the whole send.
|
|
350
|
+
const overload = createAnthropicOverloadTracker({
|
|
351
|
+
model: useModel,
|
|
352
|
+
fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
|
|
353
|
+
terminalAtLimit: !isCustomProviderName(this.name) && isTerminalOverloadModel(useModel),
|
|
354
|
+
});
|
|
355
|
+
// The SDK calls and their initial-response retry policy
|
|
356
|
+
// (anthropic-api-transport.mjs).
|
|
357
|
+
const transport = createAnthropicApiTransport({
|
|
358
|
+
client: this._currentClient(),
|
|
359
|
+
label: this.name,
|
|
360
|
+
opts,
|
|
361
|
+
useModel,
|
|
362
|
+
params,
|
|
363
|
+
requestHeaders,
|
|
364
|
+
totalSignal,
|
|
365
|
+
overload,
|
|
366
|
+
});
|
|
367
|
+
// Usage accounting + caller-visible projection, shared by the streaming
|
|
368
|
+
// success path and the non-streaming fallback (anthropic-turn-result.mjs).
|
|
369
|
+
const buildTurnResult = (parseResult) =>
|
|
370
|
+
buildAnthropicTurnResult(parseResult, { provider: this.name, useModel, opts });
|
|
371
|
+
// Non-streaming re-issue of a dead stream, behind the transport-recovery
|
|
372
|
+
// budget and the exposed-output retraction handshake
|
|
373
|
+
// (anthropic-api-recovery.mjs).
|
|
374
|
+
const recovery = createAnthropicApiRecovery({
|
|
375
|
+
label: this.name,
|
|
376
|
+
opts,
|
|
377
|
+
useModel,
|
|
378
|
+
params,
|
|
379
|
+
totalSignal,
|
|
380
|
+
transport,
|
|
381
|
+
buildTurnResult,
|
|
382
|
+
onStageChange,
|
|
383
|
+
onTextReset,
|
|
384
|
+
});
|
|
385
|
+
// Bounded mid-stream retries for transient stream loss; jittered backoff
|
|
386
|
+
// between attempts (anthropic-midstream-recovery.mjs, shared with
|
|
387
|
+
// anthropic-oauth).
|
|
388
|
+
const midstream = createAnthropicMidstreamRecovery({
|
|
389
|
+
label: this.name,
|
|
390
|
+
displayName: 'Anthropic',
|
|
391
|
+
outcomeProvider: 'anthropic',
|
|
392
|
+
midstreamOwner: `${this.name}-midstream`,
|
|
393
|
+
unreachableMessage: 'Anthropic mid-stream retry: unreachable',
|
|
394
|
+
// The transport's withRetry already exhausted the full request-level
|
|
395
|
+
// budget for a non-OK initial response, so it must not earn an
|
|
396
|
+
// additional SSE retry budget here.
|
|
397
|
+
initialResponseErrorTerminal: true,
|
|
398
|
+
maxRetries: ANTHROPIC_MAX_MIDSTREAM_RETRIES,
|
|
399
|
+
totalSignal,
|
|
400
|
+
recovery,
|
|
401
|
+
onStageChange,
|
|
402
|
+
retry529: opts.retry529 !== false,
|
|
403
|
+
overload,
|
|
404
|
+
});
|
|
405
|
+
return { transport, buildTurnResult, recovery, midstream };
|
|
406
|
+
}
|
|
359
407
|
async listModels() {
|
|
360
408
|
const apiKey = resolveProviderApiKey(this.name, this.config);
|
|
361
409
|
if (!apiKey) return MODELS;
|
|
@@ -132,103 +132,12 @@ export function createAntigravityRequest({
|
|
|
132
132
|
firstByte = ANTIGRAVITY_UNCLASSED_FIRST_BYTE,
|
|
133
133
|
afterToolResult = false,
|
|
134
134
|
}) {
|
|
135
|
-
const log = (attempt, lastErr, delayMs) =>
|
|
136
|
-
|
|
137
|
-
const firstByteLimited = isFirstByteTimeout(lastErr) && firstByte.retries != null;
|
|
138
|
-
emitProviderRetryStage(opts.onStageChange, {
|
|
139
|
-
attempt: attempt + 1,
|
|
140
|
-
maxAttempts: firstByteLimited
|
|
141
|
-
? Math.min(ANTIGRAVITY_RETRY.maxAttempts, firstByte.retries + 1)
|
|
142
|
-
: ANTIGRAVITY_RETRY.maxAttempts,
|
|
143
|
-
lastErr,
|
|
144
|
-
delayMs,
|
|
145
|
-
});
|
|
146
|
-
process.stderr.write(`[antigravity] retry ${attempt + 1} after ${lastErr?.message || 'transient error'}\n`);
|
|
147
|
-
};
|
|
148
|
-
// Stamps what the attempt tells the user: how many attempts ran and, for a
|
|
149
|
-
// first-byte timeout, how long the model was given in total. A timeout past
|
|
150
|
-
// the first-byte retry budget ends the ladder here. A caller's cancellation
|
|
151
|
-
// reason may be shared by other requests and is never stamped.
|
|
152
|
-
const noteFailure = (err, { attemptSignal, windowMs, send }) => {
|
|
153
|
-
if (!err || typeof err !== 'object' || attemptSignal?.aborted) return err;
|
|
154
|
-
send.attempts += 1;
|
|
155
|
-
err.attempts = send.attempts;
|
|
156
|
-
if (!isFirstByteTimeout(err)) return err;
|
|
157
|
-
send.firstByteTimeouts += 1;
|
|
158
|
-
send.firstByteWaitedMs += windowMs;
|
|
159
|
-
err.waitedMs = send.firstByteWaitedMs;
|
|
160
|
-
err.providerLabel = 'Antigravity';
|
|
161
|
-
if (firstByte.retries != null && send.firstByteTimeouts > firstByte.retries) {
|
|
162
|
-
markProviderRecoveryExhausted(err, { owner: 'antigravity-first-byte', attempts: send.attempts, terminal: true });
|
|
163
|
-
}
|
|
164
|
-
return err;
|
|
165
|
-
};
|
|
135
|
+
const log = (attempt, lastErr, delayMs) =>
|
|
136
|
+
logAntigravityRetry(opts.onStageChange, firstByte, attempt, lastErr, delayMs);
|
|
166
137
|
const attemptOnce = (send) =>
|
|
167
138
|
withRetry(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
opts.onStageChange?.('requesting');
|
|
171
|
-
} catch {
|
|
172
|
-
/* heartbeat */
|
|
173
|
-
}
|
|
174
|
-
const windowMs = firstByteWindowMs(firstByte, send.firstByteTimeouts);
|
|
175
|
-
try {
|
|
176
|
-
const firstByteTimer = createTimeoutSignal(attemptSignal, windowMs, 'Antigravity first byte');
|
|
177
|
-
let res;
|
|
178
|
-
try {
|
|
179
|
-
const response = fetchFn(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
|
|
180
|
-
method: 'POST',
|
|
181
|
-
headers,
|
|
182
|
-
body,
|
|
183
|
-
signal: firstByteTimer.signal,
|
|
184
|
-
dispatcher: getLlmDispatcher(),
|
|
185
|
-
});
|
|
186
|
-
// The request is on the wire and its headers are pending: the
|
|
187
|
-
// gateway sends them only with the first chunk, so this wait is
|
|
188
|
-
// the model's queueing and thinking time.
|
|
189
|
-
try {
|
|
190
|
-
opts.onStageChange?.(MODEL_THINKING_STAGE);
|
|
191
|
-
} catch {
|
|
192
|
-
/* heartbeat */
|
|
193
|
-
}
|
|
194
|
-
res = await response;
|
|
195
|
-
} catch (err) {
|
|
196
|
-
// Fetch surfaces AbortError; rethrow the timer/parent
|
|
197
|
-
// reason so same-host retry sees EPROVIDERTIMEOUT and
|
|
198
|
-
// a caller cancel stays a cancel.
|
|
199
|
-
if (firstByteTimer.signal.aborted && firstByteTimer.signal.reason instanceof Error) {
|
|
200
|
-
const reason = firstByteTimer.signal.reason;
|
|
201
|
-
if (!attemptSignal?.aborted) reason.timeoutPhase = TIMEOUT_PHASE.HEADERS;
|
|
202
|
-
throw reason;
|
|
203
|
-
}
|
|
204
|
-
throw err;
|
|
205
|
-
} finally {
|
|
206
|
-
firstByteTimer.cleanup();
|
|
207
|
-
}
|
|
208
|
-
// Headers arrived: the connection is live even while the model
|
|
209
|
-
// has not produced its first chunk.
|
|
210
|
-
try {
|
|
211
|
-
opts.onStreamDelta?.('transport');
|
|
212
|
-
} catch {}
|
|
213
|
-
if (!res.ok) {
|
|
214
|
-
const text = await res.text().catch(() => '');
|
|
215
|
-
const failure = antigravityError(res, text, endpoint);
|
|
216
|
-
// Capacity is paced by the local schedule below; the generic loop
|
|
217
|
-
// would sleep whatever wait the server names.
|
|
218
|
-
if (isCapacityFailure(failure)) markProviderRecoveryExhausted(failure, { owner: 'antigravity-capacity' });
|
|
219
|
-
throw failure;
|
|
220
|
-
}
|
|
221
|
-
try {
|
|
222
|
-
opts.onStageChange?.('streaming');
|
|
223
|
-
} catch {
|
|
224
|
-
/* heartbeat */
|
|
225
|
-
}
|
|
226
|
-
collector.beginAttempt();
|
|
227
|
-
return await collector.consume(res, attemptSignal, { firstByteTimeoutMs: windowMs });
|
|
228
|
-
} catch (err) {
|
|
229
|
-
throw noteFailure(err, { attemptSignal, windowMs, send });
|
|
230
|
-
}
|
|
231
|
-
},
|
|
139
|
+
({ signal: attemptSignal }) =>
|
|
140
|
+
streamAttempt({ fetchFn, endpoint, headers, body, opts, collector, firstByte, send, attemptSignal }),
|
|
232
141
|
{
|
|
233
142
|
...ANTIGRAVITY_RETRY,
|
|
234
143
|
finalOnExhaust: true,
|
|
@@ -261,16 +170,128 @@ export function createAntigravityRequest({
|
|
|
261
170
|
streamAttempt += 1;
|
|
262
171
|
await sleepWithAbort(reask.waitMs, signal, sleepFn, 'Antigravity retry wait aborted');
|
|
263
172
|
} catch (err) {
|
|
264
|
-
const waitMs =
|
|
265
|
-
if (waitMs == null) {
|
|
266
|
-
if (isCapacityFailure(err)) {
|
|
267
|
-
markProviderRecoveryExhausted(err, { owner: 'antigravity-capacity', attempts: tried });
|
|
268
|
-
}
|
|
269
|
-
throw err;
|
|
270
|
-
}
|
|
173
|
+
const waitMs = scheduledRetryWaitMs(err, state, tried);
|
|
271
174
|
log(tried, err, waitMs);
|
|
272
175
|
await sleepWithAbort(waitMs, signal, sleepFn, 'Antigravity retry wait aborted');
|
|
273
176
|
}
|
|
274
177
|
}
|
|
275
178
|
};
|
|
276
179
|
}
|
|
180
|
+
|
|
181
|
+
// The retry status and log line of one scheduled retry. A first-byte timeout
|
|
182
|
+
// runs on its own, shorter retry budget.
|
|
183
|
+
function logAntigravityRetry(onStageChange, firstByte, attempt, lastErr, delayMs) {
|
|
184
|
+
const firstByteLimited = isFirstByteTimeout(lastErr) && firstByte.retries != null;
|
|
185
|
+
emitProviderRetryStage(onStageChange, {
|
|
186
|
+
attempt: attempt + 1,
|
|
187
|
+
maxAttempts: firstByteLimited
|
|
188
|
+
? Math.min(ANTIGRAVITY_RETRY.maxAttempts, firstByte.retries + 1)
|
|
189
|
+
: ANTIGRAVITY_RETRY.maxAttempts,
|
|
190
|
+
lastErr,
|
|
191
|
+
delayMs,
|
|
192
|
+
providerLabel: 'Antigravity',
|
|
193
|
+
});
|
|
194
|
+
process.stderr.write(`[antigravity] retry ${attempt + 1} after ${lastErr?.message || 'transient error'}\n`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// The local wait before re-sending a failed request (429 / capacity schedule);
|
|
198
|
+
// a failure the schedule does not retry is rethrown, a capacity one marked
|
|
199
|
+
// exhausted after `tried` sends.
|
|
200
|
+
function scheduledRetryWaitMs(err, state, tried) {
|
|
201
|
+
const waitMs = antigravity429WaitMs(err, state);
|
|
202
|
+
if (waitMs == null) {
|
|
203
|
+
if (isCapacityFailure(err)) {
|
|
204
|
+
markProviderRecoveryExhausted(err, { owner: 'antigravity-capacity', attempts: tried });
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
return waitMs;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// One POST under the first-byte window: headers pending is the model's
|
|
212
|
+
// thinking time, a non-OK body becomes the typed error, and a live stream is
|
|
213
|
+
// handed to the collector for this attempt.
|
|
214
|
+
async function streamAttempt({ fetchFn, endpoint, headers, body, opts, collector, firstByte, send, attemptSignal }) {
|
|
215
|
+
try {
|
|
216
|
+
opts.onStageChange?.('requesting');
|
|
217
|
+
} catch {
|
|
218
|
+
/* heartbeat */
|
|
219
|
+
}
|
|
220
|
+
const windowMs = firstByteWindowMs(firstByte, send.firstByteTimeouts);
|
|
221
|
+
try {
|
|
222
|
+
const firstByteTimer = createTimeoutSignal(attemptSignal, windowMs, 'Antigravity first byte');
|
|
223
|
+
let res;
|
|
224
|
+
try {
|
|
225
|
+
const response = fetchFn(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
|
|
226
|
+
method: 'POST',
|
|
227
|
+
headers,
|
|
228
|
+
body,
|
|
229
|
+
signal: firstByteTimer.signal,
|
|
230
|
+
dispatcher: getLlmDispatcher(),
|
|
231
|
+
});
|
|
232
|
+
// The request is on the wire and its headers are pending: the
|
|
233
|
+
// gateway sends them only with the first chunk, so this wait is
|
|
234
|
+
// the model's queueing and thinking time.
|
|
235
|
+
try {
|
|
236
|
+
opts.onStageChange?.(MODEL_THINKING_STAGE);
|
|
237
|
+
} catch {
|
|
238
|
+
/* heartbeat */
|
|
239
|
+
}
|
|
240
|
+
res = await response;
|
|
241
|
+
} catch (err) {
|
|
242
|
+
// Fetch surfaces AbortError; rethrow the timer/parent
|
|
243
|
+
// reason so same-host retry sees EPROVIDERTIMEOUT and
|
|
244
|
+
// a caller cancel stays a cancel.
|
|
245
|
+
if (firstByteTimer.signal.aborted && firstByteTimer.signal.reason instanceof Error) {
|
|
246
|
+
const reason = firstByteTimer.signal.reason;
|
|
247
|
+
if (!attemptSignal?.aborted) reason.timeoutPhase = TIMEOUT_PHASE.HEADERS;
|
|
248
|
+
throw reason;
|
|
249
|
+
}
|
|
250
|
+
throw err;
|
|
251
|
+
} finally {
|
|
252
|
+
firstByteTimer.cleanup();
|
|
253
|
+
}
|
|
254
|
+
// Headers arrived: the connection is live even while the model
|
|
255
|
+
// has not produced its first chunk.
|
|
256
|
+
try {
|
|
257
|
+
opts.onStreamDelta?.('transport');
|
|
258
|
+
} catch {
|
|
259
|
+
// the stream-delta observer is advisory
|
|
260
|
+
}
|
|
261
|
+
if (!res.ok) {
|
|
262
|
+
const text = await res.text().catch(() => '');
|
|
263
|
+
const failure = antigravityError(res, text, endpoint);
|
|
264
|
+
// Capacity is paced by the local schedule; the generic loop
|
|
265
|
+
// would sleep whatever wait the server names.
|
|
266
|
+
if (isCapacityFailure(failure)) markProviderRecoveryExhausted(failure, { owner: 'antigravity-capacity' });
|
|
267
|
+
throw failure;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
opts.onStageChange?.('streaming');
|
|
271
|
+
} catch {
|
|
272
|
+
/* heartbeat */
|
|
273
|
+
}
|
|
274
|
+
collector.beginAttempt();
|
|
275
|
+
return await collector.consume(res, attemptSignal, { firstByteTimeoutMs: windowMs });
|
|
276
|
+
} catch (err) {
|
|
277
|
+
throw noteAttemptFailure(err, { attemptSignal, windowMs, send, firstByte });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Stamps what the attempt tells the user: how many attempts ran and, for a
|
|
282
|
+
// first-byte timeout, how long the model was given in total. A timeout past
|
|
283
|
+
// the first-byte retry budget ends the ladder here. A caller's cancellation
|
|
284
|
+
// reason may be shared by other requests and is never stamped.
|
|
285
|
+
function noteAttemptFailure(err, { attemptSignal, windowMs, send, firstByte }) {
|
|
286
|
+
if (!err || typeof err !== 'object' || attemptSignal?.aborted) return err;
|
|
287
|
+
send.attempts += 1;
|
|
288
|
+
err.attempts = send.attempts;
|
|
289
|
+
if (!isFirstByteTimeout(err)) return err;
|
|
290
|
+
send.firstByteTimeouts += 1;
|
|
291
|
+
send.firstByteWaitedMs += windowMs;
|
|
292
|
+
err.waitedMs = send.firstByteWaitedMs;
|
|
293
|
+
if (firstByte.retries != null && send.firstByteTimeouts > firstByte.retries) {
|
|
294
|
+
markProviderRecoveryExhausted(err, { owner: 'antigravity-first-byte', attempts: send.attempts, terminal: true });
|
|
295
|
+
}
|
|
296
|
+
return err;
|
|
297
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// cache-warm-admission.mjs — the account gate of a cache warm
|
|
2
|
+
// (anthropic-cache-warm.mjs). A warm never waits, queues, rotates accounts or
|
|
3
|
+
// retries: right before its POST it reads the state a normal send consults
|
|
4
|
+
// for the account it goes to — the Developer option, the account's admission
|
|
5
|
+
// lane (rate-limit cooldown, a lane throttled after a 429), the account
|
|
6
|
+
// roster's blocks and quota windows, and the session's quota-resume pause —
|
|
7
|
+
// and is skipped when any of them holds or when no fresh quota reading says
|
|
8
|
+
// the account has room. A warm's own refusal is recorded like a send's: a 429
|
|
9
|
+
// in the lane cooldown, and the roster's verdict through the account pool.
|
|
10
|
+
import {
|
|
11
|
+
ACCOUNT_PROVIDERS,
|
|
12
|
+
providerAccountExhausted,
|
|
13
|
+
quotaReadingFresh,
|
|
14
|
+
readProviderAccountPool,
|
|
15
|
+
staleQuotaWindows,
|
|
16
|
+
} from '../../../shared/provider-accounts.mjs';
|
|
17
|
+
import { quotaResumeEntry } from '../../../shared/quota-resume-store.mjs';
|
|
18
|
+
import { recordAccountRefusal } from './account-pool.mjs';
|
|
19
|
+
import { providerAdmissionKey, providerAdmissionScheduler } from './admission-scheduler.mjs';
|
|
20
|
+
import { readCachedOAuthUsageSnapshot } from './oauth-usage.mjs';
|
|
21
|
+
import { providerAvailable } from './provider-availability.mjs';
|
|
22
|
+
|
|
23
|
+
function readingBlock(providerName, windows, checkedAt, now, exhausted) {
|
|
24
|
+
if (!Array.isArray(windows) || !quotaReadingFresh(checkedAt, now)) return 'no fresh quota reading';
|
|
25
|
+
if (staleQuotaWindows(providerName, windows)) return 'no fresh quota reading';
|
|
26
|
+
return exhausted ? 'account blocked or quota exhausted' : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function rosterBlock(providerName, accountId, model, now) {
|
|
30
|
+
if (!ACCOUNT_PROVIDERS.includes(providerName)) return null;
|
|
31
|
+
const pool = readProviderAccountPool(providerName);
|
|
32
|
+
const useCredits = pool.useCredits === true;
|
|
33
|
+
if (!pool.accounts.length) {
|
|
34
|
+
if (useCredits) return null;
|
|
35
|
+
const stored = readCachedOAuthUsageSnapshot({ provider: providerName, model: '' });
|
|
36
|
+
const row = stored ? { usage: { windows: stored.quotaWindows, checkedAt: stored.cachedAt } } : null;
|
|
37
|
+
return readingBlock(
|
|
38
|
+
providerName,
|
|
39
|
+
stored?.quotaWindows,
|
|
40
|
+
stored?.cachedAt,
|
|
41
|
+
now,
|
|
42
|
+
row && providerAccountExhausted(row, now, model)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (!accountId) return 'account holding the cache unknown';
|
|
46
|
+
const row = pool.accounts.find((entry) => entry.id === accountId);
|
|
47
|
+
if (!row) return 'account not connected';
|
|
48
|
+
const exhausted = providerAccountExhausted(row, now, model, { useCredits });
|
|
49
|
+
if (useCredits) return exhausted ? 'account blocked' : null;
|
|
50
|
+
return readingBlock(providerName, row.usage?.windows, row.usage?.checkedAt, now, exhausted);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Why a warm on `instance` (account `accountId` of `providerName`; no id for
|
|
55
|
+
* a provider without an account roster) may not be sent now, or null. The
|
|
56
|
+
* instance's admission lane — the one its ordinary sends are admitted on —
|
|
57
|
+
* cooling down or throttled blocks it. Never waits.
|
|
58
|
+
*/
|
|
59
|
+
export function cacheWarmAccountBlock(
|
|
60
|
+
providerName,
|
|
61
|
+
instance,
|
|
62
|
+
{ accountId = '', model = '', sessionId = '', scheduler = providerAdmissionScheduler, now = Date.now() } = {}
|
|
63
|
+
) {
|
|
64
|
+
if (!providerAvailable(providerName)) return 'provider disabled';
|
|
65
|
+
const lane = scheduler.immediateBlock(providerAdmissionKey(providerName, instance));
|
|
66
|
+
if (lane) return lane;
|
|
67
|
+
if (sessionId && quotaResumeEntry(sessionId)) return 'session paused until its quota resets';
|
|
68
|
+
return rosterBlock(providerName, accountId, model, now);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The error a skipped warm rejects with (nothing was sent or billed). */
|
|
72
|
+
export function cacheWarmBlockedError(reason) {
|
|
73
|
+
return Object.assign(new Error(`cache warm skipped: ${reason}`), { code: 'ECACHEWARMBLOCKED', usage: null });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Feed a warm's refusal into the shared state a send's would reach (the
|
|
77
|
+
* lane keeps 429s only; the roster judges every refusal as the pool's send
|
|
78
|
+
* does). A warm the caller aborted is no refusal. */
|
|
79
|
+
export async function recordCacheWarmRefusal(
|
|
80
|
+
providerName,
|
|
81
|
+
instance,
|
|
82
|
+
error,
|
|
83
|
+
{ accountId = '', model = '', aborted = false, scheduler = providerAdmissionScheduler } = {}
|
|
84
|
+
) {
|
|
85
|
+
if (aborted || error?.code === 'ECACHEWARMBLOCKED') return;
|
|
86
|
+
scheduler.recordRateLimit(providerAdmissionKey(providerName, instance), error);
|
|
87
|
+
if (accountId && ACCOUNT_PROVIDERS.includes(providerName)) {
|
|
88
|
+
await recordAccountRefusal(providerName, accountId, model, error, instance);
|
|
89
|
+
}
|
|
90
|
+
}
|