mixdog 0.9.1 → 0.9.3

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 (301) hide show
  1. package/package.json +9 -1
  2. package/scripts/_bench-cwc.json +20 -0
  3. package/scripts/agent-loop-policy-test.mjs +37 -0
  4. package/scripts/agent-parallel-smoke.mjs +54 -10
  5. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  6. package/scripts/background-task-meta-smoke.mjs +1 -1
  7. package/scripts/bench-run.mjs +262 -0
  8. package/scripts/build-tui.mjs +13 -1
  9. package/scripts/compact-smoke.mjs +12 -0
  10. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  11. package/scripts/explore-bench.mjs +124 -0
  12. package/scripts/hook-bus-test.mjs +191 -0
  13. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  14. package/scripts/internal-comms-bench.mjs +727 -0
  15. package/scripts/internal-comms-smoke.mjs +75 -0
  16. package/scripts/lead-workflow-smoke.mjs +4 -4
  17. package/scripts/live-worker-smoke.mjs +9 -9
  18. package/scripts/output-style-bench.mjs +285 -0
  19. package/scripts/output-style-smoke.mjs +13 -10
  20. package/scripts/patch-replay.mjs +90 -0
  21. package/scripts/path-suffix-test.mjs +57 -0
  22. package/scripts/provider-stream-stall-test.mjs +276 -0
  23. package/scripts/provider-toolcall-test.mjs +599 -1
  24. package/scripts/recall-bench.mjs +207 -0
  25. package/scripts/routing-corpus.mjs +281 -0
  26. package/scripts/session-bench.mjs +1526 -0
  27. package/scripts/session-diag.mjs +595 -0
  28. package/scripts/task-bench.mjs +207 -0
  29. package/scripts/tool-failures.mjs +6 -6
  30. package/scripts/tool-smoke.mjs +310 -67
  31. package/scripts/toolcall-args-test.mjs +81 -0
  32. package/src/agents/debugger/AGENT.md +4 -4
  33. package/src/agents/heavy-worker/AGENT.md +20 -9
  34. package/src/agents/reviewer/AGENT.md +4 -4
  35. package/src/agents/worker/AGENT.md +17 -9
  36. package/src/app.mjs +10 -6
  37. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  38. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  39. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  40. package/src/headless-role.mjs +14 -14
  41. package/src/help.mjs +1 -0
  42. package/src/lib/rules-builder.cjs +32 -54
  43. package/src/mixdog-session-runtime.mjs +1040 -2036
  44. package/src/output-styles/default.md +12 -7
  45. package/src/output-styles/minimal.md +25 -0
  46. package/src/output-styles/oneline.md +21 -0
  47. package/src/output-styles/simple.md +10 -9
  48. package/src/repl.mjs +17 -7
  49. package/src/rules/agent/00-common.md +7 -5
  50. package/src/rules/agent/30-explorer.md +8 -12
  51. package/src/rules/lead/01-general.md +3 -1
  52. package/src/rules/lead/lead-tool.md +13 -2
  53. package/src/rules/shared/01-tool.md +23 -12
  54. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  55. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  59. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  60. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/context/collect.mjs +182 -67
  62. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  63. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +100 -18
  65. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +313 -84
  70. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +104 -38
  71. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +28 -33
  72. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  73. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  74. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  75. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  76. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +227 -31
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +83 -6
  79. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +229 -115
  80. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +213 -33
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  82. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +49 -17
  83. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +22 -17
  85. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  86. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  87. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  88. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
  89. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  90. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  91. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
  93. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  94. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  95. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  97. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  98. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  99. package/src/runtime/agent/orchestrator/session/loop.mjs +513 -1000
  100. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
  101. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  102. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
  103. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  104. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  105. package/src/runtime/agent/orchestrator/session/manager.mjs +232 -1152
  106. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  107. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  108. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  109. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  110. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
  111. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  113. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  118. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  119. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  121. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  122. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +14 -5
  123. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  124. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  125. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +23 -2
  126. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
  127. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  128. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  129. package/src/runtime/agent/orchestrator/tools/builtin.mjs +70 -1
  130. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  131. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  132. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  133. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
  134. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  135. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  136. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  137. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  138. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  139. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
  140. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  141. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  142. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  143. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  144. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  145. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +39 -4189
  146. package/src/runtime/agent/orchestrator/tools/patch.mjs +119 -5
  147. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
  148. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
  149. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  150. package/src/runtime/channels/backends/discord.mjs +99 -9
  151. package/src/runtime/channels/backends/telegram.mjs +501 -0
  152. package/src/runtime/channels/index.mjs +437 -1439
  153. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  154. package/src/runtime/channels/lib/config.mjs +54 -2
  155. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  156. package/src/runtime/channels/lib/format.mjs +4 -2
  157. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  158. package/src/runtime/channels/lib/output-forwarder.mjs +88 -67
  159. package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
  160. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  161. package/src/runtime/channels/lib/telegram-format.mjs +280 -0
  162. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  163. package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
  164. package/src/runtime/channels/lib/webhook.mjs +59 -31
  165. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  166. package/src/runtime/channels/tool-defs.mjs +1 -1
  167. package/src/runtime/memory/index.mjs +465 -345
  168. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  169. package/src/runtime/memory/lib/core-memory-store.mjs +352 -2
  170. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  171. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  172. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  173. package/src/runtime/memory/lib/memory-cycle2.mjs +65 -9
  174. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  175. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  176. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  177. package/src/runtime/memory/lib/memory.mjs +121 -4
  178. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  179. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  180. package/src/runtime/memory/lib/recall-format.mjs +183 -0
  181. package/src/runtime/memory/lib/session-ingest.mjs +107 -0
  182. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  183. package/src/runtime/memory/tool-defs.mjs +10 -7
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/background-tasks.mjs +2 -3
  186. package/src/runtime/shared/buffered-appender.mjs +149 -0
  187. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  188. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  189. package/src/runtime/shared/config.mjs +9 -0
  190. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  191. package/src/runtime/shared/schedules-store.mjs +21 -19
  192. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  193. package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
  194. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  195. package/src/runtime/shared/tool-surface.mjs +98 -13
  196. package/src/runtime/shared/transcript-writer.mjs +156 -0
  197. package/src/runtime/shared/update-checker.mjs +214 -0
  198. package/src/session-runtime/config-helpers.mjs +209 -0
  199. package/src/session-runtime/effort.mjs +128 -0
  200. package/src/session-runtime/fs-utils.mjs +10 -0
  201. package/src/session-runtime/model-capabilities.mjs +130 -0
  202. package/src/session-runtime/output-styles.mjs +124 -0
  203. package/src/session-runtime/plugin-mcp.mjs +114 -0
  204. package/src/session-runtime/session-text.mjs +100 -0
  205. package/src/session-runtime/statusline-route.mjs +35 -0
  206. package/src/session-runtime/tool-catalog.mjs +720 -0
  207. package/src/session-runtime/workflow.mjs +358 -0
  208. package/src/standalone/agent-tool.mjs +302 -117
  209. package/src/standalone/channel-admin.mjs +133 -40
  210. package/src/standalone/channel-worker.mjs +10 -292
  211. package/src/standalone/explore-tool.mjs +12 -5
  212. package/src/standalone/hook-bus.mjs +165 -8
  213. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  214. package/src/standalone/opencode-go-login.mjs +121 -0
  215. package/src/standalone/provider-admin.mjs +25 -3
  216. package/src/standalone/usage-dashboard.mjs +1 -1
  217. package/src/tui/App.jsx +2883 -778
  218. package/src/tui/components/ConfirmBar.jsx +47 -0
  219. package/src/tui/components/ContextPanel.jsx +5 -3
  220. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  221. package/src/tui/components/Markdown.jsx +22 -98
  222. package/src/tui/components/Message.jsx +14 -35
  223. package/src/tui/components/Picker.jsx +87 -12
  224. package/src/tui/components/PromptInput.jsx +355 -22
  225. package/src/tui/components/QueuedCommands.jsx +1 -1
  226. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  227. package/src/tui/components/Spinner.jsx +7 -7
  228. package/src/tui/components/StatusLine.jsx +40 -21
  229. package/src/tui/components/TextEntryPanel.jsx +51 -7
  230. package/src/tui/components/ToolExecution.jsx +183 -101
  231. package/src/tui/components/TurnDone.jsx +4 -4
  232. package/src/tui/components/UsagePanel.jsx +1 -1
  233. package/src/tui/components/tool-output-format.mjs +161 -23
  234. package/src/tui/components/tool-output-format.test.mjs +87 -0
  235. package/src/tui/display-width.mjs +69 -0
  236. package/src/tui/display-width.test.mjs +35 -0
  237. package/src/tui/dist/index.mjs +6731 -2333
  238. package/src/tui/engine/agent-envelope.mjs +296 -0
  239. package/src/tui/engine/boot-profile.mjs +21 -0
  240. package/src/tui/engine/labels.mjs +67 -0
  241. package/src/tui/engine/notice-text.mjs +112 -0
  242. package/src/tui/engine/queue-helpers.mjs +161 -0
  243. package/src/tui/engine/session-stats.mjs +46 -0
  244. package/src/tui/engine/tool-call-fields.mjs +23 -0
  245. package/src/tui/engine/tool-result-text.mjs +126 -0
  246. package/src/tui/engine.mjs +569 -948
  247. package/src/tui/index.jsx +117 -7
  248. package/src/tui/input-editing.mjs +58 -8
  249. package/src/tui/input-editing.selection.test.mjs +75 -0
  250. package/src/tui/keyboard-protocol.mjs +42 -0
  251. package/src/tui/lib/voice-recorder.mjs +469 -0
  252. package/src/tui/markdown/format-token.mjs +128 -76
  253. package/src/tui/markdown/format-token.test.mjs +61 -19
  254. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  255. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  256. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  257. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  258. package/src/tui/markdown/table-layout.mjs +9 -9
  259. package/src/tui/paste-attachments.mjs +36 -9
  260. package/src/tui/paste-fix.test.mjs +119 -0
  261. package/src/tui/prompt-history-store.mjs +129 -0
  262. package/src/tui/prompt-history-store.test.mjs +52 -0
  263. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  264. package/src/tui/theme.mjs +41 -657
  265. package/src/tui/themes/base.mjs +86 -0
  266. package/src/tui/themes/basic.mjs +85 -0
  267. package/src/tui/themes/catppuccin.mjs +72 -0
  268. package/src/tui/themes/dracula.mjs +70 -0
  269. package/src/tui/themes/everforest.mjs +71 -0
  270. package/src/tui/themes/gruvbox.mjs +71 -0
  271. package/src/tui/themes/index.mjs +71 -0
  272. package/src/tui/themes/indigo.mjs +78 -0
  273. package/src/tui/themes/kanagawa.mjs +80 -0
  274. package/src/tui/themes/light.mjs +81 -0
  275. package/src/tui/themes/nord.mjs +72 -0
  276. package/src/tui/themes/onedark.mjs +16 -0
  277. package/src/tui/themes/rosepine.mjs +70 -0
  278. package/src/tui/themes/teal.mjs +80 -0
  279. package/src/tui/themes/tokyonight.mjs +79 -0
  280. package/src/tui/themes/utils.mjs +106 -0
  281. package/src/tui/themes/warm.mjs +79 -0
  282. package/src/tui/transcript-tool-failures.mjs +13 -2
  283. package/src/ui/markdown.mjs +1 -1
  284. package/src/ui/model-display.mjs +2 -2
  285. package/src/ui/statusline.mjs +75 -27
  286. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  287. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  288. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  289. package/src/workflows/default/WORKFLOW.md +46 -12
  290. package/src/workflows/sequential/WORKFLOW.md +51 -0
  291. package/src/workflows/solo/WORKFLOW.md +12 -1
  292. package/vendor/ink/build/display-width.js +62 -0
  293. package/vendor/ink/build/ink.js +100 -12
  294. package/vendor/ink/build/measure-text.js +4 -1
  295. package/vendor/ink/build/output.js +115 -9
  296. package/vendor/ink/build/render-node-to-output.js +4 -1
  297. package/vendor/ink/build/render.js +4 -0
  298. package/src/output-styles/extreme-simple.md +0 -20
  299. package/src/rules/lead/04-workflow.md +0 -51
  300. package/src/workflows/default/workflow.json +0 -13
  301. package/src/workflows/solo/workflow.json +0 -7
@@ -85,7 +85,7 @@ const stripKittyQueryResponsesAndTrailingPartial = (buffer) => {
85
85
  // desynchronizes the incremental erase used for frames that exactly fill the
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
- const isWindowsConsole = process.platform === 'win32';
88
+ const isWindowsConsole = process.platform === 'win32' || Boolean(process.env.WT_SESSION);
89
89
  const shouldClearTerminalForFrame = ({ isTty, viewportRows, previousOutputHeight, nextOutputHeight, isUnmounting, }) => {
90
90
  if (!isTty) {
91
91
  return false;
@@ -336,7 +336,9 @@ export default class Ink {
336
336
  a.y2 === rect.y2 &&
337
337
  a.clipY1 === rect.clipY1 &&
338
338
  a.clipY2 === rect.clipY2 &&
339
- a.captureText === rect.captureText);
339
+ a.captureText === rect.captureText &&
340
+ a.selectionForeground === rect.selectionForeground &&
341
+ a.selectionBackground === rect.selectionBackground);
340
342
  if (same) {
341
343
  if (!options.immediate) {
342
344
  return;
@@ -380,9 +382,22 @@ export default class Ink {
380
382
  });
381
383
  };
382
384
  // [mixdog fork] Given a 0-based cell (x, y), return the inclusive rect of the
383
- // word (maximal run of non-whitespace cells) on that single row, or null if
384
- // the cell is whitespace/empty or out of range. Reuses the cached cell-value
385
- // rows from the last render so it works without retaining the Output instance.
385
+ // word at that cell on that single row, or null if the cell is whitespace/empty
386
+ // or out of range. Reuses the cached cell-value rows from the last render so it
387
+ // works without retaining the Output instance.
388
+ //
389
+ // Ported from claude-code's selection.ts charClass/wordBoundsAt (3-class word
390
+ // model) onto mixdog's rect(linear) infra. Instead of a naive "non-space run",
391
+ // expansion stops at a CHARACTER-CLASS change:
392
+ // class 1 = WORD_CHAR — letters (any script), digits, and the punctuation
393
+ // iTerm2 treats as word-part by default (/-+~_.\), so a path like
394
+ // `/usr/bin/bash` or `~/.claude/config.json` selects whole.
395
+ // class 2 = other punctuation — so `->` selects just `->`, not the words
396
+ // on either side.
397
+ // class 0 = space/empty. claude-code treats a space run as selectable
398
+ // (class 0), but mixdog intentionally returns null on empty/space
399
+ // so a double-click on blank does nothing (safer for our transcript
400
+ // where most alt-screen cells are padding).
386
401
  getWordRectAt = (x, y) => {
387
402
  const rows = this.lastPlainRows;
388
403
  if (!rows)
@@ -390,16 +405,89 @@ export default class Ink {
390
405
  const cells = rows[y];
391
406
  if (!Array.isArray(cells))
392
407
  return null;
393
- const isWordChar = (v) => !!v && !/^\s$/u.test(v);
394
- if (!isWordChar(cells[x]))
408
+ // Unicode-aware word-char set (matches selection.ts WORD_CHAR).
409
+ const WORD_CHAR = /[\p{L}\p{N}_/.\-+~\\]/u;
410
+ const charClass = (v) => {
411
+ if (!v || v === ' ')
412
+ return 0;
413
+ return WORD_CHAR.test(v) ? 1 : 2;
414
+ };
415
+ // [mixdog fork] Wide/CJK glyphs occupy 2+ grid cells: the HEAD cell
416
+ // holds the glyph and each TRAILING cell is stored as '' (spacer tail)
417
+ // carrying the glyph's styles — see output.js ~L237-243 for how wide
418
+ // chars are laid into the grid. A '' cell is a wide-char TAIL only when
419
+ // it directly follows a non-empty non-space glyph (class !== 0); a ''
420
+ // after '' or after a space is genuine blank padding. This mirrors
421
+ // selection.ts wordBoundsAt's SpacerTail step-back (L172-178) and
422
+ // expansion step-over (L206-221) on mixdog's string-cell grid.
423
+ const isWideTail = (i) => i > 0 && cells[i] === '' && charClass(cells[i - 1]) !== 0;
424
+ // On entry: if the click landed on a spacer tail, step back to the head
425
+ // so charClass sees the actual glyph. Genuine blank padding is left
426
+ // alone, preserving the null-on-blank behavior below.
427
+ let sx = x;
428
+ if (isWideTail(sx))
429
+ sx = sx - 1;
430
+ const cls = charClass(cells[sx]);
431
+ // Preserve mixdog's null-on-space/empty behavior (class 0).
432
+ if (cls === 0)
395
433
  return null;
396
- let x1 = x, x2 = x;
397
- while (x1 - 1 >= 0 && isWordChar(cells[x1 - 1]))
398
- x1--;
399
- while (x2 + 1 < cells.length && isWordChar(cells[x2 + 1]))
400
- x2++;
434
+ let x1 = sx, x2 = sx;
435
+ // Expand left: step OVER a spacer tail to the wide-char head and include
436
+ // both columns when the head matches the class; otherwise stop at a
437
+ // class change.
438
+ while (x1 - 1 >= 0) {
439
+ const p = x1 - 1;
440
+ if (isWideTail(p)) {
441
+ if (p - 1 >= 0 && charClass(cells[p - 1]) === cls) {
442
+ x1 = p - 1;
443
+ continue;
444
+ }
445
+ break;
446
+ }
447
+ if (charClass(cells[p]) === cls) {
448
+ x1 = p;
449
+ continue;
450
+ }
451
+ break;
452
+ }
453
+ // Expand right: INCLUDE a spacer tail that follows an in-run glyph so x2
454
+ // covers the wide glyph's full width; otherwise stop at a class change.
455
+ while (x2 + 1 < cells.length) {
456
+ const n = x2 + 1;
457
+ if (isWideTail(n)) {
458
+ x2 = n;
459
+ continue;
460
+ }
461
+ if (charClass(cells[n]) === cls) {
462
+ x2 = n;
463
+ continue;
464
+ }
465
+ break;
466
+ }
401
467
  return { x1, y1: y, x2, y2: y };
402
468
  };
469
+ // [mixdog fork] Given a 0-based row y, return the inclusive rect of the whole
470
+ // logical line at that row (mirrors claude-code's selectLineAt intent on
471
+ // mixdog's rect infra). x1 is always 0; x2 is the last non-space content cell
472
+ // so trailing padding isn't selected. Returns null for an empty/blank row.
473
+ // Exposed symmetrically to getWordRectAt (index.jsx wires it into the store).
474
+ getLineRectAt = (y) => {
475
+ const rows = this.lastPlainRows;
476
+ if (!rows)
477
+ return null;
478
+ const cells = rows[y];
479
+ if (!Array.isArray(cells))
480
+ return null;
481
+ let x2 = -1;
482
+ for (let x = 0; x < cells.length; x++) {
483
+ const v = cells[x];
484
+ if (v && !/^\s$/u.test(v))
485
+ x2 = x;
486
+ }
487
+ if (x2 < 0)
488
+ return null;
489
+ return { x1: 0, y1: y, x2, y2: y };
490
+ };
403
491
  restoreLastOutput = () => {
404
492
  if (!this.interactive) {
405
493
  return;
@@ -1,4 +1,7 @@
1
- import widestLine from 'widest-line';
1
+ // [mixdog fork] use the shared display-width policy so ink's text measurement
2
+ // matches OUR wrap/row math for circled digits / arrows. Kept in sync with
3
+ // src/tui/display-width.mjs.
4
+ import { displayWidestLine as widestLine } from './display-width.js';
2
5
  const cache = new Map();
3
6
  const measureText = (text) => {
4
7
  if (text.length === 0) {
@@ -1,6 +1,41 @@
1
1
  import sliceAnsi from 'slice-ansi';
2
- import stringWidth from 'string-width';
3
2
  import { styledCharsFromTokens, styledCharsToString, tokenize, } from '@alcalzone/ansi-tokenize';
3
+ // [mixdog fork] use the shared display-width policy so ink's per-character
4
+ // advance + width cache treat circled digits / arrows as 2 cells under Windows
5
+ // Terminal, matching OUR wrap/row math. See display-width.js (kept in sync with
6
+ // src/tui/display-width.mjs).
7
+ import { displayStringWidth as stringWidth } from './display-width.js';
8
+ const RGB_COLOR_RE = /^rgb\(\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\s*\)$/;
9
+ const rgbStyle = (value, type, fallback) => {
10
+ const match = RGB_COLOR_RE.exec(String(value || ''));
11
+ const parts = match
12
+ ? match.slice(1, 4).map(part => Math.max(0, Math.min(255, Number(part) || 0)))
13
+ : fallback;
14
+ const sgr = type === 'foreground' ? 38 : 48;
15
+ const reset = type === 'foreground' ? '\x1b[39m' : '\x1b[49m';
16
+ return { code: `\x1b[${sgr};2;${parts[0]};${parts[1]};${parts[2]}m`, endCode: reset };
17
+ };
18
+ const intersectClips = (clips) => {
19
+ if (clips.length === 0) {
20
+ return undefined;
21
+ }
22
+ const out = {};
23
+ for (const clip of clips) {
24
+ if (typeof clip?.x1 === 'number') {
25
+ out.x1 = typeof out.x1 === 'number' ? Math.max(out.x1, clip.x1) : clip.x1;
26
+ }
27
+ if (typeof clip?.x2 === 'number') {
28
+ out.x2 = typeof out.x2 === 'number' ? Math.min(out.x2, clip.x2) : clip.x2;
29
+ }
30
+ if (typeof clip?.y1 === 'number') {
31
+ out.y1 = typeof out.y1 === 'number' ? Math.max(out.y1, clip.y1) : clip.y1;
32
+ }
33
+ if (typeof clip?.y2 === 'number') {
34
+ out.y2 = typeof out.y2 === 'number' ? Math.min(out.y2, clip.y2) : clip.y2;
35
+ }
36
+ }
37
+ return out;
38
+ };
4
39
  class OutputCaches {
5
40
  widths = new Map();
6
41
  blockWidths = new Map();
@@ -112,10 +147,19 @@ export default class Output {
112
147
  const { text, transformers } = operation;
113
148
  let { x, y } = operation;
114
149
  let lines = text.split('\n');
115
- const clip = clips.at(-1);
150
+ // [mixdog fork] Nested overflow:hidden boxes must be clipped to
151
+ // the INTERSECTION of every active ancestor clip. Using only the
152
+ // innermost clip lets child components such as ToolExecution /
153
+ // TurnDone (which have their own overflow hidden rows) escape the
154
+ // transcript viewport and paint over command/prompt/status rows.
155
+ const clip = intersectClips(clips);
116
156
  if (clip) {
117
157
  const clipHorizontally = typeof clip?.x1 === 'number' && typeof clip?.x2 === 'number';
118
158
  const clipVertically = typeof clip?.y1 === 'number' && typeof clip?.y2 === 'number';
159
+ if ((clipHorizontally && clip.x2 <= clip.x1) ||
160
+ (clipVertically && clip.y2 <= clip.y1)) {
161
+ continue;
162
+ }
119
163
  // If text is positioned outside of clipping area altogether,
120
164
  // skip to the next operation to avoid unnecessary calculations
121
165
  if (clipHorizontally) {
@@ -218,15 +262,34 @@ export default class Output {
218
262
  // dim text, and status colors do not bleed through.
219
263
  const sel = this.selection;
220
264
  let selectedText = null;
265
+ // [mixdog fork] noSelect exclusion (claude-code skips gutter / line-number
266
+ // / diff-sigil cells from both highlight and copy via screen.noSelect):
267
+ // mixdog's cell model has NO noSelect marker — the Output grid stores only
268
+ // {value, styles} per cell, with no flag distinguishing gutter cells from
269
+ // content. Inferring gutters from position/content would be a fragile
270
+ // heuristic (line numbers, diff +/- sigils, and real content are
271
+ // indistinguishable at the cell level), so this is deliberately NOT
272
+ // implemented. It needs a noSelect bit threaded through the render
273
+ // pipeline before it can be done cleanly.
221
274
  if (sel) {
222
275
  const captureSelectedText = sel.captureText !== false;
223
276
  if (!captureSelectedText) {
224
277
  selectedText = undefined;
225
278
  }
226
- const selectionStyles = [
227
- { code: '', endCode: '' },
228
- { code: '', endCode: '' },
229
- ];
279
+ // [mixdog fork] Port applySelectionOverlay's fg-preserving principle
280
+ // from claude-code's selection.ts: REPLACE only the background and
281
+ // PRESERVE each cell's own foreground/attribute styles, so syntax
282
+ // highlighting, OSC-8 links, and dim text stay readable under the
283
+ // selection. The old code forced a black fg + light bg, flattening
284
+ // every colored glyph to one color. selectionForeground is now
285
+ // intentionally unused (kept in the setter API for compatibility).
286
+ const selectionBg = rgbStyle(sel.selectionBackground, 'background', [245, 245, 245]);
287
+ // A style entry sets the background when its opening SGR code begins
288
+ // with a background parameter: 40-47 (basic), 48 (256/truecolor),
289
+ // 49 (default), or 100-107 (bright). ansi-tokenize emits one entry
290
+ // per SGR attribute, so a prefix test on `.code` cleanly isolates the
291
+ // bg-only entries we want to drop before appending the selection bg.
292
+ const isBackgroundStyle = (s) => typeof s?.code === 'string' && /^\x1b\[(?:4[0-9]|10[0-7])[;m]/.test(s.code);
230
293
  const linear = sel.mode === 'linear';
231
294
  const start = linear && (sel.y1 > sel.y2 || (sel.y1 === sel.y2 && sel.x1 > sel.x2))
232
295
  ? { x: sel.x2, y: sel.y2 }
@@ -281,9 +344,16 @@ export default class Output {
281
344
  if (contentStart === -1 || x < contentStart || x > contentEnd) {
282
345
  continue;
283
346
  }
347
+ // [mixdog fork] fg-preserving highlight: keep the cell's own
348
+ // style entries (foreground color, bold/dim/underline, link
349
+ // attrs) and swap ONLY the background. Drop any existing
350
+ // background entry so the two bgs don't fight, then append the
351
+ // selection bg last so it wins for this cell.
352
+ const baseStyles = Array.isArray(cell.styles) ? cell.styles : [];
353
+ const preserved = baseStyles.filter(style => !isBackgroundStyle(style));
284
354
  row[x] = {
285
355
  ...cell,
286
- styles: selectionStyles,
356
+ styles: [...preserved, selectionBg],
287
357
  };
288
358
  }
289
359
  // Trailing spaces in a selected row are padding, not content.
@@ -302,6 +372,20 @@ export default class Output {
302
372
  while (selRows.length > 0 && selRows[selRows.length - 1].trim() === '') {
303
373
  selRows.pop();
304
374
  }
375
+ // [mixdog fork] SOFT-WRAP JOIN (claude-code getSelectedText):
376
+ // claude-code rejoins word-wrapped continuation rows into one
377
+ // logical line using screen.softWrap, a per-row bitmap set at
378
+ // wrap time marking a row as a continuation of the one above.
379
+ // mixdog's rect(linear) render model carries NO softWrap metadata
380
+ // — the Output grid is a flat array of visual rows with no record
381
+ // of which line breaks were inserted by wrapping vs. present in
382
+ // the source. Detecting "this row filled to content width AND the
383
+ // next continues the same logical block" from cell values alone is
384
+ // a guess (a source line that legitimately fills the width would be
385
+ // wrongly glued to the next). Per the correctness-over-cleverness
386
+ // rule we keep the honest per-visual-row '\n' join rather than
387
+ // fabricate wrap boundaries. Revisit if the render pipeline starts
388
+ // emitting a softWrap bit per row.
305
389
  selectedText = selRows.join('\n');
306
390
  }
307
391
  }
@@ -317,8 +401,30 @@ export default class Output {
317
401
  const generatedOutput = output
318
402
  .map(line => {
319
403
  // See https://github.com/vadimdemedes/ink/pull/564#issuecomment-1637022742
320
- const lineWithoutEmptyItems = line.filter(item => item !== undefined);
321
- return styledCharsToString(lineWithoutEmptyItems).trimEnd();
404
+ // [mixdog fork] Keep the serialized row inside Output.width as a
405
+ // final invariant. Component-level width budgeting can miss
406
+ // background fills, resize races, or wide-char edge cases; if the
407
+ // terminal receives a row wider than its modeled width it may
408
+ // soft-wrap/scroll outside Ink's cursor-origin model.
409
+ const lineWithinWidth = [];
410
+ let usedWidth = 0;
411
+ for (const item of line) {
412
+ if (item === undefined) {
413
+ continue;
414
+ }
415
+ const value = item.value ?? '';
416
+ if (value === '') {
417
+ lineWithinWidth.push(item);
418
+ continue;
419
+ }
420
+ const itemWidth = Math.max(1, this.caches.getStringWidth(value));
421
+ if (usedWidth + itemWidth > this.width) {
422
+ break;
423
+ }
424
+ lineWithinWidth.push(item);
425
+ usedWidth += itemWidth;
426
+ }
427
+ return styledCharsToString(lineWithinWidth).trimEnd();
322
428
  })
323
429
  .join('\n');
324
430
  return {
@@ -1,4 +1,7 @@
1
- import widestLine from 'widest-line';
1
+ // [mixdog fork] use the shared display-width policy so the wrap decision here
2
+ // agrees with measure-text.js and output.js. Kept in sync with
3
+ // src/tui/display-width.mjs.
4
+ import { displayWidestLine as widestLine } from './display-width.js';
2
5
  import indentString from 'indent-string';
3
6
  import Yoga from 'yoga-layout';
4
7
  import wrapText from './wrap-text.js';
@@ -41,6 +41,10 @@ const render = (node, options) => {
41
41
  getSelectionText: () => instance.selectedText,
42
42
  // [mixdog fork] resolve the word rect at a cell for double-click select.
43
43
  getWordRectAt: (x, y) => instance.getWordRectAt(x, y),
44
+ // [mixdog fork] resolve the whole-line rect at a row for triple-click /
45
+ // line select. Symmetric to getWordRectAt; index.jsx wires it into the
46
+ // store as store.getLineRectAt.
47
+ getLineRectAt: (y) => instance.getLineRectAt(y),
44
48
  };
45
49
  };
46
50
  export default render;
@@ -1,20 +0,0 @@
1
- ---
2
- name: extreme-simple
3
- title: Extreme Simple
4
- description: Ultra-short completion sentence
5
- aliases: extreme, extreme simple
6
- keep-coding-instructions: true
7
- ---
8
-
9
- # Output Style
10
-
11
- Extreme — one ultra-short completion sentence. The most minimal of the styles.
12
-
13
- - Reply with a single completion sentence whenever possible.
14
- - Preferred pattern: `<target> 변경되었습니다. <verification> 통과 완료입니다.`
15
- - If verification was not run, use one short sentence saying the change is done
16
- and verification was not run.
17
- - No headings, bullets, labels, explanations, tool traces, or metadata unless
18
- the user explicitly asks.
19
- - Preserve only the single decisive path, command, symbol, API name, code, or
20
- error verbatim.
@@ -1,51 +0,0 @@
1
- # Workflow
2
-
3
- Priority:
4
- - When an `# Active Workflow` block is present, it is the binding session
5
- workflow and takes precedence over all other workflow guidance.
6
-
7
- Delegation:
8
- - Lead is the supervisor of agents, responsible for task delegation,
9
- coordination, judgment, and final decisions.
10
- - As supervisor, prefer delegating implementation work to an agent; keep direct
11
- work limited to coordination and User Workflow exceptions such as
12
- one-or-two-step edits, pre-planning, config changes, and final git deployment.
13
- - Implementation edits to product/runtime/TUI code MUST be delegated to a
14
- Worker/Heavy Worker, even when small. Lead may directly edit only rules, docs,
15
- config, and final deployment/git steps, unless the user explicitly asks Lead
16
- to patch directly.
17
- - Delegation split: different code paths / entry points / file groups are
18
- separate scopes — implementation AND analysis, review, debugging split the
19
- same way. With 2+ independent scopes, spawn each scoped agent in the
20
- SAME turn, not one after another. Shared functions or cross-cutting concerns
21
- do NOT justify collapsing them into one delegation: split per path, and Lead
22
- verifies the cross-cutting parts (shared state, telemetry consistency, etc.)
23
- directly. "One agent must see the whole context" is not a valid reason to
24
- merge; the only legitimate single-scope case is a genuinely inseparable
25
- dependency — and then state the reason.
26
- - Follow-up work on the same path or scope MUST reuse the existing agent (same
27
- tag). Spawn a new agent only for a genuinely separate scope.
28
- - Pick the agent role defined in the User Workflow section that fits the task
29
- (worker, heavy-worker, reviewer, debugger, …) when spawning.
30
- - Write agent-facing briefs and follow-up task messages in English.
31
- - Agent briefs must be bounded: state the goal/success criteria, known starting
32
- files or anchors if available, allowed/forbidden changes, expected
33
- deliverable, and verification boundary. For heavy-worker, include known first
34
- components to inspect and the stopping condition; complex work must not
35
- become open-ended.
36
-
37
- Agent lifecycle:
38
- - After spawning async agent(s), END THE TURN immediately. Do not poll
39
- status/read, guess the outcome, or start dependent work — treat the result as
40
- unknown until the completion notification resumes you. status/read are manual
41
- recovery only, never progress checks.
42
- - Unless instructed otherwise, wait for the spawned agent(s); once their results
43
- are collected, automatically continue with the next turn.
44
-
45
- Agent result handling:
46
- - Cross-check material agent responses before using them for decisions or
47
- summarizing them to the user.
48
- - When an agent returns, always synthesize its result into a concise report for
49
- the user (outcome + key changes/evidence). Never forward the raw agent output.
50
- - If anything here conflicts with the User Workflow section, this Workflow takes
51
- precedence, except that the `# Active Workflow` block always wins.
@@ -1,13 +0,0 @@
1
- {
2
- "id": "default",
3
- "name": "Default",
4
- "description": "Default Mixdog agent workflow.",
5
- "entry": "WORKFLOW.md",
6
- "agents": [
7
- "maintainer",
8
- "worker",
9
- "heavy-worker",
10
- "reviewer",
11
- "debugger"
12
- ]
13
- }
@@ -1,7 +0,0 @@
1
- {
2
- "id": "solo",
3
- "name": "Solo",
4
- "description": "Lead-only workflow; delegation to any agent is forbidden.",
5
- "entry": "WORKFLOW.md",
6
- "agents": []
7
- }