mixdog 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. package/README.md +7 -5
  2. package/package.json +1 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/src/agents/reviewer/AGENT.md +3 -3
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +5 -0
  8. package/src/defaults/skills/pptx/SKILL.md +6 -6
  9. package/src/defaults/skills/pptx/references/review.md +6 -4
  10. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  11. package/src/defaults/skills/setup/references/actions.md +13 -3
  12. package/src/defaults/skills/visualize/SKILL.md +12 -9
  13. package/src/repl/slash-commands.mjs +16 -1
  14. package/src/rules/shared/60-verification.md +3 -1
  15. package/src/rules/shared/70-delivery.md +4 -2
  16. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  17. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  18. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  19. package/src/runtime/agent/orchestrator/config.mjs +10 -4
  20. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  21. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  22. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  23. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +122 -15
  24. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
  25. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
  26. package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
  27. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  28. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  29. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  30. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  31. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  32. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  33. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  36. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  37. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
  38. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  40. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
  41. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  42. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
  45. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  46. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  49. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  50. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  51. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
  53. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  54. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  55. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
  57. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  58. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  59. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
  60. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
  61. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
  62. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
  63. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  64. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  65. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  66. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +447 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
  68. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  70. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
  71. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
  73. package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
  74. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  75. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
  76. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
  78. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
  79. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  80. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  81. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  82. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  83. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
  84. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
  85. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
  86. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
  87. package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
  88. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  89. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
  90. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  91. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  92. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  93. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  94. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
  95. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
  96. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  97. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
  98. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
  109. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  110. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  111. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
  113. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
  115. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
  116. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
  117. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  119. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
  120. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  125. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
  127. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  128. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
  129. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
  130. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  132. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  133. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  135. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
  136. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
  137. package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
  138. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  139. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
  140. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  141. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  142. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  143. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  146. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  147. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  148. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  149. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  150. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
  151. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  152. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  154. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  155. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -0
  156. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  157. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  158. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
  159. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  160. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  161. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  162. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  163. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  164. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  165. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  166. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  167. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
  169. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
  170. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
  171. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
  172. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  173. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
  174. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
  175. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
  176. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
  177. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
  178. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
  179. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  182. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  183. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
  184. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
  185. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  186. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  187. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  188. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  189. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  190. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
  191. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
  192. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -12
  193. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  194. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
  195. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  196. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  197. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +41 -3
  198. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
  199. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  200. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  201. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  202. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  203. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  205. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  206. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  208. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
  209. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
  210. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  211. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  212. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  213. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  214. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  215. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  216. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  217. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  218. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  219. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  220. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  221. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  222. package/src/runtime/attachments/store.d.mts +3 -0
  223. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  224. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  225. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  226. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  227. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  228. package/src/runtime/memory/lib/memory.mjs +1 -1
  229. package/src/runtime/memory/lib/pg/process.mjs +34 -24
  230. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
  231. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  232. package/src/runtime/office/README.md +1 -1
  233. package/src/runtime/office/shared/values.mjs +0 -5
  234. package/src/runtime/shared/atomic-file.mjs +25 -4
  235. package/src/runtime/shared/background-tasks.mjs +92 -14
  236. package/src/runtime/shared/err-text.mjs +25 -0
  237. package/src/runtime/shared/error-presentation.mjs +159 -19
  238. package/src/runtime/shared/extension-scopes.mjs +5 -3
  239. package/src/runtime/shared/file-lock.mjs +37 -11
  240. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  241. package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
  242. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  243. package/src/runtime/shared/llm/model-catalog.mjs +53 -52
  244. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  245. package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
  246. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  247. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  248. package/src/runtime/shared/model-display.mjs +13 -0
  249. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  250. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  251. package/src/runtime/shared/pristine-execution.mjs +28 -4
  252. package/src/runtime/shared/provider-accounts.mjs +124 -20
  253. package/src/runtime/shared/provider-api-key.mjs +12 -0
  254. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  255. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  256. package/src/runtime/shared/shell-display.mjs +9 -3
  257. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  258. package/src/runtime/shared/skill-mentions.mjs +133 -0
  259. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  260. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  261. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  262. package/src/runtime/shared/tool-primitives.mjs +35 -2
  263. package/src/runtime/shared/turn-changes.mjs +447 -219
  264. package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
  265. package/src/runtime/shared/turn-snapshot.mjs +170 -48
  266. package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
  267. package/src/runtime/shared/worker-requests.mjs +15 -2
  268. package/src/runtime/shared/worktrees/contract.mjs +0 -24
  269. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  270. package/src/session-runtime/automation-agents.mjs +8 -2
  271. package/src/session-runtime/boot/apis.mjs +61 -0
  272. package/src/session-runtime/boot/config.mjs +5 -2
  273. package/src/session-runtime/boot/facade.mjs +1 -0
  274. package/src/session-runtime/boot/infrastructure.mjs +35 -18
  275. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  276. package/src/session-runtime/conversation-import/service.mjs +369 -0
  277. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
  278. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  279. package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
  280. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  281. package/src/session-runtime/mcp-glue/connect.mjs +83 -16
  282. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  283. package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
  284. package/src/session-runtime/mcp-glue.mjs +25 -2
  285. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  286. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  287. package/src/session-runtime/model-route/set-route.mjs +83 -15
  288. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  289. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  290. package/src/session-runtime/new-session-config.mjs +3 -2
  291. package/src/session-runtime/notification-bus.mjs +9 -2
  292. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  293. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  294. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  295. package/src/session-runtime/provider-readiness.mjs +16 -10
  296. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  297. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  298. package/src/session-runtime/resource-extension-sync.mjs +24 -5
  299. package/src/session-runtime/resource-mcp-api.mjs +198 -78
  300. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  301. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  302. package/src/session-runtime/resource-skills-api.mjs +23 -2
  303. package/src/session-runtime/runtime-review-api.mjs +13 -0
  304. package/src/session-runtime/schedule-session-run.mjs +1 -1
  305. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  306. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  307. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  308. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  310. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  311. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  312. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  313. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  314. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  315. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  316. package/src/session-runtime/services/provider-admin.mjs +64 -18
  317. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  318. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  319. package/src/session-runtime/setup-tool/executor.mjs +12 -1
  320. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  321. package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
  322. package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
  323. package/src/session-runtime/skills-api.mjs +7 -3
  324. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  325. package/src/session-runtime/webhook-session-run.mjs +16 -4
  326. package/src/skill-script-entry.mjs +36 -0
  327. package/src/standalone/daemon-drain-policy.mjs +8 -0
  328. package/src/standalone/daemon-stored-session-views.mjs +11 -3
  329. package/src/standalone/daemon-telemetry.mjs +55 -0
  330. package/src/standalone/daemon.mjs +44 -11
  331. package/src/standalone/session-protocol.mjs +12 -0
  332. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  333. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  334. package/src/standalone/session-service/agent-tree.mjs +12 -0
  335. package/src/standalone/session-service/quota-resume.mjs +8 -0
  336. package/src/standalone/session-service/retention/busy.mjs +15 -1
  337. package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
  338. package/src/standalone/session-service-api.mjs +5 -0
  339. package/src/standalone/session-service.mjs +1 -0
  340. package/src/tui/App.jsx +9 -1
  341. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  342. package/src/tui/app/app-view.jsx +7 -0
  343. package/src/tui/app/create-app-pickers.mjs +4 -0
  344. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  345. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  346. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  347. package/src/tui/app/extension-pickers.mjs +2 -1
  348. package/src/tui/app/model-options.mjs +13 -16
  349. package/src/tui/app/skill-mention-palette.mjs +88 -0
  350. package/src/tui/app/slash-commands.mjs +7 -2
  351. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  352. package/src/tui/app/slash-palette.mjs +23 -7
  353. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  354. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  355. package/src/tui/app/use-prompt-state.mjs +5 -0
  356. package/src/tui/components/Message.jsx +22 -1
  357. package/src/tui/components/PromptInput.jsx +21 -3
  358. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  359. package/src/tui/components/StatusLine.jsx +2 -0
  360. package/src/tui/components/TranscriptItem.jsx +9 -1
  361. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  362. package/src/tui/components/status-line/local-line.mjs +5 -2
  363. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  364. package/src/tui/components/user-message-skills.mjs +38 -0
  365. package/src/tui/dist/index.mjs +1323 -214
  366. package/src/tui/session/queue-helpers.mjs +7 -2
  367. package/src/tui/session/quota-resume.mjs +46 -14
  368. package/src/tui/session/session-api/commands.mjs +33 -24
  369. package/src/tui/session/session-api/extensions.mjs +26 -2
  370. package/src/tui/session/session-api/integrations.mjs +7 -0
  371. package/src/tui/session/session-api/lifecycle.mjs +5 -1
  372. package/src/tui/session/session-api/routes.mjs +71 -7
  373. package/src/tui/session/session-api/settings.mjs +12 -14
  374. package/src/tui/session/session-flow/drain.mjs +21 -5
  375. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  376. package/src/tui/session/tui-steering-persist.mjs +25 -9
  377. package/src/tui/session/turn.mjs +55 -15
  378. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  379. package/src/ui/statusline-agents.mjs +15 -4
  380. package/src/ui/statusline.mjs +7 -3
  381. package/src/ui/streaming-markdown-heal.mjs +34 -3
  382. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  383. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  384. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
  385. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  386. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -0,0 +1,447 @@
1
+ /**
2
+ * Command Code provider (commandcode.ai Provider API).
3
+ *
4
+ * Credentials are a Studio-issued API key (command-code-oauth-tokens.mjs).
5
+ * Each model is served on the endpoint its /models row declares: Chat
6
+ * Completions through the OpenAI-compatible engine, Messages-only models
7
+ * (Claude) through the Anthropic engine. Both clients are pinned to the
8
+ * Command Code API host. The key never expires and has no refresh grant: a
9
+ * 401 means the user must sign in again.
10
+ */
11
+ import { noteErrorUsage } from './lib/note-error-usage.mjs';
12
+ import { fileMtimeMs, normalizeExpiresAtMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
13
+ import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
14
+ import { COMPAT_RETRY_POLICY } from './openai-compat-retry-profile.mjs';
15
+ import { listCompatModels } from './openai-compat-models.mjs';
16
+ import { AnthropicProvider } from './anthropic.mjs';
17
+ import { toInclusiveOpenCodeGoUsage as toInclusiveUsage } from './opencode-go.mjs';
18
+ import { sanitizeToolPairs } from '../session/context-utils.mjs';
19
+ import { preparePdfTextForProvider } from './media-normalization.mjs';
20
+ import { currentUsageContext } from '../../../shared/llm/usage-context.mjs';
21
+ import { makeModelCache } from './model-cache.mjs';
22
+ import {
23
+ API_ORIGIN,
24
+ CREDITS_URL,
25
+ PROVIDER_BASE_URL,
26
+ PROVIDER_ID,
27
+ commandCodeReauthError,
28
+ getOwnTokenPath,
29
+ loadTokens,
30
+ requestCommandCodeJson,
31
+ } from './command-code-oauth-tokens.mjs';
32
+
33
+ export {
34
+ hasCommandCodeOAuthCredentials,
35
+ describeCommandCodeOAuthCredentials,
36
+ forgetCommandCodeOAuthCredentials,
37
+ } from './command-code-oauth-tokens.mjs';
38
+ export { beginOAuthLogin, loginOAuth } from './command-code-oauth-login.mjs';
39
+
40
+ export const DEFAULT_MODEL = 'deepseek/deepseek-v4-flash';
41
+ const CHAT_ENDPOINT = '/chat/completions';
42
+ const MESSAGES_ENDPOINT = '/messages';
43
+ // The Anthropic SDK appends /v1/messages itself.
44
+ const MESSAGES_BASE_URL = `${API_ORIGIN}/provider`;
45
+ const FIVE_HOURS_MS = 5 * 60 * 60 * 1000;
46
+ const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
47
+
48
+ const isUnauthorized = (err) => Number(err?.httpStatus || err?.status) === 401;
49
+
50
+ // The discovered roster (what /models returned) is persisted so the shared
51
+ // sync lookup sees what listModels found.
52
+ const commandCodeModelCache = makeModelCache({
53
+ fileName: 'command-code-oauth-models.json',
54
+ ttlMs: 24 * 60 * 60 * 1000,
55
+ version: 1,
56
+ });
57
+
58
+ /**
59
+ * The wire a /models row is served on: 'chat' when it declares Chat
60
+ * Completions or declares no endpoints at all, 'messages' when it declares
61
+ * only Messages, null when it declares neither.
62
+ */
63
+ export function modelWire(item) {
64
+ if (typeof item?.id !== 'string' || !item.id) return null;
65
+ const endpoints = item.supported_endpoints;
66
+ if (!Array.isArray(endpoints)) return 'chat';
67
+ const declared = endpoints.map((endpoint) => String(endpoint).replace(/^\/?(?:provider\/)?(?:v1\/)?/, '/'));
68
+ if (declared.includes(CHAT_ENDPOINT)) return 'chat';
69
+ if (declared.includes(MESSAGES_ENDPOINT)) return 'messages';
70
+ return null;
71
+ }
72
+
73
+ // Every request either engine makes goes through this fetch: anything outside
74
+ // the Command Code API origin is refused before the key leaves the process,
75
+ // and redirects are never followed (any 3xx is an error).
76
+ function apiOnlyFetch(fetchImpl) {
77
+ return async (input, init) => {
78
+ const url = new URL(String(input?.url ?? input));
79
+ if (url.origin !== API_ORIGIN) {
80
+ throw new Error(`[command-code-oauth] refusing to send the API key to ${url.host}`);
81
+ }
82
+ const res = await (fetchImpl || globalThis.fetch)(input, { ...init, redirect: 'manual' });
83
+ if (res.status >= 300 && res.status < 400) {
84
+ throw new Error(`[command-code-oauth] refusing to follow a redirect from ${url.host}`);
85
+ }
86
+ return res;
87
+ };
88
+ }
89
+
90
+ // Anthropic engine bound to this credential: only the Command Code key is
91
+ // sent (never an ANTHROPIC_AUTH_TOKEN from the environment) and only to the
92
+ // Command Code API host.
93
+ class CommandCodeMessagesEngine extends AnthropicProvider {
94
+ nativePdf = false;
95
+ _createClient(apiKey) {
96
+ const client = super._createClient(apiKey);
97
+ client.authToken = null;
98
+ client.fetch = apiOnlyFetch(this.config.fetchImpl);
99
+ return client;
100
+ }
101
+ }
102
+
103
+ const normalizeUsageError = (err) => {
104
+ if (err && typeof err === 'object') {
105
+ if (err.usage) err.usage = toInclusiveUsage(err.usage);
106
+ if (err.partialUsage) err.partialUsage = toInclusiveUsage(err.partialUsage);
107
+ }
108
+ return err;
109
+ };
110
+
111
+ function rosterRow(item) {
112
+ const contextWindow = Number(item.context_window ?? item.max_context_window ?? item.context_length ?? 0);
113
+ const outputTokens = Number(item.max_output_tokens ?? item.output_tokens ?? 0);
114
+ const name = [item.display_name, item.name].find((v) => typeof v === 'string' && v.trim());
115
+ return {
116
+ id: item.id,
117
+ provider: PROVIDER_ID,
118
+ ...(name ? { name: name.trim() } : {}),
119
+ ...(contextWindow > 0 ? { contextWindow } : {}),
120
+ ...(outputTokens > 0 ? { outputTokens } : {}),
121
+ };
122
+ }
123
+
124
+ export function chooseDefaultModel(ids) {
125
+ const list = ids || [];
126
+ return list.includes(DEFAULT_MODEL) ? DEFAULT_MODEL : list[0] || DEFAULT_MODEL;
127
+ }
128
+
129
+ // Strip the key from an error (message plus any echoed gateway body).
130
+ function scrubErrorSecrets(err, apiKey) {
131
+ if (!err || typeof err !== 'object' || !apiKey) return err;
132
+ const seen = new Set();
133
+ const walk = (obj, depth) => {
134
+ if (!obj || typeof obj !== 'object' || seen.has(obj) || depth > 4) return;
135
+ seen.add(obj);
136
+ for (const k of Object.keys(obj)) {
137
+ const v = obj[k];
138
+ if (typeof v === 'string') {
139
+ if (v.includes(apiKey)) obj[k] = scrubOAuthSecrets(v, [apiKey]);
140
+ } else walk(v, depth + 1);
141
+ }
142
+ };
143
+ if (typeof err.message === 'string') err.message = scrubOAuthSecrets(err.message, [apiKey]);
144
+ walk(err.error, 0);
145
+ walk(err.body, 0);
146
+ return err;
147
+ }
148
+
149
+ // Redact where the transport first surfaces errors (request open and stream
150
+ // iteration), so the shared retry reporter never logs the key.
151
+ function redactClientErrors(client, apiKey) {
152
+ const completions = client?.chat?.completions;
153
+ if (!completions || !apiKey || completions.create?.commandCodeRedacting === apiKey) return;
154
+ const create = completions.create.bind(completions);
155
+ const wrapped = async (...args) => {
156
+ try {
157
+ const stream = await create(...args);
158
+ if (stream && typeof stream[Symbol.asyncIterator] === 'function') {
159
+ const iterate = stream[Symbol.asyncIterator].bind(stream);
160
+ stream[Symbol.asyncIterator] = () => {
161
+ const it = iterate();
162
+ return {
163
+ next: async (...a) => {
164
+ try {
165
+ return await it.next(...a);
166
+ } catch (err) {
167
+ throw scrubErrorSecrets(err, apiKey);
168
+ }
169
+ },
170
+ return: (...a) => it.return?.(...a),
171
+ };
172
+ };
173
+ }
174
+ return stream;
175
+ } catch (err) {
176
+ throw scrubErrorSecrets(err, apiKey);
177
+ }
178
+ };
179
+ wrapped.commandCodeRedacting = apiKey;
180
+ completions.create = wrapped;
181
+ }
182
+
183
+ // Same for the Anthropic client's streaming request (read through
184
+ // `.asResponse()`); every other failure is scrubbed by _shapeError.
185
+ function redactMessagesErrors(client, apiKey) {
186
+ const messages = client?.messages;
187
+ if (!messages || !apiKey || messages.create?.commandCodeRedacting === apiKey) return;
188
+ const create = messages.create.bind(messages);
189
+ const wrapped = (...args) => {
190
+ const request = create(...args);
191
+ const asResponse = request?.asResponse?.bind(request);
192
+ if (asResponse) {
193
+ request.asResponse = () =>
194
+ asResponse().catch((err) => {
195
+ throw scrubErrorSecrets(err, apiKey);
196
+ });
197
+ }
198
+ return request;
199
+ };
200
+ wrapped.commandCodeRedacting = apiKey;
201
+ messages.create = wrapped;
202
+ }
203
+
204
+ function usageStatus(usedFraction) {
205
+ if (usedFraction >= 1) return 'exhausted';
206
+ if (usedFraction >= 0.9) return 'warning';
207
+ return 'ok';
208
+ }
209
+
210
+ function creditWindow({ id, label, longLabel, durationMs }, entry) {
211
+ const cap = Number(entry?.cap);
212
+ const used = Number(entry?.used);
213
+ if (!entry || !Number.isFinite(cap) || cap <= 0 || !Number.isFinite(used) || used < 0) return null;
214
+ const usedPct = Math.min(100, (used / cap) * 100);
215
+ const usedFraction = usedPct / 100;
216
+ const resetAt = normalizeExpiresAtMs(entry.resetAt);
217
+ return {
218
+ id,
219
+ label,
220
+ longLabel,
221
+ source: 'command-code-credits',
222
+ usedPct: Math.round(usedPct * 100) / 100,
223
+ usedFraction,
224
+ remainingFraction: 1 - usedFraction,
225
+ status: usageStatus(usedFraction),
226
+ durationMs,
227
+ ...(resetAt ? { resetAt } : {}),
228
+ };
229
+ }
230
+
231
+ /** Map `/alpha/billing/credits` windowLimits onto the shared quota-window shape. */
232
+ export function quotaWindowsFromCredits(body) {
233
+ const limits = (body?.data ?? body)?.windowLimits;
234
+ return [
235
+ creditWindow({ id: '5h', label: '5H', longLabel: '5 Hours', durationMs: FIVE_HOURS_MS }, limits?.fiveHour),
236
+ creditWindow({ id: '1w', label: '7D', longLabel: 'Weekly', durationMs: WEEK_MS }, limits?.weekly),
237
+ ].filter(Boolean);
238
+ }
239
+
240
+ export class CommandCodeOAuthProvider {
241
+ // No document block on this wire: a PDF is lowered to text before the request is built.
242
+ nativePdf = false;
243
+ // OpenAI-compatible usage: prompt_tokens includes cached. See registry.mjs.
244
+ static inputExcludesCache = false;
245
+ name = PROVIDER_ID;
246
+ // The retry profile's limits are final: no loop-level recovery cycles on top.
247
+ retryPolicy = COMPAT_RETRY_POLICY;
248
+ config;
249
+ tokens = null;
250
+ _inner = null;
251
+ _innerKey = null;
252
+ _defaultModel = null;
253
+ _defaultModelKey = null;
254
+ _messages = null;
255
+ _messagesKey = null;
256
+ // Model id → wire ('chat' | 'messages') from the roster fetched with `_wiresKey`.
257
+ _wires = new Map();
258
+ _wiresKey = null;
259
+
260
+ constructor(config) {
261
+ this.config = config || {};
262
+ this.tokens = loadTokens();
263
+ try {
264
+ preloadOpenAICompatRuntime();
265
+ } catch {
266
+ /* authoritative send surfaces load failures */
267
+ }
268
+ }
269
+
270
+ ensureAuth() {
271
+ // Re-read when the token file changed (sign-in from another process).
272
+ const mtime = fileMtimeMs(getOwnTokenPath());
273
+ if (!mtime) {
274
+ // File gone (signed out / forgotten): drop every cached credential.
275
+ this.tokens = null;
276
+ this._inner = null;
277
+ this._innerKey = null;
278
+ this._messages = null;
279
+ this._messagesKey = null;
280
+ }
281
+ if (!this.tokens || mtime > (this.tokens.mtimeMs || 0)) this.tokens = loadTokens() || this.tokens;
282
+ if (!this.tokens?.apiKey) {
283
+ throw new Error('[command-code-oauth] credentials not found — open /providers in mixdog to sign in first');
284
+ }
285
+ return this.tokens;
286
+ }
287
+
288
+ _ensureInner() {
289
+ const { apiKey } = this.ensureAuth();
290
+ if (this._inner && this._innerKey === apiKey) return this._inner;
291
+ const { fetchImpl, baseURL: _ignored, ...config } = this.config;
292
+ const inner = new OpenAICompatProvider(PROVIDER_ID, {
293
+ ...config,
294
+ apiKey,
295
+ // Fixed: the key is only ever sent to the Command Code API host.
296
+ baseURL: PROVIDER_BASE_URL,
297
+ // Explicit nulls keep OPENAI_ORG_ID / OPENAI_PROJECT_ID out of the requests.
298
+ clientOptions: { organization: null, project: null, fetch: apiOnlyFetch(fetchImpl) },
299
+ extraHeaders: { Accept: 'application/json' },
300
+ });
301
+ inner._fetchModelItems = ({ signal } = {}) => this._fetchRoster(apiKey, signal);
302
+ redactClientErrors(inner.client, apiKey);
303
+ this._inner = inner;
304
+ this._innerKey = apiKey;
305
+ return inner;
306
+ }
307
+
308
+ // The Anthropic engine for Messages-only models, on the same key and host.
309
+ _ensureMessages(apiKey) {
310
+ if (this._messages && this._messagesKey === apiKey) return this._messages;
311
+ const { baseURL: _ignored, ...config } = this.config;
312
+ const engine = new CommandCodeMessagesEngine({
313
+ ...config,
314
+ name: PROVIDER_ID,
315
+ apiKey,
316
+ baseURL: MESSAGES_BASE_URL,
317
+ disableBetaHeaders: true,
318
+ // x-api-key from the SDK plus the bearer form of the same key.
319
+ extraHeaders: { Authorization: `Bearer ${apiKey}` },
320
+ });
321
+ redactMessagesErrors(engine.client, apiKey);
322
+ this._messages = engine;
323
+ this._messagesKey = apiKey;
324
+ return engine;
325
+ }
326
+
327
+ // Servable rows from /models; only the latest fetch with the still-active
328
+ // key publishes the roster and the per-model wire map.
329
+ async _fetchRoster(apiKey, signal = null) {
330
+ const publish = commandCodeModelCache.beginSave(() => loadTokens()?.apiKey === apiKey);
331
+ const body = await requestCommandCodeJson(`${PROVIDER_BASE_URL}/models`, apiKey, {
332
+ fetchImpl: this.config.fetchImpl,
333
+ signal,
334
+ });
335
+ let list = [];
336
+ if (Array.isArray(body?.data)) list = body.data;
337
+ else if (Array.isArray(body)) list = body;
338
+ const items = list.filter((item) => modelWire(item));
339
+ publish(items.map(rosterRow));
340
+ if (this.tokens?.apiKey === apiKey) {
341
+ this._wires = new Map(items.map((item) => [item.id, modelWire(item)]));
342
+ this._wiresKey = apiKey;
343
+ }
344
+ return items;
345
+ }
346
+
347
+ // The wire for a model, from this credential's roster (fetched once when
348
+ // missing). A model the roster does not know, or a roster that cannot be
349
+ // read right now, stays on Chat Completions; a rejected key is surfaced.
350
+ async _wireFor(inner, model) {
351
+ if (this._wiresKey !== inner.apiKey) {
352
+ try {
353
+ await listCompatModels(inner, { throwOnError: true });
354
+ } catch (err) {
355
+ if (isUnauthorized(err)) throw err;
356
+ }
357
+ }
358
+ return (this._wiresKey === inner.apiKey && this._wires.get(model)) || 'chat';
359
+ }
360
+
361
+ // A rejected key needs a fresh sign-in; the gateway's own detail stays attached.
362
+ _shapeError(err, apiKey) {
363
+ noteErrorUsage(err);
364
+ scrubErrorSecrets(err, apiKey);
365
+ if (isUnauthorized(err) && !err?.reauthRequired) return commandCodeReauthError(String(err?.message || ''));
366
+ return err;
367
+ }
368
+
369
+ async _pickDefaultModel(inner) {
370
+ if (this._defaultModel && this._defaultModelKey === this._innerKey) return this._defaultModel;
371
+ const rows = await listCompatModels(inner, { throwOnError: true });
372
+ this._defaultModel = chooseDefaultModel(rows.map((row) => row.id));
373
+ this._defaultModelKey = this._innerKey;
374
+ return this._defaultModel;
375
+ }
376
+
377
+ async send(messages, model, tools, sendOpts) {
378
+ let inner;
379
+ try {
380
+ inner = this._ensureInner();
381
+ } catch (err) {
382
+ throw this._shapeError(err, this.tokens?.apiKey);
383
+ }
384
+ const apiKey = this._innerKey;
385
+ // The model's declared endpoint picks the wire; a caller cannot move a
386
+ // model to Responses.
387
+ const { compatWireApi: _wire, responsesTransport: _transport, ...opts } = sendOpts || {};
388
+ try {
389
+ const useModel = model || (await this._pickDefaultModel(inner));
390
+ if ((await this._wireFor(inner, useModel)) === 'messages') {
391
+ return await this._sendMessages(apiKey, messages, useModel, tools, opts);
392
+ }
393
+ // _doSend directly: the compat wrapper's 401 handler reloads a static
394
+ // config key, which does not apply to this credential.
395
+ return await inner._doSend(messages, useModel, tools, opts);
396
+ } catch (err) {
397
+ throw this._shapeError(err, apiKey);
398
+ }
399
+ }
400
+
401
+ // Messages wire. Same request preparation as the Anthropic engine's own
402
+ // send, but straight to _doSend: its 401 handler re-reads a static config
403
+ // key and replays, which does not apply to this credential. Anthropic usage
404
+ // excludes cached input; it is reported in this provider's inclusive form.
405
+ async _sendMessages(apiKey, messages, model, tools, opts) {
406
+ const engine = this._ensureMessages(apiKey);
407
+ const prepared = await preparePdfTextForProvider(sanitizeToolPairs(messages), engine);
408
+ const identity = currentUsageContext();
409
+ if (identity) identity.normalizeAbandonedUsage = toInclusiveUsage;
410
+ try {
411
+ const result = await engine._doSend(prepared, model, tools, opts);
412
+ return result?.usage ? { ...result, usage: toInclusiveUsage(result.usage) } : result;
413
+ } catch (err) {
414
+ throw normalizeUsageError(err);
415
+ }
416
+ }
417
+
418
+ async listModels() {
419
+ let inner;
420
+ try {
421
+ inner = this._ensureInner();
422
+ return await listCompatModels(inner, { throwOnError: true });
423
+ } catch (err) {
424
+ throw this._shapeError(err, this.tokens?.apiKey);
425
+ }
426
+ }
427
+
428
+ /** 5-hour and weekly credit windows for this account's key. */
429
+ async getUsageSnapshot() {
430
+ const { apiKey, userId } = this.ensureAuth();
431
+ const body = await requestCommandCodeJson(CREDITS_URL, apiKey, { fetchImpl: this.config.fetchImpl });
432
+ const quotaWindows = quotaWindowsFromCredits(body);
433
+ if (!quotaWindows.length) return null;
434
+ return {
435
+ provider: PROVIDER_ID,
436
+ model: null,
437
+ source: 'command-code-credits',
438
+ fetchedAt: Date.now(),
439
+ quotaWindows,
440
+ ...(userId ? { accountId: userId } : {}),
441
+ };
442
+ }
443
+
444
+ async isAvailable() {
445
+ return loadTokens() !== null;
446
+ }
447
+ }
@@ -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
@@ -197,8 +215,25 @@ export async function exchangeCursorToken(token, { fetchFn = fetch, signal } = {
197
215
  });
198
216
  if (!response.ok) {
199
217
  const detail = await response.text().catch(() => '');
200
- const error = new Error(`Cursor token exchange failed (${response.status})${detail ? `: ${detail}` : ''}`);
218
+ // Only a credential verdict is permanent; throttles, 5xx and network
219
+ // failures leave the refresh token valid and stay retryable upstream.
220
+ const reauthRequired = PERMANENT_REFRESH_STATUS.has(response.status);
221
+ const error = new Error(
222
+ `Cursor token exchange failed (${response.status})${detail ? `: ${detail}` : ''}${
223
+ reauthRequired ? `. ${REAUTH_GUIDANCE}` : ''
224
+ }`
225
+ );
201
226
  error.status = response.status;
227
+ error.httpStatus = response.status;
228
+ const code = EXCHANGE_STATUS_CODE[response.status];
229
+ if (code) error.code = code;
230
+ const headers = Object.fromEntries(response.headers?.entries?.() ?? []);
231
+ if (Object.keys(headers).length) {
232
+ error.headers = headers;
233
+ const retryAfterMs = retryAfterMsFromError(error);
234
+ if (Number.isFinite(retryAfterMs)) error.retryAfterMs = retryAfterMs;
235
+ }
236
+ if (reauthRequired) error.reauthRequired = true;
202
237
  throw error;
203
238
  }
204
239
  const data = await response.json();
@@ -277,9 +312,7 @@ export async function resolveCursorOAuthAccessToken({ forceRefresh = false, fetc
277
312
  if (!forceRefresh && !expiring) return tokens.access_token;
278
313
  if (!tokens.refresh_token) {
279
314
  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
- );
315
+ throw reauthError(`Cursor access token expired and has no refresh token. ${REAUTH_GUIDANCE}`);
283
316
  }
284
317
  const refreshPath = credentialsPath();
285
318
  let refreshInFlight = refreshesInFlight.get(refreshPath);
@@ -296,7 +329,7 @@ export async function resolveCursorOAuthAccessToken({ forceRefresh = false, fetc
296
329
  return latest;
297
330
  }
298
331
  if (!latest.refresh_token) {
299
- throw new Error('Cursor OAuth refresh token is unavailable. Open /providers in Mixdog to sign in again.');
332
+ throw reauthError(`Cursor OAuth refresh token is unavailable. ${REAUTH_GUIDANCE}`);
300
333
  }
301
334
  // Deliberately NOT the caller's signal: this exchange is shared by
302
335
  // every concurrent waiter, so letting the first caller's abort
@@ -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.01-e373342';
7
+ export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.10.14-0f9edde';
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
  }