mixdog 0.9.95 → 0.9.97

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 (282) hide show
  1. package/LICENSES/Apache-2.0.txt +201 -0
  2. package/LICENSES/MIT.txt +56 -0
  3. package/LICENSES/codex-NOTICE.txt +6 -0
  4. package/NOTICE.md +46 -9
  5. package/README.md +0 -17
  6. package/package.json +16 -12
  7. package/scripts/.tmp-coverage-report.mjs +96 -0
  8. package/scripts/build-tui.mjs +17 -1
  9. package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
  10. package/scripts/fixtures/patch-replay-corpus.json +98 -0
  11. package/src/defaults/mixdog-config.template.json +5 -8
  12. package/src/defaults/skills/setup/SKILL.md +1 -1
  13. package/src/lib/keychain-cjs.cjs +1 -1
  14. package/src/lib/mixdog-debug.cjs +2 -3
  15. package/src/rules/agent/30-explorer.md +7 -4
  16. package/src/rules/lead/01-general.md +1 -2
  17. package/src/rules/lead/lead-tool.md +0 -3
  18. package/src/rules/shared/01-tool.md +21 -29
  19. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +18 -2
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +31 -1
  21. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
  22. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -1
  23. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
  24. package/src/runtime/agent/orchestrator/config.mjs +246 -67
  25. package/src/runtime/agent/orchestrator/mcp/client.mjs +104 -41
  26. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +23 -0
  27. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +113 -14
  28. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
  29. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +32 -11
  30. package/src/runtime/agent/orchestrator/providers/gemini.mjs +89 -2
  31. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -1
  32. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +19 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +144 -39
  34. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -8
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
  36. package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
  37. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +3 -4
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
  39. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
  40. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +67 -7
  41. package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +291 -0
  42. package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +21 -0
  43. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +36 -10
  44. package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -33
  46. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
  47. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +2 -2
  48. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +0 -24
  49. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +21 -29
  50. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +7 -10
  51. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +45 -25
  52. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
  53. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +142 -84
  54. package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
  55. package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
  56. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
  57. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
  58. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +29 -9
  59. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
  61. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +86 -8
  62. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
  63. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
  64. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +71 -42
  65. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +4 -33
  66. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -5
  67. package/src/runtime/agent/orchestrator/stall-policy.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  69. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +35 -1
  70. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +116 -26
  71. package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
  72. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +8 -9
  73. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +2 -2
  75. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +6 -15
  77. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +12 -26
  78. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +5 -5
  79. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -33
  80. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +42 -45
  81. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +27 -9
  82. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
  83. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +17 -39
  84. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +73 -0
  85. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +0 -2
  87. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +3 -8
  88. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
  89. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +5 -5
  90. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +142 -26
  91. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
  92. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +47 -19
  93. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -2
  94. package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
  95. package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
  96. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +39 -1
  97. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -20
  99. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +16 -0
  100. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +108 -12
  101. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -11
  102. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +63 -9
  104. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
  105. package/src/runtime/channels/lib/config.mjs +34 -34
  106. package/src/runtime/channels/lib/inbound-handler.mjs +8 -8
  107. package/src/runtime/channels/lib/interaction-handlers.mjs +6 -6
  108. package/src/runtime/channels/lib/memory-client.mjs +31 -157
  109. package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
  110. package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
  111. package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
  112. package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
  113. package/src/runtime/channels/lib/runtime-paths.mjs +4 -30
  114. package/src/runtime/channels/lib/scheduler.mjs +2 -2
  115. package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
  116. package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
  117. package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
  118. package/src/runtime/channels/lib/worker-ipc.mjs +7 -7
  119. package/src/runtime/channels/lib/worker-main.mjs +38 -38
  120. package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
  121. package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
  122. package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
  123. package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
  124. package/src/runtime/channels/tool-defs.mjs +1 -1
  125. package/src/runtime/memory/index.mjs +76 -248
  126. package/src/runtime/memory/lib/agent-ipc.mjs +146 -81
  127. package/src/runtime/memory/lib/compact-vector-cache.mjs +88 -0
  128. package/src/runtime/memory/lib/core-memory-store.mjs +18 -1
  129. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +13 -34
  130. package/src/runtime/memory/lib/embedding-provider.mjs +47 -26
  131. package/src/runtime/memory/lib/embedding-worker.mjs +30 -34
  132. package/src/runtime/memory/lib/http-router.mjs +9 -6
  133. package/src/runtime/memory/lib/ko-morph.mjs +3 -20
  134. package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -1
  135. package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
  136. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -0
  137. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -2
  138. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -0
  139. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
  140. package/src/runtime/memory/lib/memory-cycle3.mjs +3 -1
  141. package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
  142. package/src/runtime/memory/lib/memory-log.mjs +1 -1
  143. package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
  144. package/src/runtime/memory/lib/memory.mjs +1 -1
  145. package/src/runtime/memory/lib/pg/process.mjs +32 -9
  146. package/src/runtime/memory/lib/session-ingest.mjs +1 -1
  147. package/src/runtime/memory/tool-defs.mjs +1 -5
  148. package/src/runtime/search/index.mjs +2 -2
  149. package/src/runtime/search/tool-defs.mjs +2 -2
  150. package/src/runtime/shared/abort-race.mjs +70 -0
  151. package/src/runtime/shared/agent-route-config.mjs +119 -0
  152. package/src/runtime/shared/atomic-file.mjs +80 -4
  153. package/src/runtime/shared/channel-notification-routing.mjs +2 -2
  154. package/src/runtime/shared/child-guardian.mjs +204 -108
  155. package/src/runtime/shared/child-spawn-gate.mjs +139 -72
  156. package/src/runtime/shared/config.mjs +120 -47
  157. package/src/runtime/shared/llm/index.mjs +1 -1
  158. package/src/runtime/shared/owner-fair-gate.mjs +134 -0
  159. package/src/runtime/shared/resource-admission.mjs +150 -100
  160. package/src/runtime/shared/stream-progress.mjs +6 -0
  161. package/src/runtime/shared/tool-execution-owner.mjs +12 -0
  162. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  163. package/src/runtime/shared/tool-surface.mjs +91 -27
  164. package/src/runtime/shared/tool-workload-gates.mjs +29 -0
  165. package/src/runtime/shared/turn-snapshot.mjs +47 -6
  166. package/src/runtime/shared/turn-worktree-snapshot.mjs +14 -2
  167. package/src/session-runtime/channel-config-api.mjs +5 -5
  168. package/src/session-runtime/config-helpers.mjs +39 -37
  169. package/src/session-runtime/config-lifecycle.mjs +27 -27
  170. package/src/session-runtime/lifecycle-api.mjs +22 -3
  171. package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
  172. package/src/session-runtime/model-route-api.mjs +7 -5
  173. package/src/session-runtime/prewarm.mjs +1 -1
  174. package/src/session-runtime/provider-auth-api.mjs +0 -7
  175. package/src/session-runtime/provider-models.mjs +1 -1
  176. package/src/session-runtime/remote-control.mjs +6 -7
  177. package/src/session-runtime/runtime-core.mjs +63 -39
  178. package/src/session-runtime/runtime-tunables.mjs +4 -4
  179. package/src/session-runtime/self-update.mjs +1 -1
  180. package/src/session-runtime/session-lifecycle.mjs +45 -17
  181. package/src/session-runtime/session-title.mjs +1 -1
  182. package/src/session-runtime/session-turn-api.mjs +96 -24
  183. package/src/session-runtime/settings-api.mjs +12 -24
  184. package/src/session-runtime/tool-defs.mjs +3 -3
  185. package/src/session-runtime/workflow-agents-api.mjs +55 -81
  186. package/src/session-runtime/workflow.mjs +14 -24
  187. package/src/standalone/agent-dispatch-broker.mjs +198 -0
  188. package/src/standalone/agent-tool/helpers.mjs +2 -2
  189. package/src/standalone/agent-tool/spawn-flow.mjs +2 -0
  190. package/src/standalone/agent-tool/spawn-preset.mjs +11 -21
  191. package/src/standalone/agent-tool/tool-def.mjs +0 -14
  192. package/src/standalone/channel-admin.mjs +53 -64
  193. package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +33 -19
  194. package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +127 -46
  195. package/src/standalone/channel-worker.mjs +124 -647
  196. package/src/standalone/{backend-daemon.mjs → daemon.mjs} +270 -154
  197. package/src/standalone/explore-tool.mjs +187 -70
  198. package/src/standalone/fair-call-scheduler.mjs +264 -0
  199. package/src/standalone/memory-runtime-proxy.mjs +58 -1
  200. package/src/standalone/session-client.mjs +1134 -0
  201. package/src/standalone/session-protocol.mjs +192 -0
  202. package/src/standalone/session-runtime-pool.mjs +393 -0
  203. package/src/standalone/session-runtime-record.mjs +20 -0
  204. package/src/standalone/session-runtime-worker.mjs +203 -0
  205. package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +481 -224
  206. package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +390 -121
  207. package/src/standalone/session-wire.mjs +55 -0
  208. package/src/tui/App.jsx +34 -21
  209. package/src/tui/app/app-format.mjs +1 -1
  210. package/src/tui/app/app-view.jsx +5 -2
  211. package/src/tui/app/channel-pickers.mjs +41 -41
  212. package/src/tui/app/core-memory-picker.mjs +19 -19
  213. package/src/tui/app/doctor.mjs +5 -11
  214. package/src/tui/app/input-parsers.mjs +2 -2
  215. package/src/tui/app/message-selector.mjs +103 -0
  216. package/src/tui/app/onboarding-steps.mjs +3 -4
  217. package/src/tui/app/project-picker.mjs +80 -56
  218. package/src/tui/app/prompt-submit.mjs +48 -43
  219. package/src/tui/app/resume-picker.mjs +2 -2
  220. package/src/tui/app/route-pickers.mjs +3 -1
  221. package/src/tui/app/settings-picker.mjs +31 -33
  222. package/src/tui/app/transcript-window.mjs +38 -0
  223. package/src/tui/app/use-prompt-draft-flow.mjs +5 -7
  224. package/src/tui/app/use-prompt-handlers.mjs +86 -21
  225. package/src/tui/app/use-prompt-hint.mjs +3 -2
  226. package/src/tui/app/use-prompt-queue-history.mjs +10 -3
  227. package/src/tui/app/use-transcript-scroll.mjs +10 -3
  228. package/src/tui/app/use-transcript-window.mjs +50 -17
  229. package/src/tui/components/PromptInput.jsx +71 -38
  230. package/src/tui/components/Spinner.jsx +1 -1
  231. package/src/tui/components/ToolExecution.jsx +1 -1
  232. package/src/tui/components/prompt-input/edit-helpers.mjs +9 -11
  233. package/src/tui/components/prompt-input/escape-policy.mjs +42 -0
  234. package/src/tui/components/prompt-input/immediate-render.mjs +0 -10
  235. package/src/tui/components/prompt-input/interrupt-policy.mjs +10 -0
  236. package/src/tui/components/prompt-input/restore-policy.mjs +10 -0
  237. package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
  238. package/src/tui/dist/index.mjs +1031 -1676
  239. package/src/tui/hooks/useSession.mjs +16 -0
  240. package/src/tui/index.jsx +12 -13
  241. package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
  242. package/src/tui/{engine → session}/agent-job-feed.mjs +4 -4
  243. package/src/tui/{engine → session}/boot-profile.mjs +2 -2
  244. package/src/tui/{engine → session}/context-state.mjs +3 -3
  245. package/src/tui/{engine → session}/labels.mjs +2 -2
  246. package/src/tui/{engine → session}/live-share.mjs +38 -4
  247. package/src/tui/{engine → session}/notice-text.mjs +2 -2
  248. package/src/tui/{engine → session}/notification-plan.mjs +3 -3
  249. package/src/tui/session/oauth-flows.mjs +140 -0
  250. package/src/tui/{engine → session}/prompt-history.mjs +3 -3
  251. package/src/tui/{engine → session}/queue-helpers.mjs +2 -2
  252. package/src/tui/{engine → session}/render-timing.mjs +2 -2
  253. package/src/tui/{engine → session}/session-api-ext.mjs +30 -20
  254. package/src/tui/{engine → session}/session-api.mjs +110 -93
  255. package/src/tui/{engine → session}/session-flow.mjs +8 -2
  256. package/src/tui/{engine → session}/session-stats.mjs +3 -3
  257. package/src/tui/{engine → session}/tool-approval.mjs +3 -3
  258. package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
  259. package/src/tui/{engine → session}/tool-card-results.mjs +8 -6
  260. package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
  261. package/src/tui/{engine → session}/tool-result-text.mjs +13 -3
  262. package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
  263. package/src/tui/{engine → session}/turn.mjs +38 -176
  264. package/src/tui/{engine-local-session.mjs → session-local.mjs} +103 -103
  265. package/src/tui/session.mjs +22 -0
  266. package/src/tui/spinner-meta.mjs +4 -4
  267. package/src/tui/spinner-verbs.mjs +51 -3
  268. package/src/ui/statusline-segments.mjs +1 -1
  269. package/src/ui/statusline.mjs +1 -1
  270. package/vendor/ink/build/components/App.js +10 -1
  271. package/src/session-runtime/memory-daemon-probe.mjs +0 -61
  272. package/src/standalone/engine-daemon-client.mjs +0 -894
  273. package/src/standalone/engine-daemon-local-bridge.mjs +0 -20
  274. package/src/standalone/engine-daemon-protocol.mjs +0 -33
  275. package/src/tui/engine/turn-watchdog.mjs +0 -92
  276. package/src/tui/engine.mjs +0 -22
  277. package/src/tui/hooks/useEngine.mjs +0 -16
  278. /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
  279. /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
  280. /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
  281. /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
  282. /package/src/tui/{engine → session}/tui-steering-persist.mjs +0 -0
@@ -83,6 +83,8 @@ function createDiffTracker(meta = {}) {
83
83
  return {
84
84
  ownerSessionId: clean(meta.ownerSessionId) || null,
85
85
  ownerGeneration: Number(meta.ownerGeneration) || 0,
86
+ worktreeRequest: clean(meta.worktreeRequest),
87
+ worktreeContended: false,
86
88
  valid: true,
87
89
  sealed: false,
88
90
  revision: 0,
@@ -336,13 +338,31 @@ export async function beginTurnSnapshot(_worktree, sessionId) {
336
338
  generation,
337
339
  agents: new Map(),
338
340
  });
339
- const tracker = resetDiffTracker(ownerSessionId);
341
+ const worktreeRequest = pathKey(_worktree);
342
+ const tracker = resetDiffTracker(ownerSessionId, { worktreeRequest });
340
343
  trimTurnCache();
341
344
  if (!tracker) return;
345
+ for (const [otherSessionId, other] of _diffTrackersBySession) {
346
+ if (otherSessionId === ownerSessionId || other.sealed) continue;
347
+ if (!worktreeRequest || other.worktreeRequest !== worktreeRequest) continue;
348
+ tracker.worktreeContended = true;
349
+ other.worktreeContended = true;
350
+ }
342
351
  try {
343
352
  const snapshot = await createTurnWorktreeSnapshot(_worktree);
344
353
  if (_diffTrackersBySession.get(ownerSessionId) === tracker) {
345
354
  tracker.worktreeSnapshot = snapshot;
355
+ if (snapshot?.root) {
356
+ for (const [otherSessionId, other] of _diffTrackersBySession) {
357
+ if (otherSessionId === ownerSessionId) continue;
358
+ if (other.worktreeSnapshot?.root !== snapshot.root) continue;
359
+ // A whole-worktree baseline cannot attribute concurrent mutations
360
+ // to either session. Mark both turns permanently contended and fall
361
+ // back to their exact session-owned mutation trackers.
362
+ tracker.worktreeContended = true;
363
+ other.worktreeContended = true;
364
+ }
365
+ }
346
366
  }
347
367
  } catch {
348
368
  // Git is optional. Exact apply_patch tracking remains the fallback.
@@ -421,7 +441,7 @@ export function completeAgentTurnReview(handle, patches = []) {
421
441
  export async function completeTurnSnapshot(sessionId) {
422
442
  const tracker = _diffTrackersBySession.get(clean(sessionId));
423
443
  if (!tracker) return false;
424
- if (tracker.worktreeSnapshot) {
444
+ if (tracker.worktreeSnapshot && !tracker.worktreeContended) {
425
445
  try { await refreshTurnWorktreeSnapshot(tracker.worktreeSnapshot); } catch {}
426
446
  }
427
447
  tracker.sealed = true;
@@ -429,16 +449,34 @@ export async function completeTurnSnapshot(sessionId) {
429
449
  return true;
430
450
  }
431
451
 
452
+ /** Abandon an aborted Lead turn without waiting for optional Git snapshot IO. */
453
+ export function cancelTurnSnapshot(sessionId) {
454
+ const ownerSessionId = clean(sessionId);
455
+ if (!ownerSessionId) return false;
456
+ let removed = false;
457
+ for (const [trackedSessionId, tracker] of _diffTrackersBySession) {
458
+ if (trackedSessionId !== ownerSessionId && tracker.ownerSessionId !== ownerSessionId) continue;
459
+ releaseDiffTrackerContent(tracker);
460
+ _diffTrackersBySession.delete(trackedSessionId);
461
+ removed = true;
462
+ }
463
+ if (_turnsBySession.delete(ownerSessionId)) removed = true;
464
+ return removed;
465
+ }
466
+
432
467
  /** Return the authoritative Lead net diff plus attributed child reviews. */
433
- export async function getTurnReviewDiff(_worktree, sessionId) {
468
+ export async function getTurnReviewDiff(_worktree, sessionId, options = {}) {
434
469
  if (DISABLED) return { supported: false, files: [], patch: '', agents: [] };
435
470
  const ownerSessionId = clean(sessionId);
436
471
  const turn = _turnsBySession.get(ownerSessionId);
437
472
  const tracker = _diffTrackersBySession.get(ownerSessionId);
438
- if (tracker?.worktreeSnapshot && !tracker.sealed) {
439
- try { await refreshTurnWorktreeSnapshot(tracker.worktreeSnapshot); } catch {}
473
+ const isolatedWorktreeSnapshot = tracker?.worktreeContended
474
+ ? null
475
+ : tracker?.worktreeSnapshot || null;
476
+ if (isolatedWorktreeSnapshot && !tracker.sealed && options.refresh !== false) {
477
+ try { await refreshTurnWorktreeSnapshot(isolatedWorktreeSnapshot); } catch {}
440
478
  }
441
- const snapshot = tracker?.worktreeSnapshot || null;
479
+ const snapshot = isolatedWorktreeSnapshot;
442
480
  return {
443
481
  supported: true,
444
482
  files: snapshot?.files || [],
@@ -455,6 +493,9 @@ export async function getTurnReviewDiff(_worktree, sessionId) {
455
493
  export async function revertTurnReviewFile(_worktree, sessionId, file) {
456
494
  const ownerSessionId = clean(sessionId);
457
495
  const tracker = _diffTrackersBySession.get(ownerSessionId);
496
+ if (tracker?.worktreeContended) {
497
+ throw new Error('turn review revert is unavailable while sessions share a worktree');
498
+ }
458
499
  if (!tracker?.worktreeSnapshot) throw new Error('turn worktree snapshot is unavailable');
459
500
  await revertTurnWorktreeFile(tracker.worktreeSnapshot, file);
460
501
  return await getTurnReviewDiff(_worktree, ownerSessionId);
@@ -43,15 +43,17 @@ function runGit(args, {
43
43
  allowFailure = false,
44
44
  } = {}) {
45
45
  return new Promise((resolvePromise, rejectPromise) => {
46
+ const hasInput = (typeof input === 'string' || Buffer.isBuffer(input)) && input.length > 0;
46
47
  const child = spawn('git', args, {
47
48
  cwd,
48
49
  windowsHide: true,
49
- stdio: ['pipe', 'pipe', 'pipe'],
50
+ stdio: [hasInput ? 'pipe' : 'ignore', 'pipe', 'pipe'],
50
51
  });
51
52
  const stdout = [];
52
53
  const stderr = [];
53
54
  let stdoutBytes = 0;
54
55
  let stderrBytes = 0;
56
+ let stdinError = null;
55
57
  let settled = false;
56
58
  const finish = (error, result) => {
57
59
  if (settled) return;
@@ -93,9 +95,19 @@ function runGit(args, {
93
95
  finish(commandError(args, result.stderr, result.code));
94
96
  return;
95
97
  }
98
+ if (stdinError && result.code === 0) {
99
+ finish(stdinError);
100
+ return;
101
+ }
96
102
  finish(null, result);
97
103
  });
98
- child.stdin.end(input);
104
+ if (child.stdin) {
105
+ // A short-lived git command can close before Node flushes stdin. Keep
106
+ // that transport race inside this promise instead of emitting an
107
+ // unhandled EPIPE that terminates the entire release validator.
108
+ child.stdin.on('error', (error) => { stdinError = error; });
109
+ child.stdin.end(input);
110
+ }
99
111
  });
100
112
  }
101
113
 
@@ -15,10 +15,10 @@ import { runScheduleSession } from '../runtime/shared/schedule-session-run.mjs';
15
15
 
16
16
  // Channel/webhook/schedule config surface. Extracted verbatim from the runtime
17
17
  // API object; the mutating admin helpers are imported directly here and the
18
- // runtime injects only the closure-owned callbacks (backend flush, channel
18
+ // runtime injects only the closure-owned callbacks (channel provider flush, channel
19
19
  // worker handle, soft reload).
20
20
  export function createChannelConfigApi({
21
- flushBackendSave,
21
+ flushChannelProviderSave,
22
22
  channels,
23
23
  reloadChannelsSoon,
24
24
  ensureAutomationRuntime = () => {},
@@ -26,10 +26,10 @@ export function createChannelConfigApi({
26
26
  }) {
27
27
  return {
28
28
  async getChannelSetup() {
29
- // Flush a pending debounced backend switch first so setup readers
29
+ // Flush a pending debounced channel provider switch first so setup readers
30
30
  // (Settings → Channel Setting, remote toggles) never observe the
31
- // previous backend during the 150ms debounce window.
32
- try { await flushBackendSave(); } catch {}
31
+ // previous provider during the 150ms debounce window.
32
+ try { await flushChannelProviderSave(); } catch {}
33
33
  // Bot tokens live in the OS keychain. Reading them before the batch
34
34
  // prewarm lands costs one SYNCHRONOUS PowerShell DPAPI decrypt each
35
35
  // (measured 971ms + 579ms on a cold desktop boot, blocking the whole
@@ -7,11 +7,13 @@ import { routeFastKey, fastPreferenceFor } from './model-capabilities.mjs';
7
7
  export const DEFAULT_PROVIDER = 'anthropic-oauth';
8
8
  export const DEFAULT_MODEL = '';
9
9
 
10
- // Resolve the provider to use when a route carries no explicit provider.
11
- // Priority: config.defaultProvider (when it names a known provider) > DEFAULT_PROVIDER.
10
+ // Resolve a provider-less MAIN selector against the selected Main preset.
11
+ // DEFAULT_PROVIDER is only the unconfigured bootstrap fallback (model remains
12
+ // empty); agent/search override resolution never calls this to synthesize one.
12
13
  export function makeResolveDefaultProvider(isKnownProvider) {
13
14
  return function resolveDefaultProvider(config) {
14
- const configured = clean(config?.defaultProvider);
15
+ const selectedMain = findPreset(config, config?.default);
16
+ const configured = clean(selectedMain?.provider);
15
17
  if (configured && isKnownProvider(configured)) return configured;
16
18
  return DEFAULT_PROVIDER;
17
19
  };
@@ -46,16 +48,18 @@ export function makeResolveRoute(resolveDefaultProvider) {
46
48
  if (explicitModel && !explicitProvider) {
47
49
  const preset = findPreset(config, explicitModel);
48
50
  if (preset) {
49
- const p = clean(preset.provider) || DEFAULT_PROVIDER;
51
+ const p = clean(preset.provider);
50
52
  const m = clean(preset.model) || DEFAULT_MODEL;
51
- const saved = modelSettingsFor(config, p, m);
52
- return {
53
- provider: p,
54
- model: m,
55
- preset,
56
- effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
57
- fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
58
- };
53
+ if (p && m) {
54
+ const saved = modelSettingsFor(config, p, m);
55
+ return {
56
+ provider: p,
57
+ model: m,
58
+ preset,
59
+ effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
60
+ fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
61
+ };
62
+ }
59
63
  }
60
64
  }
61
65
 
@@ -63,16 +67,18 @@ export function makeResolveRoute(resolveDefaultProvider) {
63
67
  const defaultKey = config?.default;
64
68
  const preset = findPreset(config, defaultKey);
65
69
  if (preset) {
66
- const p = clean(preset.provider) || DEFAULT_PROVIDER;
70
+ const p = clean(preset.provider);
67
71
  const m = clean(preset.model) || DEFAULT_MODEL;
68
- const saved = modelSettingsFor(config, p, m);
69
- return {
70
- provider: p,
71
- model: m,
72
- preset,
73
- effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
74
- fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
75
- };
72
+ if (p && m) {
73
+ const saved = modelSettingsFor(config, p, m);
74
+ return {
75
+ provider: p,
76
+ model: m,
77
+ preset,
78
+ effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
79
+ fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
80
+ };
81
+ }
76
82
  }
77
83
  }
78
84
 
@@ -197,14 +203,14 @@ export function resolveAgentTerminalReapMs(config, provider) {
197
203
  // fall back to the provider's default; else the global 1h default.
198
204
  export function resolveAutoClearIdleMs(config, provider) {
199
205
  const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
200
- const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
206
+ const idleMs = Number(raw.idleMs);
201
207
  if (Number.isFinite(idleMs) && idleMs > 0) return Math.max(60_000, Math.round(idleMs));
202
- return autoClearIdleMsForProvider(provider, raw.providerIdleMs ?? raw.providerDefaults ?? raw.providers);
208
+ return autoClearIdleMsForProvider(provider, raw.providerIdleMs);
203
209
  }
204
210
 
205
211
  export function normalizeSystemShellConfig(value = {}) {
206
212
  const raw = value && typeof value === 'object' ? value : {};
207
- const command = clean(raw.command ?? raw.path ?? raw.executable ?? raw.shell);
213
+ const command = clean(raw.command);
208
214
  const envCommand = clean(process.env.MIXDOG_SHELL);
209
215
  return {
210
216
  command,
@@ -227,7 +233,7 @@ export function normalizeSystemShellCommand(value) {
227
233
 
228
234
  export function normalizeAutoClearConfig(value = {}) {
229
235
  const raw = value && typeof value === 'object' ? value : {};
230
- const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
236
+ const idleMs = Number(raw.idleMs);
231
237
  const hasExplicitIdle = Number.isFinite(idleMs) && idleMs > 0;
232
238
  const rawMinPct = Number(raw.minContextPercent);
233
239
  const minContextPercent = Number.isFinite(rawMinPct)
@@ -241,7 +247,7 @@ export function normalizeAutoClearConfig(value = {}) {
241
247
  enabled: raw.enabled !== false,
242
248
  idleMs: hasExplicitIdle ? Math.max(60_000, Math.round(idleMs)) : null,
243
249
  custom: hasExplicitIdle,
244
- providerIdleMs: normalizeAutoClearProviderIdleMs(raw.providerIdleMs ?? raw.providerDefaults ?? raw.providers),
250
+ providerIdleMs: normalizeAutoClearProviderIdleMs(raw.providerIdleMs),
245
251
  minContextPercent,
246
252
  };
247
253
  }
@@ -254,16 +260,15 @@ export function normalizeCompactTypeSetting(value, fallback = 'recall-fasttrack'
254
260
  return fallback;
255
261
  }
256
262
 
257
- export function normalizeCompactionConfig(value = {}, { memoryEnabled = true } = {}) {
263
+ export function normalizeCompactionConfig(value = {}) {
258
264
  const raw = value && typeof value === 'object' ? value : {};
259
- let compactType = normalizeCompactTypeSetting(raw.compactType ?? raw.compact_type ?? raw.type, 'recall-fasttrack');
260
- // Memory is now always-on, so recall-fasttrack no longer downgrades to
261
- // semantic. `memoryEnabled` is retained as a param for API compatibility but
262
- // is intentionally ignored (fasttrack drains run on-demand regardless of the
263
- // recap/background-cycle toggle).
264
- void memoryEnabled;
265
+ const compactType = normalizeCompactTypeSetting(raw.type, 'recall-fasttrack');
266
+ const next = { ...raw };
267
+ delete next.compactType;
268
+ delete next.compact_type;
269
+ delete next.enabled;
265
270
  return {
266
- ...raw,
271
+ ...next,
267
272
  auto: raw.auto !== false && raw.enabled !== false,
268
273
  type: compactType,
269
274
  compactType,
@@ -284,9 +289,6 @@ export function moduleEnabled(configLike, name, fallback = true) {
284
289
  export function recapEnabled(configLike, fallback = true) {
285
290
  const entry = configLike?.recap;
286
291
  if (entry && typeof entry === 'object' && entry.enabled === false) return false;
287
- // Legacy fallback: pre-migration configs may still carry modules.memory=false.
288
- if (configLike?.modules?.memory === false) return false;
289
- if (moduleEnabled(configLike, 'memory', true) === false) return false;
290
292
  return fallback !== false;
291
293
  }
292
294
 
@@ -11,7 +11,7 @@
11
11
  // same-tick readers see fresh state, and DEBOUNCE the disk write so a burst of
12
12
  // toggles collapses into one persist. Three independent debounce channels:
13
13
  // - config save (cfgMod.saveConfig, agent-section serialize)
14
- // - backend save (channel-admin setBackend, file-locked RMW)
14
+ // - channel provider save (channel-admin setChannelProvider, file-locked RMW)
15
15
  // - outputStyle (sharedCfgMod.updateConfig whole-root RMW — cfgMod.saveConfig
16
16
  // only serializes agent-section fields, so a top-level
17
17
  // outputStyle would never reach disk via that path)
@@ -30,7 +30,7 @@ export function createConfigLifecycle({
30
30
  // shared modules / helpers
31
31
  cfgMod,
32
32
  sharedCfgMod,
33
- setBackendAsync,
33
+ setChannelProviderAsync,
34
34
  setConfiguredShell,
35
35
  normalizeSystemShellConfig,
36
36
  normalizeSearchRouteConfig,
@@ -188,40 +188,40 @@ export function createConfigLifecycle({
188
188
  return adopted;
189
189
  }
190
190
 
191
- // --- debounced backend switch ----------------------------------------------
192
- let pendingBackendName = null;
193
- let backendSaveTimer = null;
194
- let backendFlushInFlight = null;
191
+ // --- debounced channel provider switch ----------------------------------------------
192
+ let pendingChannelProvider = null;
193
+ let channelProviderSaveTimer = null;
194
+ let channelProviderFlushInFlight = null;
195
195
 
196
- async function runBackendFlushAsync() {
197
- while (pendingBackendName !== null) {
198
- const name = pendingBackendName;
196
+ async function runChannelProviderFlushAsync() {
197
+ while (pendingChannelProvider !== null) {
198
+ const name = pendingChannelProvider;
199
199
  try {
200
- await setBackendAsync(name);
200
+ await setChannelProviderAsync(name);
201
201
  } catch (err) {
202
- process.stderr.write(`[channels] async setBackend failed: ${err?.message || err}\n`);
203
- if (pendingBackendName === name) pendingBackendName = null;
202
+ process.stderr.write(`[channels] async setChannelProvider failed: ${err?.message || err}\n`);
203
+ if (pendingChannelProvider === name) pendingChannelProvider = null;
204
204
  break;
205
205
  }
206
- if (pendingBackendName === name) pendingBackendName = null;
206
+ if (pendingChannelProvider === name) pendingChannelProvider = null;
207
207
  }
208
208
  }
209
209
 
210
- function flushBackendSaveAsync() {
211
- if (backendSaveTimer) { clearTimeout(backendSaveTimer); backendSaveTimer = null; }
212
- const start = () => runBackendFlushAsync();
213
- const p = backendFlushInFlight ? backendFlushInFlight.then(start, start) : start();
214
- backendFlushInFlight = p;
215
- const clear = () => { if (backendFlushInFlight === p) backendFlushInFlight = null; };
210
+ function flushChannelProviderSaveAsync() {
211
+ if (channelProviderSaveTimer) { clearTimeout(channelProviderSaveTimer); channelProviderSaveTimer = null; }
212
+ const start = () => runChannelProviderFlushAsync();
213
+ const p = channelProviderFlushInFlight ? channelProviderFlushInFlight.then(start, start) : start();
214
+ channelProviderFlushInFlight = p;
215
+ const clear = () => { if (channelProviderFlushInFlight === p) channelProviderFlushInFlight = null; };
216
216
  p.then(clear, clear);
217
217
  return p;
218
218
  }
219
219
 
220
- function scheduleBackendSave(name) {
221
- pendingBackendName = name;
222
- if (backendSaveTimer) clearTimeout(backendSaveTimer);
223
- backendSaveTimer = setTimeout(() => { flushBackendSaveAsync(); }, CONFIG_SAVE_DEBOUNCE_MS);
224
- backendSaveTimer.unref?.();
220
+ function scheduleChannelProviderSave(name) {
221
+ pendingChannelProvider = name;
222
+ if (channelProviderSaveTimer) clearTimeout(channelProviderSaveTimer);
223
+ channelProviderSaveTimer = setTimeout(() => { flushChannelProviderSaveAsync(); }, CONFIG_SAVE_DEBOUNCE_MS);
224
+ channelProviderSaveTimer.unref?.();
225
225
  }
226
226
 
227
227
  // --- debounced skills.disabled persist -------------------------------------
@@ -326,7 +326,7 @@ export function createConfigLifecycle({
326
326
  async function flushAllConfigSavesAsync() {
327
327
  await Promise.all([
328
328
  flushConfigSaveAsync(),
329
- flushBackendSaveAsync(),
329
+ flushChannelProviderSaveAsync(),
330
330
  flushOutputStyleSaveAsync(),
331
331
  ]);
332
332
  // The shared config layer also tracks writes started directly by channel,
@@ -420,8 +420,8 @@ export function createConfigLifecycle({
420
420
  // Every lifecycle flush uses the async lock path. A synchronous waiter on
421
421
  // the same lock would block the event loop needed by an in-flight async
422
422
  // writer, so callers must await this before a dependent read/start.
423
- flushBackendSave: flushBackendSaveAsync,
424
- scheduleBackendSave,
423
+ flushChannelProviderSave: flushChannelProviderSaveAsync,
424
+ scheduleChannelProviderSave,
425
425
  flushSkillsSave,
426
426
  scheduleSkillsSave,
427
427
  flushOutputStyleSave,
@@ -15,6 +15,7 @@ import {
15
15
  getStoreDir,
16
16
  listSessionHeartbeatMtimes,
17
17
  } from '../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
18
+ import { SessionClosedError } from '../runtime/agent/orchestrator/session/manager/session-errors.mjs';
18
19
 
19
20
  export function resolveResumeCwd(session, currentCwd) {
20
21
  const desktop = session?.desktopSession;
@@ -52,7 +53,7 @@ export function createLifecycleApi(deps) {
52
53
  beginRoutePreparation, clearRoutePreparation,
53
54
  pushTranscriptRebind,
54
55
  notificationListeners, remoteStateListeners, desktopSession,
55
- disposeSessionTitles,
56
+ disposeSessionTitles, abortActiveTurns, getReservedSessionId,
56
57
  } = deps;
57
58
  const closeSurfaceSession = (session, reason, options) => {
58
59
  if (!session?.id) return false;
@@ -168,6 +169,14 @@ export function createLifecycleApi(deps) {
168
169
  // of reaping every session's jobs. CLI exit paths never set this.
169
170
  const keepBackgroundWork = options?.keepBackgroundWork === true;
170
171
  setCloseRequested(true);
172
+ const closingTurnId = getSession()?.id || getReservedSessionId?.() || 'pending';
173
+ try {
174
+ abortActiveTurns?.(new SessionClosedError(
175
+ closingTurnId,
176
+ `runtime close (reason=${reason})`,
177
+ reason,
178
+ ));
179
+ } catch {}
171
180
  try { stopSelfUpdateBootCheck?.(); } catch {}
172
181
  try { disposeSessionTitles?.(); } catch {}
173
182
  // Self-update now stages in the background and swaps on the next clean
@@ -335,8 +344,18 @@ export function createLifecycleApi(deps) {
335
344
  },
336
345
  abort(reason = 'cli-abort') {
337
346
  const session = getSession();
338
- if (!session?.id) return false;
339
- return mgr.abortSessionTurn(session.id, reason);
347
+ const sessionId = session?.id || getReservedSessionId?.() || 'pending';
348
+ const abortError = new SessionClosedError(
349
+ sessionId,
350
+ `runtime abort (reason=${reason})`,
351
+ reason,
352
+ );
353
+ let outerAborted = false;
354
+ try { outerAborted = abortActiveTurns?.(abortError) === true; } catch {}
355
+ const managerAborted = session?.id
356
+ ? mgr.abortSessionTurn(session.id, reason)
357
+ : false;
358
+ return outerAborted || managerAborted;
340
359
  },
341
360
  listSessions(options = {}) {
342
361
  return listLeadSessions(options);
@@ -0,0 +1,15 @@
1
+ // Eager in-process memory initialization for remote sessions. Channel ingest
2
+ // shares the daemon-owned proxy, so readiness is the isolated process/port
3
+ // singleflight returned by getMemoryModule.
4
+
5
+ export function prewarmMemoryRuntime({ getMemoryModule, bootProfile }) {
6
+ void (async () => {
7
+ try {
8
+ await new Promise((resolve) => setImmediate(resolve))
9
+ await getMemoryModule()
10
+ bootProfile('channels:memory-prewarm-ready')
11
+ } catch (error) {
12
+ bootProfile('channels:memory-prewarm-failed', { error: error?.message || String(error) })
13
+ }
14
+ })()
15
+ }
@@ -64,11 +64,13 @@ export function createModelRouteApi(deps) {
64
64
  return await collectSearchProviderModels({ force: options.force === true || options.refresh === true });
65
65
  },
66
66
  async setSearchRoute(next) {
67
- const searchRoute = getSearchRouteState();
68
- let selectedRoute = normalizeSearchRouteConfig(next);
69
- if (!selectedRoute && next?.model && searchRoute?.provider) {
70
- selectedRoute = normalizeSearchRouteConfig({ ...next, provider: searchRoute.provider });
71
- }
67
+ let selectedRoute = clean(next?.provider)
68
+ ? normalizeSearchRouteConfig(next)
69
+ : normalizeSearchRouteConfig({
70
+ provider: SEARCH_DEFAULT_PROVIDER,
71
+ model: SEARCH_DEFAULT_MODEL,
72
+ ...(next?.toolType ? { toolType: next.toolType } : {}),
73
+ });
72
74
  if (!selectedRoute) throw new Error('search route requires provider and model');
73
75
  if (isDefaultSearchRouteConfig(selectedRoute)) {
74
76
  await awaitKeychainPrewarm();
@@ -132,7 +132,7 @@ export function createPrewarmSchedulers({
132
132
  if (isCloseRequested()) return;
133
133
  // Channels-module and remote toggles gate MESSAGING; automation
134
134
  // (enabled schedules/webhooks) keeps the worker boot alive — its
135
- // backend runs headless when messaging is off or unconfigured.
135
+ // channel provider runs headless when messaging is off or unconfigured.
136
136
  const automation = await hasActiveAutomation().catch(() => false);
137
137
  if (!channelsEnabled() && !automation) {
138
138
  bootProfile('channels:start-disabled');
@@ -193,12 +193,5 @@ export function createProviderAuthApi({
193
193
  quick: options.quick === true,
194
194
  });
195
195
  },
196
- async setDefaultProvider(provider) {
197
- const requested = clean(provider);
198
- if (!requested) throw new Error('provider is required');
199
- if (!isKnownProvider(requested)) throw new Error(`unknown provider "${provider}"`);
200
- saveConfigAndAdopt({ ...getConfig(), defaultProvider: requested });
201
- return requested;
202
- },
203
196
  };
204
197
  }
@@ -16,7 +16,7 @@ const PROVIDER_MODELS_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(String(
16
16
  ));
17
17
 
18
18
  // Raw provider model lists are process-global because the provider registry is
19
- // process-global in the backend daemon. Session runtimes keep only the cheap
19
+ // process-global in the daemon. Session runtimes keep only the cheap
20
20
  // hydrated projection (saved effort/fast/current-route ordering). This stops
21
21
  // every open pane from repeating the same provider list walk after startup.
22
22
  let sharedCatalogRevision = -1;
@@ -2,7 +2,7 @@
2
2
  // Remote mode is opt-in per session — only a session that turns it on boots the
3
3
  // channel worker and contends for the machine-global bridge seat. Extracted
4
4
  // from runtime-core, which injects the mutable session/remote state it owns.
5
- import { startMemoryDaemonEagerly } from './memory-daemon-probe.mjs';
5
+ import { prewarmMemoryRuntime } from './memory-runtime-prewarm.mjs';
6
6
 
7
7
  export function createRemoteControl({
8
8
  getSession,
@@ -16,7 +16,7 @@ export function createRemoteControl({
16
16
  channels,
17
17
  channelsEnabled,
18
18
  hasActiveAutomation,
19
- flushBackendSave,
19
+ flushChannelProviderSave,
20
20
  invokeChannelStart,
21
21
  createCurrentSession,
22
22
  ensureRemoteTranscriptWriter,
@@ -31,9 +31,8 @@ export function createRemoteControl({
31
31
  function startRemote() {
32
32
  setRemoteEnabled(true);
33
33
  setRemoteSessionId(getSession()?.id || null);
34
- // The worker forwards transcript ingests to the memory service, so its port
35
- // must be live before the first ingest.
36
- startMemoryDaemonEagerly({ getMemoryModule, bootProfile });
34
+ // Warm the daemon-hosted memory module before the first remote ingest.
35
+ prewarmMemoryRuntime({ getMemoryModule, bootProfile });
37
36
  // Publish the session record + transcript file BEFORE the worker's
38
37
  // activate-time discovery polls, so forwarding binds to THIS terminal
39
38
  // instead of a stale neighbour. A no-op in lazy mode; the turn-start rebind
@@ -54,9 +53,9 @@ export function createRemoteControl({
54
53
  bootProfile('channels:start-disabled');
55
54
  return isRemoteEnabled();
56
55
  }
57
- // A backend switch may still be writing; drain it before the worker
56
+ // A channel provider switch may still be writing; drain it before the worker
58
57
  // reads config rather than racing a sync lock wait.
59
- try { await flushBackendSave(); } catch { /* best-effort */ }
58
+ try { await flushChannelProviderSave(); } catch { /* best-effort */ }
60
59
  // Yield before the create/transcript/fork chain so Ink's queued render
61
60
  // and input handling are not starved by this detached chain.
62
61
  await new Promise((resolve) => setImmediate(resolve));