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
@@ -26,6 +26,11 @@ import { createSdkStreamReader } from './gemini-sdk-stream/reader.mjs';
26
26
  import { createRestStreamWatchdogs } from './gemini-rest-stream/watchdogs.mjs';
27
27
  import { drainSseDataLines, sseDataPayload } from './gemini-rest-stream/sse-lines.mjs';
28
28
  import { noteAbandonedUsage } from '../../../shared/llm/usage-context.mjs';
29
+ import {
30
+ applyGeminiErrorContract,
31
+ assertNoGeminiInBandError,
32
+ geminiFinishReasonKind,
33
+ } from './gemini-error-contract.mjs';
29
34
 
30
35
  export const GEMINI_FIRST_BYTE_TIMEOUT_MS = resolveTimeoutMs(
31
36
  'MIXDOG_GEMINI_FIRST_BYTE_TIMEOUT_MS',
@@ -132,9 +137,16 @@ export function normalizeGeminiSdkError(err, label) {
132
137
  const transport = geminiSdkTransportError(err, label);
133
138
  if (transport) return transport;
134
139
  const stamped = stampGeminiRpcError(err);
135
- return isGeminiSdkStreamParseError(stamped)
136
- ? geminiStreamCorruptionError(`${label} corrupt SDK SSE JSON`, stamped)
137
- : stamped;
140
+ if (isGeminiSdkStreamParseError(stamped)) {
141
+ return geminiStreamCorruptionError(`${label} corrupt SDK SSE JSON`, stamped);
142
+ }
143
+ if (!stamped || typeof stamped !== 'object') return stamped;
144
+ return applyGeminiErrorContract(stamped, {
145
+ details: stamped.details,
146
+ errorDetails: stamped.errorDetails,
147
+ googleStatus: stamped.geminiStatus,
148
+ message: stamped.message,
149
+ });
138
150
  }
139
151
 
140
152
  // CC-rule safety stamp for Gemini stream failures (provider-stall audit):
@@ -175,8 +187,10 @@ function stampGeminiStreamFailure(
175
187
  const visible = visibleText.length > 0;
176
188
  // Native functionCall chunks are only dispatched after successful stream
177
189
  // completion in gemini.mjs. Until then a reconnect is safe. Text-leaked
178
- // calls are dispatched while parsing and remain a hard replay boundary.
179
- const pendingTool = leaked;
190
+ // calls are dispatched while parsing and remain a hard replay boundary,
191
+ // unless the guard holds them back (`deferToolCalls`): a held call never
192
+ // runs when the stream fails, so a reconnect cannot double-execute it.
193
+ const pendingTool = leaked && textLeakGuard?.defersToolCalls !== true;
180
194
  try {
181
195
  if (visible) {
182
196
  err.liveTextEmitted = true;
@@ -225,25 +239,35 @@ export function geminiChunkProgressKind(chunk) {
225
239
  * (same shape as a non-streaming generateContent JSON body).
226
240
  * Mirrors @google/generative-ai aggregateResponses().
227
241
  */
228
- function aggregateGeminiStreamChunks(responses) {
229
- const lastResponse = responses[responses.length - 1];
230
- const aggregatedResponse = {
231
- promptFeedback: lastResponse?.promptFeedback,
232
- };
242
+ function aggregateGeminiStreamChunks(responses, label = 'Gemini stream') {
243
+ // An error envelope anywhere in the stream — including after STOP — fails
244
+ // the turn instead of aggregating into a success.
245
+ for (const response of responses) assertNoGeminiInBandError(response, label);
246
+ const aggregatedResponse = {};
247
+ // Candidates are keyed by their own `index`; chunks that omit it fall back
248
+ // to the position inside that chunk's candidate list.
249
+ const candidatesByIndex = new Map();
233
250
  for (const response of responses) {
251
+ // Early block info must survive later chunks that carry none.
252
+ if (response?.promptFeedback) {
253
+ aggregatedResponse.promptFeedback = { ...aggregatedResponse.promptFeedback, ...response.promptFeedback };
254
+ }
234
255
  if (response?.candidates) {
235
- let candidateIndex = 0;
236
- for (const candidate of response.candidates) {
237
- if (!aggregatedResponse.candidates) aggregatedResponse.candidates = [];
238
- if (!aggregatedResponse.candidates[candidateIndex]) {
239
- aggregatedResponse.candidates[candidateIndex] = { index: candidateIndex };
256
+ response.candidates.forEach((candidate, position) => {
257
+ const candidateIndex = Number.isInteger(candidate?.index) ? candidate.index : position;
258
+ if (!candidatesByIndex.has(candidateIndex)) candidatesByIndex.set(candidateIndex, { index: candidateIndex });
259
+ const aggCand = candidatesByIndex.get(candidateIndex);
260
+ // A later chunk that omits a field never erases an earlier value.
261
+ if (candidate.citationMetadata !== undefined) {
262
+ aggCand.citationMetadata = mergeStreamMetadata(aggCand.citationMetadata, candidate.citationMetadata);
240
263
  }
241
- const aggCand = aggregatedResponse.candidates[candidateIndex];
242
- aggCand.citationMetadata = candidate.citationMetadata;
243
- aggCand.groundingMetadata = candidate.groundingMetadata;
244
- aggCand.finishReason = candidate.finishReason;
245
- aggCand.finishMessage = candidate.finishMessage;
246
- aggCand.safetyRatings = candidate.safetyRatings;
264
+ if (candidate.groundingMetadata !== undefined) {
265
+ aggCand.groundingMetadata = mergeStreamMetadata(aggCand.groundingMetadata, candidate.groundingMetadata);
266
+ }
267
+ if (geminiFinishReasonKind(candidate.finishReason) !== 'none') aggCand.finishReason = candidate.finishReason;
268
+ if (candidate.finishMessage !== undefined) aggCand.finishMessage = candidate.finishMessage;
269
+ if (candidate.safetyRatings !== undefined) aggCand.safetyRatings = candidate.safetyRatings;
270
+ if (candidate.continuationToken !== undefined) aggCand.continuationToken = candidate.continuationToken;
247
271
  if (candidate.content?.parts) {
248
272
  if (!aggCand.content) {
249
273
  aggCand.content = {
@@ -262,19 +286,52 @@ function aggregateGeminiStreamChunks(responses) {
262
286
  aggCand.content.parts.push(newPart);
263
287
  }
264
288
  }
265
- candidateIndex++;
266
- }
289
+ });
267
290
  }
268
291
  if (response?.usageMetadata) aggregatedResponse.usageMetadata = response.usageMetadata;
269
292
  }
293
+ if (candidatesByIndex.size) {
294
+ aggregatedResponse.candidates = [...candidatesByIndex.values()].sort((a, b) => a.index - b.index);
295
+ }
270
296
  return aggregatedResponse;
271
297
  }
272
298
 
299
+ // GroundingMetadata / CitationMetadata arrive in pieces while streaming and
300
+ // are merged, not replaced: groundingChunks carry only the chunks not sent
301
+ // before (appended as-is, so groundingSupports indices stay valid); every
302
+ // other list is a de-duplicated union, nested objects merge, scalars take the
303
+ // latest value.
304
+ function mergeStreamMetadata(previous, next) {
305
+ if (!previous) return next;
306
+ if (!next || typeof next !== 'object') return previous;
307
+ const merged = { ...previous };
308
+ for (const [key, value] of Object.entries(next)) {
309
+ if (value === undefined || value === null) continue;
310
+ const before = previous[key];
311
+ if (Array.isArray(value)) {
312
+ if (key === 'groundingChunks') {
313
+ merged[key] = [...(before || []), ...value];
314
+ } else {
315
+ const seen = new Set((before || []).map((item) => JSON.stringify(item)));
316
+ merged[key] = [...(before || []), ...value.filter((item) => !seen.has(JSON.stringify(item)))];
317
+ }
318
+ } else if (typeof value === 'object' && before && typeof before === 'object') {
319
+ merged[key] = { ...before, ...value };
320
+ } else {
321
+ merged[key] = value;
322
+ }
323
+ }
324
+ return merged;
325
+ }
326
+
327
+ // A stream that ends with no finishReason is unfinished, whatever it carried
328
+ // (thoughts only, an empty body, even right after a tool result); only a
329
+ // STOP that carries no text is a completed, empty answer.
273
330
  function assertGeminiStreamCompleted({ sawStreamChunk, finishReason, promptBlockReason, label }) {
274
331
  if (!sawStreamChunk) {
275
332
  throw geminiTruncatedStreamError(`${label} truncated: empty stream`);
276
333
  }
277
- if (!finishReason && !promptBlockReason) {
334
+ if (geminiFinishReasonKind(finishReason) === 'none' && geminiFinishReasonKind(promptBlockReason) === 'none') {
278
335
  throw geminiTruncatedStreamError(`${label} truncated: no finishReason`);
279
336
  }
280
337
  }
@@ -316,13 +373,23 @@ function relayGeminiStreamText(t, { onTextDelta, textLeakGuard }) {
316
373
  * `part.text` streams. Mirrors the Anthropic OAuth guard: suppress tags from
317
374
  * visible text, synthesize known-tool calls, dispatch via onToolCall.
318
375
  */
319
- export function createGeminiTextLeakGuard({ knownToolNames, onTextDelta, onToolCall, onStreamDelta }) {
376
+ export function createGeminiTextLeakGuard({
377
+ knownToolNames,
378
+ onTextDelta,
379
+ onToolCall,
380
+ onStreamDelta,
381
+ deferToolCalls = false,
382
+ }) {
320
383
  const _knownTools = knownToolNameSet(knownToolNames);
321
384
  const _enabled = _knownTools.size > 0;
322
385
  const _isKnownTool = (name) => _knownTools.has(name);
323
386
  let leakBuffer = '';
324
387
  let relayedText = '';
325
388
  const leakedCalls = [];
389
+ // With `deferToolCalls`, recovered calls wait here until the caller has seen
390
+ // the response finish cleanly (releaseDeferredToolCalls); a failed response
391
+ // never runs them.
392
+ const deferredCalls = [];
326
393
  const dispatchedFingerprints = new Set();
327
394
 
328
395
  const dispatchLeakedCall = (recovered) => {
@@ -344,9 +411,12 @@ export function createGeminiTextLeakGuard({ knownToolNames, onTextDelta, onToolC
344
411
  arguments: args,
345
412
  };
346
413
  leakedCalls.push(call);
347
- try {
348
- onToolCall?.(call);
349
- } catch {}
414
+ if (deferToolCalls) deferredCalls.push(call);
415
+ else {
416
+ try {
417
+ onToolCall?.(call);
418
+ } catch {}
419
+ }
350
420
  try {
351
421
  onStreamDelta?.('tool');
352
422
  } catch {}
@@ -373,6 +443,9 @@ export function createGeminiTextLeakGuard({ knownToolNames, onTextDelta, onToolC
373
443
  get enabled() {
374
444
  return _enabled;
375
445
  },
446
+ get defersToolCalls() {
447
+ return deferToolCalls === true;
448
+ },
376
449
  feedText(text) {
377
450
  if (!text) return;
378
451
  if (!_enabled) {
@@ -411,6 +484,13 @@ export function createGeminiTextLeakGuard({ knownToolNames, onTextDelta, onToolC
411
484
  getLeakedToolCalls() {
412
485
  return [...leakedCalls];
413
486
  },
487
+ releaseDeferredToolCalls() {
488
+ for (const call of deferredCalls.splice(0)) {
489
+ try {
490
+ onToolCall?.(call);
491
+ } catch {}
492
+ }
493
+ },
414
494
  getRelayedText() {
415
495
  return relayedText;
416
496
  },
@@ -436,7 +516,16 @@ function createLeakGuardFinalizer(textLeakGuard) {
436
516
  // aggregation, leak guard, watchdogs — stays shared.
437
517
  export async function consumeGeminiRestStreamResponse(
438
518
  response,
439
- { signal, onStreamDelta, onTextDelta, textLeakGuard, label, unwrapChunk = null, onChunk = null, failureUsage = null }
519
+ {
520
+ signal,
521
+ onStreamDelta,
522
+ onTextDelta,
523
+ textLeakGuard,
524
+ label,
525
+ unwrapChunk = null,
526
+ onChunk = null,
527
+ failureUsage = null,
528
+ }
440
529
  ) {
441
530
  const unwrap =
442
531
  typeof unwrapChunk === 'function'
@@ -464,7 +553,10 @@ export async function consumeGeminiRestStreamResponse(
464
553
  });
465
554
  // One decoded chunk: collected for aggregation and relayed to every sink.
466
555
  const relayChunk = (rawChunk) => {
556
+ // In-band errors stay top level even on a transport that nests payloads.
557
+ assertNoGeminiInBandError(rawChunk, label);
467
558
  const parsed = unwrap(rawChunk);
559
+ assertNoGeminiInBandError(parsed, label);
468
560
  if (!sawStreamChunk) {
469
561
  sawStreamChunk = true;
470
562
  watchdogs.clearFirstByte();
@@ -530,12 +622,13 @@ export async function consumeGeminiRestStreamResponse(
530
622
  }
531
623
  const tail = sseDataPayload(buffer.trim());
532
624
  if (tail) {
625
+ let rawTail;
533
626
  try {
534
- const [rawTail] = await parseProviderJsonBatch([tail]);
535
- relayChunk(rawTail);
627
+ [rawTail] = await parseProviderJsonBatch([tail]);
536
628
  } catch (cause) {
537
629
  throw geminiStreamCorruptionError(`${label} corrupt SSE tail JSON`, cause);
538
630
  }
631
+ relayChunk(rawTail);
539
632
  }
540
633
  } catch (err) {
541
634
  finalizeLeakGuard();
@@ -558,7 +651,7 @@ export async function consumeGeminiRestStreamResponse(
558
651
  finalizeLeakGuard();
559
652
  }
560
653
 
561
- const aggregated = aggregateGeminiStreamChunks(allChunks);
654
+ const aggregated = aggregateGeminiStreamChunks(allChunks, label);
562
655
  const finishReason = aggregated.candidates?.[0]?.finishReason || null;
563
656
  const promptBlockReason = aggregated.promptFeedback?.blockReason || null;
564
657
  // Truncation (no finishReason) after visible output must carry the same
@@ -566,7 +659,12 @@ export async function consumeGeminiRestStreamResponse(
566
659
  // above, so stamp here too (review High: transient TRUNCATED_STREAM would
567
660
  // otherwise replay/double-render live text via withRetry).
568
661
  try {
569
- assertGeminiStreamCompleted({ sawStreamChunk, finishReason, promptBlockReason, label });
662
+ assertGeminiStreamCompleted({
663
+ sawStreamChunk,
664
+ finishReason,
665
+ promptBlockReason,
666
+ label,
667
+ });
570
668
  } catch (err) {
571
669
  throw stampGeminiStreamFailure(err, { relayedText, textLeakGuard, chunks: allChunks, failureUsage });
572
670
  }
@@ -636,6 +734,7 @@ export async function consumeGeminiSdkStream(
636
734
  if (signal?.aborted) throw abortError();
637
735
  if (step.done) break;
638
736
  reader.noteChunk();
737
+ assertNoGeminiInBandError(step.value, label);
639
738
  if (step.value) collectedChunks.push(step.value);
640
739
  try {
641
740
  onStreamDelta?.(geminiChunkProgressKind(step.value));
@@ -652,7 +751,7 @@ export async function consumeGeminiSdkStream(
652
751
  // locally; retain the SDK response path only for an empty collection.
653
752
  let raw;
654
753
  if (collectedChunks.length > 0) {
655
- raw = aggregateGeminiStreamChunks(collectedChunks);
754
+ raw = aggregateGeminiStreamChunks(collectedChunks, label);
656
755
  } else {
657
756
  let response;
658
757
  try {
@@ -661,10 +760,16 @@ export async function consumeGeminiSdkStream(
661
760
  throw normalizeGeminiSdkError(err, label);
662
761
  }
663
762
  raw = response?.candidates ? response : response?.response || response;
763
+ assertNoGeminiInBandError(raw, label);
664
764
  }
665
765
  const finishReason = raw?.candidates?.[0]?.finishReason || null;
666
766
  const promptBlockReason = raw?.promptFeedback?.blockReason || null;
667
- assertGeminiStreamCompleted({ sawStreamChunk: reader.sawStreamChunk, finishReason, promptBlockReason, label });
767
+ assertGeminiStreamCompleted({
768
+ sawStreamChunk: reader.sawStreamChunk,
769
+ finishReason,
770
+ promptBlockReason,
771
+ label,
772
+ });
668
773
  return raw;
669
774
  } catch (err) {
670
775
  reader?.clearFirstByte();
@@ -1,11 +1,67 @@
1
+ import { getModelMetadataSync } from '../../../shared/llm/model-catalog.mjs';
2
+
3
+ // The thinkingLevel values the Gemini wire accepts.
1
4
  const LEVELS = ['minimal', 'low', 'medium', 'high'];
2
5
 
3
- // Only translate the public option names, not arbitrary effort-to-token ratios.
4
- // Omitting settings preserves the provider's model default.
5
- export function geminiThinkingConfig(model, opts = {}, { includeThoughts } = {}) {
6
- const id = String(model || '').toLowerCase();
6
+ // The thinkingLevel values the catalogs declare for the model (provider live
7
+ // roster, then models.dev / LiteLLM rows, resolved by getModelMetadataSync),
8
+ // limited to what the Gemini wire accepts. null when no catalog declares a ladder.
9
+ function catalogThinkingLevels(model, provider) {
10
+ const bare = String(model || '').replace(/^models\//i, '');
11
+ const effort = getModelMetadataSync(bare, provider)?.reasoningOptions?.find((option) => option?.type === 'effort');
12
+ const declared = (Array.isArray(effort?.values) ? effort.values : [])
13
+ .map((value) => String(value).toLowerCase())
14
+ .filter((value) => LEVELS.includes(value));
15
+ return declared.length ? [...new Set(declared)] : null;
16
+ }
17
+
18
+ // Gemini 2.5 models take thinkingBudget only (thinkingLevel is unsupported).
19
+ // Documented ranges: 2.5 Pro 128–32768 (cannot disable), 2.5 Flash 0–24576,
20
+ // 2.5 Flash-Lite 0 or 512–24576; -1 is dynamic thinking for all of them.
21
+ function budgetSpec(model) {
22
+ const bare = String(model || '').replace(/^models\//i, '').toLowerCase();
23
+ if (!/^gemini-2\.5(?:-|$)/.test(bare)) return null;
24
+ if (/^gemini-2\.5-pro/.test(bare)) {
25
+ return { name: 'Gemini 2.5 Pro', min: 128, max: 32768, canDisable: false, efforts: { minimal: 128, low: 1024, medium: 8192, high: 32768 } };
26
+ }
27
+ if (/^gemini-2\.5-flash-lite/.test(bare)) {
28
+ return { name: 'Gemini 2.5 Flash-Lite', min: 512, max: 24576, canDisable: true, efforts: { minimal: 512, low: 1024, medium: 8192, high: 24576 } };
29
+ }
30
+ return { name: 'Gemini 2.5 Flash', min: 0, max: 24576, canDisable: true, efforts: { minimal: 0, low: 1024, medium: 8192, high: 24576 } };
31
+ }
32
+
33
+ // Levels documented for models whose ladder differs from the generic wire
34
+ // vocabulary (a catalog ladder, when declared, still takes precedence).
35
+ function documentedThinkingLevels(model) {
36
+ const bare = String(model || '').replace(/^models\//i, '').toLowerCase();
37
+ if (bare === 'gemini-4-argon' || bare === 'gemini-3.8-flash') return ['low', 'medium', 'high'];
38
+ return null;
39
+ }
40
+
41
+ function checkBudget(model, spec, budget) {
42
+ if (!Number.isInteger(budget) || budget < -1) {
43
+ throw new TypeError('thinkingBudget must be -1 (dynamic), 0, or a positive integer.');
44
+ }
45
+ if (!spec || budget === -1) return;
46
+ if (budget === 0 && !spec.canDisable) {
47
+ throw new TypeError(`${model} (${spec.name}) cannot disable thinking; thinkingBudget 0 is not allowed.`);
48
+ }
49
+ if (budget !== 0 && (budget < spec.min || budget > spec.max)) {
50
+ throw new TypeError(`${model} (${spec.name}) thinkingBudget must be -1, ${spec.canDisable ? '0, or ' : ''}${spec.min}–${spec.max}; received ${budget}.`);
51
+ }
52
+ }
53
+
54
+ // Only translate the public option names. Omitting settings preserves the
55
+ // provider's model default. Gemini 2.5 models use thinkingBudget (a generic
56
+ // effort maps to a budget; an explicit thinkingLevel is rejected locally and
57
+ // budgets are range-checked). Other models use thinkingLevel, checked against
58
+ // the catalog/documented ladder; unknown models pass values within the wire
59
+ // vocabulary and the server decides.
60
+ export function geminiThinkingConfig(model, opts = {}, { includeThoughts, provider = 'gemini' } = {}) {
7
61
  const rawBudget = opts.thinkingBudget ?? opts.thinkingBudgetTokens;
8
62
  const hasBudget = rawBudget !== undefined && rawBudget !== null && rawBudget !== '';
63
+ const spec = budgetSpec(model);
64
+ const explicitLevel = opts.thinkingLevel == null ? '' : String(opts.thinkingLevel).trim().toLowerCase();
9
65
  const rawLevel = opts.thinkingLevel ?? (hasBudget ? undefined : opts.effort);
10
66
  const level = rawLevel == null ? '' : String(rawLevel).trim().toLowerCase();
11
67
  const config = {};
@@ -14,15 +70,16 @@ export function geminiThinkingConfig(model, opts = {}, { includeThoughts } = {})
14
70
  if (level && hasBudget) {
15
71
  throw new TypeError('Choose thinkingLevel/effort or thinkingBudget, not both.');
16
72
  }
17
- if (level) {
18
- if (!/^gemini-3(?:[.-]|$)/.test(id)) {
19
- throw new TypeError(`${model} uses thinkingBudget instead of thinkingLevel/effort.`);
73
+ if (spec && explicitLevel) {
74
+ throw new TypeError(`${model} (${spec.name}) does not support thinkingLevel; use thinkingBudget.`);
75
+ }
76
+ if (spec && level) {
77
+ if (!Object.hasOwn(spec.efforts, level)) {
78
+ throw new TypeError(`${model} supports thinking levels ${Object.keys(spec.efforts).join(', ')}; received ${level}.`);
20
79
  }
21
- let supported = LEVELS;
22
- if (/^gemini-3(?:\.0)?-pro(?:-|$)/.test(id)) supported = ['low', 'high'];
23
- else if (/^gemini-3\.1-pro(?:-|$)/.test(id) || /^gemini-3\.[78]-flash(?:-|$)/.test(id)) {
24
- supported = ['low', 'medium', 'high'];
25
- } else if (/^gemini-3\.1-flash-lite.*image/.test(id)) supported = ['minimal', 'high'];
80
+ config.thinkingBudget = spec.efforts[level];
81
+ } else if (level) {
82
+ const supported = catalogThinkingLevels(model, provider) ?? documentedThinkingLevels(model) ?? LEVELS;
26
83
  if (!supported.includes(level)) {
27
84
  throw new TypeError(`${model} supports thinking levels ${supported.join(', ')}; received ${level}.`);
28
85
  }
@@ -30,18 +87,7 @@ export function geminiThinkingConfig(model, opts = {}, { includeThoughts } = {})
30
87
  }
31
88
  if (hasBudget) {
32
89
  const budget = Number(rawBudget);
33
- if (!Number.isInteger(budget) || budget < -1) {
34
- throw new TypeError('thinkingBudget must be -1 (dynamic), 0, or a positive integer.');
35
- }
36
- if (/^gemini-2\.5-pro(?:-|$)/.test(id) && budget !== -1 && (budget < 128 || budget > 32768)) {
37
- throw new TypeError(`${model} requires thinkingBudget -1 or 128..32768.`);
38
- }
39
- if (/^gemini-2\.5-flash(?:-|$)/.test(id) && budget > 24576) {
40
- throw new TypeError(`${model} requires thinkingBudget at most 24576.`);
41
- }
42
- if (/^gemini-2\.5-flash-lite(?:-|$)/.test(id) && budget > 0 && budget < 512) {
43
- throw new TypeError(`${model} requires thinkingBudget -1, 0, or 512..24576.`);
44
- }
90
+ checkBudget(model, spec, budget);
45
91
  config.thinkingBudget = budget;
46
92
  }
47
93
  return Object.keys(config).length ? config : undefined;
@@ -1,6 +1,11 @@
1
1
  import { GoogleGenerativeAI } from '@google/generative-ai';
2
2
  import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
3
- import { canFallbackNonStreaming, emitProviderRetryStage, withRetry } from './retry-classifier.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,18 @@ 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 {
21
+ applyGeminiErrorContract,
22
+ geminiContinuationUnsupportedError,
23
+ geminiFinishErrorFields,
24
+ geminiFinishReasonKind,
25
+ } from './gemini-error-contract.mjs';
26
+ import {
27
+ GEMINI_MIDSTREAM_MAX_ATTEMPTS,
28
+ geminiEndsWithFunctionResponse,
29
+ geminiInvalidStreamReason,
30
+ runGeminiStreamAttempts,
31
+ } from './gemini-retry-policy.mjs';
15
32
  import { geminiTextLeakGuardFor, streamGeminiSdkAttempt } from './gemini-sdk-request.mjs';
16
33
  import { emitGeminiToolCalls } from './gemini-schema.mjs';
17
34
  import { buildGeminiRequest, geminiCachedRestBody } from './gemini-request-body.mjs';
@@ -32,10 +49,13 @@ import {
32
49
  GEMINI_GLOBAL_CACHE_DELETE_GRACE_MS,
33
50
  _geminiCredentialFingerprint,
34
51
  _invalidateGeminiCachesForCredentialFingerprint,
52
+ geminiExplicitCacheRefused,
53
+ markGeminiExplicitCacheRefused,
35
54
  } from './gemini-cache.mjs';
36
55
  import {
37
56
  awaitSharedCreate,
38
57
  dropRejectedGeminiCache,
58
+ geminiBareModelId,
39
59
  geminiCacheCreateBody,
40
60
  geminiCacheEntry,
41
61
  geminiCachePrefixIdentity,
@@ -59,7 +79,7 @@ import {
59
79
  let _modelRefreshInFlight = null;
60
80
  const GEMINI_AVAILABILITY_TIMEOUT_MS = 1_000;
61
81
 
62
- function geminiRestError(res, text, label) {
82
+ export function geminiRestError(res, text, label) {
63
83
  let payload = null;
64
84
  try {
65
85
  payload = text ? JSON.parse(text) : null;
@@ -78,16 +98,11 @@ function geminiRestError(res, text, label) {
78
98
  err.error = detail;
79
99
  err.data = payload;
80
100
  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
- }
101
+ const details = detail.details ?? detail.errorDetails;
102
+ if (Array.isArray(details)) err.details = details;
103
+ // Typed contract (code/retryAfterMs/resetAt/quotaScope); the core decides
104
+ // whether a 429 RESOURCE_EXHAUSTED is request-local or a quota refusal.
105
+ applyGeminiErrorContract(err, { details, googleStatus: detail.status, message: detail.message });
91
106
  }
92
107
  return err;
93
108
  }
@@ -139,6 +154,9 @@ export class GeminiProvider {
139
154
  // input already includes cache. See registry.mjs.
140
155
  static inputExcludesCache = false;
141
156
  name = 'gemini';
157
+ // The two-phase ladder (gemini-retry-policy.mjs) is the whole recovery:
158
+ // a spent ladder surfaces instead of parking on loop recovery cycles.
159
+ retryPolicy = Object.freeze({ recoveryCycles: false });
142
160
  genAI;
143
161
  config;
144
162
  _fetch;
@@ -263,7 +281,11 @@ export class GeminiProvider {
263
281
  if (decision.canReuseState) {
264
282
  return state.cacheName;
265
283
  }
266
- if (!apiKey) return null;
284
+ // A request forked from a shared prefix (/btw: opts.sharedCachePrefixCount)
285
+ // only reads the session's live cache. Creating or refreshing one would
286
+ // store the fork's own suffix (the appended reply and question).
287
+ if (opts.sharedCachePrefixCount) return decision.canAttachState ? state.cacheName : null;
288
+ if (!apiKey || geminiExplicitCacheRefused(model)) return null;
267
289
  const { canAttachState } = decision;
268
290
  if (geminiPrefixBelowMinimum({ model, systemInstruction, geminiTools, contents, opts, currentIter })) {
269
291
  return canAttachState ? state.cacheName : null;
@@ -443,6 +465,7 @@ export class GeminiProvider {
443
465
  _setGeminiGlobalCache(globalCacheKey, entry);
444
466
  return entry;
445
467
  } catch (err) {
468
+ markGeminiExplicitCacheRefused(model, err);
446
469
  process.stderr.write(`[gemini] cachedContents.create error: ${err?.message || err}\n`);
447
470
  return null;
448
471
  }
@@ -571,10 +594,28 @@ export class GeminiProvider {
571
594
  cachedContent,
572
595
  });
573
596
  const parsed = parseGeminiCandidate(response, textLeakGuard);
574
- emitGeminiToolCalls(parsed.nativeToolCalls, callbacks.onToolCall);
575
597
  const usage = resolveGeminiUsage(response, opts, cachedContent, useModel);
598
+ // Terminal status first: a failed candidate's calls are never dispatched.
576
599
  const incomplete = geminiIncompleteError(response, parsed, useModel, usage);
577
- if (incomplete) throw incomplete;
600
+ if (incomplete) {
601
+ Object.assign(incomplete, geminiFinishErrorFields(response));
602
+ if (geminiFinishReasonKind(incomplete.finishReason) === 'continuation') {
603
+ // Direct Gemini only: surface the typed result with its token.
604
+ throw geminiContinuationUnsupportedError(incomplete, response);
605
+ }
606
+ // A malformed / unexpected tool call that survived the stream ladder
607
+ // (4 attempts) is spent; the loop must not multiply that budget.
608
+ if (
609
+ geminiInvalidStreamReason(response, textLeakGuard, {
610
+ afterToolResult: geminiEndsWithFunctionResponse(request.contents),
611
+ })
612
+ ) {
613
+ markProviderRecoveryExhausted(incomplete, { owner: 'gemini-stream', attempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS });
614
+ }
615
+ throw incomplete;
616
+ }
617
+ textLeakGuard?.releaseDeferredToolCalls?.();
618
+ emitGeminiToolCalls(parsed.nativeToolCalls, callbacks.onToolCall);
578
619
  return geminiSendResult(parsed, useModel, opts, usage);
579
620
  }
580
621
 
@@ -602,7 +643,7 @@ export class GeminiProvider {
602
643
  }
603
644
 
604
645
  async _restGenerateContent(useModel, apiKey, body, fallbackSignal) {
605
- const nonStreamUrl = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(useModel)}:generateContent?key=${encodeURIComponent(apiKey)}`;
646
+ const nonStreamUrl = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(geminiBareModelId(useModel))}:generateContent?key=${encodeURIComponent(apiKey)}`;
606
647
  const res = await this._fetch(nonStreamUrl, {
607
648
  method: 'POST',
608
649
  headers: { 'Content-Type': 'application/json' },
@@ -626,10 +667,10 @@ export class GeminiProvider {
626
667
  // { retryUncached: true } when the cache itself was rejected and the send
627
668
  // must be replayed without it.
628
669
  async _streamCachedViaRest(stream, internal) {
629
- const { opts, signal, useModel, contents, generationConfig, cachedContent, callbacks } = stream;
670
+ const { opts, signal, useModel, contents, generationConfig, safetySettings, cachedContent, callbacks } = stream;
630
671
  const apiKey = this._getApiKey();
631
- const genUrl = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(useModel)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(apiKey)}`;
632
- const body = geminiCachedRestBody({ opts, contents, cachedContent, generationConfig });
672
+ const genUrl = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(geminiBareModelId(useModel))}:streamGenerateContent?alt=sse&key=${encodeURIComponent(apiKey)}`;
673
+ const body = geminiCachedRestBody({ opts, contents, cachedContent, generationConfig, safetySettings });
633
674
  // cachedContent owns tools + toolConfig. The API rejects a
634
675
  // generateContent request that repeats either field.
635
676
  // Option A (mirror anthropic-oauth): no absolute wall-clock cap on a
@@ -643,10 +684,16 @@ export class GeminiProvider {
643
684
  const restPassthrough = createPassthroughSignal(signal);
644
685
  const totalSignal = restPassthrough.signal;
645
686
  try {
646
- return await withRetry(
647
- async ({ signal: attemptSignal }) => {
687
+ const afterToolResult = geminiEndsWithFunctionResponse(contents);
688
+ return await runGeminiStreamAttempts({
689
+ signal: totalSignal,
690
+ sleepFn: this.config?.sleepFn,
691
+ onRetry: geminiRetryLogger(opts, '[gemini-rest]'),
692
+ afterToolResult,
693
+ run: async ({ signal: attemptSignal, connected }) => {
648
694
  signalRequesting(opts);
649
695
  const res = await this._openRestStream(genUrl, body, attemptSignal);
696
+ connected();
650
697
  const textLeakGuard = geminiTextLeakGuardFor(stream);
651
698
  const response = await consumeGeminiRestStreamResponse(res, {
652
699
  signal: attemptSignal,
@@ -658,8 +705,7 @@ export class GeminiProvider {
658
705
  });
659
706
  return { response, textLeakGuard };
660
707
  },
661
- { signal: totalSignal, onRetry: geminiRetryLogger(opts, '[gemini-rest]') }
662
- );
708
+ });
663
709
  } catch (err) {
664
710
  if (
665
711
  !internal.skipExplicitCache &&
@@ -685,26 +731,31 @@ export class GeminiProvider {
685
731
  }
686
732
 
687
733
  async _streamViaSdk(stream) {
688
- const { opts, signal, useModel, systemInstruction, geminiTools, toolConfig, generationConfig, contents } = stream;
734
+ const { opts, signal, useModel, systemInstruction, geminiTools, toolConfig, generationConfig, safetySettings, contents } =
735
+ stream;
689
736
  const genModel = this._currentGenAI().getGenerativeModel({
690
737
  model: useModel,
691
738
  systemInstruction,
692
739
  tools: geminiTools,
693
740
  ...(toolConfig ? { toolConfig } : {}),
694
741
  ...(generationConfig ? { generationConfig } : {}),
742
+ ...(safetySettings ? { safetySettings } : {}),
695
743
  });
696
744
  // Option A (mirror anthropic-oauth): pure pass-through of the external
697
745
  // signal, no absolute streaming total cap. See the REST path.
698
746
  const sdkPassthrough = createPassthroughSignal(signal);
699
747
  const totalSignal = sdkPassthrough.signal;
700
748
  try {
701
- return await withRetry(
702
- async ({ signal: attemptSignal }) => {
749
+ return await runGeminiStreamAttempts({
750
+ signal: totalSignal,
751
+ sleepFn: this.config?.sleepFn,
752
+ onRetry: geminiRetryLogger(opts, '[gemini]'),
753
+ afterToolResult: geminiEndsWithFunctionResponse(contents),
754
+ run: async ({ signal: attemptSignal, connected }) => {
703
755
  signalRequesting(opts);
704
- return await streamGeminiSdkAttempt(genModel, stream, attemptSignal);
756
+ return await streamGeminiSdkAttempt(genModel, stream, attemptSignal, connected);
705
757
  },
706
- { signal: totalSignal, onRetry: geminiRetryLogger(opts, '[gemini]') }
707
- );
758
+ });
708
759
  } catch (err) {
709
760
  const recovered = await this._recoverGeminiNonStreaming({
710
761
  streamErr: err,
@@ -3,7 +3,7 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
3
3
  // Grok CLI client version for the cli-chat-proxy version gate (HTTP 426).
4
4
  // Effective = env override, else max(floor, learned-from-426, live npm latest
5
5
  // of the official @xai-official/grok CLI).
6
- export const GROK_CLI_VERSION_FLOOR = '1.0.52';
6
+ export const GROK_CLI_VERSION_FLOOR = '1.0.60';
7
7
  export const GROK_CLI_NPM_PACKAGE = '@xai-official/grok';
8
8
 
9
9
  const live = createNpmVersionSource(GROK_CLI_NPM_PACKAGE, { persistKey: 'grok-cli' });