mixdog 1.1.1 → 1.2.0

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 (317) hide show
  1. package/README.md +1 -0
  2. package/package.json +2 -2
  3. package/scripts/lib/run-node-tests.mjs +9 -0
  4. package/scripts/prune-desktop-runtime.mjs +3 -0
  5. package/scripts/release-paths.mjs +39 -2
  6. package/scripts/test-direct.mjs +4 -2
  7. package/src/defaults/shipped-history.json +6 -0
  8. package/src/defaults/skills/setup/references/actions.md +5 -2
  9. package/src/defaults/skills/visualize/SKILL.md +157 -0
  10. package/src/defaults/skills/visualize/references/craft.md +88 -0
  11. package/src/headless-command.mjs +56 -3
  12. package/src/headless-exec.mjs +17 -1
  13. package/src/headless-role.mjs +2 -2
  14. package/src/help.mjs +2 -0
  15. package/src/lib/rules-builder.cjs +0 -23
  16. package/src/rules/lead/LEAD.md +2 -2
  17. package/src/rules/shared/70-delivery.md +2 -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/config-presets.mjs +2 -0
  22. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
  23. package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
  24. package/src/runtime/agent/orchestrator/config.mjs +9 -1
  25. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +47 -7
  26. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +51 -8
  27. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +25 -5
  28. package/src/runtime/agent/orchestrator/mcp/client.mjs +348 -122
  29. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  31. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
  32. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +13 -1
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
  34. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
  35. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +12 -4
  37. package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
  38. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  40. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
  41. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
  43. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
  44. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +67 -0
  45. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +521 -0
  46. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
  47. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
  48. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
  49. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +3 -0
  50. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
  51. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
  53. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
  54. package/src/runtime/agent/orchestrator/providers/registry.mjs +15 -1
  55. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
  56. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
  57. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +12 -11
  58. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
  59. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +11 -0
  60. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +11 -8
  61. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
  62. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
  63. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
  64. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
  65. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +18 -11
  66. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
  67. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
  68. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
  69. package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
  70. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
  72. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
  73. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
  74. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -1
  75. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +1 -0
  76. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +17 -7
  77. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
  78. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -2
  79. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -0
  80. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +5 -3
  81. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
  82. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
  83. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
  84. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
  85. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
  86. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
  87. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
  88. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
  89. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
  90. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  91. package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
  92. package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
  93. package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
  94. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +29 -19
  95. package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
  96. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
  97. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
  98. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
  99. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +29 -0
  100. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +5 -1
  101. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +23 -4
  102. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
  103. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +28 -8
  104. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +140 -0
  105. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
  106. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
  107. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
  108. package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
  109. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +31 -12
  110. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
  111. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
  112. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
  113. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -66
  114. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
  115. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -1
  116. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
  117. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
  118. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
  119. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
  120. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
  121. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
  122. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
  123. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
  124. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
  125. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
  126. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  127. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -10
  128. package/src/runtime/attachments/store.mjs +38 -0
  129. package/src/runtime/channels/index.mjs +1 -5
  130. package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
  131. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
  132. package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
  133. package/src/runtime/channels/lib/worker-main.mjs +4 -34
  134. package/src/runtime/shared/atomic-file.mjs +8 -2
  135. package/src/runtime/shared/best-effort-stderr.mjs +8 -0
  136. package/src/runtime/shared/code-graph-state.mjs +12 -0
  137. package/src/runtime/shared/defaults-separation.mjs +2 -0
  138. package/src/runtime/shared/git-runner-worker.mjs +3 -2
  139. package/src/runtime/shared/git-runner.mjs +4 -3
  140. package/src/runtime/shared/llm/default-context-window.mjs +6 -0
  141. package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
  142. package/src/runtime/shared/llm/model-catalog.mjs +240 -118
  143. package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
  144. package/src/runtime/shared/llm/provider-catalog-registry.mjs +156 -0
  145. package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
  146. package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
  147. package/src/runtime/shared/model-proxy-mode.mjs +19 -0
  148. package/src/runtime/shared/provider-accounts.mjs +2 -2
  149. package/src/runtime/shared/provider-api-key.mjs +21 -1
  150. package/src/runtime/shared/schedule-model-ref.mjs +11 -2
  151. package/src/runtime/shared/semver.d.mts +1 -0
  152. package/src/runtime/shared/semver.mjs +57 -0
  153. package/src/runtime/shared/session-usage-totals.mjs +19 -0
  154. package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
  155. package/src/runtime/shared/singleton-owner.mjs +36 -6
  156. package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
  157. package/src/runtime/shared/tool-surface.mjs +9 -2
  158. package/src/runtime/shared/tool-work-units.mjs +14 -0
  159. package/src/runtime/shared/transcript-metadata.mjs +17 -1
  160. package/src/runtime/shared/turn-changes.mjs +791 -0
  161. package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
  162. package/src/runtime/shared/turn-snapshot.mjs +690 -81
  163. package/src/runtime/shared/turn-worktree-snapshot.mjs +161 -11
  164. package/src/runtime/shared/update-checker.mjs +2 -31
  165. package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
  166. package/src/runtime/shared/worktrees/contract.mjs +551 -0
  167. package/src/runtime/shared/worktrees/disk.mjs +57 -0
  168. package/src/runtime/shared/worktrees/git.mjs +247 -0
  169. package/src/runtime/shared/worktrees/include.mjs +480 -0
  170. package/src/runtime/shared/worktrees/lock.mjs +46 -0
  171. package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
  172. package/src/runtime/shared/worktrees/manager.mjs +1215 -0
  173. package/src/runtime/shared/worktrees/merge.mjs +182 -0
  174. package/src/runtime/shared/worktrees/owner.mjs +46 -0
  175. package/src/runtime/shared/worktrees/paths.mjs +62 -0
  176. package/src/runtime/shared/worktrees/registry.mjs +110 -0
  177. package/src/runtime/shared/worktrees/settings.mjs +84 -0
  178. package/src/runtime/shared/worktrees/slug.mjs +62 -0
  179. package/src/runtime/shared/worktrees/test-support.mjs +96 -0
  180. package/src/runtime/visualize-bridge/client.mjs +181 -0
  181. package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
  182. package/src/session-runtime/boot/apis.mjs +35 -0
  183. package/src/session-runtime/boot/begin.mjs +0 -2
  184. package/src/session-runtime/boot/config.mjs +2 -0
  185. package/src/session-runtime/boot/facade.mjs +20 -0
  186. package/src/session-runtime/boot/infrastructure.mjs +1 -0
  187. package/src/session-runtime/boot/session.mjs +1 -0
  188. package/src/session-runtime/boot/tools.mjs +9 -4
  189. package/src/session-runtime/config-lifecycle.mjs +10 -1
  190. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +11 -1
  191. package/src/session-runtime/global-extensions.mjs +2 -2
  192. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
  193. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
  194. package/src/session-runtime/internal-tool-executor.mjs +9 -1
  195. package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
  196. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
  197. package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
  198. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
  199. package/src/session-runtime/mcp-glue/connect.mjs +11 -6
  200. package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -3
  201. package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
  202. package/src/session-runtime/model-route/route-persist.mjs +3 -0
  203. package/src/session-runtime/model-route/set-route.mjs +15 -2
  204. package/src/session-runtime/notification-bus.mjs +9 -1
  205. package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
  206. package/src/session-runtime/resource-extension-sync.mjs +30 -7
  207. package/src/session-runtime/resource-mcp-api.mjs +27 -10
  208. package/src/session-runtime/route-resolution.mjs +10 -1
  209. package/src/session-runtime/route-state.mjs +16 -2
  210. package/src/session-runtime/runtime-bootstrap.mjs +0 -4
  211. package/src/session-runtime/runtime-core.mjs +5 -0
  212. package/src/session-runtime/runtime-facade.mjs +8 -0
  213. package/src/session-runtime/runtime-paths.mjs +0 -1
  214. package/src/session-runtime/runtime-review-api.mjs +9 -0
  215. package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
  216. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
  217. package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
  218. package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
  219. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
  220. package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
  221. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
  222. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
  223. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  224. package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
  225. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
  226. package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
  227. package/src/session-runtime/services/agent-tool.mjs +1 -0
  228. package/src/session-runtime/services/channel-worker.mjs +1 -1
  229. package/src/session-runtime/services/daemon-candidate.mjs +3 -2
  230. package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
  231. package/src/session-runtime/session-create/session-options.mjs +10 -1
  232. package/src/session-runtime/session-turn-api.mjs +1 -0
  233. package/src/session-runtime/settings-api.mjs +4 -2
  234. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
  235. package/src/session-runtime/settings-profile-api.mjs +27 -2
  236. package/src/session-runtime/settings-worktree-api.mjs +17 -0
  237. package/src/session-runtime/setup-tool/executor.mjs +11 -4
  238. package/src/session-runtime/setup-tool/settings-contract.mjs +3 -1
  239. package/src/session-runtime/setup-tool/tool-defs.mjs +31 -3
  240. package/src/session-runtime/starter-guide-state.mjs +62 -0
  241. package/src/session-runtime/turn/agent-ops.mjs +12 -1
  242. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +13 -3
  243. package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
  244. package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
  245. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
  246. package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
  247. package/src/session-runtime/turn/turn-run.mjs +6 -7
  248. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
  249. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
  250. package/src/session-runtime/workspace/admission-gate.mjs +197 -0
  251. package/src/session-runtime/workspace/project-branches.mjs +136 -0
  252. package/src/session-runtime/workspace/transition-steps.mjs +364 -0
  253. package/src/session-runtime/workspace/transitions.mjs +651 -0
  254. package/src/session-runtime/workspace/workspace-state.mjs +112 -0
  255. package/src/standalone/channel-binding.mjs +1 -2
  256. package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
  257. package/src/standalone/channel-transport/http-routes.mjs +17 -0
  258. package/src/standalone/channel-transport.mjs +13 -1
  259. package/src/standalone/daemon-channels-loader.mjs +2 -3
  260. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  261. package/src/standalone/daemon-owner.mjs +372 -0
  262. package/src/standalone/daemon-stored-session-views.mjs +4 -1
  263. package/src/standalone/daemon.mjs +139 -5
  264. package/src/standalone/desktop-service-registry.mjs +20 -0
  265. package/src/standalone/local-session-runtime.mjs +13 -78
  266. package/src/standalone/session-client/attachment-calls.mjs +3 -1
  267. package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
  268. package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
  269. package/src/standalone/session-client.mjs +104 -13
  270. package/src/standalone/session-protocol.mjs +17 -0
  271. package/src/standalone/session-proxy.mjs +3 -0
  272. package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
  273. package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
  274. package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
  275. package/src/standalone/session-service/projection/revisions.mjs +2 -0
  276. package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
  277. package/src/standalone/session-service/projection/workspace.mjs +14 -0
  278. package/src/standalone/session-service/session-calls/session-action.mjs +11 -3
  279. package/src/standalone/session-transport/call-route.mjs +13 -0
  280. package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
  281. package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
  282. package/src/standalone/session-transport/client-registry.mjs +20 -3
  283. package/src/standalone/session-transport/routes.mjs +79 -2
  284. package/src/standalone/session-transport/stack.mjs +2 -0
  285. package/src/standalone/session-transport.mjs +24 -1
  286. package/src/tui/app/message-selector.mjs +2 -1
  287. package/src/tui/app/model-options.mjs +35 -5
  288. package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
  289. package/src/tui/app/model-picker.mjs +4 -2
  290. package/src/tui/dist/index.mjs +332 -36
  291. package/src/tui/index.jsx +10 -2
  292. package/src/tui/session/compaction-view.mjs +147 -0
  293. package/src/tui/session/context-state.mjs +6 -0
  294. package/src/tui/session/initial-state.mjs +3 -0
  295. package/src/tui/session/quota-resume.mjs +2 -1
  296. package/src/tui/session/session-api/commands.mjs +10 -2
  297. package/src/tui/session/session-api/controls.mjs +4 -0
  298. package/src/tui/session/session-api/extensions.mjs +12 -8
  299. package/src/tui/session/session-api/intake/rewind.mjs +2 -1
  300. package/src/tui/session/session-api/integrations.mjs +18 -0
  301. package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
  302. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
  303. package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
  304. package/src/tui/session/session-api/routes.mjs +10 -1
  305. package/src/tui/session/session-api/settings.mjs +2 -0
  306. package/src/tui/session/session-api/shared.mjs +10 -6
  307. package/src/tui/session/session-api/worktree.mjs +60 -0
  308. package/src/tui/session/session-api.mjs +2 -0
  309. package/src/tui/session/session-flow/auto-clear.mjs +31 -12
  310. package/src/tui/session/session-flow/drain.mjs +15 -1
  311. package/src/tui/session/session-flow/session-reset.mjs +4 -3
  312. package/src/tui/session/session-flow.mjs +2 -0
  313. package/src/tui/session/transcript-intake.mjs +14 -5
  314. package/src/tui/session/turn-stream.mjs +32 -4
  315. package/src/tui/session/turn-tool-cards.mjs +10 -2
  316. package/src/tui/session/turn.mjs +39 -10
  317. package/src/runtime/channels/tool-defs.mjs +0 -17
@@ -30,6 +30,7 @@ const PROFILE_VALUE_IDS = Object.freeze({
30
30
  const ACTION_APPLIES_TO = {
31
31
  set_recap_enabled: 'background Memory cycles; no restart required',
32
32
  set_auto_update: 'subsequent automatic update checks',
33
+ set_worktree_settings: 'new tasks and worktrees created afterwards; existing worktrees are unchanged',
33
34
  set_system_shell: 'subsequent shell calls',
34
35
  save_automation: 'the automation worker; scheduled runs follow the saved timing',
35
36
  delete_automation: 'future automation runs',
@@ -93,6 +94,7 @@ function routeInput(source) {
93
94
  if (typeof source.fast === 'boolean') next.fast = source.fast;
94
95
  if (Object.hasOwn(source, 'modelParameters')) next.modelParameters = { ...source.modelParameters };
95
96
  if (Object.hasOwn(source, 'contextPercent')) next.contextPercent = source.contextPercent;
97
+ if (Object.hasOwn(source, 'contextTokens')) next.contextTokens = source.contextTokens;
96
98
  if (Object.hasOwn(source, 'disabled')) next.disabled = source.disabled;
97
99
  return next;
98
100
  }
@@ -178,6 +180,7 @@ const mainRoute = (rt) => ({
178
180
  fastCapable: rt.fastCapable === true,
179
181
  modelParameters: rt.modelParameters || {},
180
182
  contextPercent: rt.contextPercent ?? null,
183
+ contextTokens: rt.contextTokens ?? null,
181
184
  });
182
185
 
183
186
  async function automationStatus(rt, { domain }) {
@@ -303,6 +306,7 @@ const SETUP_STATUS_READERS = {
303
306
  plugins: (rt) => rt.pluginsStatus?.() || {},
304
307
  update: (rt) => rt.getUpdateSettings?.() || {},
305
308
  onboarding: (rt) => rt.getOnboardingStatus?.() || {},
309
+ worktree: (rt) => rt.getWorktreeSettings(),
306
310
  schedules: automationStatus,
307
311
  webhooks: automationStatus,
308
312
  hooks: hookStatus,
@@ -328,11 +332,13 @@ function validateSetupInput(args) {
328
332
  if (action !== 'set_agent_route' && Object.hasOwn(args.route, 'disabled')) {
329
333
  throw new Error('route.disabled is only accepted by set_agent_route');
330
334
  }
331
- if (action !== 'set_route' && action !== 'set_agent_route' && Object.hasOwn(args.route, 'contextPercent')) {
332
- throw new Error('route.contextPercent is only accepted by set_route and set_agent_route');
335
+ for (const field of ['contextPercent', 'contextTokens']) {
336
+ if (action !== 'set_route' && action !== 'set_agent_route' && Object.hasOwn(args.route, field)) {
337
+ throw new Error(`route.${field} is only accepted by set_route and set_agent_route`);
338
+ }
333
339
  }
334
340
  }
335
- for (const field of ['desktop', 'appearance', 'webhook', 'compaction', 'providerAccount']) {
341
+ for (const field of ['desktop', 'appearance', 'webhook', 'compaction', 'providerAccount', 'worktree']) {
336
342
  if (args[field] && !Object.keys(args[field]).length) throw new Error(`${field} requires at least one setting`);
337
343
  }
338
344
  return action;
@@ -507,6 +513,7 @@ const SETUP_ACTION_HANDLERS = {
507
513
  rt.deleteLocalProviderModel(requireText(args.confirmationToken, 'confirmationToken')),
508
514
  set_system_shell: (rt, args) => rt.setSystemShell({ command: clean(args.command) }),
509
515
  set_auto_update: (rt, args) => rt.setAutoUpdate(requireBoolean(args.enabled)),
516
+ set_worktree_settings: (rt, args) => rt.setWorktreeSettings(args.worktree),
510
517
  forget_provider_auth: async (rt, args) => {
511
518
  const name = requireText(args.name, 'name');
512
519
  const setup = await rt.getProviderSetup({});
@@ -541,7 +548,7 @@ const SETUP_ACTION_HANDLERS = {
541
548
  set_mcp_enabled: async (rt, args) => ({
542
549
  mcp: mcpRows(await rt.setMcpServerEnabled(requireText(args.name, 'name'), requireBoolean(args.enabled))),
543
550
  }),
544
- reconnect_mcp: async (rt) => ({ mcp: mcpRows(await rt.reconnectMcp()) }),
551
+ reconnect_mcp: async (rt, args) => ({ mcp: mcpRows(await rt.reconnectMcp(clean(args.name) || undefined)) }),
545
552
  set_disabled_skills: (rt, args) => {
546
553
  if (!Array.isArray(args.skills)) throw new Error('skills (array of names) is required');
547
554
  return rt.setDisabledSkills(args.skills.map(clean).filter(Boolean));
@@ -108,6 +108,8 @@ export const SETUP_MCP_SCHEMA = object({
108
108
  additionalProperties: removableText,
109
109
  description: 'HTTP header names mapped to existing environment variable names.',
110
110
  },
111
+ startupTimeoutSec: { type: 'number', description: 'Connect/handshake timeout in seconds (>0, default 30).' },
112
+ toolTimeoutSec: { type: 'number', description: 'Per tool-call timeout in seconds (>0, default 120).' },
111
113
  });
112
114
 
113
115
  export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
@@ -158,7 +160,7 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
158
160
  model: {
159
161
  ...text,
160
162
  description:
161
- 'Required for new schedules; no default-model fallback. Use provider/model[@effort][+fast][?parameter=value] or an existing preset name. On overwrite, omission preserves the saved model, which must be non-empty.',
163
+ 'Required for new schedules; no default-model fallback. Use provider/model[@effort][+fast][+noauto][?parameter=value] (+noauto: Auto reasoning off; absent = on) or an existing preset name. On overwrite, omission preserves the saved model, which must be non-empty.',
162
164
  },
163
165
  cwd: text,
164
166
  workflow: text,
@@ -6,6 +6,7 @@ import {
6
6
  } from './settings-contract.mjs';
7
7
  import { MEDIA_KINDS } from '../../runtime/media/tool-defs.mjs';
8
8
  import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-mode.mjs';
9
+ import { WORKTREE_NEW_TASK_DEFAULTS } from '../../runtime/shared/worktrees/settings.mjs';
9
10
 
10
11
  /** Built-in `setup` tool: the model's handle on a Mixdog user's persisted
11
12
  * configuration. The description is a contract only; how to use each action
@@ -31,6 +32,7 @@ export const SETUP_STATUS_DOMAINS = Object.freeze([
31
32
  'plugins',
32
33
  'update',
33
34
  'onboarding',
35
+ 'worktree',
34
36
  ...SETUP_EXTENDED_DOMAINS,
35
37
  ]);
36
38
 
@@ -100,7 +102,7 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
100
102
  save_mcp_server: 'server',
101
103
  remove_mcp_server: 'name',
102
104
  set_mcp_enabled: 'name enabled',
103
- reconnect_mcp: '',
105
+ reconnect_mcp: 'name?',
104
106
  set_disabled_skills: 'skills',
105
107
  set_extension_scope: 'kind name projects',
106
108
  add_plugin: 'source',
@@ -108,6 +110,7 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
108
110
  set_plugin_enabled: 'name enabled',
109
111
  remove_plugin: 'name',
110
112
  enable_plugin_mcp: 'name',
113
+ set_worktree_settings: 'worktree',
111
114
  ...SETUP_EXTENDED_ACTION_FIELDS,
112
115
  });
113
116
 
@@ -122,6 +125,7 @@ export const SETUP_BUILTIN_TOGGLE_FEATURES = Object.freeze([
122
125
  'browser',
123
126
  'computer',
124
127
  'voice',
128
+ 'visualize',
125
129
  ]);
126
130
 
127
131
  const ROUTE_SCHEMA = {
@@ -136,6 +140,11 @@ const ROUTE_SCHEMA = {
136
140
  // A typed enum is unrepresentable on Gemini; the bounds project everywhere
137
141
  // and the executor rejects values that are not a multiple of 10.
138
142
  contextPercent: { type: 'integer', minimum: 10, maximum: 100, description: 'A multiple of 10.' },
143
+ contextTokens: {
144
+ type: 'integer',
145
+ minimum: 1,
146
+ description: 'Exact context window in tokens; overrides contextPercent, clamped to 10%-100% of the model window.',
147
+ },
139
148
  disabled: {
140
149
  type: 'boolean',
141
150
  description: 'Agent routes only: true disables without losing the route; false re-enables.',
@@ -176,7 +185,7 @@ export const SETUP_TOOL_DEFS = Object.freeze([
176
185
  route: {
177
186
  ...ROUTE_SCHEMA,
178
187
  description:
179
- 'Partial route update; omitted fields are preserved. Agent or Web Search provider "" restores Main inheritance. contextPercent applies to Main and agent routes only.',
188
+ 'Partial route update; omitted fields are preserved. Agent or Web Search provider "" restores Main inheritance. contextPercent and contextTokens apply to Main and agent routes only.',
180
189
  },
181
190
  agent: { type: 'string', description: 'set_agent_route: agent id.' },
182
191
  workflow: { type: 'string', description: 'set_workflow: pack id.' },
@@ -222,7 +231,7 @@ export const SETUP_TOOL_DEFS = Object.freeze([
222
231
  },
223
232
  name: {
224
233
  type: 'string',
225
- description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled. revoke_linked_device: client id from status connection, not its display name; set_developer_option: option id from status developer; read_definition/delete_definition: the definition id; else MCP/plugin/provider id.`,
234
+ description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled. revoke_linked_device: client id from status connection, not its display name; set_developer_option: option id from status developer; read_definition/delete_definition: the definition id; reconnect_mcp: optional MCP server name (omit for all); else MCP/plugin/provider id.`,
226
235
  },
227
236
  accountId: {
228
237
  type: 'string',
@@ -320,6 +329,25 @@ export const SETUP_TOOL_DEFS = Object.freeze([
320
329
  items: { type: 'string', minLength: 1, pattern: '\\S' },
321
330
  description: 'set_extension_scope: required project roots; [] = every project.',
322
331
  },
332
+ worktree: {
333
+ type: 'object',
334
+ additionalProperties: false,
335
+ properties: {
336
+ newTaskDefault: {
337
+ type: 'string',
338
+ enum: [...WORKTREE_NEW_TASK_DEFAULTS],
339
+ description: 'branch = new tasks use the current branch (default); worktree = a new worktree.',
340
+ },
341
+ branchPrefix: { type: 'string', description: 'Ref prefix ending with "/"; default "mixdog/".' },
342
+ sizeWarnBytes: {
343
+ type: 'integer',
344
+ minimum: 0,
345
+ description: 'Repository size that asks for confirmation first; 0 never asks.',
346
+ },
347
+ autoCleanup: { type: 'boolean', description: 'Remove clean, merged worktrees when their task ends.' },
348
+ },
349
+ description: 'set_worktree_settings: only the given fields change.',
350
+ },
323
351
  ...SETUP_EXTENDED_PROPERTIES,
324
352
  },
325
353
  },
@@ -0,0 +1,62 @@
1
+ // The desktop "Getting started" guide state, kept in the config beside the
2
+ // onboarding flags (`onboarding.starterGuide`) so it outlives the renderer's
3
+ // localStorage. The desktop IPC gate (ipc-validation.ts) mirrors the schema.
4
+ export const STARTER_GUIDE_STEPS = Object.freeze(['provider', 'project', 'workflow', 'extensions', 'remote']);
5
+ const STARTER_GUIDE_KEYS = new Set(['closed', 'welcomed', 'visited']);
6
+
7
+ const plainObject = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
8
+
9
+ /** The guide state of a user who has not answered it yet. */
10
+ export function freshStarterGuide() {
11
+ return { closed: false, welcomed: false, visited: [] };
12
+ }
13
+
14
+ /** The stored copy, read tolerantly; null when there is none. */
15
+ export function storedStarterGuide(value) {
16
+ if (!plainObject(value)) return null;
17
+ const visited = Array.isArray(value.visited) ? value.visited : [];
18
+ return {
19
+ closed: value.closed === true,
20
+ welcomed: value.welcomed === true,
21
+ visited: STARTER_GUIDE_STEPS.filter((step) => visited.includes(step)),
22
+ };
23
+ }
24
+
25
+ /** A save request, read strictly: anything off-schema is rejected, never coerced. */
26
+ export function requiredStarterGuideState(value) {
27
+ if (!plainObject(value)) throw new TypeError('starter guide state must be an object.');
28
+ for (const key of Object.keys(value)) {
29
+ if (!STARTER_GUIDE_KEYS.has(key)) throw new TypeError(`starter guide state has an unknown field: ${key}.`);
30
+ }
31
+ if (typeof value.closed !== 'boolean' || typeof value.welcomed !== 'boolean') {
32
+ throw new TypeError('starter guide closed and welcomed must be booleans.');
33
+ }
34
+ if (
35
+ !Array.isArray(value.visited) ||
36
+ value.visited.length > STARTER_GUIDE_STEPS.length * 2 ||
37
+ value.visited.some((step) => !STARTER_GUIDE_STEPS.includes(step))
38
+ ) {
39
+ throw new TypeError('starter guide visited must list known steps.');
40
+ }
41
+ return storedStarterGuide(value);
42
+ }
43
+
44
+ /** Every field only moves forward: a stale save can add, never undo. */
45
+ export function mergeStarterGuide(stored, next) {
46
+ const base = stored || freshStarterGuide();
47
+ return {
48
+ closed: base.closed || next.closed,
49
+ welcomed: base.welcomed || next.welcomed,
50
+ visited: STARTER_GUIDE_STEPS.filter((step) => base.visited.includes(step) || next.visited.includes(step)),
51
+ };
52
+ }
53
+
54
+ /**
55
+ * The onboarding section after a completion: a first completion starts the
56
+ * guide fresh, so an absent copy afterwards means a profile onboarded before
57
+ * the guide was persisted here.
58
+ */
59
+ export function withStarterGuideOnCompletion(previous, onboarding) {
60
+ if (previous?.completed === true || storedStarterGuide(previous?.starterGuide)) return onboarding;
61
+ return { ...onboarding, starterGuide: freshStarterGuide() };
62
+ }
@@ -56,6 +56,17 @@ export function createAgentOps(deps) {
56
56
  });
57
57
  }
58
58
 
59
+ // Stop one background shell job of THIS session through the task tool's
60
+ // cancel path; the answer is structured so a surface never parses text.
61
+ async function stopShellJob(taskId) {
62
+ const session = getSession();
63
+ const { stopBackgroundShellJob } = await import('../../runtime/agent/orchestrator/tools/builtin/task-tool.mjs');
64
+ return stopBackgroundShellJob(taskId, {
65
+ callerSessionId: session?.id || null,
66
+ clientHostPid: session?.clientHostPid || process.pid,
67
+ });
68
+ }
69
+
59
70
  function onNotification(listener) {
60
71
  if (typeof listener !== 'function') return () => {};
61
72
  const sessionId = getSession()?.id || getReservedSessionId?.() || '';
@@ -94,5 +105,5 @@ export function createAgentOps(deps) {
94
105
  return { ...result, status: toolsStatus() };
95
106
  }
96
107
 
97
- return { agentControl, taskControl, onNotification, toolsStatus, selectTools };
108
+ return { agentControl, taskControl, stopShellJob, onNotification, toolsStatus, selectTools };
98
109
  }
@@ -55,18 +55,28 @@ export function createAskCallbacks({ transcript, notifyFnForSession, scheduleSea
55
55
  };
56
56
  }
57
57
 
58
+ // The turn's change-record key rides the caller's transcript metadata (in
59
+ // place: the stream stamps assistantAt on this same object), so the
60
+ // persisted user row and completion carry it across restore.
61
+ function stampCheckpoint(options) {
62
+ const meta = options.transcriptMeta;
63
+ const checkpointId = String(options.id || '').trim();
64
+ if (checkpointId && meta && typeof meta === 'object') meta.checkpointId = checkpointId;
65
+ return meta;
66
+ }
67
+
58
68
  function askCallbacks(turn, session0, options) {
59
69
  return {
60
70
  beforeToolExecution: () => turn.snapshot.promise || Promise.resolve(),
61
- transcriptMeta: options.transcriptMeta,
71
+ transcriptMeta: stampCheckpoint(options),
62
72
  promptSource: options.promptSource,
63
73
  retryFailedTurn: options.retryFailedTurn === true,
64
74
  onTextDelta: options.onTextDelta,
65
75
  onTextReset: options.onTextReset,
66
76
  onReasoningDelta: options.onReasoningDelta,
67
- onAssistantText: (text) => {
77
+ onAssistantText: (text, detail) => {
68
78
  transcript.appendAssistantText(text);
69
- return options.onAssistantText?.(text);
79
+ return options.onAssistantText?.(text, detail);
70
80
  },
71
81
  onUsageDelta: options.onUsageDelta,
72
82
  onAssistantToolCallObserved: options.onAssistantToolCallObserved,
@@ -1,25 +1,21 @@
1
1
  import { settleWithin } from '../../../runtime/shared/abort-race.mjs';
2
2
 
3
- // Worktree snapshot for the turn review. Captured immediately, but never on
4
- // the first-token critical path: every actual tool execution joins the same
5
- // promise, so shell/apply_patch cannot mutate the worktree before the baseline.
3
+ // Worktree snapshot for the turn review. Captured as soon as the turn's
4
+ // workspace is final (after any lazy worktree provisioning, so the baseline is
5
+ // taken in the root the turn runs in), but never on the first-token critical
6
+ // path: every actual tool execution joins the same promise, so
7
+ // shell/apply_patch cannot mutate the worktree before the baseline.
6
8
  export function createSnapshotTrackerFactory({
7
9
  getCurrentCwd,
8
10
  beginTurnSnapshotForTurn,
9
- cancelTurnSnapshotForTurn,
10
11
  completeTurnSnapshotForTurn,
11
12
  turnCleanupSettleMs,
12
13
  }) {
13
14
  return function createSnapshotTracker(options) {
15
+ // `promise` resolves to the turn token beginTurnSnapshot returns
16
+ // (`{ generation, checkpointId }`), or undefined when nothing began.
14
17
  const tracker = { sessionId: null, promise: null };
15
- // A completion wake continues the user turn whose review already exists;
16
- // aborting the wake seals that review instead of discarding it.
17
18
  const continueTurn = options.continueTurn === true;
18
- const cancel = () => {
19
- try {
20
- cancelTurnSnapshotForTurn(tracker.sessionId);
21
- } catch {}
22
- };
23
19
  tracker.start = (sessionId) => {
24
20
  const id = typeof sessionId === 'string' ? sessionId.trim() : '';
25
21
  if (!id || (tracker.promise && tracker.sessionId === id)) return;
@@ -36,19 +32,28 @@ export function createSnapshotTrackerFactory({
36
32
  .catch(() => undefined);
37
33
  };
38
34
  tracker.finish = async (signal, awaitTurn) => {
35
+ // A turn refused before its workspace was final never began a review.
36
+ if (!tracker.promise) return;
37
+ // Captured now: a later start (or the next turn) cannot redirect this seal.
38
+ const sessionId = tracker.sessionId;
39
+ const started = Promise.resolve(tracker.promise);
39
40
  if (signal.aborted && !continueTurn) {
40
- cancel();
41
- void Promise.resolve(tracker.promise).catch(() => {});
41
+ // A cancelled turn keeps its edits as a change record. Sealing waits
42
+ // for the snapshot start, never for the abort's settlement, and only
43
+ // seals the generation this turn began: delayed cleanup cannot seal
44
+ // a newer turn.
45
+ void started
46
+ .then((token) => (token ? completeTurnSnapshotForTurn(sessionId, token) : undefined))
47
+ .catch(() => {});
42
48
  return;
43
49
  }
44
- const cleanup = Promise.resolve(tracker.promise).then(() => completeTurnSnapshotForTurn(tracker.sessionId));
50
+ const cleanup = started.then((token) => completeTurnSnapshotForTurn(sessionId, token));
45
51
  cleanup.catch(() => {});
46
52
  try {
47
53
  await awaitTurn(() => settleWithin(cleanup, turnCleanupSettleMs));
48
54
  } catch {
49
55
  /* optional review cleanup never overrides turn settlement */
50
56
  }
51
- if (signal.aborted && !continueTurn) cancel();
52
57
  };
53
58
  return tracker;
54
59
  };
@@ -1,13 +1,11 @@
1
1
  import { runAbortable } from '../../../runtime/shared/abort-race.mjs';
2
2
 
3
3
  // Opens one turn: abort controller registration, timing, the worktree
4
- // snapshot tracker, the active-turn count and the one-shot heavy-runtime
5
- // warmup arm.
4
+ // snapshot tracker (started by session preparation once the turn's workspace
5
+ // is final), the active-turn count and the one-shot heavy-runtime warmup arm.
6
6
  export function createTurnOpener({
7
7
  createTiming,
8
8
  createSnapshot,
9
- getSession,
10
- getReservedSessionId,
11
9
  registerActiveTurnController,
12
10
  getActiveTurnCount,
13
11
  setActiveTurnCount,
@@ -41,7 +39,6 @@ export function createTurnOpener({
41
39
  const signal = controller.signal;
42
40
  const snapshot = createSnapshot(options);
43
41
  const routeStartedAt = performance.now();
44
- snapshot.start(getSession()?.id || getReservedSessionId?.());
45
42
  setActiveTurnCount(getActiveTurnCount() + 1);
46
43
  return {
47
44
  signal,
@@ -54,6 +51,8 @@ export function createTurnOpener({
54
51
  armHeavyRuntimeWarmup: heavyRuntimeWarmupArm(),
55
52
  session0: null,
56
53
  releaseFirstTitle: null,
54
+ /** Set by turn admission (admitTurnWorkspace); released by settleTurn. */
55
+ releaseWorkspace: null,
57
56
  startedAt: Date.now(),
58
57
  };
59
58
  };
@@ -21,6 +21,8 @@ export function createTurnPreparation({
21
21
  agentTool,
22
22
  getConfig,
23
23
  assertSessionAdmission,
24
+ prepareTurnWorkspace,
25
+ admitTurnWorkspace,
24
26
  }) {
25
27
  const scopedMcpTools = (session) => scopedMcpToolsFor(session, getConfig());
26
28
 
@@ -84,7 +86,7 @@ export function createTurnPreparation({
84
86
  // Historical-session resume publishes its transcript immediately while
85
87
  // provider/model metadata initializes in the background. Only the next
86
88
  // actual turn waits, guaranteeing that it cannot run on a stale route.
87
- async function prepareSession(turn, prompt) {
89
+ async function prepareSession(turn, prompt, options = {}) {
88
90
  // A closing/closed addressed runtime admits no turn (checked again after
89
91
  // every await that could have outlived a close).
90
92
  assertSessionAdmission?.();
@@ -96,6 +98,32 @@ export function createTurnPreparation({
96
98
  }
97
99
  throwIfAborted(turn.signal);
98
100
  assertSessionAdmission?.();
101
+ // Turn admission: waits while this session's workspace transitions or
102
+ // another session changes the checkout this session works in, then holds
103
+ // the turn registered (released by settleTurn) so no checkout changes
104
+ // under it.
105
+ if (typeof admitTurnWorkspace === 'function') {
106
+ turn.releaseWorkspace = await admitTurnWorkspace({ signal: turn.signal });
107
+ throwIfAborted(turn.signal);
108
+ assertSessionAdmission?.();
109
+ }
110
+ // Workspace gate: a session in recovery refuses the turn, and a pending
111
+ // worktree intent is provisioned and applied (cwd, record, prompt context)
112
+ // BEFORE the user row, the review snapshot, hooks, the provider and any
113
+ // worker spawn. The submission id is the provisioning transaction id, so a
114
+ // retried or duplicate send reuses the same worktree.
115
+ if (typeof prepareTurnWorkspace === 'function') {
116
+ await turn.awaitTurn(() =>
117
+ prepareTurnWorkspace({
118
+ submissionId: String(options.id || '').trim(),
119
+ prompt,
120
+ signal: turn.signal,
121
+ confirmLarge: options.confirmLarge === true,
122
+ })
123
+ );
124
+ throwIfAborted(turn.signal);
125
+ assertSessionAdmission?.();
126
+ }
99
127
  ensureSessionTranscriptWriter?.();
100
128
  transcript.appendUser(prompt);
101
129
  const session0 = getSession();
@@ -84,7 +84,12 @@ export function createTurnSettlement({
84
84
  turn.releaseFirstTitle?.();
85
85
  turn.emitTiming(turn.timing.status);
86
86
  turn.armHeavyRuntimeWarmup('turn-settled');
87
- await turn.snapshot.finish(turn.signal, turn.awaitTurn);
87
+ try {
88
+ await turn.snapshot.finish(turn.signal, turn.awaitTurn);
89
+ } finally {
90
+ // The turn no longer works in its checkout: transitions may proceed.
91
+ turn.releaseWorkspace?.();
92
+ }
88
93
  try {
89
94
  turn.unregister?.();
90
95
  } catch {}
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * src/session-runtime/turn/turn-run.mjs - one user turn end to end: viewer
3
- * forwarding, turn open (abort controller, timing, worktree snapshot, warmup
4
- * arming), session preparation, the askSession callback bridge, and the
5
- * settlement that always runs. The phases live under turn-run/.
3
+ * forwarding, turn open (abort controller, timing, warmup arming), session
4
+ * preparation (workspace provisioning, then the worktree review snapshot), the
5
+ * askSession callback bridge, and the settlement that always runs. The phases
6
+ * live under turn-run/.
6
7
  */
7
- import { beginTurnSnapshot, cancelTurnSnapshot, completeTurnSnapshot } from '../../runtime/shared/turn-snapshot.mjs';
8
+ import { beginTurnSnapshot, completeTurnSnapshot } from '../../runtime/shared/turn-snapshot.mjs';
8
9
  import { runWithCwdOverride } from '../../runtime/shared/user-cwd.mjs';
9
10
  import { createTurnTranscript } from './turn-run/transcript.mjs';
10
11
  import { createTurnTimingFactory } from './turn-run/turn-timing.mjs';
@@ -23,7 +24,6 @@ export function createTurnRunner(deps) {
23
24
  endComputerExecution = async () => false,
24
25
  deferComputerSessionRelease = () => false,
25
26
  beginTurnSnapshotForTurn = beginTurnSnapshot,
26
- cancelTurnSnapshotForTurn = cancelTurnSnapshot,
27
27
  completeTurnSnapshotForTurn = completeTurnSnapshot,
28
28
  turnCleanupSettleMs = 2_000,
29
29
  getConfig = () => null,
@@ -35,7 +35,6 @@ export function createTurnRunner(deps) {
35
35
  createSnapshot: createSnapshotTrackerFactory({
36
36
  getCurrentCwd,
37
37
  beginTurnSnapshotForTurn,
38
- cancelTurnSnapshotForTurn,
39
38
  completeTurnSnapshotForTurn,
40
39
  turnCleanupSettleMs,
41
40
  }),
@@ -93,7 +92,7 @@ export function createTurnRunner(deps) {
93
92
  if (attachedSession?.remoteAttached) return forwardAttachedPrompt(attachedSession, prompt, options);
94
93
  const turn = openTurn(options);
95
94
  try {
96
- const session0 = await prepareSession(turn, prompt);
95
+ const session0 = await prepareSession(turn, prompt, options);
97
96
  const turnContext = await dispatchPromptSubmit(turn, session0, prompt, options);
98
97
  const result = await mgr.askSession(
99
98
  session0.id,
@@ -3,7 +3,7 @@ import { hasOwn } from '../../runtime/shared/object.mjs';
3
3
  import { isHiddenAgent } from '../../runtime/agent/orchestrator/internal-agents.mjs';
4
4
  import { normalizeWorkflowRoute } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
5
5
  import { ensureProviderEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
6
- import { selectionFastCapable } from '../route-state.mjs';
6
+ import { resolveRouteContextState, selectionFastCapable } from '../route-state.mjs';
7
7
  import { canonicalizeAgentRouteStorage, withAgentDisabled } from '../../runtime/shared/agent-route-config.mjs';
8
8
  import { agentEditorId } from './shared.mjs';
9
9
 
@@ -43,19 +43,31 @@ export function createAgentRouteApi(deps) {
43
43
  clean(selectedRoute.provider) === clean(stored.provider) && clean(selectedRoute.model) === clean(stored.model);
44
44
  const inheritedEffort = sameModel ? stored.effort || null : null;
45
45
  const inheritedModelParameters = sameModel ? stored.modelParameters || {} : {};
46
- const inheritedContextPercent = sameModel ? stored.contextPercent : undefined;
46
+ // A requested percent replaces the stored exact window and requested
47
+ // tokens replace the stored percent.
48
+ const requestsPercent = requested.contextPercent !== undefined;
49
+ const requestsTokens = requested.contextTokens !== undefined;
50
+ const inheritedContextPercent = sameModel && !requestsTokens ? stored.contextPercent : undefined;
51
+ const inheritedContextTokens = sameModel && !requestsPercent ? stored.contextTokens : undefined;
47
52
  selectedRoute = {
48
53
  ...selectedRoute,
49
54
  effort: requested.effort !== undefined ? selectedRoute.effort : inheritedEffort,
50
55
  fast: requested.fast !== undefined ? selectedRoute.fast === true : sameModel && stored.fast === true,
56
+ // Main's modelSettings bucket must not decide an agent's Auto effort.
57
+ autoEffort:
58
+ requested.autoEffort !== undefined ? requested.autoEffort !== false : !sameModel || stored.autoEffort !== false,
51
59
  modelParameters:
52
60
  requested.modelParameters !== undefined ? selectedRoute.modelParameters : inheritedModelParameters,
53
61
  // resolveRoute reads contextPercent from Main's modelSettings bucket;
54
62
  // an agent route owns its own.
55
- contextPercent: requested.contextPercent !== undefined ? requested.contextPercent : inheritedContextPercent,
63
+ contextPercent: requestsPercent ? requested.contextPercent : inheritedContextPercent,
64
+ contextTokens: requestsTokens ? requested.contextTokens : inheritedContextTokens,
56
65
  };
57
66
  await ensureProvidersReady(ensureProviderEnabled(getConfig(), selectedRoute.provider));
58
67
  const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
68
+ // Stored clamped, and not at all at the default window, as for Main.
69
+ const contextState = resolveRouteContextState(selectedRoute, modelMeta);
70
+ if (contextState.contextDefaultPercent !== undefined) selectedRoute.contextTokens = contextState.contextTokens;
59
71
  const fastCapable = selectionFastCapable(
60
72
  selectedRoute.provider,
61
73
  modelMeta,
@@ -12,6 +12,7 @@ import {
12
12
  } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
13
13
  import { ONBOARDING_VERSION } from '../quick-web-search-models.mjs';
14
14
  import { canonicalizeAgentRouteStorage } from '../../runtime/shared/agent-route-config.mjs';
15
+ import { withStarterGuideOnCompletion } from '../starter-guide-state.mjs';
15
16
  import { resolveDataDir } from './shared.mjs';
16
17
 
17
18
  // Onboarding slot picks: lead → default preset, agent → worker route,
@@ -91,12 +92,12 @@ export function createOnboardingApi(deps) {
91
92
  ]);
92
93
  nextConfig.agents = applyAgentRouteInput(agentInput, draft.agentRoutes, configurableIds);
93
94
  }
94
- nextConfig.onboarding = {
95
+ nextConfig.onboarding = withStarterGuideOnCompletion(config.onboarding, {
95
96
  ...(nextConfig.onboarding || {}),
96
97
  completed: true,
97
98
  version: ONBOARDING_VERSION,
98
99
  completedAt: new Date().toISOString(),
99
- };
100
+ });
100
101
  if (payload.webSearchRoute) {
101
102
  const webSearchToSave = webSearchRouteToSave(payload.webSearchRoute);
102
103
  if (webSearchToSave) nextConfig.webSearchRoute = webSearchToSave;