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.
Files changed (300) hide show
  1. package/LICENSES/browser-import-NOTICE.txt +11 -0
  2. package/NOTICE.md +11 -0
  3. package/README.md +8 -10
  4. package/package.json +19 -3
  5. package/scripts/computer-task-cost-report.mjs +144 -0
  6. package/scripts/run-office-live-tests.mjs +32 -0
  7. package/src/agents/front-worker/AGENT.md +13 -0
  8. package/src/agents/front-worker/agent.json +6 -0
  9. package/src/agents/heavy-worker/AGENT.md +10 -11
  10. package/src/agents/heavy-worker/agent.json +1 -1
  11. package/src/agents/maintainer/AGENT.md +0 -2
  12. package/src/agents/reviewer/AGENT.md +9 -13
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/agents/security/AGENT.md +14 -0
  15. package/src/agents/security/agent.json +6 -0
  16. package/src/agents/worker/AGENT.md +8 -8
  17. package/src/agents/worker/agent.json +1 -1
  18. package/src/agents/writer/AGENT.md +14 -0
  19. package/src/agents/writer/agent.json +6 -0
  20. package/src/rules/agent/00-common.md +4 -7
  21. package/src/rules/lead/01-general.md +0 -3
  22. package/src/rules/shared/10-tool-workflow.md +10 -2
  23. package/src/rules/shared/60-verification.md +2 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
  25. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
  26. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
  27. package/src/runtime/agent/orchestrator/config.mjs +7 -2
  28. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
  29. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -1
  30. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
  31. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
  32. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +25 -2
  33. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -10
  34. package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
  35. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
  36. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +140 -86
  37. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -13
  38. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -4
  39. package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
  40. package/src/runtime/agent/orchestrator/session/compact/handoff.mjs +0 -22
  41. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +11 -23
  43. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +28 -269
  44. package/src/runtime/agent/orchestrator/session/compact.mjs +0 -6
  45. package/src/runtime/agent/orchestrator/session/context-utils.mjs +86 -11
  46. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
  47. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
  48. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
  49. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
  50. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +80 -18
  51. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
  52. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +3 -0
  53. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
  54. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +80 -21
  55. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +33 -72
  56. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +21 -2
  57. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -51
  58. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
  59. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
  60. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +39 -15
  61. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +14 -4
  62. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +41 -8
  63. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
  64. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +2 -2
  65. package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
  66. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
  67. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
  68. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
  69. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
  70. package/src/runtime/agent/orchestrator/session/store.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
  72. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
  73. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
  74. package/src/runtime/attachments/pdf-extract.mjs +3 -0
  75. package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
  76. package/src/runtime/browser-bridge/action-schema.mjs +251 -0
  77. package/src/runtime/browser-bridge/client.mjs +75 -26
  78. package/src/runtime/browser-bridge/tool-defs.mjs +132 -15
  79. package/src/runtime/computer-bridge/action-schema.mjs +653 -0
  80. package/src/runtime/computer-bridge/client.mjs +211 -28
  81. package/src/runtime/computer-bridge/tool-defs.mjs +25 -45
  82. package/src/runtime/office/assurance-benchmark.mjs +358 -0
  83. package/src/runtime/office/assurance.mjs +833 -0
  84. package/src/runtime/office/benchmark.mjs +496 -0
  85. package/src/runtime/office/capabilities.mjs +551 -0
  86. package/src/runtime/office/com-adapter.mjs +283 -0
  87. package/src/runtime/office/content-model.mjs +183 -0
  88. package/src/runtime/office/contract-benchmark.mjs +105 -0
  89. package/src/runtime/office/design-library.mjs +1562 -0
  90. package/src/runtime/office/design-system.mjs +1966 -0
  91. package/src/runtime/office/index.mjs +2413 -0
  92. package/src/runtime/office/journal.mjs +82 -0
  93. package/src/runtime/office/office-com-host.ps1 +3781 -0
  94. package/src/runtime/office/office-com-session-host.ps1 +855 -0
  95. package/src/runtime/office/ooxml-validator.mjs +271 -0
  96. package/src/runtime/office/pagination.mjs +132 -0
  97. package/src/runtime/office/pdf-adapter.mjs +591 -0
  98. package/src/runtime/office/pdf-analysis.mjs +372 -0
  99. package/src/runtime/office/pdf-render.mjs +167 -0
  100. package/src/runtime/office/pdf-security.mjs +63 -0
  101. package/src/runtime/office/polish-benchmark.mjs +190 -0
  102. package/src/runtime/office/portable-ooxml.mjs +1867 -0
  103. package/src/runtime/office/quality-live-benchmark.mjs +486 -0
  104. package/src/runtime/office/quality-pipeline.mjs +136 -0
  105. package/src/runtime/office/tabular.mjs +302 -0
  106. package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
  107. package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
  108. package/src/runtime/office/tool-defs.mjs +113 -0
  109. package/src/runtime/office/visual-diff.mjs +89 -0
  110. package/src/runtime/office/xlsx-assertions.mjs +172 -0
  111. package/src/runtime/office/xlsx-contract.mjs +147 -0
  112. package/src/runtime/shared/agent-route-config.mjs +9 -0
  113. package/src/runtime/shared/tool-execution-contract.mjs +16 -0
  114. package/src/runtime/shared/tool-surface.mjs +9 -0
  115. package/src/session-runtime/context-status.mjs +9 -3
  116. package/src/session-runtime/deferred-tool-delta.mjs +104 -0
  117. package/src/session-runtime/goal-runtime.mjs +617 -197
  118. package/src/session-runtime/lifecycle-api.mjs +50 -8
  119. package/src/session-runtime/provider-request-snapshot.mjs +33 -9
  120. package/src/session-runtime/resource-api.mjs +31 -94
  121. package/src/session-runtime/runtime-core.mjs +78 -26
  122. package/src/session-runtime/session-lifecycle.mjs +34 -12
  123. package/src/session-runtime/session-title.mjs +2 -2
  124. package/src/session-runtime/session-turn-api.mjs +15 -14
  125. package/src/session-runtime/tool-catalog-data.mjs +1 -1
  126. package/src/session-runtime/tool-catalog.mjs +38 -99
  127. package/src/session-runtime/tool-defs.mjs +6 -1
  128. package/src/session-runtime/tool-surface.mjs +18 -8
  129. package/src/session-runtime/workflow-agents-api.mjs +8 -25
  130. package/src/session-runtime/workflow.mjs +8 -6
  131. package/src/standalone/agent-tool/helpers.mjs +2 -2
  132. package/src/standalone/agent-tool/job-views.mjs +3 -1
  133. package/src/standalone/agent-tool/notify.mjs +5 -1
  134. package/src/standalone/agent-tool/render.mjs +3 -0
  135. package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
  136. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  137. package/src/standalone/agent-tool/worker-index.mjs +123 -0
  138. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  139. package/src/standalone/agent-tool.mjs +57 -10
  140. package/src/standalone/daemon.mjs +105 -6
  141. package/src/standalone/session-runtime-host.mjs +61 -48
  142. package/src/standalone/session-runtime-inline-host.mjs +34 -127
  143. package/src/standalone/session-runtime-worker.mjs +20 -198
  144. package/src/standalone/session-service.mjs +632 -7
  145. package/src/tui/dist/index.mjs +24 -1
  146. package/src/tui/lib/voice-setup.mjs +4 -1
  147. package/src/tui/session/context-state.mjs +2 -13
  148. package/src/tui/session/goal-continuation.mjs +38 -7
  149. package/src/tui/session/session-api-ext.mjs +9 -0
  150. package/src/tui/session/session-api.mjs +15 -1
  151. package/src/tui/session/session-flow.mjs +25 -1
  152. package/src/tui/session/turn.mjs +88 -16
  153. package/src/tui/session-local.mjs +25 -3
  154. package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
  155. package/scripts/generate-voice-runtime-manifest.test.mjs +0 -135
  156. package/scripts/native-binary-arch.test.mjs +0 -111
  157. package/scripts/patch-replay.test.mjs +0 -94
  158. package/src/headless-exec.test.mjs +0 -539
  159. package/src/runtime/agent/orchestrator/agent-trace-timing.test.mjs +0 -57
  160. package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +0 -153
  161. package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +0 -139
  162. package/src/runtime/agent/orchestrator/mcp/features.test.mjs +0 -66
  163. package/src/runtime/agent/orchestrator/mcp/security.test.mjs +0 -80
  164. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +0 -90
  165. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +0 -215
  166. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +0 -165
  167. package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +0 -142
  168. package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -153
  169. package/src/runtime/agent/orchestrator/providers/openai-codex-model.test.mjs +0 -24
  170. package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +0 -190
  171. package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -235
  172. package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
  173. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
  174. package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -176
  175. package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -161
  176. package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -115
  177. package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
  178. package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -245
  179. package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
  180. package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +0 -325
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +0 -70
  182. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -457
  183. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +0 -117
  184. package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +0 -87
  185. package/src/runtime/agent/orchestrator/session/store/serialize.test.mjs +0 -60
  186. package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +0 -583
  187. package/src/runtime/agent/orchestrator/session/task-wait-control.test.mjs +0 -78
  188. package/src/runtime/agent/orchestrator/tools/builtin/absence-absorption.test.mjs +0 -43
  189. package/src/runtime/agent/orchestrator/tools/builtin/absolute-glob-expand.test.mjs +0 -63
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +0 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +0 -224
  192. package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +0 -224
  193. package/src/runtime/agent/orchestrator/tools/builtin/enoent-outside-project.test.mjs +0 -56
  194. package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +0 -103
  195. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +0 -30
  196. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +0 -390
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +0 -83
  198. package/src/runtime/agent/orchestrator/tools/builtin/grep-output-budget.test.mjs +0 -31
  199. package/src/runtime/agent/orchestrator/tools/builtin/grep-single-file-rescue.test.mjs +0 -106
  200. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.test.mjs +0 -102
  201. package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +0 -152
  202. package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +0 -24
  203. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +0 -24
  204. package/src/runtime/agent/orchestrator/tools/builtin/noise-dir-visibility.test.mjs +0 -69
  205. package/src/runtime/agent/orchestrator/tools/builtin/read-glob-survey.test.mjs +0 -71
  206. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.test.mjs +0 -56
  207. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.test.mjs +0 -16
  208. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-top-k.test.mjs +0 -39
  209. package/src/runtime/agent/orchestrator/tools/builtin/write-symlink.test.mjs +0 -153
  210. package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-anchor-relocation.test.mjs +0 -55
  211. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +0 -88
  212. package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +0 -72
  213. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +0 -54
  214. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +0 -20
  215. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +0 -55
  216. package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +0 -268
  217. package/src/runtime/agent/orchestrator/tools/env-scrub.test.mjs +0 -57
  218. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.test.mjs +0 -19
  219. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +0 -104
  220. package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +0 -661
  221. package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +0 -552
  222. package/src/runtime/agent/orchestrator/tools/patch/patch-symlink.test.mjs +0 -141
  223. package/src/runtime/agent/orchestrator/tools/patch/v4a-pure-move.test.mjs +0 -57
  224. package/src/runtime/agent/orchestrator/tools/patch/v4a-section-coalesce.test.mjs +0 -81
  225. package/src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.test.mjs +0 -52
  226. package/src/runtime/attachments/store.test.mjs +0 -209
  227. package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +0 -17
  228. package/src/runtime/channels/lib/webhook/deliveries.test.mjs +0 -18
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.test.mjs +0 -68
  230. package/src/runtime/media/adapters/codex-image.test.mjs +0 -22
  231. package/src/runtime/media/download.test.mjs +0 -52
  232. package/src/runtime/media/renditions.test.mjs +0 -99
  233. package/src/runtime/media/store.test.mjs +0 -159
  234. package/src/runtime/memory/lib/archive-security.test.mjs +0 -128
  235. package/src/runtime/memory/lib/embedding-model-config.test.mjs +0 -33
  236. package/src/runtime/memory/lib/http-wire.test.mjs +0 -15
  237. package/src/runtime/memory/lib/memory-cycle-packets.test.mjs +0 -49
  238. package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +0 -78
  239. package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +0 -50
  240. package/src/runtime/memory/lib/pg/process.test.mjs +0 -36
  241. package/src/runtime/memory/lib/trace-store.test.mjs +0 -57
  242. package/src/runtime/memory/lib/transcript-ingest.test.mjs +0 -52
  243. package/src/runtime/shared/background-tasks.test.mjs +0 -78
  244. package/src/runtime/shared/bounded-download.test.mjs +0 -41
  245. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -25
  246. package/src/runtime/shared/child-spawn-remote.test.mjs +0 -124
  247. package/src/runtime/shared/remote-intent.test.mjs +0 -40
  248. package/src/runtime/shared/resource-admission.test.mjs +0 -43
  249. package/src/runtime/shared/runtime-root.test.mjs +0 -34
  250. package/src/runtime/shared/schedule-time.test.mjs +0 -29
  251. package/src/runtime/shared/session-runtime-health.test.mjs +0 -83
  252. package/src/runtime/shared/skill-document.test.mjs +0 -81
  253. package/src/runtime/shared/tool-surface.test.mjs +0 -87
  254. package/src/runtime/shared/update-checker.test.mjs +0 -33
  255. package/src/runtime/shared/user-cwd.test.mjs +0 -43
  256. package/src/runtime/shared/webhook-session-run.test.mjs +0 -14
  257. package/src/session-runtime/agent-disable.test.mjs +0 -123
  258. package/src/session-runtime/cwd-plugins.test.mjs +0 -75
  259. package/src/session-runtime/cwd-tool-routing.test.mjs +0 -61
  260. package/src/session-runtime/global-extensions.test.mjs +0 -28
  261. package/src/session-runtime/goal-runtime.test.mjs +0 -108
  262. package/src/session-runtime/lifecycle-api.test.mjs +0 -21
  263. package/src/session-runtime/model-settings-persist.test.mjs +0 -105
  264. package/src/session-runtime/notification-bus.test.mjs +0 -148
  265. package/src/session-runtime/plugin-mcp-standard.test.mjs +0 -177
  266. package/src/session-runtime/prewarm.test.mjs +0 -52
  267. package/src/session-runtime/provider-request-snapshot.test.mjs +0 -37
  268. package/src/session-runtime/resource-api-global.test.mjs +0 -69
  269. package/src/session-runtime/session-lifecycle.test.mjs +0 -125
  270. package/src/session-runtime/session-title.test.mjs +0 -36
  271. package/src/session-runtime/skills-api.test.mjs +0 -99
  272. package/src/session-runtime/tool-policy-surface.test.mjs +0 -331
  273. package/src/standalone/agent-tool/helpers.test.mjs +0 -62
  274. package/src/standalone/agent-tool/notify.test.mjs +0 -37
  275. package/src/standalone/agent-tool/spawn-flow.test.mjs +0 -63
  276. package/src/standalone/channel-restart.test.mjs +0 -145
  277. package/src/standalone/channel-session-router.test.mjs +0 -37
  278. package/src/standalone/channel-worker-heartbeat.test.mjs +0 -30
  279. package/src/standalone/hook-bus/handlers.test.mjs +0 -88
  280. package/src/standalone/plugin-admin.test.mjs +0 -173
  281. package/src/standalone/session-runtime-agent-control-client.test.mjs +0 -71
  282. package/src/standalone/session-runtime-dispatch-cancel.test.mjs +0 -139
  283. package/src/standalone/session-runtime-host-factory.test.mjs +0 -31
  284. package/src/standalone/session-runtime-host-health.test.mjs +0 -160
  285. package/src/standalone/session-runtime-inline-host.test.mjs +0 -83
  286. package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
  287. package/src/standalone/session-runtime-shard-host.test.mjs +0 -569
  288. package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
  289. package/src/tui/app/live-spinner-visibility.test.mjs +0 -44
  290. package/src/tui/app/panel-epoch.test.mjs +0 -1009
  291. package/src/tui/app/panel-handoff.test.mjs +0 -311
  292. package/src/tui/app/prompt-submit.test.mjs +0 -206
  293. package/src/tui/session/completion-card-restore.test.mjs +0 -111
  294. package/src/tui/session/goal-continuation.test.mjs +0 -84
  295. package/src/tui/session/oauth-flows.test.mjs +0 -134
  296. package/src/tui/session/queue-helpers.test.mjs +0 -46
  297. package/src/tui/session/session-action-surface.test.mjs +0 -108
  298. package/src/tui/session/task-wait-submit.test.mjs +0 -80
  299. package/src/tui/session/tool-result-status.test.mjs +0 -131
  300. 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({ auth, cacheKey, statelessConversation = false }) {
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 headers = _buildOpenAIHttpFallbackHeaders({ auth, cacheKey, statelessConversation });
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 { _buildResponseCreateFrame } from './openai-ws-delta.mjs';
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) return;
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, socket, and turn-state for the later real send.
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
- emitSendSpan('ok');
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 like every other handshake secret; the length still
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. R11-R14 (2026-07-03)
316
- // measured zero turn-state EVENTS across 200+ calls despite
317
- // UA/version/beta-features/client_metadata parity, and delta at 18-28% warm
318
- // miss vs full-frame 0.0%. The event channel is still silent, but the
319
- // conclusion "the backend never issues it to us" was wrong: the token
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 { mode: 'full', reason: 'input_prefix_mismatch', frame: buildFrame(body) };
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) {