mixdog 0.9.3 → 0.9.4

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 (329) hide show
  1. package/package.json +7 -3
  2. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  3. package/scripts/bench/lead-review-tasks.json +20 -0
  4. package/scripts/bench/r4-mixed-tasks.json +20 -0
  5. package/scripts/bench/review-tasks.json +20 -0
  6. package/scripts/bench/round-codex.json +114 -0
  7. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  8. package/scripts/bench/round-mixdog-lead.json +269 -0
  9. package/scripts/bench/round-mixdog.json +126 -0
  10. package/scripts/bench/round-r10-bigsample.json +679 -0
  11. package/scripts/bench/round-r11-codexalign.json +257 -0
  12. package/scripts/bench/round-r4-codex.json +114 -0
  13. package/scripts/bench/round-r4-mixed.json +225 -0
  14. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  15. package/scripts/bench/round-r6-codex.json +114 -0
  16. package/scripts/bench/round-r6-solo.json +257 -0
  17. package/scripts/bench/round-r7-full.json +254 -0
  18. package/scripts/bench/round-r8-fulldefault.json +255 -0
  19. package/scripts/bench-run.mjs +215 -29
  20. package/scripts/freevar-smoke.mjs +95 -0
  21. package/scripts/internal-comms-bench.mjs +1 -0
  22. package/scripts/internal-comms-smoke.mjs +10 -9
  23. package/scripts/mouse-probe.mjs +45 -0
  24. package/scripts/output-style-bench.mjs +13 -6
  25. package/scripts/output-style-smoke.mjs +4 -4
  26. package/scripts/provider-toolcall-test.mjs +7 -3
  27. package/scripts/recall-usecase-cases.json +18 -0
  28. package/scripts/recall-usecase-probe.json +6 -0
  29. package/scripts/session-bench.mjs +152 -6
  30. package/scripts/tool-smoke.mjs +23 -63
  31. package/scripts/tui-render-smoke.mjs +90 -0
  32. package/scripts/webhook-smoke.mjs +208 -0
  33. package/src/agents/debugger/AGENT.md +4 -1
  34. package/src/agents/heavy-worker/AGENT.md +6 -5
  35. package/src/agents/maintainer/AGENT.md +4 -0
  36. package/src/agents/reviewer/AGENT.md +2 -1
  37. package/src/agents/worker/AGENT.md +8 -4
  38. package/src/lib/rules-builder.cjs +4 -0
  39. package/src/mixdog-session-runtime.mjs +632 -2042
  40. package/src/output-styles/default.md +34 -9
  41. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  42. package/src/output-styles/minimal.md +4 -1
  43. package/src/output-styles/simple.md +22 -7
  44. package/src/rules/agent/00-common.md +2 -0
  45. package/src/rules/lead/lead-brief.md +12 -0
  46. package/src/rules/lead/lead-tool.md +0 -11
  47. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  49. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  50. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  51. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  52. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  53. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  54. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  55. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  60. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  62. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  63. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  65. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  66. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  67. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  68. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
  77. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  78. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  79. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  80. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  83. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  84. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  85. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  87. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  88. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  89. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  90. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  91. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  93. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  94. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  95. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  97. package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
  98. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  99. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  100. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  101. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  102. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  103. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  104. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  105. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  106. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  109. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
  110. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  118. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  120. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  122. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  123. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  125. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  126. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  127. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  128. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  129. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  130. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  131. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  132. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  133. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  134. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  135. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  136. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  137. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  138. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  139. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  140. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  141. package/src/runtime/channels/backends/discord.mjs +12 -292
  142. package/src/runtime/channels/index.mjs +229 -663
  143. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  144. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  145. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  146. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  147. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  148. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  149. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  150. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  151. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  152. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  153. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  154. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  155. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  156. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  157. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  158. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  159. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  160. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  161. package/src/runtime/channels/lib/webhook.mjs +36 -570
  162. package/src/runtime/channels/tool-defs.mjs +11 -130
  163. package/src/runtime/memory/index.mjs +201 -1948
  164. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  165. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  166. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  167. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  168. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  169. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  170. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  171. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  172. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  173. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  174. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  175. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  177. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  178. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  179. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  180. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  181. package/src/runtime/memory/tool-defs.mjs +5 -13
  182. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  183. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  184. package/src/runtime/search/lib/web-tools.mjs +24 -602
  185. package/src/runtime/shared/atomic-file.mjs +26 -1
  186. package/src/runtime/shared/launcher-control.mjs +2 -2
  187. package/src/runtime/shared/tool-primitives.mjs +308 -0
  188. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  189. package/src/runtime/shared/tool-surface.mjs +80 -898
  190. package/src/runtime/shared/transcript-writer.mjs +23 -0
  191. package/src/runtime/shared/update-checker.mjs +7 -4
  192. package/src/session-runtime/config-helpers.mjs +84 -2
  193. package/src/session-runtime/config-lifecycle.mjs +232 -0
  194. package/src/session-runtime/cwd-plugins.mjs +226 -0
  195. package/src/session-runtime/mcp-glue.mjs +177 -0
  196. package/src/session-runtime/model-recency.mjs +111 -0
  197. package/src/session-runtime/native-search.mjs +247 -0
  198. package/src/session-runtime/output-styles.mjs +11 -9
  199. package/src/session-runtime/prewarm.mjs +142 -0
  200. package/src/session-runtime/provider-models.mjs +278 -0
  201. package/src/session-runtime/provider-usage.mjs +120 -0
  202. package/src/session-runtime/quick-model-rows.mjs +170 -0
  203. package/src/session-runtime/quick-search-models.mjs +46 -0
  204. package/src/session-runtime/session-hooks.mjs +93 -0
  205. package/src/session-runtime/settings-api.mjs +319 -0
  206. package/src/session-runtime/tool-catalog.mjs +29 -29
  207. package/src/session-runtime/tool-defs.mjs +84 -0
  208. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  209. package/src/standalone/agent-tool/helpers.mjs +237 -0
  210. package/src/standalone/agent-tool/notify.mjs +107 -0
  211. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  212. package/src/standalone/agent-tool/render.mjs +152 -0
  213. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  214. package/src/standalone/agent-tool.mjs +110 -671
  215. package/src/standalone/channel-worker.mjs +4 -7
  216. package/src/standalone/explore-tool.mjs +30 -9
  217. package/src/standalone/hook-bus/config.mjs +207 -0
  218. package/src/standalone/hook-bus/constants.mjs +90 -0
  219. package/src/standalone/hook-bus/handlers.mjs +481 -0
  220. package/src/standalone/hook-bus/payload.mjs +31 -0
  221. package/src/standalone/hook-bus/rules.mjs +77 -0
  222. package/src/standalone/hook-bus.mjs +77 -870
  223. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  224. package/src/standalone/opencode-go-login.mjs +5 -1
  225. package/src/standalone/provider-admin.mjs +1 -16
  226. package/src/standalone/usage-dashboard.mjs +3 -1
  227. package/src/tui/App.jsx +945 -8094
  228. package/src/tui/app/app-format.mjs +206 -0
  229. package/src/tui/app/channel-pickers.mjs +510 -0
  230. package/src/tui/app/clipboard.mjs +67 -0
  231. package/src/tui/app/core-memory-picker.mjs +210 -0
  232. package/src/tui/app/extension-pickers.mjs +506 -0
  233. package/src/tui/app/input-parsers.mjs +193 -0
  234. package/src/tui/app/maintenance-pickers.mjs +324 -0
  235. package/src/tui/app/model-options.mjs +330 -0
  236. package/src/tui/app/model-picker.mjs +365 -0
  237. package/src/tui/app/onboarding-steps.mjs +400 -0
  238. package/src/tui/app/project-picker.mjs +247 -0
  239. package/src/tui/app/provider-setup-picker.mjs +580 -0
  240. package/src/tui/app/resume-picker.mjs +55 -0
  241. package/src/tui/app/route-pickers.mjs +419 -0
  242. package/src/tui/app/settings-picker.mjs +490 -0
  243. package/src/tui/app/slash-commands.mjs +101 -0
  244. package/src/tui/app/slash-dispatch.mjs +427 -0
  245. package/src/tui/app/text-layout.mjs +46 -0
  246. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  247. package/src/tui/app/transcript-window.mjs +671 -0
  248. package/src/tui/app/use-mouse-input.mjs +460 -0
  249. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  250. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  251. package/src/tui/app/use-transcript-window.mjs +589 -0
  252. package/src/tui/components/ConfirmBar.jsx +1 -1
  253. package/src/tui/components/Picker.jsx +32 -4
  254. package/src/tui/components/PromptInput.jsx +23 -101
  255. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  256. package/src/tui/components/StatusLine.jsx +63 -12
  257. package/src/tui/components/TextEntryPanel.jsx +11 -0
  258. package/src/tui/components/ToolExecution.jsx +52 -594
  259. package/src/tui/components/TranscriptItem.jsx +105 -0
  260. package/src/tui/components/UsagePanel.jsx +18 -4
  261. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  262. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  263. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  264. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  265. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  266. package/src/tui/display-width.mjs +20 -3
  267. package/src/tui/dist/index.mjs +19652 -18630
  268. package/src/tui/engine/agent-job-feed.mjs +133 -0
  269. package/src/tui/engine/notification-plan.mjs +76 -0
  270. package/src/tui/engine/render-timing.mjs +17 -0
  271. package/src/tui/engine/tool-approval.mjs +94 -0
  272. package/src/tui/engine/tool-card-results.mjs +234 -0
  273. package/src/tui/engine/tool-result-status.mjs +135 -0
  274. package/src/tui/engine.mjs +122 -562
  275. package/src/tui/figures.mjs +5 -0
  276. package/src/tui/index.jsx +105 -0
  277. package/src/tui/input-editing.mjs +2 -2
  278. package/src/tui/markdown/format-token.mjs +4 -1
  279. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  280. package/src/tui/theme.mjs +6 -0
  281. package/src/ui/statusline-agents.mjs +213 -0
  282. package/src/ui/statusline-format.mjs +146 -0
  283. package/src/ui/statusline-segments.mjs +148 -0
  284. package/src/ui/statusline.mjs +67 -501
  285. package/src/ui/tool-card.mjs +0 -1
  286. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  287. package/src/workflows/default/WORKFLOW.md +1 -1
  288. package/src/workflows/sequential/WORKFLOW.md +1 -1
  289. package/vendor/ink/build/display-width.js +19 -3
  290. package/vendor/ink/build/ink.js +103 -6
  291. package/vendor/ink/build/log-update.js +17 -3
  292. package/vendor/ink/build/wrap-text.js +125 -0
  293. package/scripts/_test-folder-dialog.mjs +0 -30
  294. package/scripts/fix-brief-fn.mjs +0 -35
  295. package/scripts/fix-format-tool-surface.mjs +0 -24
  296. package/scripts/fix-tool-exec-visible.mjs +0 -42
  297. package/scripts/patch-agent-brief.mjs +0 -48
  298. package/scripts/patch-app.mjs +0 -21
  299. package/scripts/patch-app2.mjs +0 -18
  300. package/scripts/patch-dist-brief.mjs +0 -96
  301. package/scripts/patch-tool-exec.mjs +0 -70
  302. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  303. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  304. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  305. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  306. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  307. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  308. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  309. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  310. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  311. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  312. package/src/standalone/agent-task-status.test.mjs +0 -76
  313. package/src/tui/components/tool-output-format.test.mjs +0 -399
  314. package/src/tui/display-width.test.mjs +0 -35
  315. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  316. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  317. package/src/tui/input-editing.selection.test.mjs +0 -75
  318. package/src/tui/markdown/format-token.test.mjs +0 -354
  319. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  320. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  321. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  322. package/src/tui/paste-fix.test.mjs +0 -119
  323. package/src/tui/prompt-history-store.test.mjs +0 -52
  324. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  325. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  326. package/src/ui/markdown.test.mjs +0 -70
  327. package/src/ui/statusline-context-label.test.mjs +0 -15
  328. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  329. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -144,10 +144,33 @@ export function createTranscriptWriter({ mixdogHome, sessionId, cwd, pid } = {})
144
144
  });
145
145
  }
146
146
 
147
+ // Idempotently create the transcript file as an empty 0-byte file if it
148
+ // does not already exist. Called on every remote turn (before the first
149
+ // append) so the channel worker's fs.watch can attach to the path right
150
+ // away instead of waiting on schedulePendingTranscriptRearm(). Never
151
+ // truncates an existing transcript: guarded by existsSync + the 'wx'
152
+ // (exclusive-create) flag, and any EEXIST/other error is swallowed via
153
+ // logOnce like the other writers.
154
+ function ensureTranscriptFile() {
155
+ try {
156
+ ensureProjectDir();
157
+ if (existsSync(transcriptPath)) return;
158
+ try {
159
+ writeFileSync(transcriptPath, '', { flag: 'wx' });
160
+ } catch (err) {
161
+ if (err && err.code === 'EEXIST') return;
162
+ logOnce(err);
163
+ }
164
+ } catch (err) {
165
+ logOnce(err);
166
+ }
167
+ }
168
+
147
169
  return {
148
170
  transcriptPath,
149
171
  sessionRecordPath,
150
172
  writeSessionRecord,
173
+ ensureTranscriptFile,
151
174
  refresh,
152
175
  appendAssistant,
153
176
  appendToolUse,
@@ -154,12 +154,15 @@ export async function checkLatestVersion({ force = false, dataDir } = {}) {
154
154
  }
155
155
  // Registry unreachable: fall back to a stale cache (if any) rather than
156
156
  // reporting "no update info" every time the network hiccups.
157
- if (cache && cache.latestVersion) {
157
+ // Re-read the cache here: a force:true call skips the read above, but a
158
+ // stale answer still beats none when the network is down.
159
+ const staleCache = cache || readCache(dataDir);
160
+ if (staleCache && staleCache.latestVersion) {
158
161
  return {
159
162
  currentVersion,
160
- latestVersion: cache.latestVersion,
161
- updateAvailable: isNewerVersion(cache.latestVersion, currentVersion),
162
- lastCheckedAt: cache.lastCheckedAt || now,
163
+ latestVersion: staleCache.latestVersion,
164
+ updateAvailable: isNewerVersion(staleCache.latestVersion, currentVersion),
165
+ lastCheckedAt: staleCache.lastCheckedAt || now,
163
166
  fromCache: true,
164
167
  };
165
168
  }
@@ -112,6 +112,45 @@ export function ensureProviderEnabled(config, provider) {
112
112
 
113
113
  const AUTO_CLEAR_DEFAULT_IDLE_MS = 60 * 60 * 1000;
114
114
 
115
+ // Provider-aware auto-clear idle-sweep defaults. Rationale mirrors the
116
+ // provider cache TTLs documented in agent-runtime/cache-strategy.mjs
117
+ // (explicit breakpoint / managed-cache windows): Anthropic, Gemini, xAI and
118
+ // Mistral caches run ~1h, Groq's implicit cache persists ~2h, OpenAI/
119
+ // DeepSeek key-prefix/implicit caches persist far longer (~24h), and OpenAI
120
+ // OAuth's in-memory server cache is short-lived (~5-10min), so we round up
121
+ // slightly to 10m to avoid sweeping a still-warm cache. Unknown/
122
+ // unrecognized providers fall back to the 'default' 1h entry.
123
+ export const AUTO_CLEAR_PROVIDER_IDLE_MS = Object.freeze({
124
+ 'anthropic': 60 * 60 * 1000,
125
+ 'anthropic-oauth': 60 * 60 * 1000,
126
+ 'gemini': 60 * 60 * 1000,
127
+ 'groq': 2 * 60 * 60 * 1000,
128
+ 'openai': 24 * 60 * 60 * 1000,
129
+ 'deepseek': 24 * 60 * 60 * 1000,
130
+ 'xai': 60 * 60 * 1000,
131
+ 'openai-oauth': 10 * 60 * 1000,
132
+ 'mistral': 60 * 60 * 1000,
133
+ 'default': AUTO_CLEAR_DEFAULT_IDLE_MS,
134
+ });
135
+ export const AUTO_CLEAR_UNKNOWN_PROVIDER_IDLE_MS = AUTO_CLEAR_PROVIDER_IDLE_MS.default;
136
+
137
+ // Resolve the auto-clear idle-sweep threshold for a given provider id.
138
+ // Unknown/missing providers fall back to the 'default' 1h entry (above).
139
+ export function autoClearIdleMsForProvider(provider) {
140
+ const key = clean(provider).toLowerCase();
141
+ return AUTO_CLEAR_PROVIDER_IDLE_MS[key] ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
142
+ }
143
+
144
+ // Resolve the effective auto-clear idle window for a config + provider:
145
+ // an explicit user-set idleMs (config.autoClear.idleMs) always wins; else
146
+ // fall back to the provider's default; else the global 1h default.
147
+ export function resolveAutoClearIdleMs(config, provider) {
148
+ const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
149
+ const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
150
+ if (Number.isFinite(idleMs) && idleMs > 0) return Math.max(60_000, Math.round(idleMs));
151
+ return autoClearIdleMsForProvider(provider);
152
+ }
153
+
115
154
  export function normalizeSystemShellConfig(value = {}) {
116
155
  const raw = value && typeof value === 'object' ? value : {};
117
156
  const command = clean(raw.command ?? raw.path ?? raw.executable ?? raw.shell);
@@ -138,9 +177,15 @@ export function normalizeSystemShellCommand(value) {
138
177
  export function normalizeAutoClearConfig(value = {}) {
139
178
  const raw = value && typeof value === 'object' ? value : {};
140
179
  const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
180
+ const hasExplicitIdle = Number.isFinite(idleMs) && idleMs > 0;
181
+ // idleMs: null means "no explicit override" — callers resolve the
182
+ // effective window via resolveAutoClearIdleMs/provider default. `custom`
183
+ // tells UI whether the stored idleMs is a user override (true) or the
184
+ // provider-default mode (false, idleMs null).
141
185
  return {
142
186
  enabled: raw.enabled !== false,
143
- idleMs: Number.isFinite(idleMs) && idleMs > 0 ? Math.max(60_000, Math.round(idleMs)) : AUTO_CLEAR_DEFAULT_IDLE_MS,
187
+ idleMs: hasExplicitIdle ? Math.max(60_000, Math.round(idleMs)) : null,
188
+ custom: hasExplicitIdle,
144
189
  };
145
190
  }
146
191
 
@@ -155,7 +200,11 @@ export function normalizeCompactTypeSetting(value, fallback = 'semantic') {
155
200
  export function normalizeCompactionConfig(value = {}, { memoryEnabled = true } = {}) {
156
201
  const raw = value && typeof value === 'object' ? value : {};
157
202
  let compactType = normalizeCompactTypeSetting(raw.compactType ?? raw.compact_type ?? raw.type, 'semantic');
158
- if (compactType === 'recall-fasttrack' && memoryEnabled === false) compactType = 'semantic';
203
+ // Memory is now always-on, so recall-fasttrack no longer downgrades to
204
+ // semantic. `memoryEnabled` is retained as a param for API compatibility but
205
+ // is intentionally ignored (fasttrack drains run on-demand regardless of the
206
+ // recap/background-cycle toggle).
207
+ void memoryEnabled;
159
208
  return {
160
209
  ...raw,
161
210
  auto: raw.auto !== false && raw.enabled !== false,
@@ -170,6 +219,39 @@ export function moduleEnabled(configLike, name, fallback = true) {
170
219
  return fallback !== false;
171
220
  }
172
221
 
222
+ // Recap toggle: gates ONLY the background memory cycles (1/2/3). The memory
223
+ // module itself (transcript watcher/ingest, on-demand recall/fasttrack drains)
224
+ // is always-on. Default enabled. Reads the dedicated `recap` section; if a
225
+ // legacy `modules.memory === false` flag is present it is honored as recap off
226
+ // (migration folds it into `recap.enabled` on config load).
227
+ export function recapEnabled(configLike, fallback = true) {
228
+ const entry = configLike?.recap;
229
+ if (entry && typeof entry === 'object' && entry.enabled === false) return false;
230
+ // Legacy fallback: pre-migration configs may still carry modules.memory=false.
231
+ if (configLike?.modules?.memory === false) return false;
232
+ if (moduleEnabled(configLike, 'memory', true) === false) return false;
233
+ return fallback !== false;
234
+ }
235
+
236
+ export function setRecapEnabledInConfig(configLike, enabled) {
237
+ const next = { ...(configLike || {}) };
238
+ next.recap = { ...(next.recap || {}), enabled: enabled !== false };
239
+ // Drop the legacy flag if it was carrying the disabled state.
240
+ if (next.modules && next.modules.memory === false) {
241
+ const modules = { ...next.modules };
242
+ delete modules.memory;
243
+ next.modules = modules;
244
+ } else if (next.modules && next.modules.memory && typeof next.modules.memory === 'object') {
245
+ const modules = { ...next.modules };
246
+ const memoryMod = { ...modules.memory };
247
+ delete memoryMod.enabled;
248
+ if (Object.keys(memoryMod).length === 0) delete modules.memory;
249
+ else modules.memory = memoryMod;
250
+ next.modules = modules;
251
+ }
252
+ return next;
253
+ }
254
+
173
255
  export function setModuleEnabledInConfig(configLike, name, enabled) {
174
256
  const next = { ...(configLike || {}) };
175
257
  next.modules = { ...(next.modules || {}) };
@@ -0,0 +1,232 @@
1
+ // config-lifecycle.mjs — config reload/save/adopt family + output-style status
2
+ // cache, extracted from mixdog-session-runtime.mjs. Dependency-injected factory:
3
+ // closes over config/searchRoute mutable state via getter/setter injection
4
+ // (getConfig/setConfig/getSearchRoute/setSearchRoute) and shared helpers, so the
5
+ // facade keeps ownership of the mutable locals while the debounce/adopt logic
6
+ // lives here.
7
+ //
8
+ // Debounce rationale (unchanged from the original inline implementation):
9
+ // persisting mixdog-config.json is heavy (cross-process lock, atomic
10
+ // temp+rename, win32 icacls owner-only ACL). Adopt in-memory IMMEDIATELY so
11
+ // same-tick readers see fresh state, and DEBOUNCE the disk write so a burst of
12
+ // toggles collapses into one persist. Three independent debounce channels:
13
+ // - config save (cfgMod.saveConfig, agent-section serialize)
14
+ // - backend save (channel-admin setBackend, file-locked RMW)
15
+ // - outputStyle (sharedCfgMod.updateConfig whole-root RMW — cfgMod.saveConfig
16
+ // only serializes agent-section fields, so a top-level
17
+ // outputStyle would never reach disk via that path)
18
+
19
+ export const CONFIG_SAVE_DEBOUNCE_MS = 150;
20
+
21
+ export function createConfigLifecycle({
22
+ // config mutable-state injection
23
+ getConfig,
24
+ setConfig,
25
+ getSearchRoute,
26
+ setSearchRoute,
27
+ getConfigHasSecrets,
28
+ setConfigHasSecrets,
29
+ getRoute,
30
+ // shared modules / helpers
31
+ cfgMod,
32
+ sharedCfgMod,
33
+ setBackend,
34
+ setConfiguredShell,
35
+ normalizeSystemShellConfig,
36
+ normalizeSearchRouteConfig,
37
+ outputStyleStatus,
38
+ LAZY_SECRET_PROVIDERS,
39
+ clean,
40
+ resolve,
41
+ performanceNow = () => performance.now(),
42
+ STANDALONE_DATA_DIR,
43
+ }) {
44
+ // --- output-style status cache (short TTL, keyed on plugin data dir) --------
45
+ let outputStyleStatusCache = null;
46
+ let outputStyleStatusCacheAt = 0;
47
+ let outputStyleStatusCacheDir = '';
48
+
49
+ const getOutputStyleStatusCached = ({ fresh = false } = {}) => {
50
+ const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
51
+ const cacheDir = resolve(dataDir);
52
+ const now = performanceNow();
53
+ if (
54
+ !fresh
55
+ && outputStyleStatusCache
56
+ && outputStyleStatusCacheDir === cacheDir
57
+ && now - outputStyleStatusCacheAt < 2500
58
+ ) {
59
+ return outputStyleStatusCache;
60
+ }
61
+ outputStyleStatusCache = outputStyleStatus(dataDir);
62
+ outputStyleStatusCacheAt = now;
63
+ outputStyleStatusCacheDir = cacheDir;
64
+ return outputStyleStatusCache;
65
+ };
66
+ const invalidateOutputStyleStatusCache = () => {
67
+ outputStyleStatusCache = null;
68
+ outputStyleStatusCacheAt = 0;
69
+ outputStyleStatusCacheDir = '';
70
+ };
71
+ // In-memory seed of the status cache after an outputStyle select (avoids a
72
+ // second forced-fresh filesystem scan during the debounce window).
73
+ const seedOutputStyleStatusCache = (status) => {
74
+ outputStyleStatusCache = status;
75
+ outputStyleStatusCacheAt = performanceNow();
76
+ outputStyleStatusCacheDir = resolve(cfgMod.getPluginData?.() || STANDALONE_DATA_DIR);
77
+ };
78
+
79
+ // --- config adopt -----------------------------------------------------------
80
+ function adoptConfig(nextConfig, { hasSecrets = getConfigHasSecrets() } = {}) {
81
+ setConfig(nextConfig);
82
+ setConfigHasSecrets(hasSecrets === true);
83
+ const config = getConfig();
84
+ setConfiguredShell(normalizeSystemShellConfig(config.shell).command);
85
+ setSearchRoute(
86
+ normalizeSearchRouteConfig(config.searchRoute)
87
+ || normalizeSearchRouteConfig(getSearchRoute()),
88
+ );
89
+ return config;
90
+ }
91
+
92
+ // --- debounced config save --------------------------------------------------
93
+ let pendingConfigToSave = null;
94
+ let configSaveTimer = null;
95
+
96
+ function flushConfigSave() {
97
+ if (configSaveTimer) {
98
+ clearTimeout(configSaveTimer);
99
+ configSaveTimer = null;
100
+ }
101
+ if (pendingConfigToSave === null) return;
102
+ const snapshot = pendingConfigToSave;
103
+ pendingConfigToSave = null;
104
+ try {
105
+ cfgMod.saveConfig(snapshot);
106
+ } catch (err) {
107
+ process.stderr.write(`[config] debounced saveConfig failed: ${err?.message || err}\n`);
108
+ }
109
+ }
110
+
111
+ function saveConfigAndAdopt(nextConfig, { hasSecrets = getConfigHasSecrets() } = {}) {
112
+ // In-memory adopt is synchronous and first so callers that read back the
113
+ // value immediately (e.g. setProfile -> getProfile) see the new state.
114
+ const adopted = adoptConfig(nextConfig, { hasSecrets });
115
+ // Persist the adopted object; coalesce rapid successive changes into one
116
+ // disk write after CONFIG_SAVE_DEBOUNCE_MS of quiet.
117
+ pendingConfigToSave = getConfig();
118
+ if (configSaveTimer) clearTimeout(configSaveTimer);
119
+ configSaveTimer = setTimeout(flushConfigSave, CONFIG_SAVE_DEBOUNCE_MS);
120
+ configSaveTimer.unref?.();
121
+ return adopted;
122
+ }
123
+
124
+ // --- debounced backend switch ----------------------------------------------
125
+ let pendingBackendName = null;
126
+ let backendSaveTimer = null;
127
+
128
+ function flushBackendSave() {
129
+ if (backendSaveTimer) {
130
+ clearTimeout(backendSaveTimer);
131
+ backendSaveTimer = null;
132
+ }
133
+ if (pendingBackendName === null) return;
134
+ const name = pendingBackendName;
135
+ pendingBackendName = null;
136
+ try {
137
+ setBackend(name);
138
+ } catch (err) {
139
+ process.stderr.write(`[channels] debounced setBackend failed: ${err?.message || err}\n`);
140
+ }
141
+ }
142
+
143
+ function scheduleBackendSave(name) {
144
+ pendingBackendName = name;
145
+ if (backendSaveTimer) clearTimeout(backendSaveTimer);
146
+ backendSaveTimer = setTimeout(flushBackendSave, CONFIG_SAVE_DEBOUNCE_MS);
147
+ backendSaveTimer.unref?.();
148
+ }
149
+
150
+ // --- debounced top-level outputStyle persist -------------------------------
151
+ let pendingOutputStyleId = null;
152
+ let outputStyleSaveTimer = null;
153
+
154
+ function flushOutputStyleSave() {
155
+ if (outputStyleSaveTimer) {
156
+ clearTimeout(outputStyleSaveTimer);
157
+ outputStyleSaveTimer = null;
158
+ }
159
+ if (pendingOutputStyleId === null) return;
160
+ const styleId = pendingOutputStyleId;
161
+ pendingOutputStyleId = null;
162
+ try {
163
+ sharedCfgMod.updateConfig((root) => {
164
+ const next = { ...(root || {}), outputStyle: styleId };
165
+ if (next.agent && typeof next.agent === 'object' && !Array.isArray(next.agent)) {
166
+ const agent = { ...next.agent };
167
+ delete agent.outputStyle;
168
+ next.agent = agent;
169
+ }
170
+ return next;
171
+ });
172
+ } catch (err) {
173
+ process.stderr.write(`[config] debounced outputStyle save failed: ${err?.message || err}\n`);
174
+ }
175
+ }
176
+
177
+ function scheduleOutputStyleSave(styleId) {
178
+ pendingOutputStyleId = styleId;
179
+ if (outputStyleSaveTimer) clearTimeout(outputStyleSaveTimer);
180
+ outputStyleSaveTimer = setTimeout(flushOutputStyleSave, CONFIG_SAVE_DEBOUNCE_MS);
181
+ outputStyleSaveTimer.unref?.();
182
+ }
183
+
184
+ // --- reload / ensure --------------------------------------------------------
185
+ function reloadFullConfig() {
186
+ // A pending debounced write holds the only copy of the latest change.
187
+ // Flush it before re-reading from disk so loadConfig() observes (and the
188
+ // subsequent adopt preserves) that change instead of reverting to a stale
189
+ // on-disk snapshot.
190
+ flushConfigSave();
191
+ return adoptConfig(cfgMod.loadConfig(), { hasSecrets: true });
192
+ }
193
+
194
+ function ensureFullConfig() {
195
+ if (getConfigHasSecrets()) return getConfig();
196
+ return reloadFullConfig();
197
+ }
198
+
199
+ function displayConfig() {
200
+ return getConfig();
201
+ }
202
+
203
+ function ensureConfigForRouteProvider() {
204
+ const config = getConfig();
205
+ const providerId = clean(getRoute().provider);
206
+ const providerCfg = config?.providers?.[providerId];
207
+ if (getConfigHasSecrets() || LAZY_SECRET_PROVIDERS.has(providerId) || providerCfg?.apiKey) {
208
+ return config;
209
+ }
210
+ return ensureFullConfig();
211
+ }
212
+
213
+ return {
214
+ // output-style cache
215
+ getOutputStyleStatusCached,
216
+ invalidateOutputStyleStatusCache,
217
+ seedOutputStyleStatusCache,
218
+ // adopt / save
219
+ adoptConfig,
220
+ saveConfigAndAdopt,
221
+ flushConfigSave,
222
+ flushBackendSave,
223
+ scheduleBackendSave,
224
+ flushOutputStyleSave,
225
+ scheduleOutputStyleSave,
226
+ // reload / ensure
227
+ reloadFullConfig,
228
+ ensureFullConfig,
229
+ displayConfig,
230
+ ensureConfigForRouteProvider,
231
+ };
232
+ }
@@ -0,0 +1,226 @@
1
+ // cwd-plugins.mjs — cwd resolution/apply + plugins-status + core-memory context,
2
+ // extracted from mixdog-session-runtime.mjs. Dependency-injected factory that
3
+ // closes over the facade's mutable cwd/config/session state via getter/setter
4
+ // injection (getCurrentCwd/setCurrentCwd/getConfig/getSession/...) plus the MCP
5
+ // glue + prewarm callbacks. The facade keeps ownership of the mutable locals;
6
+ // this module owns the pure logic that was previously inline.
7
+
8
+ export function createCwdPlugins({
9
+ // mutable-state injection
10
+ getCurrentCwd,
11
+ setCurrentCwd,
12
+ getConfig,
13
+ getSession,
14
+ getRoute,
15
+ getLastProjectMcpKey,
16
+ setLastProjectMcpKey,
17
+ isCodeGraphPrewarmLazy,
18
+ isCodeGraphFirstTurnPrewarmDone,
19
+ getCodeGraphPrewarmDelayMs,
20
+ setSessionNeedsCwdRefresh,
21
+ // callbacks / deps
22
+ connectConfiguredMcp,
23
+ invalidatePreSessionToolSurface,
24
+ scheduleCodeGraphPrewarm,
25
+ hooks,
26
+ hookCommonPayload,
27
+ bootProfile,
28
+ getMemoryModule,
29
+ // channel-admin / registry helpers
30
+ listRegisteredPlugins,
31
+ pluginAdminStatus,
32
+ pluginManifest,
33
+ pluginMcpServerName,
34
+ mcpScriptForPlugin,
35
+ countSkillFiles,
36
+ readProjectMcpServers,
37
+ writeLastSessionCwd,
38
+ // shared utils
39
+ clean,
40
+ resolve,
41
+ statSync,
42
+ existsSync,
43
+ cfgMod,
44
+ STANDALONE_DATA_DIR,
45
+ }) {
46
+ function resolveCwdPath(value) {
47
+ const raw = clean(value);
48
+ if (!raw) throw new Error('cwd: path is required for action=set');
49
+ const next = resolve(getCurrentCwd() || process.cwd(), raw);
50
+ const stat = statSync(next);
51
+ if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${next}`);
52
+ return next;
53
+ }
54
+
55
+ function applyResolvedCwd(nextCwd, { markRefresh = true } = {}) {
56
+ const resolved = resolve(nextCwd);
57
+ const stat = statSync(resolved);
58
+ if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${resolved}`);
59
+ const changed = resolve(getCurrentCwd()) !== resolved;
60
+ setCurrentCwd(resolved);
61
+ const currentCwd = resolved;
62
+ process.env.MIXDOG_SESSION_CWD = currentCwd;
63
+ writeLastSessionCwd(currentCwd);
64
+ const session = getSession();
65
+ if (session) session.cwd = currentCwd;
66
+ // cwd changes NEVER recreate the session: a mid-conversation cwd switch must
67
+ // preserve the full message history (and the BP1–BP3 prompt cache). We only
68
+ // retarget the live session's cwd in place; tool execution already reads the
69
+ // current cwd per turn. `cwd` is intentionally absent from the prompt
70
+ // context (see composeSystemPrompt), so there is nothing prompt-side to
71
+ // refresh either. `markRefresh`/`changed` are kept only for signature
72
+ // compatibility with existing callers.
73
+ void markRefresh;
74
+ // Lazy mode: before the first turn (e.g. the initial project-selection
75
+ // cwd set), do NOT prewarm — that is exactly the post-first-frame freeze
76
+ // we are avoiding. Once a turn has run, an in-session cwd switch DOES
77
+ // prewarm the new dir, since a lookup there is now likely.
78
+ if (isCodeGraphPrewarmLazy() && !isCodeGraphFirstTurnPrewarmDone()) {
79
+ bootProfile('code-graph:prewarm-lazy', { reason: 'cwd-deferred-to-first-turn' });
80
+ } else {
81
+ const delay = getCodeGraphPrewarmDelayMs();
82
+ scheduleCodeGraphPrewarm(changed ? 0 : delay, changed ? 'cwd-change' : 'cwd');
83
+ }
84
+ // Project-local `.mcp.json` follows the cwd: when the effective project MCP
85
+ // set changes, reconnect in the background (never await — this stays sync,
86
+ // and the session is preserved). Guarded so a no-op cwd change does not
87
+ // churn connections.
88
+ if (changed) {
89
+ try {
90
+ const nextKey = resolved + '\u0000' + JSON.stringify(readProjectMcpServers(resolved));
91
+ if (nextKey !== getLastProjectMcpKey()) {
92
+ setLastProjectMcpKey(nextKey);
93
+ void connectConfiguredMcp({ reset: true })
94
+ .then(() => invalidatePreSessionToolSurface())
95
+ .catch(() => {});
96
+ }
97
+ } catch {}
98
+ }
99
+ // CwdChanged: bridge an effective cwd switch to the standard hook bus.
100
+ // No matcher event — payload is minimal { cwd }. Fire-and-forget.
101
+ if (changed) {
102
+ try { void hooks.dispatch('CwdChanged', hookCommonPayload({ cwd: currentCwd })); } catch {}
103
+ }
104
+ return currentCwd;
105
+ }
106
+
107
+ async function refreshSessionForCwdIfNeeded(reason = 'cwd-change') {
108
+ // No-op: cwd changes are applied in place by applyResolvedCwd and never
109
+ // tear down the session. Retained as a stable hook for ask()'s pre-turn
110
+ // call so the surrounding turn flow is unchanged.
111
+ void reason;
112
+ setSessionNeedsCwdRefresh(false);
113
+ return getSession();
114
+ }
115
+
116
+ function pluginsStatus() {
117
+ const config = getConfig();
118
+ const dataDir = cfgMod.getPluginData?.();
119
+ const configuredMcp = config?.mcpServers && typeof config.mcpServers === 'object'
120
+ ? config.mcpServers
121
+ : {};
122
+ const plugins = [];
123
+ const addRegisteredPlugin = (entry) => {
124
+ const root = clean(entry.root);
125
+ if (!root || !existsSync(root)) return;
126
+ const manifest = pluginManifest(root);
127
+ const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
128
+ const plugin = {
129
+ id: clean(entry.id) || name,
130
+ name,
131
+ title: clean(manifest.title) || clean(manifest.displayName) || clean(entry.title) || name,
132
+ version: clean(manifest.version) || clean(entry.version) || null,
133
+ description: clean(manifest.description) || clean(entry.description),
134
+ marketplace: null,
135
+ source: clean(entry.sourceType) === 'local' ? 'local' : 'registry',
136
+ sourceUrl: clean(entry.source),
137
+ sourceType: clean(entry.sourceType) || 'git',
138
+ managed: entry.managed !== false,
139
+ root,
140
+ installedAt: entry.installedAt || null,
141
+ updatedAt: entry.updatedAt || null,
142
+ skillCount: countSkillFiles(root),
143
+ mcpScript: mcpScriptForPlugin(root),
144
+ };
145
+ plugin.mcpServerName = pluginMcpServerName(plugin);
146
+ plugin.mcpEnabled = Object.prototype.hasOwnProperty.call(configuredMcp, plugin.mcpServerName)
147
+ || Object.keys(configuredMcp).some((k) => k.startsWith(`${plugin.mcpServerName}--`));
148
+ plugins.push(plugin);
149
+ };
150
+
151
+ for (const entry of listRegisteredPlugins({ dataDir })) addRegisteredPlugin(entry);
152
+
153
+ plugins.sort((a, b) => {
154
+ if (a.source !== b.source) return a.source.localeCompare(b.source);
155
+ return a.name.localeCompare(b.name);
156
+ });
157
+ const admin = pluginAdminStatus({ dataDir });
158
+ return {
159
+ count: plugins.length,
160
+ plugins,
161
+ roots: {
162
+ registry: admin.registryPath,
163
+ installed: admin.installRoot,
164
+ },
165
+ };
166
+ }
167
+
168
+ function formatCoreMemoryLines(payload = {}) {
169
+ const seen = new Set();
170
+ const lines = [];
171
+ for (const value of [
172
+ ...(Array.isArray(payload.userLines) ? payload.userLines : []),
173
+ ...(Array.isArray(payload.dbLines) ? payload.dbLines : []),
174
+ ]) {
175
+ const text = clean(value).replace(/\s+/g, ' ');
176
+ if (!text) continue;
177
+ const key = text.toLowerCase();
178
+ if (seen.has(key)) continue;
179
+ seen.add(key);
180
+ lines.push(`- ${text}`);
181
+ if (lines.length >= 40) break;
182
+ }
183
+ const out = lines.join('\n');
184
+ const maxChars = 6000;
185
+ return out.length > maxChars ? `${out.slice(0, maxChars).replace(/\s+\S*$/, '')}\n- ...` : out;
186
+ }
187
+
188
+ async function loadCoreMemoryContext() {
189
+ // Boot should not pay for memory/PG startup unless explicitly requested.
190
+ // Recall and memory tools still initialize the memory service on first use.
191
+ if (process.env.MIXDOG_BOOT_CORE_MEMORY !== '1') {
192
+ bootProfile('core-memory:skipped');
193
+ return '';
194
+ }
195
+ const startedAt = performance.now();
196
+ let timer = null;
197
+ const timeout = new Promise((resolveTimeout) => {
198
+ timer = setTimeout(() => resolveTimeout(''), 2000);
199
+ timer.unref?.();
200
+ });
201
+ try {
202
+ return await Promise.race([
203
+ (async () => {
204
+ const memoryMod = await getMemoryModule();
205
+ if (typeof memoryMod?.buildSessionCoreMemoryPayload !== 'function') return '';
206
+ return formatCoreMemoryLines(await memoryMod.buildSessionCoreMemoryPayload(getCurrentCwd()));
207
+ })(),
208
+ timeout,
209
+ ]);
210
+ } catch {
211
+ return '';
212
+ } finally {
213
+ if (timer) clearTimeout(timer);
214
+ bootProfile('core-memory:done', { ms: (performance.now() - startedAt).toFixed(1) });
215
+ }
216
+ }
217
+
218
+ return {
219
+ resolveCwdPath,
220
+ applyResolvedCwd,
221
+ refreshSessionForCwdIfNeeded,
222
+ pluginsStatus,
223
+ formatCoreMemoryLines,
224
+ loadCoreMemoryContext,
225
+ };
226
+ }