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
@@ -5,8 +5,15 @@
5
5
 
6
6
  import { parseToolArgs } from '../runtime/shared/tool-surface.mjs';
7
7
 
8
- const HOOK_DENIAL_RE = /denied by hook/i;
9
- const APPROVAL_UNAVAILABLE_RE = /approval required but no approval UI is available/i;
8
+ // Anchor to the orchestrator's actual denial envelope:
9
+ // `Error: tool "<name>" denied by hook[: ...]`
10
+ // `Error: tool "<name>" denied by hook: approval required but no approval UI is available`
11
+ // Matching the bare phrase anywhere in the body produced FALSE POSITIVES when a
12
+ // normal (successful) tool result merely CONTAINED the text — e.g. a read/grep
13
+ // over this very file or the hook source. The denial is always an Error
14
+ // envelope emitted at the START of the result, so anchor on that prefix.
15
+ const HOOK_DENIAL_RE = /^Error:\s*tool\s*"[^"]*"\s*denied by hook\b/im;
16
+ const APPROVAL_UNAVAILABLE_RE = /denied by hook:\s*approval required but no approval UI is available/i;
10
17
 
11
18
  export function toolItemResultText(item) {
12
19
  if (!item) return '';
@@ -26,6 +33,10 @@ export function isHookApprovalDenialToolResult(text) {
26
33
 
27
34
  export function isHookApprovalDenialToolItem(item) {
28
35
  if (!item || item.kind !== 'tool') return false;
36
+ // A genuine hook denial is always surfaced as an ERROR result. Gating on
37
+ // isError stops a successful result whose body happens to contain the denial
38
+ // phrase (e.g. reading the hook source) from rendering a phantom "Denied".
39
+ if (!item.isError) return false;
29
40
  return isHookApprovalDenialToolResult(toolItemResultText(item));
30
41
  }
31
42
 
@@ -137,7 +137,7 @@ function renderLine(line, width) {
137
137
  // Blockquote (possibly nested).
138
138
  const q = /^(\s*>+)\s?(.*)$/.exec(line);
139
139
  if (q) {
140
- return dim(PALETTE.quoteText('│ ')) + italic(PALETTE.quoteText(renderInline(q[2])));
140
+ return dim('│ ') + italic(renderInline(q[2]));
141
141
  }
142
142
 
143
143
  // Bullet list.
@@ -74,13 +74,13 @@ export function canonicalModelDisplay(model, provider) {
74
74
  return `Grok ${grok[1].split('-').map(titleModelPart).filter(Boolean).join(' ')}`;
75
75
  }
76
76
 
77
- const claudeLegacy = raw.match(/^claude-(\d+)(?:-(\d+))?-(opus|sonnet|haiku)(?:-|$)/i);
77
+ const claudeLegacy = raw.match(/^claude-(\d+)(?:-(\d+))?-(opus|sonnet|haiku|fable)(?:-|$)/i);
78
78
  if (claudeLegacy) {
79
79
  const version = `${claudeLegacy[1]}${claudeLegacy[2] ? `.${claudeLegacy[2]}` : ''}`;
80
80
  return `Claude ${titleModelPart(claudeLegacy[3])} ${version}`;
81
81
  }
82
82
 
83
- const claude = raw.match(/^claude-(opus|sonnet|haiku)-(.+)$/i);
83
+ const claude = raw.match(/^claude-(opus|sonnet|haiku|fable)-(.+)$/i);
84
84
  if (claude) {
85
85
  return `Claude ${titleModelPart(claude[1])} ${claude[2].replace(/-/g, '.')}`;
86
86
  }
@@ -16,7 +16,7 @@ import { bold, colorEnabled, rgb } from './ansi.mjs';
16
16
  import { displayModelName, shortenModelName } from './model-display.mjs';
17
17
  import { createSessionStats } from './session-stats.mjs';
18
18
  import { forEachSessionRuntime } from '../runtime/agent/orchestrator/session/manager.mjs';
19
- import { listHiddenRoleNames } from '../runtime/agent/orchestrator/internal-roles.mjs';
19
+ import { listHiddenAgentNames } from '../runtime/agent/orchestrator/internal-agents.mjs';
20
20
  import { getModelMetadataSync } from '../runtime/agent/orchestrator/providers/model-catalog.mjs';
21
21
  import { readCachedOAuthUsageSnapshot } from '../runtime/agent/orchestrator/providers/oauth-usage.mjs';
22
22
  import { readCachedOpenCodeGoUsageSnapshot } from '../runtime/agent/orchestrator/providers/opencode-go-usage.mjs';
@@ -61,11 +61,11 @@ const L2_SPINNER_FRAME_MS = 120;
61
61
  // replaces them. Snapshots from a previous launch stay hidden during boot so the
62
62
  // statusline starts empty until the current session captures usage once.
63
63
  const STATUSLINE_PROCESS_STARTED_AT_MS = Date.now() - Math.floor((Number(process.uptime?.()) || 0) * 1000);
64
- const DEFAULT_HIDDEN_STATUSLINE_ROLES = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
64
+ const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
65
65
  let _shellJobsSegmentCache = { ownerPid: 0, at: 0, value: { count: 0, elapsedLabel: '' } };
66
66
  let _gatewayQuotaStatusCache = { key: '', at: 0, value: null };
67
67
  let _fallbackQuotaStatusCache = { key: '', at: 0, value: null };
68
- let _hiddenStatuslineRoles = null;
68
+ let _hiddenStatuslineAgents = null;
69
69
  // Option A boot gate: the L1 usage/quota segment stays fully empty until THIS
70
70
  // process has captured its FIRST confirmed (current-process) OAuth usage
71
71
  // snapshot for THAT provider. The latch is monotonic PER PROVIDER — once a
@@ -211,7 +211,7 @@ function resolveContextUsedPct({
211
211
  }
212
212
 
213
213
  function normalizeAgentWorkerForStatusline(worker = {}) {
214
- const tag = String(worker.tag || worker.role || worker.name || '').trim();
214
+ const tag = String(worker.tag || worker.agent || worker.name || '').trim();
215
215
  if (!tag) return null;
216
216
  const statusText = String(worker.stage || worker.status || '').toLowerCase();
217
217
  const status = isTerminalBridgeStatus(statusText) ? 'idle' : 'running';
@@ -219,7 +219,7 @@ function normalizeAgentWorkerForStatusline(worker = {}) {
219
219
  tag,
220
220
  status,
221
221
  startedAtMs: timeMs(worker.startedAt || worker.startTime || worker.createdAt),
222
- role: worker.role || null,
222
+ agent: worker.agent || null,
223
223
  stage: worker.stage || worker.status || null,
224
224
  sessionId: worker.sessionId || null,
225
225
  provider: worker.provider || null,
@@ -231,7 +231,7 @@ function normalizeAgentJobForStatusline(job = {}) {
231
231
  const statusText = String(job.status || job.stage || '').toLowerCase();
232
232
  if (!statusText) return null;
233
233
  const taskId = String(job.task_id || job.taskId || '').trim();
234
- const tag = String(job.tag || job.role || job.type || taskId || '').trim();
234
+ const tag = String(job.tag || job.agent || job.type || taskId || '').trim();
235
235
  if (!tag && !taskId) return null;
236
236
  const startedAtMs = timeMs(job.startedAt);
237
237
  const finishedAtMs = timeMs(job.finishedAt);
@@ -243,7 +243,7 @@ function normalizeAgentJobForStatusline(job = {}) {
243
243
  finalStatus: statusText,
244
244
  startedAtMs,
245
245
  finishedAtMs,
246
- role: job.role || null,
246
+ agent: job.agent || null,
247
247
  stage: job.stage || job.workerStatus || job.status || null,
248
248
  sessionId: job.sessionId || null,
249
249
  provider: job.provider || null,
@@ -256,7 +256,7 @@ function normalizeAgentJobForStatusline(job = {}) {
256
256
  taskId,
257
257
  status: 'running',
258
258
  startedAtMs,
259
- role: job.role || null,
259
+ agent: job.agent || null,
260
260
  stage: job.stage || job.workerStatus || job.status || null,
261
261
  sessionId: job.sessionId || null,
262
262
  provider: job.provider || null,
@@ -387,10 +387,9 @@ function renderNativeStatusline({
387
387
 
388
388
  const agentPayload = agentStatuslinePayload([
389
389
  ...(Array.isArray(agentWorkers) ? agentWorkers : []),
390
- ...activeHiddenRoleWorkers({ sessionId, clientHostPid }),
390
+ ...activeHiddenAgentWorkers({ sessionId, clientHostPid }),
391
391
  ], agentJobs);
392
- const { maintenance, runningWorkers } = classifyAgentWorkers(agentPayload.workers);
393
- if (maintenance.length) addL1(maintenance.join(' '));
392
+ const { runningWorkers } = classifyAgentWorkers(agentPayload.workers);
394
393
  const shellStatus = shellJobsStatus({ clientHostPid });
395
394
 
396
395
  const spinnerNow = Date.now();
@@ -426,6 +425,16 @@ function renderNativeStatusline({
426
425
  const label = `Running ${n} Shell${n === 1 ? '' : 's'}`;
427
426
  addL2(`${spin} ${B}${label}${R}${elapsedSuffix(shellStatus.elapsedLabel)}`);
428
427
  }
428
+ // Memory cycle segment — single unified "Memory" wording for all states:
429
+ // running -> "⠋ Memory · 12s"; backlog warning -> "⠋ Memory · backlog 512"
430
+ // (yellow). Nothing when idle (no L2 pollution).
431
+ const memStatus = memoryCycleStatus();
432
+ if (memStatus?.kind === 'running') {
433
+ const elapsed = formatElapsed(Date.now() - memStatus.startedAt);
434
+ addL2(`${spin} ${B}Memory${R}${elapsedSuffix(elapsed)}`);
435
+ } else if (memStatus?.kind === 'backlog') {
436
+ addL2(`${spin} ${B}Memory${R} ${D}·${R} ${YLW}backlog ${memStatus.pending}${R}`);
437
+ }
429
438
  const l1 = l1Parts.join(sep) || 'mixdog';
430
439
  const l2 = l2Parts.join(sep);
431
440
  return l2 ? `${l1}\n${l2}` : l1;
@@ -688,30 +697,30 @@ function classifyAgentWorkers(workers = []) {
688
697
  }
689
698
 
690
699
  function hiddenWorkerLabel(worker = {}) {
691
- const role = String(worker?.role || '').trim();
700
+ const agent = String(worker?.agent || '').trim();
692
701
  const tag = String(worker?.tag || '').trim();
693
- return maintenanceLabel(role) || (!role ? maintenanceLabel(tag) : '');
702
+ return maintenanceLabel(agent) || (!agent ? maintenanceLabel(tag) : '');
694
703
  }
695
704
 
696
- function hiddenStatuslineRoles() {
697
- if (_hiddenStatuslineRoles) return _hiddenStatuslineRoles;
698
- const roles = new Set(DEFAULT_HIDDEN_STATUSLINE_ROLES);
705
+ function hiddenStatuslineAgents() {
706
+ if (_hiddenStatuslineAgents) return _hiddenStatuslineAgents;
707
+ const agents = new Set(DEFAULT_HIDDEN_STATUSLINE_AGENTS);
699
708
  try {
700
- for (const role of listHiddenRoleNames()) {
701
- const clean = String(role || '').trim();
702
- if (clean) roles.add(clean);
709
+ for (const agent of listHiddenAgentNames()) {
710
+ const clean = String(agent || '').trim();
711
+ if (clean) agents.add(clean);
703
712
  }
704
713
  } catch {}
705
- _hiddenStatuslineRoles = roles;
706
- return roles;
714
+ _hiddenStatuslineAgents = agents;
715
+ return agents;
707
716
  }
708
717
 
709
718
  function isActiveHiddenStatus(statusText) {
710
719
  return /^(connecting|requesting|streaming|tool_running|running)$/i.test(String(statusText || '').trim());
711
720
  }
712
721
 
713
- function activeHiddenRoleWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
714
- const roles = hiddenStatuslineRoles();
722
+ function activeHiddenAgentWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
723
+ const agents = hiddenStatuslineAgents();
715
724
  const ownerPid = positiveInt(clientHostPid);
716
725
  const ownerSessionId = String(sessionId || '').trim();
717
726
  const rows = [];
@@ -720,8 +729,8 @@ function activeHiddenRoleWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
720
729
  if (!entry || entry.closed === true) continue;
721
730
  const session = entry.session || null;
722
731
  if (!session || session.closed === true) continue;
723
- const role = String(session?.role || '').trim();
724
- if (!role || !roles.has(role)) continue;
732
+ const agent = String(session?.agent || '').trim();
733
+ if (!agent || !agents.has(agent)) continue;
725
734
  const id = session?.id || runtimeSessionId || null;
726
735
  if (ownerSessionId && id === ownerSessionId) continue;
727
736
  const sessionOwnerId = String(session?.ownerSessionId || '').trim();
@@ -732,8 +741,8 @@ function activeHiddenRoleWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
732
741
  const status = String(session?.status || stage || '').trim().toLowerCase();
733
742
  if (!isActiveHiddenStatus(stage || status)) continue;
734
743
  rows.push({
735
- tag: String(session?.agentTag || `${role}:${id || rows.length}`).trim(),
736
- role,
744
+ tag: String(session?.agentTag || `${agent}:${id || rows.length}`).trim(),
745
+ agent,
737
746
  status: 'running',
738
747
  stage: stage || status || 'running',
739
748
  sessionId: id,
@@ -768,6 +777,45 @@ function maintenanceLabel(tag) {
768
777
  }
769
778
 
770
779
  function shellJobsStatus({ clientHostPid } = {}) {
780
+ return _shellJobsStatusImpl({ clientHostPid });
781
+ }
782
+
783
+ // Memory cycle L2 segment source. The memory daemon writes
784
+ // data/memory-cycle-state.json on every cycle start/finish (index.mjs
785
+ // _writeCycleStateFile) — no HTTP call from the statusline path. Cached at
786
+ // the same 1s cadence as the shell segment. Shows a single unified "Memory"
787
+ // segment: running (spinner + elapsed) or backlog warning (yellow count).
788
+ let _memoryCycleSegmentCache = { at: 0, value: null };
789
+ const MEMORY_CYCLE_SEGMENT_CACHE_MS = 1000;
790
+ const MEMORY_CYCLE_BACKLOG_WARN = 500;
791
+ function memoryCycleStatus() {
792
+ const now = Date.now();
793
+ if (now - _memoryCycleSegmentCache.at < MEMORY_CYCLE_SEGMENT_CACHE_MS) {
794
+ return _memoryCycleSegmentCache.value;
795
+ }
796
+ let value = null;
797
+ try {
798
+ const p = join(dataDir(), 'memory-cycle-state.json');
799
+ if (existsSync(p)) {
800
+ const state = JSON.parse(readFileSync(p, 'utf-8'));
801
+ const running = state?.running || null;
802
+ const backlog = state?.backlog || {};
803
+ // Stale-file guard: a daemon that died mid-run leaves running set —
804
+ // ignore anything not refreshed in the last 10 minutes.
805
+ const fresh = Number(state?.updatedAt) > now - 10 * 60_000;
806
+ if (fresh && running?.cycle && Number(running.started_at) > 0) {
807
+ value = { kind: 'running', startedAt: Number(running.started_at) };
808
+ } else if (fresh) {
809
+ const pending = Math.max(Number(backlog?.unchunked) || 0, Number(backlog?.cycle2_pending) || 0);
810
+ if (pending > MEMORY_CYCLE_BACKLOG_WARN) value = { kind: 'backlog', pending };
811
+ }
812
+ }
813
+ } catch { value = null; }
814
+ _memoryCycleSegmentCache = { at: now, value };
815
+ return value;
816
+ }
817
+
818
+ function _shellJobsStatusImpl({ clientHostPid } = {}) {
771
819
  const ownerPid = positiveInt(clientHostPid);
772
820
  const empty = { count: 0, elapsedLabel: '' };
773
821
  if (!ownerPid) return empty;
@@ -11,6 +11,7 @@ import {
11
11
  readLatestGatewayHostRoute,
12
12
  readGatewaySessionRoute,
13
13
  } from '../src/gateway/session-routes.mjs';
14
+ import { compactBoundaryDenominator } from '../src/gateway/route-meta.mjs';
14
15
 
15
16
  function positiveInt(value) {
16
17
  const n = parseInt(String(value || ''), 10);
@@ -278,16 +279,8 @@ function resolveStatusAutoCompactTokenLimit(configured, active = {}, lastCompact
278
279
  export const _resolveStatusAutoCompactTokenLimit = resolveStatusAutoCompactTokenLimit;
279
280
 
280
281
  export function compactBoundaryForStatus(routeInfo = {}, compact = null) {
281
- const compactLimit = num(routeInfo?.autoCompactTokenLimit ?? compact?.compactLimitTokens, 0);
282
- const contextWindow = num(routeInfo?.contextWindow ?? compact?.contextWindow, 0);
283
- const budgetWindow = num(compact?.budgetWindow, 0);
284
- const rawContextWindow = num(routeInfo?.rawContextWindow ?? compact?.rawContextWindow, 0);
285
- if (compactLimit > 0 && contextWindow > 0) return Math.min(compactLimit, contextWindow);
286
- if (compactLimit > 0) return compactLimit;
287
- if (budgetWindow > 0 && contextWindow > 0) return Math.min(budgetWindow, contextWindow);
288
- if (budgetWindow > 0) return budgetWindow;
289
- if (contextWindow > 0) return contextWindow;
290
- return rawContextWindow > 0 ? rawContextWindow : null;
282
+ const n = compactBoundaryDenominator(routeInfo, compact);
283
+ return n > 0 ? n : null;
291
284
  }
292
285
 
293
286
  function sessionIdFromTranscriptPath(transcriptPath) {
@@ -378,8 +371,8 @@ function displayForModel(provider, model, info) {
378
371
  const display = cleanString(info?.display) || cleanString(info?.displayName) || cleanString(info?.name);
379
372
  if (display) return display;
380
373
  if (provider === 'anthropic-oauth') {
381
- const m = String(model || '').match(/^claude-(opus|sonnet|haiku)-(\d+)-(\d+)/i);
382
- if (m) return `${m[1][0].toUpperCase()}${m[1].slice(1).toLowerCase()} ${m[2]}.${m[3]}`;
374
+ const m = String(model || '').match(/^claude-(opus|sonnet|haiku|fable)-(\d+)(?:-(\d+))?/i);
375
+ if (m) return `${m[1][0].toUpperCase()}${m[1].slice(1).toLowerCase()} ${m[2]}${m[3] ? `.${m[3]}` : ''}`;
383
376
  }
384
377
  const raw = cleanString(model);
385
378
  const gpt = raw.match(/^gpt[-_](.+)$/i);
@@ -119,7 +119,7 @@ export function resolveClaudeModelAlias(model) {
119
119
  if (!raw) return null;
120
120
  const stripped = stripClaudeContextSuffix(raw);
121
121
  if (/^claude-/i.test(stripped)) return stripped;
122
- const family = stripped.match(/^(opus|sonnet|haiku)(?:[-_].*)?$/i)?.[1]?.toLowerCase();
122
+ const family = stripped.match(/^(opus|sonnet|haiku|fable)(?:[-_].*)?$/i)?.[1]?.toLowerCase();
123
123
  if (family) return familyDefaultModel(family);
124
124
  return null;
125
125
  }
@@ -128,11 +128,11 @@ export function displayClaudeModel(model, raw = '', display = '') {
128
128
  const explicit = cleanString(display);
129
129
  if (explicit) return explicit;
130
130
  const id = cleanString(model) || resolveClaudeModelAlias(raw) || cleanString(raw);
131
- const m = id.match(/^claude-(opus|sonnet|haiku)-(\d+)-(\d+)(?:-\d{8})?$/i);
131
+ const m = id.match(/^claude-(opus|sonnet|haiku|fable)-(\d+)(?:-(\d+))?(?:-\d{8})?$/i);
132
132
  if (m) {
133
133
  const family = `${m[1][0].toUpperCase()}${m[1].slice(1).toLowerCase()}`;
134
134
  const suffix = /\[1m\]/i.test(raw) || contextWindowFromRawModel(raw, id) === 1000000 ? ' (1M context)' : '';
135
- return `${family} ${m[2]}.${m[3]}${suffix}`;
135
+ return `${family} ${m[2]}${m[3] ? `.${m[3]}` : ''}${suffix}`;
136
136
  }
137
137
  return id || raw || '';
138
138
  }
@@ -254,8 +254,8 @@ function displayForModel(provider, model, info) {
254
254
  const display = cleanString(info?.display) || cleanString(info?.displayName) || cleanString(info?.name);
255
255
  if (display) return display;
256
256
  if (provider === 'anthropic-oauth') {
257
- const m = String(model || '').match(/^claude-(opus|sonnet|haiku)-(\d+)-(\d+)/i);
258
- if (m) return `${m[1][0].toUpperCase()}${m[1].slice(1).toLowerCase()} ${m[2]}.${m[3]}`;
257
+ const m = String(model || '').match(/^claude-(opus|sonnet|haiku|fable)-(\d+)(?:-(\d+))?/i);
258
+ if (m) return `${m[1][0].toUpperCase()}${m[1].slice(1).toLowerCase()} ${m[2]}${m[3] ? `.${m[3]}` : ''}`;
259
259
  }
260
260
  return model || '';
261
261
  }
@@ -343,25 +343,44 @@ export function readGatewayRouteInfo(seed = {}, providerObj = null) {
343
343
  };
344
344
  }
345
345
 
346
- function compactBoundaryForRoute(routeInfo = {}, compact = null) {
347
- const compactLimit = num(routeInfo?.autoCompactTokenLimit ?? compact?.compactLimitTokens, 0);
346
+ // Boundary denominator for status %, overflow checks, and gateway env window.
347
+ // Order: explicit boundary (routeInfo or compact) → contextWindow → budgetWindow
348
+ // → rawContextWindow. autoCompactTokenLimit / compactLimitTokens are triggers
349
+ // only and are never read here.
350
+ export function compactBoundaryDenominator(routeInfo = {}, compact = null) {
351
+ const fromRoute = num(
352
+ routeInfo?.compactBoundaryTokens
353
+ ?? routeInfo?.compact_boundary_tokens
354
+ ?? routeInfo?.boundaryTokens,
355
+ 0,
356
+ );
357
+ const fromCompact = num(
358
+ compact?.boundaryTokens
359
+ ?? compact?.compactBoundaryTokens
360
+ ?? compact?.compact_boundary_tokens,
361
+ 0,
362
+ );
363
+ const boundaryTokens = fromRoute > 0 ? fromRoute : fromCompact;
348
364
  const contextWindow = num(routeInfo?.contextWindow ?? compact?.contextWindow, 0);
349
365
  const budgetWindow = num(compact?.budgetWindow, 0);
350
366
  const rawContextWindow = num(routeInfo?.rawContextWindow ?? compact?.rawContextWindow, 0);
351
- if (compactLimit > 0 && contextWindow > 0) return Math.min(compactLimit, contextWindow);
352
- if (compactLimit > 0) return compactLimit;
353
- if (budgetWindow > 0 && contextWindow > 0) return Math.min(budgetWindow, contextWindow);
354
- if (budgetWindow > 0) return budgetWindow;
367
+ if (boundaryTokens > 0) return boundaryTokens;
355
368
  if (contextWindow > 0) return contextWindow;
369
+ if (budgetWindow > 0) return budgetWindow;
356
370
  return rawContextWindow > 0 ? rawContextWindow : 0;
357
371
  }
358
372
 
373
+ function compactBoundaryForRoute(routeInfo = {}, compact = null) {
374
+ return compactBoundaryDenominator(routeInfo, compact);
375
+ }
376
+
359
377
  // Single source of truth for the value synced to host env
360
378
  // CLAUDE_CODE_AUTO_COMPACT_WINDOW. BOTH the --enable-time write
361
379
  // (scripts/gateway-model.mjs) and the runtime sync (src/gateway/server.mjs)
362
380
  // must derive the window the SAME way, or they disagree on the value written to
363
- // settings.json. Prefer the explicit compact limit, bounded by the effective
364
- // context window, then fall back to context/raw metadata. Returns a positive
381
+ // settings.json. Denominator order: compactBoundaryTokens / boundaryTokens (route
382
+ // or compact) contextWindow budgetWindow rawContextWindow. Never
383
+ // autoCompactTokenLimit or compactLimitTokens (trigger-only). Returns a positive
365
384
  // integer, or null when unknown.
366
385
  export function autoCompactWindowForRoute(routeInfo) {
367
386
  const n = compactBoundaryForRoute(routeInfo);
@@ -428,12 +447,7 @@ function usageCostUsd(routeInfo, usage) {
428
447
  }
429
448
 
430
449
  function contextUsageBoundary(routeInfo, compact = null) {
431
- const contextWindow = num(routeInfo?.contextWindow ?? compact?.contextWindow, 0);
432
- const budgetWindow = num(compact?.budgetWindow, 0);
433
- const rawContextWindow = num(routeInfo?.rawContextWindow ?? compact?.rawContextWindow, 0);
434
- if (contextWindow > 0) return contextWindow;
435
- if (budgetWindow > 0) return budgetWindow;
436
- return rawContextWindow > 0 ? rawContextWindow : compactBoundaryForRoute(routeInfo, compact);
450
+ return compactBoundaryDenominator(routeInfo, compact);
437
451
  }
438
452
 
439
453
  export function summarizeGatewayUsage(routeInfo, providerOut, compact = null, durationMs = null) {
@@ -1,14 +1,48 @@
1
+ ---
2
+ id: default
3
+ name: Default
4
+ description: "Default agent workflow — fan-out parallel delegation across independent scopes."
5
+ agents: worker, heavy-worker, reviewer, debugger, maintainer
6
+ ---
7
+
1
8
  # Default Workflow
2
9
 
3
- 1. Plan — Lead discusses the request with the user, forms a plan, and waits for
4
- approval.
5
- 2. Delegate split the work into the maximum number of independent scopes.
6
- Spawn every scope in the same turn. Never merge separable scopes into one
7
- agent. Shared functions or cross-cutting concerns do not justify merging;
8
- split per path and verify the shared parts yourself.
9
- 3. Review fan out one review scope per implementation scope, in the same turn.
10
- Fact-check implementation and review results yourself. Send fixes back to the
11
- original scope and repeat until clean. Skip delegated review only for simple,
12
- low-risk tasks.
13
- 4. Reportintegrate the results, report the final state, and ask the user
14
- whether to ship/deploy when relevant.
10
+ Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
11
+ - Lead handles directly: simple 1–2 step work, plus coordination, pre-planning,
12
+ config changes, and final git deployment.
13
+ - Worker: implementation that takes several steps.
14
+ - Heavy Worker: high-complexity, multi-step implementation.
15
+ - Reviewer: verify implementation scopes (diff, regressions, missing checks).
16
+ - Debugger: deploy for very high complexity, or when root-causing has already
17
+ failed at least once.
18
+
19
+ 1. Plan — discuss the request with the user, form a plan, and wait for approval.
20
+ 2. Delegatesplit into the maximum number of independent scopes.
21
+ - PARALLEL across independent scopes by default (implementation, analysis,
22
+ review, debugging split alike). Spawn every scope in the SAME turn. Shared
23
+ or cross-cutting code does NOT justify merging: split per path and verify
24
+ the shared parts yourself. The only single scope is a genuinely
25
+ inseparable dependency — then state it.
26
+ - SEQUENTIAL within a single complex scope — split it into ordered steps
27
+ rather than handing it off in one shot, with a build/test-green gate
28
+ between steps.
29
+ - Write briefs per the lead-tool brief contract (token-optimized labeled
30
+ fragments).
31
+ - After spawning async agent(s), END THE TURN — do not poll, guess, or start
32
+ dependent work until the completion notification resumes you. Then wait
33
+ and continue automatically.
34
+ 3. Review — pair one reviewer 1:1 with each implementation scope, spawned in the
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.
42
+ 4. Report — deliver the final report: synthesize results (outcome + key
43
+ evidence; never forward raw agent output), state the final state, and ask the
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.
@@ -0,0 +1,51 @@
1
+ ---
2
+ id: sequential
3
+ name: Sequential
4
+ description: "Sequential workflow — delegates 1:1 with no fan-out; one agent at a time."
5
+ agents: worker, heavy-worker, reviewer, debugger, maintainer
6
+ ---
7
+
8
+ # Sequential Workflow
9
+
10
+ Unlike Default, which fans out independent scopes in parallel, this mode runs
11
+ with no fan-out: it delegates one scope at a time, 1:1, and only moves to the
12
+ next after the current one completes.
13
+
14
+ Lead supervises: delegates, coordinates, judges, decides. Route by complexity:
15
+ - Lead handles directly: simple 1–2 step work, plus coordination, pre-planning,
16
+ config changes, and final git deployment.
17
+ - Worker: implementation that takes several steps.
18
+ - Heavy Worker: high-complexity, multi-step implementation.
19
+ - Reviewer: verify implementation scopes (diff, regressions, missing checks).
20
+ - Debugger: deploy for very high complexity, or when root-causing has already
21
+ failed at least once.
22
+
23
+ 1. Plan — discuss the request with the user, form a plan, and wait for approval.
24
+ 2. Delegate — split into ordered scopes and hand them off ONE AT A TIME.
25
+ - NO PARALLEL, NO FAN-OUT. Even when scopes are independent, spawn exactly
26
+ one agent per turn; never spawn multiple agents in the same turn. Wait for
27
+ the current scope's completion notification before spawning the next scope
28
+ (1:1 sequential).
29
+ - SEQUENTIAL within a single complex scope — split it into ordered steps
30
+ rather than handing it off in one shot, with a build/test-green gate
31
+ between steps.
32
+ - Write briefs per the lead-tool brief contract (token-optimized labeled
33
+ fragments).
34
+ - After spawning an async agent, END THE TURN — do not poll, guess, or start
35
+ dependent or subsequent work until the completion notification resumes
36
+ you. Then wait and continue automatically.
37
+ 3. Review — after each implementation scope completes, pair one reviewer 1:1
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.
45
+ 4. Report — deliver the final report: synthesize results (outcome + key
46
+ evidence; never forward raw agent output), state the final state, and ask the
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.
@@ -1,3 +1,10 @@
1
+ ---
2
+ id: solo
3
+ name: Solo
4
+ description: "Solo workflow — Lead handles everything directly; delegation forbidden."
5
+ agents:
6
+ ---
7
+
1
8
  # Solo Workflow
2
9
 
3
10
  1. Plan — Lead discusses the request with the user, forms a plan, and waits for
@@ -7,7 +14,11 @@
7
14
  3. Verify — Lead checks the result directly, fixes issues directly, and repeats
8
15
  until the work is clean or a blocker must be reported.
9
16
  4. Report — Lead summarizes the final state, verification result, and any
10
- 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.
11
22
 
12
23
  Delegation rule:
13
24
  - Do not delegate, spawn, send, or ask any agent to perform work.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * display-width.js — [mixdog fork] vendored copy of the shared display-width
3
+ * policy from src/tui/display-width.mjs.
4
+ *
5
+ * MUST STAY IN SYNC with src/tui/display-width.mjs. ink is aliased as an
6
+ * external bundle (see scripts/build-tui.mjs), so it cannot cleanly import from
7
+ * src; the small policy is replicated here verbatim instead. If the problem
8
+ * ranges or the WT_SESSION/MIXDOG_TUI_AMBIGUOUS_WIDE gate change there, change
9
+ * them here too — ink's MEASUREMENT must agree with OUR wrap/row math or the
10
+ * overlap gets worse, not better.
11
+ *
12
+ * Policy: widen ONLY U+2460–U+24FF (enclosed alphanumerics / circled digits)
13
+ * and U+2190–U+21FF (arrows) to 2 cells when ON. NEVER widen box-drawing
14
+ * (U+2500–U+257F), block elements, or other ambiguous glyphs. ON by default
15
+ * only under Windows Terminal (WT_SESSION); MIXDOG_TUI_AMBIGUOUS_WIDE='1'/'0'
16
+ * overrides and wins. OFF ⇒ identical to plain string-width.
17
+ */
18
+ import stringWidth from 'string-width';
19
+
20
+ function isProblemCodePoint(cp) {
21
+ return (cp >= 0x2460 && cp <= 0x24ff) || (cp >= 0x2190 && cp <= 0x21ff);
22
+ }
23
+
24
+ function resolveAmbiguousWidePolicy(env = process.env) {
25
+ const override = env?.MIXDOG_TUI_AMBIGUOUS_WIDE;
26
+ if (override === '1')
27
+ return true;
28
+ if (override === '0')
29
+ return false;
30
+ return Boolean(env?.WT_SESSION);
31
+ }
32
+
33
+ // Resolved once at module load (matches src/tui/display-width.mjs).
34
+ export const AMBIGUOUS_WIDE = resolveAmbiguousWidePolicy();
35
+
36
+ export function displayWidthWith(str, wide) {
37
+ const s = String(str ?? '');
38
+ const base = stringWidth(s);
39
+ if (!wide)
40
+ return base;
41
+ let extra = 0;
42
+ for (const ch of s) {
43
+ const cp = ch.codePointAt(0);
44
+ if (isProblemCodePoint(cp) && stringWidth(ch) === 1)
45
+ extra += 1;
46
+ }
47
+ return base + extra;
48
+ }
49
+
50
+ /** Visible display width under the resolved policy (ink-side stringWidth). */
51
+ export function displayStringWidth(str) {
52
+ return displayWidthWith(str, AMBIGUOUS_WIDE);
53
+ }
54
+
55
+ /** widest-line equivalent under the policy. */
56
+ export function displayWidestLine(text) {
57
+ let lineWidth = 0;
58
+ for (const line of String(text ?? '').split('\n')) {
59
+ lineWidth = Math.max(lineWidth, displayStringWidth(line));
60
+ }
61
+ return lineWidth;
62
+ }