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
@@ -1,22 +1,27 @@
1
1
  ---
2
2
  name: default
3
- title: Summary
3
+ title: Default
4
4
  description: Concise engineering summaries
5
5
  keep-coding-instructions: true
6
6
  ---
7
7
 
8
8
  # Output Style
9
9
 
10
- Mixdog default — concise engineering summaries.
10
+ Mixdog default — the most detailed of the three styles, but only as long as the
11
+ task warrants.
11
12
 
12
- - Lead with the outcome plus a single decisive piece of evidence: a path,
13
- command, error, or verification result. Drop everything non-essential.
13
+ - Lead with the outcome, then add the supporting detail that matters: what
14
+ changed, key evidence (paths, commands, errors, verification), and important
15
+ context. Include trade-offs or follow-up only when they actually matter.
16
+ - Use a few bullets or short paragraphs when they add signal; this style may run
17
+ longer than Simple, but match length to the work — do not pad a small change
18
+ into a full report. Cut anything that does not earn its place.
14
19
  - Use labels such as `바뀐 점`, `확인한 것`, and `남은 리스크/다음 단계`
15
- only in final reports; do not add titles/labels to interim progress text.
16
- - Collapse trivial tasks to a single short sentence.
20
+ in final reports to structure the summary; skip labels on interim progress.
21
+ - Collapse trivial tasks to a couple of sentences instead of forcing sections.
17
22
  - Synthesize agent or retrieval results; never forward raw reports, long file
18
23
  lists, tool traces, or session metadata.
19
24
  - Do not hide blockers, failed verification, or required follow-up; surface them
20
- in one short clause rather than omitting them.
25
+ explicitly rather than omitting them.
21
26
  - Keep paths, commands, symbols, API names, code, and exact errors verbatim.
22
27
  - Never name this style unless asked.
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: minimal
3
+ title: Minimal
4
+ description: One- or two-sentence summary
5
+ aliases: extreme, extreme-simple
6
+ keep-coding-instructions: true
7
+ ---
8
+
9
+ # Output Style
10
+
11
+ Minimal — a very short summary: one or two sentences, nothing more.
12
+
13
+ - Summarize only the net result in one short sentence; add a second short
14
+ sentence only if a second fact (verification, blocker) genuinely needs it.
15
+ Never cram unrelated facts into one run-on line just to stay at one sentence.
16
+ - Summarize, never itemize: do not describe which files changed or how they were
17
+ edited. State only what the change accomplishes.
18
+ - No headings, bullets, numbered lists, labels, or sections — plain sentences
19
+ only. This holds even when the request says "report" or "summary"; keep it to
20
+ one or two sentences regardless.
21
+ - Preferred pattern: `<target> 변경되었습니다. <verification> 통과 완료입니다.`
22
+ - If verification was not run, say the change is done and verification was not
23
+ run.
24
+ - Preserve only the single decisive path, command, symbol, API name, code, or
25
+ error verbatim.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: oneline
3
+ title: Oneline
4
+ description: Single sentence under 100 characters
5
+ aliases: one-line, one line, mono
6
+ keep-coding-instructions: true
7
+ ---
8
+
9
+ # Output Style
10
+
11
+ Oneline — the most minimal style: exactly one sentence, under 100 characters.
12
+
13
+ - Reply with a SINGLE sentence, always under 100 characters. Never a second
14
+ sentence, clause pile-up, or run-on that smuggles in extra facts.
15
+ - State only the net result. Drop file lists, how-it-was-done, verification
16
+ detail, and follow-ups unless one is the single most decisive fact.
17
+ - No headings, bullets, numbered lists, labels, or sections — one plain sentence
18
+ only, even when the request says "report" or "summary".
19
+ - Preferred pattern: `<target> 변경 완료.`
20
+ - Preserve only the single decisive path, command, symbol, or error verbatim,
21
+ and only if it fits the limit.
@@ -1,22 +1,23 @@
1
1
  ---
2
2
  name: simple
3
- title: Practical Concise
3
+ title: Simple
4
4
  description: Outcome-first concise handoffs for coding work
5
- aliases: summary, concise, handoff
5
+ aliases: concise, handoff
6
6
  keep-coding-instructions: true
7
7
  ---
8
8
 
9
9
  # Output Style
10
10
 
11
- Practical concise — outcome-first handoffs for coding work without report bloat.
11
+ Practical concise — outcome-first handoffs for coding work: summarize the result,
12
+ do not narrate the change.
12
13
 
13
14
  - Open with the outcome in one sentence: done, blocked, or awaiting a decision.
14
- - Add only high-signal evidence next: changed paths (use `file_path:line_number`
15
- when it helps navigation), commands, lint/test results, or the exact error —
16
- not step-by-step narration.
17
- - Keep controlled detail: usually 1–3 short bullets or 2–4 sentences total.
18
- Expand only when the user asks, scope is ambiguous, or a blocker needs concrete
19
- next steps.
15
+ - Summarize what the change accomplishes rather than listing every file and how
16
+ each was edited. Name a path (`file_path:line_number`) only when the reader
17
+ truly needs it to navigate — not as a per-file changelog.
18
+ - Keep controlled detail: usually 1–3 short bullets or 2–4 sentences total. No
19
+ step-by-step narration, no exhaustive file/line inventory. Expand only when the
20
+ user asks, scope is ambiguous, or a blocker needs concrete next steps.
20
21
  - On final handoffs, optional labels such as `바뀐 점`, `확인한 것`, and
21
22
  `남은 리스크/다음 단계` fit Korean-facing profiles; use plain English labels
22
23
  when the thread is English. Do not label interim progress.
package/src/repl.mjs CHANGED
@@ -1,16 +1,16 @@
1
1
  /**
2
- * mixdog REPL — styled inline terminal loop over the ported mixdog brain.
2
+ * mixdog REPL — styled inline terminal loop over the mixdog session runtime.
3
3
  *
4
- * Drives the *ported mixdog brain* through mixdog-session-runtime.mjs:
4
+ * Drives the session runtime through mixdog-session-runtime.mjs:
5
5
  * createSession + askSession own agentLoop/provider/tools/compaction, while
6
6
  * this module stays presentation-only: markdown-rendered replies, tool-call
7
7
  * cards, a per-turn statusline footer, slash commands, and arrow-key history.
8
8
  *
9
9
  * Flow: stdin line → runtime.ask(prompt) → onTextDelta streams tokens to
10
- * stdout live → tool calls render as cards → on turn end we re-render
11
- * the assistant text as markdown → statusline footer.
10
+ * stdout live → tool calls render as cards → on turn end we re-render
11
+ * the assistant text as markdown → statusline footer.
12
12
  *
13
- * STREAMING DECISION (approach (a) from the brief):
13
+ * STREAMING DECISION:
14
14
  * Live token streaming via onTextDelta conflicts with post-hoc markdown
15
15
  * rendering (you can't style a heading until you've seen the whole line).
16
16
  * We choose: stream raw tokens live so the turn FEELS alive, then on turn
@@ -160,13 +160,17 @@ export async function runRepl({ provider: providerName, model, toolMode = 'full'
160
160
 
161
161
  let streamedText = '';
162
162
  let printedAny = false;
163
+ let printedToolCard = false;
163
164
  try {
164
165
  const runtime = await ensureRuntime();
165
166
  const { result } = await runtime.ask(
166
167
  line,
167
168
  {
168
169
  onToolCall: async (_iter, calls) => {
169
- for (const c of calls || []) stdout.write('\n' + (await renderToolCardLazy(c)) + '\n');
170
+ for (const c of calls || []) {
171
+ printedToolCard = true;
172
+ stdout.write('\n' + (await renderToolCardLazy(c)) + '\n');
173
+ }
170
174
  },
171
175
  onTextDelta: (chunk) => {
172
176
  printedAny = true;
@@ -184,12 +188,18 @@ export async function runRepl({ provider: providerName, model, toolMode = 'full'
184
188
  // raw text already on screen is fine (and we must not emit cursor escapes
185
189
  // into a pipe).
186
190
  if (finalText) {
187
- if (printedAny && colorEnabled()) {
191
+ if (printedAny && colorEnabled() && !printedToolCard) {
188
192
  eraseStreamedBlock(streamedText);
189
193
  stdout.write(await renderMarkdownLazy(finalText) + '\n');
190
194
  } else if (!printedAny) {
191
195
  // Nothing streamed live (provider without onTextDelta) — render once.
192
196
  stdout.write(await renderMarkdownLazy(finalText) + '\n');
197
+ } else if (printedToolCard) {
198
+ // Tool cards are printed after the streamed text. Erasing only the
199
+ // streamed text from the current cursor position would clear/move
200
+ // through the card rows and make the terminal scroll jump. Keep the
201
+ // live transcript as-is for mixed text+tool turns.
202
+ stdout.write('\n');
193
203
  } else {
194
204
  // Non-TTY / NO_COLOR: leave the raw stream, just terminate the line.
195
205
  stdout.write('\n');
@@ -8,8 +8,10 @@
8
8
  text before tool calls.
9
9
  - If tools are needed, call them immediately. Emit text only for the final
10
10
  handoff after tool work is done.
11
- - Final output style: follow the role's stricter output contract when one is
12
- defined; otherwise write a compact English handoff for Lead: outcome, key
13
- files, verification, and only material risks. Avoid report-style headings,
14
- markdown tables unless explicitly requested, long narration, raw logs, and
15
- unnecessary next-checks.
11
+ - Final handoff: minimum characters, maximum information for Lead. Follow the
12
+ role's stricter output contract if defined; else emit fragments outcome
13
+ (1 line), key `file:line`(s), verification result, material risks (only if
14
+ any).
15
+ - Banned as pure cost: report headings, markdown tables (unless requested),
16
+ prose narration, raw logs/tool traces, speculative next-checks, restated
17
+ brief, articles/politeness.
@@ -6,17 +6,13 @@ kind: retrieval
6
6
 
7
7
  # Role: explorer
8
8
 
9
- Locator only. Find likely file/symbol/line anchors for the query; do not
10
- analyze, debug, decide, or recommend.
9
+ You are a one-shot locator, not a researcher.
11
10
 
12
- Output only:
13
- - `path:line symbol/name short reason`
14
- - or `EXPLORATION_FAILED`
15
-
16
- No preambles, including tool-call preambles. No bullets, headings, summaries,
17
- code quotes, verdicts, or invented coordinates; output anchor lines only.
18
- Prompt queries need exact function/prompt anchors. Weak anchors: `?`.
11
+ Procedure: send ONE turn containing one `grep` (all synonyms in one
12
+ `pattern:[...]`, output_mode content_with_context) plus one `code_graph`
13
+ call. The results contain path:line — answer immediately from them.
14
+ If both miss, answer `EXPLORATION_FAILED`. Do not send a second lookup turn.
19
15
 
20
- Work fast: maximize independent read-only fan-out in every lookup turn. Once
21
- credible anchors are found, you must stop; do not call another tool or
22
- broaden/verify.
16
+ Answer format, nothing else:
17
+ - up to 5 lines of `path:line symbol/name short reason` (append `?` if weak)
18
+ - or `EXPLORATION_FAILED`
@@ -3,7 +3,9 @@
3
3
  - Omit direct names, honorifics, headings, and labels in preambles.
4
4
  - Preambles are optional: use them only when they add user-visible value, keep
5
5
  them to one short sentence, and skip routine lookup narration.
6
- - When you emit a preamble or other user-facing lead-in, it MUST be in the user's configured response language (see Profile Preferences).
7
6
  - Destructive/hard-to-reverse actions require explicit confirmation.
8
7
  - Never push, build, or deploy without an explicit user request.
9
8
  Implementation approval is not deploy approval.
9
+ - Rather than deferring checks or work to the user, proactively handle whatever
10
+ you can first, and propose only the parts that need a decision in a
11
+ consultative tone ("Shall we proceed this way?").
@@ -1,6 +1,17 @@
1
1
  # Lead Tool Use
2
2
 
3
- - Lead owns repo-local shell work: git, build, test, run, and verification commands.
3
+ - Lead owns repo-local shell work: run git/build/test/verification commands via
4
+ `shell` directly; do not delegate them to agents.
4
5
  - Use the current project/workspace selected by the session. Only change the work project when the user asks for a different project or a tool call explicitly needs another project root.
5
- - Use `shell` directly for approved git/build/test/run work; do not delegate those commands to agents.
6
6
  - Use `agent` for scoped implementation, research, review, and debugging, not for git commit/push/stash or Ship.
7
+ - A `send` to a reaped/dead tag auto-respawns a FRESH session under the same
8
+ tag (result carries `respawned: true`). That worker has no prior session
9
+ context — treat the message as a cold brief and re-supply anchors
10
+ (`file:line`) on it or the next send.
11
+ - Briefs: minimum characters, maximum information. Fixed one-line fragment
12
+ fields — `Goal:` `Anchors:` `Allow/Forbid:` `Deliver:` `Verify:` (+`Stop:` for
13
+ heavy-worker). Omit role-known rules (git/preamble bans, output format),
14
+ background, motivation; non-actionable tokens are wasted cost.
15
+ - Bridge language is ALWAYS English: every brief, follow-up `send`, and
16
+ steering message to an agent is written in English regardless of the
17
+ user-facing language.
@@ -1,14 +1,25 @@
1
1
  # Tool Use
2
2
 
3
- - Run independent tool calls in the same turn. Start source lookup with one
4
- compact combined batch, not serial probes.
5
- - Route by clue: `explore` broad unknowns, `code_graph` symbols/deps/callers,
6
- `grep` exact text, `find` path/name clues, `glob` structure, `list` dirs,
7
- `read` known file windows.
8
- - Use available `explore` for broad/uncertain repo anchors; narrow queries.
9
- - Use available `search` / `web_fetch` only for web/current external info;
10
- available `recall` only for history.
11
- - Fan out read-only lookup calls. Batch known independent files with `read`.
12
- - If editing is available, put related multi-file edits in one `apply_patch`.
13
- Do not run state-changing edits or shell alongside other tool calls.
14
- - Use available `shell` only for git/build/test/run/verification, not browsing.
3
+ - Independent lookups MUST batch in one turn; serialize only when a call needs
4
+ a prior result. This includes edit loops: batch the post-edit verification
5
+ read with the next target's lookup instead of emitting single-call turns.
6
+ - Target validity comes first: symbols/callers/deps `code_graph`; exact text in
7
+ a verified scope → `grep`; unknown path/name → `find`; structure `glob`;
8
+ dirs `list`; verified file → `read`; broad unknown with no anchor →
9
+ `explore`. Never call `grep`/`read` on guessed paths.
10
+ - Concept normalization comes first: one concept gets one batched
11
+ `grep pattern:[...]` with `output_mode:"content_with_context"` (or one
12
+ `code_graph symbols[]`). Refine from returned paths; do not repeat equivalent
13
+ patterns or scopes.
14
+ - On miss/error, normalize the target once and switch tool; on a plausible hit,
15
+ stop searching and answer from the framed context.
16
+ - Lookup budget: retrieval exists to reach the NEXT action (edit, answer,
17
+ handoff), not to build certainty. One anchor is enough to act on; re-reading
18
+ or re-grepping an area you already saw this session is waste. When acting
19
+ and looking are both possible, act.
20
+ - Avoid read fragmentation: `read` uses `offset`/`limit` only. If you need 2+
21
+ spans from one or more known files, make one batched `read` call with
22
+ `{path,offset,limit}` region objects instead of serial reads. Adjacent spans
23
+ in the same file (within a few hundred lines) are ONE window, not repeated
24
+ reads.
25
+ - Don't mix `apply_patch` with shell or other state-changing calls in one turn.
@@ -9,7 +9,7 @@
9
9
  * The returned function uses the existing caller signature, so call sites
10
10
  * do not need changes:
11
11
  *
12
- * const llm = makeAgentDispatch({ role: 'maintenance', preset: 'haiku' });
12
+ * const llm = makeAgentDispatch({ agent: 'maintenance', preset: 'haiku' });
13
13
  * const text = await llm({ prompt });
14
14
  *
15
15
  * Internally it:
@@ -23,7 +23,7 @@
23
23
 
24
24
  import { loadConfig } from '../config.mjs';
25
25
  import { resolveRuntimeSpec } from '../config.mjs';
26
- import { getHiddenRole, resolveAgentSessionPermission } from '../internal-roles.mjs';
26
+ import { getHiddenAgent, resolveAgentSessionPermission } from '../internal-agents.mjs';
27
27
  import { prepareAgentSession } from './session-builder.mjs';
28
28
  import {
29
29
  askSession,
@@ -50,6 +50,41 @@ function applyBriefCap(text) {
50
50
  return `${head}\n\n... [TRUNCATED — full answer was ~${approxTokens} tokens / ${Math.round(text.length / 1024)} KB. Re-run with brief:false for the complete synthesis]`;
51
51
  }
52
52
 
53
+ function formatCompactElapsedSeconds(ms) {
54
+ const value = Math.max(0, Number(ms) || 0);
55
+ if (value <= 0) return '';
56
+ return `${Math.max(1, Math.ceil(value / 1000))}s`;
57
+ }
58
+
59
+ function agentCompactEventLabel(event = {}) {
60
+ const status = String(event.status || '').toLowerCase();
61
+ const reactive = String(event.trigger || '').toLowerCase() === 'reactive';
62
+ if (status === 'failed') return reactive ? 'Compact failed (overflow retry)' : 'Compact failed';
63
+ if (status === 'skipped') return 'Compact skipped';
64
+ if (status === 'no_change') return 'Compact checked';
65
+ return reactive ? 'Compact complete (overflow recovery)' : 'Compact complete';
66
+ }
67
+
68
+ function agentCompactEventDetail(event = {}) {
69
+ const parts = [];
70
+ const elapsed = formatCompactElapsedSeconds(Number(event.durationMs ?? event.elapsedMs ?? 0));
71
+ if (elapsed) parts.push(elapsed);
72
+ const type = String(event.compactType || event.type || '').trim();
73
+ if (type && type !== 'semantic') parts.push(type);
74
+ const trigger = String(event.trigger || '').toLowerCase();
75
+ if (trigger === 'reactive') parts.push('reactive');
76
+ else if (trigger === 'manual') parts.push('manual');
77
+ const before = Number(event.beforeTokens ?? event.pressureTokens ?? 0);
78
+ const after = Number(event.afterTokens ?? 0);
79
+ const fmtTok = (n) => {
80
+ const v = Number(n) || 0;
81
+ if (v >= 1000) return `${(v / 1000).toFixed(v >= 10_000 ? 0 : 1)}k`;
82
+ return `${Math.round(v)}`;
83
+ };
84
+ if (before > 0 && after > 0 && after !== before) parts.push(`${fmtTok(before)}→${fmtTok(after)}`);
85
+ return parts.join(' · ');
86
+ }
87
+
53
88
  // Unified-shard policy — most agent sessions (Pool B + Pool C) share the
54
89
  // same tool schema so BP_1 is bit-identical across roles and one provider-side
55
90
  // cache shard serves every caller. Per-role behaviour is steered by:
@@ -57,9 +92,10 @@ function applyBriefCap(text) {
57
92
  // rules/agent/<role>.md
58
93
  // 2. call-time guards (loop.mjs write-block + ai-wrapped-dispatch
59
94
  // recursion break)
60
- // Hidden-role exceptions are declarative: defaults/hidden-roles.json may set
95
+ // Hidden-agent exceptions are declarative: defaults/agents.json may set
61
96
  // toolSchemaProfile when first-turn routing quality is worth a separate tool
62
- // prefix. Standard profiles are none/read/full; legacy names stay as aliases.
97
+ // prefix. Standard profiles are none/read/full/read-write-search; legacy names
98
+ // stay as aliases.
63
99
  // See manager.mjs resolveSessionTools for the single source of truth;
64
100
  // agent visibility is declared via annotations.agentHidden on each tool def.
65
101
  const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
@@ -73,6 +109,17 @@ const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
73
109
  'grep',
74
110
  'read',
75
111
  ]),
112
+ 'read-write-search': Object.freeze([
113
+ 'code_graph',
114
+ 'find',
115
+ 'glob',
116
+ 'list',
117
+ 'grep',
118
+ 'read',
119
+ 'apply_patch',
120
+ 'search',
121
+ 'web_fetch',
122
+ ]),
76
123
  // Backward-compatible aliases for older hidden-role definitions.
77
124
  unified: null,
78
125
  'llm-only': Object.freeze([]),
@@ -95,7 +142,7 @@ export function resolveHiddenRoleSchemaAllowedTools(hidden) {
95
142
  if (Object.prototype.hasOwnProperty.call(HIDDEN_ROLE_TOOL_SCHEMA_PROFILES, profile)) {
96
143
  return HIDDEN_ROLE_TOOL_SCHEMA_PROFILES[profile];
97
144
  }
98
- process.stderr.write(`[agent-dispatch] unknown hidden-role toolSchemaProfile="${profile}" role="${hidden.name || 'unknown'}"; using full schema\n`);
145
+ process.stderr.write(`[agent-dispatch] unknown hidden-agent toolSchemaProfile="${profile}" agent="${hidden.agent || 'unknown'}"; using full schema\n`);
99
146
  return null;
100
147
  }
101
148
 
@@ -114,11 +161,11 @@ export function resolveHiddenRoleSchemaAllowedTools(hidden) {
114
161
  * Hidden roles read their slot from `maint[maintKey || slot]`; the cycle1/2/3
115
162
  * agents share one knob via the `maintKey: 'memory'` override.
116
163
  */
117
- export function resolveMaintenanceRoute({ preset, optsPreset, role, config: cfgIn = null }) {
164
+ export function resolveMaintenanceRoute({ preset, optsPreset, agent, config: cfgIn = null }) {
118
165
  if (preset) return preset;
119
166
  if (optsPreset) return optsPreset;
120
- if (!role) return null;
121
- const hidden = getHiddenRole(role);
167
+ if (!agent) return null;
168
+ const hidden = getHiddenAgent(agent);
122
169
  if (hidden) {
123
170
  try {
124
171
  const config = cfgIn || loadConfig({ secrets: false });
@@ -134,14 +181,14 @@ export function resolveMaintenanceRoute({ preset, optsPreset, role, config: cfgI
134
181
  export const resolvePresetName = resolveMaintenanceRoute;
135
182
 
136
183
  // A maintenance slot value is a direct route when it carries provider+model.
137
- function maintenanceRouteToPreset(routeOrName, role) {
184
+ function maintenanceRouteToPreset(routeOrName, agent) {
138
185
  if (!routeOrName || typeof routeOrName !== 'object') return null;
139
186
  const provider = String(routeOrName.provider || '').trim();
140
187
  const model = String(routeOrName.model || '').trim();
141
188
  if (!provider || !model) return null;
142
189
  const out = {
143
- id: `maint-${role}`,
144
- name: `MAINT ${String(role || '').toUpperCase()}`,
190
+ id: `maint-${agent}`,
191
+ name: `MAINT ${String(agent || '').toUpperCase()}`,
145
192
  type: 'agent',
146
193
  provider,
147
194
  model,
@@ -157,50 +204,50 @@ function maintenanceRouteToPreset(routeOrName, role) {
157
204
  * Build an agent-backed dispatch callback.
158
205
  *
159
206
  * @param {object} opts
160
- * @param {string} opts.role — REQUIRED; canonical role name (worker, cycle1-agent, scheduler-task, ...)
207
+ * @param {string} opts.agent — REQUIRED; canonical agent name (worker, cycle1-agent, scheduler-task, ...)
161
208
  * @param {string} [opts.taskType] — optional internal classification stamped on the session
162
- * @param {string} [opts.preset] — explicit preset override (bypasses role → preset lookup)
209
+ * @param {string} [opts.preset] — explicit preset override (bypasses agent → preset lookup)
163
210
  * @param {string} [opts.parentSessionId] — parent agent session for trace aggregation
164
211
  * @param {string|null} [opts.ownerSessionId] — owning Mixdog session for statusline isolation
165
212
  * @param {AbortSignal} [opts.parentSignal] — optional AbortSignal from the fan-out coordinator;
166
- * when aborted the agent role session's own controller is also aborted so the
213
+ * when aborted the agent session's own controller is also aborted so the
167
214
  * provider call tears down promptly (parent→child cascade).
168
215
  * @returns {(args: { prompt, preset?, sourceName? }) => Promise<string>}
169
216
  */
170
217
  export function makeAgentDispatch(opts = {}) {
171
- if (!opts.role || typeof opts.role !== 'string') {
172
- throw new Error('[agent-dispatch] opts.role is required');
218
+ if (!opts.agent || typeof opts.agent !== 'string') {
219
+ throw new Error('[agent-dispatch] opts.agent is required');
173
220
  }
174
- const role = opts.role;
221
+ const agent = opts.agent;
175
222
 
176
223
  return async function agentDispatch({ prompt, preset: presetArg, sourceName: sourceNameArg, parentSignal: callParentSignal, idleTimeoutMs: callIdleTimeoutMs }) {
177
224
  if (typeof prompt !== 'string' || !prompt) {
178
- throw new Error(`[agent-dispatch] prompt required for role "${role}"`);
225
+ throw new Error(`[agent-dispatch] prompt required for agent "${agent}"`);
179
226
  }
180
227
 
181
228
  const config = opts.config || loadConfig({ secrets: false });
182
229
  const routeOrName = resolveMaintenanceRoute({
183
230
  preset: presetArg,
184
231
  optsPreset: opts.preset,
185
- role,
232
+ agent,
186
233
  config,
187
234
  });
188
235
  if (!routeOrName) {
189
236
  throw new Error(
190
- `[agent-dispatch] maintenance route unresolved for role "${role}" `
237
+ `[agent-dispatch] maintenance route unresolved for agent "${agent}" `
191
238
  + `(preset="${presetArg || opts.preset || ''}")`,
192
239
  );
193
240
  }
194
241
  // Preferred path: a maintenance slot that stores its model directly
195
242
  // (route object). Legacy path: a slot still holding a preset NAME —
196
243
  // resolve it against config.presets for backward compatibility.
197
- let preset = maintenanceRouteToPreset(routeOrName, role);
244
+ let preset = maintenanceRouteToPreset(routeOrName, agent);
198
245
  if (!preset) {
199
246
  const legacyName = String(routeOrName || '').trim();
200
247
  preset = config.presets?.find((p) => p.id === legacyName || p.name === legacyName) || null;
201
248
  if (!preset) {
202
249
  throw new Error(
203
- `[agent-dispatch] maintenance route for role "${role}" is neither a `
250
+ `[agent-dispatch] maintenance route for agent "${agent}" is neither a `
204
251
  + `{provider,model} route nor a known preset name ("${legacyName}")`,
205
252
  );
206
253
  }
@@ -208,11 +255,11 @@ export function makeAgentDispatch(opts = {}) {
208
255
  // Stable label for traces / session metadata, derived from the resolved
209
256
  // preset object regardless of whether it came from a direct route or a
210
257
  // legacy preset name.
211
- const presetName = preset.id || preset.name || `maint-${role}`;
258
+ const presetName = preset.id || preset.name || `maint-${agent}`;
212
259
 
213
260
  const runtimeSpec = resolveRuntimeSpec(preset, {
214
261
  lane: 'agent',
215
- agentId: role,
262
+ agentId: agent,
216
263
  });
217
264
 
218
265
  // Callers (e.g. aiWrapped explore dispatch) may pass an explicit
@@ -232,12 +279,12 @@ export function makeAgentDispatch(opts = {}) {
232
279
  // Runtime permission enforcement was removed (every tool call is
233
280
  // trusted); schema profiles remain a routing-efficiency layer that
234
281
  // narrows the advertised tool list, not a runtime safety gate.
235
- const hidden = getHiddenRole(role);
282
+ const hidden = getHiddenAgent(agent);
236
283
  const isPoolC = Boolean(hidden);
237
284
  // Permission: read-declared hidden roles are locked in
238
285
  // resolveAgentSessionPermission (prepareAgentSession applies the same).
239
286
  const permission = resolveAgentSessionPermission(
240
- role,
287
+ agent,
241
288
  opts.permission ?? (isPoolC ? (hidden?.permission || 'read') : null),
242
289
  );
243
290
  // Pool C hidden-role instructions live in BP2 role-scoped context
@@ -252,7 +299,7 @@ export function makeAgentDispatch(opts = {}) {
252
299
  // layer (account-level), not the session level.
253
300
  const finalPrompt = prompt;
254
301
  const { session } = prepareAgentSession({
255
- role,
302
+ agent,
256
303
  presetName,
257
304
  preset,
258
305
  runtimeSpec,
@@ -273,7 +320,7 @@ export function makeAgentDispatch(opts = {}) {
273
320
  // count-only "tools=N" line.
274
321
  try {
275
322
  const _toolNames = (session.tools || []).map((t) => t?.name).filter(Boolean);
276
- process.stderr.write(`[agent-dispatch] role=${role} tool-list (${_toolNames.length}): ${_toolNames.join(',')}\n`);
323
+ process.stderr.write(`[agent-dispatch] agent=${agent} tool-list (${_toolNames.length}): ${_toolNames.join(',')}\n`);
277
324
  } catch { /* best-effort diagnostic */ }
278
325
 
279
326
  await updateSessionStatus(session.id, 'running');
@@ -297,7 +344,7 @@ export function makeAgentDispatch(opts = {}) {
297
344
  // - firstResponseTimeoutMs cuts quickly only when the model produces no
298
345
  // first stream/tool activity at all.
299
346
  // - idle/tool-running caps come from role stallCap (hidden roles) or env defaults.
300
- const _watchdogPolicy = resolveAgentWatchdogPolicy(role, {
347
+ const _watchdogPolicy = resolveAgentWatchdogPolicy(agent, {
301
348
  idleTimeoutMs: Number.isFinite(callIdleTimeoutMs)
302
349
  ? callIdleTimeoutMs
303
350
  : opts.idleTimeoutMs,
@@ -330,11 +377,22 @@ export function makeAgentDispatch(opts = {}) {
330
377
  : null;
331
378
  if (_idleTimer && typeof _idleTimer.unref === 'function') _idleTimer.unref();
332
379
  let terminalStatus = 'idle';
333
- process.stderr.write(`[agent-dispatch] role=${role} preset=${presetName} model=${preset.model} provider=${preset.provider} session=${session.id}\n`);
380
+ process.stderr.write(`[agent-dispatch] agent=${agent} preset=${presetName} model=${preset.model} provider=${preset.provider} session=${session.id}\n`);
334
381
  const _agentDispatchT0 = Date.now();
335
382
  try {
336
- const result = await askSession(session.id, finalPrompt, null, null, cwd);
337
- process.stderr.write(`[agent-dispatch] role=${role} session=${session.id} elapsed=${Date.now() - _agentDispatchT0}ms\n`);
383
+ const result = await askSession(session.id, finalPrompt, null, null, cwd, undefined, {
384
+ onCompactEvent: (event) => {
385
+ try {
386
+ const label = agentCompactEventLabel(event);
387
+ const detail = agentCompactEventDetail(event);
388
+ const suffix = detail ? ` (${detail})` : '';
389
+ process.stderr.write(
390
+ `[agent-dispatch] agent=${agent} session=${session.id} compact: ${label}${suffix}\n`,
391
+ );
392
+ } catch { /* best-effort compact visibility */ }
393
+ },
394
+ });
395
+ process.stderr.write(`[agent-dispatch] agent=${agent} session=${session.id} elapsed=${Date.now() - _agentDispatchT0}ms\n`);
338
396
  const raw = result?.content || '';
339
397
  // Brief cap. Agent role answers (explore/recall/search)
340
398
  // occasionally balloon to 8-10k token walls that then ride in the
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Agent loop ceiling — a single high runaway-guard shared by every session
3
+ * (Lead and delegated sub-agents alike). There are intentionally NO low
4
+ * per-agent caps: a worker/heavy-worker must be free to run as many tool +
5
+ * synthesis turns as the task needs. This ceiling exists ONLY to stop a truly
6
+ * runaway loop; it is not a task-length budget.
7
+ */
8
+
9
+ function envPositiveInt(name, fallback) {
10
+ const raw = process.env[name];
11
+ if (raw === undefined || raw === '') return fallback;
12
+ const n = Number(raw);
13
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
14
+ }
15
+
16
+ // Single runaway guard for ALL sessions. High by design; env-overridable only
17
+ // to raise/lower the safety ceiling, never used as a per-agent task budget.
18
+ export const LEAD_MAX_LOOP_ITERATIONS = envPositiveInt('MIXDOG_AGENT_MAX_LOOP', 200);
19
+
20
+ /**
21
+ * Resolve the hard cap used by agentLoop for this session.
22
+ *
23
+ * Order: explicit override → session-pinned value → shared runaway guard.
24
+ * No per-agent low caps are applied.
25
+ */
26
+ export function resolveSessionMaxLoopIterations(sessionRef, explicit) {
27
+ if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);
28
+ if (Number.isFinite(sessionRef?.maxLoopIterations) && sessionRef.maxLoopIterations > 0) {
29
+ return Math.floor(sessionRef.maxLoopIterations);
30
+ }
31
+ return LEAD_MAX_LOOP_ITERATIONS;
32
+ }