mixdog 1.3.5 → 1.4.1
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/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- 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 +35 -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 +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- 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/cache-break-trace.mjs +2 -0
- 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 +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- 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/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- 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-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- 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/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- 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/mimo-oauth-login.mjs +1 -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-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- 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/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- 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 +38 -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 +49 -16
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- 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/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- 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/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- 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 +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -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/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- 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 +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
- 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-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
- 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 +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -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-delete.mjs +7 -0
- 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 +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -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 +79 -3
- 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/stall-policy.mjs +23 -0
- 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/invocation-plan.mjs +2 -2
- 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/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- 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/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- 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 +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -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/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- 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 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- 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/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- 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 +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -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 +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- 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-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -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 +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- 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 +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.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/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- 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/model-route/set-route.mjs +11 -2
- 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/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- 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/provider-admin.mjs +2 -1
- 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 +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- 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 +142 -190
- 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/agent-tree/agent-turns.mjs +16 -12
- 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 +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- 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 +16 -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/ui/statusline-agents.mjs +1 -3
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* openai-http-sse-watchdogs.mjs — liveness gates of one HTTP/SSE fallback
|
|
3
3
|
* stream: the total/external abort relay, the fixed first-server-event
|
|
4
|
-
* deadline, the SEMANTIC idle watchdog, and the
|
|
4
|
+
* deadline, the first-OUTPUT window, the SEMANTIC idle watchdog, and the
|
|
5
|
+
* cancellable reader.read().
|
|
6
|
+
*
|
|
7
|
+
* Until the model's first semantic output (reasoning, text, a tool call) only
|
|
8
|
+
* the first-server-event deadline and the first-output window run: server
|
|
9
|
+
* metadata (response.created / in_progress, an empty reasoning or message
|
|
10
|
+
* item) and keepalives neither end that phase nor start the semantic-idle
|
|
11
|
+
* clock, so a model may think silently longer than the idle window. From the
|
|
12
|
+
* first semantic output on, the semantic idle owns the stream.
|
|
5
13
|
*
|
|
6
14
|
* After headerTimeout.cleanup() the in-flight fetch no longer carries a live
|
|
7
15
|
* signal, so a totalTimeout / external abort that fires during a pending
|
|
@@ -17,8 +25,10 @@ import {
|
|
|
17
25
|
PROVIDER_FIRST_BYTE_TIMEOUT_MS,
|
|
18
26
|
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
19
27
|
PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
|
|
28
|
+
providerFirstOutputTimeoutMs,
|
|
20
29
|
streamStalledError,
|
|
21
30
|
} from '../stall-policy.mjs';
|
|
31
|
+
import { streamFirstOutputWindow } from './lib/first-output-window.mjs';
|
|
22
32
|
|
|
23
33
|
function positiveOverride(value) {
|
|
24
34
|
const ms = Number(value);
|
|
@@ -31,8 +41,18 @@ function positiveOverride(value) {
|
|
|
31
41
|
* @param {ReadableStreamDefaultReader} deps.reader
|
|
32
42
|
* @param {{ signal?: AbortSignal, cleanup(): void }} deps.totalTimeout
|
|
33
43
|
* @param {() => object} deps.stallPartial streamed partial state attached to a semantic-idle stall
|
|
44
|
+
* @param {string|null} [deps.sessionId] the session whose logical request this stream serves
|
|
45
|
+
* @param {Function} [deps.onStreamDelta] the stream's progress callback; without it the stream is a fork of the request
|
|
34
46
|
*/
|
|
35
|
-
export function createHttpSseWatchdogs({
|
|
47
|
+
export function createHttpSseWatchdogs({
|
|
48
|
+
opts,
|
|
49
|
+
reader,
|
|
50
|
+
totalTimeout,
|
|
51
|
+
stallPartial,
|
|
52
|
+
label: LABEL,
|
|
53
|
+
sessionId = null,
|
|
54
|
+
onStreamDelta,
|
|
55
|
+
}) {
|
|
36
56
|
let abortReason = null;
|
|
37
57
|
let pendingReadReject = null;
|
|
38
58
|
const rejectPendingRead = (err) => {
|
|
@@ -58,9 +78,46 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
58
78
|
else totalTimeout.signal.addEventListener('abort', onTotalAbort, { once: true });
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
81
|
+
// SEMANTIC idle watchdog: reset ONLY on meaningful() (text/reasoning/tool
|
|
82
|
+
// deltas), never on raw bytes/keepalive frames, so a stream that emits some
|
|
83
|
+
// deltas then goes silent trips a short, named terminal failure instead of
|
|
84
|
+
// hanging until the 30-min agent watchdog. Disablable via the shared env.
|
|
85
|
+
let semanticIdleTimer = null;
|
|
86
|
+
const semanticIdleOverrideMs = positiveOverride(opts?._semanticIdleTimeoutMs);
|
|
87
|
+
const semanticIdleMs = semanticIdleOverrideMs || PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
88
|
+
const semanticIdleEnabled = semanticIdleOverrideMs > 0 || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
89
|
+
// FIRST-OUTPUT window of the logical request this stream serves, retired by
|
|
90
|
+
// the first semantic output. Metadata-only streams (repeated
|
|
91
|
+
// response.in_progress, keepalive/ping frames, unknown event types) cannot
|
|
92
|
+
// refresh it, so a wedge that never produces a token still ends here.
|
|
93
|
+
const firstOutputOverrideMs = positiveOverride(opts?._firstOutputTimeoutMs);
|
|
94
|
+
const firstOutput = streamFirstOutputWindow({
|
|
95
|
+
sessionId,
|
|
96
|
+
onStreamDelta,
|
|
97
|
+
timeoutMs: firstOutputOverrideMs || providerFirstOutputTimeoutMs(semanticIdleMs),
|
|
98
|
+
});
|
|
99
|
+
const firstOutputEnabled = firstOutputOverrideMs > 0 || semanticIdleEnabled;
|
|
100
|
+
|
|
101
|
+
// A stalled stream fails with the named stall error, carrying the streamed
|
|
102
|
+
// partial state so the agent loop can accept a wedged FINAL no-tool summary
|
|
103
|
+
// as a successful partial-final instead of dropping the result.
|
|
104
|
+
// pendingToolUse gates out any mid-flight tool call.
|
|
105
|
+
const abortStalled = (timeoutMs) => {
|
|
106
|
+
const partial = stallPartial();
|
|
107
|
+
const err = streamStalledError(LABEL, timeoutMs, { emittedToolCall: partial.emittedToolCall });
|
|
108
|
+
try {
|
|
109
|
+
err.partialContent = partial.content;
|
|
110
|
+
err.partialToolCalls = partial.toolCalls;
|
|
111
|
+
err.pendingToolUse = partial.pendingToolUse;
|
|
112
|
+
err.partialModel = partial.model;
|
|
113
|
+
} catch {
|
|
114
|
+
/* best-effort enrichment */
|
|
115
|
+
}
|
|
116
|
+
abortStream(err);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Initial wait for any server event is governed by the fixed
|
|
120
|
+
// first-server-event policy; a lower semantic-idle override cannot shorten it.
|
|
64
121
|
let firstServerEventTimer = null;
|
|
65
122
|
const firstServerEventMs = positiveOverride(opts?._firstServerEventTimeoutMs) || PROVIDER_FIRST_BYTE_TIMEOUT_MS;
|
|
66
123
|
const clearFirstServerEvent = () => {
|
|
@@ -82,15 +139,15 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
82
139
|
firstServerEventTimer.unref?.();
|
|
83
140
|
} catch {}
|
|
84
141
|
};
|
|
142
|
+
// Until the stream's first output the request's first-output window bounds
|
|
143
|
+
// it. A request that already had output has none: its stream runs under the
|
|
144
|
+
// semantic idle from its first server event (noteServerEvent).
|
|
145
|
+
const armFirstOutput = () => {
|
|
146
|
+
if (!firstOutput.outputSeen && firstOutputEnabled && firstOutput.timeoutMs > 0) {
|
|
147
|
+
firstOutput.arm(() => abortStalled(firstOutput.timeoutMs));
|
|
148
|
+
}
|
|
149
|
+
};
|
|
85
150
|
|
|
86
|
-
// SEMANTIC idle watchdog: reset ONLY on meaningful() (text/reasoning/tool
|
|
87
|
-
// deltas), never on raw bytes/keepalive frames, so a stream that emits some
|
|
88
|
-
// deltas then goes silent trips a short, named terminal failure instead of
|
|
89
|
-
// hanging until the 30-min agent watchdog. Disablable via the shared env.
|
|
90
|
-
let semanticIdleTimer = null;
|
|
91
|
-
const semanticIdleOverrideMs = positiveOverride(opts?._semanticIdleTimeoutMs);
|
|
92
|
-
const semanticIdleMs = semanticIdleOverrideMs || PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
93
|
-
const semanticIdleEnabled = semanticIdleOverrideMs > 0 || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
94
151
|
const clearSemanticIdle = () => {
|
|
95
152
|
if (semanticIdleTimer) {
|
|
96
153
|
clearTimeout(semanticIdleTimer);
|
|
@@ -100,43 +157,22 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
100
157
|
const armSemanticIdle = () => {
|
|
101
158
|
if (!semanticIdleEnabled || !(semanticIdleMs > 0)) return;
|
|
102
159
|
clearSemanticIdle();
|
|
103
|
-
semanticIdleTimer = setTimeout(() =>
|
|
104
|
-
const partial = stallPartial();
|
|
105
|
-
const err = streamStalledError(LABEL, semanticIdleMs, { emittedToolCall: partial.emittedToolCall });
|
|
106
|
-
// Partial-final recovery: attach the streamed partial state so the
|
|
107
|
-
// agent loop can accept a wedged FINAL no-tool summary as a successful
|
|
108
|
-
// partial-final instead of dropping the result. pendingToolUse gates
|
|
109
|
-
// out any mid-flight tool call.
|
|
110
|
-
try {
|
|
111
|
-
err.partialContent = partial.content;
|
|
112
|
-
err.partialToolCalls = partial.toolCalls;
|
|
113
|
-
err.pendingToolUse = partial.pendingToolUse;
|
|
114
|
-
err.partialModel = partial.model;
|
|
115
|
-
} catch {
|
|
116
|
-
/* best-effort enrichment */
|
|
117
|
-
}
|
|
118
|
-
abortStream(err);
|
|
119
|
-
}, semanticIdleMs);
|
|
160
|
+
semanticIdleTimer = setTimeout(() => abortStalled(semanticIdleMs), semanticIdleMs);
|
|
120
161
|
try {
|
|
121
162
|
semanticIdleTimer.unref?.();
|
|
122
163
|
} catch {}
|
|
123
164
|
};
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
// let a metadata-only stream (repeated response.in_progress, keepalive/ping
|
|
128
|
-
// frames, unknown event types) refresh the watchdog forever without
|
|
129
|
-
// producing a single token. Only meaningful progress re-arms it from then on.
|
|
130
|
-
let semanticIdleArmed = false;
|
|
165
|
+
// A parsed server event satisfies the first-server-event deadline and, for a
|
|
166
|
+
// request that already had output, starts the semantic idle (once: only
|
|
167
|
+
// output re-arms it).
|
|
131
168
|
const noteServerEvent = () => {
|
|
132
169
|
clearFirstServerEvent();
|
|
133
|
-
if (!
|
|
134
|
-
semanticIdleArmed = true;
|
|
135
|
-
armSemanticIdle();
|
|
136
|
-
}
|
|
170
|
+
if (firstOutput.outputSeen && !semanticIdleTimer) armSemanticIdle();
|
|
137
171
|
};
|
|
172
|
+
// Semantic output: the first one ends the first-output phase; every one
|
|
173
|
+
// re-arms the semantic idle.
|
|
138
174
|
const noteMeaningful = () => {
|
|
139
|
-
|
|
175
|
+
firstOutput.noteOutput();
|
|
140
176
|
armSemanticIdle();
|
|
141
177
|
};
|
|
142
178
|
|
|
@@ -151,6 +187,7 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
151
187
|
const dispose = () => {
|
|
152
188
|
pendingReadReject = null;
|
|
153
189
|
clearFirstServerEvent();
|
|
190
|
+
firstOutput.clear();
|
|
154
191
|
clearSemanticIdle();
|
|
155
192
|
try {
|
|
156
193
|
reader.releaseLock?.();
|
|
@@ -165,9 +202,11 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
165
202
|
return {
|
|
166
203
|
abortReason: () => abortReason,
|
|
167
204
|
armFirstServerEvent,
|
|
205
|
+
armFirstOutput,
|
|
168
206
|
clearSemanticIdle,
|
|
169
207
|
noteServerEvent,
|
|
170
208
|
noteMeaningful,
|
|
209
|
+
semanticSeen: () => firstOutput.outputSeen,
|
|
171
210
|
read,
|
|
172
211
|
dispose,
|
|
173
212
|
};
|
|
@@ -183,6 +183,8 @@ export async function sendViaHttpSse({
|
|
|
183
183
|
totalTimeout,
|
|
184
184
|
stallPartial: () => state.stallPartial(),
|
|
185
185
|
label,
|
|
186
|
+
sessionId: opts?.sessionId,
|
|
187
|
+
onStreamDelta,
|
|
186
188
|
});
|
|
187
189
|
const meaningful = (kind = 'semantic') => {
|
|
188
190
|
if (ttftMs == null) ttftMs = Date.now() - sseStartedAt;
|
|
@@ -191,12 +193,17 @@ export async function sendViaHttpSse({
|
|
|
191
193
|
onStreamDelta?.(kind);
|
|
192
194
|
} catch {}
|
|
193
195
|
};
|
|
196
|
+
// An empty reasoning or message item added: progress only after output.
|
|
197
|
+
const lifecycle = (kind = 'semantic') => {
|
|
198
|
+
if (watchdogs.semanticSeen()) meaningful(kind);
|
|
199
|
+
};
|
|
194
200
|
const state = createHttpSseResponseState({
|
|
195
201
|
body,
|
|
196
202
|
onToolCall,
|
|
197
203
|
onTextDelta,
|
|
198
204
|
onStreamItemStart: typeof opts?.onStreamItemStart === 'function' ? opts.onStreamItemStart : null,
|
|
199
205
|
meaningful,
|
|
206
|
+
lifecycle,
|
|
200
207
|
onServerEvent: watchdogs.noteServerEvent,
|
|
201
208
|
label,
|
|
202
209
|
});
|
|
@@ -210,6 +217,7 @@ export async function sendViaHttpSse({
|
|
|
210
217
|
let buffer = '';
|
|
211
218
|
try {
|
|
212
219
|
watchdogs.armFirstServerEvent();
|
|
220
|
+
watchdogs.armFirstOutput();
|
|
213
221
|
while (true) {
|
|
214
222
|
if (totalTimeout.signal?.aborted) {
|
|
215
223
|
watchdogs.clearSemanticIdle();
|
|
@@ -13,8 +13,9 @@ import { randomBytes } from 'node:crypto';
|
|
|
13
13
|
import { createOAuthPkce, parseOAuthCodeInput } from './lib/oauth-pkce.mjs';
|
|
14
14
|
import { OAUTH_PAGE_CONTENT_TYPE, oauthErrorHtml, oauthSuccessHtml } from './lib/oauth-page.mjs';
|
|
15
15
|
import { openLoginUrl } from './lib/open-login-url.mjs';
|
|
16
|
+
import { sleepWithAbort } from './retry-backoff.mjs';
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
const DEFAULT_ISSUER = 'https://auth.openai.com';
|
|
18
19
|
const AUTHORIZE_URL = 'https://auth.openai.com/oauth/authorize';
|
|
19
20
|
const CODEX_OAUTH_SCOPE = 'openid profile email offline_access api.connectors.read api.connectors.invoke';
|
|
20
21
|
const CALLBACK_HOST = '127.0.0.1';
|
|
@@ -23,6 +24,9 @@ const CALLBACK_PATH = '/auth/callback';
|
|
|
23
24
|
const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
24
25
|
const LOGIN_TIMEOUT_MS = 5 * 60_000;
|
|
25
26
|
const TOKEN_TIMEOUT_MS = 30_000;
|
|
27
|
+
// Codex device-code login: the user has 15 minutes to enter the code.
|
|
28
|
+
const DEVICE_LOGIN_TIMEOUT_MS = 15 * 60_000;
|
|
29
|
+
const DEFAULT_DEVICE_INTERVAL_S = 5;
|
|
26
30
|
|
|
27
31
|
function _scrubOAuthLoginBody(text) {
|
|
28
32
|
return String(text || '')
|
|
@@ -40,21 +44,41 @@ function _scrubOAuthLoginBody(text) {
|
|
|
40
44
|
* @param {(...tokens:string[])=>(object|null)} deps.identityFromTokens
|
|
41
45
|
* @param {(token:string)=>number} deps.expiryFromAccessToken
|
|
42
46
|
* @param {(tokens:object)=>void} deps.saveTokens
|
|
47
|
+
* @param {string} [deps.issuer] auth server base URL (tests point it at a fake server)
|
|
48
|
+
* @param {(ms:number, signal?:AbortSignal)=>Promise<void>} [deps.sleep] device-poll wait (tests inject a fast one)
|
|
49
|
+
* @param {()=>number} [deps.now]
|
|
50
|
+
* @param {number} [deps.deviceTimeoutMs] device-code login deadline (tests shorten it)
|
|
51
|
+
* @param {number} [deps.requestTimeoutMs] deadline of the device-code start request (tests shorten it)
|
|
43
52
|
*/
|
|
44
53
|
export function createOpenAIOAuthLogin(deps) {
|
|
45
|
-
const {
|
|
54
|
+
const {
|
|
55
|
+
clientId,
|
|
56
|
+
originator,
|
|
57
|
+
extractAccountId,
|
|
58
|
+
identityFromTokens,
|
|
59
|
+
expiryFromAccessToken,
|
|
60
|
+
saveTokens,
|
|
61
|
+
issuer = DEFAULT_ISSUER,
|
|
62
|
+
sleep = sleepWithAbort,
|
|
63
|
+
now = Date.now,
|
|
64
|
+
deviceTimeoutMs = DEVICE_LOGIN_TIMEOUT_MS,
|
|
65
|
+
requestTimeoutMs = TOKEN_TIMEOUT_MS,
|
|
66
|
+
} = deps;
|
|
67
|
+
const issuerBase = issuer.replace(/\/+$/, '');
|
|
68
|
+
const tokenUrl = `${issuerBase}/oauth/token`;
|
|
69
|
+
const deviceApiBase = `${issuerBase}/api/accounts`;
|
|
46
70
|
|
|
47
|
-
async function exchangeAuthorizationCode({ pkce, code, signal = null }) {
|
|
71
|
+
async function exchangeAuthorizationCode({ pkce, code, redirectUri = REDIRECT_URI, signal = null }) {
|
|
48
72
|
signal?.throwIfAborted();
|
|
49
73
|
const cleanCode = String(code || '').trim();
|
|
50
74
|
if (!cleanCode) throw new Error('[openai-oauth] authorization code is required');
|
|
51
|
-
const tokenRes = await fetch(
|
|
75
|
+
const tokenRes = await fetch(tokenUrl, {
|
|
52
76
|
method: 'POST',
|
|
53
77
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
54
78
|
body: new URLSearchParams({
|
|
55
79
|
grant_type: 'authorization_code',
|
|
56
80
|
code: cleanCode,
|
|
57
|
-
redirect_uri:
|
|
81
|
+
redirect_uri: redirectUri,
|
|
58
82
|
client_id: clientId,
|
|
59
83
|
code_verifier: pkce.verifier,
|
|
60
84
|
}),
|
|
@@ -87,7 +111,114 @@ export function createOpenAIOAuthLogin(deps) {
|
|
|
87
111
|
return tokens;
|
|
88
112
|
}
|
|
89
113
|
|
|
114
|
+
const postJson = (path, body, signal) =>
|
|
115
|
+
fetch(`${deviceApiBase}${path}`, {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: { 'Content-Type': 'application/json' },
|
|
118
|
+
body: JSON.stringify(body),
|
|
119
|
+
redirect: 'error',
|
|
120
|
+
signal,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Codex device-code flow (`codex login --device-auth`): the user enters a
|
|
124
|
+
// short code at the verification URL on any machine; this host polls until
|
|
125
|
+
// the server hands back an authorization code, then exchanges it with the
|
|
126
|
+
// deviceauth redirect URI. Resolves null when the server has no device flow.
|
|
127
|
+
async function beginDeviceLogin() {
|
|
128
|
+
// No login deadline or cancel handle exists yet, so the start request
|
|
129
|
+
// carries its own timeout; it also bounds reading the response body.
|
|
130
|
+
const startSignal = AbortSignal.timeout(requestTimeoutMs);
|
|
131
|
+
const res = await postJson('/deviceauth/usercode', { client_id: clientId }, startSignal);
|
|
132
|
+
if (res.status === 404) return null;
|
|
133
|
+
if (!res.ok) throw new Error(`[openai-oauth] device code request failed with status ${res.status}`);
|
|
134
|
+
const started = await res.json();
|
|
135
|
+
const userCode = started.user_code || started.usercode;
|
|
136
|
+
const deviceAuthId = started.device_auth_id;
|
|
137
|
+
if (!userCode || !deviceAuthId)
|
|
138
|
+
throw new Error('[openai-oauth] device code response missing user_code or device_auth_id');
|
|
139
|
+
const interval = Number(String(started.interval ?? '').trim());
|
|
140
|
+
const intervalMs = (Number.isFinite(interval) && interval > 0 ? interval : DEFAULT_DEVICE_INTERVAL_S) * 1000;
|
|
141
|
+
const url = `${issuerBase}/codex/device`;
|
|
142
|
+
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const { signal } = controller;
|
|
145
|
+
const timedOut = () => new Error('[openai-oauth] device auth timed out after 15 minutes');
|
|
146
|
+
// One poll, aborted at the login deadline so a stalled request cannot
|
|
147
|
+
// outlive it. A reply that arrives in time is honoured whatever the clock
|
|
148
|
+
// says afterwards (the user did authorize).
|
|
149
|
+
const pollOnce = async (left) => {
|
|
150
|
+
const deadlineController = new AbortController();
|
|
151
|
+
const timer = setTimeout(() => deadlineController.abort(), left);
|
|
152
|
+
try {
|
|
153
|
+
const poll = await postJson(
|
|
154
|
+
'/deviceauth/token',
|
|
155
|
+
{ device_auth_id: deviceAuthId, user_code: userCode },
|
|
156
|
+
AbortSignal.any([signal, deadlineController.signal])
|
|
157
|
+
);
|
|
158
|
+
if (poll.ok) return { granted: await poll.json() };
|
|
159
|
+
// 403/404: the user has not approved the code yet.
|
|
160
|
+
if (poll.status !== 403 && poll.status !== 404) {
|
|
161
|
+
throw new Error(`[openai-oauth] device auth failed with status ${poll.status}`);
|
|
162
|
+
}
|
|
163
|
+
return { granted: null };
|
|
164
|
+
} catch (err) {
|
|
165
|
+
if (!signal.aborted && deadlineController.signal.aborted) throw timedOut();
|
|
166
|
+
throw err;
|
|
167
|
+
} finally {
|
|
168
|
+
clearTimeout(timer);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const pollForCode = async () => {
|
|
172
|
+
const deadline = now() + deviceTimeoutMs;
|
|
173
|
+
for (;;) {
|
|
174
|
+
const left = deadline - now();
|
|
175
|
+
if (left <= 0) throw timedOut();
|
|
176
|
+
const { granted } = await pollOnce(left);
|
|
177
|
+
if (granted) return granted;
|
|
178
|
+
await sleep(Math.min(intervalMs, Math.max(0, deadline - now())), signal);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const waitForCallback = (async () => {
|
|
182
|
+
try {
|
|
183
|
+
const granted = await pollForCode();
|
|
184
|
+
return await exchangeAuthorizationCode({
|
|
185
|
+
pkce: { verifier: granted.code_verifier },
|
|
186
|
+
code: granted.authorization_code,
|
|
187
|
+
redirectUri: `${issuerBase}/deviceauth/callback`,
|
|
188
|
+
signal,
|
|
189
|
+
});
|
|
190
|
+
} catch (err) {
|
|
191
|
+
// A cancelled login settles empty, as the browser flow does.
|
|
192
|
+
if (signal.aborted) return null;
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
})();
|
|
196
|
+
// A cancelled or expired login may reject before a host attaches a listener.
|
|
197
|
+
waitForCallback.catch(() => {});
|
|
198
|
+
process.stderr.write(
|
|
199
|
+
`\n[openai-oauth] To log in to ChatGPT (OpenAI OAuth), open:\n${url}\nand enter this code: ${userCode}\n\n`
|
|
200
|
+
);
|
|
201
|
+
return {
|
|
202
|
+
provider: 'openai-oauth',
|
|
203
|
+
url,
|
|
204
|
+
userCode,
|
|
205
|
+
waitForCallback,
|
|
206
|
+
cancel: () => controller.abort(),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Without a browser on this host (headless/SSH) the device-code flow is used;
|
|
211
|
+
// like Codex, a server that has none (404) falls back to the browser flow.
|
|
90
212
|
async function beginOAuthLogin({ openBrowser = true } = {}) {
|
|
213
|
+
if (!openBrowser) {
|
|
214
|
+
const device = await beginDeviceLogin();
|
|
215
|
+
if (device) return device;
|
|
216
|
+
process.stderr.write('\n[openai-oauth] Device code login is not enabled; falling back to browser login.\n');
|
|
217
|
+
}
|
|
218
|
+
return await beginBrowserLogin({ openBrowser });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function beginBrowserLogin({ openBrowser }) {
|
|
91
222
|
const pkce = createOAuthPkce(64);
|
|
92
223
|
const state = randomBytes(16).toString('hex');
|
|
93
224
|
const url = new URL(AUTHORIZE_URL);
|
|
@@ -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
|