mixdog 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/scripts/provider-pingpong-live.mjs +509 -0
  6. package/src/agents/advisor/AGENT.md +1 -1
  7. package/src/agents/advisor/agent.json +1 -1
  8. package/src/agents/reviewer/AGENT.md +3 -3
  9. package/src/agents/reviewer/agent.json +1 -1
  10. package/src/defaults/shipped-history.json +6 -0
  11. package/src/defaults/skills/setup/SKILL.md +6 -1
  12. package/src/defaults/skills/setup/references/actions.md +44 -22
  13. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  14. package/src/headless-exec.mjs +15 -8
  15. package/src/lib/keychain-cjs.cjs +7 -4
  16. package/src/output-styles/common.md +11 -10
  17. package/src/repl/slash-commands.mjs +16 -1
  18. package/src/rules/agent/AGENT.md +5 -5
  19. package/src/rules/lead/LEAD.md +5 -5
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  22. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  23. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  33. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  36. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  37. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  38. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  39. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  40. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  41. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  44. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  45. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  47. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
  49. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
  50. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  51. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
  58. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  63. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  65. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
  70. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  71. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  72. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
  73. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
  74. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
  75. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  76. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
  77. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  79. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  80. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
  81. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  83. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
  84. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  85. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  86. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
  87. package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
  88. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  89. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  90. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  91. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  92. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  93. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
  94. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
  96. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
  97. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  99. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  100. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  102. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
  103. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
  105. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
  106. package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
  107. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  109. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
  110. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  113. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  114. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  115. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  116. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  117. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  118. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  120. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  121. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  122. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  123. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  125. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  126. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  127. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  128. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  129. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  138. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  140. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  141. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  142. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
  143. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
  144. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  145. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  147. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
  148. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  150. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
  151. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
  153. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
  154. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
  155. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
  156. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
  157. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  158. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  159. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  160. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
  161. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  163. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
  164. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
  166. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  167. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  168. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  169. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  170. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
  171. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
  172. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  173. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
  174. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
  175. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  176. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  177. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  178. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  179. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  180. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  181. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  182. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  183. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  184. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  185. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  186. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  187. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  188. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
  189. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  190. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  191. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  192. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
  193. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  195. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
  196. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  197. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  198. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  199. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  200. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  201. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  202. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  203. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  204. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  205. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  206. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  207. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  208. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  210. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  211. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  212. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  213. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  214. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  215. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  216. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  217. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  218. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  219. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  220. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  221. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  222. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
  223. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  224. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  225. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  226. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  227. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  228. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  229. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  230. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  231. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  232. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  233. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  234. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  235. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  236. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  237. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  238. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  239. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  240. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
  241. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  242. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  243. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  244. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  245. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  246. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  248. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  249. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  250. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  251. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
  252. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  253. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  254. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  255. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  256. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  257. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  258. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
  259. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  260. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  261. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  262. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  263. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  264. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  265. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  266. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  267. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
  268. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  269. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  270. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  271. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  272. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  273. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  274. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  275. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  276. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  277. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  278. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  279. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  280. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  281. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
  282. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
  283. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
  284. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  285. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  286. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  287. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  288. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  289. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  290. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  291. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  292. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  293. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  294. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  295. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  296. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  297. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  298. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  299. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  300. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  301. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  302. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  303. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  304. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  305. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  306. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  307. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  308. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  309. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  310. package/src/runtime/github/tool.mjs +92 -2
  311. package/src/runtime/memory/index.mjs +1 -0
  312. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  313. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  314. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  315. package/src/runtime/memory/lib/pg/process.mjs +65 -50
  316. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  317. package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
  318. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  319. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  320. package/src/runtime/office/README.md +2 -2
  321. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  322. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  323. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  324. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  325. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  326. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  327. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  328. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  329. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  330. package/src/runtime/office/core/office-sessions.mjs +5 -0
  331. package/src/runtime/office/core/office-transactions.mjs +3 -0
  332. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  333. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  334. package/src/runtime/office/index.mjs +30 -0
  335. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  336. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  337. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  338. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  339. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  340. package/src/runtime/office/shared/values.mjs +0 -5
  341. package/src/runtime/shared/background-tasks.mjs +92 -14
  342. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  343. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  344. package/src/runtime/shared/buffered-appender.mjs +18 -0
  345. package/src/runtime/shared/config.mjs +78 -18
  346. package/src/runtime/shared/err-text.mjs +31 -0
  347. package/src/runtime/shared/error-presentation.mjs +159 -19
  348. package/src/runtime/shared/file-permissions.d.mts +6 -1
  349. package/src/runtime/shared/file-permissions.mjs +54 -6
  350. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  351. package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
  352. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  353. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  354. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  355. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  356. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  357. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  358. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  359. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  360. package/src/runtime/shared/loopback-listener.mjs +9 -0
  361. package/src/runtime/shared/model-display.mjs +13 -0
  362. package/src/runtime/shared/provider-api-key.mjs +2 -0
  363. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  364. package/src/runtime/shared/review-diff.mjs +52 -0
  365. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  366. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  367. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  368. package/src/runtime/shared/slow-span.mjs +114 -0
  369. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  370. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  371. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  372. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  373. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  374. package/src/runtime/shared/tool-card-model.mjs +5 -2
  375. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  376. package/src/runtime/shared/tool-primitives.mjs +13 -0
  377. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  378. package/src/runtime/shared/tool-surface.mjs +18 -3
  379. package/src/runtime/shared/tool-work-units.mjs +40 -4
  380. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  381. package/src/runtime/shared/transcript-writer.mjs +10 -2
  382. package/src/runtime/shared/turn-changes.mjs +670 -274
  383. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  384. package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
  385. package/src/runtime/shared/turn-snapshot.mjs +986 -690
  386. package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
  387. package/src/runtime/shared/user-data-guard.mjs +262 -49
  388. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  389. package/src/runtime/shared/worktrees/contract.mjs +34 -24
  390. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  391. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  392. package/src/runtime/shared/worktrees/git.mjs +8 -3
  393. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  394. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  395. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  396. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  397. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  398. package/src/runtime/terminal-bridge/client.mjs +44 -11
  399. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  400. package/src/runtime/tidy/apply.mjs +47 -2
  401. package/src/runtime/tidy/run-engines.mjs +11 -5
  402. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  403. package/src/session-runtime/boot/apis.mjs +19 -5
  404. package/src/session-runtime/boot/facade.mjs +1 -0
  405. package/src/session-runtime/boot/infrastructure.mjs +12 -1
  406. package/src/session-runtime/boot/session.mjs +0 -1
  407. package/src/session-runtime/boot/tools.mjs +4 -1
  408. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  409. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  410. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  411. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  412. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  413. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  414. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  415. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  416. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
  417. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  418. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  419. package/src/session-runtime/lifecycle/teardown.mjs +73 -8
  420. package/src/session-runtime/lifecycle-api.mjs +1 -0
  421. package/src/session-runtime/mcp-glue/connect.mjs +46 -14
  422. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  423. package/src/session-runtime/mcp-glue.mjs +3 -0
  424. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  425. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  426. package/src/session-runtime/model-route/set-route.mjs +85 -17
  427. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  428. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  429. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  430. package/src/session-runtime/native-web-search.mjs +10 -1
  431. package/src/session-runtime/new-session-config.mjs +4 -2
  432. package/src/session-runtime/provider-auth/login.mjs +4 -0
  433. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  434. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  435. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  436. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  437. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  438. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  439. package/src/session-runtime/runtime-review-api.mjs +21 -2
  440. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  441. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  442. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  443. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  444. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  445. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  446. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  447. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  448. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  449. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  450. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  451. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  452. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  453. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  454. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  455. package/src/session-runtime/services/agent-tool.mjs +3 -0
  456. package/src/session-runtime/services/channel-admin.mjs +19 -3
  457. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  458. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  459. package/src/session-runtime/services/provider-admin.mjs +81 -15
  460. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  461. package/src/session-runtime/session-turn-api.mjs +14 -5
  462. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  463. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  464. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  465. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  466. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  467. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  468. package/src/session-runtime/skills-api.mjs +3 -5
  469. package/src/session-runtime/tool-surface.mjs +5 -1
  470. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  471. package/src/session-runtime/turn/session-ops.mjs +7 -5
  472. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  473. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  474. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  475. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  476. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  477. package/src/session-runtime/turn/turn-run.mjs +25 -9
  478. package/src/session-runtime/usage-stats-api.mjs +43 -11
  479. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  480. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  481. package/src/session-runtime/workspace/transitions.mjs +190 -15
  482. package/src/standalone/daemon-agent-control.mjs +9 -1
  483. package/src/standalone/daemon-drain-policy.mjs +8 -0
  484. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  485. package/src/standalone/daemon-owner.mjs +17 -4
  486. package/src/standalone/daemon-pg-stop.mjs +51 -0
  487. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  488. package/src/standalone/daemon-stop-budget.mjs +18 -0
  489. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  490. package/src/standalone/daemon-telemetry.mjs +74 -0
  491. package/src/standalone/daemon.mjs +64 -8
  492. package/src/standalone/desktop-service-registry.mjs +5 -0
  493. package/src/standalone/local-session-runtime.mjs +70 -19
  494. package/src/standalone/session-client.mjs +42 -0
  495. package/src/standalone/session-protocol.mjs +14 -0
  496. package/src/standalone/session-proxy/projection.mjs +41 -4
  497. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  498. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  499. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  500. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  501. package/src/standalone/session-service/agent-tree.mjs +12 -0
  502. package/src/standalone/session-service/quota-resume.mjs +8 -0
  503. package/src/standalone/session-service/retention/busy.mjs +15 -1
  504. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  505. package/src/standalone/session-service-api.mjs +11 -1
  506. package/src/standalone/session-service.mjs +1 -0
  507. package/src/standalone/session-state-patch.d.mts +2 -1
  508. package/src/standalone/session-state-patch.mjs +41 -1
  509. package/src/tui/App.jsx +25 -0
  510. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  511. package/src/tui/app/app-view.jsx +1 -0
  512. package/src/tui/app/btw-controller.mjs +118 -0
  513. package/src/tui/app/create-app-pickers.mjs +6 -0
  514. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  515. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  516. package/src/tui/app/extension-pickers.mjs +2 -1
  517. package/src/tui/app/model-options.mjs +11 -16
  518. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  519. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  520. package/src/tui/app/slash-commands.mjs +20 -5
  521. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  522. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  523. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  524. package/src/tui/app/use-global-key-input.mjs +5 -12
  525. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  526. package/src/tui/components/BtwPanel.jsx +53 -0
  527. package/src/tui/components/QuestionPanel.jsx +275 -0
  528. package/src/tui/components/StatusLine.jsx +2 -0
  529. package/src/tui/components/question-panel-model.mjs +156 -0
  530. package/src/tui/components/status-line/local-line.mjs +5 -2
  531. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  532. package/src/tui/dist/index.mjs +3221 -1500
  533. package/src/tui/session/context-state.mjs +4 -2
  534. package/src/tui/session/quota-resume.mjs +46 -14
  535. package/src/tui/session/session-api/commands.mjs +43 -24
  536. package/src/tui/session/session-api/controls.mjs +2 -2
  537. package/src/tui/session/session-api/extensions.mjs +22 -0
  538. package/src/tui/session/session-api/integrations.mjs +16 -2
  539. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  540. package/src/tui/session/session-api/lifecycle.mjs +7 -2
  541. package/src/tui/session/session-api/routes.mjs +71 -7
  542. package/src/tui/session/session-api/settings.mjs +11 -14
  543. package/src/tui/session/session-api.mjs +1 -0
  544. package/src/tui/session/session-flow/drain.mjs +7 -1
  545. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  546. package/src/tui/session/tool-approval.mjs +10 -7
  547. package/src/tui/session/transcript-spill.mjs +14 -7
  548. package/src/tui/session/turn-stream.mjs +174 -31
  549. package/src/tui/session/turn.mjs +46 -16
  550. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  551. package/src/ui/statusline-agents.mjs +15 -4
  552. package/src/ui/statusline.mjs +7 -3
  553. package/src/ui/streaming-markdown-heal.mjs +34 -3
  554. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  555. package/src/workflows/default/WORKFLOW.md +3 -1
  556. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  557. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -0,0 +1,720 @@
1
+ // Provider error kind: one semantic verdict for a thrown provider error,
2
+ // shared by retry policy, account rotation and the UI. Leaf module: it reads
3
+ // the typed fields provider boundaries stamp on errors (status, code,
4
+ // headers, retryAfterMs, resetAt, quotaScope, reauthRequired) and falls back
5
+ // to the older fields (httpStatus, responseFailed, providerError, SDK error
6
+ // bodies, RPC status strings). Message text is consulted only for untyped
7
+ // errors and only for unambiguous wordings.
8
+
9
+ export const PROVIDER_ERROR_KINDS = Object.freeze([
10
+ 'overloaded',
11
+ 'rate_limited',
12
+ 'quota_exhausted',
13
+ 'entitlement',
14
+ 'billing',
15
+ 'auth',
16
+ 'permission',
17
+ 'context_overflow',
18
+ 'request_too_large',
19
+ 'policy',
20
+ 'service_tier_unavailable',
21
+ 'invalid_request',
22
+ 'transient',
23
+ 'unknown',
24
+ ]);
25
+
26
+ const MAX_CAUSE_CHAIN_DEPTH = 8;
27
+
28
+ export function boundedCauseChain(err) {
29
+ const chain = [];
30
+ const seen = new Set();
31
+ let cursor = err;
32
+ while (cursor && chain.length < MAX_CAUSE_CHAIN_DEPTH && !seen.has(cursor)) {
33
+ chain.push(cursor);
34
+ seen.add(cursor);
35
+ cursor = cursor?.cause;
36
+ }
37
+ return chain;
38
+ }
39
+
40
+ const TOOL_SURFACE_EXCEEDS_CONTEXT = 'TOOL_SURFACE_EXCEEDS_CONTEXT';
41
+ const TOOL_SURFACE_MARKER = `(${TOOL_SURFACE_EXCEEDS_CONTEXT})`;
42
+
43
+ /**
44
+ * The local fixed-overhead refusal (session/loop/context-overflow.mjs), bare,
45
+ * wrapped as a cause, or persisted as its text. Never a provider overflow:
46
+ * its diagnostic names tools, and a tool named like a provider error code
47
+ * must not turn it into one.
48
+ */
49
+ export function isToolSurfaceExceedsContextError(err) {
50
+ if (!err || (typeof err !== 'string' && typeof err !== 'object' && typeof err !== 'function')) return false;
51
+ // Every node of the chain: an Error, or persisted failure text kept as a cause.
52
+ return boundedCauseChain(err).some((item) =>
53
+ typeof item === 'string'
54
+ ? item.includes(TOOL_SURFACE_MARKER)
55
+ : item?.code === TOOL_SURFACE_EXCEEDS_CONTEXT ||
56
+ (typeof item?.message === 'string' && item.message.includes(TOOL_SURFACE_MARKER))
57
+ );
58
+ }
59
+
60
+ export function headerValue(headers, name) {
61
+ if (!headers) return null;
62
+ const lower = name.toLowerCase();
63
+ if (typeof headers.get === 'function') return headers.get(name) ?? headers.get(lower);
64
+ for (const [key, value] of Object.entries(headers)) {
65
+ if (String(key).toLowerCase() === lower) return Array.isArray(value) ? value[0] : value;
66
+ }
67
+ return null;
68
+ }
69
+
70
+ function wireDetail(err) {
71
+ const failed = err?.responseFailed;
72
+ return failed?.response?.error || failed?.error || err?.providerError || failed || null;
73
+ }
74
+
75
+ // Every place a provider error may carry its response headers, outermost
76
+ // first: the error itself, the SDK response, preserved wire `error.headers`.
77
+ function headerSources(err) {
78
+ const detail = wireDetail(err);
79
+ return [
80
+ err?.headers,
81
+ err?.response?.headers,
82
+ err?.data?.responseHeaders,
83
+ err?.error?.headers,
84
+ detail?.headers,
85
+ err?.providerError?.headers,
86
+ err?.payload?.error?.headers,
87
+ ].filter((headers) => headers && typeof headers === 'object');
88
+ }
89
+
90
+ /** The first value of response header `name` across every preserved header source. */
91
+ export function errorHeaderValue(err, name) {
92
+ for (const headers of headerSources(err)) {
93
+ const value = headerValue(headers, name);
94
+ if (value != null && value !== '') return value;
95
+ }
96
+ return null;
97
+ }
98
+
99
+ export function typedErrorCode(err) {
100
+ const detail = wireDetail(err);
101
+ const canonical = err && typeof err === 'object' ? codeFields(err).canonical.find(Boolean) : null;
102
+ if (canonical) return canonical;
103
+ for (const field of [detail?.code, detail?.type, err?.providerErrorCode, err?.code, err?.error?.code]) {
104
+ if (typeof field === 'string' && field.trim()) return field.trim().toLowerCase();
105
+ }
106
+ return '';
107
+ }
108
+
109
+ // ── Retry-After ─────────────────────────────────────────────────────────────
110
+ const MESSAGE_DELAY_CODES = new Set(['rate_limit_exceeded', 'slow_down']);
111
+ const MESSAGE_DELAY_RE =
112
+ /(?:try again in|retry (?:again )?(?:in|after)|wait(?: for)?)\s*(\d+(?:\.\d+)?)\s*(ms|milliseconds?|s|secs?|seconds?|m|mins?|minutes?)\b/i;
113
+
114
+ // Zero or non-positive advice is no advice: the caller's local backoff applies
115
+ // and a later source may still advise.
116
+ function finitePositive(value) {
117
+ if (value == null || value === '') return null;
118
+ const n = Number(value);
119
+ return Number.isFinite(n) && n > 0 ? n : null;
120
+ }
121
+
122
+ function retryInfoDelayMs(details) {
123
+ if (!Array.isArray(details)) return null;
124
+ for (const detail of details) {
125
+ const delay = detail?.retryDelay;
126
+ let ms = null;
127
+ if (typeof delay === 'string') {
128
+ const match = delay.trim().match(/^(\d+(?:\.\d+)?)s$/);
129
+ if (match) ms = Number(match[1]) * 1000;
130
+ } else if (delay && typeof delay === 'object') {
131
+ ms = Number(delay.seconds || 0) * 1000 + Number(delay.nanos || 0) / 1_000_000;
132
+ }
133
+ if (finitePositive(ms) != null) return Math.ceil(ms);
134
+ }
135
+ return null;
136
+ }
137
+
138
+ function messageDelayMs(err) {
139
+ const detail = wireDetail(err) || err?.error || null;
140
+ for (const message of [detail?.message, err?.error?.message, err?.message]) {
141
+ if (typeof message !== 'string' || !message) continue;
142
+ const match = message.match(MESSAGE_DELAY_RE);
143
+ if (!match) continue;
144
+ const value = finitePositive(match[1]);
145
+ if (value == null) continue;
146
+ const unit = String(match[2] || '').toLowerCase();
147
+ if (unit === 'ms' || unit.startsWith('millisecond')) return Math.ceil(value);
148
+ if (unit === 'm' || unit.startsWith('min')) return Math.ceil(value * 60_000);
149
+ return Math.ceil(value * 1000);
150
+ }
151
+ return null;
152
+ }
153
+
154
+ /**
155
+ * The server-advised wait before re-issuing, in ms; null when the server gave
156
+ * none. Precedence: the boundary-stamped `retryAfterMs`, Retry-After headers
157
+ * (any preserved source), Google RetryInfo (`details` / SDK `errorDetails`),
158
+ * and finally the delay a TYPED rate_limit_exceeded / slow_down message names.
159
+ * Window resets (`resetAt`, anthropic-ratelimit-unified-reset) are never
160
+ * retry advice: they are reported separately for display and cooldown.
161
+ */
162
+ export function retryAfterMsFromError(err) {
163
+ if (!err || typeof err !== 'object') return null;
164
+ // A deadline captured once at the boundary (`retryAfterAt`)
165
+ // survives a transport switch: report what is left of it.
166
+ for (const item of boundedCauseChain(err).slice(0, 2)) {
167
+ const at = Number(item?.retryAfterAt);
168
+ if (Number.isFinite(at) && at > 0) return Math.max(0, at - Date.now());
169
+ }
170
+ for (const item of boundedCauseChain(err).slice(0, 2)) {
171
+ const stamped = finitePositive(item?.retryAfterMs);
172
+ if (stamped != null) return stamped;
173
+ }
174
+ const retryAfterMs = finitePositive(errorHeaderValue(err, 'retry-after-ms'));
175
+ if (retryAfterMs != null) return retryAfterMs;
176
+ const retryAfter = errorHeaderValue(err, 'retry-after');
177
+ if (retryAfter != null) {
178
+ const seconds = finitePositive(retryAfter);
179
+ if (seconds != null) return Math.ceil(seconds * 1000);
180
+ const untilDate = finitePositive(Date.parse(String(retryAfter)) - Date.now());
181
+ if (untilDate != null) return untilDate;
182
+ }
183
+ const detail = wireDetail(err);
184
+ for (const details of [
185
+ err.details,
186
+ err.error?.details,
187
+ err.data?.error?.details,
188
+ err.errorDetails,
189
+ err.error?.errorDetails,
190
+ detail?.details,
191
+ ]) {
192
+ const delay = retryInfoDelayMs(details);
193
+ if (delay != null) return delay;
194
+ }
195
+ if (MESSAGE_DELAY_CODES.has(typedErrorCode(err))) return messageDelayMs(err);
196
+ return null;
197
+ }
198
+
199
+ // ── Transport evidence ──────────────────────────────────────────────────────
200
+ export const TRANSIENT_SDK_NAMES = new Set([
201
+ 'APIConnectionError',
202
+ 'APIConnectionTimeoutError',
203
+ 'ConnectTimeoutError',
204
+ 'HeadersTimeoutError',
205
+ 'BodyTimeoutError',
206
+ ]);
207
+ const STALL_NAMES = new Set(['StreamStalledError', 'StreamStalledAbortError', 'AgentStallAbortError']);
208
+ // 'terminated' / 'other side closed' / 'socket hang up' are the body-read
209
+ // forms of the same disconnect (undici aborts the response body when the peer
210
+ // closes mid-stream and may not attach a cause).
211
+ export const BARE_FETCH_TRANSPORT_MESSAGE_RE =
212
+ /^(?:fetch failed|failed to fetch|couldn'?t fetch\.?|load failed|network error|terminated|other side closed|socket hang up)$/i;
213
+ const TRANSIENT_ERROR_CODES = new Set([
214
+ 'ECONNRESET',
215
+ 'ETIMEDOUT',
216
+ 'ESOCKETTIMEDOUT',
217
+ 'EAI_AGAIN',
218
+ 'ENOTFOUND',
219
+ 'EAI_NODATA',
220
+ 'ECONNREFUSED',
221
+ 'ENETUNREACH',
222
+ 'EHOSTUNREACH',
223
+ 'EPIPE',
224
+ 'EPROVIDERTIMEOUT',
225
+ 'EGEMINITIMEOUT',
226
+ 'ESTREAMSTALL',
227
+ 'EWSACQUIRETIMEOUT',
228
+ 'UND_ERR_SOCKET',
229
+ 'UND_ERR_CONNECT_TIMEOUT',
230
+ 'UND_ERR_HEADERS_TIMEOUT',
231
+ 'UND_ERR_BODY_TIMEOUT',
232
+ 'UND_ERR_CONNECT',
233
+ 'EPROTO',
234
+ 'UND_ERR_DESTROYED',
235
+ 'UND_ERR_CLOSED',
236
+ 'ECONNABORTED',
237
+ 'ENETRESET',
238
+ 'ERR_STREAM_DESTROYED',
239
+ 'ERR_HTTP2_STREAM_ERROR',
240
+ 'ERR_HTTP2_SESSION_ERROR',
241
+ 'ERR_HTTP2_INVALID_SESSION',
242
+ 'ERR_SOCKET_CONNECTION_TIMEOUT',
243
+ 'ERR_SSL_WRONG_VERSION_NUMBER',
244
+ 'ERR_STREAM_PREMATURE_CLOSE',
245
+ ]);
246
+ // TLS record corruption on a flaky link or a meddling proxy. Node builds the
247
+ // ERR_SSL_* code from the OpenSSL reason string, which differs across OpenSSL
248
+ // versions, so the stable suffix names the class.
249
+ export const TLS_RECORD_FAILURE_RE = /^ERR_SSL_[A-Z0-9_/]*BAD_RECORD_MAC$/i;
250
+
251
+ /** True for a socket/TLS errno that describes a transient transport fault. */
252
+ export function isTransientErrorCode(code) {
253
+ const value = String(code || '');
254
+ return TRANSIENT_ERROR_CODES.has(value) || TLS_RECORD_FAILURE_RE.test(value);
255
+ }
256
+
257
+ const TRANSIENT_WS_CLOSE_CODES = new Set([1000, 1001, 1005, 1006, 1011, 1012]);
258
+
259
+ function transportEvidence(err, chain, status) {
260
+ if (err.truncatedStream === true || err.code === 'TRUNCATED_STREAM' || err.streamStalled === true) return true;
261
+ if (TRANSIENT_WS_CLOSE_CODES.has(Number(err.wsCloseCode ?? err.streamCloseCode ?? 0))) return true;
262
+ if (chain.some((item) => isTransientErrorCode(item?.code))) return true;
263
+ const names = chain.map((item) => String(item?.name || ''));
264
+ if (names.some((name) => TRANSIENT_SDK_NAMES.has(name) || STALL_NAMES.has(name))) return true;
265
+ return !status && chain.some((item) => BARE_FETCH_TRANSPORT_MESSAGE_RE.test(String(item?.message || '').trim()));
266
+ }
267
+
268
+ // ── Context / size wording (untyped fallback) ───────────────────────────────
269
+ // Token-context refusals: the conversation is longer than the model's window.
270
+ export const TOKEN_CONTEXT_OVERFLOW_PATTERNS = [
271
+ /input (?:length|tokens?) exceeds? the context window/i,
272
+ /exceeds? the (?:maximum )?context (?:window|length)/i,
273
+ /maximum context length/i,
274
+ /context[_ ]length[_ ]exceeded/i,
275
+ /prompt is too long/i,
276
+ /reduce the length of (?:the )?(?:messages|input|prompt)/i,
277
+ // Wordings other backends use for the same refusal, each tied to a context
278
+ // or input-length noun so a tokens-per-minute rate limit never matches.
279
+ /prompt too long/i, // z.ai, Ollama
280
+ /prompt exceeds max length/i, // z.ai CN endpoint
281
+ /input is too long for requested model/i, // Amazon Bedrock
282
+ /input token count.*exceeds the maximum/i, // Google
283
+ /maximum prompt length is \d+/i, // xAI
284
+ /exceeds (?:the )?maximum allowed input length/i, // OpenRouter relays
285
+ /is longer than the model'?s context length/i, // Together AI
286
+ /exceeds the available context size/i, // llama.cpp server
287
+ /greater than the context length/i, // LM Studio
288
+ /context window exceeds limit/i, // MiniMax
289
+ /exceeded model token limit/i, // Kimi
290
+ /too large for model with \d+ maximum context length/i, // Mistral
291
+ /range of input length should be/i, // DashScope / Qwen
292
+ /model_context_window_exceeded/i, // z.ai finish reason surfaced as text
293
+ ];
294
+ // Byte/media size refusals: the request body itself is too large.
295
+ export const REQUEST_SIZE_PATTERNS = [
296
+ /request payload size exceeds the limit/i, // Google (400, byte-size limit)
297
+ // A 413 re-wrapped by a proxy or aggregator without its status.
298
+ /request entity too large/i,
299
+ /request exceeds the maximum size/i,
300
+ ];
301
+
302
+ // Capacity wording on an untyped 429 ("… is at capacity").
303
+ const CAPACITY_MESSAGE_RE = /\bat capacity\b|\boverloaded\b|\bserver is busy\b/i;
304
+ // Unambiguous quota wordings, read only under `legacyQuotaWording`.
305
+ const LEGACY_BILLING_MESSAGE_RE = /\binsufficient_quota\b|exceeded your (?:current )?quota/i;
306
+ const LEGACY_QUOTA_MESSAGE_RE = /\bquota_exceeded\b|\busage_limit_reached\b|hit your usage limit/i;
307
+
308
+ // ── Typed codes ─────────────────────────────────────────────────────────────
309
+ const CODE_KINDS = new Map(
310
+ Object.entries({
311
+ policy: [
312
+ 'cyber_policy',
313
+ 'bio_policy',
314
+ 'cyber_policy_violation',
315
+ 'bio_policy_violation',
316
+ 'misalignment_policy_violation',
317
+ 'content_policy_violation',
318
+ 'content_filter',
319
+ 'safety',
320
+ 'recitation',
321
+ 'invalid_prompt',
322
+ ],
323
+ billing: [
324
+ 'insufficient_quota',
325
+ 'credit_balance_exhausted',
326
+ 'organization_spend_limit_exceeded',
327
+ 'project_spend_limit_exceeded',
328
+ 'organization_usage_limit_exceeded',
329
+ 'billing_not_active',
330
+ 'billing_hard_limit_reached',
331
+ 'payment_required',
332
+ 'insufficient_g1_credits_balance',
333
+ ],
334
+ entitlement: ['usage_not_included'],
335
+ quota_exhausted: ['quota_exceeded', 'quota_exhausted', 'usage_limit_reached', 'provider_accounts_exhausted'],
336
+ context_overflow: ['context_length_exceeded', 'context_window_exceeded', 'model_context_window_exceeded'],
337
+ request_too_large: ['request_too_large', 'payload_too_large', 'request_entity_too_large'],
338
+ auth: ['invalid_api_key', 'authentication_error', 'unauthenticated', 'unauthorized', 'invalid_authentication'],
339
+ permission: ['permission_error', 'permission_denied', 'forbidden'],
340
+ overloaded: [
341
+ 'server_is_overloaded',
342
+ 'overloaded_error',
343
+ 'overloaded',
344
+ 'server_overloaded',
345
+ 'model_capacity_exhausted',
346
+ 'model_capacity_exceeded',
347
+ ],
348
+ // The requested service tier has no capacity: the same request at the
349
+ // same tier is refused again, so it is final rather than an overload.
350
+ service_tier_unavailable: ['flex_unavailable'],
351
+ rate_limited: ['rate_limit_exceeded', 'rate_limit_error', 'rate_limited', 'slow_down', 'too_many_requests'],
352
+ transient: [
353
+ 'server_error',
354
+ 'api_error',
355
+ 'internal',
356
+ 'internal_error',
357
+ 'unavailable',
358
+ 'service_unavailable',
359
+ 'deadline_exceeded',
360
+ 'timeout',
361
+ 'previous_response_not_found',
362
+ 'websocket_connection_limit_reached',
363
+ 'stream_disconnected',
364
+ 'network_error',
365
+ // Replay safety is still the stream-outcome gate's call (classifyError).
366
+ 'malformed_stream',
367
+ ],
368
+ invalid_request: [
369
+ 'invalid_request',
370
+ 'invalid_request_error',
371
+ 'invalid_argument',
372
+ 'failed_precondition',
373
+ 'bad_request',
374
+ 'not_found',
375
+ 'not_found_error',
376
+ 'model_not_found',
377
+ 'unsupported_parameter',
378
+ ],
379
+ }).flatMap(([kind, codes]) => codes.map((code) => [code, kind]))
380
+ );
381
+ const RESOURCE_EXHAUSTED = 'resource_exhausted';
382
+ // When several typed codes are present (an envelope type plus a specific
383
+ // code), the most specific verdict wins.
384
+ const KIND_RANK = [
385
+ 'policy',
386
+ 'billing',
387
+ 'entitlement',
388
+ 'quota_exhausted',
389
+ 'context_overflow',
390
+ 'request_too_large',
391
+ 'auth',
392
+ 'permission',
393
+ 'service_tier_unavailable',
394
+ 'overloaded',
395
+ 'rate_limited',
396
+ 'transient',
397
+ 'invalid_request',
398
+ ];
399
+
400
+ // OpenRouter's canonical `error_type` vocabulary, stable across its Chat,
401
+ // Responses and Messages skins (the native code beside it is lossy: on
402
+ // Responses an auth failure arrives as `server_error`, a context overflow as
403
+ // `invalid_prompt`), mapped onto the typed codes classified here.
404
+ const GATEWAY_ERROR_TYPE_CODES = Object.freeze({
405
+ authentication: 'authentication_error',
406
+ permission_denied: 'permission_denied',
407
+ payment_required: 'payment_required',
408
+ context_length_exceeded: 'context_length_exceeded',
409
+ payload_too_large: 'payload_too_large',
410
+ image_too_large: 'request_too_large',
411
+ rate_limit_exceeded: 'rate_limit_exceeded',
412
+ provider_overloaded: 'overloaded',
413
+ provider_unavailable: 'service_unavailable',
414
+ timeout: 'timeout',
415
+ server: 'server_error',
416
+ content_policy_violation: 'content_policy_violation',
417
+ refusal: 'content_policy_violation',
418
+ invalid_request: 'invalid_request',
419
+ invalid_prompt: 'invalid_request',
420
+ unprocessable: 'invalid_request',
421
+ not_found: 'not_found',
422
+ precondition_failed: 'failed_precondition',
423
+ });
424
+
425
+ /** The typed code a gateway `error_type` names, or null for an absent/unmapped one. */
426
+ export function gatewayErrorTypeCode(errorType) {
427
+ const key = typeof errorType === 'string' ? errorType.trim().toLowerCase() : '';
428
+ return key && Object.hasOwn(GATEWAY_ERROR_TYPE_CODES, key) ? GATEWAY_ERROR_TYPE_CODES[key] : null;
429
+ }
430
+
431
+ /**
432
+ * Pre-classification normalizer for a wire error detail: when the envelope
433
+ * (or the detail itself) carries a gateway `error_type`, its typed code
434
+ * replaces the lossy native code (kept as `native_code`). Any other detail is
435
+ * returned unchanged.
436
+ */
437
+ export function normalizeGatewayErrorDetail(detail, envelope = null) {
438
+ const errorType =
439
+ envelope?.response?.error_type ?? envelope?.error_type ?? detail?.error_type ?? detail?.metadata?.error_type;
440
+ const code = gatewayErrorTypeCode(errorType);
441
+ if (!code) return detail;
442
+ const nativeCode = detail?.code;
443
+ return {
444
+ ...(detail && typeof detail === 'object' ? detail : {}),
445
+ code,
446
+ error_type: errorType,
447
+ ...(nativeCode != null && nativeCode !== code ? { native_code: nativeCode } : {}),
448
+ };
449
+ }
450
+
451
+ function codeFields(item) {
452
+ const detail = wireDetail(item);
453
+ const canonical = [
454
+ gatewayErrorTypeCode(detail?.error_type ?? detail?.metadata?.error_type),
455
+ gatewayErrorTypeCode(item?.error_type),
456
+ gatewayErrorTypeCode(item?.error?.error_type ?? item?.error?.metadata?.error_type),
457
+ gatewayErrorTypeCode(item?.error?.error?.error_type),
458
+ ];
459
+ const native = [
460
+ detail?.code,
461
+ detail?.type,
462
+ detail?.status,
463
+ item?.providerErrorCode,
464
+ item?.code,
465
+ item?.type,
466
+ item?.error?.code,
467
+ item?.error?.type,
468
+ item?.error?.status,
469
+ item?.error?.error?.code,
470
+ item?.error?.error?.type,
471
+ item?.payload?.code,
472
+ item?.payload?.type,
473
+ item?.payload?.error?.code,
474
+ item?.payload?.error?.type,
475
+ item?.geminiStatus,
476
+ item?.status,
477
+ ];
478
+ return { canonical, native };
479
+ }
480
+
481
+ // A gateway's canonical `error_type` supersedes every native code in the chain.
482
+ function typedCodes(chain) {
483
+ const canonical = [];
484
+ const native = [];
485
+ const add = (codes, field) => {
486
+ if (typeof field !== 'string') return;
487
+ const code = field.trim().toLowerCase();
488
+ if (code && !codes.includes(code)) codes.push(code);
489
+ };
490
+ for (const item of chain) {
491
+ if (!item || typeof item !== 'object') continue;
492
+ const fields = codeFields(item);
493
+ for (const field of fields.canonical) add(canonical, field);
494
+ for (const field of fields.native) add(native, field);
495
+ }
496
+ return canonical.length ? canonical : native;
497
+ }
498
+
499
+ const VALID_STATUS = (n) => Number.isFinite(n) && n >= 100 && n <= 599;
500
+
501
+ function statusOf(err) {
502
+ for (const value of [err?.httpStatus, err?.status, err?.statusCode, err?.response?.status]) {
503
+ const n = Number(value);
504
+ if (VALID_STATUS(n)) return Math.floor(n);
505
+ }
506
+ return 0;
507
+ }
508
+
509
+ // OpenRouter answers a 402 with this marker when concurrent in-flight spend is
510
+ // capped; it clears as requests finish (Retry-After), unlike a spent balance.
511
+ const IN_FLIGHT_BUDGET = 'openrouter_in_flight_budget';
512
+
513
+ const IN_FLIGHT_BUDGET_MAX_WAIT_MS = 60_000;
514
+
515
+ // Retry-After advice for the budget exception: a valid `0` ("retry now") is
516
+ // advice here, although retryAfterMsFromError treats zero as none.
517
+ function inFlightAdviceMs(err) {
518
+ const advised = retryAfterMsFromError(err);
519
+ if (advised != null) return advised;
520
+ for (const [name, unitMs] of [['retry-after-ms', 1], ['retry-after', 1000]]) {
521
+ const value = errorHeaderValue(err, name);
522
+ const n = Number(value);
523
+ if (value != null && String(value).trim() !== '' && Number.isFinite(n) && n >= 0) return n * unitMs;
524
+ }
525
+ return null;
526
+ }
527
+
528
+ // Only a 402 that names the marker AND carries Retry-After advice of 0..60s
529
+ // inclusive is a wait; without advice (or with a long one) it stays billing.
530
+ function isInFlightBudget402(chain, status, adviceMs) {
531
+ if (status !== 402 || adviceMs == null || adviceMs > IN_FLIGHT_BUDGET_MAX_WAIT_MS) return false;
532
+ return chain.some((item) => {
533
+ if (!item || typeof item !== 'object') return false;
534
+ const detail = wireDetail(item);
535
+ return [
536
+ detail?.code,
537
+ detail?.type,
538
+ detail?.metadata?.code,
539
+ detail?.metadata?.type,
540
+ detail?.metadata?.reason,
541
+ detail?.metadata?.limit_source,
542
+ item.code,
543
+ item.type,
544
+ item.error?.code,
545
+ item.error?.type,
546
+ item.error?.metadata?.code,
547
+ item.error?.metadata?.type,
548
+ item.error?.metadata?.reason,
549
+ item.error?.metadata?.limit_source,
550
+ item.error?.error?.code,
551
+ item.error?.error?.type,
552
+ ].some((value) => typeof value === 'string' && value.trim().toLowerCase() === IN_FLIGHT_BUDGET);
553
+ });
554
+ }
555
+
556
+ function kindFromStatus(status) {
557
+ if (status === 401) return 'auth';
558
+ if (status === 402) return 'billing';
559
+ if (status === 403) return 'permission';
560
+ if (status === 413) return 'request_too_large';
561
+ if (status === 429) return 'rate_limited';
562
+ if (status === 529) return 'overloaded';
563
+ if (status === 408 || status === 409 || status === 425) return 'transient';
564
+ if (status === 525 || status === 526) return null;
565
+ if (status >= 400 && status < 500) return 'invalid_request';
566
+ if (status >= 500 && status < 600) return 'transient';
567
+ return null;
568
+ }
569
+
570
+ function firstOf(chain, read) {
571
+ for (const item of chain) {
572
+ const value = item && typeof item === 'object' ? read(item) : undefined;
573
+ if (value !== undefined && value !== null) return value;
574
+ }
575
+ return null;
576
+ }
577
+
578
+ function resetAtOf(err, chain) {
579
+ const stamped = firstOf(chain, (item) => {
580
+ const n = Number(item.resetAt);
581
+ return Number.isFinite(n) && n > 0 ? n : null;
582
+ });
583
+ if (stamped != null) return stamped;
584
+ const exhausted = Number(err?.quotaExhausted?.resetAt);
585
+ return Number.isFinite(exhausted) && exhausted > 0 ? exhausted : null;
586
+ }
587
+
588
+ function quotaScopeOf(chain) {
589
+ return firstOf(chain, (item) => {
590
+ const scope = typeof item.quotaScope === 'string' ? item.quotaScope.trim().toLowerCase() : '';
591
+ return scope === 'minute' || scope === 'day' || scope === 'plan' ? scope : null;
592
+ });
593
+ }
594
+
595
+ // The most specific kind the typed codes name; { kind: null } when none does.
596
+ function kindFromCodes(codes, scope) {
597
+ let kind = null;
598
+ let code = '';
599
+ for (const candidate of codes) {
600
+ let candidateKind = CODE_KINDS.get(candidate) || null;
601
+ if (candidate === RESOURCE_EXHAUSTED) {
602
+ // Google uses one code for per-minute throttles and spent quotas; the
603
+ // boundary names a spent quota with quotaScope day/plan. Without that
604
+ // evidence it is a request-local rate limit.
605
+ candidateKind = scope === 'day' || scope === 'plan' ? 'quota_exhausted' : 'rate_limited';
606
+ }
607
+ if (candidateKind && (kind === null || KIND_RANK.indexOf(candidateKind) < KIND_RANK.indexOf(kind))) {
608
+ kind = candidateKind;
609
+ code = candidate;
610
+ }
611
+ }
612
+ return { kind, code };
613
+ }
614
+
615
+ /** The kind an error's typed codes alone name (no status, flag or wording); null when none does. */
616
+ export function typedCodeKind(err) {
617
+ if (!err || (typeof err !== 'object' && typeof err !== 'function')) return null;
618
+ const chain = boundedCauseChain(err);
619
+ return kindFromCodes(typedCodes(chain), quotaScopeOf(chain)).kind;
620
+ }
621
+
622
+ function messageOf(err, chain) {
623
+ const detail = wireDetail(err);
624
+ return [detail?.message, err?.error?.message, ...chain.map((item) => item?.message)]
625
+ .filter((message) => typeof message === 'string' && message)
626
+ .join('\n');
627
+ }
628
+
629
+ /**
630
+ * Classify a provider error into one semantic kind.
631
+ * `options.text:false` skips the message-wording fallbacks, for retry
632
+ * policy that must decide from typed evidence alone. `options.quotaFlags:false`
633
+ * ignores the header-derived providerQuota / quotaExceeded flags.
634
+ * @returns {{ kind: string, code: string, status: number, retryAfterMs: number|null, resetAt: number|null }}
635
+ */
636
+ export function providerErrorKind(err, options = {}) {
637
+ if (!err || (typeof err !== 'object' && typeof err !== 'function')) {
638
+ return { kind: 'unknown', code: '', status: 0, retryAfterMs: null, resetAt: null };
639
+ }
640
+ const chain = boundedCauseChain(err);
641
+ const status = statusOf(err);
642
+ const retryAfterMs = retryAfterMsFromError(err);
643
+ const resetAt = resetAtOf(err, chain);
644
+ const scope = quotaScopeOf(chain);
645
+ const codes = typedCodes(chain);
646
+ const describe = (kind, code = typedErrorCode(err)) => ({ kind, code: code || '', status, retryAfterMs, resetAt });
647
+
648
+ // A local fixed-overhead refusal is no provider kind, whatever its text names.
649
+ if (isToolSurfaceExceedsContextError(err)) return describe('unknown', TOOL_SURFACE_EXCEEDS_CONTEXT);
650
+ // Validated account-pool evidence and boundary-stamped re-auth decide first.
651
+ if (err.quotaExhausted && typeof err.quotaExhausted === 'object') return describe('quota_exhausted');
652
+ if (chain.some((item) => item?.reauthRequired === true)) return describe('auth');
653
+
654
+ if (isInFlightBudget402(chain, status, inFlightAdviceMs(err))) return describe('transient', IN_FLIGHT_BUDGET);
655
+ // Every other 402 is a billing refusal, whatever generic retryable code
656
+ // (server_error, rate_limit_exceeded) its body also carries.
657
+ if (status === 402) return describe('billing');
658
+
659
+ let { kind, code } = kindFromCodes(codes, scope);
660
+ // Explicit quota flags (Anthropic unified-status rejected headers) outrank a
661
+ // generic rate-limit code or status; they never turn an overload (or a more
662
+ // specific refusal) into quota.
663
+ const quotaFlagged =
664
+ options.quotaFlags !== false &&
665
+ chain.some((item) => item?.providerQuota === true || item?.quotaExceeded === true);
666
+ if (quotaFlagged && (!kind || kind === 'rate_limited')) kind = 'quota_exhausted';
667
+ // Wordings, for untyped errors only (a typed code always decides).
668
+ const text = options.text !== false && (!kind || kind === 'invalid_request') ? messageOf(err, chain) : '';
669
+ // Capacity wording counts only on a typed 429 ("… is at capacity"): it
670
+ // keeps such a 429 from reading as a rate limit. An untyped 5xx stays an
671
+ // ordinary transient server fault, whatever its body says. Quota or billing
672
+ // wording never yields a quota kind: that takes a typed code, a quota flag
673
+ // or a quota scope (text may refine only the displayed message).
674
+ if (!kind && !codes.length && text && status === 429 && CAPACITY_MESSAGE_RE.test(text)) kind = 'overloaded';
675
+ // Presentation-only fallback (`legacyQuotaWording`): a fully UNTYPED error
676
+ // (no status, code, retry advice, quota flag or scope — e.g. persisted
677
+ // failure text) may still read as a usage limit from unambiguous wording.
678
+ // Retry, rotation and blocking never enable it.
679
+ if (
680
+ options.legacyQuotaWording === true &&
681
+ !kind &&
682
+ !codes.length &&
683
+ !status &&
684
+ retryAfterMs == null &&
685
+ !scope &&
686
+ text
687
+ ) {
688
+ if (LEGACY_BILLING_MESSAGE_RE.test(text)) kind = 'billing';
689
+ else if (LEGACY_QUOTA_MESSAGE_RE.test(text)) kind = 'quota_exhausted';
690
+ }
691
+ if (!kind) kind = kindFromStatus(status);
692
+ if ((!kind || kind === 'invalid_request') && text) {
693
+ if (TOKEN_CONTEXT_OVERFLOW_PATTERNS.some((re) => re.test(text))) kind = 'context_overflow';
694
+ else if (REQUEST_SIZE_PATTERNS.some((re) => re.test(text))) kind = 'request_too_large';
695
+ }
696
+ if (!kind && transportEvidence(err, chain, status)) kind = 'transient';
697
+ // A provider wire error event without a deterministic code is a server-side fault.
698
+ if (!kind && (err.responseFailed != null || err.providerWireError === true)) kind = 'transient';
699
+ kind = kind || 'unknown';
700
+ // The quota window the server names outranks the code's default reading.
701
+ if (scope === 'minute' && kind === 'quota_exhausted') kind = 'rate_limited';
702
+ // A day/plan window on a rate limit is a spent quota. A resetAt alone is
703
+ // not: rate-limit 429s carry window resets too (kept for display/cooldown).
704
+ if (kind === 'rate_limited' && (scope === 'day' || scope === 'plan')) kind = 'quota_exhausted';
705
+ return describe(kind, code || undefined);
706
+ }
707
+
708
+ /** Kinds whose refusal no wait or replay can change: quota, plan and billing. */
709
+ export const QUOTA_KINDS = new Set(['quota_exhausted', 'entitlement', 'billing']);
710
+ /** Kinds that are deterministic refusals of this request (never replayed as-is). */
711
+ export const DETERMINISTIC_KINDS = new Set([
712
+ ...QUOTA_KINDS,
713
+ 'policy',
714
+ 'auth',
715
+ 'permission',
716
+ 'context_overflow',
717
+ 'request_too_large',
718
+ 'service_tier_unavailable',
719
+ 'invalid_request',
720
+ ]);