mixdog 0.9.152 → 0.9.154
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 +8 -10
- package/package.json +19 -3
- package/scripts/computer-task-cost-report.mjs +144 -0
- package/scripts/run-office-live-tests.mjs +32 -0
- 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/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/rules/shared/60-verification.md +2 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
- 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/config.mjs +7 -2
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -10
- package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +140 -86
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -13
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -4
- package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/compact/handoff.mjs +0 -22
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +11 -23
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +28 -269
- package/src/runtime/agent/orchestrator/session/compact.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +86 -11
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
- 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/recall-fasttrack.mjs +80 -18
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +80 -21
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +33 -72
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -51
- 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 +39 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +41 -8
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +2 -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-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-visibility.mjs +57 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
- package/src/runtime/attachments/pdf-extract.mjs +3 -0
- package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
- package/src/runtime/browser-bridge/action-schema.mjs +251 -0
- package/src/runtime/browser-bridge/client.mjs +75 -26
- package/src/runtime/browser-bridge/tool-defs.mjs +132 -15
- package/src/runtime/computer-bridge/action-schema.mjs +653 -0
- package/src/runtime/computer-bridge/client.mjs +211 -28
- package/src/runtime/computer-bridge/tool-defs.mjs +25 -45
- 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-com-host.ps1 +3781 -0
- package/src/runtime/office/office-com-session-host.ps1 +855 -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/tool-execution-contract.mjs +16 -0
- package/src/runtime/shared/tool-surface.mjs +9 -0
- package/src/session-runtime/context-status.mjs +9 -3
- package/src/session-runtime/deferred-tool-delta.mjs +104 -0
- package/src/session-runtime/goal-runtime.mjs +617 -197
- package/src/session-runtime/lifecycle-api.mjs +50 -8
- package/src/session-runtime/provider-request-snapshot.mjs +33 -9
- package/src/session-runtime/resource-api.mjs +31 -94
- package/src/session-runtime/runtime-core.mjs +78 -26
- 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 +15 -14
- package/src/session-runtime/tool-catalog-data.mjs +1 -1
- package/src/session-runtime/tool-catalog.mjs +38 -99
- package/src/session-runtime/tool-defs.mjs +6 -1
- package/src/session-runtime/tool-surface.mjs +18 -8
- package/src/session-runtime/workflow-agents-api.mjs +8 -25
- package/src/session-runtime/workflow.mjs +8 -6
- 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 +5 -1
- package/src/standalone/agent-tool/render.mjs +3 -0
- package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
- 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-rows.mjs +2 -1
- package/src/standalone/agent-tool.mjs +57 -10
- package/src/standalone/daemon.mjs +105 -6
- package/src/standalone/session-runtime-host.mjs +61 -48
- package/src/standalone/session-runtime-inline-host.mjs +34 -127
- package/src/standalone/session-runtime-worker.mjs +20 -198
- package/src/standalone/session-service.mjs +632 -7
- package/src/tui/dist/index.mjs +24 -1
- package/src/tui/lib/voice-setup.mjs +4 -1
- package/src/tui/session/context-state.mjs +2 -13
- package/src/tui/session/goal-continuation.mjs +38 -7
- package/src/tui/session/session-api-ext.mjs +9 -0
- package/src/tui/session/session-api.mjs +15 -1
- package/src/tui/session/session-flow.mjs +25 -1
- package/src/tui/session/turn.mjs +88 -16
- package/src/tui/session-local.mjs +25 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +0 -135
- package/scripts/native-binary-arch.test.mjs +0 -111
- package/scripts/patch-replay.test.mjs +0 -94
- package/src/headless-exec.test.mjs +0 -539
- package/src/runtime/agent/orchestrator/agent-trace-timing.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +0 -139
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +0 -66
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +0 -80
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +0 -90
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +0 -215
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +0 -165
- package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +0 -142
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +0 -190
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -235
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -176
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -161
- package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -115
- package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -245
- package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +0 -325
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +0 -70
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -457
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +0 -117
- package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +0 -87
- package/src/runtime/agent/orchestrator/session/store/serialize.test.mjs +0 -60
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +0 -583
- package/src/runtime/agent/orchestrator/session/task-wait-control.test.mjs +0 -78
- package/src/runtime/agent/orchestrator/tools/builtin/absence-absorption.test.mjs +0 -43
- package/src/runtime/agent/orchestrator/tools/builtin/absolute-glob-expand.test.mjs +0 -63
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +0 -14
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +0 -224
- package/src/runtime/agent/orchestrator/tools/builtin/enoent-outside-project.test.mjs +0 -56
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +0 -103
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +0 -30
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +0 -390
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +0 -83
- package/src/runtime/agent/orchestrator/tools/builtin/grep-output-budget.test.mjs +0 -31
- package/src/runtime/agent/orchestrator/tools/builtin/grep-single-file-rescue.test.mjs +0 -106
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.test.mjs +0 -102
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +0 -152
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +0 -24
- package/src/runtime/agent/orchestrator/tools/builtin/noise-dir-visibility.test.mjs +0 -69
- package/src/runtime/agent/orchestrator/tools/builtin/read-glob-survey.test.mjs +0 -71
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.test.mjs +0 -56
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.test.mjs +0 -16
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-top-k.test.mjs +0 -39
- package/src/runtime/agent/orchestrator/tools/builtin/write-symlink.test.mjs +0 -153
- package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-anchor-relocation.test.mjs +0 -55
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +0 -88
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +0 -72
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +0 -54
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +0 -20
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +0 -55
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +0 -268
- package/src/runtime/agent/orchestrator/tools/env-scrub.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.test.mjs +0 -19
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +0 -104
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +0 -661
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +0 -552
- package/src/runtime/agent/orchestrator/tools/patch/patch-symlink.test.mjs +0 -141
- package/src/runtime/agent/orchestrator/tools/patch/v4a-pure-move.test.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/patch/v4a-section-coalesce.test.mjs +0 -81
- package/src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.test.mjs +0 -52
- package/src/runtime/attachments/store.test.mjs +0 -209
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +0 -17
- package/src/runtime/channels/lib/webhook/deliveries.test.mjs +0 -18
- package/src/runtime/channels/lib/webhook/relay-tunnel.test.mjs +0 -68
- package/src/runtime/media/adapters/codex-image.test.mjs +0 -22
- package/src/runtime/media/download.test.mjs +0 -52
- package/src/runtime/media/renditions.test.mjs +0 -99
- package/src/runtime/media/store.test.mjs +0 -159
- package/src/runtime/memory/lib/archive-security.test.mjs +0 -128
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +0 -33
- package/src/runtime/memory/lib/http-wire.test.mjs +0 -15
- package/src/runtime/memory/lib/memory-cycle-packets.test.mjs +0 -49
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +0 -78
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +0 -50
- package/src/runtime/memory/lib/pg/process.test.mjs +0 -36
- package/src/runtime/memory/lib/trace-store.test.mjs +0 -57
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +0 -52
- package/src/runtime/shared/background-tasks.test.mjs +0 -78
- package/src/runtime/shared/bounded-download.test.mjs +0 -41
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -25
- package/src/runtime/shared/child-spawn-remote.test.mjs +0 -124
- package/src/runtime/shared/remote-intent.test.mjs +0 -40
- package/src/runtime/shared/resource-admission.test.mjs +0 -43
- package/src/runtime/shared/runtime-root.test.mjs +0 -34
- package/src/runtime/shared/schedule-time.test.mjs +0 -29
- package/src/runtime/shared/session-runtime-health.test.mjs +0 -83
- package/src/runtime/shared/skill-document.test.mjs +0 -81
- package/src/runtime/shared/tool-surface.test.mjs +0 -87
- package/src/runtime/shared/update-checker.test.mjs +0 -33
- package/src/runtime/shared/user-cwd.test.mjs +0 -43
- package/src/runtime/shared/webhook-session-run.test.mjs +0 -14
- package/src/session-runtime/agent-disable.test.mjs +0 -123
- package/src/session-runtime/cwd-plugins.test.mjs +0 -75
- package/src/session-runtime/cwd-tool-routing.test.mjs +0 -61
- package/src/session-runtime/global-extensions.test.mjs +0 -28
- package/src/session-runtime/goal-runtime.test.mjs +0 -108
- package/src/session-runtime/lifecycle-api.test.mjs +0 -21
- package/src/session-runtime/model-settings-persist.test.mjs +0 -105
- package/src/session-runtime/notification-bus.test.mjs +0 -148
- package/src/session-runtime/plugin-mcp-standard.test.mjs +0 -177
- package/src/session-runtime/prewarm.test.mjs +0 -52
- package/src/session-runtime/provider-request-snapshot.test.mjs +0 -37
- package/src/session-runtime/resource-api-global.test.mjs +0 -69
- package/src/session-runtime/session-lifecycle.test.mjs +0 -125
- package/src/session-runtime/session-title.test.mjs +0 -36
- package/src/session-runtime/skills-api.test.mjs +0 -99
- package/src/session-runtime/tool-policy-surface.test.mjs +0 -331
- package/src/standalone/agent-tool/helpers.test.mjs +0 -62
- package/src/standalone/agent-tool/notify.test.mjs +0 -37
- package/src/standalone/agent-tool/spawn-flow.test.mjs +0 -63
- package/src/standalone/channel-restart.test.mjs +0 -145
- package/src/standalone/channel-session-router.test.mjs +0 -37
- package/src/standalone/channel-worker-heartbeat.test.mjs +0 -30
- package/src/standalone/hook-bus/handlers.test.mjs +0 -88
- package/src/standalone/plugin-admin.test.mjs +0 -173
- package/src/standalone/session-runtime-agent-control-client.test.mjs +0 -71
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +0 -139
- package/src/standalone/session-runtime-host-factory.test.mjs +0 -31
- package/src/standalone/session-runtime-host-health.test.mjs +0 -160
- package/src/standalone/session-runtime-inline-host.test.mjs +0 -83
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
- package/src/standalone/session-runtime-shard-host.test.mjs +0 -569
- package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
- package/src/tui/app/live-spinner-visibility.test.mjs +0 -44
- package/src/tui/app/panel-epoch.test.mjs +0 -1009
- package/src/tui/app/panel-handoff.test.mjs +0 -311
- package/src/tui/app/prompt-submit.test.mjs +0 -206
- package/src/tui/session/completion-card-restore.test.mjs +0 -111
- package/src/tui/session/goal-continuation.test.mjs +0 -84
- package/src/tui/session/oauth-flows.test.mjs +0 -134
- package/src/tui/session/queue-helpers.test.mjs +0 -46
- package/src/tui/session/session-action-surface.test.mjs +0 -108
- package/src/tui/session/task-wait-submit.test.mjs +0 -80
- package/src/tui/session/tool-result-status.test.mjs +0 -131
- package/src/tui/session/tui-steering-persist.test.mjs +0 -104
|
@@ -41,6 +41,10 @@ import { createActiveToolItemTracker } from './tool-stream-state.mjs';
|
|
|
41
41
|
import { createProviderReplay } from './lib/provider-replay.mjs';
|
|
42
42
|
import { parseProviderJsonBatch } from './stream-json-pool.mjs';
|
|
43
43
|
import { envFlag as _envFlag } from './lib/env-utils.mjs';
|
|
44
|
+
import {
|
|
45
|
+
activateCodexTurnState,
|
|
46
|
+
captureCodexTurnState,
|
|
47
|
+
} from './openai-turn-state.mjs';
|
|
44
48
|
export { envPositiveInt as _envPositiveInt } from './lib/env-utils.mjs';
|
|
45
49
|
export { _envFlag };
|
|
46
50
|
|
|
@@ -164,7 +168,13 @@ function _pushOutputTextAnnotations(part, citations, citationKeys) {
|
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
|
|
167
|
-
function _buildOpenAIHttpFallbackHeaders({
|
|
171
|
+
export function _buildOpenAIHttpFallbackHeaders({
|
|
172
|
+
auth,
|
|
173
|
+
cacheKey,
|
|
174
|
+
statelessConversation = false,
|
|
175
|
+
poolKey = null,
|
|
176
|
+
turnId = null,
|
|
177
|
+
}) {
|
|
168
178
|
if (auth?.type === 'openai-direct') {
|
|
169
179
|
// Public API-key auth: Bearer <OPENAI_API_KEY>, no chatgpt-account-id /
|
|
170
180
|
// originator (mirrors openai-ws-pool _buildHandshakeHeaders' direct
|
|
@@ -195,6 +205,8 @@ function _buildOpenAIHttpFallbackHeaders({ auth, cacheKey, statelessConversation
|
|
|
195
205
|
headers['session-id'] = sid;
|
|
196
206
|
headers['thread-id'] = sid;
|
|
197
207
|
}
|
|
208
|
+
const turnState = activateCodexTurnState(poolKey, turnId);
|
|
209
|
+
if (turnState) headers['x-codex-turn-state'] = turnState;
|
|
198
210
|
return headers;
|
|
199
211
|
}
|
|
200
212
|
|
|
@@ -251,7 +263,14 @@ export async function sendViaHttpSse({
|
|
|
251
263
|
const totalTimeout = createPassthroughSignal(externalSignal);
|
|
252
264
|
const statelessConversation = opts?.statelessConversation === true
|
|
253
265
|
|| _envFlag('MIXDOG_OAI_STATELESS_HTTP', false);
|
|
254
|
-
const
|
|
266
|
+
const turnId = opts?.turnId || opts?.codexTurnId || opts?.session?.turnId || null;
|
|
267
|
+
const headers = _buildOpenAIHttpFallbackHeaders({
|
|
268
|
+
auth,
|
|
269
|
+
cacheKey,
|
|
270
|
+
statelessConversation,
|
|
271
|
+
poolKey,
|
|
272
|
+
turnId,
|
|
273
|
+
});
|
|
255
274
|
const fetchStartedAt = Date.now();
|
|
256
275
|
const responsesUrl = auth?.type === 'openai-direct'
|
|
257
276
|
? OPENAI_DIRECT_RESPONSES_URL
|
|
@@ -380,6 +399,10 @@ export async function sendViaHttpSse({
|
|
|
380
399
|
totalTimeout.cleanup();
|
|
381
400
|
throw err;
|
|
382
401
|
}
|
|
402
|
+
if (auth?.type !== 'openai-direct') {
|
|
403
|
+
const responseTurnState = response.headers?.get?.('x-codex-turn-state');
|
|
404
|
+
if (responseTurnState) captureCodexTurnState(poolKey, turnId, responseTurnState);
|
|
405
|
+
}
|
|
383
406
|
if (!response.body) {
|
|
384
407
|
totalTimeout.cleanup();
|
|
385
408
|
throw Object.assign(new Error('OpenAI OAuth HTTP fallback returned no response body'), { initialResponseError: true });
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
grokCacheChainTraceFields,
|
|
33
33
|
appendAgentTrace,
|
|
34
34
|
} from '../agent-trace.mjs';
|
|
35
|
+
import { traceCacheBreak } from '../cache-break-trace.mjs';
|
|
35
36
|
import {
|
|
36
37
|
classifyHandshakeError,
|
|
37
38
|
classifyMidstreamError,
|
|
@@ -65,7 +66,10 @@ import {
|
|
|
65
66
|
parseToolSearchArgs,
|
|
66
67
|
_streamResponse,
|
|
67
68
|
} from './openai-ws-stream.mjs';
|
|
68
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
_buildResponseCreateFrame,
|
|
71
|
+
_requestInputMismatchDiagnostics,
|
|
72
|
+
} from './openai-ws-delta.mjs';
|
|
69
73
|
import { resolveOpenAiTransportPolicy } from './openai-transport-policy.mjs';
|
|
70
74
|
import { envPositiveInt } from './lib/env-utils.mjs';
|
|
71
75
|
|
|
@@ -589,6 +593,10 @@ export async function sendViaWebSocket({
|
|
|
589
593
|
// One compact row per logical iteration. Values aggregate all handshake
|
|
590
594
|
// and mid-stream attempts, including warmup, without retaining request data.
|
|
591
595
|
const sendSpan = {
|
|
596
|
+
admissionQueueWaitMs: Math.max(0, Number(sendOpts?._providerAdmission?.queueWaitMs) || 0),
|
|
597
|
+
providerConcurrentRequests: Math.max(1, Number(sendOpts?._providerAdmission?.active) || 1),
|
|
598
|
+
providerQueuedRequests: Math.max(0, Number(sendOpts?._providerAdmission?.queued) || 0),
|
|
599
|
+
poolOwnerWaitMs: 0,
|
|
592
600
|
poolAcquireMs: 0,
|
|
593
601
|
requestBuildSerializationMs: 0,
|
|
594
602
|
preResponseCreatedMs: 0,
|
|
@@ -598,14 +606,44 @@ export async function sendViaWebSocket({
|
|
|
598
606
|
acquireAttempts: 0,
|
|
599
607
|
acquireMode: null,
|
|
600
608
|
emitted: false,
|
|
609
|
+
timing: null,
|
|
601
610
|
};
|
|
602
|
-
const emitSendSpan = (outcome) => {
|
|
603
|
-
if (sendSpan.emitted)
|
|
611
|
+
const emitSendSpan = (outcome, target = null) => {
|
|
612
|
+
if (sendSpan.emitted) {
|
|
613
|
+
if (target && sendSpan.timing) {
|
|
614
|
+
try { target.transportTiming = sendSpan.timing; } catch {}
|
|
615
|
+
}
|
|
616
|
+
return sendSpan.timing;
|
|
617
|
+
}
|
|
604
618
|
sendSpan.emitted = true;
|
|
619
|
+
const socketAcquireMs = Math.max(0, sendSpan.poolAcquireMs - sendSpan.poolOwnerWaitMs);
|
|
620
|
+
const timing = {
|
|
621
|
+
transport: 'websocket',
|
|
622
|
+
outcome,
|
|
623
|
+
admissionQueueWaitMs: sendSpan.admissionQueueWaitMs,
|
|
624
|
+
providerConcurrentRequests: sendSpan.providerConcurrentRequests,
|
|
625
|
+
providerQueuedRequests: sendSpan.providerQueuedRequests,
|
|
626
|
+
poolOwnerWaitMs: sendSpan.poolOwnerWaitMs,
|
|
627
|
+
socketAcquireMs,
|
|
628
|
+
poolAcquireMs: sendSpan.poolAcquireMs,
|
|
629
|
+
requestBuildSerializationMs: sendSpan.requestBuildSerializationMs,
|
|
630
|
+
preResponseCreatedMs: sendSpan.preResponseCreatedMs,
|
|
631
|
+
providerFirstEventMs: sendSpan.firstEventMs,
|
|
632
|
+
retryBackoffMs: sendSpan.retryBackoffMs,
|
|
633
|
+
handshakeRetries: sendSpan.handshakeRetries,
|
|
634
|
+
acquireAttempts: sendSpan.acquireAttempts,
|
|
635
|
+
acquireMode: sendSpan.acquireMode || 'failed',
|
|
636
|
+
};
|
|
637
|
+
sendSpan.timing = timing;
|
|
605
638
|
const payload = {
|
|
606
639
|
provider: traceProvider,
|
|
607
640
|
model: useModel,
|
|
608
641
|
transport: 'websocket',
|
|
642
|
+
admission_queue_wait_ms: timing.admissionQueueWaitMs,
|
|
643
|
+
provider_concurrent_requests: timing.providerConcurrentRequests,
|
|
644
|
+
provider_queued_requests: timing.providerQueuedRequests,
|
|
645
|
+
pool_owner_wait_ms: timing.poolOwnerWaitMs,
|
|
646
|
+
socket_acquire_ms: timing.socketAcquireMs,
|
|
609
647
|
acquire_mode: sendSpan.acquireMode || 'failed',
|
|
610
648
|
acquire_attempts: sendSpan.acquireAttempts,
|
|
611
649
|
handshake_retries: sendSpan.handshakeRetries,
|
|
@@ -625,6 +663,10 @@ export async function sendViaWebSocket({
|
|
|
625
663
|
payload,
|
|
626
664
|
});
|
|
627
665
|
} catch {}
|
|
666
|
+
if (target) {
|
|
667
|
+
try { target.transportTiming = timing; } catch {}
|
|
668
|
+
}
|
|
669
|
+
return timing;
|
|
628
670
|
};
|
|
629
671
|
// Single caller-visible recovery path for both handshake/acquire retries
|
|
630
672
|
// and retryable stream failures. The session/TUI stage bridge renders this
|
|
@@ -699,6 +741,7 @@ export async function sendViaWebSocket({
|
|
|
699
741
|
} catch (err) {
|
|
700
742
|
_stampWarmup(err);
|
|
701
743
|
sendSpan.poolAcquireMs += performance.now() - handshakeStart;
|
|
744
|
+
sendSpan.poolOwnerWaitMs += Math.max(0, Number(err?.ownerWaitMs) || 0);
|
|
702
745
|
// Provenance only; policy remains provider-owned below. This lets
|
|
703
746
|
// the direct wrapper distinguish an upgrade rejection from an
|
|
704
747
|
// application error carrying the same HTTP status.
|
|
@@ -744,6 +787,7 @@ export async function sendViaWebSocket({
|
|
|
744
787
|
const retryable = classifier
|
|
745
788
|
&& Number(err?.httpStatus || 0) !== 401
|
|
746
789
|
&& Number(err?.httpStatus || 0) !== 426
|
|
790
|
+
&& err?.unsafeToRetry !== true
|
|
747
791
|
&& !externalSignal?.aborted;
|
|
748
792
|
if (retryable && attemptIndex < MAX_MIDSTREAM_RETRIES) {
|
|
749
793
|
if (!firstAttemptError) {
|
|
@@ -777,7 +821,7 @@ export async function sendViaWebSocket({
|
|
|
777
821
|
try { err.midstreamClassifier = classifier; } catch {}
|
|
778
822
|
try { err.wsRetriesExhausted = true; } catch {}
|
|
779
823
|
}
|
|
780
|
-
emitSendSpan('error');
|
|
824
|
+
emitSendSpan('error', err);
|
|
781
825
|
throw _stampTool(_stampLiveText(err));
|
|
782
826
|
}
|
|
783
827
|
if (attemptIndex > 0 && firstAttemptError) {
|
|
@@ -794,6 +838,7 @@ export async function sendViaWebSocket({
|
|
|
794
838
|
}
|
|
795
839
|
const { entry, reused } = acquired;
|
|
796
840
|
sendSpan.poolAcquireMs += performance.now() - handshakeStart;
|
|
841
|
+
sendSpan.poolOwnerWaitMs += Math.max(0, Number(acquired?.ownerWaitMs) || 0);
|
|
797
842
|
sendSpan.acquireMode = acquired.prewarmed
|
|
798
843
|
? 'prewarmed'
|
|
799
844
|
: entry?.ephemeral ? 'ephemeral' : (reused ? 'reused' : 'new');
|
|
@@ -863,6 +908,7 @@ export async function sendViaWebSocket({
|
|
|
863
908
|
let strippedResponseItems = 0;
|
|
864
909
|
let skippedResponseItems = 0;
|
|
865
910
|
let responseOutputMismatch = null;
|
|
911
|
+
let requestInputMismatch = null;
|
|
866
912
|
let wireFrameHadTurnState = false;
|
|
867
913
|
let wireFrameMetadataTrace = _metadataTrace(null);
|
|
868
914
|
let framePrefixHash = null;
|
|
@@ -950,7 +996,6 @@ export async function sendViaWebSocket({
|
|
|
950
996
|
entry.lastInputPrefixHash = createHash('sha256')
|
|
951
997
|
.update(JSON.stringify(warmupInputArr))
|
|
952
998
|
.digest('hex');
|
|
953
|
-
entry.turnStateFromPrewarm = true;
|
|
954
999
|
try {
|
|
955
1000
|
const warmupPayload = {
|
|
956
1001
|
provider: traceProvider,
|
|
@@ -976,7 +1021,7 @@ export async function sendViaWebSocket({
|
|
|
976
1021
|
// Codex performs generate:false during session startup, then hands
|
|
977
1022
|
// this live client session to the first real turn. Startup callers
|
|
978
1023
|
// stop here; the pooled entry retains its response id, request
|
|
979
|
-
// snapshot
|
|
1024
|
+
// snapshot and socket for the later real send.
|
|
980
1025
|
if (sendOpts?._startupPrewarmOnly === true) {
|
|
981
1026
|
const responseId = warmupResult?.responseId
|
|
982
1027
|
|| startupWarmupResponseId
|
|
@@ -984,8 +1029,7 @@ export async function sendViaWebSocket({
|
|
|
984
1029
|
|| null;
|
|
985
1030
|
if (responseId) entry.startupWarmupResponseId = responseId;
|
|
986
1031
|
else releaseWebSocket({ entry, poolKey, keep: false });
|
|
987
|
-
|
|
988
|
-
return {
|
|
1032
|
+
const out = {
|
|
989
1033
|
content: '',
|
|
990
1034
|
model: warmupResult?.model || useModel,
|
|
991
1035
|
toolCalls: [],
|
|
@@ -1001,6 +1045,8 @@ export async function sendViaWebSocket({
|
|
|
1001
1045
|
? { entry, poolKey, cacheKey }
|
|
1002
1046
|
: null,
|
|
1003
1047
|
};
|
|
1048
|
+
out.transportTiming = emitSendSpan('ok');
|
|
1049
|
+
return out;
|
|
1004
1050
|
}
|
|
1005
1051
|
|
|
1006
1052
|
// A completed generate:false prewarm is a valid continuation
|
|
@@ -1015,6 +1061,7 @@ export async function sendViaWebSocket({
|
|
|
1015
1061
|
strippedResponseItems = delta.strippedResponseItems || 0;
|
|
1016
1062
|
skippedResponseItems = delta.skippedResponseItems || 0;
|
|
1017
1063
|
responseOutputMismatch = delta.responseOutputMismatch || null;
|
|
1064
|
+
requestInputMismatch = delta.requestInputMismatch || null;
|
|
1018
1065
|
const wireFrame = _withCodexWsClientMetadata(frame, entry, useCodexWsClientMetadata, codexMetadataContext);
|
|
1019
1066
|
wireFrameHadTurnState = !!wireFrame?.client_metadata?.['x-codex-turn-state'];
|
|
1020
1067
|
wireFrameMetadataTrace = _metadataTrace(wireFrame?.client_metadata);
|
|
@@ -1312,6 +1359,12 @@ export async function sendViaWebSocket({
|
|
|
1312
1359
|
body: requestBody,
|
|
1313
1360
|
traceProvider,
|
|
1314
1361
|
});
|
|
1362
|
+
if (cacheContinuityResetReason === 'input_prefix_mismatch' && !requestInputMismatch) {
|
|
1363
|
+
requestInputMismatch = _requestInputMismatchDiagnostics(
|
|
1364
|
+
requestBody?.input,
|
|
1365
|
+
entry?.lastRequestInput,
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1315
1368
|
if (result.responseId && keepResponseChain) {
|
|
1316
1369
|
entry.lastResponseId = result.responseId;
|
|
1317
1370
|
entry.lastRequestSansInput = _stableStringify(_sansInput(requestBody, {
|
|
@@ -1431,6 +1484,23 @@ export async function sendViaWebSocket({
|
|
|
1431
1484
|
});
|
|
1432
1485
|
} catch {}
|
|
1433
1486
|
}
|
|
1487
|
+
if (cacheObservation.actualMiss) {
|
|
1488
|
+
traceCacheBreak({
|
|
1489
|
+
sessionId: poolKey,
|
|
1490
|
+
iteration,
|
|
1491
|
+
classification: 'provider_miss',
|
|
1492
|
+
reason: cacheObservation.missReason || 'warm_session_cache_miss',
|
|
1493
|
+
source: 'provider_usage',
|
|
1494
|
+
provider: traceProvider,
|
|
1495
|
+
model: liveModel,
|
|
1496
|
+
transport: 'websocket',
|
|
1497
|
+
cachedTokens: cacheObservation.cachedTokens,
|
|
1498
|
+
promptTokens: cacheObservation.promptTokens,
|
|
1499
|
+
uncachedTokens: cacheObservation.uncachedTokens,
|
|
1500
|
+
cacheRatio: cacheObservation.cacheRatio,
|
|
1501
|
+
actualCacheMiss: true,
|
|
1502
|
+
}, { traceFn: _agentTraceFn });
|
|
1503
|
+
}
|
|
1434
1504
|
// Rebase after a genuine provider retreat so one eviction produces one
|
|
1435
1505
|
// diagnostic instead of a long run of duplicate "dropped" rows. The
|
|
1436
1506
|
// request that exposed the retreat has already rebuilt the prefix; its
|
|
@@ -1494,7 +1564,7 @@ export async function sendViaWebSocket({
|
|
|
1494
1564
|
ws_client_metadata_has_turn_state: wireFrameHadTurnState,
|
|
1495
1565
|
ws_entry_turn_state_available: useCodexWsClientMetadata && !!entry.turnState,
|
|
1496
1566
|
// Fingerprint only. The token is a routing credential, so it is
|
|
1497
|
-
// hashed
|
|
1567
|
+
// hashed rather than logged; the length still
|
|
1498
1568
|
// distinguishes a real server token from a stub value, and the
|
|
1499
1569
|
// hash shows whether one session keeps a single pin or is
|
|
1500
1570
|
// re-issued (reconnect / turn rollover).
|
|
@@ -1505,6 +1575,7 @@ export async function sendViaWebSocket({
|
|
|
1505
1575
|
chain_stripped_response_items: strippedResponseItems,
|
|
1506
1576
|
chain_skipped_response_items: skippedResponseItems,
|
|
1507
1577
|
...(responseOutputMismatch || {}),
|
|
1578
|
+
...(requestInputMismatch || {}),
|
|
1508
1579
|
chain_response_items: Array.isArray(result.responseItems) ? result.responseItems.length : 0,
|
|
1509
1580
|
body_input_items: Array.isArray(requestBody.input) ? requestBody.input.length : null,
|
|
1510
1581
|
frame_input_items: Array.isArray(frame.input) ? frame.input.length : null,
|
|
@@ -1532,10 +1603,29 @@ export async function sendViaWebSocket({
|
|
|
1532
1603
|
const intentionalTransition = typeof sendOpts?.cacheBreakIntent === 'string'
|
|
1533
1604
|
? sendOpts.cacheBreakIntent
|
|
1534
1605
|
: null;
|
|
1606
|
+
traceCacheBreak({
|
|
1607
|
+
sessionId: poolKey,
|
|
1608
|
+
iteration,
|
|
1609
|
+
classification: intentionalTransition ? 'intentional' : 'provider_transition',
|
|
1610
|
+
reason: mode === 'delta' ? deltaReason : (deltaReason || 'full_frame'),
|
|
1611
|
+
source: 'openai_ws_delta',
|
|
1612
|
+
provider: traceProvider,
|
|
1613
|
+
model: liveModel,
|
|
1614
|
+
transport: 'websocket',
|
|
1615
|
+
intentionalTransition,
|
|
1616
|
+
cachedTokens: cacheObservation.cachedTokens,
|
|
1617
|
+
promptTokens: cacheObservation.promptTokens,
|
|
1618
|
+
uncachedTokens: cacheObservation.uncachedTokens,
|
|
1619
|
+
cacheRatio: cacheObservation.cacheRatio,
|
|
1620
|
+
actualCacheMiss: cacheObservation.actualMiss,
|
|
1621
|
+
...(requestInputMismatch || {}),
|
|
1622
|
+
}, { traceFn: null });
|
|
1535
1623
|
_agentTraceFn({
|
|
1536
1624
|
sessionId: poolKey,
|
|
1537
1625
|
iteration,
|
|
1538
1626
|
kind: 'cache_break',
|
|
1627
|
+
classification: intentionalTransition ? 'intentional' : 'provider_transition',
|
|
1628
|
+
source: 'openai_ws_delta',
|
|
1539
1629
|
provider: traceProvider,
|
|
1540
1630
|
model: liveModel,
|
|
1541
1631
|
payload: {
|
|
@@ -1544,6 +1634,8 @@ export async function sendViaWebSocket({
|
|
|
1544
1634
|
transport: 'websocket',
|
|
1545
1635
|
ws_mode: mode,
|
|
1546
1636
|
reason: mode === 'delta' ? deltaReason : (deltaReason || 'full_frame'),
|
|
1637
|
+
classification: intentionalTransition ? 'intentional' : 'provider_transition',
|
|
1638
|
+
source: 'openai_ws_delta',
|
|
1547
1639
|
intentional_transition: intentionalTransition,
|
|
1548
1640
|
request_tool_choice: requestBody.tool_choice ?? null,
|
|
1549
1641
|
cache_key_hash: transportCacheKeyHash,
|
|
@@ -1556,6 +1648,7 @@ export async function sendViaWebSocket({
|
|
|
1556
1648
|
chain_stripped_response_items: strippedResponseItems,
|
|
1557
1649
|
chain_skipped_response_items: skippedResponseItems,
|
|
1558
1650
|
...(responseOutputMismatch || {}),
|
|
1651
|
+
...(requestInputMismatch || {}),
|
|
1559
1652
|
chain_response_items: Array.isArray(result.responseItems) ? result.responseItems.length : 0,
|
|
1560
1653
|
body_input_items: Array.isArray(requestBody.input) ? requestBody.input.length : null,
|
|
1561
1654
|
frame_input_items: Array.isArray(frame.input) ? frame.input.length : null,
|
|
@@ -1584,7 +1677,7 @@ export async function sendViaWebSocket({
|
|
|
1584
1677
|
// Leave a breadcrumb on the result so downstream callers can observe
|
|
1585
1678
|
// that a retry was used (0 = first-try success, up to 2 for ws_1006/1011).
|
|
1586
1679
|
try { Object.defineProperty(out, '__midstreamRetries', { value: attemptIndex, enumerable: false }); } catch {}
|
|
1587
|
-
emitSendSpan('ok');
|
|
1680
|
+
out.transportTiming = emitSendSpan('ok');
|
|
1588
1681
|
return out;
|
|
1589
1682
|
}
|
|
1590
1683
|
// Unreachable — the loop either returns or throws above.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const MAX_TURN_STATE_SCOPES = 4096;
|
|
2
|
+
const _turnStateByScope = new Map();
|
|
3
|
+
|
|
4
|
+
function _clean(value) {
|
|
5
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function _ensureCapacity(scopeKey) {
|
|
9
|
+
if (_turnStateByScope.has(scopeKey) || _turnStateByScope.size < MAX_TURN_STATE_SCOPES) return;
|
|
10
|
+
const oldest = _turnStateByScope.keys().next().value;
|
|
11
|
+
if (oldest !== undefined) _turnStateByScope.delete(oldest);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// A turn-state token belongs to one logical turn, not to the physical
|
|
15
|
+
// connection that happened to carry it. Activating a new turn clears the prior
|
|
16
|
+
// value even when the caller reuses the same WebSocket or HTTP session.
|
|
17
|
+
export function activateCodexTurnState(scopeKey, turnId, owner = null) {
|
|
18
|
+
const scope = _clean(scopeKey);
|
|
19
|
+
const turn = _clean(turnId);
|
|
20
|
+
if (!scope || !turn) return null;
|
|
21
|
+
const current = _turnStateByScope.get(scope);
|
|
22
|
+
if (current?.turnId === turn) {
|
|
23
|
+
// One physical WS connection owns the token while it is live. HTTP
|
|
24
|
+
// fallback has no WS owner and may read the same turn-scoped value.
|
|
25
|
+
if (owner && current.owner && current.owner !== owner) return null;
|
|
26
|
+
if (owner && !current.owner) current.owner = owner;
|
|
27
|
+
return current.value || null;
|
|
28
|
+
}
|
|
29
|
+
_ensureCapacity(scope);
|
|
30
|
+
_turnStateByScope.set(scope, { turnId: turn, value: null, owner: owner || null });
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// The first server value wins for the whole logical turn.
|
|
35
|
+
export function captureCodexTurnState(scopeKey, turnId, value, owner = null) {
|
|
36
|
+
const scope = _clean(scopeKey);
|
|
37
|
+
const turn = _clean(turnId);
|
|
38
|
+
const token = _clean(value);
|
|
39
|
+
if (!scope || !turn || !token) return null;
|
|
40
|
+
activateCodexTurnState(scope, turn, owner);
|
|
41
|
+
const current = _turnStateByScope.get(scope);
|
|
42
|
+
if (!current || current.turnId !== turn) return null;
|
|
43
|
+
if (owner && current.owner && current.owner !== owner) return null;
|
|
44
|
+
if (owner && !current.owner) current.owner = owner;
|
|
45
|
+
if (current.value) return current.value;
|
|
46
|
+
current.value = token;
|
|
47
|
+
return token;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// A replacement connection may adopt the turn token only after the physical
|
|
51
|
+
// connection that owned it has actually closed.
|
|
52
|
+
export function retireCodexTurnStateOwner(scopeKey, owner) {
|
|
53
|
+
const scope = _clean(scopeKey);
|
|
54
|
+
if (!scope || !owner) return;
|
|
55
|
+
const current = _turnStateByScope.get(scope);
|
|
56
|
+
if (current?.owner === owner) current.owner = null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function clearCodexTurnStateScope(scopeKey) {
|
|
60
|
+
const scope = _clean(scopeKey);
|
|
61
|
+
if (scope) _turnStateByScope.delete(scope);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function clearAllCodexTurnStates() {
|
|
65
|
+
_turnStateByScope.clear();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const _clearCodexTurnStatesForTest = clearAllCodexTurnStates;
|
|
@@ -196,6 +196,31 @@ function _normalizedResponseItemDiagnosticHash(item) {
|
|
|
196
196
|
.digest('hex');
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
export function _requestInputMismatchDiagnostics(currentInput, previousInput) {
|
|
200
|
+
const current = Array.isArray(currentInput) ? currentInput : [];
|
|
201
|
+
const previous = Array.isArray(previousInput) ? previousInput : [];
|
|
202
|
+
let index = Math.min(current.length, previous.length);
|
|
203
|
+
const limit = Math.min(current.length, previous.length);
|
|
204
|
+
for (let cursor = 0; cursor < limit; cursor += 1) {
|
|
205
|
+
if (_requestInputItemsMatch(current[cursor], previous[cursor])) continue;
|
|
206
|
+
index = cursor;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
const expected = previous[index];
|
|
210
|
+
const actual = current[index];
|
|
211
|
+
return {
|
|
212
|
+
inputPrefixMismatchIndex: index,
|
|
213
|
+
inputPrefixMismatchPreviousCount: previous.length,
|
|
214
|
+
inputPrefixMismatchCurrentCount: current.length,
|
|
215
|
+
inputPrefixMismatchExpectedType: expected?.type
|
|
216
|
+
|| (expected?.role === 'assistant' ? 'message' : (expected ? 'unknown' : 'missing')),
|
|
217
|
+
inputPrefixMismatchExpectedHash: _normalizedResponseItemDiagnosticHash(expected),
|
|
218
|
+
inputPrefixMismatchActualType: actual?.type
|
|
219
|
+
|| (actual?.role === 'assistant' ? 'message' : (actual ? 'unknown' : 'missing')),
|
|
220
|
+
inputPrefixMismatchActualHash: _normalizedResponseItemDiagnosticHash(actual),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
199
224
|
function _responseOutputMismatchDiagnostics(inputItem, responseItem, replayItemCount, responseItemCount) {
|
|
200
225
|
return {
|
|
201
226
|
response_output_mismatch_expected_type: responseItem?.type || 'unknown',
|
|
@@ -312,16 +337,11 @@ export function _buildResponseCreateFrame(body, { previousResponseId = null, inp
|
|
|
312
337
|
|
|
313
338
|
export function _computeDelta({ entry, body, traceProvider }) {
|
|
314
339
|
// DEFAULT: full-frame sends. codex's delta path is only cache-safe with
|
|
315
|
-
// the x-codex-turn-state sticky-routing token.
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
// arrives on the handshake 101 response instead (2026-08-21 bench: 81 of 88
|
|
321
|
-
// calls carried it from iteration 1, before any response event existed), so
|
|
322
|
-
// the pool captures it at upgrade and the frame metadata replays it.
|
|
323
|
-
// Without the sticky token, previous_response_id requests hop cache nodes
|
|
324
|
-
// and only the first prefix blocks hit.
|
|
340
|
+
// the x-codex-turn-state sticky-routing token. The token is request-scoped:
|
|
341
|
+
// response metadata establishes it, later response.create frames replay it
|
|
342
|
+
// for the same logical turn, and a new turn starts empty even when it reuses
|
|
343
|
+
// the physical connection. Handshake state is deliberately ignored because
|
|
344
|
+
// a connection can outlive the turn that created it.
|
|
325
345
|
// Delta gating flows through the single transport-policy switch
|
|
326
346
|
// (MIXDOG_OAI_TRANSPORT: ws-full | ws-delta | http-sse). Default ws-delta
|
|
327
347
|
// selects the refs-compatible safe delta; 'ws-full'/'http-sse' force full
|
|
@@ -379,7 +399,12 @@ export function _computeDelta({ entry, body, traceProvider }) {
|
|
|
379
399
|
const curInput = Array.isArray(body.input) ? body.input : [];
|
|
380
400
|
const afterPreviousInput = _stripRequestPrefix(curInput, entry.lastRequestInput);
|
|
381
401
|
if (!afterPreviousInput) {
|
|
382
|
-
return {
|
|
402
|
+
return {
|
|
403
|
+
mode: 'full',
|
|
404
|
+
reason: 'input_prefix_mismatch',
|
|
405
|
+
requestInputMismatch: _requestInputMismatchDiagnostics(curInput, entry.lastRequestInput),
|
|
406
|
+
frame: buildFrame(body),
|
|
407
|
+
};
|
|
383
408
|
}
|
|
384
409
|
const stripped = _stripResponseItemsFromHead(afterPreviousInput, entry.lastResponseItems);
|
|
385
410
|
if (!stripped.ok) {
|