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
@@ -1,6 +1,11 @@
1
1
  import { GoogleGenerativeAI } from '@google/generative-ai';
2
- import { getAgentApiKey } from '../../../shared/provider-api-key.mjs';
3
- import { canFallbackNonStreaming, emitProviderRetryStage, withRetry } from './retry-classifier.mjs';
2
+ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
3
+ import {
4
+ canFallbackNonStreaming,
5
+ emitProviderRetryStage,
6
+ markProviderRecoveryExhausted,
7
+ withRetry,
8
+ } from './retry-classifier.mjs';
4
9
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
5
10
  import { traceNonStreamingFallback } from './lib/transport-fallback-trace.mjs';
6
11
  import {
@@ -12,6 +17,13 @@ import {
12
17
  } from '../stall-policy.mjs';
13
18
  import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
14
19
  import { GEMINI_FIRST_BYTE_TIMEOUT_MS, consumeGeminiRestStreamResponse } from './gemini-stream.mjs';
20
+ import { applyGeminiErrorContract } from './gemini-error-contract.mjs';
21
+ import {
22
+ GEMINI_MIDSTREAM_MAX_ATTEMPTS,
23
+ geminiEndsWithFunctionResponse,
24
+ geminiInvalidStreamReason,
25
+ runGeminiStreamAttempts,
26
+ } from './gemini-retry-policy.mjs';
15
27
  import { geminiTextLeakGuardFor, streamGeminiSdkAttempt } from './gemini-sdk-request.mjs';
16
28
  import { emitGeminiToolCalls } from './gemini-schema.mjs';
17
29
  import { buildGeminiRequest, geminiCachedRestBody } from './gemini-request-body.mjs';
@@ -32,6 +44,8 @@ import {
32
44
  GEMINI_GLOBAL_CACHE_DELETE_GRACE_MS,
33
45
  _geminiCredentialFingerprint,
34
46
  _invalidateGeminiCachesForCredentialFingerprint,
47
+ geminiExplicitCacheRefused,
48
+ markGeminiExplicitCacheRefused,
35
49
  } from './gemini-cache.mjs';
36
50
  import {
37
51
  awaitSharedCreate,
@@ -59,7 +73,7 @@ import {
59
73
  let _modelRefreshInFlight = null;
60
74
  const GEMINI_AVAILABILITY_TIMEOUT_MS = 1_000;
61
75
 
62
- function geminiRestError(res, text, label) {
76
+ export function geminiRestError(res, text, label) {
63
77
  let payload = null;
64
78
  try {
65
79
  payload = text ? JSON.parse(text) : null;
@@ -78,16 +92,11 @@ function geminiRestError(res, text, label) {
78
92
  err.error = detail;
79
93
  err.data = payload;
80
94
  if (detail.status) err.geminiStatus = detail.status;
81
- if (Array.isArray(detail.details)) err.details = detail.details;
82
- const retryAfter = res.headers?.get?.('retry-after') ?? res.headers?.get?.('retry-after-ms');
83
- // RESOURCE_EXHAUSTED without a server retry window is deterministic
84
- // quota exhaustion. With Retry-After present, leave it request-local
85
- // so withRetry can honor the mandated delay.
86
- // A 429 RESOURCE_EXHAUSTED is a request-local rate limit even without
87
- // Retry-After (Google/LiteLLM retry). Do not stamp it as a quota code.
88
- if (detail.status && (retryAfter == null || retryAfter === '') && res.status !== 429) {
89
- err.code = detail.status;
90
- }
95
+ const details = detail.details ?? detail.errorDetails;
96
+ if (Array.isArray(details)) err.details = details;
97
+ // Typed contract (code/retryAfterMs/resetAt/quotaScope); the core decides
98
+ // whether a 429 RESOURCE_EXHAUSTED is request-local or a quota refusal.
99
+ applyGeminiErrorContract(err, { details, googleStatus: detail.status, message: detail.message });
91
100
  }
92
101
  return err;
93
102
  }
@@ -139,6 +148,9 @@ export class GeminiProvider {
139
148
  // input already includes cache. See registry.mjs.
140
149
  static inputExcludesCache = false;
141
150
  name = 'gemini';
151
+ // The two-phase ladder (gemini-retry-policy.mjs) is the whole recovery:
152
+ // a spent ladder surfaces instead of parking on loop recovery cycles.
153
+ retryPolicy = Object.freeze({ recoveryCycles: false });
142
154
  genAI;
143
155
  config;
144
156
  _fetch;
@@ -153,8 +165,9 @@ export class GeminiProvider {
153
165
  this._createGenAI =
154
166
  typeof config.createGenAI === 'function' ? config.createGenAI : (apiKey) => new GoogleGenerativeAI(apiKey);
155
167
  this._modelCache = config.modelCache || _modelCache;
156
- const apiKey = config.apiKey || process.env.GEMINI_API_KEY || '';
157
- this.genAI = config.genAI || this._createGenAI(apiKey);
168
+ this._injectedGenAI = Boolean(config.genAI);
169
+ this._genAIKey = resolveProviderApiKey('gemini', config);
170
+ this.genAI = config.genAI || this._createGenAI(this._genAIKey);
158
171
  // Warm a kept-alive socket to the Gemini REST API so the first cache/
159
172
  // generateContent request skips the cold TLS handshake. Best-effort.
160
173
  this._preconnect('https://generativelanguage.googleapis.com');
@@ -162,12 +175,13 @@ export class GeminiProvider {
162
175
 
163
176
  reloadApiKey() {
164
177
  try {
165
- const newKey = getAgentApiKey('gemini') || this.config?.apiKey || process.env.GEMINI_API_KEY;
178
+ const newKey = reloadProviderApiKey('gemini', this.config);
166
179
  if (newKey) {
167
- // Keep this.config in sync so REST/cache paths (which read the
168
- // key via _getApiKey() → this.config.apiKey) don't keep using
169
- // the stale key after a rotation; genAI alone is not enough.
180
+ // Keep this.config in sync so REST/cache paths don't keep using the
181
+ // stale key after a rotation; genAI alone is not enough.
170
182
  this.config = { ...(this.config || {}), apiKey: newKey };
183
+ this._injectedGenAI = false;
184
+ this._genAIKey = newKey;
171
185
  this.genAI = this._createGenAI(newKey);
172
186
  }
173
187
  return newKey || '';
@@ -178,7 +192,18 @@ export class GeminiProvider {
178
192
  }
179
193
 
180
194
  _getApiKey() {
181
- return this.config?.apiKey || process.env.GEMINI_API_KEY || '';
195
+ return resolveProviderApiKey('gemini', this.config);
196
+ }
197
+
198
+ // SDK client for the currently resolved key; rebuilt when the key changed
199
+ // since construction (e.g. a keyless-constructed instance).
200
+ _currentGenAI() {
201
+ const key = this._getApiKey();
202
+ if (!this._injectedGenAI && key !== this._genAIKey) {
203
+ this._genAIKey = key;
204
+ this.genAI = this._createGenAI(key);
205
+ }
206
+ return this.genAI;
182
207
  }
183
208
 
184
209
  /**
@@ -250,7 +275,7 @@ export class GeminiProvider {
250
275
  if (decision.canReuseState) {
251
276
  return state.cacheName;
252
277
  }
253
- if (!apiKey) return null;
278
+ if (!apiKey || geminiExplicitCacheRefused(model)) return null;
254
279
  const { canAttachState } = decision;
255
280
  if (geminiPrefixBelowMinimum({ model, systemInstruction, geminiTools, contents, opts, currentIter })) {
256
281
  return canAttachState ? state.cacheName : null;
@@ -430,6 +455,7 @@ export class GeminiProvider {
430
455
  _setGeminiGlobalCache(globalCacheKey, entry);
431
456
  return entry;
432
457
  } catch (err) {
458
+ markGeminiExplicitCacheRefused(model, err);
433
459
  process.stderr.write(`[gemini] cachedContents.create error: ${err?.message || err}\n`);
434
460
  return null;
435
461
  }
@@ -561,7 +587,18 @@ export class GeminiProvider {
561
587
  emitGeminiToolCalls(parsed.nativeToolCalls, callbacks.onToolCall);
562
588
  const usage = resolveGeminiUsage(response, opts, cachedContent, useModel);
563
589
  const incomplete = geminiIncompleteError(response, parsed, useModel, usage);
564
- if (incomplete) throw incomplete;
590
+ if (incomplete) {
591
+ // A malformed / unexpected tool call that survived the stream ladder
592
+ // (4 attempts) is spent; the loop must not multiply that budget.
593
+ if (
594
+ geminiInvalidStreamReason(response, textLeakGuard, {
595
+ afterToolResult: geminiEndsWithFunctionResponse(request.contents),
596
+ })
597
+ ) {
598
+ markProviderRecoveryExhausted(incomplete, { owner: 'gemini-stream', attempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS });
599
+ }
600
+ throw incomplete;
601
+ }
565
602
  return geminiSendResult(parsed, useModel, opts, usage);
566
603
  }
567
604
 
@@ -630,12 +667,19 @@ export class GeminiProvider {
630
667
  const restPassthrough = createPassthroughSignal(signal);
631
668
  const totalSignal = restPassthrough.signal;
632
669
  try {
633
- return await withRetry(
634
- async ({ signal: attemptSignal }) => {
670
+ const afterToolResult = geminiEndsWithFunctionResponse(contents);
671
+ return await runGeminiStreamAttempts({
672
+ signal: totalSignal,
673
+ sleepFn: this.config?.sleepFn,
674
+ onRetry: geminiRetryLogger(opts, '[gemini-rest]'),
675
+ afterToolResult,
676
+ run: async ({ signal: attemptSignal, connected }) => {
635
677
  signalRequesting(opts);
636
678
  const res = await this._openRestStream(genUrl, body, attemptSignal);
679
+ connected();
637
680
  const textLeakGuard = geminiTextLeakGuardFor(stream);
638
681
  const response = await consumeGeminiRestStreamResponse(res, {
682
+ acceptEmptyNoFinish: afterToolResult,
639
683
  signal: attemptSignal,
640
684
  onStreamDelta: callbacks.onStreamDelta,
641
685
  onTextDelta: callbacks.onTextDelta,
@@ -645,8 +689,7 @@ export class GeminiProvider {
645
689
  });
646
690
  return { response, textLeakGuard };
647
691
  },
648
- { signal: totalSignal, onRetry: geminiRetryLogger(opts, '[gemini-rest]') }
649
- );
692
+ });
650
693
  } catch (err) {
651
694
  if (
652
695
  !internal.skipExplicitCache &&
@@ -673,7 +716,7 @@ export class GeminiProvider {
673
716
 
674
717
  async _streamViaSdk(stream) {
675
718
  const { opts, signal, useModel, systemInstruction, geminiTools, toolConfig, generationConfig, contents } = stream;
676
- const genModel = this.genAI.getGenerativeModel({
719
+ const genModel = this._currentGenAI().getGenerativeModel({
677
720
  model: useModel,
678
721
  systemInstruction,
679
722
  tools: geminiTools,
@@ -685,13 +728,16 @@ export class GeminiProvider {
685
728
  const sdkPassthrough = createPassthroughSignal(signal);
686
729
  const totalSignal = sdkPassthrough.signal;
687
730
  try {
688
- return await withRetry(
689
- async ({ signal: attemptSignal }) => {
731
+ return await runGeminiStreamAttempts({
732
+ signal: totalSignal,
733
+ sleepFn: this.config?.sleepFn,
734
+ onRetry: geminiRetryLogger(opts, '[gemini]'),
735
+ afterToolResult: geminiEndsWithFunctionResponse(contents),
736
+ run: async ({ signal: attemptSignal, connected }) => {
690
737
  signalRequesting(opts);
691
- return await streamGeminiSdkAttempt(genModel, stream, attemptSignal);
738
+ return await streamGeminiSdkAttempt(genModel, stream, attemptSignal, connected);
692
739
  },
693
- { signal: totalSignal, onRetry: geminiRetryLogger(opts, '[gemini]') }
694
- );
740
+ });
695
741
  } catch (err) {
696
742
  const recovered = await this._recoverGeminiNonStreaming({
697
743
  streamErr: err,
@@ -714,7 +760,7 @@ export class GeminiProvider {
714
760
  const cached = this._modelCache.loadSync();
715
761
  if (cached) return cached;
716
762
  // Dynamic lookup via Gemini v1beta /models. Requires API key.
717
- const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY;
763
+ const apiKey = this._getApiKey();
718
764
  if (!apiKey) return MODELS; // no key — return minimal static list
719
765
  try {
720
766
  return await this._fetchAndCacheModels(apiKey);
@@ -738,26 +784,26 @@ export class GeminiProvider {
738
784
 
739
785
  // Force a catalog refresh (ignores the 24h disk TTL). De-duped via
740
786
  // _modelRefreshInFlight so concurrent callers share one HTTP round-trip.
741
- // Fire-and-forget context: failures are caught/logged, returning null.
787
+ // Resolves null when there is no key; fetch/normalize failures reject so
788
+ // callers (registry refresh) report them.
742
789
  async _refreshModelCache() {
743
790
  if (_modelRefreshInFlight) return _modelRefreshInFlight;
744
- _modelRefreshInFlight = (async () => {
745
- try {
746
- const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY;
747
- if (!apiKey) return null; // no key — nothing to refresh
748
- const enriched = await this._fetchAndCacheModels(apiKey);
749
- if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
750
- process.stderr.write(`[gemini] catalog refreshed (${enriched.length} models)\n`);
751
- return enriched;
752
- } catch (err) {
753
- if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
754
- process.stderr.write(`[gemini] catalog refresh failed (${err.message})\n`);
755
- return null;
756
- } finally {
757
- _modelRefreshInFlight = null;
758
- }
791
+ const refresh = (async () => {
792
+ const apiKey = this._getApiKey();
793
+ if (!apiKey) return null; // no key — nothing to refresh
794
+ const enriched = await this._fetchAndCacheModels(apiKey);
795
+ if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
796
+ process.stderr.write(`[gemini] catalog refreshed (${enriched.length} models)\n`);
797
+ return enriched;
759
798
  })();
760
- return _modelRefreshInFlight;
799
+ // Clear only after the shared promise is published: a refresh that settles
800
+ // synchronously (no key) must not leave its settled promise memoized.
801
+ _modelRefreshInFlight = refresh;
802
+ try {
803
+ return await refresh;
804
+ } finally {
805
+ if (_modelRefreshInFlight === refresh) _modelRefreshInFlight = null;
806
+ }
761
807
  }
762
808
 
763
809
  async isAvailable() {
@@ -766,7 +812,7 @@ export class GeminiProvider {
766
812
  controller.abort(providerTimeoutError('Gemini availability probe', GEMINI_AVAILABILITY_TIMEOUT_MS));
767
813
  }, GEMINI_AVAILABILITY_TIMEOUT_MS);
768
814
  try {
769
- const model = this.genAI.getGenerativeModel({ model: DEFAULT_MODEL });
815
+ const model = this._currentGenAI().getGenerativeModel({ model: DEFAULT_MODEL });
770
816
  const generation = Promise.resolve(model.generateContent('hi', { signal: controller.signal }));
771
817
  generation.catch(() => {});
772
818
  await Promise.race([
@@ -333,6 +333,9 @@ async function _postRefresh(tokens) {
333
333
  isTerminalRefresh,
334
334
  reauthRequired: isTerminalRefresh,
335
335
  oauthError: oauthError || null,
336
+ status: res.status,
337
+ headers: res.headers,
338
+ ...(oauthError ? { code: oauthError } : {}),
336
339
  });
337
340
  }
338
341
  const accessToken = json?.access_token;
@@ -18,6 +18,8 @@ import { enrichModels, getModelMetadataSync } from './model-catalog.mjs';
18
18
  import { sanitizeModelList } from './model-list-sanitize.mjs';
19
19
  import { makeModelCache } from './model-cache.mjs';
20
20
  import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
21
+ import { xaiModelSupportsReasoningEffort } from './openai-compat-xai.mjs';
22
+ import { XAI_COMPAT_RETRY_POLICY } from './openai-compat-retry-profile.mjs';
21
23
  import { createTimeoutSignal } from '../stall-policy.mjs';
22
24
  import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
23
25
  import { normalizeGrokToolSchemas } from './lib/grok-tool-schema.mjs';
@@ -48,19 +50,44 @@ export {
48
50
  } from './grok-oauth-tokens.mjs';
49
51
  export { beginOAuthLogin, loginOAuth } from './grok-oauth-login.mjs';
50
52
 
53
+ // Typed wire codes that say the bearer itself expired or was revoked. An
54
+ // ordinary permission 403 carries none of them and is never a refresh signal.
55
+ const EXPIRED_TOKEN_CODES = new Set([
56
+ 'token_expired',
57
+ 'expired_token',
58
+ 'access_token_expired',
59
+ 'token_revoked',
60
+ 'revoked_token',
61
+ 'invalid_token',
62
+ ]);
63
+
64
+ function hasExpiredTokenSignature(err) {
65
+ return [err?.code, err?.type, err?.providerErrorCode, err?.error?.code, err?.error?.type].some(
66
+ (field) => typeof field === 'string' && EXPIRED_TOKEN_CODES.has(field.trim().toLowerCase())
67
+ );
68
+ }
69
+
51
70
  const _modelCache = makeModelCache({
52
71
  fileName: 'grok-oauth-models.json',
53
72
  ttlMs: MODEL_CACHE_TTL_MS,
54
73
  version: GROK_MODEL_CACHE_SCHEMA_VERSION,
55
74
  });
56
75
 
57
- function _grokModelSupportsEffort(id) {
76
+ // A model takes a reasoning effort when its own roster row or the catalog
77
+ // publishes effort values; otherwise the shared xAI rule decides (catalog row
78
+ // first, then pass-through). Two id tokens are kept: xAI's own
79
+ // `non-reasoning` variant marker, and the Build endpoint family. Neither
80
+ // publishes capabilities, and both 400 on any effort.
81
+ function _grokModelSupportsEffort(id, model = null) {
58
82
  const text = String(id || '').toLowerCase();
59
83
  if (!text) return false;
60
84
  if (NON_CHAT_MODEL_RE.test(text)) return false;
61
- if (text.includes('non-reasoning')) return false;
62
- if (text === 'grok-build' || text.startsWith('grok-build-')) return false;
63
- return text.includes('reasoning') || /^grok-\d/.test(text);
85
+ if (_grokCatalogEffortValues(model).length || _grokApiEffortValues(model).length) return true;
86
+ if (/non[-_]?reasoning/.test(text)) return false;
87
+ // Build endpoint models reject reasoning_effort outright; a catalog or
88
+ // roster that publishes effort values has already answered above.
89
+ if (/^grok[-_]build/.test(text)) return false;
90
+ return xaiModelSupportsReasoningEffort(text);
64
91
  }
65
92
 
66
93
  // Pre-catalog fallback matches Grok 4.5 (low/medium/high). Grok 4.6 adds
@@ -86,7 +113,8 @@ function _grokApiEffortValues(model) {
86
113
  model?.reasoningEfforts ??
87
114
  model?.reasoning_efforts ??
88
115
  model?._meta?.reasoningEfforts ??
89
- model?._meta?.reasoning_efforts;
116
+ model?._meta?.reasoning_efforts ??
117
+ model?.capabilities?.reasoning_effort;
90
118
  return Array.isArray(raw) ? _uniqueKnownGrokEfforts(raw) : [];
91
119
  }
92
120
 
@@ -103,7 +131,7 @@ function _grokCatalogEffortValues(model) {
103
131
  }
104
132
 
105
133
  function _grokReasoningLevels(id, model) {
106
- if (!_grokModelSupportsEffort(id)) return [];
134
+ if (!_grokModelSupportsEffort(id, model)) return [];
107
135
  const catalog = _grokCatalogEffortValues(model);
108
136
  if (catalog.length) return catalog;
109
137
  const api = _grokApiEffortValues(model);
@@ -144,13 +172,14 @@ function _normalizeGrokModel(m) {
144
172
  const contextWindow = _grokApiContextWindow(m);
145
173
  const apiEfforts = _grokApiEffortValues(m);
146
174
  const reasoningOptions = apiEfforts.length ? [{ type: 'effort', values: apiEfforts }] : undefined;
175
+ const row = { ...m, reasoningOptions };
147
176
  return {
148
177
  id,
149
178
  name: id,
150
179
  display: _displayGrokModel(m),
151
180
  provider: 'grok-oauth',
152
- family: _normalizeGrokFamily(id),
153
- reasoningLevels: _grokReasoningLevels(id, { ...m, reasoningOptions }),
181
+ family: _normalizeGrokFamily(id, row),
182
+ reasoningLevels: _grokReasoningLevels(id, row),
154
183
  ...(reasoningOptions ? { reasoningOptions } : {}),
155
184
  tier: 'version',
156
185
  latest: false,
@@ -165,7 +194,7 @@ function _sanitizeGrokModels(models) {
165
194
  const next = models.map((m) => {
166
195
  let out = m;
167
196
  const display = _displayGrokModel(m);
168
- const family = _normalizeGrokFamily(m?.id);
197
+ const family = _normalizeGrokFamily(m?.id, out);
169
198
  const reasoningLevels = _grokReasoningLevels(m?.id, out);
170
199
  // api.x.ai /models does not expose a max-output field. Historical
171
200
  // enrichment could persist contextWindow as outputTokens (for example
@@ -248,6 +277,15 @@ function resolveLatestGrokModel() {
248
277
  return _newestDatedGrokChatModel(cached)?.id || null;
249
278
  }
250
279
 
280
+ // Whether a request for this model may carry an effort: the levels its cached
281
+ // roster row already resolved from the roster and catalog answer, else the id
282
+ // alone follows the catalog rule.
283
+ function _grokSendSupportsEffort(id) {
284
+ const cached = _modelCache.loadSync();
285
+ const row = Array.isArray(cached) ? cached.find((m) => m?.id === id) : null;
286
+ return Array.isArray(row?.reasoningLevels) ? row.reasoningLevels.length > 0 : _grokModelSupportsEffort(id);
287
+ }
288
+
251
289
  async function ensureLatestGrokModel(provider) {
252
290
  let m = resolveLatestGrokModel();
253
291
  if (m) return m;
@@ -265,6 +303,8 @@ export class GrokOAuthProvider {
265
303
  // OpenAI-compatible usage: prompt_tokens includes cached. See registry.mjs.
266
304
  static inputExcludesCache = false;
267
305
  name = 'grok-oauth';
306
+ // The inner xai profile's limits are final: no loop-level recovery cycles on top.
307
+ retryPolicy = XAI_COMPAT_RETRY_POLICY;
268
308
  config;
269
309
  tokens = null;
270
310
  _inner = null;
@@ -395,11 +435,12 @@ export class GrokOAuthProvider {
395
435
  });
396
436
  const inner = this._ensureInner(tokens.access_token, useModel, requestHeaders);
397
437
  const grokTools = normalizeGrokToolSchemas(tools);
398
- // Effort-less models (grok-build, composer): the service 400s on ANY
399
- // reasoningEffort parameter, and pinned model settings can carry one
400
- // (e.g. the bench harness pins effort per route). Strip it here so a
401
- // saved effort never breaks a model that cannot accept it.
402
- if (!_grokModelSupportsEffort(useModel) && sendOpts) {
438
+ // Effort-less models: the service 400s on ANY reasoningEffort parameter,
439
+ // and pinned model settings can carry one (e.g. the bench harness pins
440
+ // effort per route). Strip it here so a saved effort never breaks a model
441
+ // that cannot accept it. The model's own roster row decides; a model not
442
+ // on the roster yet follows the catalog rule.
443
+ if (sendOpts && !_grokSendSupportsEffort(useModel)) {
403
444
  // 'none' (not null) so the ?? fallback chain in the xai builder
404
445
  // cannot fall through to a config/env effort; the normalizer maps
405
446
  // it to "omit the parameter".
@@ -414,9 +455,10 @@ export class GrokOAuthProvider {
414
455
  return await inner._doSend(messages, useModel, grokTools, sendOpts);
415
456
  } catch (err) {
416
457
  noteErrorUsage(err);
417
- // Refresh-and-retry on a server-rejected OAuth session (401/403).
418
- // TYPED status only: a rejection whose only evidence is message
419
- // text is never treated as an auth failure worth re-issuing.
458
+ // Refresh-and-retry on a server-rejected OAuth session: 401, or a 403
459
+ // carrying an explicit expired/revoked-token code. TYPED evidence only:
460
+ // message text is never an auth verdict, and a plain permission 403 is
461
+ // surfaced as-is.
420
462
  const rejectedStatus = Number(err?.httpStatus || err?.status);
421
463
  // 426 = client version gate. Retry once only when the body stated a
422
464
  // minimum we did not already satisfy; nothing was emitted before it.
@@ -434,7 +476,7 @@ export class GrokOAuthProvider {
434
476
  sendOpts
435
477
  );
436
478
  }
437
- if (rejectedStatus === 401 || rejectedStatus === 403) {
479
+ if (rejectedStatus === 401 || (rejectedStatus === 403 && hasExpiredTokenSignature(err))) {
438
480
  // A stream-level rejection after text/tool dispatch cannot be safely
439
481
  // replayed: the client has already observed output or may have
440
482
  // executed a side-effecting tool.
@@ -444,7 +486,14 @@ export class GrokOAuthProvider {
444
486
  process.stderr.write(`[grok-oauth] ${rejectedStatus}, force-refreshing token...\n`);
445
487
  const fresh = await this.ensureAuth({ forceRefresh: true });
446
488
  const retryInner = this._ensureInner(fresh.access_token, useModel, requestHeaders);
447
- return await retryInner._doSend(messages, useModel, grokTools, sendOpts);
489
+ try {
490
+ return await retryInner._doSend(messages, useModel, grokTools, sendOpts);
491
+ } catch (retryErr) {
492
+ // Rejected again on a token that was just refreshed: re-login is the
493
+ // only remaining path.
494
+ if (Number(retryErr?.httpStatus || retryErr?.status) === 401) retryErr.reauthRequired = true;
495
+ throw retryErr;
496
+ }
448
497
  }
449
498
  throw err;
450
499
  }
@@ -79,7 +79,13 @@ function resolveLatestGeminiModel() {
79
79
  export async function ensureLatestGeminiModel(provider) {
80
80
  let model = resolveLatestGeminiModel();
81
81
  if (model) return model;
82
- await provider._refreshModelCache();
82
+ try {
83
+ await provider._refreshModelCache();
84
+ } catch (err) {
85
+ throw new Error(`[gemini] model catalog refresh failed — cannot resolve default model (${err.message})`, {
86
+ cause: err,
87
+ });
88
+ }
83
89
  model = resolveLatestGeminiModel();
84
90
  if (model) return model;
85
91
  throw new Error('[gemini] model catalog unavailable after warmup — cannot resolve default model');
@@ -9,6 +9,7 @@
9
9
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
10
10
  import { fileMtimeMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
11
11
  import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
12
+ import { COMPAT_RETRY_POLICY } from './openai-compat-retry-profile.mjs';
12
13
  import { fetchCompatModelItems, listCompatModels } from './openai-compat-models.mjs';
13
14
  import { makeModelCache } from './model-cache.mjs';
14
15
  import {
@@ -114,6 +115,8 @@ export class MimoOAuthProvider {
114
115
  // OpenAI-compatible usage: prompt_tokens includes cached. See registry.mjs.
115
116
  static inputExcludesCache = false;
116
117
  name = 'mimo-oauth';
118
+ // The retry profile's limits are final: no loop-level recovery cycles on top.
119
+ retryPolicy = COMPAT_RETRY_POLICY;
117
120
  config;
118
121
  tokens = null;
119
122
  _inner = null;
@@ -208,7 +208,8 @@ function _stalenessFamily(row, id) {
208
208
  const modelId = String(id || row?.id || row?.name || '')
209
209
  .trim()
210
210
  .toLowerCase();
211
- const claude = modelId.match(/(?:^|\/)claude-(opus|sonnet|haiku|fable)(?:-|$)/);
211
+ // Claude ids name their family right after the brand (`claude-<family>-<version>`).
212
+ const claude = modelId.match(/(?:^|\/)claude-([a-z]+)(?:-|$)/);
212
213
  if (claude) return `claude-${claude[1]}`;
213
214
  const family = row && typeof row.family === 'string' ? row.family.trim().toLowerCase() : '';
214
215
  return family || null;