mixdog 0.8.1 → 0.9.1

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 (299) hide show
  1. package/README.md +47 -23
  2. package/package.json +34 -28
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +205 -33
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1222 -90
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -29
  62. package/src/lib/plugin-paths.cjs +1 -7
  63. package/src/lib/rules-builder.cjs +242 -98
  64. package/src/lib/text-utils.cjs +1 -1
  65. package/src/mixdog-session-runtime.mjs +2211 -446
  66. package/src/output-styles/default.md +12 -28
  67. package/src/output-styles/extreme-simple.md +9 -6
  68. package/src/output-styles/simple.md +22 -9
  69. package/src/repl.mjs +108 -59
  70. package/src/rules/agent/00-common.md +15 -0
  71. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  72. package/src/rules/agent/30-explorer.md +22 -0
  73. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  75. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  76. package/src/rules/lead/01-general.md +9 -5
  77. package/src/rules/lead/04-workflow.md +51 -12
  78. package/src/rules/lead/lead-tool.md +6 -0
  79. package/src/rules/shared/01-tool.md +12 -1
  80. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  81. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  82. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  83. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  85. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  86. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  87. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  88. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  89. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  90. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  92. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  93. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  94. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  95. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +380 -268
  96. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +131 -93
  97. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  98. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  99. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  100. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  101. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  102. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  103. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  104. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  107. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +203 -71
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +512 -312
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -518
  110. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  111. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  112. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +289 -11
  113. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  114. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  115. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  116. package/src/runtime/agent/orchestrator/session/loop.mjs +840 -612
  117. package/src/runtime/agent/orchestrator/session/manager.mjs +1574 -459
  118. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  119. package/src/runtime/agent/orchestrator/session/store.mjs +67 -29
  120. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  121. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  122. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  123. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +35 -8
  124. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -8
  125. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +10 -53
  126. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  127. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  128. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  130. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  131. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  132. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  133. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  138. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  139. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  140. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  141. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  142. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  143. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  144. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  145. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  146. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  147. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  148. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  149. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  150. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  151. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  152. package/src/runtime/channels/index.mjs +149 -51
  153. package/src/runtime/channels/lib/cli-worker-host.mjs +1 -8
  154. package/src/runtime/channels/lib/config.mjs +0 -1
  155. package/src/runtime/channels/lib/drop-trace.mjs +1 -1
  156. package/src/runtime/channels/lib/executor.mjs +0 -3
  157. package/src/runtime/channels/lib/memory-client.mjs +0 -38
  158. package/src/runtime/channels/lib/output-forwarder.mjs +1 -8
  159. package/src/runtime/channels/lib/runtime-paths.mjs +0 -6
  160. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  161. package/src/runtime/channels/lib/session-discovery.mjs +3 -6
  162. package/src/runtime/channels/lib/tool-format.mjs +0 -1
  163. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -12
  164. package/src/runtime/channels/lib/webhook.mjs +1 -1
  165. package/src/runtime/channels/tool-defs.mjs +29 -29
  166. package/src/runtime/lib/keychain-cjs.cjs +0 -1
  167. package/src/runtime/memory/data/runtime-manifest.json +6 -7
  168. package/src/runtime/memory/index.mjs +519 -118
  169. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  170. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  171. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  172. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  173. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  174. package/src/runtime/memory/lib/llm-worker-host.mjs +0 -4
  175. package/src/runtime/memory/lib/memory-cycle1.mjs +28 -7
  176. package/src/runtime/memory/lib/memory-cycle2.mjs +4 -5
  177. package/src/runtime/memory/lib/memory-cycle3.mjs +2 -2
  178. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -2
  179. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  180. package/src/runtime/memory/lib/memory.mjs +88 -9
  181. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  182. package/src/runtime/memory/lib/pg/adapter.mjs +1 -1
  183. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  184. package/src/runtime/memory/lib/runtime-fetcher.mjs +80 -21
  185. package/src/runtime/memory/lib/session-ingest.mjs +196 -0
  186. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  187. package/src/runtime/memory/tool-defs.mjs +46 -37
  188. package/src/runtime/search/index.mjs +104 -473
  189. package/src/runtime/search/lib/config.mjs +0 -4
  190. package/src/runtime/search/lib/state.mjs +1 -15
  191. package/src/runtime/search/lib/web-tools.mjs +44 -25
  192. package/src/runtime/search/tool-defs.mjs +16 -23
  193. package/src/runtime/shared/abort-controller.mjs +1 -1
  194. package/src/runtime/shared/atomic-file.mjs +4 -3
  195. package/src/runtime/shared/background-tasks.mjs +122 -11
  196. package/src/runtime/shared/child-spawn-gate.mjs +139 -0
  197. package/src/runtime/shared/config.mjs +7 -4
  198. package/src/runtime/shared/err-text.mjs +131 -4
  199. package/src/runtime/shared/llm/cost.mjs +2 -2
  200. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  201. package/src/runtime/shared/llm/index.mjs +34 -11
  202. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  203. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  204. package/src/runtime/shared/singleton-owner.mjs +104 -0
  205. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  206. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  207. package/src/runtime/shared/tool-surface.mjs +624 -95
  208. package/src/runtime/shared/user-data-guard.mjs +0 -2
  209. package/src/standalone/agent-task-status.mjs +203 -0
  210. package/src/standalone/agent-task-status.test.mjs +76 -0
  211. package/src/standalone/agent-tool.mjs +1913 -0
  212. package/src/standalone/channel-worker.mjs +370 -14
  213. package/src/standalone/explore-tool.mjs +165 -70
  214. package/src/standalone/folder-dialog.mjs +314 -0
  215. package/src/standalone/hook-bus.mjs +898 -22
  216. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  217. package/src/standalone/projects.mjs +226 -0
  218. package/src/standalone/provider-admin.mjs +41 -24
  219. package/src/standalone/seeds.mjs +1 -78
  220. package/src/standalone/usage-dashboard.mjs +96 -8
  221. package/src/tui/App.jsx +4800 -2140
  222. package/src/tui/components/AnsiText.jsx +39 -28
  223. package/src/tui/components/ContextPanel.jsx +14 -4
  224. package/src/tui/components/Markdown.jsx +43 -77
  225. package/src/tui/components/MarkdownTable.jsx +9 -184
  226. package/src/tui/components/Message.jsx +28 -11
  227. package/src/tui/components/Picker.jsx +95 -56
  228. package/src/tui/components/PromptInput.jsx +428 -239
  229. package/src/tui/components/QueuedCommands.jsx +1 -1
  230. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  231. package/src/tui/components/Spinner.jsx +67 -38
  232. package/src/tui/components/StatusLine.jsx +606 -38
  233. package/src/tui/components/TextEntryPanel.jsx +128 -9
  234. package/src/tui/components/ToolExecution.jsx +597 -362
  235. package/src/tui/components/TurnDone.jsx +3 -3
  236. package/src/tui/components/UsagePanel.jsx +3 -5
  237. package/src/tui/components/tool-output-format.mjs +499 -0
  238. package/src/tui/components/tool-output-format.test.mjs +312 -0
  239. package/src/tui/dist/index.mjs +9192 -2399
  240. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  241. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  242. package/src/tui/engine.mjs +1455 -279
  243. package/src/tui/figures.mjs +21 -40
  244. package/src/tui/index.jsx +75 -31
  245. package/src/tui/input-editing.mjs +25 -0
  246. package/src/tui/markdown/format-token.mjs +671 -69
  247. package/src/tui/markdown/format-token.test.mjs +312 -0
  248. package/src/tui/markdown/render-ansi.mjs +94 -0
  249. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  250. package/src/tui/markdown/stream-fence.mjs +34 -0
  251. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  252. package/src/tui/markdown/table-layout.mjs +250 -0
  253. package/src/tui/paste-attachments.mjs +0 -7
  254. package/src/tui/spinner-verbs.mjs +1 -2
  255. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  256. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  257. package/src/tui/theme.mjs +756 -24
  258. package/src/tui/time-format.mjs +1 -1
  259. package/src/tui/transcript-tool-failures.mjs +67 -0
  260. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  261. package/src/ui/ansi.mjs +1 -2
  262. package/src/ui/markdown.mjs +85 -26
  263. package/src/ui/markdown.test.mjs +70 -0
  264. package/src/ui/model-display.mjs +121 -0
  265. package/src/ui/session-stats.mjs +44 -0
  266. package/src/ui/statusline-context-label.test.mjs +15 -0
  267. package/src/ui/statusline.mjs +386 -178
  268. package/src/ui/tool-card.mjs +2 -13
  269. package/src/vendor/statusline/bin/statusline-lib.mjs +1 -620
  270. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  271. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  272. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  273. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  274. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  275. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  276. package/src/workflows/default/WORKFLOW.md +12 -5
  277. package/src/workflows/default/workflow.json +0 -1
  278. package/src/workflows/solo/WORKFLOW.md +15 -0
  279. package/src/workflows/solo/workflow.json +7 -0
  280. package/vendor/ink/build/ink.js +54 -8
  281. package/vendor/ink/build/output.js +6 -1
  282. package/src/agents/scheduler-task.md +0 -3
  283. package/src/agents/web-researcher/AGENT.md +0 -3
  284. package/src/agents/web-researcher/agent.json +0 -6
  285. package/src/agents/webhook-handler.md +0 -3
  286. package/src/hooks/lib/permission-rules.cjs +0 -170
  287. package/src/hooks/lib/settings-loader.cjs +0 -112
  288. package/src/lib/hook-pipe-path.cjs +0 -10
  289. package/src/rules/bridge/00-common.md +0 -5
  290. package/src/rules/bridge/30-explorer.md +0 -4
  291. package/src/rules/lead/00-tool-lead.md +0 -5
  292. package/src/rules/shared/00-language.md +0 -3
  293. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  294. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  295. package/src/runtime/channels/lib/hook-pipe-server.mjs +0 -671
  296. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  297. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  298. package/src/standalone/bridge-tool.mjs +0 -1414
  299. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -1,50 +1,31 @@
1
1
  /**
2
2
  * figures.mjs — TUI glyph constants.
3
3
  *
4
- * Claude Code uses Unicode markers heavily. Keep those markers intact so UI
5
- * styling does not change under environment-dependent ASCII conversion.
4
+ * Unicode markers are used heavily in the TUI. Each glyph is declared with its
5
+ * `\uXXXX` escape (encoding-safe across editors/transports) and the literal
6
+ * glyph + usage is kept in the trailing comment for readability.
6
7
  */
7
8
 
8
- export const BLACK_CIRCLE = process.platform === 'darwin' ? '⏺' : '●';
9
- export const BULLET_OPERATOR = '·';
10
- export const TEARDROP_ASTERISK = '';
11
- export const UP_ARROW = '';
12
- export const DOWN_ARROW = '';
13
- export const LIGHTNING_BOLT = '';
14
- export const EFFORT_LOW = '○';
15
- export const EFFORT_MEDIUM = '◐';
16
- export const EFFORT_HIGH = '●';
17
- export const EFFORT_MAX = '◉';
9
+ // Turn marker. \u23fa (⏺) aligns better but is poorly supported off macOS, so
10
+ // other platforms use \u25cf (●).
11
+ export const BLACK_CIRCLE = process.platform === 'darwin' ? '\u23fa' : '\u25cf'; // ⏺ / ● - turn marker
12
+ export const BULLET_OPERATOR = '\u00b7'; // · - inline separator
13
+ export const UP_ARROW = '\u2191'; // ↑ - history/scroll up
14
+ export const DOWN_ARROW = '\u2193'; // ↓ - history/scroll down
18
15
 
19
- // Media/trigger status indicators
20
- export const PLAY_ICON = '▶';
21
- export const PAUSE_ICON = '⏸';
16
+ // Tool-result tree gutter. \u2514 (└, BOX DRAWINGS LIGHT UP AND RIGHT) renders
17
+ // in virtually every monospace font; the previous \u23bf (bracket extension)
18
+ // had no glyph in common terminal fonts (Cascadia/Consolas) and fell back to a
19
+ // literal "L", breaking the result tree alignment.
20
+ export const RESULT_GUTTER_GLYPH = '\u2514'; // └ - tool-result tree gutter
21
+ export const RESULT_GUTTER_CONT_GLYPH = '\u2502'; // │ - tool-result continuation rail
22
22
 
23
- // Tool-result tree gutter.
24
- export const RESULT_GUTTER_GLYPH = '⎿';
23
+ export const BLOCKQUOTE_BAR = '\u258e'; // - blockquote line prefix
25
24
 
26
- // MCP subscription indicators
27
- export const REFRESH_ARROW = '↻';
28
- export const CHANNEL_ARROW = '←';
29
- export const INJECTED_ARROW = '→';
30
- export const FORK_GLYPH = '⑂';
31
-
32
- // Review status indicators
33
- export const DIAMOND_OPEN = '◇';
34
- export const DIAMOND_FILLED = '◆';
35
- export const REFERENCE_MARK = '※';
36
-
37
- // Issue flag indicator
38
- export const FLAG_ICON = '⚑';
39
-
40
- // Blockquote indicator
41
- export const BLOCKQUOTE_BAR = '▎';
42
- export const HEAVY_HORIZONTAL = '━';
43
-
44
- // The "therefore" sign — Claude Code's collapsed-thinking prefix (∴ Thinking).
45
- export const THEREFORE = '∴';
25
+ export const THEREFORE = '\u2234'; // - collapsed-thinking prefix (∴ Thinking)
46
26
  // Turn-complete marker — kept distinct from live-thinking's therefore sign.
47
- export const TURN_DONE_MARKER = '';
27
+ export const TURN_DONE_MARKER = '\u25c8'; // ◈ - turn-complete marker
48
28
 
49
- // Prompt pointer (CC uses figures.pointer for the input glyph).
50
- export const POINTER = '❯';
29
+ // Horizontal rule fill BOX DRAWINGS LIGHT HORIZONTAL. Repeated to span the
30
+ // available content width for a clean full-width `---` rule.
31
+ export const HR_LINE = '\u2500'; // ─ - markdown horizontal rule
package/src/tui/index.jsx CHANGED
@@ -1,9 +1,9 @@
1
- /**
2
- * src/tui/index.jsx — entry that mounts the React/ink TUI.
3
- *
4
- * Creates the engine session (runs OUR agentLoop outside React) and ink-renders
1
+ /**
2
+ * src/tui/index.jsx — entry that mounts the React/ink TUI.
3
+ *
4
+ * Creates the engine session (runs OUR agentLoop outside React) and ink-renders
5
5
  * <App store={...}/>. Resolves when the app exits (/exit or /quit).
6
- */
6
+ */
7
7
  import React from 'react';
8
8
  import { render } from 'ink';
9
9
  import { createWriteStream, mkdirSync } from 'node:fs';
@@ -13,8 +13,10 @@ import { performance } from 'node:perf_hooks';
13
13
  import { App } from './App.jsx';
14
14
  import { createEngineSession } from './engine.mjs';
15
15
  import { installProcessSignalCleanup } from '../runtime/shared/process-shutdown.mjs';
16
-
16
+ import { emitTerminalBackground, loadThemeSettingFromConfig, theme } from './theme.mjs';
17
+
17
18
  const TERMINAL_MODE_RESET = '\x1b[?1006l\x1b[?1005l\x1b[?1015l\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?2004l\x1b[?25h';
19
+ const TERMINAL_OSC_RESET_BG = '\x1b]111\x07';
18
20
  const TERMINAL_MODE_RESET_HIDDEN_CURSOR = TERMINAL_MODE_RESET.replace('\x1b[?25h', '\x1b[?25l');
19
21
  const MOUSE_TRACKING_ON = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
20
22
  const BOOT_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ''));
@@ -23,12 +25,43 @@ const EXIT_WAIT_TIMEOUT_MS = positiveIntEnv('MIXDOG_TUI_EXIT_WAIT_MS', 2500);
23
25
  const EXIT_HARD_DELAY_MS = positiveIntEnv('MIXDOG_TUI_HARD_EXIT_DELAY_MS', 500);
24
26
  const EXIT_HARD_ENABLED = !/^(0|false|no|off)$/i.test(String(process.env.MIXDOG_TUI_HARD_EXIT || '1'));
25
27
  const EXIT_DEBUG_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_TUI_EXIT_DEBUG || ''));
28
+ const PERF_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_TUI_PERF || ''));
26
29
 
27
30
  function positiveIntEnv(name, fallback) {
28
31
  const value = Number(process.env[name]);
29
32
  return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
30
33
  }
31
34
 
35
+ // Lightweight render-frame profiler. Forked ink calls options.onRender with the
36
+ // per-frame render() wall time (renderNodeToOutput serialization). We aggregate
37
+ // and emit a rolling summary every PERF_REPORT_EVERY frames so typing latency
38
+ // can be measured without flooding output. Entirely no-op unless
39
+ // MIXDOG_TUI_PERF=1, so it costs nothing in normal runs.
40
+ const PERF_REPORT_EVERY = positiveIntEnv('MIXDOG_TUI_PERF_EVERY', 60);
41
+ function makeRenderProfiler() {
42
+ if (!PERF_ENABLED) return undefined;
43
+ let count = 0;
44
+ let sum = 0;
45
+ let max = 0;
46
+ let slow = 0;
47
+ return ({ renderTime } = {}) => {
48
+ const ms = Number(renderTime) || 0;
49
+ count += 1;
50
+ sum += ms;
51
+ if (ms > max) max = ms;
52
+ if (ms >= 16) slow += 1;
53
+ if (count >= PERF_REPORT_EVERY) {
54
+ const avg = sum / count;
55
+ try {
56
+ process.stderr.write(
57
+ `[mixdog-perf] frames=${count} avg=${avg.toFixed(2)}ms max=${max.toFixed(2)}ms slow16+=${slow}\n`,
58
+ );
59
+ } catch { /* ignore */ }
60
+ count = 0; sum = 0; max = 0; slow = 0;
61
+ }
62
+ };
63
+ }
64
+
32
65
  function bootProfile(event, fields = {}) {
33
66
  if (!BOOT_PROFILE_ENABLED) return;
34
67
  const elapsedMs = performance.now() - BOOT_PROFILE_START;
@@ -92,7 +125,7 @@ function scheduleHardExit(code = 0) {
92
125
  if (!EXIT_HARD_ENABLED) return;
93
126
  const timer = setTimeout(() => {
94
127
  dumpActiveHandles('hard-exit');
95
- try { process.stdout.write(TERMINAL_MODE_RESET); } catch { /* ignore */ }
128
+ try { process.stdout.write(`${TERMINAL_MODE_RESET}${TERMINAL_OSC_RESET_BG}`); } catch { /* ignore */ }
96
129
  process.exit(code);
97
130
  }, EXIT_HARD_DELAY_MS);
98
131
  timer.unref?.();
@@ -158,27 +191,48 @@ export async function runTui({ provider, model, toolMode } = {}) {
158
191
  }
159
192
 
160
193
  const restoreStderr = installTuiStderrGuard();
194
+ const restorePrimedInput = () => {}; // stdin raw mode is owned by Ink's useInput effects
195
+ let restored = false;
196
+ const restoreTerminal = () => {
197
+ if (restored) return;
198
+ restored = true;
199
+ restorePrimedInput();
200
+ try { process.stdout.write(`${TERMINAL_MODE_RESET}\x1b[0 q\x1b[?1049l${TERMINAL_MODE_RESET}${TERMINAL_OSC_RESET_BG}`); } catch { /* ignore */ }
201
+ };
202
+
203
+ // Enter the alternate screen buffer before session/runtime boot so no stale
204
+ // shell rows are visible while the statusline/TUI data warms up. The first
205
+ // real Ink frame may still arrive after createEngineSession(), but the user
206
+ // sees a clean fullscreen surface immediately instead of the previous terminal
207
+ // contents bleeding through the bottom status area.
208
+ process.stdout.write(`${TERMINAL_MODE_RESET_HIDDEN_CURSOR}\x1b[?1049h${TERMINAL_MODE_RESET_HIDDEN_CURSOR}\x1b[2J\x1b[H`);
209
+
210
+ // Use a blinking BAR cursor (DECSCUSR 5) — a thin caret behind the text, not a
211
+ // fat block. PromptInput parks this hardware cursor at the insertion point via
212
+ // useCursor; the terminal also anchors IME composition to it.
213
+ process.stdout.write('\x1b[5 q'); // blinking bar
214
+
215
+ process.on('exit', restoreTerminal);
216
+
217
+ // Apply the persisted UI theme (ui.theme in mixdog-config.json) before the
218
+ // first React frame so the whole tree paints in the chosen palette. Unknown
219
+ // or missing values leave the default Mixdog dark palette in place; a failed
220
+ // config read never blocks boot.
221
+ try { await loadThemeSettingFromConfig(); } catch { /* default theme stays */ }
222
+ emitTerminalBackground(theme.background);
223
+
161
224
  let store;
162
225
  try {
163
226
  store = await createEngineSession({ provider, model, toolMode });
164
227
  bootProfile('store:ready', { ms: (performance.now() - startedAt).toFixed(1) });
165
228
  } catch (error) {
229
+ restoreTerminal();
230
+ try { process.off('exit', restoreTerminal); } catch { /* ignore */ }
166
231
  restoreStderr();
167
232
  process.stderr.write(`mixdog: ${error?.message || error}\n`);
168
233
  return 1;
169
234
  }
170
-
171
- // Enter the alternate screen buffer for a true fullscreen UI: the input bar
172
- // pins to the physical bottom (App uses height={rows}) and, on exit, the
173
- // shell's original screen is restored untouched. \x1b[?1049h enters alt
174
- // screen; then clear it and home the cursor so we start from a clean top.
175
- process.stdout.write(`${TERMINAL_MODE_RESET_HIDDEN_CURSOR}\x1b[?1049h${TERMINAL_MODE_RESET_HIDDEN_CURSOR}\x1b[2J\x1b[H`);
176
-
177
- // Use a blinking BAR cursor (DECSCUSR 5) — a thin caret behind the text, not a
178
- // fat block. PromptInput parks this hardware cursor at the insertion point via
179
- // useCursor; the terminal also anchors IME composition to it.
180
- process.stdout.write('\x1b[5 q'); // blinking bar
181
-
235
+
182
236
  // Keep mouse handling app-owned by default: native terminal selections are
183
237
  // cleared by the fullscreen redraws that happen while a turn streams. Users
184
238
  // who prefer their terminal's native mouse behavior can opt out with
@@ -187,16 +241,6 @@ export async function runTui({ provider, model, toolMode } = {}) {
187
241
  if (mouseTracking) {
188
242
  process.stdout.write(MOUSE_TRACKING_ON);
189
243
  }
190
- const restorePrimedInput = () => {}; // stdin raw mode is owned by Ink's useInput effects
191
-
192
- let restored = false;
193
- const restoreTerminal = () => {
194
- if (restored) return;
195
- restored = true;
196
- restorePrimedInput();
197
- try { process.stdout.write(`${TERMINAL_MODE_RESET}\x1b[0 q\x1b[?1049l${TERMINAL_MODE_RESET}`); } catch { /* ignore */ }
198
- };
199
- process.on('exit', restoreTerminal);
200
244
  let storeDisposed = false;
201
245
  const disposeStoreOnce = async (reason = 'cli-react-exit') => {
202
246
  if (storeDisposed) return;
@@ -215,12 +259,12 @@ export async function runTui({ provider, model, toolMode } = {}) {
215
259
  restoreStderr();
216
260
  },
217
261
  });
218
-
262
+
219
263
  // exitOnCtrlC:false — App handles Ctrl+C as an interrupt/line-clear so Ink
220
264
  // does not exit abruptly. Explicit exits go through /exit or /quit so teardown
221
265
  // still restores the cursor, mouse mode, and alternate screen cleanly.
222
266
  try {
223
- const instance = render(<App store={store} />, { exitOnCtrlC: false, maxFps: 120 });
267
+ const instance = render(<App store={store} />, { exitOnCtrlC: false, maxFps: 120, onRender: makeRenderProfiler() });
224
268
  bootProfile('render:mounted', { ms: (performance.now() - startedAt).toFixed(1) });
225
269
  const { waitUntilExit } = instance;
226
270
  // [mixdog fork] Hand the ink renderer's drag-selection setter to the store so
@@ -154,6 +154,31 @@ export function caretPosition(text, offset, width) {
154
154
  return { row, col };
155
155
  }
156
156
 
157
+ // Inverse of caretPosition: given a visual (row, col) cell inside the wrapped
158
+ // content of `width` columns, return the character offset whose caret sits
159
+ // closest to that cell. Used to map a mouse click/drag cell in the prompt box
160
+ // to an edit offset so mouse selection can drive selectionAnchor + cursor.
161
+ export function offsetAtCell(text, row, col, width) {
162
+ const w = safeWidth(width);
163
+ const value = String(text || '');
164
+ const positions = boundaryPositions(value, w);
165
+ if (positions.length === 0) return 0;
166
+ let best = positions[0];
167
+ let bestScore = Infinity;
168
+ for (const position of positions) {
169
+ const rowDist = Math.abs(position.row - row);
170
+ const colDist = Math.abs(position.col - col);
171
+ // Row distance dominates so a click never jumps to a different visual line;
172
+ // within the matched row the nearest column wins.
173
+ const score = rowDist * 100000 + colDist;
174
+ if (score < bestScore) {
175
+ best = position;
176
+ bestScore = score;
177
+ }
178
+ }
179
+ return best.offset;
180
+ }
181
+
157
182
  function boundaryPositions(text, width) {
158
183
  const value = String(text || '');
159
184
  const offsets = [0];