mixdog 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/scripts/provider-pingpong-live.mjs +509 -0
  6. package/src/agents/advisor/AGENT.md +1 -1
  7. package/src/agents/advisor/agent.json +1 -1
  8. package/src/agents/reviewer/AGENT.md +3 -3
  9. package/src/agents/reviewer/agent.json +1 -1
  10. package/src/defaults/shipped-history.json +6 -0
  11. package/src/defaults/skills/setup/SKILL.md +6 -1
  12. package/src/defaults/skills/setup/references/actions.md +44 -22
  13. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  14. package/src/headless-exec.mjs +15 -8
  15. package/src/lib/keychain-cjs.cjs +7 -4
  16. package/src/output-styles/common.md +11 -10
  17. package/src/repl/slash-commands.mjs +16 -1
  18. package/src/rules/agent/AGENT.md +5 -5
  19. package/src/rules/lead/LEAD.md +5 -5
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  22. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  23. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  33. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  36. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  37. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  38. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  39. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  40. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  41. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  44. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  45. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  47. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
  49. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
  50. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  51. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
  58. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  63. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  65. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
  70. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  71. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  72. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
  73. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
  74. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
  75. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  76. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
  77. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  79. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  80. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
  81. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  83. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
  84. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  85. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  86. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
  87. package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
  88. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  89. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  90. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  91. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  92. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  93. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
  94. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
  96. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
  97. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  99. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  100. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  102. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
  103. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
  105. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
  106. package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
  107. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  109. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
  110. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  113. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  114. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  115. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  116. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  117. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  118. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  120. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  121. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  122. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  123. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  125. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  126. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  127. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  128. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  129. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  138. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  140. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  141. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  142. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
  143. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
  144. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  145. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  147. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
  148. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  150. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
  151. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
  153. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
  154. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
  155. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
  156. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
  157. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  158. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  159. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  160. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
  161. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  163. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
  164. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
  166. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  167. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  168. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  169. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  170. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
  171. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
  172. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  173. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
  174. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
  175. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  176. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  177. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  178. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  179. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  180. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  181. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  182. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  183. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  184. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  185. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  186. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  187. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  188. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
  189. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  190. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  191. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  192. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
  193. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  195. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
  196. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  197. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  198. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  199. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  200. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  201. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  202. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  203. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  204. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  205. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  206. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  207. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  208. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  210. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  211. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  212. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  213. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  214. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  215. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  216. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  217. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  218. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  219. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  220. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  221. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  222. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
  223. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  224. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  225. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  226. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  227. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  228. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  229. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  230. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  231. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  232. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  233. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  234. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  235. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  236. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  237. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  238. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  239. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  240. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
  241. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  242. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  243. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  244. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  245. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  246. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  248. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  249. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  250. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  251. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
  252. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  253. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  254. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  255. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  256. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  257. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  258. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
  259. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  260. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  261. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  262. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  263. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  264. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  265. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  266. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  267. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
  268. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  269. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  270. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  271. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  272. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  273. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  274. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  275. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  276. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  277. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  278. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  279. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  280. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  281. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
  282. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
  283. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
  284. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  285. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  286. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  287. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  288. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  289. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  290. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  291. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  292. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  293. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  294. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  295. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  296. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  297. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  298. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  299. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  300. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  301. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  302. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  303. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  304. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  305. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  306. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  307. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  308. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  309. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  310. package/src/runtime/github/tool.mjs +92 -2
  311. package/src/runtime/memory/index.mjs +1 -0
  312. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  313. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  314. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  315. package/src/runtime/memory/lib/pg/process.mjs +65 -50
  316. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  317. package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
  318. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  319. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  320. package/src/runtime/office/README.md +2 -2
  321. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  322. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  323. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  324. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  325. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  326. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  327. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  328. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  329. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  330. package/src/runtime/office/core/office-sessions.mjs +5 -0
  331. package/src/runtime/office/core/office-transactions.mjs +3 -0
  332. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  333. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  334. package/src/runtime/office/index.mjs +30 -0
  335. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  336. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  337. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  338. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  339. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  340. package/src/runtime/office/shared/values.mjs +0 -5
  341. package/src/runtime/shared/background-tasks.mjs +92 -14
  342. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  343. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  344. package/src/runtime/shared/buffered-appender.mjs +18 -0
  345. package/src/runtime/shared/config.mjs +78 -18
  346. package/src/runtime/shared/err-text.mjs +31 -0
  347. package/src/runtime/shared/error-presentation.mjs +159 -19
  348. package/src/runtime/shared/file-permissions.d.mts +6 -1
  349. package/src/runtime/shared/file-permissions.mjs +54 -6
  350. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  351. package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
  352. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  353. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  354. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  355. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  356. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  357. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  358. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  359. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  360. package/src/runtime/shared/loopback-listener.mjs +9 -0
  361. package/src/runtime/shared/model-display.mjs +13 -0
  362. package/src/runtime/shared/provider-api-key.mjs +2 -0
  363. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  364. package/src/runtime/shared/review-diff.mjs +52 -0
  365. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  366. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  367. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  368. package/src/runtime/shared/slow-span.mjs +114 -0
  369. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  370. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  371. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  372. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  373. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  374. package/src/runtime/shared/tool-card-model.mjs +5 -2
  375. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  376. package/src/runtime/shared/tool-primitives.mjs +13 -0
  377. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  378. package/src/runtime/shared/tool-surface.mjs +18 -3
  379. package/src/runtime/shared/tool-work-units.mjs +40 -4
  380. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  381. package/src/runtime/shared/transcript-writer.mjs +10 -2
  382. package/src/runtime/shared/turn-changes.mjs +670 -274
  383. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  384. package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
  385. package/src/runtime/shared/turn-snapshot.mjs +986 -690
  386. package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
  387. package/src/runtime/shared/user-data-guard.mjs +262 -49
  388. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  389. package/src/runtime/shared/worktrees/contract.mjs +34 -24
  390. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  391. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  392. package/src/runtime/shared/worktrees/git.mjs +8 -3
  393. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  394. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  395. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  396. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  397. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  398. package/src/runtime/terminal-bridge/client.mjs +44 -11
  399. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  400. package/src/runtime/tidy/apply.mjs +47 -2
  401. package/src/runtime/tidy/run-engines.mjs +11 -5
  402. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  403. package/src/session-runtime/boot/apis.mjs +19 -5
  404. package/src/session-runtime/boot/facade.mjs +1 -0
  405. package/src/session-runtime/boot/infrastructure.mjs +12 -1
  406. package/src/session-runtime/boot/session.mjs +0 -1
  407. package/src/session-runtime/boot/tools.mjs +4 -1
  408. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  409. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  410. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  411. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  412. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  413. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  414. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  415. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  416. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
  417. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  418. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  419. package/src/session-runtime/lifecycle/teardown.mjs +73 -8
  420. package/src/session-runtime/lifecycle-api.mjs +1 -0
  421. package/src/session-runtime/mcp-glue/connect.mjs +46 -14
  422. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  423. package/src/session-runtime/mcp-glue.mjs +3 -0
  424. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  425. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  426. package/src/session-runtime/model-route/set-route.mjs +85 -17
  427. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  428. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  429. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  430. package/src/session-runtime/native-web-search.mjs +10 -1
  431. package/src/session-runtime/new-session-config.mjs +4 -2
  432. package/src/session-runtime/provider-auth/login.mjs +4 -0
  433. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  434. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  435. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  436. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  437. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  438. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  439. package/src/session-runtime/runtime-review-api.mjs +21 -2
  440. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  441. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  442. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  443. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  444. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  445. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  446. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  447. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  448. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  449. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  450. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  451. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  452. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  453. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  454. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  455. package/src/session-runtime/services/agent-tool.mjs +3 -0
  456. package/src/session-runtime/services/channel-admin.mjs +19 -3
  457. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  458. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  459. package/src/session-runtime/services/provider-admin.mjs +81 -15
  460. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  461. package/src/session-runtime/session-turn-api.mjs +14 -5
  462. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  463. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  464. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  465. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  466. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  467. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  468. package/src/session-runtime/skills-api.mjs +3 -5
  469. package/src/session-runtime/tool-surface.mjs +5 -1
  470. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  471. package/src/session-runtime/turn/session-ops.mjs +7 -5
  472. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  473. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  474. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  475. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  476. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  477. package/src/session-runtime/turn/turn-run.mjs +25 -9
  478. package/src/session-runtime/usage-stats-api.mjs +43 -11
  479. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  480. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  481. package/src/session-runtime/workspace/transitions.mjs +190 -15
  482. package/src/standalone/daemon-agent-control.mjs +9 -1
  483. package/src/standalone/daemon-drain-policy.mjs +8 -0
  484. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  485. package/src/standalone/daemon-owner.mjs +17 -4
  486. package/src/standalone/daemon-pg-stop.mjs +51 -0
  487. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  488. package/src/standalone/daemon-stop-budget.mjs +18 -0
  489. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  490. package/src/standalone/daemon-telemetry.mjs +74 -0
  491. package/src/standalone/daemon.mjs +64 -8
  492. package/src/standalone/desktop-service-registry.mjs +5 -0
  493. package/src/standalone/local-session-runtime.mjs +70 -19
  494. package/src/standalone/session-client.mjs +42 -0
  495. package/src/standalone/session-protocol.mjs +14 -0
  496. package/src/standalone/session-proxy/projection.mjs +41 -4
  497. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  498. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  499. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  500. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  501. package/src/standalone/session-service/agent-tree.mjs +12 -0
  502. package/src/standalone/session-service/quota-resume.mjs +8 -0
  503. package/src/standalone/session-service/retention/busy.mjs +15 -1
  504. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  505. package/src/standalone/session-service-api.mjs +11 -1
  506. package/src/standalone/session-service.mjs +1 -0
  507. package/src/standalone/session-state-patch.d.mts +2 -1
  508. package/src/standalone/session-state-patch.mjs +41 -1
  509. package/src/tui/App.jsx +25 -0
  510. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  511. package/src/tui/app/app-view.jsx +1 -0
  512. package/src/tui/app/btw-controller.mjs +118 -0
  513. package/src/tui/app/create-app-pickers.mjs +6 -0
  514. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  515. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  516. package/src/tui/app/extension-pickers.mjs +2 -1
  517. package/src/tui/app/model-options.mjs +11 -16
  518. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  519. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  520. package/src/tui/app/slash-commands.mjs +20 -5
  521. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  522. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  523. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  524. package/src/tui/app/use-global-key-input.mjs +5 -12
  525. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  526. package/src/tui/components/BtwPanel.jsx +53 -0
  527. package/src/tui/components/QuestionPanel.jsx +275 -0
  528. package/src/tui/components/StatusLine.jsx +2 -0
  529. package/src/tui/components/question-panel-model.mjs +156 -0
  530. package/src/tui/components/status-line/local-line.mjs +5 -2
  531. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  532. package/src/tui/dist/index.mjs +3221 -1500
  533. package/src/tui/session/context-state.mjs +4 -2
  534. package/src/tui/session/quota-resume.mjs +46 -14
  535. package/src/tui/session/session-api/commands.mjs +43 -24
  536. package/src/tui/session/session-api/controls.mjs +2 -2
  537. package/src/tui/session/session-api/extensions.mjs +22 -0
  538. package/src/tui/session/session-api/integrations.mjs +16 -2
  539. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  540. package/src/tui/session/session-api/lifecycle.mjs +7 -2
  541. package/src/tui/session/session-api/routes.mjs +71 -7
  542. package/src/tui/session/session-api/settings.mjs +11 -14
  543. package/src/tui/session/session-api.mjs +1 -0
  544. package/src/tui/session/session-flow/drain.mjs +7 -1
  545. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  546. package/src/tui/session/tool-approval.mjs +10 -7
  547. package/src/tui/session/transcript-spill.mjs +14 -7
  548. package/src/tui/session/turn-stream.mjs +174 -31
  549. package/src/tui/session/turn.mjs +46 -16
  550. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  551. package/src/ui/statusline-agents.mjs +15 -4
  552. package/src/ui/statusline.mjs +7 -3
  553. package/src/ui/streaming-markdown-heal.mjs +34 -3
  554. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  555. package/src/workflows/default/WORKFLOW.md +3 -1
  556. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  557. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -0,0 +1,273 @@
1
+ /**
2
+ * gemini-error-contract.mjs — Google RPC error details → the provider error
3
+ * contract shared by Gemini (REST + SDK) and Antigravity:
4
+ * status, code (lowercased), retryAfterMs (uncapped), resetAt (epoch ms),
5
+ * quotaScope ('minute' | 'day' | undefined).
6
+ *
7
+ * Details come from `error.details` (REST body) or `errorDetails` (SDK
8
+ * GoogleGenerativeAIFetchError). Entries are google.rpc.RetryInfo,
9
+ * QuotaFailure and ErrorInfo.
10
+ */
11
+
12
+ const DURATION_RE = /^(?:\d+(?:\.\d+)?(?:ms|s|m|h))+$/;
13
+ const DURATION_PART_RE = /(\d+(?:\.\d+)?)(ms|s|m|h)/g;
14
+ const DURATION_UNIT_MS = { ms: 1, s: 1000, m: 60_000, h: 3_600_000 };
15
+ const BARE_SECONDS_RE = /^\d+(?:\.\d+)?$/;
16
+
17
+ // Google error classification: a per-minute quota with no RetryInfo is
18
+ // retried after one quota window.
19
+ export const GEMINI_PER_MINUTE_RETRY_MS = 60_000;
20
+
21
+ // "Please retry in 34.07s" (Gemini API) / "reset after 2h1m1s" (Cloud Code).
22
+ const MESSAGE_DELAY_RE = /(?:please retry in|reset after)\s+((?:\d+(?:\.\d+)?(?:ms|h|m|s))+)/i;
23
+ const LIMIT_ZERO_RE = /limit:\s*0(?!\d|\.\d)/i;
24
+ const CAPACITY_MESSAGE_RE = /exhausted your capacity|capacity exceeded|model_capacity_exhausted/i;
25
+ // ErrorInfo reasons that name a spent quota with no window of its own.
26
+ const TERMINAL_REASONS = new Set(['quota_exhausted', 'insufficient_g1_credits_balance']);
27
+ const CAPACITY_REASONS = new Set(['model_capacity_exhausted', 'model_capacity_exceeded']);
28
+
29
+ const GOOGLE_STATUS_HTTP = {
30
+ INVALID_ARGUMENT: 400,
31
+ UNAUTHENTICATED: 401,
32
+ PERMISSION_DENIED: 403,
33
+ NOT_FOUND: 404,
34
+ RESOURCE_EXHAUSTED: 429,
35
+ INTERNAL: 500,
36
+ UNAVAILABLE: 503,
37
+ DEADLINE_EXCEEDED: 504,
38
+ };
39
+
40
+ function detailType(entry) {
41
+ return typeof entry?.['@type'] === 'string' ? entry['@type'] : '';
42
+ }
43
+
44
+ function durationMs(value) {
45
+ if (typeof value === 'string') {
46
+ const text = value.trim();
47
+ if (BARE_SECONDS_RE.test(text)) return Math.ceil(Number(text) * 1000);
48
+ if (!DURATION_RE.test(text)) return null;
49
+ let total = 0;
50
+ for (const [, amount, unit] of text.matchAll(DURATION_PART_RE)) total += Number(amount) * DURATION_UNIT_MS[unit];
51
+ return Math.ceil(total);
52
+ }
53
+ if (value && typeof value === 'object') {
54
+ const ms = Number(value.seconds || 0) * 1000 + Number(value.nanos || 0) / 1_000_000;
55
+ return Number.isFinite(ms) && ms >= 0 ? Math.ceil(ms) : null;
56
+ }
57
+ return null;
58
+ }
59
+
60
+ // A zero wait is no advice: the caller's normal backoff (or the per-minute
61
+ // window) applies instead of an immediate re-issue.
62
+ function adviceMs(value) {
63
+ const ms = durationMs(value);
64
+ return ms > 0 ? ms : undefined;
65
+ }
66
+
67
+ function scopeOf(text) {
68
+ if (/per[\s_-]?day|daily/i.test(text)) return 'day';
69
+ if (/per[\s_-]?minute/i.test(text)) return 'minute';
70
+ return undefined;
71
+ }
72
+
73
+ /**
74
+ * `quotaScope` is 'minute' / 'day' from the typed quota ids only (never from
75
+ * the length of the server's wait); 'plan' marks a spent
76
+ * quota with no window (ErrorInfo QUOTA_EXHAUSTED, or a QuotaFailure
77
+ * violation with a zero limit; message prose alone never counts). Capacity
78
+ * (MODEL_CAPACITY_EXHAUSTED / "exhausted your capacity") is an overload of the
79
+ * shared pool: code `model_capacity_exhausted`, never a quota scope.
80
+ * @param {{ details?: unknown, errorDetails?: unknown, googleStatus?: string, message?: string }} input
81
+ * @returns {{ code?: string, retryAfterMs?: number, resetAt?: number, quotaScope?: 'minute'|'day'|'plan' }}
82
+ */
83
+ export function geminiErrorContract({ details, errorDetails, googleStatus, message } = {}) {
84
+ const list = [details, errorDetails].flatMap((entry) => (Array.isArray(entry) ? entry : []));
85
+ let reason = '';
86
+ let retryAfterMs;
87
+ let resetAt;
88
+ const scopes = new Set();
89
+ let zeroLimit = false;
90
+ for (const entry of list) {
91
+ const type = detailType(entry);
92
+ if (type.endsWith('.RetryInfo') || entry?.retryDelay !== undefined) {
93
+ retryAfterMs ??= adviceMs(entry.retryDelay);
94
+ }
95
+ if (type.endsWith('.QuotaFailure') || Array.isArray(entry?.violations)) {
96
+ for (const violation of entry.violations || []) {
97
+ const scope = scopeOf(String(violation?.quotaId || ''));
98
+ if (scope) scopes.add(scope);
99
+ // A spent allowance is named by the typed violation, not by prose.
100
+ if (String(violation?.quotaValue ?? '').trim() === '0' || LIMIT_ZERO_RE.test(String(violation?.description || ''))) {
101
+ zeroLimit = true;
102
+ }
103
+ }
104
+ }
105
+ if (type.endsWith('.ErrorInfo') || typeof entry?.reason === 'string') {
106
+ if (!reason && typeof entry.reason === 'string') reason = entry.reason.trim().toLowerCase();
107
+ const metadata = entry.metadata && typeof entry.metadata === 'object' ? entry.metadata : {};
108
+ retryAfterMs ??= adviceMs(metadata.quotaResetDelay);
109
+ const stamp = Date.parse(String(metadata.quotaResetTimeStamp || ''));
110
+ if (resetAt === undefined && Number.isFinite(stamp)) resetAt = stamp;
111
+ const scope = scopeOf(
112
+ [metadata.quotaId, metadata.quota_limit, metadata.quota_metric, entry.reason].filter(Boolean).join(' ')
113
+ );
114
+ if (scope) scopes.add(scope);
115
+ }
116
+ }
117
+ const text = typeof message === 'string' ? message : '';
118
+ // The message names capacity only when nothing typed said otherwise (a
119
+ // reason, a quota id or RetryInfo outranks it, as in classifyGoogleError).
120
+ const capacity =
121
+ CAPACITY_REASONS.has(reason) ||
122
+ (!reason && scopes.size === 0 && retryAfterMs === undefined && CAPACITY_MESSAGE_RE.test(text));
123
+ // A per-day violation outlasts any per-minute one reported with it.
124
+ let quotaScope = scopes.has('day') ? 'day' : scopes.has('minute') ? 'minute' : undefined;
125
+ if (retryAfterMs === undefined) {
126
+ const named = MESSAGE_DELAY_RE.exec(text);
127
+ if (named) retryAfterMs = adviceMs(named[1]);
128
+ }
129
+ // Per-minute quota with no RetryInfo: wait one quota window.
130
+ if (retryAfterMs === undefined && quotaScope === 'minute' && !capacity) retryAfterMs = GEMINI_PER_MINUTE_RETRY_MS;
131
+ if (resetAt === undefined && retryAfterMs !== undefined) resetAt = Date.now() + retryAfterMs;
132
+ if (!capacity && !quotaScope && (TERMINAL_REASONS.has(reason) || zeroLimit)) quotaScope = 'plan';
133
+ const status = typeof googleStatus === 'string' ? googleStatus.trim().toLowerCase() : '';
134
+ const code = capacity ? 'model_capacity_exhausted' : reason || status;
135
+ return {
136
+ ...(code ? { code } : {}),
137
+ ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
138
+ ...(resetAt !== undefined ? { resetAt } : {}),
139
+ ...(quotaScope ? { quotaScope } : {}),
140
+ };
141
+ }
142
+
143
+ /** Copy the contract fields onto `err`; an already-typed `code` is kept. */
144
+ export function applyGeminiErrorContract(err, input) {
145
+ const { code, ...rest } = geminiErrorContract(input);
146
+ Object.assign(err, rest);
147
+ if (code && !(typeof err.code === 'string' && err.code)) err.code = code;
148
+ return err;
149
+ }
150
+
151
+ /**
152
+ * The `{ error: {...} }` envelope a Gemini/Cloud Code stream carries in-band
153
+ * instead of an HTTP error status. Returns the typed error, or null when the
154
+ * chunk is an ordinary payload.
155
+ */
156
+ export function geminiInBandError(chunk, label) {
157
+ const detail = chunk && typeof chunk === 'object' ? chunk.error : null;
158
+ if (!detail || (typeof detail !== 'object' && typeof detail !== 'string')) return null;
159
+ const body = typeof detail === 'object' ? detail : { message: detail };
160
+ const googleStatus = typeof body.status === 'string' ? body.status : '';
161
+ const status = Number.isFinite(Number(body.code)) && Number(body.code) > 0
162
+ ? Number(body.code)
163
+ : GOOGLE_STATUS_HTTP[googleStatus.toUpperCase()];
164
+ const err = new Error(`${label} in-band error${status ? ` ${status}` : ''}: ${String(body.message || googleStatus || 'unknown').slice(0, 300)}`);
165
+ err.name = 'GeminiInBandError';
166
+ if (status) {
167
+ err.status = status;
168
+ err.httpStatus = status;
169
+ }
170
+ if (googleStatus) err.geminiStatus = googleStatus.toUpperCase();
171
+ err.error = body;
172
+ const details = body.details ?? body.errorDetails;
173
+ if (Array.isArray(details)) err.details = details;
174
+ return applyGeminiErrorContract(err, { details, googleStatus, message: body.message });
175
+ }
176
+
177
+ /** Throw the typed in-band error when `chunk` is an error envelope. */
178
+ export function assertNoGeminiInBandError(chunk, label) {
179
+ const err = geminiInBandError(chunk, label);
180
+ if (err) throw err;
181
+ }
182
+
183
+ const FINISH_KINDS = new Map(
184
+ Object.entries({
185
+ complete: ['STOP'],
186
+ output_limit: ['MAX_TOKENS'],
187
+ // Content refused by policy: deterministic, never re-asked.
188
+ policy: [
189
+ 'SAFETY',
190
+ 'BLOCKLIST',
191
+ 'PROHIBITED_CONTENT',
192
+ 'SPII',
193
+ 'IMAGE_SAFETY',
194
+ 'IMAGE_PROHIBITED_CONTENT',
195
+ 'ESCALATION',
196
+ 'PUP_LIMITED_DISABLED',
197
+ ],
198
+ language: ['LANGUAGE'],
199
+ recitation: ['RECITATION', 'IMAGE_RECITATION'],
200
+ // The model produced no usable call: the same request is re-asked.
201
+ invalid_tool_call: ['MALFORMED_FUNCTION_CALL', 'UNEXPECTED_TOOL_CALL', 'MALFORMED_RESPONSE'],
202
+ // Tool-call terminals a re-ask cannot fix.
203
+ tool_call: ['TOO_MANY_TOOL_CALLS', 'MISSING_THOUGHT_SIGNATURE'],
204
+ continuation: ['CONTINUATION'],
205
+ other: ['OTHER', 'IMAGE_OTHER', 'NO_IMAGE'],
206
+ }).flatMap(([kind, reasons]) => reasons.map((reason) => [reason, kind]))
207
+ );
208
+
209
+ /** Finish (or `PROMPT_`-prefixed block) reason without wire prefixes. */
210
+ export function geminiNormalizeFinishReason(finishReason) {
211
+ return String(finishReason || '')
212
+ .replace(/^FINISH_REASON_/, '')
213
+ .replace(/^PROMPT_/, '')
214
+ .replace(/^BLOCK_REASON_/, '');
215
+ }
216
+
217
+ /**
218
+ * Kind of a documented finishReason / blockReason: 'none' (absent or
219
+ * UNSPECIFIED: the stream is unfinished), 'complete', 'output_limit',
220
+ * 'policy', 'language', 'recitation', 'invalid_tool_call', 'tool_call',
221
+ * 'continuation' or 'other' (OTHER and any undocumented future reason).
222
+ */
223
+ export function geminiFinishReasonKind(finishReason) {
224
+ const reason = geminiNormalizeFinishReason(finishReason);
225
+ if (!reason || reason === 'UNSPECIFIED') return 'none';
226
+ return FINISH_KINDS.get(reason) || 'other';
227
+ }
228
+
229
+ /** Policy-refusal code for a finishReason, or null for any other reason. */
230
+ export function geminiPolicyFinishCode(finishReason) {
231
+ switch (geminiFinishReasonKind(finishReason)) {
232
+ case 'policy':
233
+ return 'safety';
234
+ case 'recitation':
235
+ return 'recitation';
236
+ case 'language':
237
+ return 'content_filter';
238
+ default:
239
+ return null;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Evidence a terminal response carries beyond its finishReason: candidate and
245
+ * prompt safety ratings, the finish message, the prompt block reason and any
246
+ * continuation token. Only present fields are returned.
247
+ */
248
+ export function geminiFinishErrorFields(response) {
249
+ const candidate = response?.candidates?.[0];
250
+ const feedback = response?.promptFeedback;
251
+ const fields = {
252
+ finishMessage: candidate?.finishMessage,
253
+ safetyRatings: candidate?.safetyRatings,
254
+ blockReason: feedback?.blockReason,
255
+ promptSafetyRatings: feedback?.safetyRatings,
256
+ continuationToken: candidate?.continuationToken,
257
+ };
258
+ return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
259
+ }
260
+
261
+ /**
262
+ * Typed result for a CONTINUATION finish, which this client cannot resume
263
+ * (Gemini direct only). Keeps the original error's partial turn and the
264
+ * token so a caller can continue it out of band.
265
+ */
266
+ export function geminiContinuationUnsupportedError(incomplete, response) {
267
+ return Object.assign(incomplete, geminiFinishErrorFields(response), {
268
+ name: 'GeminiContinuationUnsupportedError',
269
+ code: 'CONTINUATION_UNSUPPORTED',
270
+ continuationUnsupported: true,
271
+ message: 'Gemini response stopped with finishReason=CONTINUATION; continuation is not supported by this client',
272
+ });
273
+ }
@@ -0,0 +1,9 @@
1
+ // True for an id in the Gemini wire family ("gemini-4-argon", "models/gemini-3.1-pro",
2
+ // "gemini-pro-agent"). It names the family only, never a generation: it separates
3
+ // Gemini from the other model families an Antigravity gateway serves (Claude,
4
+ // GPT-OSS), which reject Gemini-only request fields outright.
5
+ const GEMINI_FAMILY = /(?:^|\/)gemini-/i;
6
+
7
+ export function isGeminiFamilyModel(model) {
8
+ return GEMINI_FAMILY.test(String(model || ''));
9
+ }
@@ -1,10 +1,15 @@
1
+ import { isGeminiFamilyModel } from './gemini-family.mjs';
2
+
1
3
  const SYNTHETIC_TOOL_SIGNATURE = 'skip_thought_signature_validator';
2
4
 
3
- // Gemini 3 validates the first functionCall in every step of the current turn.
4
- // Foreign/interrupted histories may not have a native signature for that call.
5
- // Preserve real signatures and unsigned siblings of a signed parallel batch.
5
+ // Gemini models that validate thought signatures check the first functionCall
6
+ // in every step of the current turn. Foreign/interrupted histories may not have
7
+ // a native signature for that call. Preserve real signatures and unsigned
8
+ // siblings of a signed parallel batch. The validator-bypass token is a Gemini
9
+ // wire token: it is only fabricated for the Gemini family, because the other
10
+ // families an Antigravity gateway serves reject it.
6
11
  export function ensureGeminiToolCallSignatures(contents, model) {
7
- if (!/^(?:models\/)?gemini-3(?:[.-]|$)/i.test(String(model || ''))) return contents;
12
+ if (!isGeminiFamilyModel(model)) return contents;
8
13
  let activeTurnStart = 0;
9
14
  for (let index = contents.length - 1; index >= 0; index -= 1) {
10
15
  const content = contents[index];
@@ -12,6 +12,30 @@ import { geminiThinkingConfig } from './gemini-thinking.mjs';
12
12
  import { toGeminiContents, toGeminiRequestTools, toGeminiToolConfig } from './gemini-schema.mjs';
13
13
 
14
14
  // Request pieces shared by the cached REST path and the SDK path.
15
+ // Caller-supplied generation controls the wire carries in generationConfig.
16
+ // maxOutputTokens counts thought tokens too (see the thinking docs). Anything
17
+ // invalid is rejected here instead of being dropped. (safetySettings is a
18
+ // request-level field, forwarded separately.)
19
+ function geminiGenerationControls(opts) {
20
+ const controls = {};
21
+ const maxRaw = opts.maxOutputTokens ?? opts.max_output_tokens;
22
+ if (maxRaw !== undefined && maxRaw !== null) {
23
+ const max = Number(maxRaw);
24
+ if (!Number.isInteger(max) || max <= 0) {
25
+ throw new TypeError('maxOutputTokens must be a positive integer (it includes thinking tokens).');
26
+ }
27
+ controls.maxOutputTokens = max;
28
+ }
29
+ if (opts.temperature !== undefined && opts.temperature !== null) {
30
+ const temperature = Number(opts.temperature);
31
+ if (!Number.isFinite(temperature) || temperature < 0 || temperature > 2) {
32
+ throw new TypeError('temperature must be a number between 0 and 2.');
33
+ }
34
+ controls.temperature = temperature;
35
+ }
36
+ return controls;
37
+ }
38
+
15
39
  export function buildGeminiRequest(messages, useModel, tools, opts) {
16
40
  // Gemini returns thought summaries only when the request asks for them.
17
41
  // Without this the reasoning channel stays empty for the whole turn and
@@ -19,7 +43,11 @@ export function buildGeminiRequest(messages, useModel, tools, opts) {
19
43
  // round reads as another preamble. On by default for every model; an
20
44
  // explicit opts.includeThoughts still wins.
21
45
  const thinkingConfig = geminiThinkingConfig(useModel, opts, { includeThoughts: true });
22
- const generationConfig = thinkingConfig ? { thinkingConfig } : undefined;
46
+ const controls = geminiGenerationControls(opts);
47
+ const generationConfig =
48
+ thinkingConfig || Object.keys(controls).length
49
+ ? { ...(thinkingConfig ? { thinkingConfig } : {}), ...controls }
50
+ : undefined;
23
51
  const systemInstruction =
24
52
  messages
25
53
  .filter((m) => m.role === 'system')
@@ -29,17 +57,24 @@ export function buildGeminiRequest(messages, useModel, tools, opts) {
29
57
  const contents = toGeminiContents(chatMsgs, useModel);
30
58
  if (!contents.length) throw new Error('No messages to send');
31
59
 
32
- const requestTools = toGeminiRequestTools(tools, opts.nativeTools);
60
+ const requestTools = toGeminiRequestTools(tools, opts.nativeTools, { onToolExcluded: opts.onToolExcluded });
33
61
  const geminiTools = requestTools.length ? requestTools : undefined;
34
- const toolConfig = tools?.length ? toGeminiToolConfig(opts.toolChoice) : undefined;
35
- return { generationConfig, systemInstruction, contents, geminiTools, toolConfig };
62
+ // Tool config only accompanies function declarations that were kept.
63
+ const toolConfig = requestTools.some((entry) => entry.functionDeclarations?.length)
64
+ ? toGeminiToolConfig(opts.toolChoice)
65
+ : undefined;
66
+ if (opts.safetySettings !== undefined && opts.safetySettings !== null && !Array.isArray(opts.safetySettings)) {
67
+ throw new TypeError('safetySettings must be an array of { category, threshold } entries.');
68
+ }
69
+ const safetySettings = opts.safetySettings?.length ? opts.safetySettings : undefined;
70
+ return { generationConfig, systemInstruction, contents, geminiTools, toolConfig, safetySettings };
36
71
  }
37
72
 
38
73
  // The generateContent body for a cached prefix. The cache carries the
39
74
  // recorded prefix; every uncached tail turn is sent, not just the last
40
75
  // message, so reused cachedContents preserve full conversation context
41
76
  // between periodic refreshes.
42
- export function geminiCachedRestBody({ opts, contents, cachedContent, generationConfig }) {
77
+ export function geminiCachedRestBody({ opts, contents, cachedContent, generationConfig, safetySettings }) {
43
78
  const cachedPrefixContentCount = Number.isFinite(Number(opts.providerState?.gemini?.cachePrefixContentCount))
44
79
  ? Math.max(0, Math.min(contents.length, Math.trunc(Number(opts.providerState.gemini.cachePrefixContentCount))))
45
80
  : 0;
@@ -48,5 +83,6 @@ export function geminiCachedRestBody({ opts, contents, cachedContent, generation
48
83
  contents: deltaContents.length ? deltaContents : contents.slice(-1),
49
84
  cachedContent,
50
85
  ...(generationConfig ? { generationConfig } : {}),
86
+ ...(safetySettings ? { safetySettings } : {}),
51
87
  };
52
88
  }
@@ -14,6 +14,7 @@ import { createProviderReplay } from './lib/provider-replay.mjs';
14
14
  import { collectGeminiGroundingSources, parseGeminiTextPartMetadata, parseToolCalls } from './gemini-schema.mjs';
15
15
  import { _resolveGeminiCacheUsage } from './gemini-cache.mjs';
16
16
  import { traceGeminiCache } from './gemini-cache-policy.mjs';
17
+ import { geminiPolicyFinishCode } from './gemini-error-contract.mjs';
17
18
 
18
19
  // Candidate text and tool calls after the text-leak guard: leaked
19
20
  // (text-embedded) tool calls are appended to the native ones and empty the
@@ -64,7 +65,8 @@ export function geminiIncompleteError(response, parsed, useModel, usage = null)
64
65
  if (!finishReason || normalizedFinishReason === 'STOP') return null;
65
66
  return Object.assign(new Error(`Gemini response incomplete: finishReason=${finishReason}`), {
66
67
  name: 'ProviderIncompleteError',
67
- code: 'PROVIDER_INCOMPLETE',
68
+ // Safety/recitation are policy refusals the core presents as such.
69
+ code: geminiPolicyFinishCode(finishReason) || 'PROVIDER_INCOMPLETE',
68
70
  providerIncomplete: true,
69
71
  finishReason,
70
72
  partialContent: parsed.content,
@@ -0,0 +1,180 @@
1
+ /**
2
+ * gemini-retry-policy.mjs — Gemini API retry constants and the two-phase
3
+ * attempt loop (request backoff and stream retry):
4
+ *
5
+ * connection phase up to 10 attempts, 5s initial delay doubling to a 30s
6
+ * cap, ±30% jitter (a server-advised wait replaces it);
7
+ * stream phase a failure after the response opened (a stream that ends
8
+ * with no finish reason is one: the transport raises it as
9
+ * a truncated stream), or a response the model left
10
+ * unusable (malformed / unexpected tool call, empty text),
11
+ * re-issues the whole request up to 4 attempts total at
12
+ * 1s / 2s / 4s. A response that finished with no finish
13
+ * reason is never judged here, and right after a tool
14
+ * result an empty STOP answer is accepted as the model's
15
+ * answer (an empty stream with no finish reason is still
16
+ * unfinished). An empty answer that
17
+ * survives all 4 attempts throws a typed, provider-terminal
18
+ * invalid-stream error instead of returning nothing.
19
+ * long advice a server wait over 300s ends recovery on both phases
20
+ * (provider-terminal, no quota scope; the wait stays on
21
+ * the error for display).
22
+ *
23
+ * Replay stays gated by the stream-outcome contract: a stream that exposed
24
+ * text or dispatched a tool is never re-issued.
25
+ */
26
+ import { readStreamOutcome } from './lib/stream-outcome.mjs';
27
+ import { DETERMINISTIC_KINDS, providerErrorKind, typedCodeKind } from './provider-error-kind.mjs';
28
+ import { midstreamRetryDelayMs } from './retry-backoff.mjs';
29
+ import { geminiFinishReasonKind, geminiNormalizeFinishReason } from './gemini-error-contract.mjs';
30
+ import { classifyError, markProviderRecoveryExhausted, sleepWithAbort, withRetry } from './retry-classifier.mjs';
31
+
32
+ export const GEMINI_REQUEST_RETRY = Object.freeze({
33
+ maxAttempts: 10,
34
+ // Wait before retry N reads index N (the leading 5s covers index 0 and 1):
35
+ // 5s, 10s, 20s, then the 30s cap.
36
+ backoffMs: Object.freeze([5000, 5000, 10_000, 20_000, 30_000]),
37
+ retryJitterRatio: 0.3,
38
+ // Server advice gets positive-only jitter and is never below the step.
39
+ retryAfterJitterRatio: 0.2,
40
+ adviceAtLeastBackoff: true,
41
+ retry499: true,
42
+ });
43
+
44
+ // A server wait longer than this is a lock-out, not a retry.
45
+ export const GEMINI_MAX_RETRYABLE_ADVICE_MS = 300_000;
46
+
47
+ function hasLongAdvice(err) {
48
+ const { kind, retryAfterMs } = providerErrorKind(err);
49
+ return retryAfterMs > GEMINI_MAX_RETRYABLE_ADVICE_MS && !DETERMINISTIC_KINDS.has(kind);
50
+ }
51
+
52
+ /** True when the request's last turn is a tool result (functionResponse). */
53
+ export function geminiEndsWithFunctionResponse(contents) {
54
+ const last = Array.isArray(contents) ? contents[contents.length - 1] : null;
55
+ return Array.isArray(last?.parts) && last.parts.some((part) => part?.functionResponse);
56
+ }
57
+
58
+ export const GEMINI_MIDSTREAM_MAX_ATTEMPTS = 4;
59
+ export const GEMINI_MIDSTREAM_INITIAL_DELAY_MS = 1000;
60
+
61
+ const midstreamStepMs = (streamAttempt) => GEMINI_MIDSTREAM_INITIAL_DELAY_MS * 2 ** streamAttempt;
62
+
63
+ /**
64
+ * Wait before re-issuing after a stream-phase failure, or null when it must
65
+ * surface. A server-advised wait on a retryable kind replaces the step.
66
+ */
67
+ export function geminiMidstreamDelayMs(err, streamAttempt, signal = null) {
68
+ if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS || signal?.aborted) return null;
69
+ if (readStreamOutcome(err).replaySafe !== true) return null;
70
+ const { kind } = providerErrorKind(err);
71
+ if (hasLongAdvice(err)) return null;
72
+ // A status-only 499 (client closed request) is retried, as on the request
73
+ // ladder; a typed code on the 499 keeps it final.
74
+ const closed499 =
75
+ GEMINI_REQUEST_RETRY.retry499 && kind === 'invalid_request' && statusOf(err) === 499 && !typedCodeKind(err);
76
+ if (DETERMINISTIC_KINDS.has(kind) && !closed499) return null;
77
+ // 429 is request-local here: classifyError calls it permanent, but a rate
78
+ // limit that is not a spent quota is retried like any other stream fault.
79
+ if (!closed499 && classifyError(err) !== 'transient' && kind !== 'rate_limited') return null;
80
+ return midstreamRetryDelayMs(err, midstreamStepMs(streamAttempt));
81
+ }
82
+
83
+ const statusOf = (err) => Number(err?.httpStatus || err?.status || 0);
84
+
85
+ /**
86
+ * Invalid stream: the reason a finished response is unusable
87
+ * ('MALFORMED_FUNCTION_CALL' | 'UNEXPECTED_TOOL_CALL' | 'MALFORMED_RESPONSE' |
88
+ * 'NO_RESPONSE_TEXT'), or null. A response that carried a tool call, or whose
89
+ * text was already relayed, is never invalid (and never replayed). Only a
90
+ * STOP with no text is "no text"; every other documented finish (policy,
91
+ * output limit, continuation, OTHER, ...) is owned by the incomplete handling.
92
+ */
93
+ export function geminiInvalidStreamReason(response, textLeakGuard = null, { afterToolResult = false } = {}) {
94
+ const candidate = response?.candidates?.[0];
95
+ const parts = Array.isArray(candidate?.content?.parts) ? candidate.content.parts : [];
96
+ if (parts.some((part) => part?.functionCall)) return null;
97
+ if ((textLeakGuard?.getLeakedToolCalls?.() || []).length > 0) return null;
98
+ if ((textLeakGuard?.getRelayedText?.() || '').length > 0) return null;
99
+ const finish = geminiNormalizeFinishReason(candidate?.finishReason);
100
+ const kind = geminiFinishReasonKind(finish);
101
+ if (kind === 'invalid_tool_call') return finish;
102
+ if (kind !== 'complete') return null;
103
+ // Right after a tool result the model may legitimately answer with nothing.
104
+ if (afterToolResult) return null;
105
+ const text = parts
106
+ .filter((part) => part?.thought !== true && typeof part?.text === 'string')
107
+ .map((part) => part.text)
108
+ .join('')
109
+ .replace(/[\u200B-\u200D\uFEFF\u200E\u200F]/g, '')
110
+ .trim();
111
+ return text ? null : 'NO_RESPONSE_TEXT';
112
+ }
113
+
114
+ /**
115
+ * Run `run` under the Gemini ladder. `run({ signal, connected })` performs one
116
+ * open + consume and calls `connected()` once the response opened; it resolves
117
+ * `{ response, textLeakGuard }`.
118
+ */
119
+ export async function runGeminiStreamAttempts({ run, signal, onRetry, sleepFn, afterToolResult = false }) {
120
+ for (let streamAttempt = 0; ; streamAttempt++) {
121
+ let connected = false;
122
+ let result;
123
+ try {
124
+ result = await withRetry(
125
+ async ({ signal: attemptSignal }) => {
126
+ try {
127
+ return await run({
128
+ signal: attemptSignal,
129
+ connected: () => {
130
+ connected = true;
131
+ },
132
+ });
133
+ } catch (err) {
134
+ // A server wait over the limit ends recovery on either phase.
135
+ if (hasLongAdvice(err)) {
136
+ throw markProviderRecoveryExhausted(err, {
137
+ owner: connected ? 'gemini-stream' : 'gemini-connection',
138
+ terminal: true,
139
+ });
140
+ }
141
+ // The connection ladder is spent on opening; a stream-phase
142
+ // failure belongs to the stream ladder below.
143
+ throw connected ? markProviderRecoveryExhausted(err, { owner: 'gemini-connection' }) : err;
144
+ }
145
+ },
146
+ { ...GEMINI_REQUEST_RETRY, finalOnExhaust: true, signal, sleepFn, onRetry }
147
+ );
148
+ } catch (err) {
149
+ const waitMs = connected ? geminiMidstreamDelayMs(err, streamAttempt, signal) : null;
150
+ if (waitMs == null) throw err;
151
+ onRetry?.({ attempt: streamAttempt + 1, maxAttempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS, lastErr: err, delayMs: waitMs });
152
+ await sleepWithAbort(waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
153
+ continue;
154
+ }
155
+ const invalid = geminiInvalidStreamReason(result.response, result.textLeakGuard, { afterToolResult });
156
+ if (!invalid || signal?.aborted) return result;
157
+ const unusable = Object.assign(new Error(`Gemini stream unusable: ${invalid}`), {
158
+ name: 'InvalidStreamError',
159
+ code: invalid.toLowerCase(),
160
+ });
161
+ if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS) {
162
+ // A spent ladder on an empty answer is a failure, not an answer; a
163
+ // malformed / unexpected tool call keeps its finish-reason error.
164
+ if (invalid !== 'NO_RESPONSE_TEXT') return result;
165
+ throw markProviderRecoveryExhausted(unusable, {
166
+ owner: 'gemini-stream',
167
+ attempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
168
+ terminal: true,
169
+ });
170
+ }
171
+ const waitMs = midstreamStepMs(streamAttempt);
172
+ onRetry?.({
173
+ attempt: streamAttempt + 1,
174
+ maxAttempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
175
+ lastErr: unusable,
176
+ delayMs: waitMs,
177
+ });
178
+ await sleepWithAbort(waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
179
+ }
180
+ }