mixdog 0.9.151 → 0.9.153
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/browser-import-NOTICE.txt +11 -0
- package/NOTICE.md +11 -0
- package/README.md +108 -68
- package/package.json +17 -4
- package/scripts/computer-task-cost-report.mjs +144 -0
- package/scripts/run-office-live-tests.mjs +32 -0
- package/scripts/tool-stress.mjs +0 -2
- package/src/agents/front-worker/AGENT.md +13 -0
- package/src/agents/front-worker/agent.json +6 -0
- package/src/agents/heavy-worker/AGENT.md +10 -11
- package/src/agents/heavy-worker/agent.json +1 -1
- package/src/agents/maintainer/AGENT.md +0 -2
- package/src/agents/reviewer/AGENT.md +9 -13
- package/src/agents/reviewer/agent.json +1 -1
- package/src/agents/security/AGENT.md +14 -0
- package/src/agents/security/agent.json +6 -0
- package/src/agents/worker/AGENT.md +8 -8
- package/src/agents/worker/agent.json +1 -1
- package/src/agents/writer/AGENT.md +14 -0
- package/src/agents/writer/agent.json +6 -0
- package/src/defaults/skills/setup/SKILL.md +18 -27
- package/src/headless-exec.mjs +3 -10
- package/src/headless-exec.test.mjs +2 -7
- package/src/rules/agent/00-common.md +4 -7
- package/src/rules/lead/01-general.md +0 -3
- package/src/rules/shared/10-tool-workflow.md +10 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.test.mjs +378 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.test.mjs +115 -0
- package/src/runtime/agent/orchestrator/config.mjs +13 -68
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
- package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
- package/src/runtime/agent/orchestrator/providers/admission-scheduler-metrics.test.mjs +47 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -3
- package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +59 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +71 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -12
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +122 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +144 -93
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +18 -13
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +288 -0
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +86 -7
- package/src/runtime/agent/orchestrator/session/agent-loop.test.mjs +222 -0
- package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.test.mjs +83 -0
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +26 -2
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +112 -0
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/context-usage-restart.test.mjs +72 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +100 -15
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +74 -0
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +20 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.test.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +111 -39
- package/src/runtime/agent/orchestrator/session/manager/ask-session.test.mjs +46 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -6
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +27 -3
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +26 -54
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +63 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +580 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +593 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +119 -190
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +43 -8
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store-live-publication.test.mjs +83 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +64 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.test.mjs +219 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +26 -2
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/attachments/pdf-extract.mjs +3 -0
- package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
- package/src/runtime/bridge-clients.test.mjs +827 -0
- package/src/runtime/browser-bridge/action-schema.mjs +251 -0
- package/src/runtime/browser-bridge/client.mjs +152 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +159 -0
- package/src/runtime/computer-bridge/action-schema.mjs +653 -0
- package/src/runtime/computer-bridge/client.mjs +231 -0
- package/src/runtime/computer-bridge/tool-defs.mjs +33 -0
- package/src/runtime/media/store.mjs +18 -8
- package/src/runtime/media/store.test.mjs +14 -0
- package/src/runtime/memory/index.mjs +1 -1
- package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
- package/src/runtime/office/assurance-benchmark.mjs +358 -0
- package/src/runtime/office/assurance.mjs +833 -0
- package/src/runtime/office/benchmark.mjs +496 -0
- package/src/runtime/office/capabilities.mjs +551 -0
- package/src/runtime/office/com-adapter.mjs +283 -0
- package/src/runtime/office/content-model.mjs +183 -0
- package/src/runtime/office/contract-benchmark.mjs +105 -0
- package/src/runtime/office/design-library.mjs +1562 -0
- package/src/runtime/office/design-system.mjs +1966 -0
- package/src/runtime/office/index.mjs +2413 -0
- package/src/runtime/office/journal.mjs +82 -0
- package/src/runtime/office/office-assurance.test.mjs +371 -0
- package/src/runtime/office/office-com-host.ps1 +3781 -0
- package/src/runtime/office/office-com-session-host.ps1 +855 -0
- package/src/runtime/office/office-live-runtime.test.mjs +819 -0
- package/src/runtime/office/office-runtime.test.mjs +2070 -0
- package/src/runtime/office/ooxml-validator.mjs +271 -0
- package/src/runtime/office/pagination.mjs +132 -0
- package/src/runtime/office/pdf-adapter.mjs +591 -0
- package/src/runtime/office/pdf-analysis.mjs +372 -0
- package/src/runtime/office/pdf-render.mjs +167 -0
- package/src/runtime/office/pdf-security.mjs +63 -0
- package/src/runtime/office/polish-benchmark.mjs +190 -0
- package/src/runtime/office/portable-ooxml.mjs +1867 -0
- package/src/runtime/office/quality-live-benchmark.mjs +486 -0
- package/src/runtime/office/quality-pipeline.mjs +136 -0
- package/src/runtime/office/tabular.mjs +302 -0
- package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
- package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
- package/src/runtime/office/tool-defs.mjs +113 -0
- package/src/runtime/office/visual-diff.mjs +89 -0
- package/src/runtime/office/xlsx-assertions.mjs +172 -0
- package/src/runtime/office/xlsx-contract.mjs +147 -0
- package/src/runtime/shared/agent-route-config.mjs +9 -0
- package/src/runtime/shared/child-spawn-gate.mjs +17 -0
- package/src/runtime/shared/child-spawn-remote.mjs +69 -7
- package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
- package/src/runtime/shared/pristine-execution-contract.json +2 -1
- package/src/runtime/shared/provider-api-key.mjs +1 -0
- package/src/runtime/shared/session-runtime-health.mjs +91 -0
- package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
- package/src/runtime/shared/skill-document.mjs +86 -0
- package/src/runtime/shared/skill-document.test.mjs +81 -0
- package/src/runtime/shared/tool-execution-contract.mjs +16 -0
- package/src/runtime/shared/tool-surface.mjs +27 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
- package/src/runtime/shared/turn-snapshot.mjs +32 -9
- package/src/runtime/shared/user-cwd.mjs +33 -14
- package/src/runtime/shared/user-cwd.test.mjs +43 -0
- package/src/session-runtime/agent-disable.test.mjs +20 -1
- package/src/session-runtime/context-status.mjs +9 -3
- package/src/session-runtime/cwd-plugins.mjs +26 -26
- package/src/session-runtime/cwd-plugins.test.mjs +75 -0
- package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
- package/src/session-runtime/deferred-tool-delta.mjs +104 -0
- package/src/session-runtime/deferred-tool-delta.test.mjs +40 -0
- package/src/session-runtime/global-extensions.mjs +26 -0
- package/src/session-runtime/global-extensions.test.mjs +28 -0
- package/src/session-runtime/global-settings-session-identity.test.mjs +76 -0
- package/src/session-runtime/goal-runtime.mjs +1141 -0
- package/src/session-runtime/goal-runtime.test.mjs +645 -0
- package/src/session-runtime/lifecycle-api.mjs +58 -11
- package/src/session-runtime/lifecycle-api.test.mjs +183 -0
- package/src/session-runtime/mcp-glue.mjs +49 -47
- package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
- package/src/session-runtime/plugin-mcp.mjs +96 -22
- package/src/session-runtime/prewarm.mjs +1 -10
- package/src/session-runtime/provider-request-snapshot.mjs +33 -9
- package/src/session-runtime/provider-request-snapshot.test.mjs +27 -5
- package/src/session-runtime/resource-api-global.test.mjs +109 -0
- package/src/session-runtime/resource-api.mjs +168 -116
- package/src/session-runtime/runtime-core.mjs +208 -29
- package/src/session-runtime/runtime-tunables.mjs +6 -8
- package/src/session-runtime/session-lifecycle.mjs +34 -12
- package/src/session-runtime/session-title.mjs +2 -2
- package/src/session-runtime/session-turn-api.mjs +37 -25
- package/src/session-runtime/skills-api.mjs +79 -25
- package/src/session-runtime/skills-api.test.mjs +99 -0
- package/src/session-runtime/tool-catalog-data.mjs +1 -1
- package/src/session-runtime/tool-catalog-schema.mjs +0 -5
- package/src/session-runtime/tool-catalog.mjs +42 -112
- package/src/session-runtime/tool-defs.mjs +6 -1
- package/src/session-runtime/tool-policy-surface.test.mjs +93 -17
- package/src/session-runtime/tool-surface.mjs +30 -8
- package/src/session-runtime/workflow-agents-api.mjs +8 -25
- package/src/session-runtime/workflow.mjs +8 -6
- package/src/standalone/agent-dispatch-broker.mjs +5 -9
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/job-views.mjs +3 -1
- package/src/standalone/agent-tool/notify.mjs +11 -9
- package/src/standalone/agent-tool/notify.test.mjs +37 -0
- package/src/standalone/agent-tool/render.mjs +3 -0
- package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
- package/src/standalone/agent-tool/spawn-flow.test.mjs +41 -0
- package/src/standalone/agent-tool/tool-def.mjs +1 -1
- package/src/standalone/agent-tool/worker-index.mjs +123 -0
- package/src/standalone/agent-tool/worker-index.test.mjs +132 -0
- package/src/standalone/agent-tool/worker-rows.mjs +2 -1
- package/src/standalone/agent-tool.mjs +57 -10
- package/src/standalone/daemon.mjs +113 -15
- package/src/standalone/hook-bus/config.mjs +1 -0
- package/src/standalone/plugin-admin.mjs +17 -0
- package/src/standalone/plugin-admin.test.mjs +7 -0
- package/src/standalone/provider-admin.mjs +1 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
- package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
- package/src/standalone/session-runtime-host-factory.mjs +17 -0
- package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
- package/src/standalone/session-runtime-host-health.test.mjs +5 -0
- package/src/standalone/session-runtime-host.mjs +709 -58
- package/src/standalone/session-runtime-inline-host.mjs +258 -0
- package/src/standalone/session-runtime-inline-host.test.mjs +385 -0
- package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
- package/src/standalone/session-runtime-shard-host.test.mjs +642 -0
- package/src/standalone/session-runtime-shard-router.mjs +149 -0
- package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
- package/src/standalone/session-runtime-worker.mjs +302 -26
- package/src/standalone/session-service-agent-tree.test.mjs +984 -0
- package/src/standalone/session-service-goal-recovery.test.mjs +177 -0
- package/src/standalone/session-service.mjs +711 -11
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/core-memory-picker.mjs +2 -0
- package/src/tui/app/extension-pickers.mjs +1 -1
- package/src/tui/app/maintenance-pickers.mjs +3 -3
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/model-picker.mjs +14 -4
- package/src/tui/app/panel-handoff.test.mjs +311 -0
- package/src/tui/app/project-picker.mjs +1 -0
- package/src/tui/app/route-pickers.mjs +21 -6
- package/src/tui/app/settings-picker.mjs +26 -3
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch.mjs +73 -22
- package/src/tui/app/theme-effort-pickers.mjs +7 -2
- package/src/tui/components/Picker.jsx +10 -1
- package/src/tui/dist/index.mjs +187 -45
- package/src/tui/lib/voice-setup.mjs +4 -1
- package/src/tui/session/agent-message-source.test.mjs +37 -0
- package/src/tui/session/completion-card-restore.test.mjs +50 -0
- package/src/tui/session/context-state.mjs +3 -13
- package/src/tui/session/goal-continuation.mjs +141 -0
- package/src/tui/session/goal-continuation.test.mjs +203 -0
- package/src/tui/session/queue-helpers.mjs +5 -2
- package/src/tui/session/session-action-surface.test.mjs +190 -0
- package/src/tui/session/session-api-ext.mjs +48 -8
- package/src/tui/session/session-api.mjs +73 -1
- package/src/tui/session/session-flow.mjs +31 -1
- package/src/tui/session/turn.mjs +88 -15
- package/src/tui/session-local.mjs +39 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
|
@@ -26,6 +26,11 @@ import {
|
|
|
26
26
|
// Human-readable transport label for handshake/acquire error messages. Shared
|
|
27
27
|
// with openai-oauth-ws.mjs (stream-side errors use the same labels).
|
|
28
28
|
import { _envOn, _codexBetaFeatures, _dumpHandshakeHeaders, _dumpFrame, _formatRedactedHeaders, _cfCookieHeader, _cfCookieCapture } from './openai-ws-headers.mjs';
|
|
29
|
+
import {
|
|
30
|
+
clearAllCodexTurnStates,
|
|
31
|
+
clearCodexTurnStateScope,
|
|
32
|
+
retireCodexTurnStateOwner,
|
|
33
|
+
} from './openai-turn-state.mjs';
|
|
29
34
|
export function _wsErrLabel(p) {
|
|
30
35
|
if (p === 'xai') return 'xAI WS';
|
|
31
36
|
if (p === 'openai-direct' || p === 'openai') return 'OpenAI WS';
|
|
@@ -81,44 +86,94 @@ const MAX_POOLED_SOCKETS_PER_KEY = 8;
|
|
|
81
86
|
export const _wsPool = new Map();
|
|
82
87
|
let _releaseSequence = 0;
|
|
83
88
|
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
89
|
+
// Codex gives one turn-scoped session exclusive use of one WebSocket until its
|
|
90
|
+
// response stream ends. Mirror that ownership per poolKey: a concurrent acquire
|
|
91
|
+
// waits instead of opening a sibling connection with a mismatched response
|
|
92
|
+
// chain. The claim is retained through close when keep=false so only an actual
|
|
93
|
+
// close can hand turn state to a replacement socket.
|
|
94
|
+
const _poolOwnerByKey = new Map();
|
|
95
|
+
|
|
96
|
+
function _acquireAbortError(externalSignal) {
|
|
97
|
+
const reason = externalSignal?.reason;
|
|
98
|
+
return reason instanceof Error ? reason : new Error('OpenAI OAuth WS acquire aborted');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function _waitForPoolOwnerRelease(claim, externalSignal) {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
let settled = false;
|
|
104
|
+
const finish = (fn, value) => {
|
|
105
|
+
if (settled) return;
|
|
106
|
+
settled = true;
|
|
107
|
+
claim.waiters.delete(onRelease);
|
|
108
|
+
try { externalSignal?.removeEventListener('abort', onAbort); } catch {}
|
|
109
|
+
fn(value);
|
|
110
|
+
};
|
|
111
|
+
const onRelease = () => finish(resolve);
|
|
112
|
+
const onAbort = () => finish(reject, _acquireAbortError(externalSignal));
|
|
113
|
+
claim.waiters.add(onRelease);
|
|
114
|
+
if (externalSignal) {
|
|
115
|
+
if (externalSignal.aborted) {
|
|
116
|
+
onAbort();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
externalSignal.addEventListener('abort', onAbort, { once: true });
|
|
120
|
+
}
|
|
113
121
|
});
|
|
114
122
|
}
|
|
115
123
|
|
|
116
|
-
function
|
|
124
|
+
async function _claimPoolOwner(poolKey, externalSignal) {
|
|
117
125
|
if (!poolKey) return null;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
const waitStartedAt = performance.now();
|
|
127
|
+
for (;;) {
|
|
128
|
+
if (externalSignal?.aborted) throw _acquireAbortError(externalSignal);
|
|
129
|
+
const current = _poolOwnerByKey.get(poolKey);
|
|
130
|
+
if (!current) {
|
|
131
|
+
const claim = {
|
|
132
|
+
entry: null,
|
|
133
|
+
waiters: new Set(),
|
|
134
|
+
waitMs: Math.max(0, performance.now() - waitStartedAt),
|
|
135
|
+
};
|
|
136
|
+
_poolOwnerByKey.set(poolKey, claim);
|
|
137
|
+
return claim;
|
|
138
|
+
}
|
|
139
|
+
await _waitForPoolOwnerRelease(current, externalSignal);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function _bindPoolOwner(poolKey, claim, entry) {
|
|
144
|
+
if (!poolKey || !claim) return;
|
|
145
|
+
if (_poolOwnerByKey.get(poolKey) !== claim) {
|
|
146
|
+
try { entry?.socket?.close?.(1000, 'pool_owner_changed'); } catch {}
|
|
147
|
+
_scheduleForcedTerminate(entry?.socket);
|
|
148
|
+
throw new Error('OpenAI OAuth WS ownership changed during acquire');
|
|
149
|
+
}
|
|
150
|
+
claim.entry = entry;
|
|
151
|
+
entry.poolOwnerClaim = claim;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function _releasePoolOwner(poolKey, owner) {
|
|
155
|
+
if (!poolKey || !owner) return;
|
|
156
|
+
const claim = owner.poolOwnerClaim || owner;
|
|
157
|
+
if (_poolOwnerByKey.get(poolKey) !== claim) return;
|
|
158
|
+
_poolOwnerByKey.delete(poolKey);
|
|
159
|
+
if (claim.entry?.poolOwnerClaim === claim) {
|
|
160
|
+
try { delete claim.entry.poolOwnerClaim; } catch {}
|
|
161
|
+
}
|
|
162
|
+
for (const waiter of [...claim.waiters]) {
|
|
163
|
+
try { waiter(); } catch {}
|
|
164
|
+
}
|
|
165
|
+
claim.waiters.clear();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function _clearPoolOwnerScope(poolKey) {
|
|
169
|
+
const claim = poolKey ? _poolOwnerByKey.get(poolKey) : null;
|
|
170
|
+
if (claim) _releasePoolOwner(poolKey, claim);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function _clearAllPoolOwners() {
|
|
174
|
+
for (const [poolKey, claim] of [..._poolOwnerByKey.entries()]) {
|
|
175
|
+
_releasePoolOwner(poolKey, claim);
|
|
176
|
+
}
|
|
122
177
|
}
|
|
123
178
|
|
|
124
179
|
function _poolCompatibility(auth, cacheKey) {
|
|
@@ -141,12 +196,9 @@ function _entryCompatible(entry, compatibility) {
|
|
|
141
196
|
&& entry?.cacheKeyIdentity === compatibility.cacheKeyIdentity;
|
|
142
197
|
}
|
|
143
198
|
|
|
144
|
-
// A previous_response_id is valid on the connection that produced it.
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
// to be inserted first. This is the closest representation of Codex's
|
|
148
|
-
// turn-local single-connection ownership without weakening the existing
|
|
149
|
-
// full-frame fallback checks.
|
|
199
|
+
// A previous_response_id is valid on the connection that produced it. If a
|
|
200
|
+
// bucket contains multiple legacy idle entries, continue on the most recently
|
|
201
|
+
// completed one instead of whichever socket happened to be inserted first.
|
|
150
202
|
function _selectIdleEntry(entries, compatibility) {
|
|
151
203
|
let selected;
|
|
152
204
|
for (const entry of entries || []) {
|
|
@@ -184,7 +236,7 @@ function _getPoolArr(poolKey) {
|
|
|
184
236
|
// cap-overflow ephemeral. They were invisible to per-session close and to the
|
|
185
237
|
// global drain, so a wedged one survived both. Tracking them here keeps the
|
|
186
238
|
// reuse map unchanged (nothing can select them) while making every owned socket
|
|
187
|
-
// reachable by closeOpenaiWsPoolForSession/
|
|
239
|
+
// reachable by closeOpenaiWsPoolForSession/drainOpenaiWsPool.
|
|
188
240
|
const _unpooledSockets = new Set();
|
|
189
241
|
|
|
190
242
|
function _trackUnpooled(entry, poolKey) {
|
|
@@ -203,9 +255,6 @@ function _removeFromPool(poolKey, entry) {
|
|
|
203
255
|
// idle-close or liveness-ping interval.
|
|
204
256
|
_clearIdle(entry);
|
|
205
257
|
_clearLiveness(entry);
|
|
206
|
-
// The entry is going away; keep its shard pin so the session's next socket
|
|
207
|
-
// can reconnect onto the same backend node.
|
|
208
|
-
_retainTurnState(poolKey, entry);
|
|
209
258
|
if (!poolKey) return;
|
|
210
259
|
const arr = _wsPool.get(poolKey);
|
|
211
260
|
if (!arr) return;
|
|
@@ -375,7 +424,7 @@ export function _sendFrame(entry, frame, sendSpan = null, timeoutMs = WS_SEND_TI
|
|
|
375
424
|
});
|
|
376
425
|
}
|
|
377
426
|
|
|
378
|
-
function _buildHandshakeHeaders({ auth, sessionToken,
|
|
427
|
+
function _buildHandshakeHeaders({ auth, sessionToken, cacheKey: _cacheKey, codexHeaders }) {
|
|
379
428
|
// xAI WS: do NOT pin x-grok-conv-id. Measured parallel runs show that
|
|
380
429
|
// forcing a routing shard via that header alternates cold caches across
|
|
381
430
|
// parallel workers; the automatic prompt-prefix cache holds up better
|
|
@@ -431,7 +480,6 @@ function _buildHandshakeHeaders({ auth, sessionToken, turnState, cacheKey: _cach
|
|
|
431
480
|
// distinguishable across reconnects.
|
|
432
481
|
headers['x-client-request-id'] = randomBytes(16).toString('hex');
|
|
433
482
|
}
|
|
434
|
-
if (turnState) headers['x-codex-turn-state'] = turnState;
|
|
435
483
|
// Probe knobs (cache-route hunt 2026-07-04): see jar block at top of file.
|
|
436
484
|
if (isOpenAiOauth) {
|
|
437
485
|
const jar = _cfCookieHeader(auth);
|
|
@@ -454,8 +502,8 @@ function _mintSessionToken(cacheKey, auth) {
|
|
|
454
502
|
return cacheKey || 'mixdog-default';
|
|
455
503
|
}
|
|
456
504
|
|
|
457
|
-
function _openSocket({ auth, sessionToken,
|
|
458
|
-
const headers = _buildHandshakeHeaders({ auth, sessionToken,
|
|
505
|
+
function _openSocket({ auth, sessionToken, externalSignal, cacheKey, codexHeaders }) {
|
|
506
|
+
const headers = _buildHandshakeHeaders({ auth, sessionToken, cacheKey, codexHeaders });
|
|
459
507
|
const baseUrl = auth.type === 'xai'
|
|
460
508
|
? XAI_WS_URL
|
|
461
509
|
: auth.type === 'openai-direct'
|
|
@@ -510,11 +558,8 @@ function _openSocket({ auth, sessionToken, turnState, externalSignal, cacheKey,
|
|
|
510
558
|
));
|
|
511
559
|
}, WS_ACQUIRE_TIMEOUT_MS);
|
|
512
560
|
try { acquireTimer.unref?.(); } catch {}
|
|
513
|
-
const capturedHeaders = { turnState: null };
|
|
514
561
|
socket.once('upgrade', (res) => {
|
|
515
562
|
try {
|
|
516
|
-
const ts = res?.headers?.['x-codex-turn-state'];
|
|
517
|
-
if (typeof ts === 'string' && ts.length) capturedHeaders.turnState = ts;
|
|
518
563
|
_cfCookieCapture(auth, res?.headers?.['set-cookie']);
|
|
519
564
|
// Probe: dump the full 101-upgrade response header set so we can
|
|
520
565
|
// see what the server actually issues (turn-state investigation).
|
|
@@ -541,7 +586,7 @@ function _openSocket({ auth, sessionToken, turnState, externalSignal, cacheKey,
|
|
|
541
586
|
if (process.env.MIXDOG_DEBUG_AGENT) {
|
|
542
587
|
process.stderr.write(`[agent-trace] ws-open-ok elapsed=${Date.now() - _wsOpenStart}ms\n`);
|
|
543
588
|
}
|
|
544
|
-
settle(true, { socket
|
|
589
|
+
settle(true, { socket });
|
|
545
590
|
});
|
|
546
591
|
socket.once('error', (err) => {
|
|
547
592
|
if (process.env.MIXDOG_DEBUG_AGENT) {
|
|
@@ -590,15 +635,23 @@ function _openSocket({ auth, sessionToken, turnState, externalSignal, cacheKey,
|
|
|
590
635
|
|
|
591
636
|
let _openSocketImpl = _openSocket;
|
|
592
637
|
|
|
593
|
-
export async function acquireWebSocket({
|
|
638
|
+
export async function acquireWebSocket({
|
|
639
|
+
auth,
|
|
640
|
+
poolKey,
|
|
641
|
+
cacheKey,
|
|
642
|
+
codexHeaders,
|
|
643
|
+
forceFresh,
|
|
644
|
+
externalSignal,
|
|
645
|
+
}) {
|
|
594
646
|
const _acqStart = Date.now();
|
|
595
647
|
if (process.env.MIXDOG_DEBUG_AGENT) {
|
|
596
648
|
process.stderr.write(`[agent-trace] acquire-start poolKey=${poolKey} cacheKey=${cacheKey} forceFresh=${forceFresh} externalAborted=${!!externalSignal?.aborted} ts=${_acqStart}\n`);
|
|
597
649
|
}
|
|
598
650
|
if (externalSignal?.aborted) {
|
|
599
|
-
|
|
600
|
-
throw reason instanceof Error ? reason : new Error('OpenAI OAuth WS acquire aborted');
|
|
651
|
+
throw _acquireAbortError(externalSignal);
|
|
601
652
|
}
|
|
653
|
+
const ownerClaim = await _claimPoolOwner(poolKey, externalSignal);
|
|
654
|
+
try {
|
|
602
655
|
if (poolKey && !forceFresh) {
|
|
603
656
|
const arr = _wsPool.get(poolKey) || [];
|
|
604
657
|
const compatibility = _poolCompatibility(auth, cacheKey);
|
|
@@ -610,13 +663,6 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
610
663
|
if (!_isOpen(arr[i]) || arr[i].closing || incompatibleIdle) {
|
|
611
664
|
if (incompatibleIdle) {
|
|
612
665
|
try { arr[i].socket.close(1000, 'pool_boundary_changed'); } catch {}
|
|
613
|
-
} else {
|
|
614
|
-
// A dead socket pruned here never reaches _removeFromPool,
|
|
615
|
-
// so retain its shard pin explicitly — this is the common
|
|
616
|
-
// reconnect path and the one the replacement handshake
|
|
617
|
-
// below reads. A boundary change deliberately drops the
|
|
618
|
-
// pin: it belongs to the old auth/cache identity.
|
|
619
|
-
_retainTurnState(poolKey, arr[i]);
|
|
620
666
|
}
|
|
621
667
|
_clearIdle(arr[i]);
|
|
622
668
|
_clearLiveness(arr[i]);
|
|
@@ -660,7 +706,8 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
660
706
|
if (process.env.MIXDOG_DEBUG_AGENT) {
|
|
661
707
|
process.stderr.write(`[agent-trace] acquire-reuse poolKey=${poolKey} openSockets=${arr.length} elapsed=${Date.now() - _acqStart}ms\n`);
|
|
662
708
|
}
|
|
663
|
-
|
|
709
|
+
_bindPoolOwner(poolKey, ownerClaim, idle);
|
|
710
|
+
return { entry: idle, reused: true, ownerWaitMs: ownerClaim?.waitMs || 0 };
|
|
664
711
|
}
|
|
665
712
|
// All entries busy and bucket at cap: fall through to ephemeral socket.
|
|
666
713
|
if (arr.length >= MAX_POOLED_SOCKETS_PER_KEY) {
|
|
@@ -668,7 +715,7 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
668
715
|
process.stderr.write(`[agent-trace] acquire-ephemeral cacheKey=${cacheKey} reason=cap elapsed=${Date.now() - _acqStart}ms\n`);
|
|
669
716
|
}
|
|
670
717
|
const ephSessionToken = _mintSessionToken(cacheKey, auth);
|
|
671
|
-
const { socket
|
|
718
|
+
const { socket } = await _openSocketImpl({ auth, sessionToken: ephSessionToken, externalSignal, cacheKey, codexHeaders });
|
|
672
719
|
// Drain-complete fence: same invariant as the normal acquire path —
|
|
673
720
|
// if drain fired during the await, do NOT push an ephemeral entry
|
|
674
721
|
// back into the pool.
|
|
@@ -688,7 +735,7 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
688
735
|
lastInputPrefixHash: null,
|
|
689
736
|
releaseSequence: 0,
|
|
690
737
|
...compatibility,
|
|
691
|
-
turnState:
|
|
738
|
+
turnState: null,
|
|
692
739
|
closing: false,
|
|
693
740
|
ephemeral: true,
|
|
694
741
|
sessionToken: ephSessionToken,
|
|
@@ -698,26 +745,27 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
698
745
|
entry.probing = false;
|
|
699
746
|
socket.on('pong', () => { entry.lastAliveAt = Date.now(); });
|
|
700
747
|
socket.on('message', () => { entry.lastAliveAt = Date.now(); });
|
|
701
|
-
socket.on('close', () => {
|
|
748
|
+
socket.on('close', () => {
|
|
749
|
+
entry.closing = true;
|
|
750
|
+
retireCodexTurnStateOwner(entry.turnStateScope || poolKey, entry);
|
|
751
|
+
_releasePoolOwner(poolKey, entry);
|
|
752
|
+
_untrackUnpooled(entry);
|
|
753
|
+
});
|
|
702
754
|
// Cap-overflow ephemeral: never pooled, still owned by this session.
|
|
703
755
|
_trackUnpooled(entry, poolKey);
|
|
704
|
-
|
|
756
|
+
_bindPoolOwner(poolKey, ownerClaim, entry);
|
|
757
|
+
return { entry, reused: false, ownerWaitMs: ownerClaim?.waitMs || 0 };
|
|
705
758
|
}
|
|
706
759
|
}
|
|
707
|
-
//
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
// propagates within a single entry across its own iterations — plus the
|
|
711
|
-
// codex reconnect case below, where the bucket holds no live entry and the
|
|
712
|
-
// retained token re-pins the shard the dead socket was using. forceFresh
|
|
713
|
-
// asks for a deliberately unpinned socket, so it always opens clean.
|
|
760
|
+
// A handshake is scoped to a physical connection, while turn state belongs
|
|
761
|
+
// to one logical turn. Every new socket therefore opens without turn state;
|
|
762
|
+
// request metadata restores it later only when the turn id matches.
|
|
714
763
|
const sessionToken = _mintSessionToken(cacheKey, auth);
|
|
715
764
|
const compatibility = _poolCompatibility(auth, cacheKey);
|
|
716
|
-
const retainedTurnState = forceFresh ? null : _replacementTurnState(poolKey);
|
|
717
765
|
if (process.env.MIXDOG_DEBUG_AGENT) {
|
|
718
766
|
process.stderr.write(`[agent-trace] acquire-new tokenHash=${createHash('sha256').update(String(sessionToken)).digest('hex').slice(0, 8)} elapsed=${Date.now() - _acqStart}ms\n`);
|
|
719
767
|
}
|
|
720
|
-
const { socket
|
|
768
|
+
const { socket } = await _openSocketImpl({ auth, sessionToken, externalSignal, cacheKey, codexHeaders });
|
|
721
769
|
// Drain may complete while the normal handshake is awaiting 'open'. Never
|
|
722
770
|
// return or insert that late socket into the already-drained process pool.
|
|
723
771
|
if (_drainComplete) {
|
|
@@ -736,11 +784,8 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
736
784
|
lastInputPrefixHash: null,
|
|
737
785
|
releaseSequence: 0,
|
|
738
786
|
...compatibility,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
// original turn owner so the per-turn drop guard still retires it.
|
|
742
|
-
turnState: turnState || retainedTurnState?.turnState || null,
|
|
743
|
-
turnStateTurnId: turnState ? null : (retainedTurnState?.turnStateTurnId ?? null),
|
|
787
|
+
turnState: null,
|
|
788
|
+
turnStateTurnId: null,
|
|
744
789
|
closing: false,
|
|
745
790
|
ephemeral: false,
|
|
746
791
|
sessionToken,
|
|
@@ -757,10 +802,17 @@ export async function acquireWebSocket({ auth, poolKey, cacheKey, codexHeaders,
|
|
|
757
802
|
else _trackUnpooled(entry, poolKey);
|
|
758
803
|
socket.on('close', () => {
|
|
759
804
|
entry.closing = true;
|
|
805
|
+
retireCodexTurnStateOwner(entry.turnStateScope || poolKey, entry);
|
|
806
|
+
_releasePoolOwner(poolKey, entry);
|
|
760
807
|
_untrackUnpooled(entry);
|
|
761
808
|
_removeFromPool(poolKey, entry);
|
|
762
809
|
});
|
|
763
|
-
|
|
810
|
+
_bindPoolOwner(poolKey, ownerClaim, entry);
|
|
811
|
+
return { entry, reused: false, ownerWaitMs: ownerClaim?.waitMs || 0 };
|
|
812
|
+
} catch (err) {
|
|
813
|
+
_releasePoolOwner(poolKey, ownerClaim);
|
|
814
|
+
throw err;
|
|
815
|
+
}
|
|
764
816
|
}
|
|
765
817
|
|
|
766
818
|
export function releaseWebSocket({ entry, poolKey, keep }) {
|
|
@@ -790,14 +842,13 @@ export function releaseWebSocket({ entry, poolKey, keep }) {
|
|
|
790
842
|
// Idle pooled sockets retain reuse state without retaining the process.
|
|
791
843
|
// acquireWebSocket re-refs the transport before probing or handing it out.
|
|
792
844
|
_setTransportReferenced(entry, false);
|
|
845
|
+
_releasePoolOwner(poolKey, entry);
|
|
793
846
|
}
|
|
794
847
|
|
|
795
848
|
export function closeOpenaiWsPoolForSession(poolKey, reason = 'session_closed') {
|
|
796
849
|
if (!poolKey) return;
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
// close() calls also keeps the async 'close' handlers from re-retaining it.
|
|
800
|
-
_turnStateByPoolKey.delete(poolKey);
|
|
850
|
+
_clearPoolOwnerScope(poolKey);
|
|
851
|
+
clearCodexTurnStateScope(poolKey);
|
|
801
852
|
const closeReason = String(reason || 'session_closed');
|
|
802
853
|
const entries = _wsPool.get(poolKey);
|
|
803
854
|
if (entries) {
|
|
@@ -859,6 +910,7 @@ export function hasPooledWebSocket(poolKey) {
|
|
|
859
910
|
}
|
|
860
911
|
|
|
861
912
|
export function _clearWebSocketPoolForTest() {
|
|
913
|
+
_clearAllPoolOwners();
|
|
862
914
|
for (const arr of _wsPool.values()) {
|
|
863
915
|
for (const entry of arr) {
|
|
864
916
|
entry.turnState = null;
|
|
@@ -875,7 +927,7 @@ export function _clearWebSocketPoolForTest() {
|
|
|
875
927
|
}
|
|
876
928
|
_unpooledSockets.clear();
|
|
877
929
|
_wsPool.clear();
|
|
878
|
-
|
|
930
|
+
clearAllCodexTurnStates();
|
|
879
931
|
_releaseSequence = 0;
|
|
880
932
|
}
|
|
881
933
|
|
|
@@ -883,17 +935,17 @@ export function _setOpenSocketForTest(fn) {
|
|
|
883
935
|
_openSocketImpl = typeof fn === 'function' ? fn : _openSocket;
|
|
884
936
|
}
|
|
885
937
|
|
|
886
|
-
// Drain-complete fence — set true once
|
|
938
|
+
// Drain-complete fence — set true once drainOpenaiWsPool runs so any
|
|
887
939
|
// in-flight acquire that resumes after drain throws instead of pushing a
|
|
888
940
|
// fresh socket into the cleared pool. Single-set, process-lifetime invariant.
|
|
889
941
|
let _drainComplete = false;
|
|
890
942
|
|
|
891
943
|
// Drain hook — self-registered exit drain.
|
|
892
944
|
// Force-closes pooled sockets and fences subsequent acquires.
|
|
893
|
-
|
|
894
|
-
// `_closeAllPooledSockets` kept for backward compat with existing call sites.
|
|
895
|
-
export function _closeAllPooledSockets(reason = 'shutdown', { immediate = false } = {}) {
|
|
945
|
+
export function drainOpenaiWsPool(reason = 'shutdown', { immediate = false } = {}) {
|
|
896
946
|
_drainComplete = true;
|
|
947
|
+
_clearAllPoolOwners();
|
|
948
|
+
clearAllCodexTurnStates();
|
|
897
949
|
for (const arr of _wsPool.values()) {
|
|
898
950
|
// _shutdownEntry tears down per-entry timers before the map is dropped
|
|
899
951
|
// (otherwise the idle-close and liveness-ping intervals outlive the
|
|
@@ -909,7 +961,6 @@ export function _closeAllPooledSockets(reason = 'shutdown', { immediate = false
|
|
|
909
961
|
_unpooledSockets.clear();
|
|
910
962
|
_wsPool.clear();
|
|
911
963
|
}
|
|
912
|
-
export const drainOpenaiWsPool = _closeAllPooledSockets;
|
|
913
964
|
// Session-close / drain hooks are shared across provider transports (cursor-wire
|
|
914
965
|
// registers the same globals), so chain whatever is already installed instead of
|
|
915
966
|
// replacing it — import order must not decide which transport gets torn down.
|
|
@@ -925,4 +976,4 @@ globalThis.__mixdogDrainProviderConnections = (reason) => {
|
|
|
925
976
|
};
|
|
926
977
|
// 'exit' hands the listener an exit CODE, never a reason string, and no timer
|
|
927
978
|
// can fire after it: terminate immediately instead of scheduling.
|
|
928
|
-
process.on('exit', () =>
|
|
979
|
+
process.on('exit', () => drainOpenaiWsPool('process-exit', { immediate: true }));
|
|
@@ -33,7 +33,9 @@ import {
|
|
|
33
33
|
streamStalledError,
|
|
34
34
|
} from '../stall-policy.mjs';
|
|
35
35
|
import { customToolCallFromResponseItem } from './custom-tool-wire.mjs';
|
|
36
|
+
import { createProviderReplay } from './lib/provider-replay.mjs';
|
|
36
37
|
import { _wsErrLabel, WS_MAX_INCOMING_FRAME_BYTES } from './openai-ws-pool.mjs';
|
|
38
|
+
import { captureCodexTurnState } from './openai-turn-state.mjs';
|
|
37
39
|
import {
|
|
38
40
|
_sansInput,
|
|
39
41
|
_stableStringify,
|
|
@@ -180,24 +182,19 @@ function _hasHeaderKey(headers, name) {
|
|
|
180
182
|
|
|
181
183
|
export function _captureTurnStateFromEvent(entry, event) {
|
|
182
184
|
if (!entry || entry.turnState || !event || typeof event !== 'object') return;
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
// previous_response_id calls. This event path is the fallback for
|
|
186
|
-
// deployments that surface the header on a response event instead; the
|
|
187
|
-
// write-once guard above means it can never swap a live chain's token.
|
|
185
|
+
// WebSocket turn state is returned by response metadata and replayed only
|
|
186
|
+
// in later response.create metadata for the same logical turn.
|
|
188
187
|
const turnState = _headerString(event.headers, X_CODEX_TURN_STATE_HEADER)
|
|
189
188
|
|| _headerString(event.response?.headers, X_CODEX_TURN_STATE_HEADER)
|
|
190
189
|
|| _headerString(event.response?.metadata?.headers, X_CODEX_TURN_STATE_HEADER)
|
|
191
190
|
|| _headerString(event.metadata?.headers, X_CODEX_TURN_STATE_HEADER);
|
|
192
191
|
if (turnState) {
|
|
193
|
-
entry.turnState =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// the next real turn instead of leaking onto it.
|
|
200
|
-
entry.turnStateTurnId = entry.currentTurnId != null ? entry.currentTurnId : null;
|
|
192
|
+
entry.turnState = captureCodexTurnState(
|
|
193
|
+
entry.turnStateScope,
|
|
194
|
+
entry.turnStateTurnId,
|
|
195
|
+
turnState,
|
|
196
|
+
entry,
|
|
197
|
+
) || ((!entry.turnStateScope || !entry.turnStateTurnId) ? turnState : null);
|
|
201
198
|
}
|
|
202
199
|
}
|
|
203
200
|
|
|
@@ -675,6 +672,10 @@ export async function _streamResponse({
|
|
|
675
672
|
try {
|
|
676
673
|
err.partialContent = content;
|
|
677
674
|
err.partialToolCalls = toolCalls.length ? toolCalls.slice() : undefined;
|
|
675
|
+
err.partialProviderReplay = createProviderReplay(
|
|
676
|
+
traceProvider === 'xai' ? 'xai-responses' : 'openai-responses',
|
|
677
|
+
responseItemsAdded,
|
|
678
|
+
);
|
|
678
679
|
err.pendingToolUse = pendingCalls.size > 0
|
|
679
680
|
|| activeToolItems.size > 0
|
|
680
681
|
|| _toolInFlight === true;
|
|
@@ -794,6 +795,10 @@ export async function _streamResponse({
|
|
|
794
795
|
model,
|
|
795
796
|
reasoningItems: reasoningItems.length ? reasoningItems : undefined,
|
|
796
797
|
responseItems: responseItemsAdded.length ? responseItemsAdded : undefined,
|
|
798
|
+
providerReplay: createProviderReplay(
|
|
799
|
+
traceProvider === 'xai' ? 'xai-responses' : 'openai-responses',
|
|
800
|
+
responseItemsAdded,
|
|
801
|
+
),
|
|
797
802
|
// Dedupe by name+args (Fix 2, array side) so an identical
|
|
798
803
|
// synthetic-leaked + native pair can't run the tool twice.
|
|
799
804
|
toolCalls: toolCalls.length ? dedupeToolCallList(toolCalls) : undefined,
|