mixdog 1.1.2 → 1.2.1

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 (424) hide show
  1. package/README.md +8 -5
  2. package/package.json +2 -1
  3. package/scripts/lib/run-node-tests.mjs +9 -0
  4. package/scripts/prune-desktop-runtime.mjs +3 -0
  5. package/scripts/test-direct.mjs +4 -2
  6. package/src/defaults/shipped-history.json +10 -0
  7. package/src/defaults/skills/pptx/SKILL.md +6 -6
  8. package/src/defaults/skills/pptx/references/review.md +6 -4
  9. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  10. package/src/defaults/skills/setup/references/actions.md +18 -5
  11. package/src/defaults/skills/visualize/SKILL.md +160 -0
  12. package/src/defaults/skills/visualize/references/craft.md +88 -0
  13. package/src/headless-command.mjs +56 -3
  14. package/src/headless-exec.mjs +17 -1
  15. package/src/help.mjs +2 -0
  16. package/src/rules/shared/60-verification.md +3 -1
  17. package/src/rules/shared/70-delivery.md +4 -2
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
  21. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  22. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
  24. package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +19 -5
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
  33. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  34. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  35. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
  36. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
  41. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  43. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  45. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  50. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
  51. package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
  52. package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
  53. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  54. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  55. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  56. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
  57. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
  58. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
  59. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
  60. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  61. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
  62. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
  64. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
  65. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
  66. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
  67. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  68. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
  78. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
  79. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
  80. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
  81. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  82. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  83. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
  85. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  86. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  87. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  88. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
  90. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
  91. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
  92. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  93. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
  94. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  95. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  96. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
  97. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
  98. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
  99. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
  100. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
  101. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  102. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  103. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  104. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  105. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  106. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  107. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  108. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  109. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
  112. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
  114. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
  115. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
  116. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
  120. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
  124. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
  125. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
  126. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
  128. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
  131. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
  133. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  136. package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
  137. package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
  138. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
  139. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  140. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
  141. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  142. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
  143. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
  144. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
  145. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
  146. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
  147. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  148. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
  149. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
  150. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  151. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
  153. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
  157. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
  158. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
  159. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  160. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  161. package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
  162. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
  163. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
  164. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  165. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
  166. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
  167. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
  168. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
  169. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
  175. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
  176. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  177. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  179. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
  180. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
  181. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  182. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
  184. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  185. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  186. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  187. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  188. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  189. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  190. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
  191. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  192. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  193. package/src/runtime/attachments/store.d.mts +3 -0
  194. package/src/runtime/attachments/store.mjs +38 -0
  195. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  196. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  197. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  199. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  200. package/src/runtime/memory/lib/memory.mjs +1 -1
  201. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  202. package/src/runtime/shared/atomic-file.mjs +33 -6
  203. package/src/runtime/shared/best-effort-stderr.mjs +8 -0
  204. package/src/runtime/shared/code-graph-state.mjs +12 -0
  205. package/src/runtime/shared/defaults-separation.mjs +2 -0
  206. package/src/runtime/shared/extension-scopes.mjs +5 -3
  207. package/src/runtime/shared/file-lock.mjs +37 -11
  208. package/src/runtime/shared/git-runner-worker.mjs +3 -2
  209. package/src/runtime/shared/git-runner.mjs +4 -3
  210. package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
  211. package/src/runtime/shared/llm/default-context-window.mjs +6 -0
  212. package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
  213. package/src/runtime/shared/llm/model-catalog.mjs +240 -130
  214. package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
  215. package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
  216. package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
  217. package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
  218. package/src/runtime/shared/model-proxy-mode.mjs +19 -0
  219. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  220. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  221. package/src/runtime/shared/pristine-execution.mjs +28 -4
  222. package/src/runtime/shared/provider-accounts.mjs +125 -21
  223. package/src/runtime/shared/provider-api-key.mjs +33 -1
  224. package/src/runtime/shared/schedule-model-ref.mjs +11 -2
  225. package/src/runtime/shared/session-usage-totals.mjs +19 -0
  226. package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
  227. package/src/runtime/shared/shell-display.mjs +9 -3
  228. package/src/runtime/shared/singleton-owner.mjs +36 -6
  229. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  230. package/src/runtime/shared/skill-mentions.mjs +133 -0
  231. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  232. package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
  233. package/src/runtime/shared/tool-primitives.mjs +35 -2
  234. package/src/runtime/shared/tool-surface.mjs +7 -0
  235. package/src/runtime/shared/tool-work-units.mjs +14 -0
  236. package/src/runtime/shared/transcript-metadata.mjs +17 -1
  237. package/src/runtime/shared/turn-changes.mjs +791 -0
  238. package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
  239. package/src/runtime/shared/turn-snapshot.mjs +690 -81
  240. package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
  241. package/src/runtime/shared/worker-requests.mjs +15 -2
  242. package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
  243. package/src/runtime/shared/worktrees/contract.mjs +551 -0
  244. package/src/runtime/shared/worktrees/disk.mjs +57 -0
  245. package/src/runtime/shared/worktrees/git.mjs +247 -0
  246. package/src/runtime/shared/worktrees/include.mjs +480 -0
  247. package/src/runtime/shared/worktrees/lock.mjs +46 -0
  248. package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
  249. package/src/runtime/shared/worktrees/manager.mjs +1215 -0
  250. package/src/runtime/shared/worktrees/merge.mjs +182 -0
  251. package/src/runtime/shared/worktrees/owner.mjs +46 -0
  252. package/src/runtime/shared/worktrees/paths.mjs +62 -0
  253. package/src/runtime/shared/worktrees/registry.mjs +110 -0
  254. package/src/runtime/shared/worktrees/settings.mjs +84 -0
  255. package/src/runtime/shared/worktrees/slug.mjs +62 -0
  256. package/src/runtime/shared/worktrees/test-support.mjs +96 -0
  257. package/src/runtime/visualize-bridge/client.mjs +181 -0
  258. package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
  259. package/src/session-runtime/automation-agents.mjs +8 -2
  260. package/src/session-runtime/boot/apis.mjs +90 -0
  261. package/src/session-runtime/boot/config.mjs +7 -2
  262. package/src/session-runtime/boot/facade.mjs +21 -0
  263. package/src/session-runtime/boot/infrastructure.mjs +30 -18
  264. package/src/session-runtime/boot/session.mjs +1 -0
  265. package/src/session-runtime/boot/tools.mjs +4 -0
  266. package/src/session-runtime/config-lifecycle.mjs +10 -1
  267. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  268. package/src/session-runtime/conversation-import/service.mjs +369 -0
  269. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
  270. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  271. package/src/session-runtime/global-extensions.mjs +2 -2
  272. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
  273. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
  274. package/src/session-runtime/internal-tool-executor.mjs +9 -1
  275. package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
  276. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
  277. package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
  278. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
  279. package/src/session-runtime/mcp-glue/connect.mjs +68 -16
  280. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  281. package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
  282. package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
  283. package/src/session-runtime/mcp-glue.mjs +22 -2
  284. package/src/session-runtime/model-route/route-persist.mjs +3 -0
  285. package/src/session-runtime/model-route/set-route.mjs +15 -2
  286. package/src/session-runtime/new-session-config.mjs +3 -2
  287. package/src/session-runtime/notification-bus.mjs +18 -3
  288. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  289. package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
  290. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  291. package/src/session-runtime/provider-readiness.mjs +16 -10
  292. package/src/session-runtime/resource-extension-sync.mjs +45 -7
  293. package/src/session-runtime/resource-mcp-api.mjs +121 -84
  294. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  295. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  296. package/src/session-runtime/resource-skills-api.mjs +23 -2
  297. package/src/session-runtime/route-resolution.mjs +10 -1
  298. package/src/session-runtime/route-state.mjs +16 -2
  299. package/src/session-runtime/runtime-core.mjs +5 -0
  300. package/src/session-runtime/runtime-facade.mjs +8 -0
  301. package/src/session-runtime/runtime-review-api.mjs +9 -0
  302. package/src/session-runtime/schedule-session-run.mjs +1 -1
  303. package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
  304. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
  305. package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
  306. package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
  307. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
  308. package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
  310. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
  311. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  312. package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
  313. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
  314. package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
  315. package/src/session-runtime/services/agent-tool.mjs +1 -0
  316. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  317. package/src/session-runtime/services/daemon-candidate.mjs +3 -2
  318. package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
  319. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  320. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  321. package/src/session-runtime/services/provider-admin.mjs +64 -18
  322. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  323. package/src/session-runtime/session-create/session-options.mjs +10 -1
  324. package/src/session-runtime/session-turn-api.mjs +1 -0
  325. package/src/session-runtime/settings-api.mjs +4 -2
  326. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
  327. package/src/session-runtime/settings-worktree-api.mjs +17 -0
  328. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  329. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  330. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
  331. package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
  332. package/src/session-runtime/skills-api.mjs +7 -3
  333. package/src/session-runtime/turn/agent-ops.mjs +12 -1
  334. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
  335. package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
  336. package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
  337. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
  338. package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
  339. package/src/session-runtime/turn/turn-run.mjs +6 -7
  340. package/src/session-runtime/webhook-session-run.mjs +16 -4
  341. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
  342. package/src/session-runtime/workspace/admission-gate.mjs +197 -0
  343. package/src/session-runtime/workspace/project-branches.mjs +136 -0
  344. package/src/session-runtime/workspace/transition-steps.mjs +364 -0
  345. package/src/session-runtime/workspace/transitions.mjs +651 -0
  346. package/src/session-runtime/workspace/workspace-state.mjs +112 -0
  347. package/src/skill-script-entry.mjs +36 -0
  348. package/src/standalone/channel-transport/http-routes.mjs +17 -0
  349. package/src/standalone/channel-transport.mjs +13 -1
  350. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  351. package/src/standalone/daemon-owner.mjs +372 -0
  352. package/src/standalone/daemon-stored-session-views.mjs +15 -4
  353. package/src/standalone/daemon.mjs +179 -8
  354. package/src/standalone/desktop-service-registry.mjs +20 -0
  355. package/src/standalone/local-session-runtime.mjs +11 -0
  356. package/src/standalone/session-client/attachment-calls.mjs +3 -1
  357. package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
  358. package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
  359. package/src/standalone/session-client.mjs +104 -13
  360. package/src/standalone/session-protocol.mjs +21 -0
  361. package/src/standalone/session-proxy.mjs +3 -0
  362. package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
  363. package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
  364. package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
  365. package/src/standalone/session-service/projection/revisions.mjs +2 -0
  366. package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
  367. package/src/standalone/session-service/projection/workspace.mjs +14 -0
  368. package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
  369. package/src/standalone/session-transport/call-route.mjs +13 -0
  370. package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
  371. package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
  372. package/src/standalone/session-transport/client-registry.mjs +20 -3
  373. package/src/standalone/session-transport/routes.mjs +79 -2
  374. package/src/standalone/session-transport/stack.mjs +2 -0
  375. package/src/standalone/session-transport.mjs +24 -1
  376. package/src/tui/App.jsx +9 -1
  377. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  378. package/src/tui/app/app-view.jsx +6 -0
  379. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  380. package/src/tui/app/message-selector.mjs +2 -1
  381. package/src/tui/app/model-options.mjs +37 -5
  382. package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
  383. package/src/tui/app/model-picker.mjs +4 -2
  384. package/src/tui/app/skill-mention-palette.mjs +88 -0
  385. package/src/tui/app/slash-palette.mjs +23 -7
  386. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  387. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  388. package/src/tui/app/use-prompt-state.mjs +5 -0
  389. package/src/tui/components/Message.jsx +22 -1
  390. package/src/tui/components/PromptInput.jsx +21 -3
  391. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  392. package/src/tui/components/TranscriptItem.jsx +9 -1
  393. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  394. package/src/tui/components/user-message-skills.mjs +38 -0
  395. package/src/tui/dist/index.mjs +839 -207
  396. package/src/tui/index.jsx +10 -2
  397. package/src/tui/session/compaction-view.mjs +147 -0
  398. package/src/tui/session/context-state.mjs +6 -0
  399. package/src/tui/session/initial-state.mjs +3 -0
  400. package/src/tui/session/queue-helpers.mjs +7 -2
  401. package/src/tui/session/quota-resume.mjs +2 -1
  402. package/src/tui/session/session-api/commands.mjs +10 -2
  403. package/src/tui/session/session-api/controls.mjs +4 -0
  404. package/src/tui/session/session-api/extensions.mjs +4 -2
  405. package/src/tui/session/session-api/intake/rewind.mjs +2 -1
  406. package/src/tui/session/session-api/integrations.mjs +17 -0
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
  408. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
  409. package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
  410. package/src/tui/session/session-api/routes.mjs +10 -1
  411. package/src/tui/session/session-api/settings.mjs +9 -0
  412. package/src/tui/session/session-api/worktree.mjs +60 -0
  413. package/src/tui/session/session-api.mjs +2 -0
  414. package/src/tui/session/session-flow/auto-clear.mjs +31 -12
  415. package/src/tui/session/session-flow/drain.mjs +27 -3
  416. package/src/tui/session/session-flow/session-reset.mjs +4 -3
  417. package/src/tui/session/session-flow.mjs +2 -0
  418. package/src/tui/session/transcript-intake.mjs +14 -5
  419. package/src/tui/session/tui-steering-persist.mjs +25 -9
  420. package/src/tui/session/turn-stream.mjs +32 -4
  421. package/src/tui/session/turn-tool-cards.mjs +10 -2
  422. package/src/tui/session/turn.mjs +56 -10
  423. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  424. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
@@ -0,0 +1,244 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parseJsonText, updateJsonAtomicSync } from '../../../shared/atomic-file.mjs';
4
+ import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
5
+
6
+ // An OpenAI-compatible endpoint that rejects the requested reasoning effort
7
+ // (HTTP 400 naming the value) is replayed once at the nearest accepted level,
8
+ // and the rejection is remembered per provider+model so the level stops being
9
+ // sent (clamped before the request) and stops being offered (the sync reader).
10
+
11
+ const EFFORT_ORDER = Object.freeze(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
12
+ const FILE_NAME = 'effort-rejections.json';
13
+ // Per-provider request fields that can carry the effort next to `opts.effort`.
14
+ const EFFORT_OPT_KEYS = ['xaiReasoningEffort', 'openRouterReasoningEffort', 'deepseekReasoningEffort'];
15
+ const LEVEL_RE = new RegExp(`\\b(${EFFORT_ORDER.join('|')})\\b`, 'gi');
16
+ const LIST_RE =
17
+ /\b(?:supported|allowed|valid|accepted|available)\s+(?:values?|levels?|options?|efforts?)|(?:must|should|expected to) be one of|expected one of|one of|choose from|input should be/i;
18
+ const REJECT_RE =
19
+ /invalid|unsupported|not supported|does not support|doesn't support|not allowed|not a valid|unrecognized|must be one of|expected one of|supported values/i;
20
+ // Anthropic's `invalid_request_error` words a bad enum value as "Input should be 'low', ...".
21
+ const ANTHROPIC_REJECT_RE = /input should be|should be one of|not one of/i;
22
+ const EFFORT_RE = /reasoning[._ ]?effort|\beffort\b/i;
23
+ const REASONING_RE = /\breasoning\b/i;
24
+ // A request field other than the effort: `reasoning.summary`, `reasoning_content`,
25
+ // `thinking.type`, `budget_tokens`, ...
26
+ const OTHER_FIELD_RE = /\breasoning[._[](?!effort)\w+|(?:^|[\s'"`(])(?:thinking\.\w+|budget_tokens)\b/i;
27
+ // The mid-conversation effort update rejection belongs to effort-configuration.mjs.
28
+ const EFFORT_CONFIGURATION_RE = /mid-conversation|configuration_update|reasoning_effort_update/i;
29
+ /** The effort levels the Anthropic Messages wire accepts (`output_config.effort`). */
30
+ export const ANTHROPIC_EFFORT_LEVELS = Object.freeze(['low', 'medium', 'high', 'xhigh', 'max']);
31
+
32
+ const cache = new Map();
33
+
34
+ const filePath = () => join(resolvePluginData(), FILE_NAME);
35
+ const entryKey = (provider, model) =>
36
+ `${String(provider || '').trim()}:${String(model || '')
37
+ .trim()
38
+ .toLowerCase()}`;
39
+ const levelOf = (value) => {
40
+ const level = String(value ?? '')
41
+ .trim()
42
+ .toLowerCase();
43
+ return EFFORT_ORDER.includes(level) ? level : null;
44
+ };
45
+
46
+ // A rejection persisted by another process changes the file; the cached copy is
47
+ // only trusted while the file's mtime and size are unchanged.
48
+ function signature(file) {
49
+ try {
50
+ const stat = statSync(file);
51
+ return `${stat.mtimeMs}:${stat.size}`;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ function load(file) {
58
+ const sig = signature(file);
59
+ const cached = cache.get(file);
60
+ if (cached && cached.sig === sig) return cached.data;
61
+ let data = null;
62
+ try {
63
+ data = parseJsonText(readFileSync(file, 'utf8'));
64
+ } catch {}
65
+ const valid =
66
+ data?.version === 1 && data.models && typeof data.models === 'object' ? data : { version: 1, models: {} };
67
+ cache.set(file, { sig, data: valid });
68
+ return valid;
69
+ }
70
+
71
+ function addRejection(data, key, level, allowed) {
72
+ const models = { ...data.models };
73
+ const prev = models[key] || {};
74
+ const rejected = new Set(prev.rejected || []);
75
+ if (level) rejected.add(level);
76
+ const nextAllowed = allowed?.length ? allowed : prev.allowed;
77
+ models[key] = { rejected: [...rejected], ...(nextAllowed?.length ? { allowed: nextAllowed } : {}) };
78
+ return { version: 1, models };
79
+ }
80
+
81
+ function entry(provider, model) {
82
+ return load(filePath()).models[entryKey(provider, model)] || null;
83
+ }
84
+
85
+ /** Levels this provider+model is known not to accept (rejected, or outside a
86
+ * list the endpoint named). Sync, for the runtime effort catalog. */
87
+ export function rejectedEffortLevelsSync(provider, model) {
88
+ const found = entry(provider, model);
89
+ const rejected = new Set(found?.rejected || []);
90
+ if (found?.allowed?.length) for (const level of EFFORT_ORDER) if (!found.allowed.includes(level)) rejected.add(level);
91
+ return rejected;
92
+ }
93
+
94
+ /** Remember a rejected level (and the allowed list the endpoint named, if any). */
95
+ export function recordEffortRejection(provider, model, level, allowed = null) {
96
+ const file = filePath();
97
+ const key = entryKey(provider, model);
98
+ const normalized = levelOf(level);
99
+ try {
100
+ const data = updateJsonAtomicSync(
101
+ file,
102
+ (cur) =>
103
+ addRejection(cur?.version === 1 && cur.models ? cur : { version: 1, models: {} }, key, normalized, allowed),
104
+ { lock: true, timeoutMs: 1000 }
105
+ );
106
+ cache.set(file, { sig: signature(file), data });
107
+ } catch {
108
+ cache.set(file, { sig: signature(file), data: addRejection(load(file), key, normalized, allowed) });
109
+ }
110
+ }
111
+
112
+ /** Forget what was loaded from disk (a process restart, for tests). */
113
+ export function resetEffortRejectionCache() {
114
+ cache.clear();
115
+ }
116
+
117
+ const collect = (error) => {
118
+ const nested = error?.error?.error && typeof error.error.error === 'object' ? error.error.error : null;
119
+ return [error, error?.error, nested].filter((part) => part && typeof part === 'object');
120
+ };
121
+
122
+ const hasLevel = (text) => new RegExp(LEVEL_RE.source, 'i').test(text);
123
+
124
+ /**
125
+ * A 400-class provider error that rejects the reasoning effort, as
126
+ * `{ allowed }` (levels the message lists, or null), else null. It must be
127
+ * effort-specific: the `param` names the effort field, or the message names the
128
+ * effort field together with a level, or (given the `requested` level) the
129
+ * message calls that level invalid while talking about reasoning. A rejection
130
+ * of another reasoning field (`reasoning.summary`, `reasoning.include`) never matches.
131
+ */
132
+ export function detectEffortRejection(error, requested = null) {
133
+ const status = [error?.status, error?.httpStatus, error?.response?.status].map(Number).find((value) => value > 0);
134
+ if (status !== 400 && status !== 422) return null;
135
+ const parts = collect(error);
136
+ const param = parts.map((part) => String(part.param ?? '')).join(' ');
137
+ const text = parts
138
+ .flatMap((part) => [part.message, part.code, part.param])
139
+ .filter(Boolean)
140
+ .join(' ');
141
+ if (EFFORT_CONFIGURATION_RE.test(text)) return null;
142
+ const anthropic = parts.some((part) => part.type === 'invalid_request_error') || /invalid_request_error/.test(text);
143
+ if (!REJECT_RE.test(text) && !(anthropic && ANTHROPIC_REJECT_RE.test(text))) return null;
144
+ const level = levelOf(requested);
145
+ const targeted =
146
+ /effort/i.test(param) ||
147
+ (!OTHER_FIELD_RE.test(`${param} ${text}`) &&
148
+ ((EFFORT_RE.test(text) && hasLevel(text)) ||
149
+ (level && REASONING_RE.test(text) && new RegExp(`\\b${level}\\b`, 'i').test(text))));
150
+ if (!targeted) return null;
151
+ const list = LIST_RE.exec(text);
152
+ const allowed = list
153
+ ? [...new Set([...text.slice(list.index + list[0].length).matchAll(LEVEL_RE)].map((m) => m[1].toLowerCase()))]
154
+ : [];
155
+ return { allowed: allowed.length ? allowed : null };
156
+ }
157
+
158
+ /**
159
+ * The nearest lower level not in `rejected`; with `allowHigher` (the accepted
160
+ * set is known) the nearest higher one when nothing lower is left.
161
+ */
162
+ export function nearestAcceptedEffort(requested, rejected = new Set(), { allowHigher = false } = {}) {
163
+ const from = EFFORT_ORDER.indexOf(requested);
164
+ if (from < 0) return null;
165
+ const open = EFFORT_ORDER.filter((level, index) => index !== from && !rejected.has(level));
166
+ const lower = open.filter((level) => EFFORT_ORDER.indexOf(level) < from).at(-1);
167
+ return lower ?? (allowHigher ? (open[0] ?? null) : null);
168
+ }
169
+
170
+ const requestedEffort = (opts, configEffort) => levelOf(opts?.effort ?? configEffort);
171
+
172
+ function withEffort(opts, level) {
173
+ const next = { ...opts, effort: level };
174
+ for (const key of EFFORT_OPT_KEYS) if (opts?.[key] !== undefined) next[key] = level;
175
+ return next;
176
+ }
177
+
178
+ // The rejected set seen by a transport that only takes `levels` of the ladder.
179
+ function rejectedWithin(provider, model, levels) {
180
+ const rejected = rejectedEffortLevelsSync(provider, model);
181
+ if (levels) for (const level of EFFORT_ORDER) if (!levels.includes(level)) rejected.add(level);
182
+ return rejected;
183
+ }
184
+
185
+ /** `opts` with a level already known to be rejected stepped down; unchanged otherwise. */
186
+ export function clampEffortOpts(provider, model, opts, configEffort, levels = null) {
187
+ const requested = requestedEffort(opts, configEffort);
188
+ if (!requested || (levels && !levels.includes(requested))) return opts;
189
+ const rejected = rejectedWithin(provider, model, levels);
190
+ if (!rejected.has(requested)) return opts;
191
+ const level = nearestAcceptedEffort(requested, rejected, { allowHigher: Boolean(entry(provider, model)?.allowed) });
192
+ return level ? withEffort(opts, level) : opts;
193
+ }
194
+
195
+ const STREAM_CALLBACKS = ['onStreamDelta', 'onTextDelta', 'onToolCall'];
196
+
197
+ /**
198
+ * Runs `send(opts)` with known-rejected levels clamped first; when the model
199
+ * rejects the effort, remembers it and replays the request once at the nearest
200
+ * accepted level. Never replays after output reached the caller, and never
201
+ * replays the replay. `levels` limits the ladder to what the transport's wire
202
+ * accepts. A send re-entered from inside a wrapped send (a transport's own
203
+ * replays call `send` again) passes straight through: the outer wrapper owns it.
204
+ */
205
+ export async function withEffortRejectionFallback(provider, model, opts, configEffort, send, { levels = null } = {}) {
206
+ if (opts?._effortRejectionActive) return send(opts);
207
+ let streamed = false;
208
+ const tracked = { ...opts, _effortRejectionActive: true };
209
+ for (const name of STREAM_CALLBACKS) {
210
+ const callback = opts?.[name];
211
+ if (typeof callback === 'function') {
212
+ tracked[name] = (...args) => {
213
+ streamed = true;
214
+ return callback(...args);
215
+ };
216
+ }
217
+ }
218
+ const first = clampEffortOpts(provider, model, tracked, configEffort, levels);
219
+ try {
220
+ return await send(first);
221
+ } catch (error) {
222
+ const requested = requestedEffort(first, configEffort);
223
+ const rejection = detectEffortRejection(error, requested);
224
+ const emitted =
225
+ streamed || error?.liveTextEmitted === true || error?.emittedToolCall === true || error?.unsafeToRetry === true;
226
+ if (!rejection || !requested || (levels && !levels.includes(requested))) throw error;
227
+ recordEffortRejection(provider, model, requested, rejection.allowed);
228
+ if (emitted) throw error;
229
+ const level = nearestAcceptedEffort(requested, rejectedWithin(provider, model, levels), {
230
+ allowHigher: Boolean(entry(provider, model)?.allowed),
231
+ });
232
+ if (!level) throw error;
233
+ process.stderr.write(
234
+ `[${provider}] ${model} rejected reasoning effort '${requested}'; retrying once at '${level}'\n`
235
+ );
236
+ try {
237
+ return await send(withEffort(first, level));
238
+ } catch (retryError) {
239
+ const again = detectEffortRejection(retryError, level);
240
+ if (again) recordEffortRejection(provider, model, level, again.allowed);
241
+ throw retryError;
242
+ }
243
+ }
244
+ }
@@ -1,5 +1,5 @@
1
1
  import { GoogleGenerativeAI } from '@google/generative-ai';
2
- import { getAgentApiKey } from '../../../shared/provider-api-key.mjs';
2
+ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
3
3
  import { canFallbackNonStreaming, emitProviderRetryStage, withRetry } from './retry-classifier.mjs';
4
4
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
5
5
  import { traceNonStreamingFallback } from './lib/transport-fallback-trace.mjs';
@@ -153,8 +153,9 @@ export class GeminiProvider {
153
153
  this._createGenAI =
154
154
  typeof config.createGenAI === 'function' ? config.createGenAI : (apiKey) => new GoogleGenerativeAI(apiKey);
155
155
  this._modelCache = config.modelCache || _modelCache;
156
- const apiKey = config.apiKey || process.env.GEMINI_API_KEY || '';
157
- this.genAI = config.genAI || this._createGenAI(apiKey);
156
+ this._injectedGenAI = Boolean(config.genAI);
157
+ this._genAIKey = resolveProviderApiKey('gemini', config);
158
+ this.genAI = config.genAI || this._createGenAI(this._genAIKey);
158
159
  // Warm a kept-alive socket to the Gemini REST API so the first cache/
159
160
  // generateContent request skips the cold TLS handshake. Best-effort.
160
161
  this._preconnect('https://generativelanguage.googleapis.com');
@@ -162,12 +163,13 @@ export class GeminiProvider {
162
163
 
163
164
  reloadApiKey() {
164
165
  try {
165
- const newKey = getAgentApiKey('gemini') || this.config?.apiKey || process.env.GEMINI_API_KEY;
166
+ const newKey = reloadProviderApiKey('gemini', this.config);
166
167
  if (newKey) {
167
- // Keep this.config in sync so REST/cache paths (which read the
168
- // key via _getApiKey() → this.config.apiKey) don't keep using
169
- // the stale key after a rotation; genAI alone is not enough.
168
+ // Keep this.config in sync so REST/cache paths don't keep using the
169
+ // stale key after a rotation; genAI alone is not enough.
170
170
  this.config = { ...(this.config || {}), apiKey: newKey };
171
+ this._injectedGenAI = false;
172
+ this._genAIKey = newKey;
171
173
  this.genAI = this._createGenAI(newKey);
172
174
  }
173
175
  return newKey || '';
@@ -178,7 +180,18 @@ export class GeminiProvider {
178
180
  }
179
181
 
180
182
  _getApiKey() {
181
- return this.config?.apiKey || process.env.GEMINI_API_KEY || '';
183
+ return resolveProviderApiKey('gemini', this.config);
184
+ }
185
+
186
+ // SDK client for the currently resolved key; rebuilt when the key changed
187
+ // since construction (e.g. a keyless-constructed instance).
188
+ _currentGenAI() {
189
+ const key = this._getApiKey();
190
+ if (!this._injectedGenAI && key !== this._genAIKey) {
191
+ this._genAIKey = key;
192
+ this.genAI = this._createGenAI(key);
193
+ }
194
+ return this.genAI;
182
195
  }
183
196
 
184
197
  /**
@@ -673,7 +686,7 @@ export class GeminiProvider {
673
686
 
674
687
  async _streamViaSdk(stream) {
675
688
  const { opts, signal, useModel, systemInstruction, geminiTools, toolConfig, generationConfig, contents } = stream;
676
- const genModel = this.genAI.getGenerativeModel({
689
+ const genModel = this._currentGenAI().getGenerativeModel({
677
690
  model: useModel,
678
691
  systemInstruction,
679
692
  tools: geminiTools,
@@ -714,7 +727,7 @@ export class GeminiProvider {
714
727
  const cached = this._modelCache.loadSync();
715
728
  if (cached) return cached;
716
729
  // Dynamic lookup via Gemini v1beta /models. Requires API key.
717
- const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY;
730
+ const apiKey = this._getApiKey();
718
731
  if (!apiKey) return MODELS; // no key — return minimal static list
719
732
  try {
720
733
  return await this._fetchAndCacheModels(apiKey);
@@ -738,26 +751,26 @@ export class GeminiProvider {
738
751
 
739
752
  // Force a catalog refresh (ignores the 24h disk TTL). De-duped via
740
753
  // _modelRefreshInFlight so concurrent callers share one HTTP round-trip.
741
- // Fire-and-forget context: failures are caught/logged, returning null.
754
+ // Resolves null when there is no key; fetch/normalize failures reject so
755
+ // callers (registry refresh) report them.
742
756
  async _refreshModelCache() {
743
757
  if (_modelRefreshInFlight) return _modelRefreshInFlight;
744
- _modelRefreshInFlight = (async () => {
745
- try {
746
- const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY;
747
- if (!apiKey) return null; // no key — nothing to refresh
748
- const enriched = await this._fetchAndCacheModels(apiKey);
749
- if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
750
- process.stderr.write(`[gemini] catalog refreshed (${enriched.length} models)\n`);
751
- return enriched;
752
- } catch (err) {
753
- if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
754
- process.stderr.write(`[gemini] catalog refresh failed (${err.message})\n`);
755
- return null;
756
- } finally {
757
- _modelRefreshInFlight = null;
758
- }
758
+ const refresh = (async () => {
759
+ const apiKey = this._getApiKey();
760
+ if (!apiKey) return null; // no key — nothing to refresh
761
+ const enriched = await this._fetchAndCacheModels(apiKey);
762
+ if (!process.env.MIXDOG_QUIET_PROVIDER_LOG)
763
+ process.stderr.write(`[gemini] catalog refreshed (${enriched.length} models)\n`);
764
+ return enriched;
759
765
  })();
760
- return _modelRefreshInFlight;
766
+ // Clear only after the shared promise is published: a refresh that settles
767
+ // synchronously (no key) must not leave its settled promise memoized.
768
+ _modelRefreshInFlight = refresh;
769
+ try {
770
+ return await refresh;
771
+ } finally {
772
+ if (_modelRefreshInFlight === refresh) _modelRefreshInFlight = null;
773
+ }
761
774
  }
762
775
 
763
776
  async isAvailable() {
@@ -766,7 +779,7 @@ export class GeminiProvider {
766
779
  controller.abort(providerTimeoutError('Gemini availability probe', GEMINI_AVAILABILITY_TIMEOUT_MS));
767
780
  }, GEMINI_AVAILABILITY_TIMEOUT_MS);
768
781
  try {
769
- const model = this.genAI.getGenerativeModel({ model: DEFAULT_MODEL });
782
+ const model = this._currentGenAI().getGenerativeModel({ model: DEFAULT_MODEL });
770
783
  const generation = Promise.resolve(model.generateContent('hi', { signal: controller.signal }));
771
784
  generation.catch(() => {});
772
785
  await Promise.race([
@@ -3,7 +3,7 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
3
3
  // Grok CLI client version for the cli-chat-proxy version gate (HTTP 426).
4
4
  // Effective = env override, else max(floor, learned-from-426, live npm latest
5
5
  // of the official @xai-official/grok CLI).
6
- export const GROK_CLI_VERSION_FLOOR = '1.0.50';
6
+ export const GROK_CLI_VERSION_FLOOR = '1.0.52';
7
7
  export const GROK_CLI_NPM_PACKAGE = '@xai-official/grok';
8
8
 
9
9
  const live = createNpmVersionSource(GROK_CLI_NPM_PACKAGE, { persistKey: 'grok-cli' });
@@ -10,6 +10,7 @@ import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs
10
10
  import { createTimeoutSignal } from '../stall-policy.mjs';
11
11
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
12
12
  import { grokClientVersionHeaders } from './grok-client-version.mjs';
13
+ import { modelProxyClientMode, serverModelCredentialsDisabledError } from '../../../shared/model-proxy-mode.mjs';
13
14
  import {
14
15
  accountIdentityFields,
15
16
  decodeJwtPayload,
@@ -174,6 +175,7 @@ export function _mtimeMs(path) {
174
175
  // mixdog's own login store (grok-oauth.json). Single writer, accurate
175
176
  // numeric expires_at from refresh.
176
177
  export function _loadOwnTokens() {
178
+ if (modelProxyClientMode()) return null;
177
179
  const path = getOwnTokenPath();
178
180
  if (!existsSync(path)) return null;
179
181
  try {
@@ -375,6 +377,7 @@ async function _postRefreshWithRetry(tokens) {
375
377
  // single-use. Hold a cross-process lease across the re-read, exchange, and
376
378
  // atomic save so only one process can spend a generation.
377
379
  export async function refreshTokens(tokens, { force = false } = {}) {
380
+ if (modelProxyClientMode()) throw serverModelCredentialsDisabledError('Grok OAuth credentials');
378
381
  if (!tokens?.refresh_token) {
379
382
  throw new Error('[grok-oauth] refresh token not available — open /providers in mixdog to sign in again');
380
383
  }
@@ -79,7 +79,13 @@ function resolveLatestGeminiModel() {
79
79
  export async function ensureLatestGeminiModel(provider) {
80
80
  let model = resolveLatestGeminiModel();
81
81
  if (model) return model;
82
- await provider._refreshModelCache();
82
+ try {
83
+ await provider._refreshModelCache();
84
+ } catch (err) {
85
+ throw new Error(`[gemini] model catalog refresh failed — cannot resolve default model (${err.message})`, {
86
+ cause: err,
87
+ });
88
+ }
83
89
  model = resolveLatestGeminiModel();
84
90
  if (model) return model;
85
91
  throw new Error('[gemini] model catalog unavailable after warmup — cannot resolve default model');
@@ -6,6 +6,7 @@ import { scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
6
6
  import { PROVIDER_MAX_BEFORE_WARN_MS, STALL_ABORT_S, STALL_TICK_MS } from '../stall-policy.mjs';
7
7
  import { PACKAGE_VERSION } from '../../../web-search/lib/package-version.mjs';
8
8
  import { DEFAULT_MODEL } from './mimo-oauth-tokens.mjs';
9
+ import { MIMO_SEED_MODELS, mimoSeedRow as seedRow } from '../../../shared/llm/provider-seed-rosters.mjs';
9
10
 
10
11
  const MIMO_EFFORT_LEVELS = Object.freeze(['minimal', 'low', 'medium', 'high']);
11
12
  const MIMO_DEFAULT_TEMPERATURE = 1.0;
@@ -15,25 +16,12 @@ const MIMO_DEFAULT_MAX_OUTPUT_TOKENS = 128_000;
15
16
  export const MIMO_FIRST_BYTE_TIMEOUT_MS = Math.min(300_000, PROVIDER_MAX_BEFORE_WARN_MS);
16
17
  export const MIMO_SEMANTIC_IDLE_TIMEOUT_MS = Math.min(480_000, Math.max(10_000, STALL_ABORT_S * 1000 - STALL_TICK_MS));
17
18
 
18
- // Local roster used when /models fails or comes back empty.
19
- const SEED_CONTEXT = 1_048_576;
20
- const SEED_OUTPUT = 131_072;
21
- export const SEED_MODELS = Object.freeze(
22
- [
23
- ['mimo-v2.6-pro', 'MiMo-V2.6-Pro', SEED_CONTEXT],
24
- ['mimo-v2.6-flash', 'MiMo-V2.6-Flash', SEED_CONTEXT],
25
- ['mimo-v2.6-pro-ultraspeed', 'MiMo-V2.6-Pro-Ultraspeed', SEED_CONTEXT],
26
- ['mimo-v2.5-pro', 'MiMo-V2.5-Pro', SEED_CONTEXT],
27
- ['mimo-v2.5', 'MiMo-V2.5', SEED_CONTEXT],
28
- ['mimo-v2-pro', 'MiMo-V2-Pro', SEED_CONTEXT],
29
- ['mimo-v2-omni', 'MiMo-V2-Omni', 262_144],
30
- ].map(([id, display_name, context_window]) =>
31
- Object.freeze({ id, display_name, context_window, max_output_tokens: SEED_OUTPUT })
32
- )
33
- );
19
+ // Local roster used when /models fails or comes back empty. Its limits are not
20
+ // discovered data: decorateModelRows applies them only where nothing fetched did.
21
+ export const SEED_MODELS = MIMO_SEED_MODELS;
34
22
 
35
23
  export function seedModelItems() {
36
- return SEED_MODELS.map((item) => ({ ...item }));
24
+ return SEED_MODELS.map(({ id, display_name }) => ({ id, display_name }));
37
25
  }
38
26
 
39
27
  // Speech/transcription ids cannot serve a text chat turn.
@@ -50,28 +38,8 @@ export function chooseDefaultModel(ids) {
50
38
  return chat[0] || DEFAULT_MODEL;
51
39
  }
52
40
 
53
- // Exact seed id, else the longest seed whose id is a lineage prefix of the
54
- // model (dated or suffixed variants of a seeded model).
55
- function seedRow(model) {
56
- const id = String(model || '').toLowerCase();
57
- let seed = SEED_MODELS.find((item) => item.id === id) || null;
58
- if (!seed) {
59
- for (const item of SEED_MODELS) {
60
- if (id.startsWith(`${item.id}-`) && (!seed || item.id.length > seed.id.length)) seed = item;
61
- }
62
- }
63
- if (!seed) return null;
64
- return {
65
- id: String(model),
66
- contextWindow: seed.context_window,
67
- outputTokens: seed.max_output_tokens,
68
- reasoning: true,
69
- vision: true,
70
- inputModalities: ['text', 'image'],
71
- };
72
- }
73
-
74
- // Fill the gaps a /models row leaves (names, limits, effort levels).
41
+ // Fill the gaps a /models row leaves (names, limits, effort levels); seeds
42
+ // never replace a value the row already carries.
75
43
  export function decorateModelRows(rows, items) {
76
44
  const display = new Map();
77
45
  for (const item of items || []) {
@@ -89,7 +57,7 @@ export function decorateModelRows(rows, items) {
89
57
  ...(seed && row.reasoning === undefined ? { reasoning: seed.reasoning } : {}),
90
58
  ...(seed && row.vision === undefined ? { vision: seed.vision } : {}),
91
59
  ...(seed && row.inputModalities === undefined ? { inputModalities: seed.inputModalities } : {}),
92
- reasoningLevels: [...MIMO_EFFORT_LEVELS],
60
+ reasoningLevels: row.reasoningLevels?.length ? row.reasoningLevels : [...MIMO_EFFORT_LEVELS],
93
61
  reasoningContentField: 'reasoning_content',
94
62
  };
95
63
  });
@@ -8,6 +8,7 @@ import { getPluginData } from '../config.mjs';
8
8
  import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
9
9
  import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
10
10
  import { accountIdentityFields, fileMtimeMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
11
+ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
11
12
 
12
13
  const DEFAULT_PLATFORM_ORIGIN = 'https://platform.xiaomimimo.com';
13
14
  const DEFAULT_BASE_URL = 'https://api.xiaomimimo.com/v1';
@@ -98,6 +99,7 @@ export function getKeyName() {
98
99
  }
99
100
 
100
101
  export function loadTokens() {
102
+ if (modelProxyClientMode()) return null;
101
103
  const path = getOwnTokenPath();
102
104
  if (!existsSync(path)) return null;
103
105
  try {
@@ -10,6 +10,7 @@ import { noteErrorUsage } from './lib/note-error-usage.mjs';
10
10
  import { fileMtimeMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
11
11
  import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
12
12
  import { fetchCompatModelItems, listCompatModels } from './openai-compat-models.mjs';
13
+ import { makeModelCache } from './model-cache.mjs';
13
14
  import {
14
15
  SOURCE_HEADER,
15
16
  ensureTokenPlanBaseUrl,
@@ -29,6 +30,23 @@ import {
29
30
 
30
31
  const isUnauthorized = (err) => Number(err?.httpStatus || err?.status) === 401;
31
32
 
33
+ // The discovered roster (what /models returned, never seed values) is
34
+ // persisted so the shared sync lookup sees what listModels found.
35
+ const mimoModelCache = makeModelCache({ fileName: 'mimo-oauth-models.json', ttlMs: 24 * 60 * 60 * 1000, version: 1 });
36
+
37
+ function rosterRow(item) {
38
+ const contextWindow = Number(item.context_window ?? item.max_context_window ?? item.context_length ?? 0);
39
+ const outputTokens = Number(item.max_output_tokens ?? item.output_tokens ?? 0);
40
+ const name = typeof item.display_name === 'string' ? item.display_name.trim() : '';
41
+ return {
42
+ id: item.id,
43
+ provider: 'mimo-oauth',
44
+ ...(name ? { name } : {}),
45
+ ...(contextWindow > 0 ? { contextWindow } : {}),
46
+ ...(outputTokens > 0 ? { outputTokens } : {}),
47
+ };
48
+ }
49
+
32
50
  // Strip the key from an error (message plus any echoed gateway body).
33
51
  function scrubErrorSecrets(err, apiKey) {
34
52
  if (!err || typeof err !== 'object' || !apiKey) return err;
@@ -101,6 +119,7 @@ export class MimoOAuthProvider {
101
119
  _inner = null;
102
120
  _innerKey = null;
103
121
  _rosterItems = [];
122
+ _rosterSeq = 0;
104
123
  _defaultModelKey = null;
105
124
  _defaultModel = null;
106
125
 
@@ -143,7 +162,7 @@ export class MimoOAuthProvider {
143
162
  clientOptions: { organization: null, project: null },
144
163
  extraHeaders: { ...SOURCE_HEADER, Accept: 'application/json' },
145
164
  });
146
- inner._fetchModelItems = (args) => this._fetchRoster(inner, args);
165
+ inner._fetchModelItems = async (args) => (await this._fetchRoster(inner, args)).items;
147
166
  redactClientErrors(inner.client, apiKey);
148
167
  this._inner = inner;
149
168
  this._innerKey = key;
@@ -153,15 +172,28 @@ export class MimoOAuthProvider {
153
172
  // Chat models from /models; a failed or empty listing falls back to the local
154
173
  // seed roster. Only a rejected key is surfaced.
155
174
  async _fetchRoster(inner, args) {
175
+ // Only the latest request, fetched with the still-active key, publishes.
176
+ const apiKey = inner.apiKey;
177
+ const seq = ++this._rosterSeq;
178
+ const publish = mimoModelCache.beginSave(() => loadTokens()?.apiKey === apiKey);
156
179
  let items = [];
157
180
  try {
158
181
  items = (await fetchCompatModelItems(inner, args)).filter((item) => isChatModelId(item?.id));
159
182
  } catch (err) {
160
183
  if (isUnauthorized(err)) throw err;
161
184
  }
162
- if (!items.length) items = seedModelItems();
163
- this._rosterItems = items;
164
- return items;
185
+ // `published` is false when disk kept a newer roster; the seed fallback
186
+ // publishes nothing. `_listRows` revalidates `seq` and the key before
187
+ // touching shared state, since enrichment runs after this returns.
188
+ let published = true;
189
+ if (items.length) published = publish(items.map(rosterRow));
190
+ else items = seedModelItems();
191
+ return { items, published, seq, apiKey };
192
+ }
193
+
194
+ // Still the latest request, fetched with the still-active key.
195
+ _isCurrentRoster({ published, seq, apiKey }) {
196
+ return published && seq === this._rosterSeq && loadTokens()?.apiKey === apiKey;
165
197
  }
166
198
 
167
199
  // Shared send/list error shaping, also applied to credential setup failures.
@@ -180,19 +212,26 @@ export class MimoOAuthProvider {
180
212
  // Configured model wins; otherwise pick from this credential's own roster.
181
213
  async _pickDefaultModel(inner) {
182
214
  if (this._defaultModel && this._defaultModelKey === this._innerKey) return this._defaultModel;
183
- await this._listRows(inner);
184
- this._defaultModel = chooseDefaultModel(this._enrichedIds(inner));
215
+ const rows = await this._listRows(inner);
216
+ this._defaultModel = chooseDefaultModel(rows.map((row) => row.id));
185
217
  this._defaultModelKey = this._innerKey;
186
218
  return this._defaultModel;
187
219
  }
188
220
 
189
- _enrichedIds(inner) {
190
- return (inner._enrichedModels || []).map((row) => row.id);
191
- }
192
-
221
+ // The request keeps its own items and rows; only a request that is still
222
+ // current publishes them to the shared in-memory roster and metadata.
193
223
  async _listRows(inner) {
194
- const rows = decorateModelRows(await listCompatModels(inner, { throwOnError: true }), this._rosterItems);
195
- inner._enrichedModels = rows;
224
+ let fetched = null;
225
+ const request = Object.create(inner);
226
+ request._fetchModelItems = async (args) => {
227
+ fetched = await this._fetchRoster(inner, args);
228
+ return fetched.items;
229
+ };
230
+ const rows = decorateModelRows(await listCompatModels(request, { throwOnError: true }), fetched.items);
231
+ if (this._isCurrentRoster(fetched)) {
232
+ this._rosterItems = fetched.items;
233
+ inner._enrichedModels = rows;
234
+ }
196
235
  return rows;
197
236
  }
198
237