mixdog 1.2.1 → 1.2.3

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 (557) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/scripts/provider-pingpong-live.mjs +509 -0
  6. package/src/agents/advisor/AGENT.md +1 -1
  7. package/src/agents/advisor/agent.json +1 -1
  8. package/src/agents/reviewer/AGENT.md +3 -3
  9. package/src/agents/reviewer/agent.json +1 -1
  10. package/src/defaults/shipped-history.json +6 -0
  11. package/src/defaults/skills/setup/SKILL.md +6 -1
  12. package/src/defaults/skills/setup/references/actions.md +44 -22
  13. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  14. package/src/headless-exec.mjs +15 -8
  15. package/src/lib/keychain-cjs.cjs +7 -4
  16. package/src/output-styles/common.md +11 -10
  17. package/src/repl/slash-commands.mjs +16 -1
  18. package/src/rules/agent/AGENT.md +5 -5
  19. package/src/rules/lead/LEAD.md +5 -5
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  22. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  23. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  33. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  36. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  37. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  38. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  39. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  40. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  41. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  44. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  45. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  47. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
  49. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
  50. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  51. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
  58. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  63. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  65. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
  70. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  71. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  72. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
  73. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
  74. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
  75. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  76. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
  77. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  79. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  80. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
  81. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  83. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
  84. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  85. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  86. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
  87. package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
  88. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  89. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  90. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  91. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  92. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  93. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
  94. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
  96. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
  97. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  99. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  100. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  102. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
  103. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
  105. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
  106. package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
  107. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  109. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
  110. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  113. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  114. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  115. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  116. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  117. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  118. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  120. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  121. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  122. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  123. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  125. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  126. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  127. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  128. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  129. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  138. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  140. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  141. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  142. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
  143. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
  144. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  145. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  147. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
  148. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  150. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
  151. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
  153. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
  154. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
  155. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
  156. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
  157. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  158. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  159. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  160. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
  161. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  163. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
  164. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
  166. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  167. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  168. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  169. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  170. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
  171. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
  172. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  173. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
  174. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
  175. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  176. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  177. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  178. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  179. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  180. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  181. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  182. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  183. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  184. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  185. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  186. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  187. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  188. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
  189. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  190. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  191. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  192. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
  193. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  195. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
  196. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  197. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  198. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  199. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  200. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  201. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  202. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  203. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  204. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  205. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  206. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  207. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  208. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  210. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  211. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  212. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  213. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  214. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  215. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  216. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  217. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  218. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  219. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  220. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  221. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  222. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
  223. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  224. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  225. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  226. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  227. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  228. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  229. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  230. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  231. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  232. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  233. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  234. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  235. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  236. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  237. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  238. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  239. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  240. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
  241. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  242. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  243. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  244. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  245. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  246. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  248. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  249. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  250. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  251. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
  252. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  253. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  254. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  255. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  256. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  257. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  258. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
  259. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  260. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  261. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  262. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  263. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  264. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  265. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  266. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  267. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
  268. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  269. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  270. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  271. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  272. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  273. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  274. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  275. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  276. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  277. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  278. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  279. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  280. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  281. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
  282. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
  283. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
  284. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  285. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  286. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  287. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  288. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  289. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  290. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  291. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  292. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  293. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  294. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  295. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  296. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  297. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  298. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  299. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  300. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  301. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  302. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  303. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  304. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  305. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  306. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  307. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  308. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  309. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  310. package/src/runtime/github/tool.mjs +92 -2
  311. package/src/runtime/memory/index.mjs +1 -0
  312. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  313. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  314. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  315. package/src/runtime/memory/lib/pg/process.mjs +65 -50
  316. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  317. package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
  318. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  319. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  320. package/src/runtime/office/README.md +2 -2
  321. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  322. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  323. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  324. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  325. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  326. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  327. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  328. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  329. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  330. package/src/runtime/office/core/office-sessions.mjs +5 -0
  331. package/src/runtime/office/core/office-transactions.mjs +3 -0
  332. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  333. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  334. package/src/runtime/office/index.mjs +30 -0
  335. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  336. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  337. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  338. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  339. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  340. package/src/runtime/office/shared/values.mjs +0 -5
  341. package/src/runtime/shared/background-tasks.mjs +92 -14
  342. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  343. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  344. package/src/runtime/shared/buffered-appender.mjs +18 -0
  345. package/src/runtime/shared/config.mjs +78 -18
  346. package/src/runtime/shared/err-text.mjs +31 -0
  347. package/src/runtime/shared/error-presentation.mjs +159 -19
  348. package/src/runtime/shared/file-permissions.d.mts +6 -1
  349. package/src/runtime/shared/file-permissions.mjs +54 -6
  350. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  351. package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
  352. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  353. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  354. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  355. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  356. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  357. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  358. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  359. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  360. package/src/runtime/shared/loopback-listener.mjs +9 -0
  361. package/src/runtime/shared/model-display.mjs +13 -0
  362. package/src/runtime/shared/provider-api-key.mjs +2 -0
  363. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  364. package/src/runtime/shared/review-diff.mjs +52 -0
  365. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  366. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  367. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  368. package/src/runtime/shared/slow-span.mjs +114 -0
  369. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  370. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  371. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  372. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  373. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  374. package/src/runtime/shared/tool-card-model.mjs +5 -2
  375. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  376. package/src/runtime/shared/tool-primitives.mjs +13 -0
  377. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  378. package/src/runtime/shared/tool-surface.mjs +18 -3
  379. package/src/runtime/shared/tool-work-units.mjs +40 -4
  380. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  381. package/src/runtime/shared/transcript-writer.mjs +10 -2
  382. package/src/runtime/shared/turn-changes.mjs +670 -274
  383. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  384. package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
  385. package/src/runtime/shared/turn-snapshot.mjs +986 -690
  386. package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
  387. package/src/runtime/shared/user-data-guard.mjs +262 -49
  388. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  389. package/src/runtime/shared/worktrees/contract.mjs +34 -24
  390. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  391. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  392. package/src/runtime/shared/worktrees/git.mjs +8 -3
  393. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  394. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  395. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  396. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  397. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  398. package/src/runtime/terminal-bridge/client.mjs +44 -11
  399. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  400. package/src/runtime/tidy/apply.mjs +47 -2
  401. package/src/runtime/tidy/run-engines.mjs +11 -5
  402. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  403. package/src/session-runtime/boot/apis.mjs +19 -5
  404. package/src/session-runtime/boot/facade.mjs +1 -0
  405. package/src/session-runtime/boot/infrastructure.mjs +12 -1
  406. package/src/session-runtime/boot/session.mjs +0 -1
  407. package/src/session-runtime/boot/tools.mjs +4 -1
  408. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  409. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  410. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  411. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  412. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  413. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  414. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  415. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  416. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
  417. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  418. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  419. package/src/session-runtime/lifecycle/teardown.mjs +73 -8
  420. package/src/session-runtime/lifecycle-api.mjs +1 -0
  421. package/src/session-runtime/mcp-glue/connect.mjs +46 -14
  422. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  423. package/src/session-runtime/mcp-glue.mjs +3 -0
  424. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  425. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  426. package/src/session-runtime/model-route/set-route.mjs +85 -17
  427. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  428. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  429. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  430. package/src/session-runtime/native-web-search.mjs +10 -1
  431. package/src/session-runtime/new-session-config.mjs +4 -2
  432. package/src/session-runtime/provider-auth/login.mjs +4 -0
  433. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  434. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  435. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  436. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  437. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  438. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  439. package/src/session-runtime/runtime-review-api.mjs +21 -2
  440. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  441. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  442. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  443. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  444. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  445. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  446. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  447. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  448. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  449. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  450. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  451. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  452. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  453. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  454. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  455. package/src/session-runtime/services/agent-tool.mjs +3 -0
  456. package/src/session-runtime/services/channel-admin.mjs +19 -3
  457. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  458. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  459. package/src/session-runtime/services/provider-admin.mjs +81 -15
  460. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  461. package/src/session-runtime/session-turn-api.mjs +14 -5
  462. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  463. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  464. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  465. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  466. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  467. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  468. package/src/session-runtime/skills-api.mjs +3 -5
  469. package/src/session-runtime/tool-surface.mjs +5 -1
  470. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  471. package/src/session-runtime/turn/session-ops.mjs +7 -5
  472. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  473. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  474. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  475. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  476. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  477. package/src/session-runtime/turn/turn-run.mjs +25 -9
  478. package/src/session-runtime/usage-stats-api.mjs +43 -11
  479. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  480. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  481. package/src/session-runtime/workspace/transitions.mjs +190 -15
  482. package/src/standalone/daemon-agent-control.mjs +9 -1
  483. package/src/standalone/daemon-drain-policy.mjs +8 -0
  484. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  485. package/src/standalone/daemon-owner.mjs +17 -4
  486. package/src/standalone/daemon-pg-stop.mjs +51 -0
  487. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  488. package/src/standalone/daemon-stop-budget.mjs +18 -0
  489. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  490. package/src/standalone/daemon-telemetry.mjs +74 -0
  491. package/src/standalone/daemon.mjs +64 -8
  492. package/src/standalone/desktop-service-registry.mjs +5 -0
  493. package/src/standalone/local-session-runtime.mjs +70 -19
  494. package/src/standalone/session-client.mjs +42 -0
  495. package/src/standalone/session-protocol.mjs +14 -0
  496. package/src/standalone/session-proxy/projection.mjs +41 -4
  497. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  498. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  499. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  500. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  501. package/src/standalone/session-service/agent-tree.mjs +12 -0
  502. package/src/standalone/session-service/quota-resume.mjs +8 -0
  503. package/src/standalone/session-service/retention/busy.mjs +15 -1
  504. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  505. package/src/standalone/session-service-api.mjs +11 -1
  506. package/src/standalone/session-service.mjs +1 -0
  507. package/src/standalone/session-state-patch.d.mts +2 -1
  508. package/src/standalone/session-state-patch.mjs +41 -1
  509. package/src/tui/App.jsx +25 -0
  510. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  511. package/src/tui/app/app-view.jsx +1 -0
  512. package/src/tui/app/btw-controller.mjs +118 -0
  513. package/src/tui/app/create-app-pickers.mjs +6 -0
  514. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  515. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  516. package/src/tui/app/extension-pickers.mjs +2 -1
  517. package/src/tui/app/model-options.mjs +11 -16
  518. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  519. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  520. package/src/tui/app/slash-commands.mjs +20 -5
  521. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  522. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  523. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  524. package/src/tui/app/use-global-key-input.mjs +5 -12
  525. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  526. package/src/tui/components/BtwPanel.jsx +53 -0
  527. package/src/tui/components/QuestionPanel.jsx +275 -0
  528. package/src/tui/components/StatusLine.jsx +2 -0
  529. package/src/tui/components/question-panel-model.mjs +156 -0
  530. package/src/tui/components/status-line/local-line.mjs +5 -2
  531. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  532. package/src/tui/dist/index.mjs +3221 -1500
  533. package/src/tui/session/context-state.mjs +4 -2
  534. package/src/tui/session/quota-resume.mjs +46 -14
  535. package/src/tui/session/session-api/commands.mjs +43 -24
  536. package/src/tui/session/session-api/controls.mjs +2 -2
  537. package/src/tui/session/session-api/extensions.mjs +22 -0
  538. package/src/tui/session/session-api/integrations.mjs +16 -2
  539. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  540. package/src/tui/session/session-api/lifecycle.mjs +7 -2
  541. package/src/tui/session/session-api/routes.mjs +71 -7
  542. package/src/tui/session/session-api/settings.mjs +11 -14
  543. package/src/tui/session/session-api.mjs +1 -0
  544. package/src/tui/session/session-flow/drain.mjs +7 -1
  545. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  546. package/src/tui/session/tool-approval.mjs +10 -7
  547. package/src/tui/session/transcript-spill.mjs +14 -7
  548. package/src/tui/session/turn-stream.mjs +174 -31
  549. package/src/tui/session/turn.mjs +46 -16
  550. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  551. package/src/ui/statusline-agents.mjs +15 -4
  552. package/src/ui/statusline.mjs +7 -3
  553. package/src/ui/streaming-markdown-heal.mjs +34 -3
  554. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  555. package/src/workflows/default/WORKFLOW.md +3 -1
  556. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  557. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -8,9 +8,11 @@ import { typedStatusFrom } from '../../retry-classifier.mjs';
8
8
  import {
9
9
  endTurnFromEvent,
10
10
  incompleteReasonFromEvent,
11
+ isIncompleteToolItem,
11
12
  isMaxOutputIncompleteReason,
12
13
  } from '../../lib/responses-terminal-fields.mjs';
13
- import { LABEL, toolInputPending } from '../response-state.mjs';
14
+ import { applyErrorContract, markContentFilter } from '../../openai-error-contract.mjs';
15
+ import { toolInputPending } from '../response-state.mjs';
14
16
  import { normalizeWsUsage } from '../../openai-ws-response-state/usage.mjs';
15
17
 
16
18
  // Typed status only — nothing is synthesized from text. The frame itself is
@@ -19,11 +21,14 @@ import { normalizeWsUsage } from '../../openai-ws-response-state/usage.mjs';
19
21
  function wireFailureError(message, event, detail, typedSources) {
20
22
  const err = new Error(message);
21
23
  err.responseFailed = event;
22
- const typed = typedStatusFrom(...typedSources);
23
- if (typed) err.httpStatus = typed;
24
24
  const code = detail?.code ?? detail?.type ?? null;
25
25
  if (typeof code === 'string' && code) err.providerErrorCode = code;
26
- return err;
26
+ return applyErrorContract(err, {
27
+ status: typedStatusFrom(...typedSources),
28
+ code: typeof code === 'string' ? code : undefined,
29
+ error: detail,
30
+ event,
31
+ });
27
32
  }
28
33
 
29
34
  export function createTerminalFrameEvents({ state, items, text, outcome, meaningful }) {
@@ -32,6 +37,13 @@ export function createTerminalFrameEvents({ state, items, text, outcome, meaning
32
37
  const absorbCompletedOutput = (output) => {
33
38
  let reported = false;
34
39
  for (const item of output) {
40
+ if (isIncompleteToolItem(item)) {
41
+ state.toolTracker.mark(item);
42
+ state.toolInFlight = true;
43
+ meaningful('tool');
44
+ reported = true;
45
+ continue;
46
+ }
35
47
  items.pushResponseItem(item);
36
48
  if (item.type === 'message') {
37
49
  if (text.absorbCompletedMessage(item)) reported = true;
@@ -89,7 +101,7 @@ export function createTerminalFrameEvents({ state, items, text, outcome, meaning
89
101
  absorbTerminalResponse(event.response || {});
90
102
  const reason = incompleteReasonFromEvent(event);
91
103
  if (!isMaxOutputIncompleteReason(reason)) {
92
- const err = new Error(`${LABEL} ${frame}: ${reason}`);
104
+ const err = markContentFilter(new Error(`${state.label} ${frame}: ${reason}`), reason);
93
105
  // Still billed: the provider-boundary accounting records reported usage.
94
106
  if (state.usage) {
95
107
  err.partialUsage = state.usage;
@@ -97,31 +109,47 @@ export function createTerminalFrameEvents({ state, items, text, outcome, meaning
97
109
  }
98
110
  throw err;
99
111
  }
112
+ for (const item of event.response?.output || []) {
113
+ if (isIncompleteToolItem(item)) {
114
+ state.toolTracker.mark(item);
115
+ state.toolInFlight = true;
116
+ }
117
+ }
100
118
  if (toolInputPending(state)) {
101
- const err = outcome.stampToolSafety(new Error(`${LABEL} ${frame} (max_output_tokens) with tool call in flight`));
119
+ // Same carried state as the WS path: text (leak-guard tail flushed),
120
+ // the calls already complete and dispatched, model and billed usage.
121
+ text.flushLeak();
122
+ const err = outcome.stampToolSafety(new Error(`${state.label} ${frame} (max_output_tokens) with tool call in flight`));
123
+ outcome.attachStreamPartial(err);
102
124
  err.streamStalled = true;
103
125
  err.pendingToolUse = true;
104
- err.partialContent = state.content;
105
- err.partialModel = state.model || undefined;
126
+ err.incompleteReason = reason;
106
127
  if (state.usage) err.partialUsage = state.usage;
107
128
  throw err;
108
129
  }
109
130
  state.completed = true;
110
131
  state.stopReason = 'length';
132
+ state.incompleteReason = reason;
111
133
  };
112
134
 
113
135
  const onDone = (event) => {
114
- if (!event.response || event.response.status === 'completed') {
115
- if (event.response) absorbTerminalResponse(event.response);
136
+ if (!event.response) {
116
137
  state.completed = true;
117
138
  // Terminal success frame for streams that never emit a separate
118
139
  // response.completed — same optional end_turn.
119
140
  setEndTurn(event);
141
+ } else if (event.response.status === 'completed') {
142
+ // Same terminal as response.completed: usage, end_turn and the output
143
+ // bundle reconciled through the same deduplicated collectors.
144
+ onCompleted(event);
120
145
  } else if (event.response.status === 'failed') {
121
146
  const msg = event.response?.error?.message || 'response.done failed';
122
- const err = new Error(`${LABEL} response.done failed: ${msg}`);
123
- const typed = typedStatusFrom(event.response?.error, event.error, event);
124
- if (typed) err.httpStatus = typed;
147
+ const err = new Error(`${state.label} response.done failed: ${msg}`);
148
+ applyErrorContract(err, {
149
+ status: typedStatusFrom(event.response?.error, event.error, event),
150
+ error: event.response?.error || event.error || null,
151
+ event,
152
+ });
125
153
  throw withFailedUsage(err, event);
126
154
  } else if (event.response.status === 'incomplete') {
127
155
  onIncomplete(event, 'response.done incomplete');
@@ -131,7 +159,7 @@ export function createTerminalFrameEvents({ state, items, text, outcome, meaning
131
159
  const failedFrameError = (event) =>
132
160
  withFailedUsage(
133
161
  wireFailureError(
134
- `${LABEL} response.failed: ${event.response?.error?.message || event.error?.message || event.message || 'response.failed'}`,
162
+ `${state.label} response.failed: ${event.response?.error?.message || event.error?.message || event.message || 'response.failed'}`,
135
163
  event,
136
164
  event.response?.error || event.error || null,
137
165
  [event.response?.error, event.error, event]
@@ -142,7 +170,7 @@ export function createTerminalFrameEvents({ state, items, text, outcome, meaning
142
170
  // Same wire-error contract as response.failed.
143
171
  const errorFrameError = (event) =>
144
172
  wireFailureError(
145
- `${LABEL} error: ${event.message || event.error?.message || 'unknown'}`,
173
+ `${state.label} error: ${event.message || event.error?.message || 'unknown'}`,
146
174
  event,
147
175
  { code: event.error?.code ?? event.error?.type ?? event.code ?? null },
148
176
  [event.error, event]
@@ -42,6 +42,16 @@ export function createResponseEvents({ state, items, text, outcome, meaningful,
42
42
  case 'response.function_call_arguments.done':
43
43
  output.onFunctionCallArgumentsDone(event);
44
44
  break;
45
+ // Refusal text is collected for the typed refusal result, never relayed
46
+ // as visible answer text.
47
+ case 'response.refusal.delta':
48
+ state.refusals.delta(event);
49
+ meaningful('semantic');
50
+ break;
51
+ case 'response.refusal.done':
52
+ state.refusals.done(event);
53
+ meaningful('semantic');
54
+ break;
45
55
  case 'response.output_item.done':
46
56
  output.onOutputItemDone(event.item || {});
47
57
  break;
@@ -5,7 +5,12 @@
5
5
  * pending function_call completion.
6
6
  */
7
7
  import { makeInvalidToolArgsMarker } from '../lib/openai-tool-args.mjs';
8
- import { customToolCallFromResponseItem, nativeToolSearchCallFromArguments } from '../custom-tool-wire.mjs';
8
+ import {
9
+ customToolCallFromResponseItem,
10
+ nativeToolSearchCallFromArguments,
11
+ qualifiedToolCallName,
12
+ withToolCallNamespace,
13
+ } from '../custom-tool-wire.mjs';
9
14
 
10
15
  // Completed function_call.arguments parse for the OpenAI Responses stream.
11
16
  // A function_call item arrives only on a completion/done signal, so a
@@ -37,7 +42,7 @@ export function createResponseItems({ state, onToolCall }) {
37
42
  if (!call?.id) return;
38
43
  if (state.emittedToolCallIds.has(call.id)) return;
39
44
  state.emittedToolCallIds.add(call.id);
40
- if (!state.toolDedupe.shouldDispatch(call.name, call.arguments, call.id)) return;
45
+ if (!state.toolDedupe.shouldDispatch(qualifiedToolCallName(call), call.arguments, call.id)) return;
41
46
  state.emittedToolCall = true;
42
47
  try {
43
48
  onToolCall?.(call);
@@ -77,6 +82,8 @@ export function createResponseItems({ state, onToolCall }) {
77
82
  };
78
83
  const pushToolSearchCall = (item) => {
79
84
  if (item?.type !== 'tool_search_call') return;
85
+ // Server-executed search already ran inside the response; replay-only.
86
+ if (item.execution === 'server') return;
80
87
  const callId = item.call_id || item.id || '';
81
88
  if (!callId || state.toolCalls.some((t) => t.id === callId)) return;
82
89
  let args = {};
@@ -104,6 +111,7 @@ export function createResponseItems({ state, onToolCall }) {
104
111
  const completePendingCall = (tc, item) => {
105
112
  if (!tc.id && item.call_id) tc.id = item.call_id;
106
113
  if (!tc.name && item.name) tc.name = item.name;
114
+ withToolCallNamespace(tc, item);
107
115
  if (tc.id && tc.name) {
108
116
  delete tc._pendingItemId;
109
117
  emitToolCall(tc);
@@ -119,11 +127,14 @@ export function createResponseItems({ state, onToolCall }) {
119
127
  if (tc) {
120
128
  completePendingCall(tc, item);
121
129
  } else if (item.call_id && item.name) {
122
- const call = {
123
- id: item.call_id,
124
- name: item.name,
125
- arguments: parseJsonObject(item.arguments),
126
- };
130
+ const call = withToolCallNamespace(
131
+ {
132
+ id: item.call_id,
133
+ name: item.name,
134
+ arguments: parseJsonObject(item.arguments),
135
+ },
136
+ item
137
+ );
127
138
  state.toolCalls.push(call);
128
139
  emitToolCall(call);
129
140
  }
@@ -8,8 +8,8 @@ import { traceAgentSse, traceAgentUsage } from '../../agent-trace.mjs';
8
8
  import { dedupeToolCallList } from '../lib/leaked-toolcall.mjs';
9
9
  import { _displayCodexModel } from '../openai-codex-model.mjs';
10
10
  import { createProviderReplay } from '../lib/provider-replay.mjs';
11
- import { LABEL } from './response-state.mjs';
12
11
  import { createResponseStamps } from './response-stamps.mjs';
12
+ import { refusalResultFields } from '../openai-ws-response-state/refusals.mjs';
13
13
 
14
14
  export function createResponseOutcome({ state }) {
15
15
  const { stampToolSafety, stampOutcome, stallPartial, stampStreamError, attachStreamPartial, endedEarlyError } =
@@ -50,7 +50,7 @@ export function createResponseOutcome({ state }) {
50
50
  throw stampOutcome(
51
51
  stampToolSafety(
52
52
  new Error(
53
- `${LABEL} function_call salvage failed: missing call_id/name for item_id=${unresolved._pendingItemId || '?'}`
53
+ `${state.label} function_call salvage failed: missing call_id/name for item_id=${unresolved._pendingItemId || '?'}`
54
54
  )
55
55
  )
56
56
  );
@@ -75,6 +75,7 @@ export function createResponseOutcome({ state }) {
75
75
  webSearchCalls: state.webSearchCalls.length ? state.webSearchCalls : undefined,
76
76
  usage: state.usage || undefined,
77
77
  stopReason: state.stopReason || undefined,
78
+ incompleteReason: state.incompleteReason || undefined,
78
79
  // Only present when the terminal frame carried the wire field.
79
80
  ...(typeof state.endTurn === 'boolean' ? { endTurn: state.endTurn } : {}),
80
81
  // Text-only max-output cutoff (status:'incomplete'/reason=max_output_tokens
@@ -84,6 +85,8 @@ export function createResponseOutcome({ state }) {
84
85
  ...(state.stopReason === 'length' && state.content.length > 0 ? { truncated: true } : {}),
85
86
  responseId: state.responseId || undefined,
86
87
  serviceTier: state.serviceTier || undefined,
88
+ // A refused turn: typed stop reason and the refusal text.
89
+ ...refusalResultFields(state.refusals, state.content),
87
90
  };
88
91
  };
89
92
 
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { stampStreamOutcome, STREAM_TRANSPORTS } from '../lib/stream-outcome.mjs';
11
11
  import { createProviderReplay } from '../lib/provider-replay.mjs';
12
- import { LABEL, toolInputPending } from './response-state.mjs';
12
+ import { toolInputPending } from './response-state.mjs';
13
13
 
14
14
  export function createResponseStamps({ state }) {
15
15
  const stampToolSafety = (err) => {
@@ -111,7 +111,7 @@ export function createResponseStamps({ state }) {
111
111
  const err = attachStreamPartial(
112
112
  stampToolSafety(
113
113
  new Error(
114
- `${LABEL} ended before response.completed (text=${state.content.length} chars, toolCalls=${state.toolCalls.length})`
114
+ `${state.label} ended before response.completed (text=${state.content.length} chars, toolCalls=${state.toolCalls.length})`
115
115
  )
116
116
  )
117
117
  );
@@ -7,11 +7,11 @@
7
7
  */
8
8
  import { createActiveToolItemTracker } from '../tool-stream-state.mjs';
9
9
  import { createToolCallDedupe } from '../lib/leaked-toolcall.mjs';
10
+ import { createRefusals } from '../openai-ws-response-state/refusals.mjs';
10
11
 
11
- export const LABEL = 'OpenAI OAuth HTTP fallback';
12
-
13
- export function createResponseState() {
12
+ export function createResponseState(label) {
14
13
  return {
14
+ label,
15
15
  content: '',
16
16
  model: '',
17
17
  responseId: '',
@@ -32,8 +32,12 @@ export function createResponseState() {
32
32
  citationKeys: new Set(),
33
33
  webSearchCalls: [],
34
34
  webSearchCallKeys: new Set(),
35
+ // Refusal parts (streamed and final), folded into one typed refusal.
36
+ refusals: createRefusals(),
35
37
  completed: false,
36
38
  stopReason: null,
39
+ // Why a max-output cutoff completed the turn (response.incomplete_details).
40
+ incompleteReason: null,
37
41
  // Normalized wire `end_turn` from the terminal frame; undefined unless the
38
42
  // server actually supplied a boolean.
39
43
  endTurn: undefined,
@@ -97,10 +97,18 @@ export function createTextRelay({ state, body, onTextDelta, meaningful, emitTool
97
97
  };
98
98
  const absorbCompletedMessage = (item) => {
99
99
  let reported = false;
100
+ // Decided once per message item (as the WS completed-item fold does), so
101
+ // every content part of an item with no streamed text is absorbed rather
102
+ // than only the first one that fills `state.content`.
103
+ const absorbText = !state.content;
100
104
  for (const part of item.content || []) {
101
- if (!state.content && part.type === 'output_text' && absorbCompletedText(part)) reported = true;
105
+ if (absorbText && part.type === 'output_text' && absorbCompletedText(part)) reported = true;
102
106
  if (part.type === 'output_text') pushOutputTextAnnotations(part, state.citations, state.citationKeys);
103
107
  }
108
+ if (state.refusals.absorbMessage(item)) {
109
+ meaningful('semantic');
110
+ reported = true;
111
+ }
104
112
  return reported;
105
113
  };
106
114
 
@@ -26,8 +26,8 @@ import { createResponseEvents } from './openai-http-sse-response-state/response-
26
26
  * @param {(kind: string) => void} deps.meaningful semantic progress (re-arms the idle watchdog)
27
27
  * @param {() => void} deps.onServerEvent first-event → semantic-idle handover
28
28
  */
29
- export function createHttpSseResponseState({ body, onToolCall, onTextDelta, meaningful, onServerEvent }) {
30
- const state = createResponseState();
29
+ export function createHttpSseResponseState({ body, onToolCall, onTextDelta, meaningful, onServerEvent, label }) {
30
+ const state = createResponseState(label);
31
31
  const outcome = createResponseOutcome({ state });
32
32
  const items = createResponseItems({ state, onToolCall });
33
33
  const text = createTextRelay({ state, body, onTextDelta, meaningful, emitToolCall: items.emitToolCall });
@@ -20,8 +20,6 @@ import {
20
20
  streamStalledError,
21
21
  } from '../stall-policy.mjs';
22
22
 
23
- const LABEL = 'OpenAI OAuth HTTP fallback';
24
-
25
23
  function positiveOverride(value) {
26
24
  const ms = Number(value);
27
25
  return Number.isFinite(ms) && ms > 0 ? ms : 0;
@@ -34,7 +32,7 @@ function positiveOverride(value) {
34
32
  * @param {{ signal?: AbortSignal, cleanup(): void }} deps.totalTimeout
35
33
  * @param {() => object} deps.stallPartial streamed partial state attached to a semantic-idle stall
36
34
  */
37
- export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartial }) {
35
+ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartial, label: LABEL }) {
38
36
  let abortReason = null;
39
37
  let pendingReadReject = null;
40
38
  const rejectPendingRead = (err) => {
@@ -0,0 +1,80 @@
1
+ // Public OpenAI API model capabilities for Responses request construction.
2
+ // Catalog metadata decides where it declares a capability; otherwise the
3
+ // documented model-family rules below apply. The Codex (openai-oauth) route is
4
+ // separate: its backend serves only current GPT-5.x models and keeps its own
5
+ // defaults, so none of these gates apply to it.
6
+ // reasoning: https://developers.openai.com/api/docs/guides/reasoning/
7
+ // tool_search: https://developers.openai.com/api/docs/guides/tools-tool-search/ (gpt-5.4 and later)
8
+ // cache: https://developers.openai.com/api/docs/guides/prompt-caching/
9
+ import { getModelMetadataSync } from '../../../shared/llm/model-catalog.mjs';
10
+
11
+ const SNAPSHOT_SUFFIX = /-\d{4}-\d{2}-\d{2}$/;
12
+ const modelId = (model) => String(model?.id || model || '').trim();
13
+
14
+ // The catalog metadata of one model. A dated snapshot reads as its undated
15
+ // alias when only the alias is listed.
16
+ export function openAiCatalogMeta(model) {
17
+ const id = modelId(model);
18
+ for (const candidate of new Set([id, id.replace(SNAPSHOT_SUFFIX, '')])) {
19
+ const meta = candidate ? getModelMetadataSync(candidate, 'openai') : null;
20
+ if (meta) return meta;
21
+ }
22
+ return null;
23
+ }
24
+
25
+ // gpt-5.4 -> [5, 4]; gpt-6-astra -> [6, 0]; non-GPT ids -> null.
26
+ // A bare multi-digit major with no minor or family suffix (gpt-60) is not a
27
+ // generation id.
28
+ function gptVersion(id) {
29
+ const m = /^gpt-(\d+)(?:\.(\d+))?(?=-|$)/.exec(id);
30
+ if (!m || (m[1].length > 1 && m[2] === undefined && m[0] === id)) return null;
31
+ return [Number(m[1]), Number(m[2] || 0)];
32
+ }
33
+ const atLeast = (version, major, minor) =>
34
+ Boolean(version) && (version[0] > major || (version[0] === major && version[1] >= minor));
35
+
36
+ // Reasoning models: the o-series and GPT-5 and later (gpt-5-chat-latest is the
37
+ // non-reasoning chat snapshot).
38
+ export function openAiPublicSupportsReasoning(model) {
39
+ const flag = openAiCatalogMeta(model)?.supportsReasoning;
40
+ if (typeof flag === 'boolean') return flag;
41
+ const id = modelId(model);
42
+ if (/^gpt-5-chat/.test(id)) return false;
43
+ return /^o\d/.test(id) || atLeast(gptVersion(id), 5, 0);
44
+ }
45
+
46
+ // text.verbosity is documented for GPT-5 and later.
47
+ export function openAiPublicSupportsVerbosity(model) {
48
+ return atLeast(gptVersion(modelId(model)), 5, 0);
49
+ }
50
+
51
+ // Native tool_search: Responses API gpt-5.4 and later.
52
+ export function openAiPublicSupportsToolSearch(model) {
53
+ return atLeast(gptVersion(modelId(model)), 5, 4);
54
+ }
55
+
56
+ // GPT-5.6 and later take prompt_cache_options.ttl instead of prompt_cache_retention.
57
+ export function openAiUsesPromptCacheOptions(model) {
58
+ return atLeast(gptVersion(modelId(model)), 5, 6);
59
+ }
60
+
61
+ // Documented extended (24h) prompt_cache_retention list; dated snapshots read
62
+ // as their alias.
63
+ const EXTENDED_RETENTION = new Set([
64
+ 'gpt-5.5',
65
+ 'gpt-5.5-pro',
66
+ 'gpt-5.4',
67
+ 'gpt-5.2',
68
+ 'gpt-5.1-codex-max',
69
+ 'gpt-5.1',
70
+ 'gpt-5.1-codex',
71
+ 'gpt-5.1-codex-mini',
72
+ 'gpt-5.1-chat-latest',
73
+ 'gpt-5',
74
+ 'gpt-5-codex',
75
+ 'gpt-4.1',
76
+ ]);
77
+ export function openAiSupportsExtendedRetention(model) {
78
+ const id = modelId(model);
79
+ return EXTENDED_RETENTION.has(id) || EXTENDED_RETENTION.has(id.replace(SNAPSHOT_SUFFIX, ''));
80
+ }
@@ -13,6 +13,9 @@ import { codexCatalogHas } from './openai-oauth-catalog.mjs';
13
13
  import { _displayCodexModel } from './openai-codex-model.mjs';
14
14
  import { _envFlag, _shouldUseOpenAIHttpFallback } from './openai-oauth-http-sse.mjs';
15
15
  import { isWsMessageTooBigClose } from './retry-classifier.mjs';
16
+ import { waitForRetryDeadline } from './openai-error-contract.mjs';
17
+ import { isReplaySafe } from './lib/stream-outcome.mjs';
18
+ import { noteErrorUsage } from './lib/note-error-usage.mjs';
16
19
 
17
20
  function openAiOAuthHandshakeErrorPolicy({ status }) {
18
21
  if (Number(status) === 404) {
@@ -99,7 +102,9 @@ export function createOpenAiOAuthDispatch({
99
102
  for (const [key, expiresAt] of provider._httpFallbackUntilByPoolKey) {
100
103
  if (!(expiresAt > now)) provider._httpFallbackUntilByPoolKey.delete(key);
101
104
  }
102
- return (provider._httpFallbackUntilByPoolKey.get(poolKey) || 0) > now;
105
+ // A forked transport scope follows its session's switch; it records its own.
106
+ const sessionUntil = opts.sessionId ? provider._httpFallbackUntilByPoolKey.get(opts.sessionId) || 0 : 0;
107
+ return Math.max(provider._httpFallbackUntilByPoolKey.get(poolKey) || 0, sessionUntil) > now;
103
108
  };
104
109
  const markStickyHttpFallback = () => {
105
110
  if (!poolKey) return;
@@ -130,6 +135,17 @@ export function createOpenAiOAuthDispatch({
130
135
  });
131
136
  } catch {}
132
137
  };
138
+ // A 401 is recoverable once per send, and only while nothing the turn
139
+ // produced could be duplicated by reissuing it.
140
+ const canReissueAfter401 = (err) =>
141
+ Number(err?.httpStatus || err?.status || 0) === 401 &&
142
+ authState.authRefreshed !== true &&
143
+ !externalSignal?.aborted &&
144
+ err?.liveTextEmitted !== true &&
145
+ err?.emittedToolCall !== true &&
146
+ err?.toolCallEmitted !== true &&
147
+ err?.unsafeToRetry !== true &&
148
+ isReplaySafe(err);
133
149
  const dispatchHttp = async (reason, originalErr = null, { sticky = false } = {}) => {
134
150
  // Transport switching is a session decision, not an HTTP-success
135
151
  // side effect. Persist it before the fallback can fail or abort.
@@ -158,9 +174,10 @@ export function createOpenAiOAuthDispatch({
158
174
  if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
159
175
  process.stderr.write(`[openai-oauth] WebSocket unhealthy (${reason}); falling back to HTTP/SSE\n`);
160
176
  }
161
- let result;
162
- try {
163
- result = await sendHttp({
177
+ // The server's retry deadline outlives a transport switch.
178
+ if (originalErr && reason !== 'forced') await waitForRetryDeadline(originalErr, externalSignal, opts._sleepFn);
179
+ const sendHttpOnce = () =>
180
+ sendHttp({
164
181
  auth: authState.tokens,
165
182
  body,
166
183
  opts,
@@ -175,6 +192,22 @@ export function createOpenAiOAuthDispatch({
175
192
  useModel,
176
193
  fetchFn: opts._fetchFn,
177
194
  });
195
+ let result;
196
+ try {
197
+ try {
198
+ result = await sendHttpOnce();
199
+ } catch (firstErr) {
200
+ if (!canReissueAfter401(firstErr)) throw firstErr;
201
+ // Same bounded, replay-safe recovery the WS path applies: one forced
202
+ // refresh per send, one reissue of the identical request.
203
+ noteErrorUsage(firstErr);
204
+ traceTransportError(firstErr, 'primary', 'http');
205
+ process.stderr.write('[openai-oauth-http] 401 — forcing refresh and retrying once over HTTP/SSE\n');
206
+ authState.authRefreshed = true;
207
+ provider._refreshFallbackUntil = 0;
208
+ authState.tokens = await provider.ensureAuth({ forceRefresh: true, reason: '401' });
209
+ result = await sendHttpOnce();
210
+ }
178
211
  } catch (httpErr) {
179
212
  // The current transport owns the terminal decision, including
180
213
  // auth, rate limits, cancellation and output-safety markers.
@@ -16,9 +16,12 @@ import { CODEX_OAUTH_ORIGINATOR } from './openai-codex-endpoints.mjs';
16
16
  import { parseProviderJsonBatch } from './stream-json-pool.mjs';
17
17
  import { envFlag as _envFlag } from '../../../shared/env.mjs';
18
18
  import { activateCodexTurnState } from './openai-turn-state.mjs';
19
+ import { httpLabel } from './openai-error-contract.mjs';
19
20
  import { openHttpSseResponse } from './openai-http-sse-request.mjs';
20
21
  import { createHttpSseWatchdogs } from './openai-http-sse-watchdogs.mjs';
21
22
  import { createHttpSseResponseState } from './openai-http-sse-response-state.mjs';
23
+ import { codexUserAgent, codexVersionHeader } from './codex-client-meta.mjs';
24
+ import { _codexBetaFeatures } from './openai-ws-headers.mjs';
22
25
  export { _envFlag };
23
26
 
24
27
  function _sseEventsFromBuffer(buffer) {
@@ -72,6 +75,11 @@ export function _buildOpenAIHttpFallbackHeaders({
72
75
  originator: CODEX_OAUTH_ORIGINATOR,
73
76
  'chatgpt-account-id': auth.account_id || '',
74
77
  'x-client-request-id': randomBytes(16).toString('hex'),
78
+ // Codex identity shared with the WS handshake (build_responses_headers);
79
+ // WS-only OpenAI-Beta value stays HTTP-specific above.
80
+ 'User-Agent': codexUserAgent(),
81
+ version: codexVersionHeader(),
82
+ 'x-codex-beta-features': _codexBetaFeatures(),
75
83
  };
76
84
  if (cacheKey && !statelessConversation) {
77
85
  const sid = String(cacheKey);
@@ -165,6 +173,7 @@ export async function sendViaHttpSse({
165
173
  onStageChange?.('streaming');
166
174
  } catch {}
167
175
  const sseStartedAt = Date.now();
176
+ const label = httpLabel(auth);
168
177
  const reader = response.body.getReader();
169
178
  const decoder = new TextDecoder();
170
179
  let ttftMs = null;
@@ -173,6 +182,7 @@ export async function sendViaHttpSse({
173
182
  reader,
174
183
  totalTimeout,
175
184
  stallPartial: () => state.stallPartial(),
185
+ label,
176
186
  });
177
187
  const meaningful = (kind = 'semantic') => {
178
188
  if (ttftMs == null) ttftMs = Date.now() - sseStartedAt;
@@ -187,6 +197,7 @@ export async function sendViaHttpSse({
187
197
  onTextDelta,
188
198
  meaningful,
189
199
  onServerEvent: watchdogs.noteServerEvent,
200
+ label,
190
201
  });
191
202
  const foldFrames = async (text) => {
192
203
  const parsed = _sseEventsFromBuffer(text);
@@ -202,7 +213,7 @@ export async function sendViaHttpSse({
202
213
  if (totalTimeout.signal?.aborted) {
203
214
  watchdogs.clearSemanticIdle();
204
215
  const reason = totalTimeout.signal.reason;
205
- throw reason instanceof Error ? reason : new Error('OpenAI OAuth HTTP fallback aborted');
216
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
206
217
  }
207
218
  const abortReason = watchdogs.abortReason();
208
219
  if (abortReason) throw abortReason;
@@ -189,6 +189,7 @@ export function forgetOpenAIOAuthCredentials() {
189
189
  }
190
190
 
191
191
  // --- Token refresh ---
192
+ const PERMANENT_REFRESH_FAILURE_RE = /invalid_grant|revoked|reused|refresh_token_(?:expired|invalidated)/i;
192
193
  async function exchangeRefreshToken(refreshToken, previousIdentity = null) {
193
194
  const controller = new AbortController();
194
195
  const timeout = setTimeout(() => controller.abort(), TOKEN_REFRESH_TIMEOUT_MS);
@@ -214,10 +215,17 @@ async function exchangeRefreshToken(refreshToken, previousIdentity = null) {
214
215
  // CLI's single-use lineage) from transient failures, so the caller can
215
216
  // re-read disk and retry once with a newer token instead of
216
217
  // collapsing every failure to a generic null.
217
- if (res.status === 400 || res.status === 401 || /invalid_grant|revoked|reused/i.test(text)) {
218
+ // Only a CONFIRMED permanent refusal sets reauthRequired: a 401, or a
219
+ // typed refresh-token code. A bare 400/5xx/429 is not evidence the
220
+ // refresh token is dead.
221
+ if (res.status === 401 || PERMANENT_REFRESH_FAILURE_RE.test(text)) {
218
222
  throw Object.assign(new Error(`OpenAI OAuth token refresh ${res.status} (invalid_grant)`), {
219
223
  isInvalidGrant: true,
220
224
  reauthRequired: true,
225
+ status: res.status,
226
+ httpStatus: res.status,
227
+ code: 'invalid_grant',
228
+ headers: res.headers,
221
229
  });
222
230
  }
223
231
  return null;
@@ -312,7 +320,19 @@ async function refreshUnderLock({ currentToken, force, reason, startingTokens })
312
320
  process.stderr.write(`[openai-oauth] Refresh failed (${msg}); using still-valid current token\n`);
313
321
  return { tokens: reusable, coastOnCurrent: true };
314
322
  }
315
- throw new Error(`OpenAI OAuth token refresh failed (${msg}). Re-authenticate via provider login.`);
323
+ const failure = new Error(`OpenAI OAuth token refresh failed (${msg}). Re-authenticate via provider login.`);
324
+ // The wrapper must not hide a confirmed permanent refusal from the account pool.
325
+ if (err?.reauthRequired === true) {
326
+ Object.assign(failure, {
327
+ reauthRequired: true,
328
+ isInvalidGrant: true,
329
+ status: err.status,
330
+ httpStatus: err.httpStatus,
331
+ code: err.code,
332
+ headers: err.headers,
333
+ });
334
+ }
335
+ throw failure;
316
336
  }
317
337
  }
318
338
 
@@ -86,6 +86,9 @@ export class OpenAIOAuthProvider {
86
86
  // so input alone is the context footprint. See registry.mjs.
87
87
  static inputExcludesCache = false;
88
88
  name = 'openai-oauth';
89
+ // Loop retry policy (send-with-recovery loopRetryPolicy): a connect failure waits for
90
+ // the network, and a content_filter refusal is replayed under the budget.
91
+ retryPolicy = Object.freeze({ unboundedConnectionRetry: true, contentFilterRetryable: true });
89
92
  tokens = null;
90
93
  _refreshFallbackUntil = 0;
91
94
  // Sticky transport fallback is isolated by the WS pool/session key. A
@@ -149,6 +152,11 @@ export class OpenAIOAuthProvider {
149
152
  return this.tokens;
150
153
  }
151
154
 
155
+ /** Drop a disposed transport scope's sticky transport record. */
156
+ releaseTransportScope(scope) {
157
+ this._httpFallbackUntilByPoolKey.delete(scope);
158
+ }
159
+
152
160
  send(messages, model, tools, sendOpts) {
153
161
  return withEffortConfigurationFallback('openai-oauth', model, sendOpts || {}, (opts) =>
154
162
  this._sendOnce(messages, model, tools, opts)
@@ -204,7 +212,8 @@ export class OpenAIOAuthProvider {
204
212
  // session. cacheKey is body.prompt_cache_key and affects prompt-cache
205
213
  // routing only; Codex handshake identity comes from sendOpts and remains
206
214
  // independent so a future cache-lane policy cannot merge conversations.
207
- const poolKey = opts.sessionId || null;
215
+ // A forked request runs in its own transport scope (transport-scope.mjs).
216
+ const poolKey = opts.transportScope || opts.sessionId || null;
208
217
  const cacheKey = body.prompt_cache_key || resolveProviderCacheKey(opts, 'openai-oauth');
209
218
  const iteration = Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : null;
210
219
  const sendWs = typeof opts._sendViaWebSocketFn === 'function' ? opts._sendViaWebSocketFn : sendViaWebSocket;
@@ -277,6 +286,8 @@ export class OpenAIOAuthProvider {
277
286
  process.stderr.write(`[agent-trace] provider-${status}-retry attempt=1\n`);
278
287
  }
279
288
  this._refreshFallbackUntil = 0;
289
+ // One forced refresh per send, shared with the HTTP/SSE dispatch.
290
+ authState.authRefreshed = true;
280
291
  authState.tokens = await this.ensureAuth({ forceRefresh: true, reason: String(status) });
281
292
  try {
282
293
  const result = await dispatch.dispatchWs(true);