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
|
@@ -5,6 +5,7 @@ import http2 from 'node:http2';
|
|
|
5
5
|
import { MAX_CONNECT_FRAME_BYTES, createCursorByteQueue } from './cursor-wire-guards.mjs';
|
|
6
6
|
import { cursorClientVersion, warmCursorClientVersion } from './cursor-client-version.mjs';
|
|
7
7
|
import { retryAfterMsFromError } from './retry-classifier.mjs';
|
|
8
|
+
import { providerTimeoutError } from '../stall-policy.mjs';
|
|
8
9
|
|
|
9
10
|
export { warmCursorClientVersion };
|
|
10
11
|
|
|
@@ -153,7 +154,9 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
153
154
|
if (closed || session.closed || session.destroyed) return;
|
|
154
155
|
try {
|
|
155
156
|
session.ping(() => {});
|
|
156
|
-
} catch {
|
|
157
|
+
} catch {
|
|
158
|
+
// the session may have closed since the check above
|
|
159
|
+
}
|
|
157
160
|
}, H2_PING_INTERVAL_MS);
|
|
158
161
|
ping.unref?.();
|
|
159
162
|
|
|
@@ -175,20 +178,28 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
175
178
|
closeError = error || (status >= 400 ? httpStatusError(status, responseHeaders) : null);
|
|
176
179
|
try {
|
|
177
180
|
request.close();
|
|
178
|
-
} catch {
|
|
181
|
+
} catch {
|
|
182
|
+
// the request stream may already be closed
|
|
183
|
+
}
|
|
179
184
|
try {
|
|
180
185
|
session.close();
|
|
181
|
-
} catch {
|
|
186
|
+
} catch {
|
|
187
|
+
// the session may already be closed
|
|
188
|
+
}
|
|
182
189
|
closeHandler?.(closeError);
|
|
183
190
|
};
|
|
184
191
|
const close = (error = null) => {
|
|
185
192
|
if (closed) return;
|
|
186
193
|
try {
|
|
187
194
|
request.close(http2.constants.NGHTTP2_CANCEL);
|
|
188
|
-
} catch {
|
|
195
|
+
} catch {
|
|
196
|
+
// the request stream may already be closed
|
|
197
|
+
}
|
|
189
198
|
try {
|
|
190
199
|
session.destroy();
|
|
191
|
-
} catch {
|
|
200
|
+
} catch {
|
|
201
|
+
// the session may already be destroyed
|
|
202
|
+
}
|
|
192
203
|
finish(error);
|
|
193
204
|
};
|
|
194
205
|
|
|
@@ -243,17 +254,21 @@ export async function callCursorUnary({ accessToken, path, body, url = API_URL,
|
|
|
243
254
|
let status = 0;
|
|
244
255
|
let responseHeaders = null;
|
|
245
256
|
let settled = false;
|
|
246
|
-
const timeout = setTimeout(() => finish(
|
|
257
|
+
const timeout = setTimeout(() => finish(providerTimeoutError('Cursor request', timeoutMs)), timeoutMs);
|
|
247
258
|
const finish = (error = null) => {
|
|
248
259
|
if (settled) return;
|
|
249
260
|
settled = true;
|
|
250
261
|
clearTimeout(timeout);
|
|
251
262
|
try {
|
|
252
263
|
request.close();
|
|
253
|
-
} catch {
|
|
264
|
+
} catch {
|
|
265
|
+
// the request stream may already be closed
|
|
266
|
+
}
|
|
254
267
|
try {
|
|
255
268
|
session.close();
|
|
256
|
-
} catch {
|
|
269
|
+
} catch {
|
|
270
|
+
// the session may already be closed
|
|
271
|
+
}
|
|
257
272
|
if (error) reject(error);
|
|
258
273
|
else if (status >= 400) reject(httpStatusError(status, responseHeaders));
|
|
259
274
|
else resolve(Buffer.concat(chunks));
|
|
@@ -322,7 +337,8 @@ export function parseEndStream(bytes) {
|
|
|
322
337
|
if (!payload?.error) return null;
|
|
323
338
|
const connectCode = String(payload.error.code || 'error').toLowerCase();
|
|
324
339
|
const detail = String(payload.error.message || 'request failed');
|
|
325
|
-
const tooLarge =
|
|
340
|
+
const tooLarge =
|
|
341
|
+
connectCode === 'resource_exhausted' && !THROTTLE_CUE_RE.test(detail) && REQUEST_TOO_LARGE_RE.test(detail);
|
|
326
342
|
const retryAfter =
|
|
327
343
|
payload.error.retryAfter ??
|
|
328
344
|
payload.error.retry_after ??
|
|
@@ -132,7 +132,9 @@ async function* responseSseEvents(response, signal) {
|
|
|
132
132
|
if (signal) {
|
|
133
133
|
try {
|
|
134
134
|
signal.removeEventListener('abort', onAbort);
|
|
135
|
-
} catch {
|
|
135
|
+
} catch {
|
|
136
|
+
// the abort listener may already be gone
|
|
137
|
+
}
|
|
136
138
|
}
|
|
137
139
|
if (aborted) await reader.cancel().catch(() => {});
|
|
138
140
|
}
|
|
@@ -480,16 +482,10 @@ function selectParameterizedCursorVariant(group, requested, sendOpts = {}) {
|
|
|
480
482
|
availableVariants[0] ||
|
|
481
483
|
null;
|
|
482
484
|
const parameters = { ...(defaultVariant?.parameters || {}), ...aliasParameters };
|
|
485
|
+
const defaultContext = parameters.context;
|
|
483
486
|
const routeParameters =
|
|
484
487
|
sendOpts.modelParameters && typeof sendOpts.modelParameters === 'object' ? sendOpts.modelParameters : {};
|
|
485
|
-
// The window picked the mode above: a stored `context` no variant of that
|
|
486
|
-
// mode offers (a 256k left beside a raised 500K window) is a stale value and
|
|
487
|
-
// yields to the mode's own; one the mode offers stays the user's choice.
|
|
488
|
-
const staleContext =
|
|
489
|
-
Object.hasOwn(routeParameters, 'context') &&
|
|
490
|
-
!availableVariants.some((variant) => String(variant.parameters?.context ?? '') === String(routeParameters.context));
|
|
491
488
|
for (const definition of group.definitions) {
|
|
492
|
-
if (staleContext && definition.id === 'context') continue;
|
|
493
489
|
if (Object.hasOwn(routeParameters, definition.id)) {
|
|
494
490
|
parameters[definition.id] = String(routeParameters[definition.id]);
|
|
495
491
|
}
|
|
@@ -508,9 +504,18 @@ function selectParameterizedCursorVariant(group, requested, sendOpts = {}) {
|
|
|
508
504
|
) {
|
|
509
505
|
parameters.fast = sendOpts.fast === true ? 'true' : 'false';
|
|
510
506
|
}
|
|
511
|
-
const
|
|
512
|
-
Object.entries(parameters).every(([id, value]) => String(variant.parameters?.[id] ?? '') === String(value))
|
|
513
|
-
);
|
|
507
|
+
const matches = (variant) =>
|
|
508
|
+
Object.entries(parameters).every(([id, value]) => String(variant.parameters?.[id] ?? '') === String(value));
|
|
509
|
+
let selected = availableVariants.find(matches);
|
|
510
|
+
// Keep a supported explicit context. A persisted context can exist in this
|
|
511
|
+
// mode but conflict with its requested effort/fast/other options. In that
|
|
512
|
+
// case try only the mode's default context, preserving every other choice
|
|
513
|
+
// and still rejecting the request unless the full combination is offered.
|
|
514
|
+
if (!selected && Object.hasOwn(routeParameters, 'context')) {
|
|
515
|
+
if (defaultContext === undefined) delete parameters.context;
|
|
516
|
+
else parameters.context = defaultContext;
|
|
517
|
+
selected = availableVariants.find(matches);
|
|
518
|
+
}
|
|
514
519
|
if (!selected && availableVariants.length) {
|
|
515
520
|
throw new Error(`Cursor model ${group.id} does not offer the selected parameter combination`);
|
|
516
521
|
}
|
|
@@ -649,7 +654,9 @@ class CursorProviderBase {
|
|
|
649
654
|
let emittedToolCall = false;
|
|
650
655
|
try {
|
|
651
656
|
sendOpts.onStageChange?.('requesting');
|
|
652
|
-
} catch {
|
|
657
|
+
} catch {
|
|
658
|
+
// the stage observer is advisory
|
|
659
|
+
}
|
|
653
660
|
try {
|
|
654
661
|
const response = await runtime.handleChatCompletion(body, accessToken, {
|
|
655
662
|
onStageChange: sendOpts.onStageChange,
|
|
@@ -664,7 +671,9 @@ class CursorProviderBase {
|
|
|
664
671
|
});
|
|
665
672
|
try {
|
|
666
673
|
sendOpts.onStageChange?.('streaming');
|
|
667
|
-
} catch {
|
|
674
|
+
} catch {
|
|
675
|
+
// the stage observer is advisory
|
|
676
|
+
}
|
|
668
677
|
return await consumeCompatChatCompletionStream(responseSseEvents(response, signal), {
|
|
669
678
|
signal,
|
|
670
679
|
label: this.name,
|
|
@@ -67,10 +67,10 @@ export function sameAccountIdentity(a, b) {
|
|
|
67
67
|
return Boolean(left.email && right.email && left.email.toLowerCase() === right.email.toLowerCase());
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
// expires_at may arrive as a unix number (seconds or milliseconds)
|
|
71
|
-
// ISO-8601 string. Normalize to epoch milliseconds; 0 means unknown.
|
|
70
|
+
// expires_at may arrive as a unix number (seconds or milliseconds), a numeric
|
|
71
|
+
// string, or an ISO-8601 string. Normalize to epoch milliseconds; 0 means unknown.
|
|
72
72
|
export function normalizeExpiresAtMs(value) {
|
|
73
|
-
if (typeof value === 'string') {
|
|
73
|
+
if (typeof value === 'string' && !Number.isFinite(Number(value.trim() || NaN))) {
|
|
74
74
|
const ms = Date.parse(value);
|
|
75
75
|
return Number.isFinite(ms) ? ms : 0;
|
|
76
76
|
}
|
|
@@ -79,6 +79,20 @@ export function normalizeExpiresAtMs(value) {
|
|
|
79
79
|
return n < 1e12 ? n * 1000 : n;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Whether a stored `{ access_token, refresh_token?, expires_at }` document is a
|
|
84
|
+
* sign-in: it can refresh, or its access token is known to be unexpired. A copy
|
|
85
|
+
* made without a refresh token (an isolated dev instance, so it cannot rotate
|
|
86
|
+
* the original's) coasts on the access token until then; one with no known
|
|
87
|
+
* expiry and no refresh token is incomplete, not a sign-in.
|
|
88
|
+
*/
|
|
89
|
+
export function isSignedInTokenDocument(raw) {
|
|
90
|
+
if (!raw?.access_token) return false;
|
|
91
|
+
if (raw.refresh_token) return true;
|
|
92
|
+
const expiresAt = normalizeExpiresAtMs(raw.expires_at ?? raw.expiresAt) || expiryFromAccessToken(raw.access_token);
|
|
93
|
+
return expiresAt > Date.now();
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
/**
|
|
83
97
|
* Status record for a stored OAuth credential that has an access token.
|
|
84
98
|
* `expiresAt` is epoch ms (0 = unknown); `detail` labels the credential source.
|
|
@@ -274,7 +274,7 @@ function* walkContentParts(content) {
|
|
|
274
274
|
* beforehand by preparePdfTextForProvider, so one reaching here is a bug.
|
|
275
275
|
* Null when the part is not a file at all.
|
|
276
276
|
*/
|
|
277
|
-
|
|
277
|
+
function textFormOfFilePart(part) {
|
|
278
278
|
if (part?.type !== 'file' && part?.type !== 'document') return null;
|
|
279
279
|
try {
|
|
280
280
|
const file = fileInfo(part);
|
|
@@ -840,6 +840,56 @@ function partCarriesLiveMedia(part) {
|
|
|
840
840
|
return Array.isArray(part.content) && contentCarriesLiveMedia(part.content);
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
+
/** An image part in any spelling the providers use: inline, URL, file id or
|
|
844
|
+
* URI, Gemini inline data. */
|
|
845
|
+
function isImagePart(part) {
|
|
846
|
+
return Boolean(
|
|
847
|
+
part.type === 'image' ||
|
|
848
|
+
part.type === 'image_url' ||
|
|
849
|
+
part.type === 'input_image' ||
|
|
850
|
+
imageUrlFromPart(part) ||
|
|
851
|
+
imageFileIdFromPart(part) ||
|
|
852
|
+
imageFileUriFromPart(part) ||
|
|
853
|
+
geminiInlineInfo(part)
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function mapPartText(part, mapText) {
|
|
858
|
+
if (typeof part === 'string') return mapText(part);
|
|
859
|
+
if (!part || typeof part !== 'object') return part;
|
|
860
|
+
// Media and attachment references are encodings, never text.
|
|
861
|
+
if (isAttachmentReference(part) || isImagePart(part) || fileInfo(part)) return part;
|
|
862
|
+
for (const key of ['text', 'content']) {
|
|
863
|
+
if (typeof part[key] === 'string') {
|
|
864
|
+
const next = mapText(part[key]);
|
|
865
|
+
return next === part[key] ? part : { ...part, [key]: next };
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (Array.isArray(part.content)) {
|
|
869
|
+
const nextContent = mapContentText(part.content, mapText);
|
|
870
|
+
if (nextContent !== part.content) return { ...part, content: nextContent };
|
|
871
|
+
}
|
|
872
|
+
return part;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/** `content` with `mapText` applied to its text and nothing else: a string,
|
|
876
|
+
* the text a text part or a part's string content carries (textFromPart),
|
|
877
|
+
* and nested part content; media parts and attachment references stay as
|
|
878
|
+
* they are. The same value when nothing changed. */
|
|
879
|
+
export function mapContentText(content, mapText) {
|
|
880
|
+
if (typeof content === 'string') return mapText(content);
|
|
881
|
+
const parts = contentParts(content);
|
|
882
|
+
if (!parts) return content;
|
|
883
|
+
let changed = false;
|
|
884
|
+
const out = parts.map((part) => {
|
|
885
|
+
const next = mapPartText(part, mapText);
|
|
886
|
+
if (next !== part) changed = true;
|
|
887
|
+
return next;
|
|
888
|
+
});
|
|
889
|
+
if (!changed) return content;
|
|
890
|
+
return Array.isArray(content) ? out : { ...content, content: out };
|
|
891
|
+
}
|
|
892
|
+
|
|
843
893
|
function sanitizePartForStoredHistory(part) {
|
|
844
894
|
if (typeof part === 'string') return part;
|
|
845
895
|
if (!part || typeof part !== 'object') return part;
|
|
@@ -849,15 +899,7 @@ function sanitizePartForStoredHistory(part) {
|
|
|
849
899
|
// reloading the session keeps the provider prompt cache intact.
|
|
850
900
|
const storedImage = part.type === 'image' ? storedImagePart(part) : null;
|
|
851
901
|
if (storedImage) return storedImage;
|
|
852
|
-
if (
|
|
853
|
-
part.type === 'image' ||
|
|
854
|
-
part.type === 'image_url' ||
|
|
855
|
-
part.type === 'input_image' ||
|
|
856
|
-
imageUrlFromPart(part) ||
|
|
857
|
-
imageFileIdFromPart(part) ||
|
|
858
|
-
imageFileUriFromPart(part) ||
|
|
859
|
-
geminiInlineInfo(part)
|
|
860
|
-
) {
|
|
902
|
+
if (isImagePart(part)) {
|
|
861
903
|
return { type: 'text', text: storedHistoryImagePlaceholder(part) };
|
|
862
904
|
}
|
|
863
905
|
const file = fileInfo(part);
|
|
@@ -5,7 +5,14 @@ export const MODEL_PROXY_OPERATIONS: Readonly<{
|
|
|
5
5
|
resolve: 'modelProxyResolve';
|
|
6
6
|
heartbeat: 'modelProxyHeartbeat';
|
|
7
7
|
}>;
|
|
8
|
-
export type ModelProxyMethod =
|
|
8
|
+
export type ModelProxyMethod =
|
|
9
|
+
| 'send'
|
|
10
|
+
| 'listModels'
|
|
11
|
+
| 'refreshModels'
|
|
12
|
+
| 'isAvailable'
|
|
13
|
+
| 'quotaUsable'
|
|
14
|
+
| 'resolveWireRoute'
|
|
15
|
+
| 'providerConfig';
|
|
9
16
|
export const MODEL_PROXY_METHODS: ReadonlyArray<ModelProxyMethod>;
|
|
10
17
|
export const MODEL_PROXY_HEARTBEAT_MS: number;
|
|
11
18
|
export const MODEL_PROXY_LEASE_MS: number;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// so a new process (or an offline one) starts from the newest version this
|
|
9
9
|
// machine has seen rather than from a floor that only moves with a release.
|
|
10
10
|
|
|
11
|
+
import { runAbortable } from '../../../shared/abort-race.mjs';
|
|
11
12
|
import { readLastKnownVersion, rememberLastKnownVersion } from './client-version-store.mjs';
|
|
12
13
|
|
|
13
14
|
const SEMVER = /^\d+\.\d+\.\d+$/;
|
|
@@ -41,11 +42,18 @@ export async function parseNpmLatest(res) {
|
|
|
41
42
|
return SEMVER.test(v) ? v : null;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
/** One GET of `url` parsed to a version; null when offline, slow, or unrecognised.
|
|
45
|
+
/** One GET of `url` parsed to a version; null when offline, slow, or unrecognised.
|
|
46
|
+
* The deadline settles the lookup itself instead of relying on the request
|
|
47
|
+
* and its body read to honour the abort: a send awaits this lookup, and its
|
|
48
|
+
* timer is unref'd, so once the abort tears the socket down nothing else is
|
|
49
|
+
* left to wake a read that missed it. */
|
|
45
50
|
export async function fetchRemoteVersion(url, parse, timeoutMs = TIMEOUT_MS) {
|
|
51
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
46
52
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
return await runAbortable(signal, async () => {
|
|
54
|
+
const res = await fetch(url, { signal });
|
|
55
|
+
return res.ok ? (await parse(res)) || null : null;
|
|
56
|
+
});
|
|
49
57
|
} catch {
|
|
50
58
|
return null;
|
|
51
59
|
}
|
|
@@ -5,6 +5,7 @@ import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
|
5
5
|
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
6
6
|
import { cursorTokenExpiry } from './cursor-auth.mjs';
|
|
7
7
|
import { describeDevinOAuthCredentials } from './devin-auth.mjs';
|
|
8
|
+
import { isSignedInTokenDocument } from './lib/oauth-token-utils.mjs';
|
|
8
9
|
import { isValidMuseCredentialDocument } from './muse-code-oauth-tokens.mjs';
|
|
9
10
|
import { isValidOpenCodeGoCredentialDocument } from './opencode-go-oauth-tokens.mjs';
|
|
10
11
|
import { developerOptionEnabled, developerOptionForProvider } from '../../../shared/developer-options.mjs';
|
|
@@ -152,7 +153,7 @@ function openAIOAuthState() {
|
|
|
152
153
|
join(resolvePluginData(), 'openai-oauth.json'),
|
|
153
154
|
];
|
|
154
155
|
return memoProbe('openai-oauth', paths, () =>
|
|
155
|
-
resolveProbeState(paths, (docs) => docs.some((raw) =>
|
|
156
|
+
resolveProbeState(paths, (docs) => docs.some((raw) => isSignedInTokenDocument(raw)))
|
|
156
157
|
);
|
|
157
158
|
}
|
|
158
159
|
|
|
@@ -18,12 +18,15 @@ import { getPluginData } from '../config.mjs';
|
|
|
18
18
|
import { writeJsonAtomicSync, withFileLock } from '../../../shared/atomic-file.mjs';
|
|
19
19
|
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
20
20
|
import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
|
|
21
|
+
import { TIMEOUT_PHASE } from '../../../shared/provider-timeout-phase.mjs';
|
|
21
22
|
import { modelProxyClientMode, serverModelCredentialsDisabledError } from '../../../shared/model-proxy-mode.mjs';
|
|
22
23
|
import {
|
|
23
24
|
accountIdentityFields,
|
|
24
25
|
decodeJwtPayload,
|
|
25
26
|
expiryFromAccessToken,
|
|
27
|
+
isSignedInTokenDocument,
|
|
26
28
|
normalizeAccountIdentity,
|
|
29
|
+
normalizeExpiresAtMs,
|
|
27
30
|
oauthCredentialStatus,
|
|
28
31
|
} from './lib/oauth-token-utils.mjs';
|
|
29
32
|
import { createOpenAIOAuthLogin } from './openai-oauth-login.mjs';
|
|
@@ -60,11 +63,8 @@ function getRefreshLockPath() {
|
|
|
60
63
|
return `${getOwnTokenPath()}.refresh.lock`;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!Number.isFinite(n) || n <= 0) return 0;
|
|
66
|
-
return n < 1e12 ? n * 1000 : n;
|
|
67
|
-
}
|
|
66
|
+
// One normalizer for the store and the sign-in predicate (isSignedInTokenDocument).
|
|
67
|
+
const _normalizeExpiresAt = normalizeExpiresAtMs;
|
|
68
68
|
|
|
69
69
|
export function extractAccountId(token) {
|
|
70
70
|
return decodeJwtPayload(token)?.['https://api.openai.com/auth']?.chatgpt_account_id;
|
|
@@ -117,7 +117,7 @@ export function loadTokens() {
|
|
|
117
117
|
try {
|
|
118
118
|
const stat = statSync(ownPath);
|
|
119
119
|
const own = JSON.parse(readFileSync(ownPath, 'utf-8'));
|
|
120
|
-
if (own
|
|
120
|
+
if (isSignedInTokenDocument(own)) {
|
|
121
121
|
const identity = identityFromTokens(own.id_token, own.access_token);
|
|
122
122
|
return {
|
|
123
123
|
...own,
|
|
@@ -144,8 +144,7 @@ export function saveTokens(tokens) {
|
|
|
144
144
|
// same loader the runtime uses (loadTokens), no parallel hard-coded path probe.
|
|
145
145
|
export function hasOpenAIOAuthCredentials() {
|
|
146
146
|
try {
|
|
147
|
-
|
|
148
|
-
return !!(tokens?.access_token && tokens?.refresh_token);
|
|
147
|
+
return !!loadTokens()?.access_token;
|
|
149
148
|
} catch {
|
|
150
149
|
return false;
|
|
151
150
|
}
|
|
@@ -256,7 +255,9 @@ async function exchangeRefreshToken(refreshToken, previousIdentity = null) {
|
|
|
256
255
|
return tokens;
|
|
257
256
|
} catch (err) {
|
|
258
257
|
if (err?.name === 'AbortError')
|
|
259
|
-
throw new Error(`OpenAI OAuth token refresh timed out after ${TOKEN_REFRESH_TIMEOUT_MS}ms`)
|
|
258
|
+
throw Object.assign(new Error(`OpenAI OAuth token refresh timed out after ${TOKEN_REFRESH_TIMEOUT_MS}ms`), {
|
|
259
|
+
timeoutPhase: TIMEOUT_PHASE.AUTH,
|
|
260
|
+
});
|
|
260
261
|
throw err;
|
|
261
262
|
} finally {
|
|
262
263
|
clearTimeout(timeout);
|
|
@@ -7,13 +7,17 @@ import { createStreamSafetyStamps, resolveStallRetryBudget } from '../retry-clas
|
|
|
7
7
|
import { _codexWsCompatibilityHeaders } from '../openai-codex-metadata.mjs';
|
|
8
8
|
import { createWsSendAttempts } from '../openai-ws-send-attempts.mjs';
|
|
9
9
|
import { createWsSendSpan } from '../openai-ws-send-span.mjs';
|
|
10
|
+
import { emitProviderRetryStage } from '../retry-executor.mjs';
|
|
11
|
+
import { retryReasonClass } from '../provider-error-kind.mjs';
|
|
10
12
|
|
|
11
13
|
/** Stage callbacks are observers; a throwing one never fails the send. */
|
|
12
14
|
export function notifyStage(onStageChange, stage, detail) {
|
|
13
15
|
try {
|
|
14
16
|
if (detail === undefined) onStageChange?.(stage);
|
|
15
17
|
else onStageChange?.(stage, detail);
|
|
16
|
-
} catch {
|
|
18
|
+
} catch {
|
|
19
|
+
// the stage observer is advisory
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export function createWsSendContext(opts) {
|
|
@@ -81,10 +85,24 @@ export function createWsSendContext(opts) {
|
|
|
81
85
|
// Single caller-visible recovery path for both handshake/acquire retries
|
|
82
86
|
// and retryable stream failures. The session/TUI stage bridge renders this
|
|
83
87
|
// as non-terminal reconnect progress; transport code must not also print it
|
|
84
|
-
// to stderr.
|
|
85
|
-
|
|
88
|
+
// to stderr. A retry that knows its failure carries the reason and the wait
|
|
89
|
+
// (retryAt), so a long server-advised wait counts down with its cause
|
|
90
|
+
// instead of sitting silently at "n/max".
|
|
91
|
+
const emitReconnectProgress = ({ attempt, max, classifier, error = null, waitMs = 0 }) => {
|
|
86
92
|
const retryAttempt = Number(attempt) || 1;
|
|
87
93
|
const retryMax = Number(max) || 1;
|
|
94
|
+
if (error) {
|
|
95
|
+
// The reason shown is the fixed class, never the server's own text.
|
|
96
|
+
emitProviderRetryStage(onStageChange, {
|
|
97
|
+
attempt: retryAttempt,
|
|
98
|
+
maxAttempts: retryMax,
|
|
99
|
+
lastErr: error,
|
|
100
|
+
delayMs: waitMs,
|
|
101
|
+
classifier,
|
|
102
|
+
reasonClass: retryReasonClass(error),
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
88
106
|
notifyStage(onStageChange, 'reconnecting', {
|
|
89
107
|
attempt: retryAttempt,
|
|
90
108
|
max: retryMax,
|
|
@@ -105,6 +123,7 @@ export function createWsSendContext(opts) {
|
|
|
105
123
|
retry429,
|
|
106
124
|
stallRetryBudget,
|
|
107
125
|
trace: { poolKey, traceProvider, useModel },
|
|
126
|
+
traceFn: _agentTraceFn,
|
|
108
127
|
auth,
|
|
109
128
|
body,
|
|
110
129
|
});
|
package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs
CHANGED
|
@@ -3,7 +3,34 @@
|
|
|
3
3
|
* WS send, plus the surface/backoff primitives both failure resolvers use.
|
|
4
4
|
*/
|
|
5
5
|
import { performance } from 'node:perf_hooks';
|
|
6
|
+
import { appendAgentTrace } from '../../agent-trace.mjs';
|
|
6
7
|
import { _sleepWithAbort, MIDSTREAM_WS_TRANSIENT_RETRY_LIMIT, midstreamBackoffFor, tag } from './policy.mjs';
|
|
8
|
+
import { retryReasonClass, safeRetryErrorCode } from '../provider-error-kind.mjs';
|
|
9
|
+
|
|
10
|
+
/** One row per WS retry: the server's typed verdict (status, close code, an
|
|
11
|
+
* identifier-shaped code, the reason class) and the wait it bought, so a
|
|
12
|
+
* long advised wait is diagnosable after the fact. Never the server's free
|
|
13
|
+
* text: it can echo request content. Diagnostics only; never throws. */
|
|
14
|
+
function traceRetry(traceFn, trace, err, { attempt, max, classifier, waitMs }) {
|
|
15
|
+
try {
|
|
16
|
+
const payload = {
|
|
17
|
+
provider: trace?.traceProvider || null,
|
|
18
|
+
model: trace?.useModel || null,
|
|
19
|
+
transport: 'websocket',
|
|
20
|
+
attempt,
|
|
21
|
+
max,
|
|
22
|
+
classifier: classifier || null,
|
|
23
|
+
reason: retryReasonClass(err),
|
|
24
|
+
wait_ms: Math.round(Number(waitMs) || 0),
|
|
25
|
+
http_status: Number(err?.httpStatus || err?.status || 0) || null,
|
|
26
|
+
code: safeRetryErrorCode(err),
|
|
27
|
+
ws_close_code: Number(err?.wsCloseCode || 0) || null,
|
|
28
|
+
};
|
|
29
|
+
traceFn({ sessionId: trace?.poolKey || null, kind: 'ws_retry', ...payload, payload });
|
|
30
|
+
} catch {
|
|
31
|
+
// the retry trace sink is advisory
|
|
32
|
+
}
|
|
33
|
+
}
|
|
7
34
|
|
|
8
35
|
export function createAttemptContext(deps) {
|
|
9
36
|
const { sendSpan, externalSignal, sleepFn, safetyStamps } = deps;
|
|
@@ -31,10 +58,13 @@ export function createAttemptContext(deps) {
|
|
|
31
58
|
};
|
|
32
59
|
// Server advice (Retry-After, or a typed rate limit's "try again in")
|
|
33
60
|
// replaces the local curve, like the reference client's retry deadline.
|
|
34
|
-
|
|
61
|
+
// Resolved before the retry is announced so the reconnect stage can name
|
|
62
|
+
// the wait it is about to sit through.
|
|
63
|
+
const retryWaitMs = (retryNumber, err = null) => midstreamBackoffFor(retryNumber, err);
|
|
64
|
+
const backoff = async (waitMs) => {
|
|
35
65
|
const sleepStart = performance.now();
|
|
36
66
|
try {
|
|
37
|
-
await _sleepWithAbort(
|
|
67
|
+
await _sleepWithAbort(waitMs, externalSignal, sleepFn);
|
|
38
68
|
} catch (sleepErr) {
|
|
39
69
|
sendSpan.retryBackoffMs += performance.now() - sleepStart;
|
|
40
70
|
sendSpan.emit('error');
|
|
@@ -53,14 +83,21 @@ export function createAttemptContext(deps) {
|
|
|
53
83
|
}
|
|
54
84
|
return first;
|
|
55
85
|
};
|
|
56
|
-
/** Stash the error a retry is about to leave behind and announce the retry
|
|
57
|
-
|
|
86
|
+
/** Stash the error a retry is about to leave behind and announce the retry
|
|
87
|
+
* with its cause and the wait before the next attempt. */
|
|
88
|
+
const scheduleRetry = (err, { attemptIndex, classifier, retryLimit, remember, waitMs = 0 }) => {
|
|
58
89
|
if (remember) {
|
|
59
90
|
state.firstAttemptError = err;
|
|
60
91
|
state.firstAttemptClassifier = classifier;
|
|
61
92
|
}
|
|
62
93
|
tag(err, { midstreamClassifier: classifier });
|
|
63
|
-
deps.emitReconnectProgress({ attempt: attemptIndex + 1, max: retryLimit, classifier });
|
|
94
|
+
deps.emitReconnectProgress({ attempt: attemptIndex + 1, max: retryLimit, classifier, error: err, waitMs });
|
|
95
|
+
traceRetry(deps.traceFn || appendAgentTrace, deps.trace, err, {
|
|
96
|
+
attempt: attemptIndex + 1,
|
|
97
|
+
max: retryLimit,
|
|
98
|
+
classifier,
|
|
99
|
+
waitMs,
|
|
100
|
+
});
|
|
64
101
|
};
|
|
65
102
|
|
|
66
103
|
return {
|
|
@@ -69,6 +106,7 @@ export function createAttemptContext(deps) {
|
|
|
69
106
|
maxMidstreamRetries: MIDSTREAM_WS_TRANSIENT_RETRY_LIMIT,
|
|
70
107
|
stampAll,
|
|
71
108
|
surface,
|
|
109
|
+
retryWaitMs,
|
|
72
110
|
backoff,
|
|
73
111
|
surfaceFirstAttempt,
|
|
74
112
|
scheduleRetry,
|
package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs
CHANGED
|
@@ -81,13 +81,15 @@ export async function resolveHandshakeFailure(
|
|
|
81
81
|
const retryable =
|
|
82
82
|
classifier && status !== 401 && status !== 426 && err?.unsafeToRetry !== true && !externalSignal?.aborted;
|
|
83
83
|
if (retryable && attemptIndex < ctx.maxMidstreamRetries) {
|
|
84
|
+
const waitMs = ctx.retryWaitMs(attemptIndex + 1, err);
|
|
84
85
|
ctx.scheduleRetry(err, {
|
|
85
86
|
attemptIndex,
|
|
86
87
|
classifier,
|
|
87
88
|
retryLimit: ctx.maxMidstreamRetries,
|
|
88
89
|
remember: !state.firstAttemptError,
|
|
90
|
+
waitMs,
|
|
89
91
|
});
|
|
90
|
-
await ctx.backoff(
|
|
92
|
+
await ctx.backoff(waitMs);
|
|
91
93
|
return true;
|
|
92
94
|
}
|
|
93
95
|
// A later auth/upgrade decision must win over an earlier transient
|
|
@@ -77,7 +77,9 @@ export async function resolveStreamFailure(ctx, err, { attemptIndex, entry, midS
|
|
|
77
77
|
process.stderr.write(
|
|
78
78
|
`[openai-oauth] stall retry budget exhausted (${STREAM_STALL_RETRY_BUDGET_MS}ms since first stall) — surfacing provider-terminal failure\n`
|
|
79
79
|
);
|
|
80
|
-
} catch {
|
|
80
|
+
} catch {
|
|
81
|
+
// diagnostic write only; the provider-terminal failure is thrown next
|
|
82
|
+
}
|
|
81
83
|
throw ctx.surface(markProviderRecoveryExhausted(err, { owner: 'openai-oauth-ws-stall-budget' }));
|
|
82
84
|
}
|
|
83
85
|
const retryLimit = classifier ? midstreamRetryLimit(classifier) : 0;
|
|
@@ -89,8 +91,9 @@ export async function resolveStreamFailure(ctx, err, { attemptIndex, entry, midS
|
|
|
89
91
|
state.carryForwardCache = null;
|
|
90
92
|
tag(entry, { lastResponseId: null });
|
|
91
93
|
}
|
|
92
|
-
ctx.
|
|
93
|
-
|
|
94
|
+
const waitMs = ctx.retryWaitMs(attemptIndex + 1, err);
|
|
95
|
+
ctx.scheduleRetry(err, { attemptIndex, classifier, retryLimit, remember: true, waitMs });
|
|
96
|
+
await ctx.backoff(waitMs);
|
|
94
97
|
return true;
|
|
95
98
|
}
|
|
96
99
|
// Not retryable, OR we've already exhausted the retry budget. Do not
|