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
@@ -2,9 +2,23 @@
2
2
  // sampled: a 429 becomes a typed quota error and a transient status a
3
3
  // retryable error; both drain the response and release its abort wiring so
4
4
  // re-issuing the POST is safe.
5
- import { noteFastModeCapacityError } from '../anthropic-fast-mode.mjs';
5
+ import { fastModeDowngraded, noteFastModeCapacityError } from '../anthropic-fast-mode.mjs';
6
+ import { applyAnthropicErrorContract, retryAfterMsFromHeaders } from '../anthropic-error-contract.mjs';
6
7
  import { headerValue } from '../retry-classification.mjs';
7
- import { classifyError, retryAfterMsFromError } from '../retry-classifier.mjs';
8
+ import { classifyError } from '../retry-classifier.mjs';
9
+
10
+ // Anthropic's exact official revoked-token 403 body; any other 403 is an
11
+ // ordinary permission/policy refusal and stays terminal.
12
+ const REVOKED_TOKEN_MARKER = 'OAuth token has been revoked';
13
+
14
+ /**
15
+ * True when a rejected initial response is one a forced credential refresh can
16
+ * fix: every 401, and only the exact revoked-token 403 signature. Shared by
17
+ * the streaming request and the non-streaming fallback.
18
+ */
19
+ export function isRefreshableAuthRejection(status, bodyText) {
20
+ return status === 401 || (status === 403 && String(bodyText ?? '').includes(REVOKED_TOKEN_MARKER));
21
+ }
8
22
 
9
23
  // A subscription-window refusal carries these unified quota headers. A 429
10
24
  // without them is an entitlement/routing refusal or a capacity blip — the
@@ -31,16 +45,13 @@ function formatRetryAfter(ms) {
31
45
  }
32
46
 
33
47
  export function anthropicQuotaError(status, headers, bodyText = '') {
34
- const retryAfterMs = retryAfterMsFromError({ headers, response: { headers } });
48
+ const retryAfterMs = retryAfterMsFromHeaders(headers);
35
49
  const retryAfter = formatRetryAfter(retryAfterMs);
36
50
  const detail = bodyText ? `: ${String(bodyText).slice(0, 200)}` : '';
37
51
  const retry = retryAfter ? ` retryAfter=${retryAfter}` : '';
38
52
  const quota = hasQuotaHeaders(headers);
39
53
  const err = new Error(`Anthropic OAuth API ${status} ${quota ? 'quota/rate limit' : 'request rejected'}${retry}${detail}`);
40
54
  err.name = quota ? 'ProviderQuotaError' : 'ProviderRequestRejectedError';
41
- err.code = quota ? 'PROVIDER_QUOTA' : 'PROVIDER_REQUEST_REJECTED';
42
- err.httpStatus = status;
43
- err.status = status;
44
55
  err.headers = headers;
45
56
  err.response = { status, headers };
46
57
  err.retryAfterMs = retryAfterMs;
@@ -50,7 +61,7 @@ export function anthropicQuotaError(status, headers, bodyText = '') {
50
61
  // SSE parsing can expose text or a tool call. It is therefore safe for the
51
62
  // request-local withRetry loop. Mid-stream paths stamp unsafeToRetry when
52
63
  // output/tool exposure actually occurs.
53
- return err;
64
+ return applyAnthropicErrorContract(err, { status, headers, bodyText });
54
65
  }
55
66
 
56
67
  export async function judgeInitialStatus(result, requestBody, { provider, cleanupCancelHandler }) {
@@ -71,26 +82,41 @@ export async function judgeInitialStatus(result, requestBody, { provider, cleanu
71
82
  // in-loop; a drained or unavailable fast pool says nothing about
72
83
  // standard-speed capacity, so `speed` is dropped and the error is
73
84
  // flagged for a single standard-speed replay by the caller.
74
- const fastDecision = noteFastModeCapacityError(
75
- { httpStatus: status, headers: result?.response?.headers, message: quotaText },
76
- { fast: requestBody?.speed === 'fast' }
77
- );
78
- const fastDowngraded = fastDecision === 'downgrade' || fastDecision === 'disabled';
79
- if (fastDowngraded) delete requestBody.speed;
85
+ const fastDowngraded = dropFastSpeed(requestBody, status, result?.response?.headers, quotaText);
80
86
  throw Object.assign(anthropicQuotaError(status, result?.response?.headers, provider.scrubTokens(quotaText)), {
81
87
  initialResponseError: true,
82
88
  fastDowngraded,
83
89
  });
84
90
  }
91
+ let overloadText = '';
92
+ try {
93
+ overloadText = await result.response.text();
94
+ } catch {}
95
+ release();
96
+ // A drained fast pool (529) is replayed at standard speed within the same
97
+ // attempt, exactly like a fast-pool 429, instead of waiting out its window.
98
+ const fastDowngraded = dropFastSpeed(requestBody, status, result?.response?.headers, overloadText);
85
99
  const err = new Error(`Anthropic OAuth API ${status}`);
86
- err.httpStatus = status;
87
- err.status = status;
88
100
  err.headers = result?.response?.headers;
89
101
  err.response = { status, headers: result?.response?.headers };
90
102
  err.initialResponseError = true;
91
- try {
92
- await result.response.text();
93
- } catch {}
94
- release();
95
- throw err;
103
+ err.fastDowngraded = fastDowngraded;
104
+ throw applyAnthropicErrorContract(err, {
105
+ status,
106
+ headers: result?.response?.headers,
107
+ bodyText: provider.scrubTokens(overloadText),
108
+ });
109
+ }
110
+
111
+ // Records a fast-pool capacity refusal; on an explicit downgrade/disable the
112
+ // request drops `speed` and the caller replays once at standard speed. Any
113
+ // other verdict ('ignored', 'retry-fast') leaves the request untouched.
114
+ function dropFastSpeed(requestBody, status, headers, text) {
115
+ const decision = noteFastModeCapacityError(
116
+ { httpStatus: status, headers, message: text },
117
+ { fast: requestBody?.speed === 'fast' }
118
+ );
119
+ if (!fastModeDowngraded(decision)) return false;
120
+ delete requestBody.speed;
121
+ return true;
96
122
  }
@@ -11,7 +11,6 @@
11
11
  import { traceAgentFetch } from '../agent-trace.mjs';
12
12
  import { createAbortController } from '../../../shared/abort-controller.mjs';
13
13
  import { notifyCurrentAnthropicRateLimit } from './admission-scheduler.mjs';
14
- import { noteFastModeCapacityError } from './anthropic-fast-mode.mjs';
15
14
  import { noteRequestServiceTier } from '../../../shared/llm/usage-context.mjs';
16
15
  import {
17
16
  ANTHROPIC_RETRY_BACKOFF_MS,
@@ -21,6 +20,7 @@ import {
21
20
  emitProviderRetryStage,
22
21
  withRetry,
23
22
  } from './retry-classifier.mjs';
23
+ import { ANTHROPIC_MAX_CONSECUTIVE_529 } from './retry-backoff.mjs';
24
24
  import { postMessages } from './anthropic-oauth-request/gzip-post.mjs';
25
25
  import { removeAbortListener, wireRequestAbort } from './anthropic-oauth-request/abort-wiring.mjs';
26
26
  import { judgeInitialStatus } from './anthropic-oauth-request/initial-status.mjs';
@@ -38,6 +38,7 @@ export { anthropicQuotaError } from './anthropic-oauth-request/initial-status.mj
38
38
  * @param {AbortSignal|null} deps.totalSignal session-lifetime pass-through
39
39
  * @param {(requestBody: object) => string} deps.betaHeadersFor
40
40
  * @param {((stage: string) => void)|null} deps.onStageChange
41
+ * @param {{ state: { consecutive529Errors: number } }|null} [deps.overload] the send's shared overload counter
41
42
  */
42
43
  export function createAnthropicOAuthRequest({
43
44
  provider,
@@ -48,8 +49,10 @@ export function createAnthropicOAuthRequest({
48
49
  totalSignal,
49
50
  betaHeadersFor,
50
51
  onStageChange,
52
+ overload = null,
51
53
  }) {
52
54
  const requestTimeoutMs = anthropicRequestTimeoutMs();
55
+ const isEnterprise = String(provider?.credentials?.subscriptionType || '').toLowerCase() === 'enterprise';
53
56
  const cleanupCancelHandler = (handler) => removeAbortListener(totalSignal, handler);
54
57
 
55
58
  const doRequest = async (accessToken, requestSignal = null, requestBody = body) => {
@@ -100,7 +103,7 @@ export function createAnthropicOAuthRequest({
100
103
  try {
101
104
  await judgeInitialStatus(result, requestBody, { provider, cleanupCancelHandler });
102
105
  } catch (err) {
103
- // Fast-pool 429 before any byte was sampled: `speed` is already
106
+ // Fast-pool 429/529 before any byte was sampled: `speed` is already
104
107
  // dropped, so replay once at standard speed within this attempt.
105
108
  if (err?.fastDowngraded !== true) throw err;
106
109
  const replay = await doRequestImpl(accessToken, attemptSignal, requestBody);
@@ -118,23 +121,25 @@ export function createAnthropicOAuthRequest({
118
121
  // Max/Pro OAuth sessions use subscription quota windows. Claude
119
122
  // Code fails their 429s immediately rather than waiting through
120
123
  // the API-key/PAYG retry budget (which may carry hours-long
121
- // Retry-After values).
122
- retry429: false,
124
+ // Retry-After values). Enterprise bills PAYG-style, so its 429s
125
+ // are retried like an API key's.
126
+ retry429: isEnterprise,
123
127
  retry529: opts.retry529,
124
128
  perAttemptTimeoutMs: requestTimeoutMs,
125
129
  perAttemptLabel: 'Anthropic OAuth initial response',
126
130
  provider: 'anthropic',
131
+ // This transport honors the positive x-should-retry override.
132
+ honorShouldRetryTrue: true,
127
133
  model: useModel,
128
134
  fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
135
+ // One overload counter for the send: SSE overloads count toward the
136
+ // same limit as request-loop 529s.
137
+ overloadState: overload?.state,
138
+ overloadFallbackAfter: ANTHROPIC_MAX_CONSECUTIVE_529,
129
139
  onRetry: ({ attempt, maxAttempts, lastErr, delayMs, delayReason }) => {
130
140
  emitProviderRetryStage(onStageChange, { attempt: attempt + 1, maxAttempts, lastErr, delayMs });
131
141
  const status = Number(lastErr?.httpStatus || lastErr?.status || lastErr?.response?.status || 0) || null;
132
142
  if (status === 429) notifyCurrentAnthropicRateLimit(lastErr);
133
- // Fast capacity exhausted: drop `speed` so the replay runs at
134
- // standard speed instead of re-hitting the drained pool.
135
- if (requestBody?.speed === 'fast' && noteFastModeCapacityError(lastErr, { fast: true }) !== 'retry-fast') {
136
- delete requestBody.speed;
137
- }
138
143
  const reason = status || lastErr?.code || lastErr?.message || 'network error';
139
144
  const suffix = delayReason ? ` (${delayReason})` : '';
140
145
  try {
@@ -42,8 +42,12 @@ import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
42
42
  import { ANTHROPIC_MAX_MIDSTREAM_RETRIES, parseSSEStream, _classifyMidstreamError } from './anthropic-sse.mjs';
43
43
  import { buildAnthropicBetaHeaders, supportsAnthropicFastMode } from './anthropic-betas.mjs';
44
44
  import { applyAnthropicServerFallback } from './anthropic-server-fallback.mjs';
45
- import { fastModeAvailable } from './anthropic-fast-mode.mjs';
45
+ import { fastModeAvailable, fastModeDowngraded, noteFastModeCapacityError } from './anthropic-fast-mode.mjs';
46
46
  import { ANTHROPIC_VERSION, anthropicQuotaError, createAnthropicOAuthRequest } from './anthropic-oauth-request.mjs';
47
+ import { isRefreshableAuthRejection } from './anthropic-oauth-request/initial-status.mjs';
48
+ import { applyAnthropicErrorContract } from './anthropic-error-contract.mjs';
49
+ import { adjustMaxTokensForContextOverflow } from './anthropic-max-tokens-recovery.mjs';
50
+ import { announceAnthropicModelFallback, createAnthropicOverloadTracker } from './anthropic-overload-fallback.mjs';
47
51
  import { createAnthropicOAuthRecovery } from './anthropic-oauth-recovery.mjs';
48
52
  import { createMidState, createMidstreamRecovery } from './anthropic-oauth-midstream.mjs';
49
53
  import { assertAnthropicStreamNotEmpty } from './anthropic-midstream-recovery.mjs';
@@ -91,12 +95,6 @@ function logQuiet(line) {
91
95
  if (!process.env.MIXDOG_QUIET_PROVIDER_LOG) process.stderr.write(`[anthropic-oauth] ${line}\n`);
92
96
  }
93
97
 
94
- function requiresSystemPrefix(model) {
95
- // Every Claude OAuth model needs the first-party system prefix for OAuth
96
- // pool routing; without it the server answers 429 rate_limit_error "Error".
97
- return /^claude-/.test(String(model || '').toLowerCase());
98
- }
99
-
100
98
  function buildOAuthBetaHeaders(body, { fastMode = false, toolSearch = false, model, opts = {} } = {}) {
101
99
  return buildAnthropicBetaHeaders({
102
100
  base: usesAnthropicEffortBody(body) ? `${OAUTH_BETA_HEADERS},${EFFORT_CONFIGURATION_BETA}` : OAUTH_BETA_HEADERS,
@@ -114,23 +112,21 @@ function buildOAuthBetaHeaders(body, { fastMode = false, toolSearch = false, mod
114
112
  // content (even the prefix concatenated with extra text in the same block)
115
113
  // falls into the standard pool and Opus/Sonnet return 429. Splitting into
116
114
  // two blocks — [prefix, rest] — keeps both routing and user instructions.
117
- function buildSystemBlocks(systemMsgs, model, systemTtl, tier3Ttl) {
115
+ function buildSystemBlocks(systemMsgs, systemTtl, tier3Ttl) {
118
116
  // systemMsgs is an array of { content, cacheTier } — each non-empty element
119
117
  // becomes its own Anthropic content block with its own cache_control
120
118
  // breakpoint. Blocks tagged cacheTier:'tier3' (BP3 sessionMarker) take the
121
119
  // tier3 TTL; every other block (BP1 baseRules / BP2 stableSystem) takes the
122
120
  // system TTL. Invariant: callers must pass an array.
123
121
  const items = systemBlockItems(systemMsgs);
124
- const gated = requiresSystemPrefix(model);
125
122
 
126
- const blocks = [];
127
- if (gated) {
128
- blocks.push({ type: 'text', text: CLAUDE_CODE_SYSTEM_PREFIX });
129
- }
123
+ // Every model on the OAuth route is routed by this prefix block, so it is
124
+ // sent unconditionally rather than keyed on the model id.
125
+ const blocks = [{ type: 'text', text: CLAUDE_CODE_SYSTEM_PREFIX }];
130
126
  for (let i = 0; i < items.length; i++) {
131
127
  let body = items[i].text;
132
128
  // Strip a duplicated OAuth system prefix from the first block if present.
133
- if (gated && i === 0 && body.startsWith(CLAUDE_CODE_SYSTEM_PREFIX)) {
129
+ if (i === 0 && body.startsWith(CLAUDE_CODE_SYSTEM_PREFIX)) {
134
130
  body = body.slice(CLAUDE_CODE_SYSTEM_PREFIX.length).trim();
135
131
  if (!body) continue;
136
132
  }
@@ -170,8 +166,8 @@ function buildSystemBlocks(systemMsgs, model, systemTtl, tier3Ttl) {
170
166
  // 1. MIXDOG_ANTHROPIC_MAX_OUTPUT_TOKENS env override, if set, wins outright.
171
167
  // 2. Catalog outputTokens (trusted over hardcoded heuristics when present),
172
168
  // clamped to [MAX_TOKENS_FLOOR, safetyCap].
173
- // 3. Static MAX_TOKENS table / family heuristic fallback when the catalog
174
- // has no entry for this model, also clamped to the safety cap.
169
+ // 3. Shared model catalog outputTokens, then the floor, when the live
170
+ // roster has no entry for this model.
175
171
  function resolveMaxTokens(model) {
176
172
  return resolveAnthropicMaxTokens(model, { catalogLookup: _catalogOutputTokens });
177
173
  }
@@ -202,7 +198,7 @@ function buildRequestBody(messages, model, tools, sendOpts) {
202
198
  // Each system message becomes its own Anthropic content block with its own
203
199
  // breakpoint: BP1 baseRules + BP2 stableSystem at ttls.system, BP3
204
200
  // sessionMarker (cacheTier:'tier3') at ttls.tier3.
205
- const systemBlocks = buildSystemBlocks(systemMsgs, model, ttls?.system, ttls?.tier3);
201
+ const systemBlocks = buildSystemBlocks(systemMsgs, ttls?.system, ttls?.tier3);
206
202
 
207
203
  // Message-tail cache budget (4-BP layout + ANTHROPIC_MSG_SLOTS) is shared
208
204
  // with anthropic.mjs — see resolveAnthropicMessageCacheSlots.
@@ -480,6 +476,12 @@ export class AnthropicOAuthProvider {
480
476
  // totalSignal is therefore a pure pass-through of externalSignal with no timer.
481
477
  const totalTimeout = createPassthroughSignal(externalSignal);
482
478
  const totalSignal = totalTimeout.signal;
479
+ // Request-loop 529s and SSE overload events share one counter; at the
480
+ // limit the configured fallback model takes over the whole send.
481
+ const overload = createAnthropicOverloadTracker({
482
+ model: useModel,
483
+ fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
484
+ });
483
485
  const { requestWithRetry, cleanupCancelHandler } = createAnthropicOAuthRequest({
484
486
  provider: this,
485
487
  opts,
@@ -487,6 +489,7 @@ export class AnthropicOAuthProvider {
487
489
  useModel,
488
490
  sessionId,
489
491
  totalSignal,
492
+ overload,
490
493
  betaHeadersFor: (requestBody) =>
491
494
  buildOAuthBetaHeaders(requestBody, {
492
495
  fastMode: this.fastModeBetaHeaderLatched,
@@ -516,24 +519,14 @@ export class AnthropicOAuthProvider {
516
519
  recovery,
517
520
  onStageChange,
518
521
  retry529: opts.retry529 !== false,
522
+ overload,
519
523
  });
520
524
 
525
+ let maxTokensAdjusted = false;
521
526
  try {
522
527
  for (let attemptIndex = 0; attemptIndex <= ANTHROPIC_MAX_MIDSTREAM_RETRIES; attemptIndex++) {
523
528
  let response, controller, cancelHandler;
524
- try {
525
- ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
526
- } catch (err) {
527
- if (err instanceof AnthropicFallbackTriggeredError) {
528
- process.stderr.write(`[anthropic-oauth] ${err.message}\n`);
529
- return this.send(messages, err.fallbackModel, tools, {
530
- ...opts,
531
- fallbackModel: undefined,
532
- _fallbackTriggered: true,
533
- });
534
- }
535
- throw err;
536
- }
529
+ ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
537
530
 
538
531
  // Refresh on 401, and only Anthropic's exact official revoked-token
539
532
  // 403 signature. Ordinary permission/policy 403s are terminal.
@@ -541,8 +534,7 @@ export class AnthropicOAuthProvider {
541
534
  if (response.status === 401 || response.status === 403) {
542
535
  rejectedAuthBody = await response.text().catch(() => '');
543
536
  }
544
- const revoked403 = response.status === 403 && String(rejectedAuthBody).includes('OAuth token has been revoked');
545
- if (response.status === 401 || revoked403) {
537
+ if (isRefreshableAuthRejection(response.status, rejectedAuthBody)) {
546
538
  process.stderr.write(`[anthropic-oauth] ${response.status} — forcing refresh and retrying once\n`);
547
539
  cleanupCancelHandler(cancelHandler);
548
540
  // Body was drained above; abort closes any remaining transport
@@ -555,6 +547,20 @@ export class AnthropicOAuthProvider {
555
547
  rejectedAuthBody = null;
556
548
  }
557
549
 
550
+ // "input length and max_tokens exceed context limit": shrink max_tokens
551
+ // to what the context has room for and replay once.
552
+ if (response.status === 400 && !maxTokensAdjusted) {
553
+ rejectedAuthBody = await response.text().catch(() => '');
554
+ if (adjustMaxTokensForContextOverflow(body, 400, rejectedAuthBody)) {
555
+ maxTokensAdjusted = true;
556
+ cleanupCancelHandler(cancelHandler);
557
+ try {
558
+ controller?.abort?.();
559
+ } catch {}
560
+ ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
561
+ rejectedAuthBody = null;
562
+ }
563
+ }
558
564
  if (!response.ok) {
559
565
  cleanupCancelHandler(cancelHandler);
560
566
  return await this._resendAfterRejection({
@@ -615,6 +621,18 @@ export class AnthropicOAuthProvider {
615
621
  }
616
622
  }
617
623
  throw midstream.exhaustedError();
624
+ } catch (err) {
625
+ // The fallback trigger can surface from the request loop, a mid-stream
626
+ // overload, or the non-streaming recovery: handle it around the whole
627
+ // operation, not just the first request.
628
+ if (!(err instanceof AnthropicFallbackTriggeredError)) throw err;
629
+ process.stderr.write(`[anthropic-oauth] ${err.message}\n`);
630
+ announceAnthropicModelFallback(onStageChange, _displayModel(useModel), _displayModel(err.fallbackModel));
631
+ return await this.send(messages, err.fallbackModel, tools, {
632
+ ...opts,
633
+ fallbackModel: undefined,
634
+ _fallbackTriggered: true,
635
+ });
618
636
  } finally {
619
637
  totalTimeout.cleanup();
620
638
  }
@@ -632,6 +650,21 @@ export class AnthropicOAuthProvider {
632
650
  throw anthropicQuotaError(response.status, response.headers, safeText);
633
651
  }
634
652
 
653
+ // An account that cannot use fast mode answers the speed field with a
654
+ // 400. Fast mode is now off for this process, so replaying the turn once
655
+ // builds a standard-speed request.
656
+ if (
657
+ response.status === 400 &&
658
+ opts.fast === true &&
659
+ !opts._fastModeRetry &&
660
+ fastModeDowngraded(
661
+ noteFastModeCapacityError({ httpStatus: 400, message: scrubbedText }, { fast: true })
662
+ )
663
+ ) {
664
+ process.stderr.write(`[anthropic-oauth] fast mode is not enabled for this account; retrying at standard speed\n`);
665
+ return this.send(messages, useModel, tools, { ...opts, _fastModeRetry: true });
666
+ }
667
+
635
668
  // A newer model that rejects the mid-conversation effort update: switch
636
669
  // the updates off for it in this process and replay the turn once
637
670
  // without them (the request keeps its turn effort).
@@ -694,9 +727,14 @@ export class AnthropicOAuthProvider {
694
727
  return this.send(messages, fallbackModel || model, tools, { ...opts, _modelRetry: true });
695
728
  }
696
729
  const err = new Error(`Anthropic OAuth API ${response.status}: ${safeText}`);
697
- err.status = response.status;
698
- err.httpStatus = response.status;
699
- throw err;
730
+ // 413 stays `request_too_large` (a byte/media size refusal) and a 400
731
+ // "prompt is too long" stays `invalid_request_error`, so the core can tell
732
+ // a payload it must shrink from a context it can compact.
733
+ throw applyAnthropicErrorContract(err, {
734
+ status: response.status,
735
+ headers: response.headers,
736
+ bodyText: scrubbedText,
737
+ });
700
738
  }
701
739
 
702
740
  // Traces + catalog upkeep for a streamed turn, then the empty-stream guard.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * anthropic-overload-fallback.mjs — one overload counter per logical send,
3
+ * shared by the request loop (HTTP 529 before a stream exists) and the SSE
4
+ * parser (`overloaded_error` event after one started). Both reach it through
5
+ * the same error fields — `status: 529` / `code: 'overloaded_error'` — so a
6
+ * mix of the two trips the configured fallback model after the same number of
7
+ * consecutive overloads, instead of each path counting only its own.
8
+ *
9
+ * The request loop's counter is withRetry's `overloadState`; this tracker owns
10
+ * that object, so the SSE path increments the very counter the loop reads.
11
+ */
12
+ import { ANTHROPIC_MAX_CONSECUTIVE_529 } from './retry-backoff.mjs';
13
+ import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
14
+
15
+ export const OVERLOADED_ERROR_CODE = 'overloaded_error';
16
+
17
+ /** Consecutive 529s end the request only for API-key (non-subscriber) first-party Opus. */
18
+ export function isTerminalOverloadModel(model) {
19
+ return /^claude-opus-/.test(String(model || '').trim().toLowerCase());
20
+ }
21
+
22
+ export function isAnthropicOverloadError(err) {
23
+ const status = Number(err?.httpStatus || err?.status || err?.response?.status || 0);
24
+ return status === 529 || String(err?.code || '').toLowerCase() === OVERLOADED_ERROR_CODE;
25
+ }
26
+
27
+ /**
28
+ * @param {{ model: string, fallbackModel?: string|null, terminalAtLimit?: boolean }} deps fallbackModel is
29
+ * already undefined when this send IS the fallback (no second hop).
30
+ */
31
+ export function createAnthropicOverloadTracker({ model, fallbackModel, terminalAtLimit = false }) {
32
+ const canFallback = Boolean(fallbackModel) && fallbackModel !== model;
33
+ // Handed to every withRetry of the send as `overloadState`: the request
34
+ // loop increments this same object on each 529.
35
+ const state = { consecutive529Errors: 0 };
36
+ return {
37
+ state,
38
+ // Repeated-overload stop, handed to withRetry as `stopAfterRepeated529`: with no
39
+ // fallback model, API-key Opus stops after repeated 529s. A configured
40
+ // fallback always wins (the executor checks it first).
41
+ stopAfterRepeated529: terminalAtLimit && !canFallback,
42
+ /**
43
+ * An overload SSE error. Returns the fallback trigger to throw once the
44
+ * consecutive limit is reached and a fallback model is configured.
45
+ */
46
+ noteStreamOverload(err) {
47
+ state.consecutive529Errors += 1;
48
+ if (state.consecutive529Errors < ANTHROPIC_MAX_CONSECUTIVE_529) return null;
49
+ if (canFallback) return new AnthropicFallbackTriggeredError(model, fallbackModel, err);
50
+ // No fallback model: API-key Opus stops here ("Repeated 529 Overloaded
51
+ // errors") instead of spending the full budget.
52
+ if (!terminalAtLimit) return null;
53
+ err.repeated529 = true;
54
+ return err;
55
+ },
56
+ };
57
+ }
58
+
59
+ /** Visible model-switch notice on the existing display-only stage channel. */
60
+ export function announceAnthropicModelFallback(onStageChange, fromLabel, toLabel) {
61
+ if (typeof onStageChange !== 'function') return;
62
+ try {
63
+ onStageChange('reconnecting', {
64
+ message: `${fromLabel} is overloaded — continuing on ${toLabel}`,
65
+ });
66
+ } catch {
67
+ /* display-only */
68
+ }
69
+ }
@@ -6,6 +6,10 @@ function normalizedModelId(value) {
6
6
  return typeof raw === 'string' ? raw.trim() : '';
7
7
  }
8
8
 
9
+ // Kept as an id pattern: no catalog field declares server-side fallback, and
10
+ // `fallbacks` is a beta-gated body field other models reject (a wrong "yes" is
11
+ // a 400, a wrong "no" only skips an optional refusal fallback), so unknown ids
12
+ // stay off.
9
13
  export function supportsAnthropicServerFallback(model) {
10
14
  const id = normalizeClaudeModelId(normalizedModelId(model));
11
15
  return /^claude-(?:opus|fable)-5(?:$|[-@])/.test(id);
@@ -109,6 +109,13 @@ function _anthropicSseError(event, labels) {
109
109
  err.httpStatus = status;
110
110
  err.status = status;
111
111
  }
112
+ // Provider-boundary contract: `code` is the lowercased Anthropic error type
113
+ // (an overload carries `overloaded_error` + 529, which the overload counter
114
+ // reads); an untyped `error` frame keeps the generic SSE code. A 413 is a
115
+ // byte-size refusal, never a prompt-token one.
116
+ const wireType = typeof type === 'string' ? type.trim().toLowerCase() : '';
117
+ if (status === 413) err.code = 'request_too_large';
118
+ else if (wireType && wireType !== 'error') err.code = wireType;
112
119
  // Wire-error marker: an error type OUTSIDE the documented enumeration
113
120
  // (no status resolved above) default-retries under the shared wire-error
114
121
  // contract instead of failing the turn as 'unknown'. Typed statuses and
@@ -162,7 +169,7 @@ function applyAnthropicSseEvent(framedEvent, { turn, watchdogs, state, progress,
162
169
  return false;
163
170
  }
164
171
  } catch (err) {
165
- if (err?.code === 'EANTHROPIC_SSE_ERROR') throw err;
172
+ if (err?.name === 'AnthropicSseError') throw err;
166
173
  /* skip malformed events */
167
174
  return false;
168
175
  }
@@ -1,6 +1,11 @@
1
- import { getAgentApiKey } from '../../../shared/provider-api-key.mjs';
1
+ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
2
2
  import { sanitizeToolPairs } from '../session/context-utils.mjs';
3
3
  import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
4
+ import {
5
+ announceAnthropicModelFallback,
6
+ createAnthropicOverloadTracker,
7
+ isTerminalOverloadModel,
8
+ } from './anthropic-overload-fallback.mjs';
4
9
  import { readStreamOutcome } from './lib/stream-outcome.mjs';
5
10
  import { createPassthroughSignal } from '../stall-policy.mjs';
6
11
  import { createAbortController } from '../../../shared/abort-controller.mjs';
@@ -42,9 +47,19 @@ export class AnthropicProvider {
42
47
  constructor(config) {
43
48
  this.config = config || {};
44
49
  this.name = this.config.name || 'anthropic';
45
- this.apiKey = this.config.apiKey || (this.name === 'anthropic' ? process.env.ANTHROPIC_API_KEY : null);
50
+ this.apiKey = resolveProviderApiKey(this.name, this.config) || null;
46
51
  this.client = this._createClient(this.apiKey);
47
52
  }
53
+ // Client for the currently resolved key; rebuilt when the key changed since
54
+ // it was built (e.g. a keyless-constructed instance).
55
+ _currentClient() {
56
+ const key = resolveProviderApiKey(this.name, this.config) || null;
57
+ if (key && key !== this.apiKey) {
58
+ this.apiKey = key;
59
+ this.client = this._createClient(key);
60
+ }
61
+ return this.client;
62
+ }
48
63
  // Tool-search is a request capability, not an account capability. Keep it
49
64
  // off the client defaults and add it only on turns that actually serialize
50
65
  // defer_loading tools.
@@ -55,8 +70,10 @@ export class AnthropicProvider {
55
70
  const custom = isCustomProviderName(this.name);
56
71
  const keyless = custom && this.config.keyless === true;
57
72
  const betaHeaders = this.config.disableBetaHeaders || custom ? null : buildAnthropicBetaHeaders();
73
+ let credentials = { apiKey };
74
+ if (custom) credentials = { apiKey: keyless ? '' : apiKey || '', authToken: null };
58
75
  return new (loadAnthropic())({
59
- ...(custom ? { apiKey: keyless ? '' : apiKey || '', authToken: null } : { apiKey }),
76
+ ...credentials,
60
77
  ...(this.config.baseURL ? { baseURL: this.config.baseURL } : {}),
61
78
  defaultHeaders: {
62
79
  ...(betaHeaders ? { 'anthropic-beta': betaHeaders } : {}),
@@ -68,10 +85,7 @@ export class AnthropicProvider {
68
85
  }
69
86
  reloadApiKey() {
70
87
  try {
71
- const newKey =
72
- getAgentApiKey(this.name) ||
73
- this.config.apiKey ||
74
- (this.name === 'anthropic' ? process.env.ANTHROPIC_API_KEY : null);
88
+ const newKey = reloadProviderApiKey(this.name, this.config);
75
89
  if (newKey) {
76
90
  this.config = { ...(this.config || {}), apiKey: newKey };
77
91
  this.apiKey = newKey;
@@ -157,16 +171,24 @@ export class AnthropicProvider {
157
171
  } catch {}
158
172
  };
159
173
 
174
+ // Request-loop 529s and SSE overload events share one counter; at the
175
+ // limit the configured fallback model takes over the whole send.
176
+ const overload = createAnthropicOverloadTracker({
177
+ model: useModel,
178
+ fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
179
+ terminalAtLimit: !isCustomProviderName(this.name) && isTerminalOverloadModel(useModel),
180
+ });
160
181
  // The SDK calls and their initial-response retry policy
161
182
  // (anthropic-api-transport.mjs).
162
183
  const transport = createAnthropicApiTransport({
163
- client: this.client,
184
+ client: this._currentClient(),
164
185
  label: this.name,
165
186
  opts,
166
187
  useModel,
167
188
  params,
168
189
  requestHeaders,
169
190
  totalSignal,
191
+ overload,
170
192
  });
171
193
  // Usage accounting + caller-visible projection, shared by the streaming
172
194
  // success path and the non-streaming fallback (anthropic-turn-result.mjs).
@@ -204,8 +226,10 @@ export class AnthropicProvider {
204
226
  recovery,
205
227
  onStageChange,
206
228
  retry529: opts.retry529 !== false,
229
+ overload,
207
230
  });
208
231
 
232
+ let streamingFallbackTried = false;
209
233
  try {
210
234
  for (let attemptIndex = 0; attemptIndex <= ANTHROPIC_MAX_MIDSTREAM_RETRIES; attemptIndex++) {
211
235
  const streamController = createAbortController();
@@ -265,13 +289,13 @@ export class AnthropicProvider {
265
289
 
266
290
  return buildTurnResult(parseResult);
267
291
  } catch (err) {
268
- if (err instanceof AnthropicFallbackTriggeredError) {
269
- process.stderr.write(`[${this.name}] ${err.message}\n`);
270
- return this._doSend(messages, err.fallbackModel, tools, {
271
- ...opts,
272
- fallbackModel: undefined,
273
- _fallbackTriggered: true,
274
- });
292
+ // The fallback trigger is handled around the whole send below.
293
+ if (err instanceof AnthropicFallbackTriggeredError) throw err;
294
+ // Gateways that 404 the streaming endpoint but serve non-streaming:
295
+ // one non-streaming replay (nothing was streamed yet).
296
+ if (err?.initialResponseError === true && Number(err.status) === 404 && !streamingFallbackTried) {
297
+ streamingFallbackTried = true;
298
+ return await recovery.issueNonStreamingFallback(streamController, err);
275
299
  }
276
300
  // Ordered recovery ladder: canonical outcome stamp, acknowledged
277
301
  // non-streaming replay for exposed output, bounded streaming
@@ -291,13 +315,24 @@ export class AnthropicProvider {
291
315
  }
292
316
  }
293
317
  throw midstream.exhaustedError();
318
+ } catch (err) {
319
+ // The fallback trigger can surface from the request loop, a mid-stream
320
+ // overload, or the non-streaming recovery: handle it around the whole
321
+ // operation, not just the first request.
322
+ if (!(err instanceof AnthropicFallbackTriggeredError)) throw err;
323
+ process.stderr.write(`[${this.name}] ${err.message}\n`);
324
+ announceAnthropicModelFallback(onStageChange, useModel, err.fallbackModel);
325
+ return await this._doSend(messages, err.fallbackModel, tools, {
326
+ ...opts,
327
+ fallbackModel: undefined,
328
+ _fallbackTriggered: true,
329
+ });
294
330
  } finally {
295
331
  totalTimeout.cleanup();
296
332
  }
297
333
  }
298
334
  async listModels() {
299
- const apiKey =
300
- this.apiKey || this.config?.apiKey || (this.name === 'anthropic' ? process.env.ANTHROPIC_API_KEY : null);
335
+ const apiKey = resolveProviderApiKey(this.name, this.config);
301
336
  if (!apiKey) return MODELS;
302
337
  try {
303
338
  const base = String(this.config?.baseURL || 'https://api.anthropic.com').replace(/\/+$/, '');
@@ -329,6 +364,6 @@ export class AnthropicProvider {
329
364
  async isAvailable() {
330
365
  // Availability probes must not spend tokens or depend on a live
331
366
  // network. Dispatch owns authentication validation and 401 reload.
332
- return !!(this.apiKey || this.config?.apiKey || (this.name === 'anthropic' && process.env.ANTHROPIC_API_KEY));
367
+ return !!resolveProviderApiKey(this.name, this.config);
333
368
  }
334
369
  }