mixdog 0.9.0 → 0.9.2

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 (240) hide show
  1. package/package.json +10 -3
  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/background-task-meta-smoke.mjs +1 -1
  6. package/scripts/bench-run.mjs +262 -0
  7. package/scripts/compact-smoke.mjs +12 -0
  8. package/scripts/compact-trigger-migration-smoke.mjs +67 -1
  9. package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
  10. package/scripts/internal-comms-bench.mjs +727 -0
  11. package/scripts/internal-comms-smoke.mjs +75 -0
  12. package/scripts/lead-workflow-smoke.mjs +4 -4
  13. package/scripts/live-worker-smoke.mjs +9 -9
  14. package/scripts/output-style-bench.mjs +285 -0
  15. package/scripts/output-style-smoke.mjs +13 -10
  16. package/scripts/patch-replay.mjs +90 -0
  17. package/scripts/provider-stream-stall-test.mjs +276 -0
  18. package/scripts/provider-toolcall-test.mjs +599 -1
  19. package/scripts/routing-corpus.mjs +281 -0
  20. package/scripts/session-bench.mjs +1526 -0
  21. package/scripts/session-diag.mjs +595 -0
  22. package/scripts/session-ingest-smoke.mjs +2 -2
  23. package/scripts/task-bench.mjs +207 -0
  24. package/scripts/tool-failures.mjs +6 -6
  25. package/scripts/tool-smoke.mjs +306 -66
  26. package/scripts/toolcall-args-test.mjs +81 -0
  27. package/src/agents/debugger/AGENT.md +4 -4
  28. package/src/agents/heavy-worker/AGENT.md +4 -2
  29. package/src/agents/reviewer/AGENT.md +4 -4
  30. package/src/agents/worker/AGENT.md +4 -2
  31. package/src/app.mjs +10 -6
  32. package/src/defaults/{hidden-roles.json → agents.json} +7 -7
  33. package/src/examples/schedules/SCHEDULE.example.md +32 -0
  34. package/src/examples/webhooks/WEBHOOK.example.md +40 -0
  35. package/src/headless-role.mjs +14 -14
  36. package/src/help.mjs +1 -0
  37. package/src/lib/mixdog-debug.cjs +0 -22
  38. package/src/lib/plugin-paths.cjs +1 -7
  39. package/src/lib/rules-builder.cjs +34 -56
  40. package/src/mixdog-session-runtime.mjs +710 -319
  41. package/src/output-styles/default.md +12 -7
  42. package/src/output-styles/minimal.md +25 -0
  43. package/src/output-styles/oneline.md +21 -0
  44. package/src/output-styles/simple.md +10 -9
  45. package/src/repl.mjs +12 -4
  46. package/src/rules/agent/00-common.md +7 -5
  47. package/src/rules/agent/30-explorer.md +7 -8
  48. package/src/rules/lead/01-general.md +3 -1
  49. package/src/rules/lead/lead-tool.md +7 -0
  50. package/src/rules/shared/01-tool.md +17 -12
  51. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
  52. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
  53. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
  54. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
  55. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
  56. package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
  57. package/src/runtime/agent/orchestrator/config.mjs +3 -0
  58. package/src/runtime/agent/orchestrator/context/collect.mjs +131 -67
  59. package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
  60. package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
  61. package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -16
  62. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
  63. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
  65. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +359 -106
  66. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +63 -51
  67. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +27 -20
  68. package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
  70. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +210 -21
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +86 -30
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +254 -280
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +191 -50
  75. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
  76. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +11 -5
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
  78. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +265 -1
  79. package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
  80. package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
  81. package/src/runtime/agent/orchestrator/session/loop.mjs +394 -132
  82. package/src/runtime/agent/orchestrator/session/manager.mjs +217 -170
  83. package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
  84. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
  85. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
  86. package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
  87. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -32
  89. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
  90. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +1 -44
  91. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
  92. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -0
  93. package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
  94. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
  95. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
  96. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
  97. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +13 -4
  99. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
  100. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +18 -2
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -1
  104. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
  105. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4076 -3985
  106. package/src/runtime/agent/orchestrator/tools/patch.mjs +116 -2
  107. package/src/runtime/channels/backends/discord.mjs +99 -9
  108. package/src/runtime/channels/backends/telegram.mjs +501 -0
  109. package/src/runtime/channels/index.mjs +441 -1254
  110. package/src/runtime/channels/lib/cli-worker-host.mjs +1 -8
  111. package/src/runtime/channels/lib/config.mjs +54 -3
  112. package/src/runtime/channels/lib/drop-trace.mjs +1 -1
  113. package/src/runtime/channels/lib/executor.mjs +0 -3
  114. package/src/runtime/channels/lib/format.mjs +4 -2
  115. package/src/runtime/channels/lib/memory-client.mjs +0 -38
  116. package/src/runtime/channels/lib/output-forwarder.mjs +77 -71
  117. package/src/runtime/channels/lib/runtime-paths.mjs +29 -6
  118. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  119. package/src/runtime/channels/lib/session-discovery.mjs +0 -4
  120. package/src/runtime/channels/lib/telegram-format.mjs +283 -0
  121. package/src/runtime/channels/lib/tool-format.mjs +1 -2
  122. package/src/runtime/channels/lib/transcript-discovery.mjs +20 -11
  123. package/src/runtime/channels/lib/webhook.mjs +59 -31
  124. package/src/runtime/channels/tool-defs.mjs +1 -1
  125. package/src/runtime/lib/keychain-cjs.cjs +0 -1
  126. package/src/runtime/memory/data/runtime-manifest.json +6 -7
  127. package/src/runtime/memory/index.mjs +187 -43
  128. package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
  129. package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
  130. package/src/runtime/memory/lib/llm-worker-host.mjs +0 -4
  131. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
  132. package/src/runtime/memory/lib/memory-cycle2.mjs +9 -6
  133. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  134. package/src/runtime/memory/lib/memory-ops-policy.mjs +0 -1
  135. package/src/runtime/memory/lib/memory.mjs +101 -4
  136. package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
  137. package/src/runtime/memory/lib/runtime-fetcher.mjs +43 -18
  138. package/src/runtime/memory/lib/session-ingest.mjs +116 -7
  139. package/src/runtime/memory/lib/trace-store.mjs +69 -22
  140. package/src/runtime/memory/tool-defs.mjs +6 -3
  141. package/src/runtime/search/index.mjs +2 -7
  142. package/src/runtime/search/lib/config.mjs +0 -4
  143. package/src/runtime/search/lib/state.mjs +1 -15
  144. package/src/runtime/search/lib/web-tools.mjs +0 -1
  145. package/src/runtime/shared/channel-notification-routing.mjs +12 -0
  146. package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
  147. package/src/runtime/shared/child-spawn-gate.mjs +0 -6
  148. package/src/runtime/shared/config.mjs +9 -0
  149. package/src/runtime/shared/llm/http-agent.mjs +12 -5
  150. package/src/runtime/shared/schedules-store.mjs +21 -19
  151. package/src/runtime/shared/tool-surface.mjs +98 -13
  152. package/src/runtime/shared/transcript-writer.mjs +129 -0
  153. package/src/runtime/shared/update-checker.mjs +214 -0
  154. package/src/standalone/agent-tool.mjs +255 -109
  155. package/src/standalone/channel-admin.mjs +133 -40
  156. package/src/standalone/channel-worker.mjs +8 -291
  157. package/src/standalone/explore-tool.mjs +2 -2
  158. package/src/standalone/memory-runtime-proxy.mjs +3 -1
  159. package/src/standalone/provider-admin.mjs +11 -0
  160. package/src/standalone/seeds.mjs +1 -11
  161. package/src/standalone/usage-dashboard.mjs +1 -1
  162. package/src/tui/App.jsx +2137 -750
  163. package/src/tui/components/ConfirmBar.jsx +47 -0
  164. package/src/tui/components/ContextPanel.jsx +5 -3
  165. package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
  166. package/src/tui/components/Markdown.jsx +22 -98
  167. package/src/tui/components/Message.jsx +14 -35
  168. package/src/tui/components/Picker.jsx +87 -12
  169. package/src/tui/components/PromptInput.jsx +146 -9
  170. package/src/tui/components/QueuedCommands.jsx +1 -1
  171. package/src/tui/components/SlashCommandPalette.jsx +8 -5
  172. package/src/tui/components/Spinner.jsx +7 -7
  173. package/src/tui/components/StatusLine.jsx +40 -21
  174. package/src/tui/components/TextEntryPanel.jsx +51 -7
  175. package/src/tui/components/ToolExecution.jsx +177 -100
  176. package/src/tui/components/TurnDone.jsx +4 -4
  177. package/src/tui/components/UsagePanel.jsx +1 -1
  178. package/src/tui/components/tool-output-format.mjs +312 -40
  179. package/src/tui/components/tool-output-format.test.mjs +180 -1
  180. package/src/tui/display-width.mjs +69 -0
  181. package/src/tui/display-width.test.mjs +35 -0
  182. package/src/tui/dist/index.mjs +7324 -2393
  183. package/src/tui/engine.mjs +287 -126
  184. package/src/tui/index.jsx +117 -7
  185. package/src/tui/keyboard-protocol.mjs +42 -0
  186. package/src/tui/lib/voice-recorder.mjs +453 -0
  187. package/src/tui/markdown/format-token.mjs +354 -142
  188. package/src/tui/markdown/format-token.test.mjs +155 -17
  189. package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
  190. package/src/tui/markdown/render-ansi.test.mjs +1 -1
  191. package/src/tui/markdown/streaming-markdown.mjs +167 -0
  192. package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
  193. package/src/tui/markdown/table-layout.mjs +9 -9
  194. package/src/tui/paste-attachments.mjs +0 -11
  195. package/src/tui/prompt-history-store.mjs +129 -0
  196. package/src/tui/prompt-history-store.test.mjs +52 -0
  197. package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
  198. package/src/tui/theme.mjs +41 -647
  199. package/src/tui/themes/base.mjs +86 -0
  200. package/src/tui/themes/basic.mjs +85 -0
  201. package/src/tui/themes/catppuccin.mjs +72 -0
  202. package/src/tui/themes/dracula.mjs +70 -0
  203. package/src/tui/themes/everforest.mjs +71 -0
  204. package/src/tui/themes/gruvbox.mjs +71 -0
  205. package/src/tui/themes/index.mjs +71 -0
  206. package/src/tui/themes/indigo.mjs +78 -0
  207. package/src/tui/themes/kanagawa.mjs +80 -0
  208. package/src/tui/themes/light.mjs +81 -0
  209. package/src/tui/themes/nord.mjs +72 -0
  210. package/src/tui/themes/onedark.mjs +16 -0
  211. package/src/tui/themes/rosepine.mjs +70 -0
  212. package/src/tui/themes/teal.mjs +81 -0
  213. package/src/tui/themes/tokyonight.mjs +79 -0
  214. package/src/tui/themes/utils.mjs +106 -0
  215. package/src/tui/themes/warm.mjs +79 -0
  216. package/src/tui/transcript-tool-failures.mjs +13 -2
  217. package/src/ui/markdown.mjs +1 -1
  218. package/src/ui/model-display.mjs +2 -2
  219. package/src/ui/statusline.mjs +26 -27
  220. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -623
  221. package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
  222. package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
  223. package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
  224. package/src/workflows/default/WORKFLOW.md +39 -12
  225. package/src/workflows/sequential/WORKFLOW.md +46 -0
  226. package/src/workflows/solo/WORKFLOW.md +7 -0
  227. package/vendor/ink/build/display-width.js +62 -0
  228. package/vendor/ink/build/ink.js +154 -20
  229. package/vendor/ink/build/measure-text.js +4 -1
  230. package/vendor/ink/build/output.js +115 -9
  231. package/vendor/ink/build/render-node-to-output.js +4 -1
  232. package/vendor/ink/build/render.js +4 -0
  233. package/src/hooks/lib/permission-rules.cjs +0 -170
  234. package/src/hooks/lib/settings-loader.cjs +0 -112
  235. package/src/lib/hook-pipe-path.cjs +0 -10
  236. package/src/output-styles/extreme-simple.md +0 -20
  237. package/src/rules/lead/04-workflow.md +0 -51
  238. package/src/runtime/channels/lib/hook-pipe-server.mjs +0 -671
  239. package/src/workflows/default/workflow.json +0 -13
  240. package/src/workflows/solo/workflow.json +0 -7
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ // task-bench.mjs — quantify "how efficiently a task finished" and A/B compare.
3
+ //
4
+ // Thin wrapper over session-bench.mjs: it runs session-bench --json for a set
5
+ // of sessions, compresses the result into a one-line SCORECARD (wall / turns /
6
+ // tools / speed / context / cache / anti-patterns / completed), averages a
7
+ // group, and diffs two groups (before vs after a rule/brief change).
8
+ //
9
+ // Usage:
10
+ // node scripts/task-bench.mjs --session <id[,id2,...]> scorecard(s)
11
+ // node scripts/task-bench.mjs --session a,b,c --group one averaged card
12
+ // node scripts/task-bench.mjs --vs before.json after.json A/B diff
13
+ // node scripts/task-bench.mjs --session a,b --save before.json freeze a group
14
+ // Input is constant (finished sessions are immutable); only rules/briefs change
15
+ // between the two frozen groups you compare.
16
+ import { execFileSync } from 'node:child_process';
17
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { dirname, resolve } from 'node:path';
20
+
21
+ const __dir = dirname(fileURLToPath(import.meta.url));
22
+ const SESSION_BENCH = resolve(__dir, 'session-bench.mjs');
23
+
24
+ function argValue(name, fallback = null) {
25
+ const idx = process.argv.indexOf(name);
26
+ if (idx >= 0 && idx + 1 < process.argv.length) return process.argv[idx + 1];
27
+ const pref = `${name}=`;
28
+ const hit = process.argv.find((a) => a.startsWith(pref));
29
+ return hit ? hit.slice(pref.length) : fallback;
30
+ }
31
+ function hasFlag(name) { return process.argv.includes(name); }
32
+ function num(v) { const n = Number(v); return Number.isFinite(n) ? n : 0; }
33
+ function uniq(items) {
34
+ return [...new Set((items || []).filter(Boolean))];
35
+ }
36
+
37
+ function runSessionBench(sessionId) {
38
+ // Returns the parsed session-bench JSON for one session id (BOM-safe).
39
+ const raw = execFileSync('node', [SESSION_BENCH, '--session', sessionId, '--json'], {
40
+ encoding: 'utf8', maxBuffer: 64 * 1024 * 1024,
41
+ });
42
+ const s = raw.replace(/^\uFEFF/, '');
43
+ const i = s.indexOf('{');
44
+ return JSON.parse(i >= 0 ? s.slice(i) : s);
45
+ }
46
+
47
+ // Compress a session-bench report into the scorecard metrics.
48
+ function scorecard(report) {
49
+ const sum = report.summary || {};
50
+ const cache = report.cache || {};
51
+ const tools = report.tools || {};
52
+ const tr = report.time_range || {};
53
+ const route = Array.isArray(report.stages?.turns) ? report.stages.turns.find((t) => t?.model || t?.provider) : null;
54
+ const tokenSession = Array.isArray(report.tokens?.sessions) ? report.tokens.sessions[0] : null;
55
+ const issues = Array.isArray(report.issues) ? report.issues : [];
56
+ const antipatterns =
57
+ (tools.read_fragmentation?.length || 0) +
58
+ (tools.grep_sweeps?.length || 0) +
59
+ (tools.sequential_tool_clusters?.length || 0) +
60
+ (tools.duplicates?.length || 0) +
61
+ (tools.failed_repeats?.length || 0);
62
+ const turns = num(sum.turns);
63
+ const toolCalls = num(sum.tool_calls);
64
+ const promptTokens = num(cache.prompt_tokens);
65
+ const cachedTokens = num(cache.cached_tokens);
66
+ const uncachedTokens = tokenSession?.uncached_tokens != null
67
+ ? num(tokenSession.uncached_tokens)
68
+ : Math.max(0, promptTokens - cachedTokens);
69
+ const outputTokens = tokenSession
70
+ ? num(tokenSession.total_output) + num(tokenSession.total_thinking)
71
+ : 0;
72
+ // prompt growth = chronological last-first prompt from the per-session token
73
+ // summary. (growth_turns is sorted by prompt_delta desc — NOT chronological —
74
+ // so deriving growth from it produced garbage/negative values.)
75
+ const promptGrowth = tokenSession?.prompt_growth != null ? num(tokenSession.prompt_growth) : null;
76
+ return {
77
+ provider: route?.provider || null,
78
+ model: route?.model || null,
79
+ wall_ms: num(tr.span_ms),
80
+ turns,
81
+ tool_calls: toolCalls,
82
+ tools_per_turn: turns ? Math.round((toolCalls / turns) * 10) / 10 : 0,
83
+ total_tool_ms: num(sum.total_tool_ms),
84
+ llm_stream_ms: num(sum.llm_stream_ms),
85
+ cache_ratio: num(cache.usage_cache_ratio ?? sum.cache_ratio),
86
+ cached_tokens: cachedTokens,
87
+ prompt_tokens: promptTokens,
88
+ uncached_tokens: uncachedTokens,
89
+ output_tokens: outputTokens,
90
+ cache_weighted_input_10: Math.max(0, uncachedTokens + cachedTokens * 0.1),
91
+ cache_weighted_input_25: Math.max(0, uncachedTokens + cachedTokens * 0.25),
92
+ prompt_growth: promptGrowth,
93
+ antipatterns,
94
+ issues: issues.length,
95
+ issue_types: uniq(issues.map((i) => i?.type)),
96
+ read_fragmentation_paths: uniq((tools.read_fragmentation || []).map((f) => f?.path)),
97
+ grep_sweep_count: (tools.grep_sweeps || []).length,
98
+ };
99
+ }
100
+
101
+ function averageCards(cards) {
102
+ if (!cards.length) return null;
103
+ const keys = Object.keys(cards[0]).filter((k) => cards.some((c) => typeof c[k] === 'number' && Number.isFinite(c[k])));
104
+ const out = { n: cards.length };
105
+ for (const k of keys) {
106
+ const vals = cards.map((c) => num(c[k]));
107
+ out[k] = Math.round((vals.reduce((a, b) => a + b, 0) / vals.length) * 10) / 10;
108
+ }
109
+ return out;
110
+ }
111
+
112
+ function fmtMs(ms) { const n = num(ms); return n >= 1000 ? `${(n / 1000).toFixed(1)}s` : `${Math.round(n)}ms`; }
113
+ function fmtTok(n) { const v = num(n); return v >= 1000 ? `${(v / 1000).toFixed(1)}k` : String(Math.round(v)); }
114
+ function fmtPct(n) { return `${Math.round(num(n) * 100)}%`; }
115
+
116
+ const CARD_ORDER = ['wall_ms', 'turns', 'tool_calls', 'tools_per_turn', 'cache_ratio', 'cache_weighted_input_10', 'prompt_growth', 'antipatterns', 'issues'];
117
+ function fmtCardVal(k, v) {
118
+ if (v == null) return '-';
119
+ if (k === 'wall_ms' || k === 'total_tool_ms' || k === 'llm_stream_ms') return fmtMs(v);
120
+ if (k === 'cache_ratio') return fmtPct(v);
121
+ if (k === 'prompt_growth' || k === 'cache_weighted_input_10') return fmtTok(v);
122
+ return String(v);
123
+ }
124
+ function renderCard(label, card) {
125
+ const parts = CARD_ORDER.map((k) => `${k}=${fmtCardVal(k, card[k])}`);
126
+ return `${label}: ${parts.join(' ')}`;
127
+ }
128
+
129
+ function pctDelta(before, after) {
130
+ const b = num(before); const a = num(after);
131
+ if (b === 0) return a === 0 ? '0%' : 'n/a';
132
+ const d = Math.round(((a - b) / Math.abs(b)) * 100);
133
+ return `${d > 0 ? '+' : ''}${d}%`;
134
+ }
135
+ // For these metrics LOWER is better (efficiency); cache_ratio higher is better.
136
+ const LOWER_BETTER = new Set(['wall_ms', 'turns', 'tool_calls', 'tools_per_turn', 'total_tool_ms', 'llm_stream_ms', 'prompt_growth', 'cache_weighted_input_10', 'cache_weighted_input_25', 'antipatterns', 'issues']);
137
+
138
+ function renderDiff(before, after) {
139
+ const L = [];
140
+ L.push(`A/B compare (before n=${before.n || 1} after n=${after.n || 1})`);
141
+ for (const k of CARD_ORDER) {
142
+ const b = before[k]; const a = after[k];
143
+ const delta = pctDelta(b, a);
144
+ let verdict = '';
145
+ if (delta !== 'n/a' && delta !== '0%') {
146
+ const improved = LOWER_BETTER.has(k) ? num(a) < num(b) : num(a) > num(b);
147
+ verdict = improved ? ' ✓' : ' ✗';
148
+ }
149
+ L.push(`- ${k.padEnd(16)} ${fmtCardVal(k, b).padStart(9)} → ${fmtCardVal(k, a).padStart(9)} ${delta.padStart(6)}${verdict}`);
150
+ }
151
+ return L.join('\n');
152
+ }
153
+
154
+ // ---- main ----
155
+ const jsonMode = hasFlag('--json');
156
+ const allowPartial = hasFlag('--allow-partial');
157
+ const vs = argValue('--vs', null);
158
+
159
+ if (vs) {
160
+ // --vs before.json after.json : diff two frozen group cards
161
+ const idx = process.argv.indexOf('--vs');
162
+ const beforePath = process.argv[idx + 1];
163
+ const afterPath = process.argv[idx + 2];
164
+ if (!beforePath || !afterPath) { console.error('usage: --vs <before.json> <after.json>'); process.exit(1); }
165
+ const before = JSON.parse(readFileSync(resolve(beforePath), 'utf8'));
166
+ const after = JSON.parse(readFileSync(resolve(afterPath), 'utf8'));
167
+ const b = before.group || before;
168
+ const a = after.group || after;
169
+ if (jsonMode) console.log(JSON.stringify({ before: b, after: a }, null, 2));
170
+ else console.log(renderDiff(b, a));
171
+ process.exit(0);
172
+ }
173
+
174
+ const sessionArg = argValue('--session', null);
175
+ if (!sessionArg) { console.error('usage: --session <id[,id2,...]> [--group] [--save file.json] [--json] [--allow-partial] | --vs before.json after.json'); process.exit(1); }
176
+ const ids = sessionArg.split(',').map((s) => s.trim()).filter(Boolean);
177
+ const cards = [];
178
+ const skipped = [];
179
+ for (const id of ids) {
180
+ try { cards.push({ session: id, ...scorecard(runSessionBench(id)) }); }
181
+ catch (e) {
182
+ skipped.push({ session: id, error: e.message || String(e) });
183
+ console.error(`skip ${id}: ${e.message}`);
184
+ }
185
+ }
186
+ if (!cards.length) { console.error('no sessions scored'); process.exit(1); }
187
+ if (skipped.length && !allowPartial) {
188
+ const partial = { error: `only scored ${cards.length}/${ids.length} sessions`, sessions: ids, skipped, cards, group: averageCards(cards.map(({ session, ...c }) => c)) };
189
+ if (jsonMode) console.log(JSON.stringify(partial, null, 2));
190
+ else console.error(partial.error);
191
+ process.exit(1);
192
+ }
193
+
194
+ const group = hasFlag('--group') || argValue('--save', null);
195
+ const groupCard = averageCards(cards.map(({ session, ...c }) => c));
196
+ const savePath = argValue('--save', null);
197
+ if (savePath) {
198
+ writeFileSync(resolve(savePath), JSON.stringify({ sessions: ids, cards, skipped, group: groupCard }, null, 2));
199
+ console.error(`saved group (n=${cards.length}) → ${resolve(savePath)}`);
200
+ }
201
+ if (jsonMode) {
202
+ console.log(JSON.stringify({ cards, skipped, group: groupCard }, null, 2));
203
+ } else if (group) {
204
+ console.log(renderCard(`group (n=${cards.length})`, groupCard));
205
+ } else {
206
+ for (const c of cards) console.log(renderCard(c.session.slice(0, 22), c));
207
+ }
@@ -17,7 +17,7 @@ const mixdogHome = process.env.MIXDOG_HOME || resolve(homedir(), '.mixdog');
17
17
  const mixdogDataDir = process.env.MIXDOG_DATA_DIR || resolve(mixdogHome, 'data');
18
18
  const sinceArg = argValue('--since', null);
19
19
  const toolFilter = argValue('--tool', null);
20
- const roleFilter = argValue('--role', null);
20
+ const agentFilter = argValue('--agent', null);
21
21
  const categoryFilter = argValue('--category', null);
22
22
  const jsonMode = process.argv.includes('--json');
23
23
  const files = dataDir
@@ -91,7 +91,7 @@ const sinceTs = parseSince(sinceArg);
91
91
  const rows = files.flatMap(readRows)
92
92
  .filter((row) => sinceTs == null || Number(row.ts || 0) >= sinceTs)
93
93
  .filter((row) => !toolFilter || rowTool(row) === toolFilter)
94
- .filter((row) => !roleFilter || String(row.role || '-') === roleFilter)
94
+ .filter((row) => !agentFilter || String(row.agent || '-') === agentFilter)
95
95
  .filter((row) => !categoryFilter || rowCategory(row) === categoryFilter)
96
96
  .sort((a, b) => Number(a.ts || 0) - Number(b.ts || 0));
97
97
  const recent = rows.slice(-limit);
@@ -111,7 +111,7 @@ if (jsonMode) {
111
111
  since: sinceTs ? new Date(sinceTs).toISOString() : null,
112
112
  filters: {
113
113
  tool: toolFilter,
114
- role: roleFilter,
114
+ agent: agentFilter,
115
115
  category: categoryFilter,
116
116
  },
117
117
  sources: files.filter(existsSync),
@@ -126,7 +126,7 @@ console.log(`tool failures: ${recent.length}/${rows.length} shown`);
126
126
  if (sinceTs) console.log(`since: ${new Date(sinceTs).toISOString()}`);
127
127
  const filterParts = [
128
128
  toolFilter ? `tool=${toolFilter}` : '',
129
- roleFilter ? `role=${roleFilter}` : '',
129
+ agentFilter ? `agent=${agentFilter}` : '',
130
130
  categoryFilter ? `category=${categoryFilter}` : '',
131
131
  ].filter(Boolean);
132
132
  if (filterParts.length) console.log(`filters: ${filterParts.join(', ')}`);
@@ -138,6 +138,6 @@ for (const row of recent) {
138
138
  const category = rowCategory(row);
139
139
  const args = short(JSON.stringify(row.tool_args || row.args || {}), 140);
140
140
  const result = short(row.error_first_line || row.error_preview || row.result || row.error || row.message || '', 220);
141
- const role = row.role || '-';
142
- console.log(`- ${timeLabel(row.ts)} iter=${row.iteration ?? '-'} role=${role} ${tool} ${category} args=${args}${result ? ` result=${result}` : ''}`);
141
+ const agent = row.agent || '-';
142
+ console.log(`- ${timeLabel(row.ts)} iter=${row.iteration ?? '-'} agent=${agent} ${tool} ${category} args=${args}${result ? ` result=${result}` : ''}`);
143
143
  }