mixdog 1.2.0 → 1.2.2

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 (386) hide show
  1. package/README.md +7 -5
  2. package/package.json +1 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/src/agents/reviewer/AGENT.md +3 -3
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +5 -0
  8. package/src/defaults/skills/pptx/SKILL.md +6 -6
  9. package/src/defaults/skills/pptx/references/review.md +6 -4
  10. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  11. package/src/defaults/skills/setup/references/actions.md +13 -3
  12. package/src/defaults/skills/visualize/SKILL.md +12 -9
  13. package/src/repl/slash-commands.mjs +16 -1
  14. package/src/rules/shared/60-verification.md +3 -1
  15. package/src/rules/shared/70-delivery.md +4 -2
  16. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  17. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  18. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  19. package/src/runtime/agent/orchestrator/config.mjs +10 -4
  20. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  21. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  22. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  23. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +122 -15
  24. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
  25. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
  26. package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
  27. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  28. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  29. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  30. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  31. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  32. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  33. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  36. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  37. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
  38. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  40. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
  41. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  42. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
  45. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  46. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  49. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  50. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  51. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
  53. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  54. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  55. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
  57. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  58. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  59. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
  60. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
  61. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
  62. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
  63. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  64. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  65. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  66. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +447 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
  68. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  70. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
  71. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
  73. package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
  74. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  75. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
  76. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
  78. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
  79. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  80. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  81. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  82. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  83. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
  84. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
  85. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
  86. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
  87. package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
  88. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  89. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
  90. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  91. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  92. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  93. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  94. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
  95. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
  96. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  97. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
  98. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
  109. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  110. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  111. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
  113. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
  115. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
  116. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
  117. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  119. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
  120. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  125. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
  127. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  128. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
  129. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
  130. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  132. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  133. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  135. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
  136. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
  137. package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
  138. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  139. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
  140. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  141. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  142. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  143. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  146. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  147. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  148. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  149. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  150. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
  151. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  152. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  154. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  155. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -0
  156. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  157. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  158. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
  159. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  160. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  161. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  162. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  163. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  164. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  165. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  166. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  167. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
  169. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
  170. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
  171. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
  172. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  173. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
  174. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
  175. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
  176. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
  177. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
  178. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
  179. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  182. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  183. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
  184. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
  185. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  186. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  187. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  188. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  189. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  190. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
  191. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
  192. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -12
  193. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  194. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
  195. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  196. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  197. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +41 -3
  198. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
  199. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  200. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  201. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  202. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  203. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  205. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  206. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  208. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
  209. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
  210. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  211. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  212. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  213. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  214. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  215. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  216. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  217. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  218. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  219. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  220. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  221. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  222. package/src/runtime/attachments/store.d.mts +3 -0
  223. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  224. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  225. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  226. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  227. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  228. package/src/runtime/memory/lib/memory.mjs +1 -1
  229. package/src/runtime/memory/lib/pg/process.mjs +34 -24
  230. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
  231. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  232. package/src/runtime/office/README.md +1 -1
  233. package/src/runtime/office/shared/values.mjs +0 -5
  234. package/src/runtime/shared/atomic-file.mjs +25 -4
  235. package/src/runtime/shared/background-tasks.mjs +92 -14
  236. package/src/runtime/shared/err-text.mjs +25 -0
  237. package/src/runtime/shared/error-presentation.mjs +159 -19
  238. package/src/runtime/shared/extension-scopes.mjs +5 -3
  239. package/src/runtime/shared/file-lock.mjs +37 -11
  240. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  241. package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
  242. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  243. package/src/runtime/shared/llm/model-catalog.mjs +53 -52
  244. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  245. package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
  246. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  247. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  248. package/src/runtime/shared/model-display.mjs +13 -0
  249. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  250. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  251. package/src/runtime/shared/pristine-execution.mjs +28 -4
  252. package/src/runtime/shared/provider-accounts.mjs +124 -20
  253. package/src/runtime/shared/provider-api-key.mjs +12 -0
  254. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  255. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  256. package/src/runtime/shared/shell-display.mjs +9 -3
  257. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  258. package/src/runtime/shared/skill-mentions.mjs +133 -0
  259. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  260. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  261. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  262. package/src/runtime/shared/tool-primitives.mjs +35 -2
  263. package/src/runtime/shared/turn-changes.mjs +447 -219
  264. package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
  265. package/src/runtime/shared/turn-snapshot.mjs +170 -48
  266. package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
  267. package/src/runtime/shared/worker-requests.mjs +15 -2
  268. package/src/runtime/shared/worktrees/contract.mjs +0 -24
  269. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  270. package/src/session-runtime/automation-agents.mjs +8 -2
  271. package/src/session-runtime/boot/apis.mjs +61 -0
  272. package/src/session-runtime/boot/config.mjs +5 -2
  273. package/src/session-runtime/boot/facade.mjs +1 -0
  274. package/src/session-runtime/boot/infrastructure.mjs +35 -18
  275. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  276. package/src/session-runtime/conversation-import/service.mjs +369 -0
  277. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
  278. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  279. package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
  280. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  281. package/src/session-runtime/mcp-glue/connect.mjs +83 -16
  282. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  283. package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
  284. package/src/session-runtime/mcp-glue.mjs +25 -2
  285. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  286. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  287. package/src/session-runtime/model-route/set-route.mjs +83 -15
  288. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  289. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  290. package/src/session-runtime/new-session-config.mjs +3 -2
  291. package/src/session-runtime/notification-bus.mjs +9 -2
  292. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  293. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  294. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  295. package/src/session-runtime/provider-readiness.mjs +16 -10
  296. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  297. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  298. package/src/session-runtime/resource-extension-sync.mjs +24 -5
  299. package/src/session-runtime/resource-mcp-api.mjs +198 -78
  300. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  301. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  302. package/src/session-runtime/resource-skills-api.mjs +23 -2
  303. package/src/session-runtime/runtime-review-api.mjs +13 -0
  304. package/src/session-runtime/schedule-session-run.mjs +1 -1
  305. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  306. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  307. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  308. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  310. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  311. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  312. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  313. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  314. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  315. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  316. package/src/session-runtime/services/provider-admin.mjs +64 -18
  317. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  318. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  319. package/src/session-runtime/setup-tool/executor.mjs +12 -1
  320. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  321. package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
  322. package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
  323. package/src/session-runtime/skills-api.mjs +7 -3
  324. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  325. package/src/session-runtime/webhook-session-run.mjs +16 -4
  326. package/src/skill-script-entry.mjs +36 -0
  327. package/src/standalone/daemon-drain-policy.mjs +8 -0
  328. package/src/standalone/daemon-stored-session-views.mjs +11 -3
  329. package/src/standalone/daemon-telemetry.mjs +55 -0
  330. package/src/standalone/daemon.mjs +44 -11
  331. package/src/standalone/session-protocol.mjs +12 -0
  332. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  333. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  334. package/src/standalone/session-service/agent-tree.mjs +12 -0
  335. package/src/standalone/session-service/quota-resume.mjs +8 -0
  336. package/src/standalone/session-service/retention/busy.mjs +15 -1
  337. package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
  338. package/src/standalone/session-service-api.mjs +5 -0
  339. package/src/standalone/session-service.mjs +1 -0
  340. package/src/tui/App.jsx +9 -1
  341. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  342. package/src/tui/app/app-view.jsx +7 -0
  343. package/src/tui/app/create-app-pickers.mjs +4 -0
  344. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  345. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  346. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  347. package/src/tui/app/extension-pickers.mjs +2 -1
  348. package/src/tui/app/model-options.mjs +13 -16
  349. package/src/tui/app/skill-mention-palette.mjs +88 -0
  350. package/src/tui/app/slash-commands.mjs +7 -2
  351. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  352. package/src/tui/app/slash-palette.mjs +23 -7
  353. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  354. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  355. package/src/tui/app/use-prompt-state.mjs +5 -0
  356. package/src/tui/components/Message.jsx +22 -1
  357. package/src/tui/components/PromptInput.jsx +21 -3
  358. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  359. package/src/tui/components/StatusLine.jsx +2 -0
  360. package/src/tui/components/TranscriptItem.jsx +9 -1
  361. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  362. package/src/tui/components/status-line/local-line.mjs +5 -2
  363. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  364. package/src/tui/components/user-message-skills.mjs +38 -0
  365. package/src/tui/dist/index.mjs +1323 -214
  366. package/src/tui/session/queue-helpers.mjs +7 -2
  367. package/src/tui/session/quota-resume.mjs +46 -14
  368. package/src/tui/session/session-api/commands.mjs +33 -24
  369. package/src/tui/session/session-api/extensions.mjs +26 -2
  370. package/src/tui/session/session-api/integrations.mjs +7 -0
  371. package/src/tui/session/session-api/lifecycle.mjs +5 -1
  372. package/src/tui/session/session-api/routes.mjs +71 -7
  373. package/src/tui/session/session-api/settings.mjs +12 -14
  374. package/src/tui/session/session-flow/drain.mjs +21 -5
  375. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  376. package/src/tui/session/tui-steering-persist.mjs +25 -9
  377. package/src/tui/session/turn.mjs +55 -15
  378. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  379. package/src/ui/statusline-agents.mjs +15 -4
  380. package/src/ui/statusline.mjs +7 -3
  381. package/src/ui/streaming-markdown-heal.mjs +34 -3
  382. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  383. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  384. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
  385. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  386. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -7,6 +7,7 @@
7
7
  // turn tool loops keep the model's reasoning chain without server storage.
8
8
 
9
9
  import { markProviderRecoveryExhausted, withRetry } from './retry-classifier.mjs';
10
+ import { compatRetryOptions, withCompatRetryProfile } from './openai-compat-retry-profile.mjs';
10
11
  import { consumeCompatResponsesStream } from './openai-compat-stream.mjs';
11
12
  import { getModelMetadataSync } from './model-catalog.mjs';
12
13
  import { PROVIDER_FIRST_BYTE_TIMEOUT_MS, createPassthroughSignal } from '../stall-policy.mjs';
@@ -24,6 +25,8 @@ import {
24
25
  import { preparePdfTextForProvider } from './media-normalization.mjs';
25
26
  import { normalizeOpencodeGoReasoningEffort } from './openai-compat-xai.mjs';
26
27
  import { isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
28
+ import { projectEffortConfiguration, withEffortConfigurationFallback } from './effort-configuration.mjs';
29
+ import { normalizeCacheKey } from '../agent-runtime/cache-strategy.mjs';
27
30
  import { createProviderReplay } from './lib/provider-replay.mjs';
28
31
  import {
29
32
  applyCompatToolChoice,
@@ -53,9 +56,65 @@ function resolveReasoning(provider, useModel, opts) {
53
56
  return reasoningEffort ? { effort: reasoningEffort } : null;
54
57
  }
55
58
 
59
+ /**
60
+ * A custom route's mid-conversation effort projection (GPT-6+ on this wire)
61
+ * with every effort in the row's wire value, or null for the per-request
62
+ * effort. Consecutive efforts sharing one wire value collapse into none; an
63
+ * effort the row cannot serve falls back to the per-request effort.
64
+ */
65
+ function customEffortProjection(provider, useModel, messages, opts) {
66
+ if (!isCustomProviderName(provider.name)) return null;
67
+ const projection = projectEffortConfiguration(messages, provider.name, useModel, {
68
+ ...opts,
69
+ protocol: 'openai-responses',
70
+ });
71
+ if (!projection) return null;
72
+ const model = provider.getCachedModelInfo?.(useModel) || null;
73
+ const wire = (effort) =>
74
+ resolveCustomReasoning({ protocol: 'openai-responses', model, effort }).fields.reasoning?.effort || null;
75
+ const initialEffort = wire(projection.initialEffort);
76
+ if (!initialEffort) return null;
77
+ const updates = new Map();
78
+ let current = initialEffort;
79
+ for (const [message, effort] of projection.updates) {
80
+ const value = wire(effort);
81
+ if (!value) return null;
82
+ if (value !== current) {
83
+ current = value;
84
+ updates.set(message, value);
85
+ }
86
+ }
87
+ return { ...projection, initialEffort, effort: current, updates };
88
+ }
89
+
90
+ /**
91
+ * A custom route's session identity: its prompt_cache_key and session
92
+ * headers, '' without a session. The session wins over the provider-scoped
93
+ * cache key, so parallel sessions never evict each other; a one-shot role
94
+ * ('shared' scope, a fresh session per call) keys on the provider key instead.
95
+ * Never the effort, so an effort change keeps the cache routing.
96
+ */
97
+ function customSessionCacheKey(provider, opts) {
98
+ if (!isCustomProviderName(provider.name)) return '';
99
+ const explicit = opts.promptCacheKey || opts.providerCacheKey || opts.session?.promptCacheKey;
100
+ return normalizeCacheKey(
101
+ opts.promptCacheScope === 'shared' ? explicit || opts.sessionId : opts.sessionId || explicit
102
+ );
103
+ }
104
+
56
105
  /** The request body: lowered transcript plus the per-turn knobs (output cap,
57
- * tools and tool choice, reasoning effort). */
58
- function buildCompatResponsesParams({ provider, useModel, input, previousResponseId, tools, opts, instructions }) {
106
+ * tools and tool choice, reasoning effort, session cache key). */
107
+ function buildCompatResponsesParams({
108
+ provider,
109
+ useModel,
110
+ input,
111
+ previousResponseId,
112
+ tools,
113
+ opts,
114
+ instructions,
115
+ effortProjection,
116
+ sessionCacheKey,
117
+ }) {
59
118
  const params = {
60
119
  model: useModel,
61
120
  input,
@@ -71,14 +130,19 @@ function buildCompatResponsesParams({ provider, useModel, input, previousRespons
71
130
  params.parallel_tool_calls = true;
72
131
  }
73
132
  applyCompatToolChoice(params, opts);
74
- const reasoning = resolveReasoning(provider, useModel, opts);
133
+ // A projected conversation keeps its initial effort on top; changes ride as input items.
134
+ const reasoning = effortProjection
135
+ ? { effort: effortProjection.initialEffort }
136
+ : resolveReasoning(provider, useModel, opts);
75
137
  if (reasoning) params.reasoning = reasoning;
76
138
  // Opt-in system-as-instructions: set before the hook so the hook can override it.
77
139
  if (instructions) params.instructions = instructions;
140
+ if (sessionCacheKey) params.prompt_cache_key = sessionCacheKey;
78
141
  // Opt-in per-provider final shaping (a provider passes the hook in its config).
79
142
  const hook = provider.config?.responsesParamsHook;
80
143
  if (typeof hook === 'function') {
81
- const modelInfo = (typeof provider.getCachedModelInfo === 'function' && provider.getCachedModelInfo(useModel)) || null;
144
+ const modelInfo =
145
+ (typeof provider.getCachedModelInfo === 'function' && provider.getCachedModelInfo(useModel)) || null;
82
146
  hook(params, { useModel, opts, modelInfo });
83
147
  }
84
148
  return params;
@@ -90,16 +154,16 @@ function buildCompatResponsesParams({ provider, useModel, input, previousRespons
90
154
  * that re-opens the request. A content-idle stall is terminal for this
91
155
  * transport, so it is marked recovery-exhausted rather than retried.
92
156
  */
93
- async function streamCompatResponses({ provider, params, label, signal, opts }) {
157
+ async function streamCompatResponses({ provider, params, label, signal, opts, headers }) {
94
158
  const totalSignal = createPassthroughSignal(signal);
95
159
  try {
96
160
  return await withRetry(
97
- async ({ signal: attemptSignal }) => {
161
+ withCompatRetryProfile(provider.name, async ({ signal: attemptSignal }) => {
98
162
  const stream = await withRetry(
99
163
  ({ signal: openSignal }) =>
100
164
  provider.client.responses.create(params, {
101
165
  signal: openSignal,
102
- ...(opts.requestHeaders ? { headers: opts.requestHeaders } : {}),
166
+ ...(headers ? { headers } : {}),
103
167
  }),
104
168
  {
105
169
  signal: attemptSignal,
@@ -130,11 +194,12 @@ async function streamCompatResponses({ provider, params, label, signal, opts })
130
194
  }
131
195
  throw error;
132
196
  }
133
- },
197
+ }),
134
198
  {
135
199
  signal: totalSignal.signal,
136
200
  onRetry: compatStreamRetryReporter(label, opts),
137
201
  retry529: opts.retry529,
202
+ ...compatRetryOptions(provider.name),
138
203
  }
139
204
  );
140
205
  } finally {
@@ -145,9 +210,17 @@ async function streamCompatResponses({ provider, params, label, signal, opts })
145
210
  /**
146
211
  * Send one turn over `POST {baseURL}/responses` (streaming) using the
147
212
  * provider's existing OpenAI SDK client. Returns the same result shape as
148
- * the chat/completions path so the agent loop stays wire-agnostic.
213
+ * the chat/completions path so the agent loop stays wire-agnostic. A custom
214
+ * route whose model rejects the mid-conversation effort update is replayed
215
+ * once with the per-request effort.
149
216
  */
150
- export async function sendCompatResponses(provider, messages, useModel, tools, opts = {}) {
217
+ export function sendCompatResponses(provider, messages, useModel, tools, opts = {}) {
218
+ const send = (sendOpts) => sendCompatResponsesOnce(provider, messages, useModel, tools, sendOpts);
219
+ if (!isCustomProviderName(provider.name)) return send(opts);
220
+ return withEffortConfigurationFallback(provider.name, useModel, opts, send);
221
+ }
222
+
223
+ async function sendCompatResponsesOnce(provider, messages, useModel, tools, opts) {
151
224
  const replayProvider = compatResponsesReplayProvider(provider.name);
152
225
  const signal = opts.signal || null;
153
226
  if (signal?.aborted) {
@@ -156,7 +229,10 @@ export async function sendCompatResponses(provider, messages, useModel, tools, o
156
229
  }
157
230
  const label = `${provider.name}:responses`;
158
231
  messages = await preparePdfTextForProvider(messages, provider);
232
+ const effortProjection = customEffortProjection(provider, useModel, messages, opts);
233
+ const sessionCacheKey = customSessionCacheKey(provider, opts);
159
234
  const { input, previousResponseId, continuationResetReason } = toXaiResponsesInput(messages, opts.providerState, {
235
+ effortProjection,
160
236
  model: useModel,
161
237
  stateKey: COMPAT_RESPONSES_STATE_KEY,
162
238
  replayProvider,
@@ -165,8 +241,7 @@ export async function sendCompatResponses(provider, messages, useModel, tools, o
165
241
  // Opt-in: system prompts travel as top-level `instructions`, not input items.
166
242
  includeSystem: provider.config?.responsesSystemAsInstructions !== true,
167
243
  });
168
- const instructions =
169
- provider.config?.responsesSystemAsInstructions === true ? xaiSystemInstructions(messages) : '';
244
+ const instructions = provider.config?.responsesSystemAsInstructions === true ? xaiSystemInstructions(messages) : '';
170
245
  const params = buildCompatResponsesParams({
171
246
  provider,
172
247
  useModel,
@@ -175,14 +250,20 @@ export async function sendCompatResponses(provider, messages, useModel, tools, o
175
250
  tools,
176
251
  opts,
177
252
  instructions,
253
+ effortProjection,
254
+ sessionCacheKey,
178
255
  });
256
+ // The session identity also travels as headers; caller headers win.
257
+ const headers = sessionCacheKey
258
+ ? { 'session-id': sessionCacheKey, 'x-client-request-id': sessionCacheKey, ...opts.requestHeaders }
259
+ : opts.requestHeaders;
179
260
 
180
261
  try {
181
262
  opts.onStageChange?.('requesting');
182
263
  } catch {
183
264
  /* heartbeat best-effort */
184
265
  }
185
- const streamed = await streamCompatResponses({ provider, params, label, signal, opts });
266
+ const streamed = await streamCompatResponses({ provider, params, label, signal, opts, headers });
186
267
  const response = streamed.response;
187
268
  const usage = response?.usage || null;
188
269
  const inputTokens = Number(usage?.input_tokens ?? usage?.prompt_tokens ?? 0);
@@ -0,0 +1,193 @@
1
+ // Per-provider request retry profile for the OpenAI-compatible family. Each
2
+ // provider carries its own retry constants; the
3
+ // shared withRetry still owns classification, the exposure/replay veto,
4
+ // quota refusals, x-should-retry and Retry-After. Providers with no profile
5
+ // keep the shared defaults.
6
+ //
7
+ // `backoffMs[n]` is the base wait before retry n (index 0 is never read, so
8
+ // the first value is the duplicate the withRetry schedule expects).
9
+ // statusRetryCaps — retries allowed for one status (for example a
10
+ // 429 stops after a single retry, whatever the overall budget).
11
+ // terminalStatuses — statuses that are terminal although the
12
+ // shared classifier would retry them.
13
+ // retryOnlyStatuses — the complete retryable error set: any other 4xx/5xx
14
+ // status surfaces.
15
+ // maxRetryAfterMs — give up instead of waiting longer than this.
16
+ // maxFailedAttemptMs — a 5xx that took longer than this is not replayed.
17
+ // retryEmptyResponse — a completed turn with no text, tool call or
18
+ // reasoning is re-asked inside the ladder (nothing was exposed); a number
19
+ // bounds how many times per send, true leaves it to the ladder.
20
+ // adviceWindow - outside exceptStatuses, Retry-After is clamped to maxMs
21
+ // and jittered by jitterRatio (the advice of the excepted statuses stays raw).
22
+ // recycleDispatcherOn5xx — a retried 5xx rebuilds the shared HTTP pool.
23
+ // adviceAtLeastBackoff — the advice is a floor under the jittered backoff step.
24
+ // Every stop is provider-terminal: the profile's limits are final, so the
25
+ // agent loop never re-runs a stopped or exhausted request (see
26
+ // COMPAT_RETRY_POLICY).
27
+ import { markProviderRecoveryExhausted } from './retry-classifier.mjs';
28
+ import { retryAfterMsFromError } from './provider-error-kind.mjs';
29
+ import { STREAM_TRANSPORTS, stampStreamOutcome } from './lib/stream-outcome.mjs';
30
+ import { jitterDelayMs } from './retry-backoff.mjs';
31
+
32
+ const statusOf = (err) => Number(err?.httpStatus || err?.status || err?.response?.status || 0);
33
+
34
+ /** Loop retry policy of every profiled provider: no recovery cycles on top of the profile. */
35
+ export const COMPAT_RETRY_POLICY = Object.freeze({ recoveryCycles: false });
36
+
37
+ /** xAI additionally treats an in-band invalid-request error as retryable. */
38
+ export const XAI_COMPAT_RETRY_POLICY = Object.freeze({ ...COMPAT_RETRY_POLICY, inBandInvalidRequestRetryable: true });
39
+
40
+ export const COMPAT_RETRY_PROFILES = Object.freeze({
41
+ // A budget of 15 sends (the retry limit is reached when the next retry number
42
+ // equals it, so 14 waits), 2s doubling to 30s, ±20% jitter, one retry for a
43
+ // 429. Outside a 429, Retry-After is capped at 30s and jittered.
44
+ xai: Object.freeze({
45
+ maxAttempts: 15,
46
+ backoffMs: [2000, 2000, 4000, 8000, 16000, 30000],
47
+ retryJitterRatio: 0.2,
48
+ statusRetryCaps: { 429: 1 },
49
+ terminalStatuses: [408, 409, 425],
50
+ retryEmptyResponse: true,
51
+ adviceWindow: Object.freeze({ maxMs: 30_000, jitterRatio: 0.2, exceptStatuses: [429] }),
52
+ recycleDispatcherOn5xx: true,
53
+ }),
54
+ // 5 retries, 500ms doubling to 10s, ±10% jitter;
55
+ // a Retry-After beyond the 10s ceiling is not waited out.
56
+ deepseek: Object.freeze({
57
+ maxAttempts: 6,
58
+ backoffMs: [500, 500, 1000, 2000, 4000, 8000],
59
+ retryJitterRatio: 0.1,
60
+ maxRetryAfterMs: 10_000,
61
+ terminalStatuses: [408, 409, 425],
62
+ retryEmptyResponse: true,
63
+ }),
64
+ // The HTTP transport layer governs one request: 6 sends, 500ms doubling, no
65
+ // jitter, a Retry-After beyond 60s is not waited out; only 408, 429 and 5xx
66
+ // are transient, every other 4xx is terminal. A benign empty completion is
67
+ // re-asked at most twice.
68
+ 'muse-code-oauth': Object.freeze({
69
+ maxAttempts: 6,
70
+ backoffMs: [500, 500, 1000, 2000, 4000, 8000],
71
+ retryJitterRatio: 0,
72
+ terminalStatuses: [409, 425],
73
+ maxRetryAfterMs: 60_000,
74
+ retryEmptyResponse: 2,
75
+ }),
76
+ // server budget 8 retries (2s doubling to 30s,
77
+ // ±10%), rate-limit budget 5 retries; 408, 409, 501 and 505 are terminal
78
+ // (425 stays retryable); a Retry-After beyond 300s is not waited out.
79
+ 'mimo-oauth': Object.freeze({
80
+ maxAttempts: 9,
81
+ backoffMs: [2000, 2000, 4000, 8000, 16000, 30000],
82
+ retryJitterRatio: 0.1,
83
+ statusRetryCaps: { 429: 5 },
84
+ terminalStatuses: [408, 409, 501, 505],
85
+ maxRetryAfterMs: 300_000,
86
+ }),
87
+ // 3 sends (400ms doubling to 5s) for exactly
88
+ // 500/502/503/504/520/521/522 — every 4xx (429 included) and every other
89
+ // 5xx is terminal; a failed attempt slower than 15s is returned.
90
+ 'command-code-oauth': Object.freeze({
91
+ maxAttempts: 3,
92
+ backoffMs: [400, 400, 800],
93
+ retryOnlyStatuses: [500, 502, 503, 504, 520, 521, 522],
94
+ maxFailedAttemptMs: 15_000,
95
+ maxRetryAfterMs: 60_000,
96
+ adviceAtLeastBackoff: true,
97
+ }),
98
+ });
99
+
100
+ /** withRetry options (attempt budget, schedule, jitter) for a provider; {} for the shared defaults. */
101
+ export function compatRetryOptions(providerName) {
102
+ const profile = COMPAT_RETRY_PROFILES[providerName];
103
+ if (!profile) return {};
104
+ return {
105
+ maxAttempts: profile.maxAttempts,
106
+ backoffMs: profile.backoffMs,
107
+ retryJitterRatio: profile.retryJitterRatio,
108
+ finalOnExhaust: true,
109
+ ...(profile.adviceAtLeastBackoff ? { adviceAtLeastBackoff: true } : {}),
110
+ ...(profile.recycleDispatcherOn5xx ? { recycleDispatcherOn5xx: true } : {}),
111
+ ...(profile.retryJitterMode ? { retryJitterMode: profile.retryJitterMode } : {}),
112
+ };
113
+ }
114
+
115
+ function profileStops(profile, err, { attempt, elapsedMs }) {
116
+ const status = statusOf(err);
117
+ if (profile.terminalStatuses?.includes(status)) return true;
118
+ const cap = profile.statusRetryCaps?.[status];
119
+ if (cap !== undefined && attempt >= cap) return true;
120
+ if (profile.retryOnlyStatuses && status >= 400 && status < 600 && !profile.retryOnlyStatuses.includes(status)) {
121
+ return true;
122
+ }
123
+ if (profile.maxFailedAttemptMs && status >= 500 && status < 600 && elapsedMs > profile.maxFailedAttemptMs) {
124
+ return true;
125
+ }
126
+ const advice = retryAfterMsFromError(err);
127
+ return profile.maxRetryAfterMs !== undefined && advice != null && advice > profile.maxRetryAfterMs;
128
+ }
129
+
130
+ // Restamps the advice as a deadline (the highest-precedence source) so the
131
+ // shared loop waits the clamped, jittered value instead of the raw one.
132
+ function shapeAdvice(window, err) {
133
+ if (!window || !err || typeof err !== 'object' || window.exceptStatuses?.includes(statusOf(err))) return;
134
+ const advice = retryAfterMsFromError(err);
135
+ if (advice == null) return;
136
+ err.retryAfterAt = Date.now() + jitterDelayMs(Math.min(advice, window.maxMs), window.jitterRatio);
137
+ }
138
+
139
+ // A completed turn that produced nothing: no text, tool call or reasoning, and
140
+ // a normal finish (a Responses turn carries no stop reason when it completes).
141
+ function isEmptyNormalFinish(result) {
142
+ if (!result || typeof result !== 'object') return false;
143
+ if (result.stopReason != null && result.stopReason !== 'stop') return false;
144
+ return (
145
+ !String(result.content ?? '').trim() &&
146
+ !result.toolCalls?.length &&
147
+ !result.reasoningContent &&
148
+ !result.reasoningDetails?.length &&
149
+ result.reasoningEmitted !== true
150
+ );
151
+ }
152
+
153
+ function emptyResponseError(providerName) {
154
+ const err = Object.assign(new Error(`${providerName} returned an empty response`), {
155
+ code: 'server_error',
156
+ emptyResponse: true,
157
+ });
158
+ // The turn finished: a failed ladder must not reopen it as a non-streaming request.
159
+ stampStreamOutcome(err, { transport: STREAM_TRANSPORTS.SSE, terminalObserved: true });
160
+ return err;
161
+ }
162
+
163
+ /**
164
+ * Wrap one withRetry attempt with the provider's profile: a failure the
165
+ * profile treats as final is marked recovery-exhausted and provider-terminal
166
+ * (withRetry then surfaces it unchanged and no outer layer replays it), and a
167
+ * profiled empty response is raised as a retryable failure.
168
+ */
169
+ export function withCompatRetryProfile(providerName, attemptFn) {
170
+ const profile = COMPAT_RETRY_PROFILES[providerName];
171
+ if (!profile) return attemptFn;
172
+ let emptyRetries = 0;
173
+ return async (ctx) => {
174
+ const startedAt = Date.now();
175
+ try {
176
+ const result = await attemptFn(ctx);
177
+ if (profile.retryEmptyResponse && isEmptyNormalFinish(result)) {
178
+ const bound = profile.retryEmptyResponse === true ? Infinity : profile.retryEmptyResponse;
179
+ if (emptyRetries < bound) {
180
+ emptyRetries += 1;
181
+ throw emptyResponseError(providerName);
182
+ }
183
+ }
184
+ return result;
185
+ } catch (err) {
186
+ if (profileStops(profile, err, { attempt: ctx?.attempt | 0, elapsedMs: Date.now() - startedAt })) {
187
+ throw markProviderRecoveryExhausted(err, { owner: `${providerName}-retry-profile`, terminal: true });
188
+ }
189
+ shapeAdvice(profile.adviceWindow, err);
190
+ throw err;
191
+ }
192
+ };
193
+ }
@@ -1,6 +1,8 @@
1
1
  import { randomBytes } from 'node:crypto';
2
2
  import { PROVIDER_FIRST_BYTE_TIMEOUT_MS, providerTimeoutError, streamStalledError } from '../stall-policy.mjs';
3
3
  import { typedStatusFrom } from './retry-classifier.mjs';
4
+ import { applyCompatErrorContract } from './openai-compat-error-contract.mjs';
5
+ import { truncatedCompatStreamError } from './lib/openai-tool-args.mjs';
4
6
 
5
7
  // Shared lifecycle and replay boundaries for both OpenAI-compatible protocols.
6
8
 
@@ -22,10 +24,68 @@ function markInBandWireError(err) {
22
24
  if (typed && !err.httpStatus) err.httpStatus = typed;
23
25
  const code = detail.code ?? detail.type ?? null;
24
26
  if (code != null && code !== '' && !err.providerErrorCode) err.providerErrorCode = String(code);
27
+ applyCompatErrorContract(err, detail);
25
28
  } catch {}
26
29
  return err;
27
30
  }
28
31
 
32
+ /**
33
+ * A frame the SDK failed to JSON-decode. Raised only at the wire-decoding
34
+ * boundary (the iterator's own next()); consumers stamp the exposure evidence
35
+ * (partial output, replay safety) on it in their failure path.
36
+ */
37
+ export function malformedCompatStreamError(label, cause) {
38
+ const err = new Error(`${label || 'compat SSE'} malformed stream frame: ${cause?.message || 'invalid JSON'}`, {
39
+ cause,
40
+ });
41
+ err.name = 'MalformedStreamError';
42
+ err.code = 'malformed_stream';
43
+ err.malformedStream = true;
44
+ return err;
45
+ }
46
+
47
+ /**
48
+ * A chat `finish_reason: network_error` is an interruption the gateway reports
49
+ * in-band, never a completion. Typed as a truncated stream so the shared
50
+ * classifier treats it as transient; exposure gates decide replay.
51
+ */
52
+ export function networkInterruptedCompatStreamError(label) {
53
+ const err = truncatedCompatStreamError(label, 'finish_reason=network_error');
54
+ err.name = 'StreamInterruptedError';
55
+ err.streamInterrupted = true;
56
+ err.finishReason = 'network_error';
57
+ return err;
58
+ }
59
+
60
+ export function isNetworkErrorFinishReason(finishReason) {
61
+ return typeof finishReason === 'string' && finishReason.trim().toLowerCase() === 'network_error';
62
+ }
63
+
64
+ /**
65
+ * A chat `finish_reason: error` means the upstream failed the generation
66
+ * (gateways surface it as a 502 upstream_error). Typed like the network interruption, with the gateway status.
67
+ */
68
+ export function upstreamErrorCompatStreamError(label) {
69
+ const err = truncatedCompatStreamError(label, 'finish_reason=error');
70
+ err.name = 'StreamInterruptedError';
71
+ err.streamInterrupted = true;
72
+ err.finishReason = 'error';
73
+ err.status = 502;
74
+ err.code = 'upstream_error';
75
+ return err;
76
+ }
77
+
78
+ export function isErrorFinishReason(finishReason) {
79
+ return typeof finishReason === 'string' && finishReason.trim().toLowerCase() === 'error';
80
+ }
81
+
82
+ /** The typed interruption for a failing chat finish_reason, or null for any other value. */
83
+ export function failingFinishReasonError(finishReason, label) {
84
+ if (isNetworkErrorFinishReason(finishReason)) return networkInterruptedCompatStreamError(label);
85
+ if (isErrorFinishReason(finishReason)) return upstreamErrorCompatStreamError(label);
86
+ return null;
87
+ }
88
+
29
89
  export function synthLeakedOpenAICall(recovered) {
30
90
  let args = recovered?.arguments;
31
91
  if (args === null || typeof args !== 'object' || Array.isArray(args)) args = {};
@@ -59,7 +119,7 @@ export function closeCompatStream(stream, iterator) {
59
119
 
60
120
  export async function nextAsyncWithWatchdog(
61
121
  iterator,
62
- { signal, idleMs, idleDeadlineAt, idleEnabled, idleLabel, emittedToolCall } = {}
122
+ { signal, idleMs, idleDeadlineAt, idleEnabled, idleLabel, streamLabel, emittedToolCall } = {}
63
123
  ) {
64
124
  let idleTimer = null;
65
125
  let idleReject = null;
@@ -106,7 +166,11 @@ export async function nextAsyncWithWatchdog(
106
166
  };
107
167
  signal.addEventListener('abort', onAbort, { once: true });
108
168
  }
109
- iterator.next().then(resolve, reject);
169
+ // The SDK JSON-decodes each frame inside next(): a SyntaxError here is a
170
+ // malformed wire frame, typed at this boundary and nowhere else.
171
+ iterator.next().then(resolve, (error) =>
172
+ reject(error instanceof SyntaxError ? malformedCompatStreamError(streamLabel || idleLabel, error) : error)
173
+ );
110
174
  });
111
175
  } catch (err) {
112
176
  if (idleTimedOut)
@@ -99,6 +99,7 @@ export async function consumeCompatResponsesStream(
99
99
  idleDeadlineAt: state.semanticIdleDeadlineAt,
100
100
  idleEnabled: sawFirstEvent && idleEnabled && state.semanticIdleDeadlineAt > 0,
101
101
  idleLabel: `${label} SSE idle`,
102
+ streamLabel: label,
102
103
  // Unsafe-to-retry once any tool call (native or recovered-leaked)
103
104
  // has been emitted this stream — avoid a double side-effect.
104
105
  emittedToolCall: () => state.emittedToolCall || leakedCalls.length > 0,
@@ -144,7 +145,7 @@ export async function consumeCompatResponsesStream(
144
145
  }
145
146
  throw stampOutcome(err);
146
147
  }
147
- const unresolved = unresolvedSalvageError(state);
148
+ const unresolved = unresolvedSalvageError(state, label);
148
149
  if (unresolved) {
149
150
  attachPartialState(unresolved, state, leakedCalls);
150
151
  throw stampOutcome(unresolved);
@@ -18,6 +18,7 @@ import {
18
18
  import { customToolCallFromResponseItem, nativeToolSearchCallFromArguments } from './custom-tool-wire.mjs';
19
19
  import { providerReplayItems } from './lib/provider-replay.mjs';
20
20
  import { ensureResponsesCallOutputs } from './lib/wire-pairing.mjs';
21
+ import { responsesEffortUpdates } from './effort-configuration.mjs';
21
22
 
22
23
  function positiveTokenInt(value) {
23
24
  const n = Number(value);
@@ -423,10 +424,14 @@ export function toXaiResponsesInput(messages, providerState, options = {}) {
423
424
  if (!pendingToolMedia.length) return;
424
425
  input.push({ role: 'user', content: pendingToolMedia.splice(0) });
425
426
  };
427
+ // `options.effortProjection` (custom GPT-6+ routes) carries wire-ready effort updates.
428
+ const effortUpdates = responsesEffortUpdates(options.effortProjection);
426
429
  for (let messageIndex = startIndex; messageIndex < messages.length; messageIndex += 1) {
427
430
  const m = messages[messageIndex];
428
431
  if (!includeSystem && m.role === 'system') continue;
432
+ const updates = effortUpdates.before(m);
429
433
  if (m.role !== 'tool') flushToolMedia();
434
+ input.push(...updates);
430
435
  const orderedReplay = m.role === 'assistant' ? providerReplayItems(m, replayProvider) : undefined;
431
436
  if (orderedReplay?.length) {
432
437
  input.push(...orderedReplay);
@@ -444,6 +449,7 @@ export function toXaiResponsesInput(messages, providerState, options = {}) {
444
449
  flushToolMedia();
445
450
  const trailingReasoning = reasoningByMessageIndex.get(messages.length);
446
451
  if (trailingReasoning) input.push(...trailingReasoning);
452
+ input.push(...effortUpdates.end());
447
453
  // Wire-level pairing guard: replay envelopes can carry a call whose
448
454
  // result never committed (cancel/abort). The provider hard-rejects the
449
455
  // unpaired call, so synthesize the missing outputs here.
@@ -1,4 +1,6 @@
1
+ import { resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
1
2
  import { markProviderRecoveryExhausted, withRetry } from './retry-classifier.mjs';
3
+ import { compatRetryOptions, withCompatRetryProfile } from './openai-compat-retry-profile.mjs';
2
4
  import { consumeCompatResponsesStream } from './openai-compat-stream.mjs';
3
5
  import { PROVIDER_FIRST_BYTE_TIMEOUT_MS, createPassthroughSignal } from '../stall-policy.mjs';
4
6
  import {
@@ -90,7 +92,7 @@ export async function sendXaiResponses(provider, messages, useModel, tools, opts
90
92
  let streamed;
91
93
  try {
92
94
  streamed = await withRetry(
93
- async ({ signal: attemptSignal }) => {
95
+ withCompatRetryProfile(provider.name, async ({ signal: attemptSignal }) => {
94
96
  const stream = await withRetry(
95
97
  ({ signal: openSignal }) => provider.client.responses.create(params, { signal: openSignal }),
96
98
  {
@@ -126,11 +128,12 @@ export async function sendXaiResponses(provider, messages, useModel, tools, opts
126
128
  }
127
129
  throw error;
128
130
  }
129
- },
131
+ }),
130
132
  {
131
133
  signal: totalSignal.signal,
132
134
  onRetry: compatStreamRetryReporter('xai:responses', opts),
133
135
  retry529: opts.retry529,
136
+ ...compatRetryOptions(provider.name),
134
137
  }
135
138
  );
136
139
  } finally {
@@ -158,7 +161,7 @@ export async function sendXaiResponsesWebSocket(provider, messages, useModel, to
158
161
  const reason = signal.reason;
159
162
  throw reason instanceof Error ? reason : new Error('xAI Responses WebSocket request aborted by session close');
160
163
  }
161
- const apiKey = provider.config?.apiKey || process.env.XAI_API_KEY;
164
+ const apiKey = resolveProviderApiKey('xai', provider.config);
162
165
  if (!apiKey) throw new Error('xAI API key not configured');
163
166
  messages = await preparePdfTextForProvider(messages, provider);
164
167
  const chatMessagesForTrace = toOpenAIMessages(messages, provider.name, { nativePdf: provider.nativePdf });
@@ -14,7 +14,7 @@ import { appendAgentTrace } from '../agent-trace.mjs';
14
14
  import { resolveProviderCacheKey, resolveProviderPromptCacheLane } from '../agent-runtime/cache-strategy.mjs';
15
15
  import { shouldFallbackTransport } from './retry-classifier.mjs';
16
16
  import { envFlag as _envFlag } from '../../../shared/env.mjs';
17
- import { getModelsDevRowSync } from '../../../shared/llm/model-catalog.mjs';
17
+ import { getModelMetadataSync, getModelsDevRowSync } from '../../../shared/llm/model-catalog.mjs';
18
18
  import {
19
19
  traceHash,
20
20
  stableTraceStringify,
@@ -166,26 +166,35 @@ export function normalizeXaiReasoningEffort(value) {
166
166
  return ['low', 'medium', 'high', 'xhigh'].includes(effort) ? effort : null;
167
167
  }
168
168
 
169
- // grok-3, grok-4 and grok-4-fast REJECT reasoning_effort outright (HTTP 400
170
- // "does not support parameter reasoningEffort"), so the parameter ships only
171
- // for the families that document it. An unknown/absent model id keeps the
172
- // parameter — a custom xAI-compatible deployment that never saw the 400 must
173
- // not silently lose the caller's effort choice.
169
+ const hasEffortValues = (modelOptions) =>
170
+ (modelOptions || []).some((option) => option?.type === 'effort' && option.values?.length > 0);
171
+
172
+ // The first Grok generations (grok-3, grok-4, grok-4-fast, grok-code-fast)
173
+ // REJECT reasoning_effort outright (HTTP 400 "does not support parameter
174
+ // reasoningEffort"). That generation is closed: it is told apart by a bare
175
+ // major with no minor version, so a later release never matches.
176
+ const LEGACY_NO_EFFORT_GROK = /^grok[-_]?(?:[123]|4|code)(?![.\d])/;
177
+
178
+ // The parameter ships when the catalog says the model takes it. A model the
179
+ // catalog does not list yet is sent the caller's effort (the server decides),
180
+ // as is an unknown/absent model id — a custom xAI-compatible deployment that
181
+ // never saw the 400 must not silently lose the caller's effort choice. Only
182
+ // the closed legacy generation above is held back offline, because sending to
183
+ // it hard-fails.
174
184
  export function xaiModelSupportsReasoningEffort(model) {
175
185
  const id = String(model || '')
176
186
  .trim()
177
187
  .toLowerCase();
178
188
  if (!id) return true;
179
189
  if (!id.startsWith('grok')) return true;
180
- // The catalog answers first: a listed model takes the parameter exactly
181
- // when it publishes effort values (grok-4.3 does, grok-4.20-*-reasoning
182
- // does not), so a new release needs no edit here.
190
+ // The catalog answers first, from the live roster and the pricing catalogs:
191
+ // a model that publishes effort values takes the parameter.
192
+ if (hasEffortValues(getModelMetadataSync(id, 'xai')?.reasoningOptions)) return true;
193
+ // A listed model takes the parameter exactly when it publishes effort
194
+ // values (grok-4.3 does, grok-4.20-*-reasoning does not).
183
195
  const listed = getModelsDevRowSync(id, 'xai');
184
- if (listed) {
185
- return (listed.reasoning_options || []).some((option) => option?.type === 'effort' && option.values?.length > 0);
186
- }
187
- // grok-4.5 / grok-4.6 and anything newer in that line.
188
- return /grok[-_]?4\.(?:[5-9]|\d{2,})/.test(id);
196
+ if (listed) return hasEffortValues(listed.reasoning_options);
197
+ return !LEGACY_NO_EFFORT_GROK.test(id);
189
198
  }
190
199
 
191
200
  function opencodeGoReasoningEffortValues(modelInfo) {