mixdog 0.9.3 → 0.9.5

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 (382) hide show
  1. package/README.md +112 -38
  2. package/package.json +10 -3
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/r5-orchestrated-task.json +7 -0
  7. package/scripts/bench/review-tasks.json +20 -0
  8. package/scripts/bench/round-codex.json +114 -0
  9. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  10. package/scripts/bench/round-mixdog-lead.json +269 -0
  11. package/scripts/bench/round-mixdog.json +126 -0
  12. package/scripts/bench/round-r10-bigsample.json +679 -0
  13. package/scripts/bench/round-r11-codexalign.json +257 -0
  14. package/scripts/bench/round-r13-clientmeta.json +464 -0
  15. package/scripts/bench/round-r14-betafeatures.json +466 -0
  16. package/scripts/bench/round-r15-fulldefault.json +462 -0
  17. package/scripts/bench/round-r16-sessionid.json +466 -0
  18. package/scripts/bench/round-r17-wirebytes.json +456 -0
  19. package/scripts/bench/round-r18-prewarm.json +468 -0
  20. package/scripts/bench/round-r19-clean.json +472 -0
  21. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  22. package/scripts/bench/round-r21-delta-retry.json +473 -0
  23. package/scripts/bench/round-r22-full-probe.json +693 -0
  24. package/scripts/bench/round-r23-itemprobe.json +701 -0
  25. package/scripts/bench/round-r24-shapefix.json +677 -0
  26. package/scripts/bench/round-r25-serial.json +464 -0
  27. package/scripts/bench/round-r26-parallel3.json +671 -0
  28. package/scripts/bench/round-r27-parallel10.json +894 -0
  29. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  30. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  31. package/scripts/bench/round-r30-instid.json +253 -0
  32. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  33. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  34. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  35. package/scripts/bench/round-r34-orchestrated.json +120 -0
  36. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  37. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  38. package/scripts/bench/round-r4-codex.json +114 -0
  39. package/scripts/bench/round-r4-mixed.json +225 -0
  40. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  41. package/scripts/bench/round-r6-codex.json +114 -0
  42. package/scripts/bench/round-r6-solo.json +257 -0
  43. package/scripts/bench/round-r7-full.json +254 -0
  44. package/scripts/bench/round-r8-fulldefault.json +255 -0
  45. package/scripts/bench-run.mjs +251 -32
  46. package/scripts/freevar-smoke.mjs +95 -0
  47. package/scripts/internal-comms-bench.mjs +3 -4
  48. package/scripts/internal-comms-smoke.mjs +10 -9
  49. package/scripts/model-catalog-audit.mjs +209 -0
  50. package/scripts/model-list-sanitize-test.mjs +37 -0
  51. package/scripts/mouse-probe.mjs +45 -0
  52. package/scripts/output-style-bench.mjs +13 -6
  53. package/scripts/output-style-smoke.mjs +4 -4
  54. package/scripts/provider-toolcall-test.mjs +7 -3
  55. package/scripts/recall-bench.mjs +76 -13
  56. package/scripts/recall-quality-cases.json +12 -0
  57. package/scripts/recall-usecase-cases.json +18 -0
  58. package/scripts/session-bench.mjs +152 -6
  59. package/scripts/tool-smoke.mjs +25 -65
  60. package/scripts/tui-render-smoke.mjs +90 -0
  61. package/scripts/webhook-smoke.mjs +208 -0
  62. package/src/agents/debugger/AGENT.md +4 -1
  63. package/src/agents/heavy-worker/AGENT.md +9 -8
  64. package/src/agents/maintainer/AGENT.md +4 -0
  65. package/src/agents/reviewer/AGENT.md +2 -1
  66. package/src/agents/scheduler-task/AGENT.md +2 -3
  67. package/src/agents/webhook-handler/AGENT.md +2 -3
  68. package/src/agents/worker/AGENT.md +10 -7
  69. package/src/app.mjs +12 -1
  70. package/src/headless-role.mjs +7 -1
  71. package/src/lib/rules-builder.cjs +4 -0
  72. package/src/mixdog-session-runtime.mjs +647 -2056
  73. package/src/output-styles/default.md +30 -9
  74. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  75. package/src/output-styles/minimal.md +8 -6
  76. package/src/output-styles/simple.md +21 -7
  77. package/src/rules/agent/00-common.md +6 -3
  78. package/src/rules/agent/30-explorer.md +16 -5
  79. package/src/rules/lead/01-general.md +5 -5
  80. package/src/rules/lead/lead-brief.md +15 -0
  81. package/src/rules/lead/lead-tool.md +6 -15
  82. package/src/rules/shared/01-tool.md +17 -21
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  84. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  85. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  86. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  87. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  88. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  89. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  90. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  91. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  92. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  93. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  95. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  97. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  100. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  101. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  102. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  103. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
  104. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  109. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  110. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
  111. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  112. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
  113. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
  117. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  119. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  120. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  121. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  122. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  123. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  124. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  125. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  126. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  127. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  128. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  129. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
  132. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  133. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  134. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  135. package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
  136. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  137. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  138. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  139. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  140. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  143. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  144. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  145. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  146. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  147. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
  149. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  152. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  155. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  157. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  158. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  159. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  161. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  163. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  164. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  165. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  166. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  167. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  168. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  169. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  170. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  171. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  172. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  173. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  174. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  175. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  176. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  177. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  178. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  179. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  180. package/src/runtime/channels/backends/discord.mjs +27 -318
  181. package/src/runtime/channels/backends/telegram.mjs +8 -12
  182. package/src/runtime/channels/index.mjs +247 -701
  183. package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
  184. package/src/runtime/channels/lib/config.mjs +37 -149
  185. package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
  186. package/src/runtime/channels/lib/event-queue.mjs +78 -13
  187. package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
  188. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  189. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  190. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  191. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  192. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  193. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  194. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  195. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  196. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  197. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  198. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  199. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  200. package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
  201. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  202. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  203. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  204. package/src/runtime/channels/lib/webhook.mjs +43 -616
  205. package/src/runtime/channels/tool-defs.mjs +11 -130
  206. package/src/runtime/memory/index.mjs +210 -1948
  207. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  208. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  209. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  210. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  211. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  212. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  213. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  214. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  215. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  216. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  217. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  218. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  219. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  220. package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
  221. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  222. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  223. package/src/runtime/memory/lib/query-handlers.mjs +802 -0
  224. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  225. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  226. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  227. package/src/runtime/memory/tool-defs.mjs +5 -13
  228. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  229. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  230. package/src/runtime/search/lib/web-tools.mjs +24 -602
  231. package/src/runtime/shared/atomic-file.mjs +26 -1
  232. package/src/runtime/shared/config.mjs +14 -4
  233. package/src/runtime/shared/launcher-control.mjs +2 -2
  234. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  235. package/src/runtime/shared/schedules-store.mjs +13 -3
  236. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  237. package/src/runtime/shared/tool-primitives.mjs +308 -0
  238. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  239. package/src/runtime/shared/tool-surface.mjs +80 -898
  240. package/src/runtime/shared/transcript-writer.mjs +23 -0
  241. package/src/runtime/shared/update-checker.mjs +7 -4
  242. package/src/session-runtime/config-helpers.mjs +119 -2
  243. package/src/session-runtime/config-lifecycle.mjs +232 -0
  244. package/src/session-runtime/cwd-plugins.mjs +226 -0
  245. package/src/session-runtime/mcp-glue.mjs +177 -0
  246. package/src/session-runtime/model-recency.mjs +111 -0
  247. package/src/session-runtime/native-search.mjs +247 -0
  248. package/src/session-runtime/output-styles.mjs +11 -9
  249. package/src/session-runtime/prewarm.mjs +142 -0
  250. package/src/session-runtime/provider-models.mjs +278 -0
  251. package/src/session-runtime/provider-usage.mjs +120 -0
  252. package/src/session-runtime/quick-model-rows.mjs +205 -0
  253. package/src/session-runtime/quick-search-models.mjs +47 -0
  254. package/src/session-runtime/session-hooks.mjs +93 -0
  255. package/src/session-runtime/settings-api.mjs +352 -0
  256. package/src/session-runtime/tool-catalog.mjs +29 -29
  257. package/src/session-runtime/tool-defs.mjs +84 -0
  258. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  259. package/src/session-runtime/workflow.mjs +1 -1
  260. package/src/standalone/agent-tool/helpers.mjs +237 -0
  261. package/src/standalone/agent-tool/notify.mjs +107 -0
  262. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  263. package/src/standalone/agent-tool/render.mjs +152 -0
  264. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  265. package/src/standalone/agent-tool.mjs +138 -669
  266. package/src/standalone/channel-admin.mjs +102 -90
  267. package/src/standalone/channel-worker.mjs +4 -7
  268. package/src/standalone/explore-tool.mjs +64 -14
  269. package/src/standalone/hook-bus/config.mjs +207 -0
  270. package/src/standalone/hook-bus/constants.mjs +90 -0
  271. package/src/standalone/hook-bus/handlers.mjs +481 -0
  272. package/src/standalone/hook-bus/payload.mjs +31 -0
  273. package/src/standalone/hook-bus/rules.mjs +77 -0
  274. package/src/standalone/hook-bus.mjs +77 -870
  275. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  276. package/src/standalone/opencode-go-login.mjs +5 -1
  277. package/src/standalone/provider-admin.mjs +1 -16
  278. package/src/standalone/usage-dashboard.mjs +3 -1
  279. package/src/tui/App.jsx +1059 -8110
  280. package/src/tui/app/app-format.mjs +213 -0
  281. package/src/tui/app/channel-pickers.mjs +508 -0
  282. package/src/tui/app/clipboard.mjs +67 -0
  283. package/src/tui/app/core-memory-picker.mjs +210 -0
  284. package/src/tui/app/extension-pickers.mjs +506 -0
  285. package/src/tui/app/input-parsers.mjs +193 -0
  286. package/src/tui/app/maintenance-pickers.mjs +356 -0
  287. package/src/tui/app/model-options.mjs +334 -0
  288. package/src/tui/app/model-picker.mjs +365 -0
  289. package/src/tui/app/onboarding-steps.mjs +400 -0
  290. package/src/tui/app/project-picker.mjs +247 -0
  291. package/src/tui/app/provider-setup-picker.mjs +580 -0
  292. package/src/tui/app/resume-picker.mjs +55 -0
  293. package/src/tui/app/route-pickers.mjs +419 -0
  294. package/src/tui/app/settings-picker.mjs +489 -0
  295. package/src/tui/app/slash-commands.mjs +101 -0
  296. package/src/tui/app/slash-dispatch.mjs +427 -0
  297. package/src/tui/app/text-layout.mjs +46 -0
  298. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  299. package/src/tui/app/transcript-window.mjs +677 -0
  300. package/src/tui/app/use-mouse-input.mjs +460 -0
  301. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  302. package/src/tui/app/use-transcript-scroll.mjs +512 -0
  303. package/src/tui/app/use-transcript-window.mjs +607 -0
  304. package/src/tui/components/ConfirmBar.jsx +10 -7
  305. package/src/tui/components/Picker.jsx +64 -15
  306. package/src/tui/components/PromptInput.jsx +33 -102
  307. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  308. package/src/tui/components/StatusLine.jsx +69 -15
  309. package/src/tui/components/TextEntryPanel.jsx +11 -0
  310. package/src/tui/components/ToolExecution.jsx +52 -594
  311. package/src/tui/components/TranscriptItem.jsx +105 -0
  312. package/src/tui/components/UsagePanel.jsx +18 -4
  313. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  314. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  315. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  316. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  317. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  318. package/src/tui/display-width.mjs +20 -3
  319. package/src/tui/dist/index.mjs +13553 -12384
  320. package/src/tui/engine/agent-job-feed.mjs +133 -0
  321. package/src/tui/engine/notification-plan.mjs +76 -0
  322. package/src/tui/engine/render-timing.mjs +17 -0
  323. package/src/tui/engine/tool-approval.mjs +94 -0
  324. package/src/tui/engine/tool-card-results.mjs +234 -0
  325. package/src/tui/engine/tool-result-status.mjs +135 -0
  326. package/src/tui/engine.mjs +170 -574
  327. package/src/tui/figures.mjs +5 -0
  328. package/src/tui/index.jsx +65 -1
  329. package/src/tui/input-editing.mjs +2 -2
  330. package/src/tui/markdown/format-token.mjs +4 -1
  331. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  332. package/src/tui/theme.mjs +6 -0
  333. package/src/ui/statusline-agents.mjs +213 -0
  334. package/src/ui/statusline-format.mjs +146 -0
  335. package/src/ui/statusline-segments.mjs +148 -0
  336. package/src/ui/statusline.mjs +77 -501
  337. package/src/ui/tool-card.mjs +0 -1
  338. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  339. package/src/workflows/default/WORKFLOW.md +16 -18
  340. package/src/workflows/sequential/WORKFLOW.md +16 -18
  341. package/vendor/ink/build/display-width.js +19 -3
  342. package/vendor/ink/build/ink.js +112 -7
  343. package/vendor/ink/build/log-update.js +17 -3
  344. package/vendor/ink/build/wrap-text.js +125 -0
  345. package/scripts/_test-folder-dialog.mjs +0 -30
  346. package/scripts/fix-brief-fn.mjs +0 -35
  347. package/scripts/fix-format-tool-surface.mjs +0 -24
  348. package/scripts/fix-tool-exec-visible.mjs +0 -42
  349. package/scripts/patch-agent-brief.mjs +0 -48
  350. package/scripts/patch-app.mjs +0 -21
  351. package/scripts/patch-app2.mjs +0 -18
  352. package/scripts/patch-dist-brief.mjs +0 -96
  353. package/scripts/patch-tool-exec.mjs +0 -70
  354. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  355. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  356. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  357. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  358. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  359. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  360. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  361. package/src/runtime/channels/lib/holidays.mjs +0 -138
  362. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  363. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  364. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  365. package/src/standalone/agent-task-status.test.mjs +0 -76
  366. package/src/tui/components/tool-output-format.test.mjs +0 -399
  367. package/src/tui/display-width.test.mjs +0 -35
  368. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  369. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  370. package/src/tui/input-editing.selection.test.mjs +0 -75
  371. package/src/tui/markdown/format-token.test.mjs +0 -354
  372. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  373. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  374. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  375. package/src/tui/paste-fix.test.mjs +0 -119
  376. package/src/tui/prompt-history-store.test.mjs +0 -52
  377. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  378. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  379. package/src/ui/markdown.test.mjs +0 -70
  380. package/src/ui/statusline-context-label.test.mjs +0 -15
  381. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  382. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,436 @@
1
+ // Native mixdog-patch engine transport: persistent stdio server, env-driven
2
+ // mode gating, binary resolution, prewarm/idle lifecycle, and the char-indexed
3
+ // EDIT client. Split out of patch.mjs; behavior is identical.
4
+ //
5
+ // Backend: NATIVE-ONLY. Every supported apply/edit case is dispatched to the
6
+ // mixdog-patch Rust engine via the persistent stdio server. There is NO JS
7
+ // apply fallback: unsupported / unsafe input returns a clean Error string.
8
+
9
+ import { existsSync } from 'node:fs';
10
+ import { spawn } from 'node:child_process';
11
+ import { createInterface } from 'node:readline';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { resolve as pathResolve, dirname as pathDirname, join as pathJoin } from 'node:path';
14
+ import { performance } from 'node:perf_hooks';
15
+ import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
16
+ import { getPluginData } from '../../config.mjs';
17
+ import { ensurePatchBinary, findCachedPatchBinary } from '../patch-binary-fetcher.mjs';
18
+
19
+ const PLUGIN_ROOT = process.env.MIXDOG_ROOT
20
+ || pathResolve(pathDirname(fileURLToPath(import.meta.url)), '../../../../..');
21
+ const NATIVE_PATCH_DEFAULT_BIN = pathJoin(
22
+ PLUGIN_ROOT,
23
+ 'native/mixdog-patch/target/release',
24
+ process.platform === 'win32' ? 'mixdog-patch.exe' : 'mixdog-patch',
25
+ );
26
+ let _nativePatchServer = null;
27
+ let _nativePatchPrewarmTimer = null;
28
+ let _nativeEditServer = null;
29
+
30
+ function markNativePatchRuntimeTouched() {
31
+ try { globalThis.__mixdogNativePatchRuntimeTouched = true; } catch {}
32
+ }
33
+
34
+ function nativePatchMode() {
35
+ return String(process.env.MIXDOG_PATCH_NATIVE || 'auto').toLowerCase();
36
+ }
37
+
38
+ export function nativePatchEnabled() {
39
+ return !/^(0|false|no|off|js|legacy)$/i.test(nativePatchMode());
40
+ }
41
+
42
+ export function nativePatchTraceEnabled() {
43
+ return /^(1|true|yes)$/i.test(process.env.MIXDOG_PATCH_NATIVE_TRACE || '');
44
+ }
45
+
46
+ function ioTraceEnabled() {
47
+ return /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_IO_TRACE || ''));
48
+ }
49
+
50
+ export function ioTrace(event, fields = {}) {
51
+ if (!ioTraceEnabled()) return;
52
+ try {
53
+ process.stderr.write(`[io-trace] ${JSON.stringify({ event, ts: Date.now(), ...fields })}\n`);
54
+ } catch {}
55
+ }
56
+
57
+ export function patchTraceEnabled() {
58
+ return ioTraceEnabled()
59
+ || nativePatchTraceEnabled()
60
+ || /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_PATCH_TRACE || ''));
61
+ }
62
+
63
+ function nativePatchPrewarmEnabled() {
64
+ if (!nativePatchEnabled()) return false;
65
+ if (process.env.MIXDOG_PATCH_NATIVE_BIN && !existsSync(nativePatchBinPath())) return false;
66
+ return !/^(0|false|no)$/i.test(process.env.MIXDOG_PATCH_NATIVE_PREWARM || '');
67
+ }
68
+
69
+ function nativePatchPersistent() {
70
+ return /^(1|true|yes|server|persistent)$/i.test(nativePatchMode());
71
+ }
72
+
73
+ export function nativePatchBinPath() {
74
+ if (process.env.MIXDOG_PATCH_NATIVE_BIN) return process.env.MIXDOG_PATCH_NATIVE_BIN;
75
+ // Local cargo build first, then a fetched/cached prebuilt; absence is
76
+ // a hard error at dispatch (no JS fallback in native-only mode).
77
+ if (existsSync(NATIVE_PATCH_DEFAULT_BIN)) return NATIVE_PATCH_DEFAULT_BIN;
78
+ return findCachedPatchBinary(getPluginData()) || NATIVE_PATCH_DEFAULT_BIN;
79
+ }
80
+
81
+ export async function ensureNativePatchBinaryAvailable() {
82
+ if (!nativePatchEnabled()) {
83
+ throw new Error('apply_patch: native engine disabled via MIXDOG_PATCH_NATIVE; set it to "auto" or "1" to apply patches.');
84
+ }
85
+ const current = nativePatchBinPath();
86
+ if (existsSync(current)) return current;
87
+ if (process.env.MIXDOG_PATCH_NATIVE_BIN) {
88
+ throw new Error(`apply_patch: native patch binary not found at MIXDOG_PATCH_NATIVE_BIN=${current}.`);
89
+ }
90
+ try {
91
+ const fetched = await ensurePatchBinary(getPluginData());
92
+ if (fetched && existsSync(fetched)) return fetched;
93
+ } catch (err) {
94
+ throw new Error(`apply_patch: native patch binary unavailable — ${err?.message || String(err)}`);
95
+ }
96
+ const resolved = nativePatchBinPath();
97
+ if (existsSync(resolved)) return resolved;
98
+ throw new Error(`apply_patch: native patch binary not found at ${resolved}.`);
99
+ }
100
+
101
+ // Decode the hex-encoded failures payload that accompanies OK_PARTIAL:
102
+ // the Rust side emits utf-8 bytes (`<path>\t<reason>` records joined by
103
+ // `\n`) hex-encoded so they can ride the tab-separated response line
104
+ // without escaping. An empty / unparseable payload becomes an empty list
105
+ // so a missing field never crashes the caller.
106
+ function decodeNativeFailures(hexPayload) {
107
+ if (typeof hexPayload !== 'string' || hexPayload.length === 0) return [];
108
+ if (!/^[0-9a-fA-F]+$/.test(hexPayload) || hexPayload.length % 2 !== 0) return [];
109
+ let text = '';
110
+ try { text = Buffer.from(hexPayload, 'hex').toString('utf-8'); }
111
+ catch { return []; }
112
+ const out = [];
113
+ for (const raw of text.split('\n')) {
114
+ if (!raw) continue;
115
+ const tab = raw.indexOf('\t');
116
+ if (tab === -1) out.push({ path: '', reason: raw });
117
+ else out.push({ path: raw.slice(0, tab), reason: raw.slice(tab + 1) });
118
+ }
119
+ return out;
120
+ }
121
+
122
+ class NativePatchServer {
123
+ constructor(binPath) {
124
+ this.binPath = binPath;
125
+ // windowsHide: mixdog-patch.exe is a console binary; without this each spawn
126
+ // flashes an empty console window on Windows. Especially visible now that the
127
+ // idle watchdog exits the server and it respawns on the next request.
128
+ this.child = spawn(binPath, ['--server'], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
129
+ startChildGuardian({ childPid: this.child.pid, label: 'native-patch-server', orphanGraceMs: 5000, forceGraceMs: 2000 });
130
+ this.stderr = '';
131
+ this.lines = [];
132
+ this.waiters = [];
133
+ this.exited = false;
134
+ this.child.stderr.setEncoding('utf8');
135
+ this.child.stderr.on('data', (chunk) => { this.stderr += chunk; });
136
+ this.rl = createInterface({ input: this.child.stdout });
137
+ this.rl.on('line', (line) => {
138
+ const waiter = this.waiters.shift();
139
+ if (waiter) waiter.resolve(line);
140
+ else this.lines.push(line);
141
+ });
142
+ this.child.on('exit', (code, signal) => {
143
+ this.exited = true;
144
+ const err = new Error(`native patch server exited code=${code} signal=${signal} stderr=${this.stderr}`);
145
+ for (const waiter of this.waiters.splice(0)) waiter.reject(err);
146
+ try { this.rl.close(); } catch {}
147
+ });
148
+ }
149
+
150
+ abort(signal) {
151
+ const err = new Error(signal?.reason?.message || signal?.reason || 'native patch aborted');
152
+ err.name = 'AbortError';
153
+ if (_nativePatchServer === this) _nativePatchServer = null;
154
+ for (const waiter of this.waiters.splice(0)) waiter.reject(err);
155
+ try { this.child.kill('SIGTERM'); } catch {}
156
+ return err;
157
+ }
158
+
159
+ nextLine() {
160
+ if (this.lines.length > 0) return Promise.resolve(this.lines.shift());
161
+ if (this.exited) return Promise.reject(new Error(`native patch server already exited: ${this.stderr}`));
162
+ return new Promise((resolve, reject) => this.waiters.push({ resolve, reject }));
163
+ }
164
+
165
+ ref() {
166
+ try { this.child.ref(); } catch {}
167
+ try { this.child.stdin.ref?.(); } catch {}
168
+ try { this.child.stdout.ref?.(); } catch {}
169
+ try { this.child.stderr.ref?.(); } catch {}
170
+ }
171
+
172
+ unref() {
173
+ try { this.child.unref(); } catch {}
174
+ try { this.child.stdin.unref?.(); } catch {}
175
+ try { this.child.stdout.unref?.(); } catch {}
176
+ try { this.child.stderr.unref?.(); } catch {}
177
+ }
178
+
179
+ async ping() {
180
+ this.ref();
181
+ const linePromise = this.nextLine();
182
+ this.child.stdin.write('PING\n');
183
+ const line = await linePromise;
184
+ if (line !== 'OK\tPONG') {
185
+ throw new Error(`native patch server ping failed: ${line || 'no native response'}`);
186
+ }
187
+ }
188
+
189
+ async apply(basePath, patchText, { fuzz = 2, rejectPartial = true, dryRun = false, signal = null } = {}) {
190
+ this.ref();
191
+ if (signal?.aborted) {
192
+ const err = new Error(signal.reason?.message || signal.reason || 'native patch aborted');
193
+ err.name = 'AbortError';
194
+ throw err;
195
+ }
196
+ const started = performance.now();
197
+ const baseBuf = Buffer.from(basePath, 'utf8');
198
+ const patchBuf = Buffer.from(patchText, 'utf8');
199
+ const linePromise = this.nextLine();
200
+ if (signal) linePromise.catch(() => {});
201
+ let abortListener = null;
202
+ const abortPromise = signal ? new Promise((_, reject) => {
203
+ abortListener = () => {
204
+ reject(this.abort(signal));
205
+ };
206
+ signal.addEventListener('abort', abortListener, { once: true });
207
+ }) : null;
208
+ // 7-token APPLY protocol: APPLY <base_len> <patch_len> <timing> <dry_run> <fuzz> <reject_partial>
209
+ // - timing=1 keeps the server emitting per-phase ms fields
210
+ // - dry_run=1 validates without writing; useful for tests and explicit callers
211
+ // - fuzz=0 means strict context match; fuzz=2 absorbs minor outer-context drift and context trailing spaces/tabs
212
+ // - reject_partial=0 unlocks file-level isolation (OK_PARTIAL response)
213
+ const fuzzTok = Number.isFinite(fuzz) && fuzz >= 0 ? Math.floor(fuzz) : 2;
214
+ const rpTok = rejectPartial ? 1 : 0;
215
+ const dryTok = dryRun ? 1 : 0;
216
+ this.child.stdin.write(`APPLY ${baseBuf.length} ${patchBuf.length} 1 ${dryTok} ${fuzzTok} ${rpTok}\n`);
217
+ this.child.stdin.write(baseBuf);
218
+ this.child.stdin.write(patchBuf);
219
+ let line;
220
+ try {
221
+ line = abortPromise ? await Promise.race([linePromise, abortPromise]) : await linePromise;
222
+ } finally {
223
+ if (abortListener) {
224
+ try { signal.removeEventListener('abort', abortListener); } catch {}
225
+ }
226
+ }
227
+ if (!line) throw new Error('no native response');
228
+ if (line.startsWith('ERR\t')) throw new Error(line.slice(4));
229
+ const okFull = line.startsWith('OK\t');
230
+ const okPartial = line.startsWith('OK_PARTIAL\t');
231
+ if (!okFull && !okPartial) throw new Error(line);
232
+ const fields = line.split('\t');
233
+ // fields[0] = "OK" | "OK_PARTIAL".
234
+ // OK layout: <files> <readMs> <applyMs> <writeMs> <totalMs> <hashMs> <contentHashes>
235
+ // OK_PARTIAL layout: <files> <failed> <readMs> <applyMs> <writeMs> <totalMs> <hashMs> <contentHashes> <hexFailures>
236
+ // The OK_PARTIAL line carries an extra <failed> count between <files>
237
+ // and the timing block, plus a trailing <hexFailures> column — keep
238
+ // the two decodes separate so SKIP failure counts stay accurate.
239
+ let files; let readMs; let applyMs; let writeMs; let totalMs; let hashMs;
240
+ let contentHashesRaw; let hexFailures;
241
+ if (okPartial) {
242
+ files = fields[1];
243
+ // fields[2] = <failed> count; the JS layer already derives a failure
244
+ // count from decodeNativeFailures(hexFailures), so skip the raw cell.
245
+ readMs = fields[3];
246
+ applyMs = fields[4];
247
+ writeMs = fields[5];
248
+ totalMs = fields[6];
249
+ hashMs = fields[7];
250
+ contentHashesRaw = fields[8];
251
+ hexFailures = fields[9];
252
+ } else {
253
+ files = fields[1];
254
+ readMs = fields[2];
255
+ applyMs = fields[3];
256
+ writeMs = fields[4];
257
+ totalMs = fields[5];
258
+ hashMs = fields[6];
259
+ contentHashesRaw = fields[7];
260
+ }
261
+ const contentHashes = String(contentHashesRaw || '')
262
+ .split(',')
263
+ .filter((value) => value.length > 0)
264
+ .map((value) => (/^[a-f0-9]{64}$/i.test(value) ? value.toLowerCase() : null));
265
+ const failures = okPartial ? decodeNativeFailures(hexFailures) : [];
266
+ return {
267
+ partial: okPartial,
268
+ files: Number(files) || 0,
269
+ readMs: Number(readMs) || 0,
270
+ applyMs: Number(applyMs) || 0,
271
+ writeMs: Number(writeMs) || 0,
272
+ hashMs: Number(hashMs) || 0,
273
+ totalMs: Number(totalMs) || 0,
274
+ contentHashes,
275
+ contentHash: contentHashes.length === 1 ? contentHashes[0] : null,
276
+ failures,
277
+ roundtripMs: performance.now() - started,
278
+ };
279
+ }
280
+
281
+ // EDIT protocol client: invariant-safe char-indexed edit. Mirrors apply()'s
282
+ // abort/await-line handling. EDIT <path_len> <old_len> <new_len> <replace_all>
283
+ // <dry_run> then path+old+new bytes; response is the 8-field OK line with the
284
+ // matched tier.
285
+ async edit(fullPath, oldBuf, newBuf, { replaceAll = false, dryRun = false, signal = null } = {}) {
286
+ this.ref();
287
+ if (signal?.aborted) {
288
+ const err = new Error(signal.reason?.message || signal.reason || 'native edit aborted');
289
+ err.name = 'AbortError';
290
+ throw err;
291
+ }
292
+ const started = performance.now();
293
+ const pathBuf = Buffer.from(fullPath, 'utf8');
294
+ const linePromise = this.nextLine();
295
+ if (signal) linePromise.catch(() => {});
296
+ let abortListener = null;
297
+ const abortPromise = signal ? new Promise((_, reject) => {
298
+ abortListener = () => { reject(this.abort(signal)); };
299
+ signal.addEventListener('abort', abortListener, { once: true });
300
+ }) : null;
301
+ this.child.stdin.write(
302
+ `EDIT ${pathBuf.length} ${oldBuf.length} ${newBuf.length} ${replaceAll ? 1 : 0} ${dryRun ? 1 : 0}\n`,
303
+ );
304
+ this.child.stdin.write(pathBuf);
305
+ this.child.stdin.write(oldBuf);
306
+ this.child.stdin.write(newBuf);
307
+ let line;
308
+ try {
309
+ line = abortPromise ? await Promise.race([linePromise, abortPromise]) : await linePromise;
310
+ } finally {
311
+ if (abortListener) {
312
+ try { signal.removeEventListener('abort', abortListener); } catch {}
313
+ }
314
+ }
315
+ if (!line) throw new Error('no native response');
316
+ if (line.startsWith('ERR\t')) throw new Error(line.slice(4));
317
+ if (!line.startsWith('OK\t')) throw new Error(line);
318
+ const f = line.split('\t');
319
+ // OK \t replacements \t readMs \t applyMs \t writeMs \t totalMs \t tier \t hash
320
+ return {
321
+ replacements: Number(f[1]) || 0,
322
+ readMs: Number(f[2]) || 0,
323
+ applyMs: Number(f[3]) || 0,
324
+ writeMs: Number(f[4]) || 0,
325
+ totalMs: Number(f[5]) || 0,
326
+ tier: f[6] || 'exact',
327
+ contentHash: /^[a-f0-9]{64}$/i.test(f[7] || '') ? f[7].toLowerCase() : null,
328
+ roundtripMs: performance.now() - started,
329
+ };
330
+ }
331
+
332
+ async close(options = {}) {
333
+ if (this.exited) return;
334
+ const waitForExit = options?.waitForExit !== false;
335
+ if (!waitForExit) {
336
+ try { this.child.stdin.end('QUIT\n'); } catch {}
337
+ this.unref();
338
+ return;
339
+ }
340
+ this.ref();
341
+ try { this.child.stdin.end('QUIT\n'); } catch {}
342
+ await new Promise((resolve) => this.child.once('exit', resolve));
343
+ try { this.rl.close(); } catch {}
344
+ }
345
+ }
346
+
347
+ export function getNativePatchServer() {
348
+ markNativePatchRuntimeTouched();
349
+ const binPath = nativePatchBinPath();
350
+ if (!existsSync(binPath)) {
351
+ throw new Error(`native patch binary not found: ${binPath}`);
352
+ }
353
+ if (!_nativePatchServer || _nativePatchServer.exited || _nativePatchServer.binPath !== binPath) {
354
+ _nativePatchServer = new NativePatchServer(binPath);
355
+ }
356
+ return _nativePatchServer;
357
+ }
358
+
359
+ function getNativeEditServer() {
360
+ markNativePatchRuntimeTouched();
361
+ // Honor MIXDOG_EDIT_NATIVE_BIN (the same override the edit gating checks) so
362
+ // the gated binary and the spawned server binary cannot diverge.
363
+ const binPath = process.env.MIXDOG_EDIT_NATIVE_BIN || nativePatchBinPath();
364
+ if (!existsSync(binPath)) {
365
+ throw new Error(`native patch binary not found: ${binPath}`);
366
+ }
367
+ if (!_nativeEditServer || _nativeEditServer.exited || _nativeEditServer.binPath !== binPath) {
368
+ _nativeEditServer = new NativePatchServer(binPath);
369
+ }
370
+ return _nativeEditServer;
371
+ }
372
+
373
+ // Invariant-safe char-indexed edit over the persistent server (B2). Shares the
374
+ // NativePatchServer transport but runs on a DEDICATED instance so edit and
375
+ // patch requests never interleave their stdin framing on one stdout stream.
376
+ export async function runServerEdit({ fullPath, oldBuf, newBuf, replaceAll = false, dryRun = false, signal = null }) {
377
+ const server = getNativeEditServer();
378
+ return server.edit(fullPath, oldBuf, newBuf, { replaceAll, dryRun, signal });
379
+ }
380
+
381
+ export function scheduleNativePatchPrewarm() {
382
+ if (!nativePatchPrewarmEnabled() || _nativePatchPrewarmTimer || _nativePatchServer) return;
383
+ _nativePatchPrewarmTimer = setImmediate(() => {
384
+ void (async () => {
385
+ _nativePatchPrewarmTimer = null;
386
+ const started = performance.now();
387
+ try {
388
+ // Ensure the native binary is present (local build or fetched
389
+ // prebuilt) before starting the server. Best-effort: failures
390
+ // surface as a hard error at dispatch (no JS fallback in the
391
+ // native-only path).
392
+ if (!existsSync(nativePatchBinPath())) {
393
+ try { await ensurePatchBinary(getPluginData()); } catch { /* surfaces at dispatch */ }
394
+ }
395
+ await getNativePatchServer().ping();
396
+ if (!nativePatchPersistent() && (_nativePatchServer?.waiters?.length || 0) === 0) {
397
+ _nativePatchServer?.unref();
398
+ }
399
+ if (nativePatchTraceEnabled()) {
400
+ process.stderr.write(`[patch-native-trace] prewarm_ms=${(performance.now() - started).toFixed(3)}\n`);
401
+ }
402
+ } catch (err) {
403
+ if (nativePatchTraceEnabled()) {
404
+ process.stderr.write(`[patch-native-trace] prewarm_failed=${err?.message || String(err)}\n`);
405
+ }
406
+ }
407
+ })();
408
+ });
409
+ if (_nativePatchPrewarmTimer?.unref) _nativePatchPrewarmTimer.unref();
410
+ }
411
+
412
+ export function scheduleNativePatchIdleClose() {
413
+ if (nativePatchPersistent() || !_nativePatchServer) return;
414
+ if (process.versions?.bun) {
415
+ const server = _nativePatchServer;
416
+ _nativePatchServer = null;
417
+ void server?.close().catch(() => {});
418
+ return;
419
+ }
420
+ _nativePatchServer.unref();
421
+ }
422
+
423
+ export async function closeNativePatchServerForTests(options = {}) {
424
+ if (_nativePatchPrewarmTimer) {
425
+ try { clearImmediate(_nativePatchPrewarmTimer); } catch {}
426
+ _nativePatchPrewarmTimer = null;
427
+ }
428
+ const server = _nativePatchServer;
429
+ _nativePatchServer = null;
430
+ const editServer = _nativeEditServer;
431
+ _nativeEditServer = null;
432
+ await server?.close(options);
433
+ await editServer?.close(options);
434
+ }
435
+
436
+ try { globalThis.__mixdogCloseNativePatchServers = closeNativePatchServerForTests; } catch {}