mixdog 0.9.2 → 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 (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  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/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -26,16 +26,23 @@ Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
26
26
  - SEQUENTIAL within a single complex scope — split it into ordered steps
27
27
  rather than handing it off in one shot, with a build/test-green gate
28
28
  between steps.
29
- - Reuse the existing agent (same tag) for follow-up on the same scope; spawn
30
- new only for a separate scope. Write briefs per the lead-tool brief
31
- contract (token-optimized labeled fragments).
29
+ - Write briefs per the Lead brief contract (token-optimized labeled
30
+ fragments).
32
31
  - After spawning async agent(s), END THE TURN — do not poll, guess, or start
33
- dependent work until the completion notification resumes you (status/read
34
- are manual recovery only). Then wait and continue automatically.
32
+ dependent work until the completion notification resumes you. Then wait
33
+ and continue automatically.
35
34
  3. Review — pair one reviewer 1:1 with each implementation scope, spawned in the
36
- same turn. Cross-check implementation and review results yourself before
37
- acting on them. Send fixes back to the original scope and repeat until clean.
38
- Skip review only for simple, low-risk tasks.
35
+ same turn; never defer or batch the reviewer call — wait for its result.
36
+ Fact-check the agent response and cross-check implementation and review
37
+ results yourself before acting on them. Send fixes back to the original
38
+ scope and loop verify -> fix -> re-verify until clean. Skip review only for
39
+ simple, low-risk tasks. If the user asks for debugging, or a bug survives
40
+ 2+ fix cycles, have the debugger investigate first instead of another fix
41
+ round.
39
42
  4. Report — deliver the final report: synthesize results (outcome + key
40
43
  evidence; never forward raw agent output), state the final state, and ask the
41
- user whether to ship/deploy when relevant.
44
+ user whether to ship/deploy when relevant. Only after user feedback with no
45
+ issues prepare deploy/build/commit.
46
+
47
+ On any major change or direction shift mid-work, pause and re-consult the user
48
+ before continuing.
@@ -29,18 +29,23 @@ Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
29
29
  - SEQUENTIAL within a single complex scope — split it into ordered steps
30
30
  rather than handing it off in one shot, with a build/test-green gate
31
31
  between steps.
32
- - Reuse the existing agent (same tag) for follow-up on the same scope; spawn
33
- new only for a separate scope. Write briefs per the lead-tool brief
34
- contract (token-optimized labeled fragments).
32
+ - Write briefs per the Lead brief contract (token-optimized labeled
33
+ fragments).
35
34
  - After spawning an async agent, END THE TURN — do not poll, guess, or start
36
- dependent or subsequent work until the completion notification resumes you
37
- (status/read are manual recovery only). Then wait and continue
38
- automatically.
35
+ dependent or subsequent work until the completion notification resumes
36
+ you. Then wait and continue automatically.
39
37
  3. Review — after each implementation scope completes, pair one reviewer 1:1
40
- with that scope (spawned on its own turn, not batched). Cross-check
41
- implementation and review results yourself before acting on them. Send fixes
42
- back to the original scope and repeat until clean. Skip review only for
43
- simple, low-risk tasks.
38
+ with that scope (spawned on its own turn, not batched); never defer the
39
+ reviewer call wait for its result. Fact-check the agent response and
40
+ cross-check implementation and review results yourself before acting on
41
+ them. Send fixes back to the original scope and loop verify -> fix ->
42
+ re-verify until clean. Skip review only for simple, low-risk tasks. If the
43
+ user asks for debugging, or a bug survives 2+ fix cycles, have the debugger
44
+ investigate first instead of another fix round.
44
45
  4. Report — deliver the final report: synthesize results (outcome + key
45
46
  evidence; never forward raw agent output), state the final state, and ask the
46
- user whether to ship/deploy when relevant.
47
+ user whether to ship/deploy when relevant. Only after user feedback with no
48
+ issues prepare deploy/build/commit.
49
+
50
+ On any major change or direction shift mid-work, pause and re-consult the user
51
+ before continuing.
@@ -14,7 +14,11 @@ agents:
14
14
  3. Verify — Lead checks the result directly, fixes issues directly, and repeats
15
15
  until the work is clean or a blocker must be reported.
16
16
  4. Report — Lead summarizes the final state, verification result, and any
17
- remaining risk or requested next step.
17
+ remaining risk or requested next step. Only after user feedback with no
18
+ issues prepare deploy/build/commit.
19
+
20
+ On any major change or direction shift mid-work, pause and re-consult the user
21
+ before continuing.
18
22
 
19
23
  Delegation rule:
20
24
  - Do not delegate, spawn, send, or ask any agent to perform work.
@@ -16,11 +16,21 @@
16
16
  * overrides and wins. OFF ⇒ identical to plain string-width.
17
17
  */
18
18
  import stringWidth from 'string-width';
19
+ // [mixdog fork] Grapheme segmenter for cluster-aware width math (e.g. `↔️` =
20
+ // U+2194 U+FE0F is one cluster, counted once). Kept in sync with
21
+ // src/tui/display-width.mjs.
22
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
19
23
 
20
24
  function isProblemCodePoint(cp) {
21
25
  return (cp >= 0x2460 && cp <= 0x24ff) || (cp >= 0x2190 && cp <= 0x21ff);
22
26
  }
23
27
 
28
+ // [mixdog fork] Fast precheck for the problem ranges above. Lets the hot path
29
+ // bail before the per-grapheme segmenter loop when a string (the common
30
+ // ASCII/status-text case) contains no widenable glyph. Kept in sync with
31
+ // src/tui/display-width.mjs.
32
+ const PROBLEM_RE = /[\u2190-\u21ff\u2460-\u24ff]/;
33
+
24
34
  function resolveAmbiguousWidePolicy(env = process.env) {
25
35
  const override = env?.MIXDOG_TUI_AMBIGUOUS_WIDE;
26
36
  if (override === '1')
@@ -38,10 +48,16 @@ export function displayWidthWith(str, wide) {
38
48
  const base = stringWidth(s);
39
49
  if (!wide)
40
50
  return base;
51
+ // Fast bail: no problem glyph ⇒ segmenting can only return `base`.
52
+ if (!PROBLEM_RE.test(s))
53
+ return base;
41
54
  let extra = 0;
42
- for (const ch of s) {
43
- const cp = ch.codePointAt(0);
44
- if (isProblemCodePoint(cp) && stringWidth(ch) === 1)
55
+ // Widen per GRAPHEME CLUSTER, not per code point (kept in sync with
56
+ // src/tui/display-width.mjs): a clustered glyph like `↔️` (U+2194 U+FE0F)
57
+ // is counted once, and only when the whole cluster still measures 1 cell.
58
+ for (const { segment } of graphemeSegmenter.segment(s)) {
59
+ const cp = segment.codePointAt(0);
60
+ if (isProblemCodePoint(cp) && stringWidth(segment) === 1)
45
61
  extra += 1;
46
62
  }
47
63
  return base + extra;
@@ -86,16 +86,25 @@ const stripKittyQueryResponsesAndTrailingPartial = (buffer) => {
86
86
  // viewport, leaving stale copies of previous frames behind (#969). Keep the
87
87
  // pre-7.0 behavior of fully clearing between fullscreen frames there.
88
88
  const isWindowsConsole = process.platform === 'win32' || Boolean(process.env.WT_SESSION);
89
- const shouldClearTerminalForFrame = ({ isTty, viewportRows, previousOutputHeight, nextOutputHeight, isUnmounting, }) => {
89
+ const shouldClearTerminalForFrame = ({ isTty, viewportRows, previousViewportRows, previousOutputHeight, nextOutputHeight, isUnmounting, }) => {
90
90
  if (!isTty) {
91
91
  return false;
92
92
  }
93
+ const priorViewportRows = previousViewportRows ?? viewportRows;
93
94
  const hadPreviousFrame = previousOutputHeight > 0;
94
- const wasFullscreen = previousOutputHeight >= viewportRows;
95
- const wasOverflowing = previousOutputHeight > viewportRows;
95
+ const wasFullscreen = previousOutputHeight >= priorViewportRows;
96
+ const wasOverflowing = previousOutputHeight > priorViewportRows;
96
97
  const isOverflowing = nextOutputHeight > viewportRows;
97
98
  const isFullscreen = nextOutputHeight >= viewportRows;
98
99
  const isLeavingFullscreen = wasFullscreen && nextOutputHeight < viewportRows;
100
+ // [mixdog fork] A frame that SHRINKS by more than a line while at/over the
101
+ // viewport can leave stale wrapped rows below the new content on ANY
102
+ // terminal (not just Windows) — the incremental erase only clears the exact
103
+ // delta and a hardware wrap can desync it by a cell. Force a full clear on a
104
+ // meaningful height shrink near/at the viewport edge, cross-platform.
105
+ const isShrinkingAtViewport = hadPreviousFrame &&
106
+ nextOutputHeight < previousOutputHeight &&
107
+ (wasFullscreen || isFullscreen || wasOverflowing || isOverflowing);
99
108
  const shouldClearOnUnmount = isUnmounting && wasFullscreen;
100
109
  if (isWindowsConsole && (wasFullscreen || isFullscreen)) {
101
110
  return true;
@@ -106,6 +115,8 @@ const shouldClearTerminalForFrame = ({ isTty, viewportRows, previousOutputHeight
106
115
  (isOverflowing && hadPreviousFrame) ||
107
116
  // Clear when shrinking from fullscreen to non-fullscreen output.
108
117
  isLeavingFullscreen ||
118
+ // Clear on any height shrink at/over the viewport (cross-platform).
119
+ isShrinkingAtViewport ||
109
120
  // Preserve legacy unmount behavior for fullscreen frames: final teardown
110
121
  // render should clear once to avoid leaving a scrolled viewport state.
111
122
  shouldClearOnUnmount);
@@ -155,6 +166,7 @@ export default class Ink {
155
166
  lastOutputToRender;
156
167
  lastOutputHeight;
157
168
  lastTerminalWidth;
169
+ lastTerminalHeight;
158
170
  container;
159
171
  rootNode;
160
172
  // This variable is used only in debug mode to store full static output
@@ -165,6 +177,9 @@ export default class Ink {
165
177
  beforeExitHandler;
166
178
  restoreConsole;
167
179
  unsubscribeResize;
180
+ // [mixdog fork] Deferred repaint safety-net timer for resized(); cancelled
181
+ // by onRender when a real (App-driven) render lands first.
182
+ resizeRepaintTimer;
168
183
  throttledOnRender;
169
184
  hasPendingThrottledRender = false;
170
185
  kittyProtocolEnabled = false;
@@ -259,6 +274,7 @@ export default class Ink {
259
274
  this.lastOutputToRender = '';
260
275
  this.lastOutputHeight = 0;
261
276
  this.lastTerminalWidth = getWindowSize(this.options.stdout).columns;
277
+ this.lastTerminalHeight = getWindowSize(this.options.stdout).rows;
262
278
  // This variable is used only in debug mode to store full static output
263
279
  // so that it's rerendered every time, not just new static parts, like in non-debug mode
264
280
  this.fullStaticOutput = '';
@@ -293,16 +309,56 @@ export default class Ink {
293
309
  }
294
310
  resized = () => {
295
311
  const currentWidth = getWindowSize(this.options.stdout).columns;
296
- if (currentWidth < this.lastTerminalWidth) {
297
- // We clear the screen when decreasing terminal width to prevent duplicate overlapping re-renders.
312
+ const currentHeight = getWindowSize(this.options.stdout).rows;
313
+ if (currentWidth !== this.lastTerminalWidth || currentHeight !== this.lastTerminalHeight) {
314
+ // [mixdog fork] On viewport size change, full-clear the alt screen to
315
+ // drop the stale frame, but DO NOT immediately re-render with the old
316
+ // React tree at the new size. Ink's own onRender() here fires before
317
+ // App's debounced resizeState state update, so it paints a frame laid
318
+ // out for the PREVIOUS dimensions into the NEW viewport — that is the
319
+ // 2-frame jump/re-settle on resize. Clearing (no re-render) leaves a
320
+ // blank screen for the sub-frame until App's setResizeState commit
321
+ // drives a single, correctly-sized App-owned render. Layout listeners
322
+ // still fire so consumers observe the new size synchronously.
298
323
  this.log.clear();
324
+ this.log.reset?.();
325
+ // this.log.clear() only erases the PRIOR log frame's row span. On a
326
+ // fullscreen/alt-screen grow the newly exposed rows (below the old
327
+ // frame) are never touched by that erase, so stale scrollback can
328
+ // remain visible until the next full repaint — which, with the
329
+ // immediate onRender suppressed, may be shorter or delayed. Issue a
330
+ // true whole-screen clear so the viewport is uniformly blank until
331
+ // App's correctly-sized render lands.
332
+ this.writeBestEffort(this.options.stdout, ansiEscapes.clearTerminal);
299
333
  this.lastOutput = '';
300
334
  this.lastOutputToRender = '';
335
+ this.lastOutputHeight = 0;
336
+ this.calculateLayout();
337
+ dom.emitLayoutListeners(this.rootNode);
338
+ this.lastTerminalWidth = currentWidth;
339
+ this.lastTerminalHeight = currentHeight;
340
+ // Safety net: the early return delegates the repaint to App's stdout
341
+ // 'resize' listener (debounced setResizeState). If that render never
342
+ // arrives (listener not mounted yet, batched-away no-op, consumer
343
+ // without a resize handler), the screen would sit blank. Schedule a
344
+ // deferred onRender() that repaints with whatever tree is current;
345
+ // it is cancelled below whenever a real render happens first, so in
346
+ // the normal path it never fires and the immediate render stays
347
+ // suppressed.
348
+ if (this.resizeRepaintTimer) clearTimeout(this.resizeRepaintTimer);
349
+ this.resizeRepaintTimer = setTimeout(() => {
350
+ this.resizeRepaintTimer = undefined;
351
+ if (this.isUnmounted || this.isUnmounting) return;
352
+ this.onRender();
353
+ }, 200);
354
+ if (typeof this.resizeRepaintTimer.unref === 'function') this.resizeRepaintTimer.unref();
355
+ return;
301
356
  }
302
357
  this.calculateLayout();
303
358
  dom.emitLayoutListeners(this.rootNode);
304
359
  this.onRender();
305
360
  this.lastTerminalWidth = currentWidth;
361
+ this.lastTerminalHeight = currentHeight;
306
362
  };
307
363
  resolveExitPromise = () => { };
308
364
  rejectExitPromise = () => { };
@@ -508,6 +564,12 @@ export default class Ink {
508
564
  };
509
565
  onRender = () => {
510
566
  this.hasPendingThrottledRender = false;
567
+ // A real render is happening: cancel the resize safety-net timer so the
568
+ // deferred repaint never double-fires in the normal path.
569
+ if (this.resizeRepaintTimer) {
570
+ clearTimeout(this.resizeRepaintTimer);
571
+ this.resizeRepaintTimer = undefined;
572
+ }
511
573
  if (this.isUnmounted) {
512
574
  return;
513
575
  }
@@ -737,6 +799,12 @@ export default class Ink {
737
799
  if (typeof this.unsubscribeResize === 'function') {
738
800
  this.unsubscribeResize();
739
801
  }
802
+ // [mixdog fork] Drop the resize safety-net timer so a deferred
803
+ // repaint cannot fire against a torn-down tree.
804
+ if (this.resizeRepaintTimer) {
805
+ clearTimeout(this.resizeRepaintTimer);
806
+ this.resizeRepaintTimer = undefined;
807
+ }
740
808
  // Cancel any in-progress auto-detection before checking protocol state
741
809
  if (this.cancelKittyDetection) {
742
810
  this.cancelKittyDetection();
@@ -957,21 +1025,50 @@ export default class Ink {
957
1025
  // Detect fullscreen: output fills or exceeds terminal height.
958
1026
  // Only apply when writing to a real TTY — piped output always gets trailing newlines.
959
1027
  const viewportRows = isTty ? getWindowSize(this.options.stdout).rows : 24;
1028
+ const viewportColumns = isTty ? getWindowSize(this.options.stdout).columns : this.lastTerminalWidth;
1029
+ // [mixdog fork] Capture the previous viewport rows (as tracked at the
1030
+ // end of the last frame) before we update it below, so wasFullscreen/
1031
+ // wasOverflowing classify the PREVIOUS frame against the viewport it
1032
+ // was actually rendered into, not the current (possibly resized) one.
1033
+ const previousViewportRows = this.lastTerminalHeight ?? viewportRows;
960
1034
  const isFullscreen = isTty && outputHeight >= viewportRows;
961
1035
  const outputToRender = isFullscreen ? output : output + '\n';
962
1036
  const shouldClearTerminal = shouldClearTerminalForFrame({
963
1037
  isTty,
964
1038
  viewportRows,
1039
+ previousViewportRows,
965
1040
  previousOutputHeight: this.lastOutputHeight,
966
1041
  nextOutputHeight: outputHeight,
967
1042
  isUnmounting: this.isUnmounting,
968
1043
  });
1044
+ const viewportUnchanged = viewportRows === previousViewportRows && viewportColumns === this.lastTerminalWidth;
1045
+ if (isTty) {
1046
+ this.lastTerminalHeight = viewportRows;
1047
+ }
1048
+ // [mixdog fork] Skip redundant full clears: on Windows every fullscreen
1049
+ // frame takes the clearTerminal path, so a commit with IDENTICAL output
1050
+ // (e.g. post-mount tuiReady flip with no layout change) still blanked and
1051
+ // rewrote the whole screen — visible as boot flicker. Nothing changed →
1052
+ // nothing to write. [mixdog fork] Also require the viewport (rows AND
1053
+ // columns) to be unchanged since the last frame: a resize can arrive
1054
+ // with identical output text but must still force a full clear to
1055
+ // avoid ghosting stale cells left outside the new viewport.
1056
+ if (shouldClearTerminal &&
1057
+ !this.isUnmounting &&
1058
+ output === this.lastOutput &&
1059
+ staticOutput === '' &&
1060
+ !this.log.isCursorDirty() &&
1061
+ viewportUnchanged) {
1062
+ return;
1063
+ }
969
1064
  if (shouldClearTerminal) {
970
1065
  const sync = this.shouldSync();
971
1066
  if (sync) {
972
1067
  this.options.stdout.write(bsu);
973
1068
  }
974
- this.options.stdout.write(ansiEscapes.clearTerminal + this.fullStaticOutput + output);
1069
+ // [mixdog fork] Reset SGR before erasing so stale style/background
1070
+ // from the previous frame doesn't leak onto the cleared cells.
1071
+ this.options.stdout.write('\u001B[0m' + ansiEscapes.clearTerminal + this.fullStaticOutput + output);
975
1072
  this.lastOutput = output;
976
1073
  this.lastOutputToRender = outputToRender;
977
1074
  this.lastOutputHeight = outputHeight;
@@ -62,7 +62,9 @@ const createStandard = (stream, { showCursor = false } = {}) => {
62
62
  };
63
63
  render.clear = () => {
64
64
  const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
65
- stream.write(prefix + ansiEscapes.eraseLines(previousLineCount));
65
+ // [mixdog fork] Reset SGR before erasing so stale style/background from
66
+ // the previous frame doesn't leak onto the cleared cells.
67
+ stream.write(prefix + '\u001B[0m' + ansiEscapes.eraseLines(previousLineCount));
66
68
  previousOutput = '';
67
69
  previousLineCount = 0;
68
70
  previousCursorPosition = undefined;
@@ -171,7 +173,17 @@ const createIncremental = (stream, { showCursor = false } = {}) => {
171
173
  if (visibleCount < previousVisible) {
172
174
  const previousHadTrailingNewline = previousOutput.endsWith('\n');
173
175
  const extraSlot = previousHadTrailingNewline ? 1 : 0;
174
- buffer.push(ansiEscapes.eraseLines(previousVisible - visibleCount + extraSlot), ansiEscapes.cursorUp(visibleCount));
176
+ // [mixdog fork] Defensive erase on height shrink: clear ONE extra
177
+ // physical line beyond the counted delta. A hardware line-wrap (wide
178
+ // glyph at the viewport edge) can occupy a physical row the logical
179
+ // line count doesn't account for, leaving a ghost row below the new
180
+ // content. Not Windows-specific — the wrap desync happens on any
181
+ // terminal. We erase the extra line with eraseLine at the current
182
+ // (bottom) row FIRST — this does NOT move the cursor — then run the
183
+ // normal eraseLines walk. This keeps the post-erase cursor origin
184
+ // identical to upstream (cursorUp(visibleCount) still lands right),
185
+ // so only a stray trailing row is removed, layout is untouched.
186
+ buffer.push(ansiEscapes.eraseLine, ansiEscapes.eraseLines(previousVisible - visibleCount + extraSlot), ansiEscapes.cursorUp(visibleCount));
175
187
  }
176
188
  else {
177
189
  buffer.push(ansiEscapes.cursorUp(previousLines.length - 1));
@@ -205,7 +217,9 @@ const createIncremental = (stream, { showCursor = false } = {}) => {
205
217
  };
206
218
  render.clear = () => {
207
219
  const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
208
- stream.write(prefix + ansiEscapes.eraseLines(previousLines.length));
220
+ // [mixdog fork] Reset SGR before erasing so stale style/background from
221
+ // the previous frame doesn't leak onto the cleared cells.
222
+ stream.write(prefix + '\u001B[0m' + ansiEscapes.eraseLines(previousLines.length));
209
223
  previousOutput = '';
210
224
  previousLines = [];
211
225
  previousCursorPosition = undefined;
@@ -1,6 +1,127 @@
1
1
  import wrapAnsi from 'wrap-ansi';
2
2
  import cliTruncate from 'cli-truncate';
3
+ import stripAnsi from 'strip-ansi';
4
+ import { AMBIGUOUS_WIDE, displayStringWidth } from './display-width.js';
3
5
  const cache = {};
6
+ // [mixdog fork] wrap-ansi / cli-truncate measure with plain string-width, which
7
+ // counts our problem glyphs (circled digits U+2460–U+24FF, arrows U+2190–U+21FF)
8
+ // as 1 cell while the terminal draws 2. When the policy is ON, their column math
9
+ // under-counts and a wrapped/truncated line can still overflow by one cell,
10
+ // pushing content past the box edge (the ghosting/overlap). We can't cleanly
11
+ // fork wrap-ansi, so we compensate at THIS boundary: re-check each produced line
12
+ // with displayStringWidth. For wrap/hard modes the overflow is REFLOWED onto a
13
+ // new line (no visible content is dropped); only truncate-end may cut.
14
+ const PROBLEM_RE = /[\u2190-\u21ff\u2460-\u24ff]/;
15
+ const ANSI_RE = /^\u001B(?:\[[0-9;]*m|\].*?(?:\u0007|\u001B\\))/;
16
+ const segmenter = new Intl.Segmenter();
17
+ // SGR reset — appended after a mid-line cut so an open style doesn't leak past
18
+ // the split point onto the reflowed remainder / following cells.
19
+ const SGR_RESET = '\u001B[0m';
20
+ // OSC 8 hyperlink open (with a URL) and universal close. Cutting inside a link
21
+ // must close it on head and re-open it on tail, exactly like an open SGR style.
22
+ const OSC8_CLOSE = '\u001B]8;;\u0007';
23
+ const OSC8_RE = /^\u001B\]8;;([^\u0007\u001B]*)(?:\u0007|\u001B\\)/;
24
+ // Split one ANSI line at the first grapheme boundary where VISIBLE displayWidth
25
+ // would exceed `max`. Walks grapheme CLUSTERS (not code points) so a clustered
26
+ // glyph (e.g. `↔️` = U+2194 U+FE0F) is measured/kept whole. Returns
27
+ // { head, tail }: head fits within max (with an SGR reset appended when any
28
+ // style is still open at the cut so styling doesn't bleed); tail is the
29
+ // remaining visible content, prefixed with the carried-over open SGR codes so
30
+ // it renders with the same style it was cut from. ANSI escapes cost 0 width.
31
+ const splitAtDisplayWidth = (line, max) => {
32
+ if (displayStringWidth(line) <= max)
33
+ return { head: line, tail: '' };
34
+ let head = '';
35
+ let width = 0;
36
+ let rest = line;
37
+ // Track SGR open codes seen so far so the tail can re-open them. An empty
38
+ // params reset ('' or '0') clears the carry; anything else accumulates.
39
+ let openSgr = '';
40
+ let sawOpenStyle = false;
41
+ // Track an open OSC 8 hyperlink (empty URL '...8;;\u0007' closes it).
42
+ let openLink = '';
43
+ while (rest.length > 0) {
44
+ const m = ANSI_RE.exec(rest);
45
+ if (m) {
46
+ head += m[0];
47
+ const sgr = /^\u001B\[([0-9;]*)m$/.exec(m[0]);
48
+ if (sgr) {
49
+ const params = sgr[1];
50
+ if (params === '' || params === '0') {
51
+ openSgr = '';
52
+ sawOpenStyle = false;
53
+ }
54
+ else {
55
+ openSgr += m[0];
56
+ sawOpenStyle = true;
57
+ }
58
+ }
59
+ else {
60
+ const osc = OSC8_RE.exec(m[0]);
61
+ if (osc)
62
+ openLink = osc[1] ? m[0] : '';
63
+ }
64
+ rest = rest.slice(m[0].length);
65
+ continue;
66
+ }
67
+ const { segment } = segmenter.segment(rest)[Symbol.iterator]().next().value;
68
+ const w = displayStringWidth(segment);
69
+ // Progress guarantee: if nothing visible has landed on head yet, force
70
+ // the first grapheme through even when it alone exceeds max (a 1-cluster
71
+ // overflow is unavoidable and preferable to a zero-progress loop).
72
+ if (width + w > max && width > 0)
73
+ break;
74
+ head += segment;
75
+ width += w;
76
+ rest = rest.slice(segment.length);
77
+ }
78
+ // Close any style / link still open at the cut, and re-open on the tail.
79
+ if (openLink)
80
+ head += OSC8_CLOSE;
81
+ if (sawOpenStyle)
82
+ head += SGR_RESET;
83
+ const carry = openSgr + openLink;
84
+ const tail = rest.length > 0 ? carry + rest : '';
85
+ return { head, tail };
86
+ };
87
+ // wrap/hard: no visible content may be dropped. Repeatedly split each produced
88
+ // line at the display-width budget and REFLOW the overflow onto following lines.
89
+ const reflowToDisplayWidth = (line, max) => {
90
+ const out = [];
91
+ let cur = line;
92
+ while (displayStringWidth(cur) > max) {
93
+ const { head, tail } = splitAtDisplayWidth(cur, max);
94
+ // splitAtDisplayWidth now always advances (forces >=1 grapheme onto
95
+ // head), so head carries visible content and tail shrinks each pass.
96
+ // Belt-and-braces: if tail failed to shrink, flush the remainder. Push
97
+ // `head` (not raw `cur`) — head carries the SGR/OSC8 close sequences, so
98
+ // even a single overwide styled/linked glyph doesn't leak its style.
99
+ if (tail === '' || tail === cur) {
100
+ out.push(head || cur);
101
+ return out;
102
+ }
103
+ out.push(head);
104
+ cur = tail;
105
+ }
106
+ out.push(cur);
107
+ return out;
108
+ };
109
+ // truncate-end: cutting is the intended behavior — drop the overflow but still
110
+ // cut on grapheme boundaries and close open styles.
111
+ const hardCutToDisplayWidth = (line, max) => splitAtDisplayWidth(line, max).head;
112
+ // Re-enforce the display-width budget on every produced line when the policy is
113
+ // ON and the source contains a problem glyph. No-op otherwise (byte-identical
114
+ // to upstream wrap-ansi output), so terminals without the wide policy are
115
+ // unaffected.
116
+ const enforceDisplayWidth = (wrapped, maxWidth, mode) => {
117
+ if (!AMBIGUOUS_WIDE || !PROBLEM_RE.test(stripAnsi(wrapped)))
118
+ return wrapped;
119
+ const lines = wrapped.split('\n');
120
+ if (mode === 'truncate')
121
+ return lines.map((line) => hardCutToDisplayWidth(line, maxWidth)).join('\n');
122
+ // wrap/hard: reflow overflow, never drop.
123
+ return lines.flatMap((line) => reflowToDisplayWidth(line, maxWidth)).join('\n');
124
+ };
4
125
  const wrapText = (text, maxWidth, wrapType) => {
5
126
  const cacheKey = text + String(maxWidth) + String(wrapType);
6
127
  const cachedText = cache[cacheKey];
@@ -13,6 +134,7 @@ const wrapText = (text, maxWidth, wrapType) => {
13
134
  trim: false,
14
135
  hard: true,
15
136
  });
137
+ wrappedText = enforceDisplayWidth(wrappedText, maxWidth, 'wrap');
16
138
  }
17
139
  if (wrapType === 'hard') {
18
140
  wrappedText = wrapAnsi(text, maxWidth, {
@@ -20,6 +142,7 @@ const wrapText = (text, maxWidth, wrapType) => {
20
142
  hard: true,
21
143
  wordWrap: false,
22
144
  });
145
+ wrappedText = enforceDisplayWidth(wrappedText, maxWidth, 'wrap');
23
146
  }
24
147
  if (wrapType.startsWith('truncate')) {
25
148
  let position = 'end';
@@ -30,6 +153,8 @@ const wrapText = (text, maxWidth, wrapType) => {
30
153
  position = 'start';
31
154
  }
32
155
  wrappedText = cliTruncate(text, maxWidth, { position });
156
+ if (position === 'end')
157
+ wrappedText = enforceDisplayWidth(wrappedText, maxWidth, 'truncate');
33
158
  }
34
159
  cache[cacheKey] = wrappedText;
35
160
  return wrappedText;
@@ -1,30 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { readFileSync } from 'node:fs';
3
-
4
- const src = readFileSync('src/standalone/folder-dialog.mjs', 'utf8');
5
- const fn = src.match(/function powershellScript[\s\S]*?return \[([\s\S]*?)\]\.join/);
6
- if (!fn) throw new Error('parse failed');
7
- // Re-eval powershellScript by importing - use dynamic compile
8
- const modUrl = new URL('../src/standalone/folder-dialog.mjs', import.meta.url);
9
- // Call pickFolder with a hook - instead duplicate minimal import
10
- const { pickFolder } = await import(modUrl.href);
11
-
12
- const child = spawn('powershell.exe', [
13
- '-NoLogo', '-NoProfile', '-STA', '-Command',
14
- [
15
- '$ErrorActionPreference = "Continue"',
16
- 'try {',
17
- ...readFileSync('src/standalone/folder-dialog.mjs', 'utf8')
18
- .split('\n')
19
- .filter((l) => l.includes('Add-Type -AssemblyName') || l.includes('Add-Type -TypeDefinition') || l.includes('GetDialogOwnerCenter')),
20
- '} catch { Write-Error $_; exit 1 }',
21
- ].join('; '),
22
- ], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
23
-
24
- let err = '';
25
- child.stderr.on('data', (d) => { err += d.toString(); });
26
- child.on('close', (code) => {
27
- console.log('code', code);
28
- console.log(err.slice(0, 2000));
29
- });
30
-
@@ -1,35 +0,0 @@
1
- import fs from "node:fs";
2
- const p = "src/runtime/shared/tool-surface.mjs";
3
- let s = fs.readFileSync(p, "utf8");
4
- const start = s.indexOf("export function summarizeAgentSurfaceBrief");
5
- const end = s.indexOf("function pluralize", start);
6
- if (start < 0 || end < 0) throw new Error("markers");
7
- const clean = `export function summarizeAgentSurfaceBrief(name, args, resultText, { isError = false, isResponse = false } = {}) {
8
- const a = parseToolArgs(args);
9
- const action = String(a?.type || a?.action || "").toLowerCase();
10
- const text = String(resultText ?? "").trim();
11
-
12
- if (isResponse && text) {
13
- const fromResult = summarizeToolResult(name, args, text, isError);
14
- if (fromResult) return truncateAgentSurfaceBrief(stripInlineMarkdown(fromResult));
15
- const line = firstAgentResultLine(text);
16
- if (line) return truncateAgentSurfaceBrief(stripInlineMarkdown(line));
17
- }
18
-
19
- const outbound = firstText(a?.prompt, a?.message);
20
- if (outbound && (action === "spawn" || action === "send" || !action)) {
21
- return truncateAgentSurfaceBrief(outbound);
22
- }
23
-
24
- if ((action === "spawn" || action === "send") && text && !isResponse) {
25
- const fromResult = summarizeToolResult(name, args, text, isError);
26
- if (fromResult) return truncateAgentSurfaceBrief(stripInlineMarkdown(fromResult));
27
- }
28
-
29
- return "";
30
- }
31
-
32
- `;
33
- s = s.slice(0, start) + clean + s.slice(end);
34
- fs.writeFileSync(p, s);
35
- console.log("summarizeAgentSurfaceBrief cleaned");
@@ -1,24 +0,0 @@
1
- import fs from "node:fs";
2
- const p = "src/runtime/shared/tool-surface.mjs";
3
- let s = fs.readFileSync(p, "utf8");
4
- const block = `export function formatToolSurface(name, args, opts = {}) {
5
- const parsed = parseToolArgs(args);
6
- return {
7
- label: displayToolName(name, parsed),
8
- summary: summarizeToolArgs(name, parsed, opts),
9
- normalizedName: normalizeToolName(name),
10
- args: parsed,
11
- };
12
- }
13
-
14
- `;
15
- while (s.includes(block)) {
16
- s = s.replace(block, "");
17
- }
18
- const anchor = `/** Collapsed agent/worker surface card: one-line brief under the header (30-40 chars). */`;
19
- if (!s.includes(anchor)) throw new Error("anchor missing");
20
- if (!s.includes("export function formatToolSurface")) {
21
- s = s.replace(anchor, block + anchor);
22
- }
23
- fs.writeFileSync(p, s);
24
- console.log("fixed formatToolSurface");
@@ -1,42 +0,0 @@
1
- import fs from "node:fs";
2
- const p = "src/tui/components/ToolExecution.jsx";
3
- let s = fs.readFileSync(p, "utf8");
4
- const bad = ` let visibleDetailLines = detailLines;
5
- if (isSkillSurface && !showRawResult) {
6
- visibleDetailLines = [];
7
- } else if (isAgentSurfaceCard && !showRawResult) {
8
- if (agentSurfaceBrief) {
9
- visibleDetailLines = [agentSurfaceBrief];
10
- } else if (pending) {
11
- visibleDetailLines = [pendingDetailPlaceholder || 'Running'];
12
- } else {
13
- visibleDetailLines = [];
14
- }
15
- }
16
- } else if (isAgentSurfaceCard && !showRawResult) {
17
- } else if (isAgentSurfaceCard && !showRawResult) {
18
- const agentDetailFallback = collapsedDetail
19
- || (pending ? (pendingDetailPlaceholder || 'Running') : 'Finished');
20
- const agentDetailLine = agentSurfaceBrief
21
- || truncateToWidth(String(agentDetailFallback), Math.min(AGENT_SURFACE_BRIEF_MAX, maxResultChars));
22
- visibleDetailLines = [agentDetailLine];
23
- }`;
24
- const good = ` let visibleDetailLines = detailLines;
25
- if (isSkillSurface && !showRawResult) {
26
- visibleDetailLines = [];
27
- } else if (isAgentSurfaceCard && !showRawResult) {
28
- const agentDetailFallback = collapsedDetail
29
- || (pending ? (pendingDetailPlaceholder || 'Running') : 'Finished');
30
- const agentDetailLine = agentSurfaceBrief
31
- || truncateToWidth(String(agentDetailFallback), Math.min(AGENT_SURFACE_BRIEF_MAX, maxResultChars));
32
- visibleDetailLines = [agentDetailLine];
33
- }`;
34
- if (s.includes(bad)) {
35
- s = s.replace(bad, good);
36
- fs.writeFileSync(p, s);
37
- console.log("ToolExecution fixed");
38
- } else if (s.includes("agentDetailFallback")) {
39
- console.log("ToolExecution already ok");
40
- } else {
41
- throw new Error("ToolExecution pattern not found");
42
- }