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
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
3
- import { getAgentApiKey } from '../../../shared/provider-api-key.mjs';
3
+ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
4
4
  import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
5
5
  import { appendAgentTrace } from '../agent-trace.mjs';
6
6
  import { OPENAI_COMPAT_PRESETS } from './openai-compat-presets.mjs';
@@ -12,6 +12,7 @@ import {
12
12
  useXaiResponsesWebSocket as preferXaiResponsesWebSocket,
13
13
  _shouldFallbackXaiWsToHttp,
14
14
  } from './openai-compat-xai.mjs';
15
+ import { withEffortRejectionFallback } from './effort-rejection.mjs';
15
16
  import { sendCompatResponses } from './openai-compat-responses.mjs';
16
17
  import { sendCompatChat, recoverCompatNonStreaming } from './openai-compat-chat-send.mjs';
17
18
  import { sendXaiResponses, sendXaiResponsesWebSocket } from './openai-compat-xai-send.mjs';
@@ -68,7 +69,7 @@ export class OpenAICompatProvider {
68
69
  constructor(name, config) {
69
70
  const preset = PRESETS[name];
70
71
  const baseURL = assertSafeBaseURL(config.baseURL || preset?.baseURL || 'http://localhost:8080/v1', name);
71
- const apiKey = config.apiKey || 'no-key';
72
+ const apiKey = resolveProviderApiKey(name, config) || 'no-key';
72
73
  this.name = name;
73
74
  this.config = config;
74
75
  this.baseURL = baseURL;
@@ -120,7 +121,7 @@ export class OpenAICompatProvider {
120
121
  reloadApiKey() {
121
122
  try {
122
123
  const preset = PRESETS[this.name];
123
- const newKey = getAgentApiKey(this.name) || this.config.apiKey;
124
+ const newKey = reloadProviderApiKey(this.name, this.config);
124
125
  const baseURL = assertSafeBaseURL(
125
126
  this.config.baseURL || preset?.baseURL || 'http://localhost:8080/v1',
126
127
  this.name
@@ -137,6 +138,12 @@ export class OpenAICompatProvider {
137
138
  }
138
139
  }
139
140
  async send(messages, model, tools, sendOpts) {
141
+ // A keyless-constructed instance picks up the key once it is available.
142
+ const resolvedKey = resolveProviderApiKey(this.name, this.config) || 'no-key';
143
+ if (resolvedKey !== this.apiKey) {
144
+ this.apiKey = resolvedKey;
145
+ this.client = this._createClient(resolvedKey);
146
+ }
140
147
  try {
141
148
  return await this._doSend(messages, model, tools, sendOpts);
142
149
  } catch (err) {
@@ -161,7 +168,11 @@ export class OpenAICompatProvider {
161
168
  }
162
169
  async _doSend(messages, model, tools, sendOpts) {
163
170
  const useModel = model || this.defaultModel;
164
- const opts = sendOpts || {};
171
+ return withEffortRejectionFallback(this.name, useModel, sendOpts || {}, this.config?.reasoningEffort, (opts) =>
172
+ this._sendWithOpts(messages, useModel, tools, opts)
173
+ );
174
+ }
175
+ async _sendWithOpts(messages, useModel, tools, opts) {
165
176
  // Re-warm a kept-alive socket to the provider origin before the turn so
166
177
  // the request hot path lands on a live socket instead of paying a cold
167
178
  // TLS handshake after an idle gap. Fire-and-forget; never awaited.
@@ -13,7 +13,7 @@ import { formatScheduleModelRef, parseScheduleModelRef } from '../../../shared/s
13
13
  export const CODEX_MODEL_CACHE_OPTIONS = Object.freeze({
14
14
  fileName: 'openai-oauth-models.json',
15
15
  ttlMs: 24 * 60 * 60_000,
16
- version: 6,
16
+ version: 7,
17
17
  });
18
18
 
19
19
  export const CODEX_PROVIDER_ID = 'openai-oauth';
@@ -13,7 +13,6 @@
13
13
  import { makeModelCache } from './model-cache.mjs';
14
14
  import { CODEX_MODEL_CACHE_OPTIONS } from './openai-oauth-catalog-store.mjs';
15
15
  import { enrichModels } from './model-catalog.mjs';
16
- import { sanitizeModelList } from './model-list-sanitize.mjs';
17
16
  import { modelSupportsServiceTier } from './model-service-tiers.mjs';
18
17
  import { codexClientVersionSync, warmCodexClientVersion } from './codex-client-meta.mjs';
19
18
  import { compareSemver, maxSemver } from './npm-cli-version.mjs';
@@ -131,7 +130,11 @@ async function fetchCodexCatalog(ensureAuth, label) {
131
130
  const items = Array.isArray(data?.models) ? data.models : [];
132
131
  const normalized = items.map((m) => _normalizeCodexModel(m));
133
132
  _markLatestCodex(normalized);
134
- const enriched = sanitizeModelList((await enrichModels(normalized)).filter(Boolean), { provider: 'openai-oauth' });
133
+ // The backend curates this list per account and client version and marks
134
+ // each row's picker visibility itself, so every row is kept: generic list
135
+ // pruning (external release dates, id patterns) dropped models it still
136
+ // serves, along with their access programs.
137
+ const enriched = (await enrichModels(normalized)).filter(Boolean);
135
138
  _modelCache.save(enriched, { clientVersion });
136
139
  return enriched;
137
140
  }
@@ -18,6 +18,7 @@ import { getPluginData } from '../config.mjs';
18
18
  import { writeJsonAtomicSync, withFileLock } from '../../../shared/atomic-file.mjs';
19
19
  import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
20
20
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
21
+ import { modelProxyClientMode, serverModelCredentialsDisabledError } from '../../../shared/model-proxy-mode.mjs';
21
22
  import {
22
23
  accountIdentityFields,
23
24
  decodeJwtPayload,
@@ -101,6 +102,7 @@ export function tokensFileMtimeMs() {
101
102
  }
102
103
 
103
104
  export function loadTokens() {
105
+ if (modelProxyClientMode()) return null;
104
106
  const ownPath = getOwnTokenPath();
105
107
  if (!existsSync(ownPath)) return null;
106
108
  try {
@@ -322,6 +324,7 @@ async function refreshUnderLock({ currentToken, force, reason, startingTokens })
322
324
  * the provider can hold off re-attempting until the expiry skew.
323
325
  */
324
326
  export async function refreshStoredTokens({ current = null, force = false, reason = 'preemptive' } = {}) {
327
+ if (modelProxyClientMode()) throw serverModelCredentialsDisabledError('OpenAI OAuth credentials');
325
328
  const currentToken = current?.access_token || null;
326
329
  const disk = loadTokens();
327
330
  const adopted = adoptableDiskTokens(currentToken, disk, force);
@@ -23,7 +23,7 @@ import { resolveOpenAiTransportPolicy } from './openai-transport-policy.mjs';
23
23
  import { applyOpenAIDirectCachePolicy } from './openai-direct-request.mjs';
24
24
  import { streamCallbacks } from './lib/send-callbacks.mjs';
25
25
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
26
- import { getAgentApiKey } from '../../../shared/provider-api-key.mjs';
26
+ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/provider-api-key.mjs';
27
27
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
28
28
  import { noteRequestServiceTier } from '../../../shared/llm/usage-context.mjs';
29
29
  import { resolveProviderCacheKey, resolveProviderPromptCacheLane } from '../agent-runtime/cache-strategy.mjs';
@@ -86,7 +86,7 @@ export class OpenAIDirectProvider {
86
86
  this.config = config || {};
87
87
  }
88
88
  _ensureKey() {
89
- const k = this.config.apiKey;
89
+ const k = resolveProviderApiKey('openai', this.config);
90
90
  if (!k) throw new Error('OPENAI_API_KEY not configured (providers.openai.apiKey)');
91
91
  return k;
92
92
  }
@@ -97,7 +97,7 @@ export class OpenAIDirectProvider {
97
97
  // the WS/HTTP transports take the key per-call via the `auth` object.
98
98
  reloadApiKey() {
99
99
  try {
100
- const newKey = getAgentApiKey('openai') || this.config.apiKey;
100
+ const newKey = reloadProviderApiKey('openai', this.config);
101
101
  if (newKey) {
102
102
  this.config = { ...(this.config || {}), apiKey: newKey };
103
103
  return newKey;
@@ -295,6 +295,6 @@ export class OpenAIDirectProvider {
295
295
  }
296
296
  }
297
297
  async isAvailable() {
298
- return !!this.config.apiKey;
298
+ return !!resolveProviderApiKey('openai', this.config);
299
299
  }
300
300
  }
@@ -6,6 +6,8 @@ import { createAccountPoolProvider } from './account-pool.mjs';
6
6
  import { isInclusiveProvider } from '../../../shared/llm/cost.mjs';
7
7
  import { noEnabledProvidersError } from './registry-errors.mjs';
8
8
  import { providerTakesNativePdf } from '../../../attachments/store.mjs';
9
+ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
10
+ import { getModelProxyChannel } from './model-proxy-channel.mjs';
9
11
  // OpenAI-compat provider names are self-declared by openai-compat-presets.mjs via
10
12
  // OPENAI_COMPAT_PRESETS. No parallel list maintained here.
11
13
  const providers = new Map();
@@ -27,6 +29,7 @@ const OAUTH_PROVIDER_NAMES = new Set([
27
29
  'openai-oauth',
28
30
  'grok-oauth',
29
31
  'muse-code-oauth',
32
+ 'command-code-oauth',
30
33
  'mimo-oauth',
31
34
  'cursor-oauth',
32
35
  'antigravity-oauth',
@@ -95,6 +98,28 @@ function configSignature(cfg) {
95
98
  }
96
99
  }
97
100
 
101
+ // Remote mode: a proxy provider sends through the channel it was built on. A
102
+ // headless boundary installs a fresh channel per run (model-proxy-bridge.mjs)
103
+ // and closes it afterwards, so the channel's identity is part of every reuse
104
+ // signature — an identical PC config behind a new channel is a rebuild, never
105
+ // a provider bound to a closed channel. Local mode: '' (signatures unchanged).
106
+ const _proxyChannelIds = new WeakMap();
107
+ let _proxyChannelSeq = 0;
108
+ function proxyChannelTag(channel) {
109
+ if (!channel) return '';
110
+ let id = _proxyChannelIds.get(channel);
111
+ if (!id) {
112
+ _proxyChannelSeq += 1;
113
+ id = _proxyChannelSeq;
114
+ _proxyChannelIds.set(channel, id);
115
+ }
116
+ return `\u0000model-proxy-channel:${id}`;
117
+ }
118
+ function reuseSignature(cfg, proxyChannel) {
119
+ const sig = configSignature(cfg);
120
+ return sig === null ? null : sig + proxyChannelTag(proxyChannel);
121
+ }
122
+
98
123
  function abortError(signal) {
99
124
  return signal?.reason instanceof Error
100
125
  ? signal.reason
@@ -146,6 +171,7 @@ const PROVIDER_MODULES = {
146
171
  'anthropic-oauth': ['./anthropic-oauth.mjs', 'AnthropicOAuthProvider'],
147
172
  'grok-oauth': ['./grok-oauth.mjs', 'GrokOAuthProvider'],
148
173
  'muse-code-oauth': ['./muse-code-oauth.mjs', 'MuseCodeOAuthProvider'],
174
+ 'command-code-oauth': ['./command-code-oauth.mjs', 'CommandCodeOAuthProvider'],
149
175
  'mimo-oauth': ['./mimo-oauth.mjs', 'MimoOAuthProvider'],
150
176
  'cursor-oauth': ['./cursor.mjs', 'CursorOAuthProvider'],
151
177
  'antigravity-oauth': ['./antigravity-oauth.mjs', 'AntigravityOAuthProvider'],
@@ -175,7 +201,13 @@ async function loadProviderCtor(name, signal = null, cfg = null) {
175
201
  throw new Error(`unknown enabled provider: ${name}`);
176
202
  }
177
203
 
178
- async function instantiateProvider(name, Ctor, cfg) {
204
+ async function instantiateProvider(name, Ctor, cfg, proxyChannel = null) {
205
+ // Remote mode: the client PC executes the call with its own credentials.
206
+ // Admission and usage accounting still wrap the daemon-side stand-in.
207
+ if (modelProxyClientMode()) {
208
+ const { createModelProxyProvider } = await import('./model-proxy-provider.mjs');
209
+ return wrapProviderAdmission(createModelProxyProvider(name, cfg, proxyChannel ?? getModelProxyChannel()), name);
210
+ }
179
211
  if (isCustomProviderConfig(name, cfg)) return wrapProviderAdmission(await Ctor(name, cfg), name);
180
212
  if (Object.hasOwn(OPENAI_COMPAT_PRESETS, name) && name !== 'opencode-go') {
181
213
  return wrapProviderAdmission(new Ctor(name, cfg), name);
@@ -185,7 +217,14 @@ async function instantiateProvider(name, Ctor, cfg) {
185
217
 
186
218
  export async function initProviders(config, { signal = null } = {}) {
187
219
  throwIfAborted(signal);
188
- const sig = configSignature(config);
220
+ // Remote mode: provider availability and config are the PC's, never this
221
+ // server's credential probes. No connected client fails here, clearly.
222
+ const proxyChannel = modelProxyClientMode() ? getModelProxyChannel() : null;
223
+ if (proxyChannel) {
224
+ const { loadModelProxyProviderConfig } = await import('./model-proxy-provider.mjs');
225
+ config = await awaitWithAbort(loadModelProxyProviderConfig({ channel: proxyChannel }), signal);
226
+ }
227
+ const sig = reuseSignature(config, proxyChannel);
189
228
  // Coalesce: an identical config is already mid-init — attach to it.
190
229
  if (sig !== null && _inFlightPromise && _inFlightSig === sig) {
191
230
  return awaitWithAbort(_inFlightPromise, signal);
@@ -199,7 +238,7 @@ export async function initProviders(config, { signal = null } = {}) {
199
238
  // signatures can never run their clear()+rebuild concurrently, regardless
200
239
  // of caller-side gating (agent-tool gateOnPrior may release a queued init
201
240
  // before the prior one settled). Errors do not poison the chain.
202
- const run = () => _initProvidersUnsynchronized(config, signal);
241
+ const run = () => _initProvidersUnsynchronized(config, signal, proxyChannel);
203
242
  const next = _initChain.then(run, run);
204
243
  _initChain = next.then(
205
244
  () => {},
@@ -228,7 +267,7 @@ export async function initProviders(config, { signal = null } = {}) {
228
267
  return awaitWithAbort(tracked, signal);
229
268
  }
230
269
 
231
- async function _initProvidersUnsynchronized(config, signal = null) {
270
+ async function _initProvidersUnsynchronized(config, signal = null, proxyChannel = null) {
232
271
  throwIfAborted(signal);
233
272
  // Invariant: never wipe the live registry based on an empty / all-disabled
234
273
  // config. Without this guard, a stale `loadAgentConfig()` (e.g. mid-reload
@@ -249,14 +288,15 @@ async function _initProvidersUnsynchronized(config, signal = null) {
249
288
  // Idempotent reuse: an enabled provider whose config signature is
250
289
  // unchanged from the live registry is carried forward as-is. Only
251
290
  // added or changed providers are (re)constructed below.
252
- const sig = configSignature(cfg);
291
+ const sig = reuseSignature(cfg, proxyChannel);
253
292
  if (sig !== null && providers.has(name) && signatures.get(name) === sig) {
254
293
  return { name, inst: providers.get(name), sig };
255
294
  }
256
295
  try {
257
- const Ctor = await loadProviderCtor(name, signal, cfg);
296
+ // A proxied provider never loads the real adapter module.
297
+ const Ctor = modelProxyClientMode() ? null : await loadProviderCtor(name, signal, cfg);
258
298
  throwIfAborted(signal);
259
- const inst = await instantiateProvider(name, Ctor, cfg);
299
+ const inst = await instantiateProvider(name, Ctor, cfg, proxyChannel);
260
300
  return { name, inst, sig };
261
301
  } catch (err) {
262
302
  const msg = err instanceof Error ? err.message : String(err);
@@ -30,6 +30,7 @@ import { setBuiltinInstalledInConfig, withGrandfatheredBuiltins } from '../../..
30
30
  const BROWSER_BRIDGE_DISCOVERY_FILE = 'browser-bridge.json';
31
31
  const COMPUTER_BRIDGE_DISCOVERY_FILE = 'computer-bridge.json';
32
32
  const TERMINAL_BRIDGE_DISCOVERY_FILE = 'terminal-bridge.json';
33
+ const VISUALIZE_BRIDGE_DISCOVERY_FILE = 'visualize-bridge.json';
33
34
 
34
35
  function bridgePresent(file) {
35
36
  try {
@@ -116,6 +117,14 @@ export function builtinFeatureActive(configLike, id) {
116
117
  if (id === 'terminal') {
117
118
  return featureEnvOverride('MIXDOG_FEATURE_TERMINAL') ?? bridgePresent(TERMINAL_BRIDGE_DISCOVERY_FILE);
118
119
  }
120
+ // Visualize shows artifacts in the desktop side panel: no install step, on
121
+ // unless the user turns it off, and only while the desktop bridge is up.
122
+ if (id === 'visualize') {
123
+ return (
124
+ featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ??
125
+ (moduleEnabled(configLike, 'visualize', true) && bridgePresent(VISUALIZE_BRIDGE_DISCOVERY_FILE))
126
+ );
127
+ }
119
128
  return false;
120
129
  }
121
130
 
@@ -148,6 +157,7 @@ export function featureDisallowedToolsFor(
148
157
  ...(builtinFeatureActive(configLike, 'office') ? [] : ['office']),
149
158
  ...(builtinFeatureActive(configLike, 'media') ? [] : ['media']),
150
159
  ...(builtinFeatureActive(configLike, 'tidy') ? [] : ['tidy']),
160
+ ...(builtinFeatureActive(configLike, 'visualize') ? [] : ['visualize']),
151
161
  ];
152
162
  // Headless exec uses the Lead surface and excludes Skill/MCP tools. When
153
163
  // only its eager defaults remain, neither schemas nor loader guidance help.
@@ -3,6 +3,7 @@
3
3
  import { clean, hasOwn } from './session-text.mjs';
4
4
  import { normalizeEffortInput, normalizeSavedEffort } from './effort.mjs';
5
5
  import { routeFastKey, fastPreferenceFor } from './model-capabilities.mjs';
6
+ import { normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
6
7
 
7
8
  const DEFAULT_PROVIDER = 'anthropic-oauth';
8
9
  export const DEFAULT_MODEL = '';
@@ -53,6 +54,14 @@ function cleanContextPercent(value) {
53
54
  return Math.max(10, Math.min(100, Math.round(percent / 10) * 10));
54
55
  }
55
56
 
57
+ /** The route's exact window in tokens: an explicit request, else none when a
58
+ * percent is chosen instead, else the saved or preset selection. */
59
+ function resolveContextTokens({ contextTokens, hasExplicitContextPercent, saved, preset = null }) {
60
+ if (contextTokens !== undefined) return normalizeContextTokens(contextTokens);
61
+ if (hasExplicitContextPercent) return undefined;
62
+ return normalizeContextTokens(saved.contextTokens ?? preset?.contextTokens);
63
+ }
64
+
56
65
  /** The route's fast flag: an explicit request, else the saved choice, else the preset or the provider preference. */
57
66
  function resolveFast({ hasExplicitFast, explicitFast, saved, presetFast = false, preference }) {
58
67
  if (hasExplicitFast) return explicitFast;
@@ -60,8 +69,19 @@ function resolveFast({ hasExplicitFast, explicitFast, saved, presetFast = false,
60
69
  return presetFast || preference();
61
70
  }
62
71
 
72
+ /** The route's Auto effort flag: an explicit request, else the saved choice,
73
+ * else the preset. Absent means on; only `false` turns it off. */
74
+ function resolveAutoEffort({ autoEffort, saved, preset = null }) {
75
+ if (autoEffort !== undefined) return autoEffort !== false;
76
+ if (hasOwn(saved, 'autoEffort')) return saved.autoEffort !== false;
77
+ return preset?.autoEffort !== false;
78
+ }
79
+
63
80
  export function makeResolveRoute(resolveDefaultProvider) {
64
- return function resolveRoute(config, { provider, model, effort, fast, modelParameters, contextPercent } = {}) {
81
+ return function resolveRoute(
82
+ config,
83
+ { provider, model, effort, fast, autoEffort, modelParameters, contextPercent, contextTokens } = {}
84
+ ) {
65
85
  const explicitProvider = clean(provider);
66
86
  const explicitModel = clean(model);
67
87
  const hasExplicitEffort = effort !== undefined;
@@ -70,6 +90,9 @@ export function makeResolveRoute(resolveDefaultProvider) {
70
90
  const explicitFast = fast === true;
71
91
  const hasExplicitModelParameters = modelParameters !== undefined;
72
92
  const hasExplicitContextPercent = contextPercent !== undefined;
93
+ // An exact token selection replaces a saved percent unless a percent is
94
+ // requested alongside it.
95
+ const keepsSavedPercent = contextTokens === undefined;
73
96
 
74
97
  if (!explicitProvider) {
75
98
  const preset = findPreset(config, explicitModel || config?.default);
@@ -78,6 +101,7 @@ export function makeResolveRoute(resolveDefaultProvider) {
78
101
  const m = clean(preset.model) || DEFAULT_MODEL;
79
102
  if (p && m) {
80
103
  const saved = modelSettingsFor(config, p, m);
104
+ const tokens = resolveContextTokens({ contextTokens, hasExplicitContextPercent, saved, preset });
81
105
  return {
82
106
  provider: p,
83
107
  model: m,
@@ -90,12 +114,16 @@ export function makeResolveRoute(resolveDefaultProvider) {
90
114
  presetFast: preset.fast === true,
91
115
  preference: () => fastPreferenceFor(config, p, m),
92
116
  }),
117
+ autoEffort: resolveAutoEffort({ autoEffort, saved, preset }),
93
118
  modelParameters: hasExplicitModelParameters
94
119
  ? cleanModelParameters(modelParameters)
95
120
  : cleanModelParameters(saved.modelParameters ?? preset.modelParameters),
96
121
  contextPercent: hasExplicitContextPercent
97
122
  ? cleanContextPercent(contextPercent)
98
- : cleanContextPercent(saved.contextPercent ?? preset.contextPercent),
123
+ : keepsSavedPercent
124
+ ? cleanContextPercent(saved.contextPercent ?? preset.contextPercent)
125
+ : undefined,
126
+ ...(tokens ? { contextTokens: tokens } : {}),
99
127
  };
100
128
  }
101
129
  }
@@ -104,18 +132,23 @@ export function makeResolveRoute(resolveDefaultProvider) {
104
132
  const p = explicitProvider || resolveDefaultProvider(config);
105
133
  const m = explicitModel || DEFAULT_MODEL;
106
134
  const saved = modelSettingsFor(config, p, m);
135
+ const tokens = resolveContextTokens({ contextTokens, hasExplicitContextPercent, saved });
107
136
  return {
108
137
  provider: p,
109
138
  model: m,
110
139
  preset: null,
111
140
  effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort),
112
141
  fast: resolveFast({ hasExplicitFast, explicitFast, saved, preference: () => fastPreferenceFor(config, p, m) }),
142
+ autoEffort: resolveAutoEffort({ autoEffort, saved }),
113
143
  modelParameters: hasExplicitModelParameters
114
144
  ? cleanModelParameters(modelParameters)
115
145
  : cleanModelParameters(saved.modelParameters),
116
146
  contextPercent: hasExplicitContextPercent
117
147
  ? cleanContextPercent(contextPercent)
118
- : cleanContextPercent(saved.contextPercent),
148
+ : keepsSavedPercent
149
+ ? cleanContextPercent(saved.contextPercent)
150
+ : undefined,
151
+ ...(tokens ? { contextTokens: tokens } : {}),
119
152
  };
120
153
  };
121
154
  }
@@ -1,6 +1,7 @@
1
1
  // Reasoning-effort catalogs and coercion. Pure helpers.
2
2
  import { clean } from './session-text.mjs';
3
3
  import { customEffortLevels, snapCustomEffort } from '../providers/custom-reasoning.mjs';
4
+ import { rejectedEffortLevelsSync } from '../providers/effort-rejection.mjs';
4
5
 
5
6
  const TOOL_MODES = new Set(['full', 'readonly', 'lead']);
6
7
  const ALL_EFFORT_LEVELS = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
@@ -15,22 +16,18 @@ const EFFORT_LABELS = {
15
16
  ultra: 'Ultra',
16
17
  };
17
18
 
19
+ // Last-resort ladders for routes whose endpoints publish no effort metadata
20
+ // (no per-model levels on /models, no same-endpoint catalog row). Every other
21
+ // provider's ladder comes from its own catalog rows (live roster, models.dev
22
+ // via provider-catalog-registry, seeds); a route with none offers no effort.
18
23
  const EFFORT_OPTIONS_BY_PROVIDER = {
19
24
  openai: ['none', 'low', 'medium', 'high', 'xhigh'],
20
- // gpt-5.6+ catalogs declare max/ultra; the openai-oauth transport folds
21
- // ultra -> max on the wire (openai-oauth.mjs _normalizeReasoningEffort).
22
- 'openai-oauth': ['none', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'],
23
- anthropic: ['low', 'medium', 'high', 'xhigh', 'max'],
24
- 'anthropic-oauth': ['low', 'medium', 'high', 'xhigh', 'max'],
25
25
  // xAI Grok 4.6+: low/medium/high/xhigh. Reasoning cannot be disabled.
26
26
  xai: ['low', 'medium', 'high', 'xhigh'],
27
- 'grok-oauth': ['low', 'medium', 'high', 'xhigh'],
28
- 'muse-code-oauth': ['minimal', 'low', 'medium', 'high', 'xhigh'],
29
27
  'mimo-oauth': ['minimal', 'low', 'medium', 'high'],
30
- // Antigravity carries the effort tier inside the wire model id
31
- // (gemini-3-pro-high / -low, claude-*-thinking), so the route exposes none.
32
- 'antigravity-oauth': [],
33
- 'opencode-go': ['high', 'max'],
28
+ // Command Code relays several vendors over Chat Completions; the common
29
+ // `reasoning_effort` levels are the fallback when no catalog row declares any.
30
+ 'command-code-oauth': ['low', 'medium', 'high'],
34
31
  };
35
32
  const EFFORT_BY_FAMILY = {
36
33
  opus: ['low', 'medium', 'high', 'xhigh', 'max'],
@@ -73,7 +70,14 @@ export function normalizeEffortInput(value) {
73
70
  return v;
74
71
  }
75
72
 
73
+ // Levels the endpoint has rejected for this route stop being offered.
76
74
  export function effortOptionsFor(provider, model) {
75
+ const options = catalogEffortOptions(provider, model);
76
+ const rejected = model?.id ? rejectedEffortLevelsSync(provider, model.id) : null;
77
+ return rejected?.size ? options.filter((level) => !rejected.has(level)) : options;
78
+ }
79
+
80
+ function catalogEffortOptions(provider, model) {
77
81
  // Custom-provider rows declare their levels authoritatively: an explicit []
78
82
  // (or no declaration) means none, with no family/provider fallback.
79
83
  if (model?.customReasoning) return customEffortLevels(model) || [];
@@ -1,11 +1,8 @@
1
1
  // extension-scopes.mjs — plugin-aware layer over the shared scope model. A
2
2
  // plugin's scope cascades to the skills it owns and to the MCP server(s) it
3
- // installed, so limiting the plugin limits everything it brought along.
4
- import { readFileSync, statSync } from 'node:fs';
5
- import { join } from 'node:path';
6
-
7
- import { pluginMcpServerName, pluginServerMatcher } from './plugin-mcp.mjs';
8
- import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
3
+ // declares, so limiting the plugin limits everything it brought along. MCP
4
+ // ownership comes from the tool's `mcpPluginId`, set when the plugin's server
5
+ // connected, never from the server name.
9
6
  import { loadConfig } from '../config.mjs';
10
7
  import {
11
8
  extensionAllowedForCwd,
@@ -28,66 +25,29 @@ export {
28
25
  withExtensionScope,
29
26
  } from '../../../shared/extension-scopes.mjs';
30
27
 
31
- /** Plugin id that installed MCP server `serverName`, or ''. Plugin servers are
32
- * named `plugin-<name>` or `plugin-<name>--<suffix>` (plugin-mcp.mjs). */
33
- export function pluginIdForMcpServer(serverName, plugins) {
34
- const name = clean(serverName);
35
- if (!name) return '';
36
- for (const plugin of Array.isArray(plugins) ? plugins : []) {
37
- if (pluginServerMatcher(pluginMcpServerName(plugin))(name)) return clean(plugin?.id || plugin?.name);
38
- }
39
- return '';
40
- }
41
-
42
- export function mcpServerAllowedForCwd(scopes, serverName, cwd, { plugins = [] } = {}) {
28
+ /** Server scope ∩ owning plugin's scope (`pluginId` from owner metadata). */
29
+ export function mcpServerAllowedForCwd(scopes, serverName, cwd, { pluginId = '' } = {}) {
43
30
  if (!extensionAllowedForCwd(scopes, 'mcp', serverName, cwd)) return false;
44
- const pluginId = pluginIdForMcpServer(serverName, plugins);
45
- if (pluginId && !extensionAllowedForCwd(scopes, 'plugins', pluginId, cwd)) return false;
31
+ const owner = clean(pluginId);
32
+ if (owner && !extensionAllowedForCwd(scopes, 'plugins', owner, cwd)) return false;
46
33
  return true;
47
34
  }
48
35
 
49
36
  /** Drop tools of MCP servers whose scope excludes cwd; non-MCP tools pass. */
50
- export function filterMcpToolsForCwd(tools, scopes, cwd, options = {}) {
37
+ export function filterMcpToolsForCwd(tools, scopes, cwd) {
51
38
  const list = Array.isArray(tools) ? tools : [];
52
39
  const verdicts = new Map();
53
40
  return list.filter((tool) => {
54
- const server = mcpServerNameOfTool(tool?.name);
41
+ // A name shortened for provider limits carries its server explicitly.
42
+ const server = mcpServerNameOfTool(tool);
55
43
  if (!server) return true;
56
- if (!verdicts.has(server)) verdicts.set(server, mcpServerAllowedForCwd(scopes, server, cwd, options));
57
- return verdicts.get(server);
44
+ const pluginId = clean(tool?.mcpPluginId);
45
+ const key = `${server}\0${pluginId}`;
46
+ if (!verdicts.has(key)) verdicts.set(key, mcpServerAllowedForCwd(scopes, server, cwd, { pluginId }));
47
+ return verdicts.get(key);
58
48
  });
59
49
  }
60
50
 
61
- // `{ id, name }` of every registered plugin, from <data>/plugins/registry.json.
62
- // mtime-cached: this runs on every session create and MCP catalog fold.
63
- let _registryCache = { path: '', mtime: -1, plugins: [] };
64
- export function registeredPluginIdentities(dataDir = null) {
65
- let path = '';
66
- try {
67
- path = join(dataDir || resolvePluginData(), 'plugins', 'registry.json');
68
- } catch {
69
- return [];
70
- }
71
- let mtime = -1;
72
- try {
73
- mtime = statSync(path).mtimeMs;
74
- } catch {
75
- return [];
76
- }
77
- if (_registryCache.path === path && _registryCache.mtime === mtime) return _registryCache.plugins;
78
- let plugins = [];
79
- try {
80
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
81
- plugins = (Array.isArray(parsed?.plugins) ? parsed.plugins : [])
82
- .map((entry) => ({ id: clean(entry?.id || entry?.name), name: clean(entry?.name || entry?.id) }))
83
- .filter((entry) => entry.id);
84
- } catch {
85
- plugins = [];
86
- }
87
- _registryCache = { path, mtime, plugins };
88
- return plugins;
89
- }
90
-
91
51
  /** The MCP tools a session at `cwd` may see. No scopes configured → input
92
52
  * returned as-is, so the global-only fast path costs one config read. */
93
53
  export function filterMcpToolsForSession(tools, cwd, config = null) {
@@ -95,5 +55,5 @@ export function filterMcpToolsForSession(tools, cwd, config = null) {
95
55
  if (!list.length) return list;
96
56
  const scopes = extensionScopesFromConfig(config || loadConfig({ secrets: false }));
97
57
  if (!hasExtensionScopes(scopes)) return list;
98
- return filterMcpToolsForCwd(list, scopes, cwd, { plugins: registeredPluginIdentities() });
58
+ return filterMcpToolsForCwd(list, scopes, cwd);
99
59
  }
@@ -19,7 +19,7 @@ import {
19
19
  stopActiveClock,
20
20
  validateObjective,
21
21
  } from './goal-state.mjs';
22
- import { assertExpectedGoal } from './goal-mutations.mjs';
22
+ import { assertExpectedGoal, goalFinished } from './goal-mutations.mjs';
23
23
  import { clean } from '../../../shared/clean.mjs';
24
24
 
25
25
  /** "12m remaining" for a budgeted Goal, "3m elapsed" for an open-ended one. */
@@ -109,6 +109,17 @@ const GOAL_CONTROL_ACTIONS = {
109
109
  const goal = await ctx.updateGoal(id, { status: 'complete' }, { user: true });
110
110
  return reply(action, goal, `Goal complete · ${goal.objective} · ${durationLabel(goal.timeUsedMs)} elapsed`);
111
111
  },
112
+ async dismiss(ctx, { id, goal, at, action }) {
113
+ // The user's acknowledgement of finished work: the same archive a new
114
+ // prompt applies, so the chrome lets go while the record stays.
115
+ if (!goalFinished(goal)) throw new Error('only a finished Goal can be dismissed; stop or complete it first');
116
+ if (!goal.archivedAt) {
117
+ goal.archivedAt = at;
118
+ goal.updatedAt = at;
119
+ await ctx.commit(id, goal);
120
+ }
121
+ return reply(action, null, `Goal dismissed · ${goal.objective}`);
122
+ },
112
123
  };
113
124
 
114
125
  export async function runGoalControl(ctx, sessionId, rawArgs = {}) {
@@ -146,7 +157,9 @@ export async function runGoalControl(ctx, sessionId, rawArgs = {}) {
146
157
  throw new Error('Goal changed while editing; reopen the editor before saving');
147
158
  }
148
159
  const at = ctx.now();
149
- if (goal.status === 'stopped') throw new Error('a stopped Goal cannot be changed; create a new Goal');
160
+ if (goal.status === 'stopped' && action !== 'dismiss') {
161
+ throw new Error('a stopped Goal cannot be changed; create a new Goal');
162
+ }
150
163
  const handler = GOAL_CONTROL_ACTIONS[action];
151
164
  if (!handler) throw new Error(`unknown Goal action: ${action}`);
152
165
  return handler(ctx, { id, goal, args, at, expectedGoalId, action });
@@ -6,6 +6,7 @@ import { clean, hasOwn } from './session-text.mjs';
6
6
  import { modelSupportsServiceTier } from '../providers/model-service-tiers.mjs';
7
7
  import { openAiDirectSupportsFast, openAiDirectSupportsUltrafast } from '../providers/openai-direct-request.mjs';
8
8
  import { supportsAnthropicFastMode } from '../../../shared/llm/anthropic-betas.mjs';
9
+ import { normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
9
10
 
10
11
  const FAST_CAPABLE_PROVIDERS = new Set([
11
12
  'anthropic',
@@ -123,6 +124,11 @@ export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfi
123
124
  else delete nextSetting.effort;
124
125
  if (fastCapable) nextSetting.fast = route.fast === true;
125
126
  else nextSetting.fast = false;
127
+ // Auto effort is on unless turned off: only the off state is stored.
128
+ if (hasOwn(route, 'autoEffort')) {
129
+ if (route.autoEffort === false) nextSetting.autoEffort = false;
130
+ else delete nextSetting.autoEffort;
131
+ }
126
132
  if (route.modelParameters && typeof route.modelParameters === 'object') {
127
133
  nextSetting.modelParameters = Object.fromEntries(
128
134
  Object.entries(route.modelParameters)
@@ -136,13 +142,22 @@ export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfi
136
142
  // pickers send the default stop with every selection; storing it froze each
137
143
  // model at whatever its default was that day, so a later default never
138
144
  // reached anyone who had already selected the model.
145
+ // An exact window in tokens replaces the percentage, and a percentage
146
+ // replaces it. A token selection at the default window reaches here already
147
+ // dropped (resolveRouteContextState answers no contextTokens for it).
148
+ const contextTokens = normalizeContextTokens(route.contextTokens);
139
149
  const requestedPercent = Number(route.contextPercent);
140
150
  const contextPercent = Math.round(requestedPercent / 10) * 10;
141
151
  const isDefault = contextPercent === Number(route.contextDefaultPercent);
142
- if (requestedPercent >= 10 && requestedPercent <= 100 && !isDefault) {
152
+ if (contextTokens) {
153
+ nextSetting.contextTokens = contextTokens;
154
+ delete nextSetting.contextPercent;
155
+ } else if (requestedPercent >= 10 && requestedPercent <= 100 && !isDefault) {
143
156
  nextSetting.contextPercent = contextPercent;
157
+ delete nextSetting.contextTokens;
144
158
  } else {
145
159
  delete nextSetting.contextPercent;
160
+ delete nextSetting.contextTokens;
146
161
  }
147
162
  modelSettings[key] = nextSetting;
148
163