mixdog 0.9.1 → 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.
- package/package.json +8 -1
- package/scripts/_bench-cwc.json +20 -0
- package/scripts/agent-loop-policy-test.mjs +37 -0
- package/scripts/agent-parallel-smoke.mjs +54 -10
- package/scripts/background-task-meta-smoke.mjs +1 -1
- package/scripts/bench-run.mjs +262 -0
- package/scripts/compact-smoke.mjs +12 -0
- package/scripts/compact-trigger-migration-smoke.mjs +67 -1
- package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
- package/scripts/internal-comms-bench.mjs +727 -0
- package/scripts/internal-comms-smoke.mjs +75 -0
- package/scripts/lead-workflow-smoke.mjs +4 -4
- package/scripts/live-worker-smoke.mjs +9 -9
- package/scripts/output-style-bench.mjs +285 -0
- package/scripts/output-style-smoke.mjs +13 -10
- package/scripts/patch-replay.mjs +90 -0
- package/scripts/provider-stream-stall-test.mjs +276 -0
- package/scripts/provider-toolcall-test.mjs +599 -1
- package/scripts/routing-corpus.mjs +281 -0
- package/scripts/session-bench.mjs +1526 -0
- package/scripts/session-diag.mjs +595 -0
- package/scripts/task-bench.mjs +207 -0
- package/scripts/tool-failures.mjs +6 -6
- package/scripts/tool-smoke.mjs +306 -66
- package/scripts/toolcall-args-test.mjs +81 -0
- package/src/agents/debugger/AGENT.md +4 -4
- package/src/agents/heavy-worker/AGENT.md +4 -2
- package/src/agents/reviewer/AGENT.md +4 -4
- package/src/agents/worker/AGENT.md +4 -2
- package/src/app.mjs +10 -6
- package/src/defaults/{hidden-roles.json → agents.json} +7 -7
- package/src/examples/schedules/SCHEDULE.example.md +32 -0
- package/src/examples/webhooks/WEBHOOK.example.md +40 -0
- package/src/headless-role.mjs +14 -14
- package/src/help.mjs +1 -0
- package/src/lib/rules-builder.cjs +32 -54
- package/src/mixdog-session-runtime.mjs +710 -318
- package/src/output-styles/default.md +12 -7
- package/src/output-styles/minimal.md +25 -0
- package/src/output-styles/oneline.md +21 -0
- package/src/output-styles/simple.md +10 -9
- package/src/repl.mjs +12 -2
- package/src/rules/agent/00-common.md +7 -5
- package/src/rules/agent/30-explorer.md +7 -8
- package/src/rules/lead/01-general.md +3 -1
- package/src/rules/lead/lead-tool.md +7 -0
- package/src/rules/shared/01-tool.md +17 -12
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
- package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
- package/src/runtime/agent/orchestrator/config.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +131 -67
- package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
- package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +332 -57
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +59 -32
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +27 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +210 -21
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +78 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +202 -98
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +183 -20
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +11 -5
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +15 -9
- package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
- package/src/runtime/agent/orchestrator/session/loop.mjs +394 -132
- package/src/runtime/agent/orchestrator/session/manager.mjs +217 -170
- package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +0 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +13 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +18 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4076 -3985
- package/src/runtime/agent/orchestrator/tools/patch.mjs +116 -2
- package/src/runtime/channels/backends/discord.mjs +99 -9
- package/src/runtime/channels/backends/telegram.mjs +501 -0
- package/src/runtime/channels/index.mjs +441 -1224
- package/src/runtime/channels/lib/config.mjs +54 -2
- package/src/runtime/channels/lib/format.mjs +4 -2
- package/src/runtime/channels/lib/output-forwarder.mjs +80 -67
- package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +283 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
- package/src/runtime/channels/lib/webhook.mjs +59 -31
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +184 -19
- package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
- package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2.mjs +9 -6
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory.mjs +101 -4
- package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
- package/src/runtime/memory/lib/session-ingest.mjs +107 -0
- package/src/runtime/memory/lib/trace-store.mjs +69 -22
- package/src/runtime/memory/tool-defs.mjs +6 -3
- package/src/runtime/shared/channel-notification-routing.mjs +12 -0
- package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
- package/src/runtime/shared/config.mjs +9 -0
- package/src/runtime/shared/llm/http-agent.mjs +12 -5
- package/src/runtime/shared/schedules-store.mjs +21 -19
- package/src/runtime/shared/tool-surface.mjs +98 -13
- package/src/runtime/shared/transcript-writer.mjs +129 -0
- package/src/runtime/shared/update-checker.mjs +214 -0
- package/src/standalone/agent-tool.mjs +255 -109
- package/src/standalone/channel-admin.mjs +133 -40
- package/src/standalone/channel-worker.mjs +8 -291
- package/src/standalone/explore-tool.mjs +2 -2
- package/src/standalone/memory-runtime-proxy.mjs +3 -1
- package/src/standalone/provider-admin.mjs +11 -0
- package/src/standalone/usage-dashboard.mjs +1 -1
- package/src/tui/App.jsx +2096 -732
- package/src/tui/components/ConfirmBar.jsx +47 -0
- package/src/tui/components/ContextPanel.jsx +5 -3
- package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
- package/src/tui/components/Markdown.jsx +22 -98
- package/src/tui/components/Message.jsx +14 -35
- package/src/tui/components/Picker.jsx +87 -12
- package/src/tui/components/PromptInput.jsx +83 -7
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +8 -5
- package/src/tui/components/Spinner.jsx +7 -7
- package/src/tui/components/StatusLine.jsx +40 -21
- package/src/tui/components/TextEntryPanel.jsx +51 -7
- package/src/tui/components/ToolExecution.jsx +170 -98
- package/src/tui/components/TurnDone.jsx +4 -4
- package/src/tui/components/UsagePanel.jsx +1 -1
- package/src/tui/components/tool-output-format.mjs +159 -21
- package/src/tui/components/tool-output-format.test.mjs +87 -0
- package/src/tui/display-width.mjs +69 -0
- package/src/tui/display-width.test.mjs +35 -0
- package/src/tui/dist/index.mjs +6965 -2391
- package/src/tui/engine.mjs +287 -126
- package/src/tui/index.jsx +117 -7
- package/src/tui/keyboard-protocol.mjs +42 -0
- package/src/tui/lib/voice-recorder.mjs +453 -0
- package/src/tui/markdown/format-token.mjs +129 -76
- package/src/tui/markdown/format-token.test.mjs +61 -19
- package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
- package/src/tui/markdown/render-ansi.test.mjs +1 -1
- package/src/tui/markdown/streaming-markdown.mjs +167 -0
- package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
- package/src/tui/markdown/table-layout.mjs +9 -9
- package/src/tui/paste-attachments.mjs +0 -11
- package/src/tui/prompt-history-store.mjs +129 -0
- package/src/tui/prompt-history-store.test.mjs +52 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
- package/src/tui/theme.mjs +41 -657
- package/src/tui/themes/base.mjs +86 -0
- package/src/tui/themes/basic.mjs +85 -0
- package/src/tui/themes/catppuccin.mjs +72 -0
- package/src/tui/themes/dracula.mjs +70 -0
- package/src/tui/themes/everforest.mjs +71 -0
- package/src/tui/themes/gruvbox.mjs +71 -0
- package/src/tui/themes/index.mjs +71 -0
- package/src/tui/themes/indigo.mjs +78 -0
- package/src/tui/themes/kanagawa.mjs +80 -0
- package/src/tui/themes/light.mjs +81 -0
- package/src/tui/themes/nord.mjs +72 -0
- package/src/tui/themes/onedark.mjs +16 -0
- package/src/tui/themes/rosepine.mjs +70 -0
- package/src/tui/themes/teal.mjs +81 -0
- package/src/tui/themes/tokyonight.mjs +79 -0
- package/src/tui/themes/utils.mjs +106 -0
- package/src/tui/themes/warm.mjs +79 -0
- package/src/tui/transcript-tool-failures.mjs +13 -2
- package/src/ui/markdown.mjs +1 -1
- package/src/ui/model-display.mjs +2 -2
- package/src/ui/statusline.mjs +26 -27
- package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
- package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
- package/src/workflows/default/WORKFLOW.md +39 -12
- package/src/workflows/sequential/WORKFLOW.md +46 -0
- package/src/workflows/solo/WORKFLOW.md +7 -0
- package/vendor/ink/build/display-width.js +62 -0
- package/vendor/ink/build/ink.js +100 -12
- package/vendor/ink/build/measure-text.js +4 -1
- package/vendor/ink/build/output.js +115 -9
- package/vendor/ink/build/render-node-to-output.js +4 -1
- package/vendor/ink/build/render.js +4 -0
- package/src/output-styles/extreme-simple.md +0 -20
- package/src/rules/lead/04-workflow.md +0 -51
- package/src/workflows/default/workflow.json +0 -13
- 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
|
|
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) => !
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
142
|
-
console.log(`- ${timeLabel(row.ts)} iter=${row.iteration ?? '-'}
|
|
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
|
}
|