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,460 @@
1
+ /**
2
+ * use-mouse-input.mjs — SGR mouse handling hook for the App shell.
3
+ *
4
+ * Extracted verbatim from App.jsx: the ctrl+wheel zoom passthrough and the
5
+ * big SGR input effect (wheel scroll routing, prompt/transcript/status text
6
+ * selection with word/line multi-click, drag auto-scroll). All shared state
7
+ * stays owned by App and is injected via refs/callbacks so behavior is
8
+ * unchanged; only the zoom-passthrough timer is owned here.
9
+ */
10
+ import { useCallback, useEffect, useRef } from 'react';
11
+ import { overlayBlocksGlobalTranscriptScroll } from './slash-commands.mjs';
12
+ import { TRANSCRIPT_MEASURED_ROWS } from './transcript-window.mjs';
13
+
14
+ const MOUSE_TRACKING_ON = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
15
+ const MOUSE_TRACKING_OFF = '\x1b[?1006l\x1b[?1002l\x1b[?1000l';
16
+ // Alternate-scroll mode (DECSET 1007). In the alt-screen, Windows Terminal
17
+ // converts wheel input into arrow keys while this mode is on — and that
18
+ // conversion WINS over WT's native ctrl+wheel font zoom. During the zoom
19
+ // passthrough window we must turn BOTH mouse tracking and alternate scroll
20
+ // off, or ctrl+wheel lands as Up/Down (prompt history) instead of zooming.
21
+ const ALT_SCROLL_OFF = '\x1b[?1007l';
22
+ const ALT_SCROLL_ON = '\x1b[?1007h';
23
+ const MOUSE_MODIFIER_MASK = 4 | 8 | 16;
24
+ const MOUSE_CTRL_MASK = 16;
25
+ // Bit 2 (4) of the SGR button byte = shift held during the click. Wheel/ctrl
26
+ // masking above intentionally strips it for scroll routing; button-press
27
+ // handling below reads it separately (before baseButton = button & 3 drops
28
+ // every modifier bit) so a shift-held left-click can extend an existing
29
+ // selection instead of starting a fresh one.
30
+ const MOUSE_SHIFT_MASK = 4;
31
+
32
+ export function useMouseInput({
33
+ inkInput,
34
+ isRawModeSupported,
35
+ store,
36
+ stdout,
37
+ rows,
38
+ statuslineBandRows,
39
+ dragRef,
40
+ lastClickRef,
41
+ slashPaletteRef,
42
+ scrollFocusRef,
43
+ promptMouseSelectionRef,
44
+ frameRowsRef,
45
+ promptBoxRectRef,
46
+ transcriptViewportRef,
47
+ scrollTargetRef,
48
+ stopSmoothScroll,
49
+ applySelectionRect,
50
+ applySelectionRectThrottled,
51
+ selectionPointAtCurrentScroll,
52
+ buildSpanRect,
53
+ scrollTranscriptRows,
54
+ queueScrollCoalesced,
55
+ setSlashIndex,
56
+ setMeasuredRowsVersion,
57
+ }) {
58
+ const mouseZoomPassthroughTimerRef = useRef(null);
59
+
60
+ const passthroughCtrlWheelZoom = useCallback(() => {
61
+ if (!stdout?.write) return;
62
+ try {
63
+ stdout.write(MOUSE_TRACKING_OFF + ALT_SCROLL_OFF);
64
+ } catch {
65
+ return;
66
+ }
67
+ if (mouseZoomPassthroughTimerRef.current) clearTimeout(mouseZoomPassthroughTimerRef.current);
68
+ mouseZoomPassthroughTimerRef.current = setTimeout(() => {
69
+ mouseZoomPassthroughTimerRef.current = null;
70
+ try {
71
+ stdout.write(ALT_SCROLL_ON + MOUSE_TRACKING_ON);
72
+ } catch {
73
+ // The terminal may already be closing.
74
+ }
75
+ }, 700);
76
+ mouseZoomPassthroughTimerRef.current.unref?.();
77
+ }, [stdout]);
78
+
79
+ useEffect(() => () => {
80
+ if (mouseZoomPassthroughTimerRef.current) clearTimeout(mouseZoomPassthroughTimerRef.current);
81
+ }, []);
82
+
83
+ // Optional mouse handling. When index.jsx enables SGR mouse tracking
84
+ // (?1000h button + ?1002h drag-motion + ?1006h SGR coords).
85
+ // Every event arrives as `\x1b[<b;col;rowM`
86
+ // (press/motion) or `\x1b[<b;col;rowm` (release), 1-based col/row. We watch raw
87
+ // stdin and split it two ways, both additive to ink's keyboard handling:
88
+ // • wheel (button 64 up / 65 down) → scroll the transcript
89
+ // • left-button (0) press → drag → release → in-app text selection; dragging
90
+ // against the top/bottom edge scrolls the transcript while selecting.
91
+ // The highlight stays visible after release so the user can confirm the
92
+ // selected region; ESC or a plain click clears it.
93
+ // Because we run a true fullscreen alt-screen, the reported (row,col) maps 1:1
94
+ // to ink's absolute output grid. We keep anchor/focus points instead of a
95
+ // rectangular min/max box so multi-line drags behave like normal text
96
+ // selection, not terminal block selection.
97
+ useEffect(() => {
98
+ if (!inkInput || !isRawModeSupported) return undefined;
99
+ // Match every SGR mouse event: button, col, row, and final M(press)/m(release).
100
+ const MOUSE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
101
+ const linearSelection = (a, b) => {
102
+ return {
103
+ mode: 'linear',
104
+ x1: a.x,
105
+ y1: a.y,
106
+ x2: b.x,
107
+ y2: b.y,
108
+ };
109
+ };
110
+ // Word/line multi-click drag-extension uses the hoisted buildSpanRect (same
111
+ // logic reachable from the auto-scroll path in scrollTranscriptRows).
112
+ const transcriptViewport = () => {
113
+ const top = Math.max(0, Number(transcriptViewportRef.current?.top) || 0);
114
+ const bottom = Math.max(top, Number(transcriptViewportRef.current?.bottom) || top);
115
+ return { top, bottom };
116
+ };
117
+ const isInTranscriptViewport = (row) => {
118
+ const { top, bottom } = transcriptViewport();
119
+ return row >= top && row <= bottom;
120
+ };
121
+ const clampToTranscriptViewport = (row) => {
122
+ const { top, bottom } = transcriptViewport();
123
+ return Math.max(top, Math.min(bottom, row));
124
+ };
125
+ // [mixdog] Status-bar band = the bottom statuslineBandRows rows. The
126
+ // prompt box occupies the rows reported by PromptInput's measured rect.
127
+ const statusBand = () => {
128
+ const rows = Math.max(1, Number(frameRowsRef.current) || 24);
129
+ const top = Math.max(0, rows - statuslineBandRows);
130
+ return { top, bottom: Math.max(top, rows - 1) };
131
+ };
132
+ const isInStatusBand = (row) => {
133
+ const { top, bottom } = statusBand();
134
+ return row >= top && row <= bottom;
135
+ };
136
+ const clampToStatusBand = (row) => {
137
+ const { top, bottom } = statusBand();
138
+ return Math.max(top, Math.min(bottom, row));
139
+ };
140
+ const promptRect = () => promptBoxRectRef.current;
141
+ const isInPromptBox = (x, y) => {
142
+ const r = promptRect();
143
+ if (!r) return false;
144
+ const top = Math.max(0, Number(r.top) || 0);
145
+ const bottom = top + Math.max(1, Number(r.height) || 1) - 1;
146
+ const left = Math.max(0, Number(r.left) || 0);
147
+ const width = Math.max(1, Number(r.contentWidth) || 1);
148
+ return y >= top && y <= bottom && x >= left && x <= left + width;
149
+ };
150
+ // Map an absolute grid cell to a prompt-draft edit offset via PromptInput's
151
+ // measured box rect + its caret math (offsetAtCell handles wrapping).
152
+ const promptOffsetAt = (x, y) => {
153
+ const r = promptRect();
154
+ const ctl = promptMouseSelectionRef.current;
155
+ if (!r || !ctl) return null;
156
+ const top = Math.max(0, Number(r.top) || 0);
157
+ const left = Math.max(0, Number(r.left) || 0);
158
+ const height = Math.max(1, Number(r.height) || 1);
159
+ const width = Math.max(1, Number(r.contentWidth) || 1);
160
+ // Clamp the mapped row/col to the box's own bounds so a drag that runs
161
+ // outside the prompt (above/below/left/right, e.g. onto the transcript
162
+ // or off-screen) still tracks the nearest edge cell instead of jumping
163
+ // to whatever offset a raw negative/overflowing row would resolve to.
164
+ const row = Math.max(0, Math.min(height - 1, y - top));
165
+ const col = Math.max(0, Math.min(width, x - left));
166
+ return ctl.offsetAtCell(row, col);
167
+ };
168
+ // Clear whichever selection is active (ink-grid rect AND/OR prompt engine).
169
+ const clearAllSelections = () => {
170
+ promptMouseSelectionRef.current?.clear?.();
171
+ applySelectionRect(null);
172
+ };
173
+ const onData = (data) => {
174
+ // ink emits each parsed input event as a string; a mouse SGR sequence
175
+ // arrives whole. Guard so non-mouse keystrokes fall through untouched.
176
+ const s = typeof data === 'string' ? data : String(data ?? '');
177
+ if (s.indexOf('\x1b[<') === -1) return;
178
+ let up = 0;
179
+ let down = 0;
180
+ let m;
181
+ MOUSE.lastIndex = 0;
182
+ while ((m = MOUSE.exec(s)) !== null) {
183
+ const button = Number(m[1]);
184
+ const x = Number(m[2]) - 1; // SGR is 1-based; grid is 0-based
185
+ const y = Number(m[3]) - 1;
186
+ const press = m[4] === 'M';
187
+ const wheelButton = button & ~MOUSE_MODIFIER_MASK;
188
+ if (wheelButton === 64 || wheelButton === 65) {
189
+ if ((button & MOUSE_CTRL_MASK) !== 0) {
190
+ passthroughCtrlWheelZoom();
191
+ continue;
192
+ }
193
+ if (wheelButton === 64) up += 1;
194
+ else down += 1;
195
+ continue;
196
+ }
197
+ // Low 2 bits = button id; bit 5 (32) = motion-while-pressed flag.
198
+ const baseButton = button & 3;
199
+ const isMotion = (button & 32) !== 0;
200
+ // Shift bit must be read BEFORE baseButton drops every modifier bit
201
+ // (button & 3); MOUSE_MODIFIER_MASK above is scroll-routing-only and
202
+ // deliberately treats shift as noise there.
203
+ const shiftHeld = (button & MOUSE_SHIFT_MASK) !== 0;
204
+ if (baseButton === 0 && press && !isMotion) {
205
+ // Region router: a press decides which surface owns this selection.
206
+ // Prompt box takes priority (it overlaps no transcript rows), then the
207
+ // transcript viewport, then the bottom statusline band. A press
208
+ // anywhere else clears any prior selection (plain click).
209
+ if (isInPromptBox(x, y)) {
210
+ // Clear any ink-grid selection so only one highlight is ever visible.
211
+ applySelectionRect(null);
212
+ const offset = promptOffsetAt(x, y);
213
+ stopSmoothScroll();
214
+ dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: true, rect: null, region: 'prompt', anchorSpan: null };
215
+ const ctl = promptMouseSelectionRef.current;
216
+ // Shift+click extends the EXISTING prompt selection (anchor stays
217
+ // put, cursor jumps to the click) instead of starting a fresh
218
+ // zero-width anchor at the click point.
219
+ if (shiftHeld && ctl?.hasSelection?.()) {
220
+ if (offset != null) ctl.extendTo?.(offset, true);
221
+ lastClickRef.current = { x: -1, y: -1, t: 0 };
222
+ continue;
223
+ }
224
+ // Multi-click word/line select (double = word, triple = line),
225
+ // same "qualifying press" window/drift tolerance used by the
226
+ // transcript/status path below. Reuses lastClickRef so a rapid
227
+ // double/triple click on the prompt box behaves the same as one on
228
+ // the transcript. A qualifying press advances the count; anything
229
+ // else (moved too far, too slow, or count already at 3) resets to
230
+ // a fresh single-click anchor.
231
+ const nowPrompt = Date.now();
232
+ const lcPrompt = lastClickRef.current;
233
+ const qualifiesPrompt = (nowPrompt - lcPrompt.t) < 500
234
+ && Math.abs(lcPrompt.y - y) <= 1
235
+ && Math.abs(lcPrompt.x - x) <= 2;
236
+ let promptClickCount = qualifiesPrompt ? (lcPrompt.count || 1) + 1 : 1;
237
+ if (promptClickCount > 3) promptClickCount = 1;
238
+ if ((promptClickCount === 2 || promptClickCount === 3) && offset != null) {
239
+ if (promptClickCount === 2) ctl?.selectWordAt?.(offset);
240
+ else ctl?.selectLineAt?.(offset);
241
+ // Click-select is already final (selectWordAt/selectLineAt set the
242
+ // full word/line range). Mark the drag inactive so a subsequent
243
+ // release does NOT fall into the generic prompt release handler
244
+ // below, which calls extendTo(releaseOffset) and would collapse
245
+ // this selection back down to anchor→releaseOffset (or empty on a
246
+ // no-motion release, since anchor was never re-anchored here).
247
+ dragRef.current.active = false;
248
+ lastClickRef.current = { x, y, t: nowPrompt, count: promptClickCount };
249
+ continue;
250
+ } else if (offset != null) {
251
+ ctl?.anchorAt?.(offset);
252
+ }
253
+ lastClickRef.current = { x, y, t: nowPrompt, count: 1 };
254
+ continue;
255
+ }
256
+ const inTranscript = isInTranscriptViewport(y);
257
+ const inStatus = !inTranscript && isInStatusBand(y);
258
+ if (!inTranscript && !inStatus) {
259
+ lastClickRef.current = { x: -1, y: -1, t: 0 };
260
+ dragRef.current.active = false;
261
+ dragRef.current.region = null;
262
+ dragRef.current.anchorSpan = null;
263
+ clearAllSelections();
264
+ continue;
265
+ }
266
+ const region = inTranscript ? 'transcript' : 'status';
267
+ // A press always clears the prompt-box selection (single active region).
268
+ promptMouseSelectionRef.current?.clear?.();
269
+ const now = Date.now();
270
+ // Shift+click extends the existing ink-grid selection in this SAME
271
+ // region from its original anchor to the new click point, instead of
272
+ // starting a fresh anchor here. Only applies to a plain char-drag
273
+ // selection (no anchorSpan) with a live non-empty rect; a word/line
274
+ // anchorSpan or an empty/absent selection falls through to a normal
275
+ // fresh press below.
276
+ if (
277
+ shiftHeld
278
+ && dragRef.current.region === region
279
+ && !dragRef.current.anchorSpan
280
+ && dragRef.current.anchor
281
+ && dragRef.current.rect
282
+ && !(dragRef.current.rect.x1 === dragRef.current.rect.x2 && dragRef.current.rect.y1 === dragRef.current.rect.y2)
283
+ ) {
284
+ const selectionY = region === 'status' ? clampToStatusBand(y) : clampToTranscriptViewport(y);
285
+ const anchor = region === 'status'
286
+ ? dragRef.current.anchor
287
+ : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
288
+ const rect = linearSelection(anchor, { x, y: selectionY });
289
+ stopSmoothScroll();
290
+ dragRef.current = {
291
+ ...dragRef.current,
292
+ last: { x, y: selectionY },
293
+ active: true,
294
+ region,
295
+ };
296
+ applySelectionRect(rect);
297
+ lastClickRef.current = { x, y, t: now, count: 1 };
298
+ continue;
299
+ }
300
+ // Multi-click sequence: 2nd consecutive press = word (double-click),
301
+ // 3rd = whole line (triple-click). Each press must land near the prior
302
+ // one within 500ms — up to 2 columns and 1 row of drift (terminals
303
+ // often report a shifted cell on repeat clicks); tighter matching made
304
+ // word selection unreliable. A 4th qualifying press restarts the
305
+ // sequence at 1 (simplest: reset). Works for
306
+ // transcript AND status rows since getWordRectAt/getLineRectAt are
307
+ // grid-based. Copy still happens on Ctrl+C, never here.
308
+ const lc = lastClickRef.current;
309
+ const qualifies = (now - lc.t) < 500
310
+ && Math.abs(lc.y - y) <= 1
311
+ && Math.abs(lc.x - x) <= 2;
312
+ let clickCount = qualifies ? (lc.count || 1) + 1 : 1;
313
+ if (clickCount > 3) clickCount = 1;
314
+ if (clickCount === 2 || clickCount === 3) {
315
+ // Word (2) or line (3) select. Snap to the word/line under the cell
316
+ // and record the span on dragRef so a following drag extends by whole
317
+ // words/lines from this span (see buildSpanRect). Leave the drag
318
+ // ARMED (active:true): a release without motion keeps this highlight
319
+ // (buildSpanRect returns the span for an in-span target), while
320
+ // any motion extends it. Mirrors selectWordAt/selectLineAt setting
321
+ // isDragging=true + anchorSpan; the mouse-up finalizes.
322
+ const kind = clickCount === 2 ? 'word' : 'line';
323
+ const wr = kind === 'word' ? store.getWordRectAt?.(x, y) : store.getLineRectAt?.(y);
324
+ if (wr) {
325
+ const lo = { x: wr.x1, y: wr.y1 };
326
+ const hi = { x: wr.x2, y: wr.y2 };
327
+ const rect = linearSelection(lo, hi);
328
+ stopSmoothScroll();
329
+ dragRef.current = {
330
+ anchor: { x, y },
331
+ anchorScroll: region === 'transcript' ? scrollTargetRef.current : 0,
332
+ last: { x, y },
333
+ active: true,
334
+ rect: null,
335
+ region,
336
+ anchorSpan: { lo, hi, kind },
337
+ };
338
+ applySelectionRect(rect);
339
+ lastClickRef.current = { x, y, t: now, count: clickCount };
340
+ continue;
341
+ }
342
+ }
343
+ lastClickRef.current = { x, y, t: now, count: 1 };
344
+ // Left-button press: begin a new selection anchored here.
345
+ // Anchor the drag but do NOT paint a zero-width selection yet; a plain
346
+ // single click should not flash a one-cell highlight. The selection is
347
+ // only rendered once a drag actually extends past the anchor.
348
+ // Status-band selections do NOT scroll, so anchorScroll is irrelevant
349
+ // there; keep the transcript scroll anchor only for the transcript.
350
+ // Plain single press clears any word/line anchorSpan (char-drag mode).
351
+ stopSmoothScroll();
352
+ dragRef.current = {
353
+ anchor: { x, y },
354
+ anchorScroll: region === 'transcript' ? scrollTargetRef.current : 0,
355
+ last: { x, y },
356
+ active: true,
357
+ rect: null,
358
+ region,
359
+ anchorSpan: null,
360
+ };
361
+ } else if (baseButton === 0 && isMotion && dragRef.current.active) {
362
+ const region = dragRef.current.region;
363
+ if (region === 'prompt') {
364
+ // Prompt drag: extend the PromptInput selection to the mapped offset.
365
+ // The cell is clamped to the box rows so a drag outside still tracks
366
+ // the nearest edge of the editable content.
367
+ const offset = promptOffsetAt(x, y);
368
+ dragRef.current.last = { x, y };
369
+ if (offset != null) promptMouseSelectionRef.current?.extendTo?.(offset);
370
+ continue;
371
+ }
372
+ // Drag motion (transcript or status): extend the selection to the
373
+ // current cell, clamped to the owning region's band.
374
+ const selectionY = region === 'status' ? clampToStatusBand(y) : clampToTranscriptViewport(y);
375
+ dragRef.current.last = { x, y: selectionY };
376
+ const span = dragRef.current.anchorSpan;
377
+ if (span) {
378
+ // Word/line multi-click drag: extend by whole words/lines from the
379
+ // anchor span to the word/line under the cursor (see buildSpanRect).
380
+ const rect = buildSpanRect(span, x, selectionY, region, dragRef.current.anchorScroll);
381
+ applySelectionRectThrottled(rect);
382
+ } else {
383
+ const anchor = region === 'status'
384
+ ? dragRef.current.anchor
385
+ : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
386
+ const rect = linearSelection(anchor, { x, y: selectionY });
387
+ applySelectionRectThrottled(rect);
388
+ }
389
+ // Auto-scroll-while-dragging is transcript-only (the status band does
390
+ // not scroll).
391
+ if (region === 'transcript') {
392
+ const frameRows = Math.max(1, Number(rows) || 24);
393
+ if (y <= 1) {
394
+ queueScrollCoalesced(3);
395
+ } else if (y >= frameRows - 5) {
396
+ queueScrollCoalesced(-3);
397
+ }
398
+ }
399
+ } else if (!press && dragRef.current.active) {
400
+ const region = dragRef.current.region;
401
+ if (region === 'prompt') {
402
+ // Finalize the prompt selection; highlight persists (copy on Ctrl+C).
403
+ const offset = promptOffsetAt(x, y);
404
+ dragRef.current.active = false;
405
+ promptMouseSelectionRef.current?.extendTo?.(offset, true);
406
+ continue;
407
+ }
408
+ // Button release while dragging: finalize with the release coordinate
409
+ // (the SGR release event carries col/row) and keep the selection
410
+ // visible. Copy is NOT automatic — the user presses Ctrl+C to copy.
411
+ // The highlight stays until ESC or a plain click.
412
+ const span = dragRef.current.anchorSpan;
413
+ const releaseY = region === 'status' ? clampToStatusBand(y) : clampToTranscriptViewport(y);
414
+ dragRef.current.active = false;
415
+ if (span) {
416
+ // Word/line multi-click release: finalize from the span to the
417
+ // word/line at the release cell. A release-without-motion resolves
418
+ // to the span itself (in-span target), so the original word/line
419
+ // highlight stays — never cleared as "empty" like a bare click.
420
+ const rect = buildSpanRect(span, x, releaseY, region, dragRef.current.anchorScroll);
421
+ applySelectionRect(rect);
422
+ } else {
423
+ const anchor = region === 'status'
424
+ ? dragRef.current.anchor
425
+ : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
426
+ const rect = linearSelection(anchor, { x, y: releaseY });
427
+ const empty = rect.x1 === rect.x2 && rect.y1 === rect.y2;
428
+ if (empty) {
429
+ applySelectionRect(null); // a plain click clears any prior highlight
430
+ } else {
431
+ // Push the final rect so ink re-renders the visible selection.
432
+ applySelectionRect(rect);
433
+ }
434
+ }
435
+ // Drag is over: the measured-height harvest was skipped for every
436
+ // motion commit (see the harvest effect's dragRef guard), so force one
437
+ // re-measure now to reconcile any row whose true height drifted from
438
+ // its estimate while the drag was in flight.
439
+ if (TRANSCRIPT_MEASURED_ROWS) setMeasuredRowsVersion((v) => (v + 1) % 1000000);
440
+ }
441
+ }
442
+ if (up !== 0 || down !== 0) {
443
+ if (dragRef.current.active) return;
444
+ const palette = slashPaletteRef.current;
445
+ if (palette.open && palette.count > 0) {
446
+ const step = down - up;
447
+ if (step !== 0) {
448
+ setSlashIndex((index) => Math.max(0, Math.min(palette.count - 1, index + step)));
449
+ }
450
+ return;
451
+ }
452
+ if (overlayBlocksGlobalTranscriptScroll(scrollFocusRef.current)) return;
453
+ const STEP = 3; // rows per wheel notch; immediate updates feel steadier in Windows Terminal
454
+ queueScrollCoalesced((up - down) * STEP);
455
+ }
456
+ };
457
+ inkInput.on('input', onData);
458
+ return () => { inkInput.off('input', onData); };
459
+ }, [inkInput, isRawModeSupported, store, passthroughCtrlWheelZoom, rows, scrollTranscriptRows, queueScrollCoalesced, applySelectionRect, applySelectionRectThrottled, selectionPointAtCurrentScroll, buildSpanRect]);
460
+ }