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
@@ -0,0 +1,340 @@
1
+ // Path resolution / entry classification helpers for apply_patch. Moved
2
+ // verbatim from patch.mjs; path/diff semantics unchanged.
3
+
4
+ import { existsSync, realpathSync } from 'node:fs';
5
+ import { resolve as pathResolve, relative as pathRelative, isAbsolute, dirname as pathDirname } from 'node:path';
6
+ import {
7
+ normalizeInputPath,
8
+ normalizeOutputPath,
9
+ resolveAgainstCwd,
10
+ } from '../builtin.mjs';
11
+ import { assertPathReachable, assertPathsReachable } from '../builtin/fs-reachability.mjs';
12
+ import { nativePatchEnabled, nativePatchBinPath } from './native-server.mjs';
13
+ import { DEV_NULL } from './constants.mjs';
14
+
15
+ // Strip the leading `a/` or `b/` prefix that `diff -u` / git emit by
16
+ // default, plus timestamp suffixes (`\t2024-...`) that some tools append
17
+ // to header lines. parsePatch already splits the name from the header
18
+ // so timestamps land in `oldHeader` / `newHeader`, but be defensive.
19
+ export function stripDiffPrefix(name) {
20
+ if (!name) return name;
21
+ if (isAbsolute(name) || /^[A-Za-z]:[\\/]/.test(name)) return name;
22
+ const m = /^[ab]\/(.+)$/.exec(name);
23
+ return m ? m[1] : name;
24
+ }
25
+
26
+ export function resolveEntryPath(basePath, rawName) {
27
+ const stripped = stripDiffPrefix(rawName);
28
+ const norm = normalizeInputPath(stripped);
29
+ return isAbsolute(norm) ? pathResolve(norm) : resolveAgainstCwd(norm, basePath);
30
+ }
31
+
32
+ // V4A section paths are real repository paths and never carry the unified
33
+ // diff `a/`·`b/` prefix, so resolution must NOT apply stripDiffPrefix — a
34
+ // legitimate top-level `a/` or `b/` directory would otherwise be silently
35
+ // rewritten to its child, reading/writing the wrong file.
36
+ export function resolveV4AEntryPath(basePath, rawName) {
37
+ const norm = normalizeInputPath(rawName);
38
+ return isAbsolute(norm) ? pathResolve(norm) : resolveAgainstCwd(norm, basePath);
39
+ }
40
+
41
+ export function resolveBasePath(cwd, basePath) {
42
+ if (!basePath) return cwd;
43
+ const norm = normalizeInputPath(basePath);
44
+ return isAbsolute(norm) ? pathResolve(norm) : resolveAgainstCwd(norm, cwd);
45
+ }
46
+
47
+ // Categorise the per-file entry. A unified diff can describe:
48
+ // - modify : both files named, oldFileName exists on disk
49
+ // - create : oldFileName === /dev/null (or file doesn't exist + hunks start at 0)
50
+ // - delete : newFileName === /dev/null
51
+ export function classifyEntry(entry) {
52
+ const oldIsNull = DEV_NULL.test(entry.oldFileName || '');
53
+ const newIsNull = DEV_NULL.test(entry.newFileName || '');
54
+ if (oldIsNull && !newIsNull) return 'create';
55
+ if (!oldIsNull && newIsNull) return 'delete';
56
+ return 'modify';
57
+ }
58
+
59
+ function parsedEntryTargetKey(entry, basePath) {
60
+ if (classifyEntry(entry) !== 'modify') return '';
61
+ const headerName = entry.oldFileName || entry.newFileName;
62
+ if (!headerName || DEV_NULL.test(headerName)) return '';
63
+ const fullPath = resolveEntryPath(basePath, headerName);
64
+ return process.platform === 'win32' ? fullPath.toLowerCase() : fullPath;
65
+ }
66
+
67
+ export function mergeDuplicateParsedModifyEntries(parsed, basePath) {
68
+ const out = [];
69
+ const byTarget = new Map();
70
+ let changed = false;
71
+ for (const entry of parsed || []) {
72
+ const key = parsedEntryTargetKey(entry, basePath);
73
+ if (!key) {
74
+ out.push(entry);
75
+ continue;
76
+ }
77
+ const existing = byTarget.get(key);
78
+ if (!existing) {
79
+ byTarget.set(key, entry);
80
+ out.push(entry);
81
+ continue;
82
+ }
83
+ existing.hunks.push(...(entry.hunks || []));
84
+ changed = true;
85
+ }
86
+ return { parsed: out, changed };
87
+ }
88
+
89
+ function unifiedRange(start, lines) {
90
+ const s = Math.max(0, Number(start) || 0);
91
+ const n = Math.max(0, Number(lines) || 0);
92
+ return `${s},${n}`;
93
+ }
94
+
95
+ export function renderParsedUnifiedPatch(parsed) {
96
+ const out = [];
97
+ for (const entry of parsed || []) {
98
+ out.push(`--- ${entry.oldFileName || '/dev/null'}`);
99
+ out.push(`+++ ${entry.newFileName || '/dev/null'}`);
100
+ for (const hunk of entry.hunks || []) {
101
+ const section = hunk.section ? ` ${hunk.section}` : '';
102
+ out.push(`@@ -${unifiedRange(hunk.oldStart, hunk.oldLines)} +${unifiedRange(hunk.newStart, hunk.newLines)} @@${section}`);
103
+ for (const line of hunk.lines || []) out.push(line);
104
+ }
105
+ }
106
+ return `${out.join('\n')}\n`;
107
+ }
108
+
109
+ // Count how many source lines a hunk consumes vs produces so we can
110
+ // surface a concise `lines_changed` figure without re-diffing.
111
+ export function countHunkChanges(hunks) {
112
+ let added = 0;
113
+ let removed = 0;
114
+ for (const h of hunks || []) {
115
+ for (const line of h.lines || []) {
116
+ if (line.startsWith('+')) added++;
117
+ else if (line.startsWith('-')) removed++;
118
+ }
119
+ }
120
+ return { added, removed };
121
+ }
122
+
123
+ // Header-shape pre-validator (lexical only): a missing header, a
124
+ // `..` segment, or an absolute path that does not resolve inside the
125
+ // basePath returns false. Caller wraps the negative result into a clean
126
+ // throw so unsupported headers surface as a clean error instead of
127
+ // silently degrading to a JS fallback.
128
+ function nativeHeaderSupported(entry, basePath) {
129
+ const kind = classifyEntry(entry);
130
+ const headerName = kind === 'create' ? entry.newFileName : entry.oldFileName;
131
+ if (!headerName || DEV_NULL.test(headerName)) return false;
132
+ // Reject any `..` segment on EITHER oldFileName or newFileName (every
133
+ // non-/dev/null header) — a modify whose newFileName traverses out of
134
+ // base must still be refused even when the resolved path lands inside.
135
+ for (const which of ['oldFileName', 'newFileName']) {
136
+ const raw = entry[which];
137
+ if (!raw || DEV_NULL.test(raw)) continue;
138
+ const segs = normalizeInputPath(stripDiffPrefix(raw)).split(/[\\/]+/);
139
+ if (segs.some((part) => part === '..')) return false;
140
+ }
141
+ const stripped = stripDiffPrefix(headerName);
142
+ const norm = normalizeInputPath(stripped);
143
+ if (isAbsolute(norm) || /^[A-Za-z]:[\\/]/.test(norm)) {
144
+ if (!basePath) return false;
145
+ const absHeader = pathResolve(norm);
146
+ const absBase = pathResolve(basePath);
147
+ const rel = pathRelative(absBase, absHeader);
148
+ if (!rel || rel.startsWith('..') || isAbsolute(rel)) return false;
149
+ if (rel.split(/[\\/]+/).some((part) => part === '..')) return false;
150
+ return true;
151
+ }
152
+ return true;
153
+ }
154
+
155
+ // Resolve `absPath` via fs.realpathSync, walking up to the nearest existing
156
+ // ancestor when the leaf does not yet exist (e.g. a create-mode target).
157
+ // Returns the resolved real path, or the lexically-resolved path if no
158
+ // ancestor can be realpath'd.
159
+ export function realpathNearestExistingAncestor(absPath) {
160
+ let cur = pathResolve(absPath);
161
+ while (true) {
162
+ try {
163
+ return realpathSync(cur);
164
+ } catch {
165
+ const parent = pathDirname(cur);
166
+ if (!parent || parent === cur) return cur;
167
+ cur = parent;
168
+ }
169
+ }
170
+ }
171
+
172
+ // Pre-validator (throws): walks every parsed entry and enforces
173
+ // - native engine is enabled
174
+ // - native binary exists on disk
175
+ // - each header is shape-supported (no `..`, no out-of-base absolute)
176
+ // - realpath of each non-/dev/null header stays inside the real basePath
177
+ // (catches symlink/junction escapes that lexical checks miss)
178
+ // - no duplicate target paths in the patch (case-insensitive on win32)
179
+ // Returns the list of normalized entry rows the dispatcher uses to format
180
+ // output, plus the header-rewrite map for absolute-path normalization.
181
+ export async function preValidateNativeBatch(parsed, basePath) {
182
+ if (!nativePatchEnabled()) {
183
+ throw new Error('apply_patch: native engine disabled via MIXDOG_PATCH_NATIVE; set it to "auto" or "1" to apply patches.');
184
+ }
185
+ const binPath = nativePatchBinPath();
186
+ if (!existsSync(binPath)) {
187
+ throw new Error(`apply_patch: native patch binary not found at ${binPath}; build native/mixdog-patch or fetch the prebuilt before invoking apply_patch.`);
188
+ }
189
+ if (!Array.isArray(parsed) || parsed.length === 0) {
190
+ throw new Error('apply_patch: patch contained no file sections');
191
+ }
192
+ await assertPathReachable(basePath);
193
+ const reachabilityPaths = [];
194
+ for (const entry of parsed) {
195
+ for (const which of ['oldFileName', 'newFileName']) {
196
+ const checkName = entry[which];
197
+ if (!checkName || DEV_NULL.test(checkName)) continue;
198
+ reachabilityPaths.push(resolveEntryPath(basePath, checkName));
199
+ }
200
+ }
201
+ await assertPathsReachable(reachabilityPaths);
202
+ let realBase;
203
+ try {
204
+ realBase = realpathSync(pathResolve(basePath));
205
+ } catch (err) {
206
+ throw new Error(`apply_patch: base_path unreadable (${err?.code || err?.message || String(err)}): ${basePath}`);
207
+ }
208
+ const entries = [];
209
+ const seenPaths = new Set();
210
+ const headerRewrites = [];
211
+ for (const entry of parsed) {
212
+ const kind = classifyEntry(entry);
213
+ const headerName = kind === 'create' ? entry.newFileName : entry.oldFileName;
214
+ if (!nativeHeaderSupported(entry, basePath)) {
215
+ // Two distinct failure modes share nativeHeaderSupported()===false:
216
+ // (a) no usable target path could be parsed from the section header
217
+ // (oldFileName/newFileName empty or /dev/null) — a SHAPE problem,
218
+ // (b) a parsed path that escapes base_path (`..` segment / out-of-base
219
+ // absolute) — a SECURITY refusal.
220
+ // Collapsing both into the "path escapes base_path or contains `..`"
221
+ // message mislabels (a) as (b): the model sees a nonsensical reason for a
222
+ // patch that uses no `..`, cannot self-correct, and re-submits the same
223
+ // patch until the iteration cap. Emit a shape-specific, actionable hint
224
+ // for (a) so the loop breaks on the first failure.
225
+ const hasUsableHeader = headerName && !DEV_NULL.test(headerName);
226
+ if (!hasUsableHeader) {
227
+ throw new Error(
228
+ 'apply_patch: a file section header could not be parsed (no target path). '
229
+ + 'Each section must start with a valid header: `*** Update File: <path>` / '
230
+ + '`*** Add File: <path>` / `*** Delete File: <path>` (V4A), or a '
231
+ + '`--- a/<path>` + `+++ b/<path>` pair (unified). Wrap multi-hunk V4A '
232
+ + 'edits in a `*** Begin Patch` / `*** End Patch` envelope and pass format:"v4a".',
233
+ );
234
+ }
235
+ const display = normalizeOutputPath(stripDiffPrefix(headerName));
236
+ throw new Error(`apply_patch: header ${display} is unsupported (path escapes base_path or contains \`..\`).`);
237
+ }
238
+ if (kind !== 'delete' && !(entry.hunks?.length > 0)) {
239
+ const display = headerName ? normalizeOutputPath(stripDiffPrefix(headerName)) : '(unknown)';
240
+ throw new Error(`apply_patch: entry ${display} has no hunks — patch header malformed (use \`@@ -A,B +C,D @@\` per hunk).`);
241
+ }
242
+ // Realpath each non-/dev/null header; nearest-existing-ancestor handles
243
+ // create-mode leaves that do not yet exist. A symlink/junction whose
244
+ // target escapes basePath fails here even when the lexical check above
245
+ // looked safe.
246
+ for (const which of ['oldFileName', 'newFileName']) {
247
+ const checkName = entry[which];
248
+ if (!checkName || DEV_NULL.test(checkName)) continue;
249
+ const checkFull = resolveEntryPath(basePath, checkName);
250
+ const checkReal = realpathNearestExistingAncestor(checkFull);
251
+ const checkRel = pathRelative(realBase, checkReal);
252
+ if (checkRel.split(/[\\/]+/).some((part) => part === '..') || isAbsolute(checkRel)) {
253
+ const display = normalizeOutputPath(stripDiffPrefix(checkName));
254
+ throw new Error(`apply_patch: ${display} resolves outside base_path via symlink/junction; refusing to apply.`);
255
+ }
256
+ }
257
+ const fullPath = resolveEntryPath(basePath, headerName);
258
+ const pathKey = process.platform === 'win32' ? fullPath.toLowerCase() : fullPath;
259
+ if (seenPaths.has(pathKey)) {
260
+ const display = normalizeOutputPath(stripDiffPrefix(headerName));
261
+ throw new Error(`apply_patch: duplicate target ${display} — patch lists the same path twice.`);
262
+ }
263
+ seenPaths.add(pathKey);
264
+ const displayPath = normalizeOutputPath(stripDiffPrefix(headerName));
265
+ const { added, removed } = countHunkChanges(entry.hunks);
266
+ entries.push({
267
+ kind,
268
+ fullPath,
269
+ displayPath,
270
+ added,
271
+ removed,
272
+ hunks: entry.hunks?.length || 0,
273
+ linesChanged: added + removed,
274
+ });
275
+ // Absolute-form headers must be rewritten to repo-relative before the
276
+ // native server, which joins headers to basePath, sees them.
277
+ for (const which of ['oldFileName', 'newFileName']) {
278
+ const raw = entry[which];
279
+ if (!raw || DEV_NULL.test(raw)) continue;
280
+ const stripped = stripDiffPrefix(raw);
281
+ const norm = normalizeInputPath(stripped);
282
+ if (!isAbsolute(norm) && !/^[A-Za-z]:[\\/]/.test(norm)) continue;
283
+ const rel = pathRelative(pathResolve(basePath), pathResolve(norm)).replace(/\\/g, '/');
284
+ if (!rel || rel.startsWith('..') || isAbsolute(rel)) {
285
+ const display = normalizeOutputPath(stripDiffPrefix(raw));
286
+ throw new Error(`apply_patch: absolute header ${display} does not resolve inside base_path.`);
287
+ }
288
+ headerRewrites.push({ from: raw, to: rel });
289
+ }
290
+ }
291
+ return { entries, headerRewrites };
292
+ }
293
+
294
+ // Rewrite ONLY the file-section header lines (`--- old`/`+++ new`) that
295
+ // precede each hunk so the native server, which joins headers to
296
+ // basePath, never sees an absolute header. A hunk DELETION line is `-`
297
+ // + content, so a deleted line whose body text is `-- C:/...` renders as
298
+ // `--- C:/...`; track hunk-body state by consuming each `@@ -a,b +c,d @@`
299
+ // header's declared line counts so only lines outside any hunk body are
300
+ // eligible for rewrite.
301
+ export function rewriteHeaderPaths(patchStr, headerRewrites) {
302
+ if (!headerRewrites || headerRewrites.length === 0) return patchStr;
303
+ const lines = patchStr.split('\n');
304
+ let i = 0;
305
+ while (i < lines.length) {
306
+ const line = lines[i];
307
+ if (line.startsWith('@@ ')) {
308
+ const m = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line);
309
+ let oldRem = m && m[1] !== undefined ? Number(m[1]) : 1;
310
+ let newRem = m && m[2] !== undefined ? Number(m[2]) : 1;
311
+ i++;
312
+ while (i < lines.length && (oldRem > 0 || newRem > 0)) {
313
+ const body = lines[i];
314
+ const c = body.charAt(0);
315
+ if (c === ' ') { oldRem--; newRem--; }
316
+ else if (c === '-') { oldRem--; }
317
+ else if (c === '+') { newRem--; }
318
+ else if (c === '\\') { /* "" marker */ }
319
+ else break;
320
+ i++;
321
+ }
322
+ continue;
323
+ }
324
+ if (line.startsWith('--- ') || line.startsWith('+++ ')) {
325
+ const prefix = line.slice(0, 4);
326
+ const rest = line.slice(4);
327
+ const tabIdx = rest.indexOf('\t');
328
+ const pathPart = tabIdx === -1 ? rest : rest.slice(0, tabIdx);
329
+ const suffix = tabIdx === -1 ? '' : rest.slice(tabIdx);
330
+ for (const { from, to } of headerRewrites) {
331
+ if (pathPart === from) {
332
+ lines[i] = `${prefix}${to}${suffix}`;
333
+ break;
334
+ }
335
+ }
336
+ }
337
+ i++;
338
+ }
339
+ return lines.join('\n');
340
+ }