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
@@ -1,5 +1,6 @@
1
1
  // Shared request controls for Chat Completions and Responses gateways.
2
2
  import { emitProviderRetryStage, retryDelayLabel } from './retry-classifier.mjs';
3
+ import { isTogetherBaseURL } from './openai-compat-reasoning-field.mjs';
3
4
 
4
5
  // withRetry onRetry for the compat stream sends: one stderr line plus a
5
6
  // display-only 'reconnecting' stage for the UI.
@@ -13,8 +14,83 @@ export function compatStreamRetryReporter(label, opts) {
13
14
  };
14
15
  }
15
16
 
16
- export function applyCompatToolChoice(body, opts = {}) {
17
- if (body.tools?.length && opts.toolChoice === 'none') body.tool_choice = 'none';
17
+ // Providers whose docs accept `required` / named tool_choice, and the subset
18
+ // that also documents `parallel_tool_calls`. DeepSeek documents no
19
+ // parallel_tool_calls, and rejects forced choices in thinking mode.
20
+ const FORCED_CHOICE_PROVIDERS = new Set([
21
+ 'xai',
22
+ 'deepseek',
23
+ 'openrouter',
24
+ 'mistral',
25
+ 'groq',
26
+ 'fireworks',
27
+ 'together',
28
+ 'command-code-oauth',
29
+ ]);
30
+ const PARALLEL_CALLS_PROVIDERS = new Set([
31
+ 'xai',
32
+ 'openrouter',
33
+ 'mistral',
34
+ 'groq',
35
+ 'fireworks',
36
+ 'together',
37
+ 'command-code-oauth',
38
+ ]);
39
+
40
+ function hostMatches(baseURL, domain) {
41
+ try {
42
+ const host = new URL(String(baseURL || '')).hostname.toLowerCase();
43
+ return host === domain || host.endsWith(`.${domain}`);
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+
49
+ function compatToolFamily(provider, baseURL) {
50
+ if (FORCED_CHOICE_PROVIDERS.has(provider)) return provider;
51
+ if (isTogetherBaseURL(baseURL)) return 'together';
52
+ if (hostMatches(baseURL, 'fireworks.ai')) return 'fireworks';
53
+ if (hostMatches(baseURL, 'mistral.ai')) return 'mistral';
54
+ if (hostMatches(baseURL, 'groq.com')) return 'groq';
55
+ return null;
56
+ }
57
+
58
+ function forcedToolChoice(choice, wire) {
59
+ if (choice === 'required') return 'required';
60
+ if (!choice || typeof choice !== 'object') return null;
61
+ const name = choice.function?.name || choice.name;
62
+ if (typeof name !== 'string' || !name) return null;
63
+ return wire === 'responses' ? { type: 'function', name } : { type: 'function', function: { name } };
64
+ }
65
+
66
+ /**
67
+ * Tool-choice controls. `none` is universal. With `ctx` ({ provider, baseURL,
68
+ * wire: 'chat'|'responses', thinking: 'enabled'|'disabled'|null }) the
69
+ * documented providers also get explicit `required` / named tool_choice and an
70
+ * explicit boolean `opts.parallelToolCalls`; without it only `none` applies.
71
+ * DeepSeek rejects forced choices while thinking is explicitly enabled and
72
+ * keeps omitting them when thinking is left to the server default.
73
+ */
74
+ export function applyCompatToolChoice(body, opts = {}, ctx = null) {
75
+ if (!(body.tools?.length > 0)) return body;
76
+ if (opts.toolChoice === 'none') body.tool_choice = 'none';
77
+ const family = ctx?.provider ? compatToolFamily(ctx.provider, ctx.baseURL) : null;
78
+ if (!family) return body;
79
+ const forced = forcedToolChoice(opts.toolChoice, ctx.wire);
80
+ if (forced) {
81
+ if (family === 'deepseek' && ctx.thinking === 'enabled') {
82
+ throw Object.assign(
83
+ new Error(
84
+ '[deepseek] tool_choice "required" or a named tool is not supported in thinking mode; disable thinking (effort none) to force a tool call'
85
+ ),
86
+ { code: 'unsupported_tool_choice' }
87
+ );
88
+ }
89
+ if (family !== 'deepseek' || ctx.thinking === 'disabled') body.tool_choice = forced;
90
+ }
91
+ if (typeof opts.parallelToolCalls === 'boolean' && PARALLEL_CALLS_PROVIDERS.has(family)) {
92
+ body.parallel_tool_calls = opts.parallelToolCalls;
93
+ }
18
94
  return body;
19
95
  }
20
96
 
@@ -8,6 +8,7 @@ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
8
8
  import { openInBrowser } from '../../../shared/open-url.mjs';
9
9
  import { createOAuthPkce } from './lib/oauth-pkce.mjs';
10
10
  import { accountIdentityFields, normalizeAccountIdentity } from './lib/oauth-token-utils.mjs';
11
+ import { retryAfterMsFromError } from './retry-classifier.mjs';
11
12
 
12
13
  const LOGIN_URL = 'https://cursor.com/loginDeepControl';
13
14
  const POLL_URL = 'https://api2.cursor.sh/auth/poll';
@@ -17,6 +18,23 @@ const REFRESH_SKEW_MS = 5 * 60_000;
17
18
  // abort signal, which must never govern work every other waiter depends on.
18
19
  const REFRESH_TIMEOUT_MS = 30_000;
19
20
  const refreshesInFlight = new Map();
21
+ export const REAUTH_GUIDANCE = 'Open /providers in Mixdog to sign in again.';
22
+ // The refresh credential itself was rejected.
23
+ const PERMANENT_REFRESH_STATUS = new Set([400, 401, 403]);
24
+ const EXCHANGE_STATUS_CODE = Object.freeze({
25
+ 400: 'invalid_argument',
26
+ 401: 'unauthenticated',
27
+ 403: 'permission_denied',
28
+ 429: 'resource_exhausted',
29
+ 500: 'internal',
30
+ 502: 'unavailable',
31
+ 503: 'unavailable',
32
+ 504: 'deadline_exceeded',
33
+ });
34
+
35
+ function reauthError(message) {
36
+ return Object.assign(new Error(message), { reauthRequired: true });
37
+ }
20
38
 
21
39
  // Wait on the shared refresh while still honouring THIS caller's abort signal.
22
40
  // Aborting only ends this caller's wait; the exchange keeps running for the
@@ -112,7 +130,7 @@ function saveCredentials(tokens) {
112
130
 
113
131
  export function hasCursorOAuthCredentials() {
114
132
  const tokens = loadStoredCredentials();
115
- if (!tokens?.access_token) return false;
133
+ if (!tokens?.access_token || tokens.reauth_required === true) return false;
116
134
  const expiresAt = Number(tokens.expires_at) || 0;
117
135
  return expiresAt === 0 || expiresAt > Date.now() || Boolean(tokens.refresh_token);
118
136
  }
@@ -129,6 +147,18 @@ export function describeCursorOAuthCredentials() {
129
147
  detail: 'Mixdog token store or CURSOR_ACCESS_TOKEN',
130
148
  };
131
149
  }
150
+ if (tokens.reauth_required === true) {
151
+ return {
152
+ authenticated: false,
153
+ usable: false,
154
+ refreshable: false,
155
+ reauthRequired: true,
156
+ status: 'Reauth Required',
157
+ detail: tokens.source || 'Cursor OAuth',
158
+ expiresAt: Number(tokens.expires_at) || null,
159
+ ...accountIdentityFields(tokens.identity),
160
+ };
161
+ }
132
162
  const expiresAt = Number(tokens.expires_at) || 0;
133
163
  const expired = expiresAt > 0 && expiresAt <= Date.now();
134
164
  const expiring = expiresAt > 0 && expiresAt <= Date.now() + REFRESH_SKEW_MS;
@@ -197,8 +227,25 @@ export async function exchangeCursorToken(token, { fetchFn = fetch, signal } = {
197
227
  });
198
228
  if (!response.ok) {
199
229
  const detail = await response.text().catch(() => '');
200
- const error = new Error(`Cursor token exchange failed (${response.status})${detail ? `: ${detail}` : ''}`);
230
+ // Only a credential verdict is permanent; throttles, 5xx and network
231
+ // failures leave the refresh token valid and stay retryable upstream.
232
+ const reauthRequired = PERMANENT_REFRESH_STATUS.has(response.status);
233
+ const error = new Error(
234
+ `Cursor token exchange failed (${response.status})${detail ? `: ${detail}` : ''}${
235
+ reauthRequired ? `. ${REAUTH_GUIDANCE}` : ''
236
+ }`
237
+ );
201
238
  error.status = response.status;
239
+ error.httpStatus = response.status;
240
+ const code = EXCHANGE_STATUS_CODE[response.status];
241
+ if (code) error.code = code;
242
+ const headers = Object.fromEntries(response.headers?.entries?.() ?? []);
243
+ if (Object.keys(headers).length) {
244
+ error.headers = headers;
245
+ const retryAfterMs = retryAfterMsFromError(error);
246
+ if (Number.isFinite(retryAfterMs)) error.retryAfterMs = retryAfterMs;
247
+ }
248
+ if (reauthRequired) error.reauthRequired = true;
202
249
  throw error;
203
250
  }
204
251
  const data = await response.json();
@@ -277,9 +324,7 @@ export async function resolveCursorOAuthAccessToken({ forceRefresh = false, fetc
277
324
  if (!forceRefresh && !expiring) return tokens.access_token;
278
325
  if (!tokens.refresh_token) {
279
326
  if (!tokens.expires_at || tokens.expires_at > Date.now()) return tokens.access_token;
280
- throw new Error(
281
- 'Cursor access token expired and has no refresh token. Open /providers in Mixdog to sign in again.'
282
- );
327
+ throw reauthError(`Cursor access token expired and has no refresh token. ${REAUTH_GUIDANCE}`);
283
328
  }
284
329
  const refreshPath = credentialsPath();
285
330
  let refreshInFlight = refreshesInFlight.get(refreshPath);
@@ -296,17 +341,37 @@ export async function resolveCursorOAuthAccessToken({ forceRefresh = false, fetc
296
341
  return latest;
297
342
  }
298
343
  if (!latest.refresh_token) {
299
- throw new Error('Cursor OAuth refresh token is unavailable. Open /providers in Mixdog to sign in again.');
344
+ throw reauthError(`Cursor OAuth refresh token is unavailable. ${REAUTH_GUIDANCE}`);
300
345
  }
301
346
  // Deliberately NOT the caller's signal: this exchange is shared by
302
347
  // every concurrent waiter, so letting the first caller's abort
303
348
  // (turn cancel / session close) kill it would fail the refresh for
304
349
  // all of them and leave the process without a usable token. Its own
305
350
  // timeout bounds the request instead.
306
- const next = await exchangeCursorToken(latest.refresh_token, {
307
- fetchFn,
308
- signal: AbortSignal.timeout(REFRESH_TIMEOUT_MS),
309
- });
351
+ let next;
352
+ try {
353
+ next = await exchangeCursorToken(latest.refresh_token, {
354
+ fetchFn,
355
+ signal: AbortSignal.timeout(REFRESH_TIMEOUT_MS),
356
+ });
357
+ } catch (error) {
358
+ if (error?.reauthRequired !== true) throw error;
359
+ // Mark the account dead only if no other process replaced the
360
+ // refresh token meanwhile. Non-destructive: tokens stay intact and
361
+ // a new sign-in rewrites the file without this marker.
362
+ if (!process.env.CURSOR_ACCESS_TOKEN || boundProviderAuthPath('cursor-oauth')) {
363
+ const current = loadStoredCredentials();
364
+ if (current?.refresh_token && current.refresh_token === latest.refresh_token) {
365
+ const { source: _source, ...persisted } = current;
366
+ try {
367
+ saveCredentials({ ...persisted, reauth_required: true });
368
+ } catch (saveError) {
369
+ process.stderr.write(`[cursor-oauth] dead-account mark failed: ${String(saveError?.message || saveError).slice(0, 200)}\n`);
370
+ }
371
+ }
372
+ }
373
+ throw error;
374
+ }
310
375
  if (!process.env.CURSOR_ACCESS_TOKEN || boundProviderAuthPath('cursor-oauth')) {
311
376
  saveCredentials(next);
312
377
  }
@@ -4,7 +4,7 @@ import { createRemoteVersionSource } from './npm-cli-version.mjs';
4
4
  // documented version endpoint; the official installer script embeds the
5
5
  // current build (…/lab/<build>/…), parsed best-effort. Any format change or
6
6
  // failure falls back to the floor.
7
- export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.10.12-d47ca18';
7
+ export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.10.14-941492e';
8
8
  const BUILD_PATTERN = /downloads\.cursor\.com\/lab\/(\d{4}\.\d{2}\.\d{2}-[0-9a-f]{6,40})\//;
9
9
  export const CURSOR_INSTALL_URL = 'https://cursor.com/install';
10
10
 
@@ -1,4 +1,6 @@
1
1
  import { mergeFlatSchemaProperty } from './lib/flat-schema-merge.mjs';
2
+ import { REAUTH_GUIDANCE } from './cursor-auth.mjs';
3
+ import { isTransientErrorCode } from './provider-error-kind.mjs';
2
4
 
3
5
  const SCHEMA_ANNOTATIONS = new Set([
4
6
  '$comment',
@@ -32,6 +34,15 @@ export function resolveCursorStreamTuning(env = process.env) {
32
34
  idleTimeoutMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_IDLE_TIMEOUT_MS, 180_000),
33
35
  parkTimeoutMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_PARK_TIMEOUT_MS, 45_000, { min: 1_000 }),
34
36
  maxRetries: boundedInteger(env.MIXDOG_CURSOR_STREAM_IDLE_MAX_RETRIES, 5, { max: 10 }),
37
+ // Transport retry: 250ms base, 2s ceiling.
38
+ retryBaseDelayMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_RETRY_BASE_MS, 250, { max: 60_000 }),
39
+ retryMaxDelayMs: boundedInteger(env.MIXDOG_CURSOR_STREAM_RETRY_MAX_MS, 2_000, { max: 120_000 }),
40
+ // A rate limit gets one recovery (min(1, maxRetries)).
41
+ maxThrottleRetries: boundedInteger(env.MIXDOG_CURSOR_THROTTLE_MAX_RETRIES, 1, { max: 5 }),
42
+ // One credential-refresh retry; the outer provider layer adds none for a
43
+ // stream that already ran, and re-sending a rejected credential only
44
+ // hammers the token endpoint.
45
+ maxAuthRetries: boundedInteger(env.MIXDOG_CURSOR_AUTH_MAX_RETRIES, 1, { max: 5 }),
35
46
  };
36
47
  }
37
48
 
@@ -221,6 +232,13 @@ export function createCursorStreamWatchdog({ idleTimeoutMs, parkTimeoutMs, onTim
221
232
  if (kind === 'work') arm(idleTimeoutMs, 'idle');
222
233
  else if (kind === 'park') arm(parkTimeoutMs, 'park');
223
234
  },
235
+ // Suspend both deadlines during an intentional retry wait; the next
236
+ // start()/progress('work'|'park') re-arms them, so only real stream
237
+ // silence is ever counted.
238
+ pause() {
239
+ if (timer) clearTimeout(timer);
240
+ timer = null;
241
+ },
224
242
  stop() {
225
243
  stopped = true;
226
244
  if (timer) clearTimeout(timer);
@@ -251,8 +269,85 @@ export function cursorInteractionProgress(update) {
251
269
  return 'none';
252
270
  }
253
271
 
272
+ // Codes that describe a transient server verdict or a recognized transport
273
+ // truncation. Everything else (protocol/decoding errors, 4xx verdicts,
274
+ // unknown failures) is deterministic and is never replayed in place.
275
+ const TRANSIENT_STREAM_CODES = new Set([
276
+ 'unavailable',
277
+ 'internal',
278
+ 'deadline_exceeded',
279
+ 'stream_aborted',
280
+ 'goaway',
281
+ 'connection_timeout',
282
+ 'stream_idle_timeout',
283
+ 'stream_park_timeout',
284
+ 'incomplete_stream',
285
+ 'ERR_HTTP2_STREAM_CANCEL',
286
+ 'ERR_HTTP2_GOAWAY_SESSION',
287
+ ]);
288
+
289
+ // Protocol-mismatch check: Cursor wraps a wire mismatch in `internal`, and
290
+ // re-sending a request the schema cannot decode only burns the retry budget.
291
+ // Tested on the server's own detail, before the transient code is trusted.
292
+ const PROTOCOL_DRIFT_RE = /\b(protocol|protobuf|wire drift|invalid argument|failed_precondition)\b/i;
293
+
294
+ export function isCursorProtocolDrift(error) {
295
+ if (error?.code === 'protocol_error') return true;
296
+ return PROTOCOL_DRIFT_RE.test(String(error?.cursorDetail ?? ''));
297
+ }
298
+
254
299
  export function isRetryableCursorStreamError(error) {
255
- const status = Number(error?.httpStatus || error?.status || 0);
256
300
  if (error?.name === 'AbortError') return false;
257
- return status === 0 || status >= 500;
301
+ const code = String(error?.code || '');
302
+ if (!TRANSIENT_STREAM_CODES.has(code) && !isTransientErrorCode(code)) return false;
303
+ return !isCursorProtocolDrift(error);
304
+ }
305
+
306
+ // Rejected credentials: worth one forced token refresh before giving up.
307
+ export function isCursorAuthError(error) {
308
+ if (error?.name === 'AbortError') return false;
309
+ return error?.code === 'unauthenticated' || error?.code === 'permission_denied';
310
+ }
311
+
312
+ // Credentials for in-place restarts. Every retry force-refreshes and keeps the
313
+ // previous token if the refresh fails; a rejected credential is
314
+ // only retried when the refresh produced a different token.
315
+ export function createCursorCredentialRefresher(initialToken, getAccessToken) {
316
+ let current = initialToken;
317
+ return {
318
+ async refresh(reason, error) {
319
+ let next = current;
320
+ if (getAccessToken) {
321
+ try {
322
+ next = (await getAccessToken({ forceRefresh: true })) || current;
323
+ } catch {
324
+ /* keep the previous token */
325
+ }
326
+ }
327
+ if (reason === 'auth' && next === current) throw error;
328
+ current = next;
329
+ return current;
330
+ },
331
+ };
332
+ }
333
+
334
+ // Final-failure guidance appended once to the original Cursor detail
335
+ // (transport-failure hint).
336
+ export function withCursorFailureHint(error) {
337
+ if (!(error instanceof Error) || error.cursorHintApplied) return error;
338
+ const hint = isCursorAuthError(error)
339
+ ? REAUTH_GUIDANCE
340
+ : isCursorProtocolDrift(error)
341
+ ? 'Cursor may have changed its wire protocol; update Mixdog or set MIXDOG_CURSOR_CLIENT_VERSION if this persists.'
342
+ : '';
343
+ if (!hint) return error;
344
+ error.cursorHintApplied = true;
345
+ error.message = `${error.message} ${hint}`;
346
+ return error;
347
+ }
348
+
349
+ // resource_exhausted / 429 that is a throttle, not an oversized request.
350
+ export function isCursorThrottleError(error) {
351
+ if (error?.name === 'AbortError' || error?.code === 'request_too_large') return false;
352
+ return error?.code === 'resource_exhausted' || Number(error?.httpStatus || error?.status || 0) === 429;
258
353
  }
@@ -3,11 +3,30 @@
3
3
  * response: frame parsing, tool-batch hand-off to the run registry, the
4
4
  * close verdict and the bounded in-place restart.
5
5
  */
6
- import { isRetryableCursorStreamError } from './cursor-wire-guards.mjs';
7
- import { markProviderRecoveryExhausted } from './retry-classifier.mjs';
6
+ import {
7
+ isCursorAuthError,
8
+ isCursorThrottleError,
9
+ isRetryableCursorStreamError,
10
+ withCursorFailureHint,
11
+ } from './cursor-wire-guards.mjs';
12
+ import { emitProviderRetryStage, markProviderRecoveryExhausted, retryAfterMsFromError } from './retry-classifier.mjs';
8
13
  import { createFrameParser, cursorError, parseEndStream } from './cursor-wire-transport.mjs';
9
14
  import { processServerMessage } from './cursor-wire-stream-messages.mjs';
10
15
 
16
+ // Resolves after `ms`, or early when the stream is cancelled.
17
+ function abortableSleep(ms, signal) {
18
+ return new Promise((resolve) => {
19
+ if (signal?.aborted) return resolve();
20
+ const done = () => {
21
+ clearTimeout(timer);
22
+ signal?.removeEventListener('abort', done);
23
+ resolve();
24
+ };
25
+ const timer = setTimeout(done, ms);
26
+ signal?.addEventListener('abort', done, { once: true });
27
+ });
28
+ }
29
+
11
30
  /**
12
31
  * @param {object} ctx
13
32
  * @param {{ bridge, heartbeat }} ctx.live the bridge currently feeding the stream (mutated on restart)
@@ -16,7 +35,10 @@ import { processServerMessage } from './cursor-wire-stream-messages.mjs';
16
35
  */
17
36
  export function createBridgeController(ctx) {
18
37
  const { live, state, filter, watchdog, sink, tuning, run, runs, restart, isCancelled } = ctx;
38
+ const { sleep = abortableSleep, cancelSignal = null, onStageChange = null } = ctx;
19
39
  let retryCount = 0;
40
+ let throttleCount = 0;
41
+ let authCount = 0;
20
42
 
21
43
  const finishToolBatch = () => {
22
44
  if (state.closed || state.pending.length === 0) return;
@@ -35,37 +57,91 @@ export function createBridgeController(ctx) {
35
57
  sink.finish('tool_calls');
36
58
  };
37
59
 
60
+ // Exponential backoff with +-20% jitter for in-place retries.
61
+ const backoffMs = (attempt) => {
62
+ const ceiling = Math.min(tuning.retryMaxDelayMs, tuning.retryBaseDelayMs * 2 ** (attempt - 1));
63
+ return Math.floor(ceiling * (0.8 + Math.random() * 0.4));
64
+ };
65
+
38
66
  const recoverOrFail = (error) => {
39
67
  const retryable = isRetryableCursorStreamError(error);
68
+ // A throttle retries within the bounded throttle retry count; otherwise
69
+ // the error surfaces with retryAfterMs for the outer layer.
70
+ const throttled = !retryable && isCursorThrottleError(error);
71
+ // Rejected credentials get a forced token refresh before the retry.
72
+ const authFailure = !retryable && !throttled && isCursorAuthError(error);
73
+ // A throttle retry rotates the conversation and drops the checkpoint
74
+ // so unlike other recoveries it can only replay a run that
75
+ // has shown nothing; the rest may also continue from a checkpoint.
40
76
  const recoveryEligible =
41
- !isCancelled() && typeof restart === 'function' && (!state.visibleOutput || run.conversation.checkpoint);
42
- const canRetry = retryCount < tuning.maxRetries && recoveryEligible && retryable;
77
+ !isCancelled() &&
78
+ typeof restart === 'function' &&
79
+ (!state.visibleOutput || (!throttled && Boolean(run.conversation.checkpoint)));
80
+ const serverDelay = retryAfterMsFromError(error);
81
+ // Server advice is honored in full for every retryable kind; local
82
+ // backoff applies only when the server gave none.
83
+ const delayMs = Number.isFinite(serverDelay)
84
+ ? serverDelay
85
+ : backoffMs(throttled ? throttleCount + 1 : retryCount + 1);
86
+ const throttleOk = throttled && throttleCount < tuning.maxThrottleRetries;
87
+ const authOk = authFailure && authCount < tuning.maxAuthRetries;
88
+ const canRetry =
89
+ retryCount < tuning.maxRetries && recoveryEligible && (retryable || throttleOk || authOk);
43
90
  if (!canRetry) {
44
91
  // A fresh Cursor run owns its bounded in-place retries. Mark
45
92
  // exhaustion so the outer loop does not multiply that budget.
46
93
  // Resumed tool-result streams have no restart closure; those
47
94
  // intentionally fall through unmarked so the outer loop can
48
95
  // rebuild from the committed assistant/tool-result history.
49
- if (retryable && recoveryEligible && retryCount >= tuning.maxRetries) {
96
+ // A throttle that spent its bounded retries is final for the turn too.
97
+ if (
98
+ recoveryEligible &&
99
+ ((retryable && retryCount >= tuning.maxRetries) ||
100
+ (throttled && throttleCount >= tuning.maxThrottleRetries))
101
+ ) {
50
102
  markProviderRecoveryExhausted(error, { owner: 'cursor-wire', attempts: retryCount + 1 });
51
103
  }
52
- sink.fail(error);
104
+ sink.fail(withCursorFailureHint(error));
53
105
  return;
54
106
  }
55
107
  retryCount += 1;
108
+ if (throttled) throttleCount += 1;
109
+ if (authFailure) authCount += 1;
56
110
  state.sawEnd = false;
57
111
  state.batchBoundaryReady = false;
58
112
  state.batchBoundaryChunkSeq = -1;
59
- try {
60
- const next = restart({
61
- attempt: retryCount,
62
- fromCheckpoint: Boolean(run.conversation.checkpoint),
63
- visibleOutput: state.visibleOutput,
64
- });
65
- attach(next.bridge, next.heartbeat);
66
- } catch (restartError) {
67
- sink.fail(restartError);
68
- }
113
+ // The wait is intentional, not stream silence; attach() re-arms on restart.
114
+ watchdog.pause();
115
+ emitProviderRetryStage(onStageChange, {
116
+ attempt: retryCount + 1,
117
+ maxAttempts: tuning.maxRetries + 1,
118
+ lastErr: error,
119
+ delayMs,
120
+ });
121
+ void sleep(delayMs, cancelSignal).then(async () => {
122
+ if (isCancelled() || state.closed) return;
123
+ try {
124
+ // restart() may refresh credentials first, so it can be async.
125
+ const next = await restart({
126
+ attempt: retryCount,
127
+ fromCheckpoint: !throttled && Boolean(run.conversation.checkpoint),
128
+ visibleOutput: state.visibleOutput,
129
+ reason: throttled ? 'throttle' : authFailure ? 'auth' : 'transient',
130
+ error,
131
+ });
132
+ if (isCancelled() || state.closed) {
133
+ clearInterval(next.heartbeat);
134
+ next.bridge.close();
135
+ return;
136
+ }
137
+ attach(next.bridge, next.heartbeat);
138
+ try {
139
+ onStageChange?.('streaming');
140
+ } catch {}
141
+ } catch (restartError) {
142
+ sink.fail(withCursorFailureHint(restartError));
143
+ }
144
+ });
69
145
  };
70
146
 
71
147
  /** The Connect end frame: an error closes, pending tools delimit, a finished turn completes. */
@@ -99,20 +175,23 @@ export function createBridgeController(ctx) {
99
175
  closeError = frameError;
100
176
  }
101
177
  }
102
- // Cursor commonly closes HTTP/2 immediately after turnEnded without
103
- // a separate Connect end frame. The turn is already complete.
104
- if (state.sawTurnEnded) {
105
- sink.finish();
106
- return;
107
- }
108
178
  // Tool calls already emitted to the caller remain actionable even if
109
179
  // the parked transport vanished. The next request rebuilds/resumes.
180
+ // Checked before turnEnded: a close that follows turnEnded only ends a
181
+ // completed turn when no exec is still waiting (turn ended
182
+ // with no MCP exec outstanding), never by dropping a tool call.
110
183
  if (state.pending.length > 0) {
111
184
  sink.finish('tool_calls');
112
185
  return;
113
186
  }
187
+ // Cursor commonly closes HTTP/2 immediately after turnEnded, with or
188
+ // without an end-stream error. The turn is already complete.
189
+ if (state.sawTurnEnded) {
190
+ sink.finish();
191
+ return;
192
+ }
114
193
  if (!closeError && !state.sawEnd) {
115
- closeError = cursorError('Cursor stream closed before its end frame', { code: 'protocol_error' });
194
+ closeError = cursorError('Cursor stream closed before its end frame', { code: 'incomplete_stream' });
116
195
  }
117
196
  if (closeError) recoverOrFail(closeError);
118
197
  else sink.finish();
@@ -128,6 +207,7 @@ export function createBridgeController(ctx) {
128
207
  conversation: run.conversation,
129
208
  tools: run.tools,
130
209
  cloudRule: run.cloudRule,
210
+ sessionId: run.sessionId,
131
211
  };
132
212
  const frameParser = createFrameParser(
133
213
  // Server requests answer on whichever bridge is live when they arrive.
@@ -3,12 +3,46 @@
3
3
  * to the shared stream state and relayed to the SSE sink. Returns the
4
4
  * message's forward-progress kind for the watchdog.
5
5
  */
6
+ import { appendAgentTrace } from '../agent-trace.mjs';
6
7
  import { decodeMessage } from './cursor-wire-protobuf.mjs';
7
8
  import { MAX_CHECKPOINT_BYTES, cursorInteractionProgress } from './cursor-wire-guards.mjs';
8
9
  import { buildCursorInteractionResponse } from './cursor-wire-interactions.mjs';
9
10
  import { cursorError } from './cursor-wire-transport.mjs';
10
11
  import { handleExecMessage, handleKvMessage, sendClientMessage } from './cursor-wire-exec.mjs';
11
12
 
13
+ // Unknown fields seen on every live frame: AgentServerMessage field 8 (timing
14
+ // doubles) and InteractionUpdate field 25 (epoch-ms timestamp). Never traced.
15
+ const NOISE_SERVER_FIELDS = new Set([8]);
16
+ const NOISE_UPDATE_FIELDS = new Set([25]);
17
+ const TRACE_MAX_FIELDS = 8;
18
+ const TRACE_PREVIEW_BYTES = 48;
19
+
20
+ // Records undecoded fields (number, wire type, bounded hex of the raw field)
21
+ // so a new server event such as a mid-turn model fallback can be inspected.
22
+ function traceUnknownFields(trace, sessionId, scope, unknown, noise) {
23
+ const fields = unknown?.filter((field) => !noise.has(field.no));
24
+ if (!fields?.length) return;
25
+ try {
26
+ trace({
27
+ sessionId,
28
+ kind: 'cursor_unknown_field',
29
+ provider: 'cursor',
30
+ payload: {
31
+ scope,
32
+ count: fields.length,
33
+ fields: fields.slice(0, TRACE_MAX_FIELDS).map((field) => ({
34
+ no: field.no,
35
+ wire_type: field.wireType,
36
+ bytes: field.data.byteLength,
37
+ preview_hex: Buffer.from(field.data.subarray(0, TRACE_PREVIEW_BYTES)).toString('hex'),
38
+ })),
39
+ },
40
+ });
41
+ } catch {
42
+ /* best-effort */
43
+ }
44
+ }
45
+
12
46
  // Relays one interaction update to the SSE stream and returns its progress.
13
47
  function applyInteractionUpdate(update, state, filter, emit) {
14
48
  if (update.textDelta?.text) {
@@ -62,11 +96,18 @@ function announcePendingToolCall(state, pending, emit) {
62
96
 
63
97
  /**
64
98
  * @param {Uint8Array} bytes one Connect frame payload
65
- * @param {object} ctx { state, filter, emit, conversation, tools, cloudRule, bridge }
99
+ * @param {object} ctx { state, filter, emit, conversation, tools, cloudRule, bridge, sessionId?, trace? }
66
100
  * @returns {'work'|'park'|'none'|string} progress kind for the watchdog
67
101
  */
68
- export function processServerMessage(bytes, { state, filter, emit, conversation, tools, cloudRule, bridge }) {
102
+ export function processServerMessage(
103
+ bytes,
104
+ { state, filter, emit, conversation, tools, cloudRule, bridge, sessionId = '', trace = appendAgentTrace }
105
+ ) {
69
106
  const message = decodeMessage('AgentServerMessage', bytes);
107
+ traceUnknownFields(trace, sessionId, 'agent_server_message', message.$unknown, NOISE_SERVER_FIELDS);
108
+ if (message.interactionUpdate) {
109
+ traceUnknownFields(trace, sessionId, 'interaction_update', message.interactionUpdate.$unknown, NOISE_UPDATE_FIELDS);
110
+ }
70
111
  if (message.interactionUpdate) return applyInteractionUpdate(message.interactionUpdate, state, filter, emit);
71
112
  if (message.kvServerMessage) {
72
113
  handleKvMessage(bridge, message.kvServerMessage, conversation);