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
@@ -20,6 +20,7 @@ function bootParams({
20
20
  fast,
21
21
  modelParameters,
22
22
  contextPercent,
23
+ contextTokens,
23
24
  selectedContextWindow,
24
25
  cwd = process.cwd(),
25
26
  toolMode = 'full',
@@ -30,6 +31,7 @@ function bootParams({
30
31
  initialConfig = null,
31
32
  desktopSession = null,
32
33
  sessionProfile = null,
34
+ worktree = null,
33
35
  executeAgentControl = null,
34
36
  } = {}) {
35
37
  return {
@@ -39,6 +41,7 @@ function bootParams({
39
41
  fast,
40
42
  modelParameters,
41
43
  contextPercent,
44
+ contextTokens,
42
45
  selectedContextWindow,
43
46
  cwd,
44
47
  toolMode,
@@ -49,6 +52,8 @@ function bootParams({
49
52
  initialConfig,
50
53
  desktopSession,
51
54
  sessionProfile,
55
+ // Creation-time worktree intent (workspace/transitions.mjs creationIntent).
56
+ worktree,
52
57
  executeAgentControl,
53
58
  };
54
59
  }
@@ -74,6 +74,10 @@ export function createRuntimeFacade({
74
74
  get fast() {
75
75
  return state.route.fast === true;
76
76
  },
77
+ // The route's Auto effort switch; absent means on.
78
+ get autoEffort() {
79
+ return state.route.autoEffort !== false;
80
+ },
77
81
  get fastCapable() {
78
82
  return state.route.fastCapable === true;
79
83
  },
@@ -94,6 +98,10 @@ export function createRuntimeFacade({
94
98
  // Picker preference for a new/inherited session, not the live gauge.
95
99
  return state.route?.contextPercent ?? state.session?.contextPercent ?? null;
96
100
  },
101
+ get contextTokens() {
102
+ // Exact window preference in tokens; null when the route uses a percent stop.
103
+ return state.route ? state.route.contextTokens || null : state.session?.contextTokens || null;
104
+ },
97
105
  get rawContextWindow() {
98
106
  return state.session?.rawContextWindow || state.session?.contextWindow || null;
99
107
  },
@@ -6,7 +6,6 @@ export const RUNTIME = '../runtime/agent/orchestrator';
6
6
  export const WEB_SEARCH_RUNTIME = '../runtime/web-search/index.mjs';
7
7
  export const WEB_SEARCH_TOOL_DEFS = '../runtime/web-search/tool-defs.mjs';
8
8
  export const MEMORY_TOOL_DEFS = '../runtime/memory/tool-defs.mjs';
9
- export const CHANNEL_TOOL_DEFS = '../runtime/channels/tool-defs.mjs';
10
9
  export const CODE_GRAPH_TOOL_DEFS = '../runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs';
11
10
  export const CODE_GRAPH_RUNTIME = '../runtime/agent/orchestrator/tools/code-graph.mjs';
12
11
  export const STATUSLINE_SESSION_ROUTES = '../vendor/statusline/src/gateway/session-routes.mjs';
@@ -2,7 +2,10 @@ import { registerCacheInvalidationListener } from '../runtime/agent/orchestrator
2
2
  import { turnReviewReply } from '../runtime/shared/turn-review-reply.mjs';
3
3
  import {
4
4
  getSessionReviewDiff,
5
+ getTurnChanges,
5
6
  getTurnReviewDiff,
7
+ reapplyTurnChanges,
8
+ revertTurnChanges,
6
9
  revertTurnReview,
7
10
  revertTurnReviewFile,
8
11
  } from '../runtime/shared/turn-snapshot.mjs';
@@ -22,5 +25,11 @@ export function createRuntimeReviewApi({ getCwd, getSessionId }) {
22
25
  getSessionReviewDiff: () => getSessionReviewDiff(getCwd(), getSessionId()),
23
26
  revertTurnReview: (checkpointId) => revertTurnReview(getCwd(), getSessionId(), checkpointId),
24
27
  revertTurnReviewFile: (file, checkpointId) => revertTurnReviewFile(getCwd(), getSessionId(), file, checkpointId),
28
+ // Historical per-turn changes, addressed by checkpointId (TurnChangesReply).
29
+ getTurnChanges: (options = {}) => getTurnChanges(getSessionId(), options),
30
+ revertTurnChanges: (checkpointId, file, revision) =>
31
+ revertTurnChanges(getSessionId(), checkpointId, file, revision),
32
+ reapplyTurnChanges: (checkpointId, file, revision) =>
33
+ reapplyTurnChanges(getSessionId(), checkpointId, file, revision),
25
34
  };
26
35
  }
@@ -12,6 +12,7 @@ import {
12
12
  import { PRESET_ALIASES } from './tool-def.mjs';
13
13
  import { positiveInt } from '../../../runtime/shared/numbers.mjs';
14
14
  import { clean } from '../../../runtime/shared/clean.mjs';
15
+ import { normalizeContextTokens } from '../../../runtime/shared/llm/default-context-window.mjs';
15
16
 
16
17
  export { positiveInt };
17
18
 
@@ -220,10 +221,14 @@ export function normalizeAgentRoute(routeLike) {
220
221
  model,
221
222
  effort: clean(routeLike?.effort) || undefined,
222
223
  fast: routeLike?.fast === true,
224
+ ...(routeLike?.autoEffort === false ? { autoEffort: false } : {}),
223
225
  ...(routeLike?.modelParameters && typeof routeLike.modelParameters === 'object'
224
226
  ? { modelParameters: { ...routeLike.modelParameters } }
225
227
  : {}),
226
228
  ...(Number(routeLike?.contextPercent) > 0 ? { contextPercent: Number(routeLike.contextPercent) } : {}),
229
+ ...(normalizeContextTokens(routeLike?.contextTokens)
230
+ ? { contextTokens: normalizeContextTokens(routeLike.contextTokens) }
231
+ : {}),
227
232
  };
228
233
  }
229
234
 
@@ -3,6 +3,7 @@
3
3
  // progress, filtered by default to workers that can still do work.
4
4
  import { ACTIVE_STAGES } from '../tool-def.mjs';
5
5
  import { isTerminalWorkerStatus } from '../worker-rows.mjs';
6
+ import { sessionUsageTotals } from '../../../../runtime/shared/session-usage-totals.mjs';
6
7
 
7
8
  // Idle, closed, errored and reaped/unknown rows are history and stay reachable
8
9
  // through the task section (status/read keep their terminal result).
@@ -36,6 +37,7 @@ function agentRow({ tag, session, runtime, progress, now, indexRow = null }) {
36
37
  preset: session.presetName || null,
37
38
  effort: session.effort || null,
38
39
  fast: session.fast === true,
40
+ ...(session.autoEffort === false ? { autoEffort: false } : {}),
39
41
  // Additive: route model parameters (e.g. Daybreak Blue) when the route has any.
40
42
  ...(session.modelParameters && Object.keys(session.modelParameters).length
41
43
  ? { modelParameters: { ...session.modelParameters } }
@@ -51,6 +53,7 @@ function agentRow({ tag, session, runtime, progress, now, indexRow = null }) {
51
53
  clientHostPid: session.clientHostPid || null,
52
54
  lastStreamDeltaAt: runtime?.lastStreamDeltaAt ? new Date(runtime.lastStreamDeltaAt).toISOString() : null,
53
55
  staleSeconds: runtime?.lastStreamDeltaAt ? Math.floor((now - runtime.lastStreamDeltaAt) / 1000) : null,
56
+ ...sessionUsageTotals(session),
54
57
  windowTokens: Number(session.lastContextTokens ?? session.lastInputTokens) || 0,
55
58
  windowCap: Number(session.contextWindow) || null,
56
59
  permission: session.permission || null,
@@ -69,6 +69,7 @@ export function createJobLookup({ mgr, tags, refreshTagsFromSessions }) {
69
69
  model: session.model || null,
70
70
  effort: session.effort || null,
71
71
  fast: session.fast === true,
72
+ ...(session.autoEffort === false ? { autoEffort: false } : {}),
72
73
  },
73
74
  };
74
75
  }
@@ -16,6 +16,7 @@ export function preparedSpawnMeta(prepared, extras = {}) {
16
16
  model: prepared.preset.model,
17
17
  effort: prepared.preset.effort || null,
18
18
  fast: prepared.preset.fast === true,
19
+ ...(prepared.preset.autoEffort === false ? { autoEffort: false } : {}),
19
20
  });
20
21
  }
21
22
 
@@ -72,6 +72,20 @@ export function createLeadWorkerIndex({ dataDir, cfgMod, workerRowFromSession })
72
72
  return true;
73
73
  }
74
74
 
75
+ /** A workspace transition moved the Lead: its existing row follows (status,
76
+ * reap deadline and liveness stamps unchanged). No row: nothing to write. */
77
+ function updateLeadWorkerCwd(sessionId, cwd) {
78
+ const id = clean(sessionId);
79
+ const next = clean(cwd);
80
+ if (!id || !next) return false;
81
+ index.write((byKey) => {
82
+ const current = byKey.get(id);
83
+ if (!current || current.cwd === next) return;
84
+ byKey.set(id, { ...current, cwd: next });
85
+ });
86
+ return true;
87
+ }
88
+
75
89
  /** Graceful-exit settlement: every turn this process still claims goes idle
76
90
  * before the runtime disappears. */
77
91
  function flushActiveLeadRows() {
@@ -147,6 +161,7 @@ export function createLeadWorkerIndex({ dataDir, cfgMod, workerRowFromSession })
147
161
  /** Resolves once every queued Lead index write is on disk. */
148
162
  flushLeadWorkerIndexWrites: index.flush,
149
163
  upsertLeadSession,
164
+ updateLeadWorkerCwd,
150
165
  removeLeadWorkerRow,
151
166
  flushActiveLeadRows,
152
167
  recoverStaleLeadRows,
@@ -180,6 +180,7 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
180
180
  preset: session.presetName || null,
181
181
  effort: session.effort || null,
182
182
  fast: session.fast === true,
183
+ ...(session.autoEffort === false ? { autoEffort: false } : {}),
183
184
  },
184
185
  (job, ownerNotifyContext) => runSend(prepared, ownerNotifyContext, job),
185
186
  notifyContext
@@ -17,6 +17,7 @@ import { isAgentDisabled } from '../../../../runtime/shared/agent-route-config.m
17
17
  import { normalizeAgentPermission } from '../../../../runtime/shared/markdown-frontmatter.mjs';
18
18
  import { resolveAgentSpawnPreset } from '../spawn-preset.mjs';
19
19
  import { AGENT_OWNER } from '../../../../runtime/agent/orchestrator/agent-owner.mjs';
20
+ import { workspaceCwdLock } from '../../../workspace/admission-gate.mjs';
20
21
 
21
22
  export function createSpawnPlanner({
22
23
  cfgMod,
@@ -52,8 +53,15 @@ export function createSpawnPlanner({
52
53
  if (resolveTag(tag, context, { scanSessions: wantsSessionScan(args) })) {
53
54
  throw new Error(`agent spawn: tag "${tag}" already exists`);
54
55
  }
55
- const baseCwd = resolve(callerCwd || defaultCwd || process.cwd());
56
- const workerCwd = clean(args.cwd) ? resolve(baseCwd, args.cwd) : baseCwd;
56
+ // Workers share their Lead's execution root. While the Lead runs in its
57
+ // worktree, that root is locked: neither args.cwd nor an inherited
58
+ // (pre-transition) cwd can send a worker elsewhere.
59
+ const lockedCwd = workspaceCwdLock([
60
+ context?.ownerSessionId,
61
+ context?.callerSessionId || context?.sessionId,
62
+ ]);
63
+ const baseCwd = resolve(lockedCwd || callerCwd || defaultCwd || process.cwd());
64
+ const workerCwd = !lockedCwd && clean(args.cwd) ? resolve(baseCwd, args.cwd) : baseCwd;
57
65
  const prompt = await resolvePrompt(args, workerCwd);
58
66
  if (prepState?.timedOut) {
59
67
  throw new Error('agent spawn prep timed out before session bind');
@@ -10,6 +10,7 @@ import { prepareAgentSession } from '../../../../runtime/agent/orchestrator/agen
10
10
  import { getProvider } from '../../../../runtime/agent/orchestrator/providers/registry.mjs';
11
11
  import { resolveRouteContextState } from '../../../route-state.mjs';
12
12
  import { createSpawnPlanner } from './spawn-plan.mjs';
13
+ import { enterWorkerSpawn } from '../../../workspace/admission-gate.mjs';
13
14
 
14
15
  /** The route fields every worker-row and tag record carries. */
15
16
  export function presetDescriptor(agent, preset, presetName) {
@@ -20,6 +21,7 @@ export function presetDescriptor(agent, preset, presetName) {
20
21
  model: preset.model,
21
22
  effort: preset.effort || null,
22
23
  fast: preset.fast === true,
24
+ ...(preset.autoEffort === false ? { autoEffort: false } : {}),
23
25
  };
24
26
  }
25
27
 
@@ -84,9 +86,9 @@ export function createSpawnPreparer({
84
86
  // A sub-100 context share needs the window it selects; a percent alone
85
87
  // keeps the full served window.
86
88
  const sessionSpec = { ...spec };
87
- if (plan.preset.contextPercent) {
88
- const { contextPercent, selectedContextWindow } = resolveRouteContextState(plan.preset, null);
89
- Object.assign(sessionSpec, { contextPercent, selectedContextWindow });
89
+ if (plan.preset.contextPercent || plan.preset.contextTokens) {
90
+ const { contextPercent, selectedContextWindow, contextTokens } = resolveRouteContextState(plan.preset, null);
91
+ Object.assign(sessionSpec, { contextPercent, selectedContextWindow }, contextTokens ? { contextTokens } : {});
90
92
  }
91
93
  if (sessionSurface?.canonical === true && typeof sessionSurface.createChild === 'function') {
92
94
  return sessionSurface.createChild({ spec: sessionSpec, prompt: plan.prompt, tag: plan.tag });
@@ -99,10 +101,20 @@ export function createSpawnPreparer({
99
101
  }
100
102
 
101
103
  async function prepareSpawnInProcess(args, callerCwd = null, context = {}, prepState = null) {
102
- const plan = await resolveSpawnPlan(args, callerCwd, context, prepState);
103
- const spec = spawnSessionSpec(plan, args, context);
104
- const { session, effectiveCwd } = await createSpawnedSession(plan, spec, prepState);
105
- bindSpawnedSession(session, plan);
104
+ // Shared admission with Lead workspace transitions: no spawn is prepared
105
+ // while the Lead's workspace changes, no transition starts mid-spawn.
106
+ const leaveSpawn = enterWorkerSpawn([context?.ownerSessionId, context?.callerSessionId || context?.sessionId]);
107
+ let plan;
108
+ let session;
109
+ let effectiveCwd;
110
+ try {
111
+ plan = await resolveSpawnPlan(args, callerCwd, context, prepState);
112
+ const spec = spawnSessionSpec(plan, args, context);
113
+ ({ session, effectiveCwd } = await createSpawnedSession(plan, spec, prepState));
114
+ bindSpawnedSession(session, plan);
115
+ } finally {
116
+ leaveSpawn();
117
+ }
106
118
  if (sessionSurface?.canonical !== true) {
107
119
  maybePrewarmSpawnTransport(plan, session);
108
120
  }
@@ -20,6 +20,7 @@ export function resolveAgentSpawnPreset(config, args = {}) {
20
20
  model: clean(args.model),
21
21
  effort: clean(args.effort) || undefined,
22
22
  fast: args.fast === true,
23
+ ...(args.autoEffort === false ? { autoEffort: false } : {}),
23
24
  tools: 'full',
24
25
  },
25
26
  };
@@ -42,8 +43,10 @@ export function resolveAgentSpawnPreset(config, args = {}) {
42
43
  model: agentRoute.model,
43
44
  effort: agentRoute.effort,
44
45
  fast: agentRoute.fast === true,
46
+ ...(agentRoute.autoEffort === false ? { autoEffort: false } : {}),
45
47
  ...(agentRoute.modelParameters ? { modelParameters: agentRoute.modelParameters } : {}),
46
48
  ...(agentRoute.contextPercent ? { contextPercent: agentRoute.contextPercent } : {}),
49
+ ...(agentRoute.contextTokens ? { contextTokens: agentRoute.contextTokens } : {}),
47
50
  tools: 'full',
48
51
  },
49
52
  };
@@ -29,7 +29,7 @@ export function createTagRegistry({ dataDir, cfgMod, mgr }) {
29
29
  workerRowFromSession: index.workerRowFromSession,
30
30
  });
31
31
 
32
- const { upsertLeadSession } = leadIndex;
32
+ const { upsertLeadSession, updateLeadWorkerCwd } = leadIndex;
33
33
 
34
34
  function dispose(options) {
35
35
  try {
@@ -103,6 +103,7 @@ export function createTagRegistry({ dataDir, cfgMod, mgr }) {
103
103
  upsertWorkerSession: index.upsertWorkerSession,
104
104
  upsertWorkerSessionDeferred,
105
105
  upsertLeadSession,
106
+ updateLeadWorkerCwd,
106
107
  removeWorkerRow,
107
108
  refreshTagsFromIndex,
108
109
  tags,
@@ -38,6 +38,7 @@ export function normalizeWorkerRows(value) {
38
38
  preset: clean(row.preset) || null,
39
39
  effort: clean(row.effort) || null,
40
40
  fast: typeof row.fast === 'boolean' ? row.fast : null,
41
+ ...(row.autoEffort === false ? { autoEffort: false } : {}),
41
42
  status: clean(row.status) || 'idle',
42
43
  stage: clean(row.stage) || clean(row.status) || 'idle',
43
44
  createdAt: clean(row.createdAt) || null,
@@ -87,6 +88,10 @@ export function workerRowFromSession(session, fallbackTag = '', extra = {}, getS
87
88
  preset: clean(extra.preset) || clean(session?.presetName) || null,
88
89
  effort: clean(extra.effort) || clean(session?.effort) || null,
89
90
  fast: fastFlag(extra.fast, session?.fast),
91
+ // Absent means Auto effort is on; an explicit boolean wins over the session's.
92
+ ...((typeof extra.autoEffort === 'boolean' ? extra.autoEffort : session?.autoEffort) === false
93
+ ? { autoEffort: false }
94
+ : {}),
90
95
  status,
91
96
  stage,
92
97
  createdAt: clean(session?.createdAt) || clean(extra.createdAt) || nowIso,
@@ -133,6 +133,7 @@ export function workerRowToSession(row = {}) {
133
133
  presetName: row.preset || null,
134
134
  effort: row.effort || null,
135
135
  fast: row.fast === true,
136
+ ...(row.autoEffort === false ? { autoEffort: false } : {}),
136
137
  status: row.status || 'idle',
137
138
  stage: row.stage || row.status || 'idle',
138
139
  createdAt: row.createdAt || null,
@@ -146,6 +146,7 @@ export function createStandaloneAgent({
146
146
  getStatus,
147
147
  recoverWorkers,
148
148
  upsertLeadSession: registry.upsertLeadSession,
149
+ updateLeadWorkerCwd: registry.updateLeadWorkerCwd,
149
150
  closeAll: closeFlow.closeAll,
150
151
  dispose: (options) => registry.dispose(options),
151
152
  };
@@ -18,7 +18,7 @@ import { daemonEnv, spawnDaemonCandidate } from './channel-worker/daemon-spawn.m
18
18
 
19
19
  export { pruneStaleChannelClientHeartbeats };
20
20
 
21
- const CHANNEL_TOOLS = new Set(['activate_channel_bridge', 'reload_config', 'rebind_current_transcript']);
21
+ const CHANNEL_TOOLS = new Set(['activate_channel_bridge', 'reload_config']);
22
22
  const CALL_RETRIES = 3;
23
23
 
24
24
  function logLine(path, line) {
@@ -5,7 +5,8 @@
5
5
  // ready handoff so the daemon's code/signal is recorded against its pid, boot
6
6
  // time and ready state. Resolves once the candidate reported ready, exited,
7
7
  // failed to spawn, or the timeout elapsed; the caller then re-reads discovery
8
- // and attaches to whoever won.
8
+ // and attaches to whoever won. The resolved `summary()` describes this boot as
9
+ // observed when called, for a caller that has to give up.
9
10
  import { fork } from 'node:child_process';
10
11
  import { beginDaemonSpawnCapture } from './daemon-crash-capture.mjs';
11
12
 
@@ -34,7 +35,7 @@ export function forkDaemonCandidate({
34
35
  // Boot diagnostics written to fd 2 reach this log exactly once, even when
35
36
  // the candidate never reached ready.
36
37
  capture.mirror();
37
- resolve();
38
+ resolve({ summary: capture.summary });
38
39
  };
39
40
  let child;
40
41
  try {
@@ -20,6 +20,11 @@ const CAPTURE_ID_ATTEMPTS = 8;
20
20
  const MIRROR_MAX_BYTES = 16 * 1024;
21
21
  const MESSAGE_MAX_CHARS = 512;
22
22
  const HEAP_FLAG_RE = /^--max-(?:old-space|semi-space|heap)-size=/;
23
+ // Bytes of fd 2 a failure summary scans (the newest ones when larger).
24
+ const SUMMARY_SCAN_MAX_BYTES = 256 * 1024;
25
+ // Node's uncaught-error report (`TypeError: …`, `Error [ERR_…]: …`), V8's
26
+ // fatal abort and the daemon's own `fatal boot error` log line.
27
+ const STDERR_ERROR_LINE_RE = /\b[A-Z][A-Za-z]*Error\b(?: \[[A-Z0-9_]+\])?:|^Error\b|FATAL ERROR|(?<!non-)\bfatal\b/;
23
28
 
24
29
  function stamp(ms) {
25
30
  const iso = new Date(ms).toISOString();
@@ -256,6 +261,8 @@ function noteSpawnError(capture, error) {
256
261
  // this stream, so nothing here extends its lifetime.
257
262
  function drainStderrPipe(capture, stream) {
258
263
  const onData = (chunk) => {
264
+ // No capture file to read back: keep the newest bytes for summary().
265
+ capture.pipeText = `${capture.pipeText}${chunk}`.slice(-SUMMARY_SCAN_MAX_BYTES);
259
266
  mirrorText(capture.log, chunk);
260
267
  };
261
268
  stream.on('data', onData);
@@ -272,6 +279,52 @@ function drainStderrPipe(capture, stream) {
272
279
  });
273
280
  }
274
281
 
282
+ /** The candidate's fd 2 as text: the capture file (its newest bytes when
283
+ * large) or, when fd 2 fell back to a pipe, what that pipe delivered. */
284
+ function stderrText(capture) {
285
+ if (!capture.capturePath || capture.removed) return capture.pipeText;
286
+ try {
287
+ const handle = fs.openSync(capture.capturePath, 'r');
288
+ try {
289
+ const size = fs.fstatSync(handle).size;
290
+ const length = Math.min(size, SUMMARY_SCAN_MAX_BYTES);
291
+ const buffer = Buffer.allocUnsafe(length);
292
+ const bytesRead = fs.readSync(handle, buffer, 0, length, size - length);
293
+ return buffer.subarray(0, bytesRead).toString('utf8');
294
+ } finally {
295
+ fs.closeSync(handle);
296
+ }
297
+ } catch {
298
+ return capture.pipeText;
299
+ }
300
+ }
301
+
302
+ /** What a launcher that gave up on this boot needs to name its cause: the
303
+ * lifecycle as far as observed, the first error line on fd 2 (else its last
304
+ * line, where a stalled boot stopped) and where the full text is. */
305
+ function summarize(capture) {
306
+ const { record } = capture;
307
+ const lines = stderrText(capture)
308
+ .split(/\r?\n/)
309
+ .map((line) => line.trim())
310
+ .filter(Boolean);
311
+ const errorLine = lines.find((line) => STDERR_ERROR_LINE_RE.test(line));
312
+ return {
313
+ pid: record.pid,
314
+ ready: record.ready,
315
+ spawnedAt: record.spawnedAt,
316
+ exitedAt: record.exitedAt,
317
+ exitCode: record.exitCode,
318
+ exitSignal: record.exitSignal,
319
+ exitReason: record.exitReason ?? null,
320
+ uptimeMs: record.uptimeMs,
321
+ error: record.error,
322
+ stderrErrorLine: errorLine ? boundedMessage(errorLine) : null,
323
+ stderrLastLine: lines.length > 0 ? boundedMessage(lines.at(-1)) : null,
324
+ stderrPath: capture.removed ? null : capture.capturePath,
325
+ };
326
+ }
327
+
275
328
  /** Attach to the forked child: record its identity and keep a lifetime
276
329
  * listener that survives the ready handoff, so a later exit is still
277
330
  * correlated to this pid, boot time and ready state. */
@@ -347,6 +400,7 @@ export function beginDaemonSpawnCapture({
347
400
  readOffset: 0,
348
401
  removed: false,
349
402
  pipeDrained: null,
403
+ pipeText: '',
350
404
  stderrStdio: 'pipe',
351
405
  };
352
406
  openCapture(capture, { captureDir, prune, launcherPid, nonce });
@@ -377,5 +431,6 @@ export function beginDaemonSpawnCapture({
377
431
  * recording anything — the child, if any, keeps its duplicate. */
378
432
  release: () => closeWriteFd(capture),
379
433
  snapshot: () => ({ ...capture.record }),
434
+ summary: () => summarize(capture),
380
435
  };
381
436
  }
@@ -6,6 +6,7 @@ import { hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-te
6
6
  import { toolSpecForMode } from '../../runtime/agent/orchestrator/runtime-core/effort.mjs';
7
7
  import { pluginDataDir } from '../runtime-paths.mjs';
8
8
  import { LEAD_DISALLOWED_TOOLS } from '../../runtime/agent/orchestrator/runtime-core/tool-defs.mjs';
9
+ import { HEADLESS_TOOL_PROFILE } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
9
10
 
10
11
  // Ownership and permission fields an agent-owned session inherits from its profile.
11
12
  function agentOwnedSessionFields(sessionProfile) {
@@ -47,7 +48,13 @@ export function sessionOptions(deps, coreMemoryContext) {
47
48
  owner: agentOwned ? 'agent' : 'cli',
48
49
  agent: agentOwned ? sessionProfile?.agent || 'worker' : 'lead',
49
50
  lane: agentOwned ? 'agent' : 'cli',
50
- sourceType: agentOwned ? sessionProfile?.sourceType || 'agent' : 'lead',
51
+ // One-shot `mixdog exec` runs carry their own source so session catalogs
52
+ // can keep them out of the user's resumable list.
53
+ sourceType: agentOwned
54
+ ? sessionProfile?.sourceType || 'agent'
55
+ : rt.toolProfile === HEADLESS_TOOL_PROFILE
56
+ ? 'exec'
57
+ : 'lead',
51
58
  sourceName: agentOwned ? sessionProfile?.sourceName || sessionProfile?.agent || 'agent' : 'main',
52
59
  ...(rt.approvalMode ? { approvalMode: rt.approvalMode } : {}),
53
60
  clientHostPid: sessionProfile?.clientHostPid || process.pid,
@@ -64,8 +71,10 @@ export function sessionOptions(deps, coreMemoryContext) {
64
71
  workflowContext,
65
72
  orchestrationMode,
66
73
  fast: rt.route.fast === true,
74
+ autoEffort: rt.route.autoEffort !== false,
67
75
  modelParameters: rt.route.modelParameters || {},
68
76
  contextPercent: rt.route.contextPercent,
77
+ contextTokens: rt.route.contextTokens || null,
69
78
  selectedContextWindow: rt.route.selectedContextWindow || null,
70
79
  compaction:
71
80
  rt.config.compaction && typeof rt.config.compaction === 'object'
@@ -34,6 +34,7 @@ export function createSessionTurnApi(deps) {
34
34
  sessionOwnerGone: share.sessionOwnerGone,
35
35
  agentControl: agent.agentControl,
36
36
  taskControl: agent.taskControl,
37
+ stopShellJob: agent.stopShellJob,
37
38
  onNotification: agent.onNotification,
38
39
  toolsStatus: agent.toolsStatus,
39
40
  selectTools: agent.selectTools,
@@ -5,9 +5,9 @@
5
5
  // `this.*` resolve against the spread target, so cross-member calls (e.g.
6
6
  // setProfile -> this.getProfile) keep working when spread into the facade.
7
7
  //
8
- // The members live in five topic modules: profile/onboarding/skills,
8
+ // The members live in six topic modules: profile/onboarding/skills,
9
9
  // compaction/auto-clear, built-in tool modules, system (channels, shell,
10
- // updates) and developer options; the local provider's own methods come from
10
+ // updates), developer options and worktree defaults; the local provider's own methods come from
11
11
  // local-provider-settings.mjs.
12
12
  import { createLocalProviderSettings } from './local-provider-settings.mjs';
13
13
  import { createProfileSettings } from './settings-profile-api.mjs';
@@ -15,6 +15,7 @@ import { createCompactionSettings } from './settings-compaction-api.mjs';
15
15
  import { createBuiltinToolSettings } from './settings-builtin-tools-api.mjs';
16
16
  import { createSystemSettings } from './settings-system-api.mjs';
17
17
  import { createDeveloperSettings } from './settings-developer-api.mjs';
18
+ import { createWorktreeSettings } from './settings-worktree-api.mjs';
18
19
 
19
20
  export function createSettingsApi(deps) {
20
21
  // Optional: callers without a live local provider / gate fall back to the
@@ -38,5 +39,6 @@ export function createSettingsApi(deps) {
38
39
  ...createBuiltinToolSettings(shared, localSettings),
39
40
  ...createSystemSettings(shared),
40
41
  ...createDeveloperSettings(shared),
42
+ ...createWorktreeSettings(shared),
41
43
  };
42
44
  }
@@ -7,6 +7,7 @@ import {
7
7
  builtinInstalled,
8
8
  setBuiltinInstalledInConfig,
9
9
  } from '../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
10
+ import { featureEnvOverride, moduleEnabled } from '../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
10
11
  import { LOCAL_PROVIDER_ID } from '../runtime/local-provider/managed-runtime.mjs';
11
12
  import {
12
13
  INSTRUCTION_FILES_MODES,
@@ -23,7 +24,7 @@ import {
23
24
  shutdownEffortJudge,
24
25
  } from '../runtime/effort-judge/judge-client.mjs';
25
26
 
26
- const TOGGLEABLE_BUILTINS = ['git', 'office', 'tidy', 'localProvider', 'autoEffort'];
27
+ const TOGGLEABLE_BUILTINS = ['git', 'office', 'tidy', 'localProvider', 'autoEffort', 'visualize'];
27
28
 
28
29
  function setLocalProviderEnabledInConfig(configLike, enabled) {
29
30
  const next = { ...(configLike || {}) };
@@ -97,6 +98,12 @@ export function createBuiltinToolSettings(
97
98
  enabled: tidyToolEnabledFn ? tidyToolEnabledFn() : builtinFeatureActive(config, 'tidy'),
98
99
  installed: builtinInstalled(config, 'tidy'),
99
100
  },
101
+ // Ships with the runtime and needs no install; the toggle reflects the
102
+ // module flag, not live bridge presence.
103
+ visualize: {
104
+ enabled: featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ?? moduleEnabled(config, 'visualize', true),
105
+ installed: true,
106
+ },
100
107
  localProvider: {
101
108
  ...localProvider,
102
109
  ...localSettings.status(),
@@ -152,7 +159,7 @@ export function createBuiltinToolSettings(
152
159
  },
153
160
  async setBuiltinToolEnabled(name, enabled) {
154
161
  if (!TOGGLEABLE_BUILTINS.includes(name)) {
155
- throw new TypeError('Built-in tool must be git, office, tidy, localProvider, or autoEffort.');
162
+ throw new TypeError('Built-in tool must be git, office, tidy, localProvider, autoEffort, or visualize.');
156
163
  }
157
164
  if (name === 'autoEffort' && enabled !== false && !onnxRuntimeSupported()) {
158
165
  throw new Error(EFFORT_JUDGE_UNSUPPORTED);
@@ -1,6 +1,13 @@
1
1
  // Onboarding, the user profile (/profile) and the disabled-skill list. Methods
2
2
  // reference `this.*` so cross-member calls resolve against the facade object
3
3
  // these are spread into.
4
+ import {
5
+ mergeStarterGuide,
6
+ requiredStarterGuideState,
7
+ storedStarterGuide,
8
+ withStarterGuideOnCompletion,
9
+ } from './starter-guide-state.mjs';
10
+
4
11
  export function createProfileSettings({
5
12
  getConfig,
6
13
  adoptConfig,
@@ -19,21 +26,39 @@ export function createProfileSettings({
19
26
  version: nextConfig?.onboarding?.version || 0,
20
27
  default: nextConfig?.default || null,
21
28
  workflowRoutes: summarizeWorkflowRoutes(nextConfig),
29
+ starterGuide: storedStarterGuide(nextConfig?.onboarding?.starterGuide),
22
30
  };
23
31
  },
32
+ // Persist the desktop "Getting started" guide state beside the onboarding
33
+ // flags so it survives a lost renderer localStorage. Malformed input is
34
+ // rejected; a valid save merges forward, so a stale one cannot reopen a
35
+ // closed guide or forget a visited step.
36
+ saveStarterGuideState(state) {
37
+ const next = requiredStarterGuideState(state);
38
+ const config = getConfig();
39
+ const onboarding = config.onboarding || {};
40
+ saveConfigAndAdopt({
41
+ ...config,
42
+ onboarding: {
43
+ ...onboarding,
44
+ starterGuide: mergeStarterGuide(storedStarterGuide(onboarding.starterGuide), next),
45
+ },
46
+ });
47
+ return this.getOnboardingStatus();
48
+ },
24
49
  // Mark onboarding as done WITHOUT touching routes/agents/provider. Used by
25
50
  // the TUI "skip" (Esc) path so the wizard doesn't reappear next launch,
26
51
  // while leaving any existing config routes untouched.
27
52
  skipOnboarding() {
28
53
  const config = getConfig();
29
54
  const nextConfig = { ...config };
30
- nextConfig.onboarding = {
55
+ nextConfig.onboarding = withStarterGuideOnCompletion(config.onboarding, {
31
56
  ...(nextConfig.onboarding || {}),
32
57
  completed: true,
33
58
  version: ONBOARDING_VERSION,
34
59
  completedAt: new Date().toISOString(),
35
60
  skipped: true,
36
- };
61
+ });
37
62
  saveConfigAndAdopt(nextConfig);
38
63
  return this.getOnboardingStatus();
39
64
  },
@@ -0,0 +1,17 @@
1
+ // Settings → Worktrees: defaults for Lead-session git worktrees
2
+ // (runtime/shared/worktrees/settings.mjs). Values persist at agent config
3
+ // `worktree.<key>`; defaults are not stored.
4
+ import { applyWorktreeSettingsPatch, normalizeWorktreeSettings } from '../runtime/shared/worktrees/settings.mjs';
5
+
6
+ export function createWorktreeSettings({ getConfig, saveConfigAndAdopt }) {
7
+ return {
8
+ getWorktreeSettings() {
9
+ return normalizeWorktreeSettings(getConfig()?.worktree);
10
+ },
11
+ setWorktreeSettings(patch) {
12
+ const config = getConfig() || {};
13
+ saveConfigAndAdopt({ ...config, worktree: applyWorktreeSettingsPatch(config.worktree, patch) });
14
+ return this.getWorktreeSettings();
15
+ },
16
+ };
17
+ }