mixdog 0.9.37 → 0.9.39
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 +9 -4
- package/scripts/abort-recovery-test.mjs +43 -2
- package/scripts/agent-tag-reuse-smoke.mjs +146 -6
- package/scripts/agent-terminal-reap-test.mjs +127 -0
- package/scripts/agent-trace-io-test.mjs +69 -0
- package/scripts/code-graph-disk-hit-test.mjs +224 -0
- package/scripts/dispatch-persist-recovery-test.mjs +141 -0
- package/scripts/execution-completion-dedup-test.mjs +157 -0
- package/scripts/execution-pending-resume-kick-test.mjs +57 -2
- package/scripts/execution-resume-esc-integration-test.mjs +174 -0
- package/scripts/explore-bench.mjs +101 -8
- package/scripts/explore-prompt-policy-test.mjs +156 -11
- package/scripts/find-fuzzy-hidden-test.mjs +122 -0
- package/scripts/internal-comms-bench-test.mjs +226 -0
- package/scripts/internal-comms-bench.mjs +185 -58
- package/scripts/internal-comms-smoke.mjs +171 -23
- package/scripts/live-worker-smoke.mjs +38 -2
- package/scripts/memory-cycle-routing-test.mjs +111 -0
- package/scripts/memory-rule-contract-test.mjs +93 -0
- package/scripts/notify-completion-mirror-test.mjs +73 -0
- package/scripts/output-style-smoke.mjs +2 -2
- package/scripts/rg-runner-test.mjs +240 -0
- package/scripts/routing-corpus-test.mjs +349 -0
- package/scripts/routing-corpus.mjs +211 -32
- package/scripts/session-orphan-sweep-test.mjs +83 -0
- package/scripts/session-sweep.mjs +266 -0
- package/scripts/steering-drain-buckets-test.mjs +179 -0
- package/scripts/tool-smoke.mjs +21 -13
- package/scripts/tool-tui-presentation-test.mjs +202 -0
- package/src/agents/heavy-worker/AGENT.md +10 -7
- package/src/agents/reviewer/AGENT.md +6 -4
- package/src/agents/worker/AGENT.md +7 -5
- package/src/rules/agent/00-common.md +4 -4
- package/src/rules/agent/00-core.md +11 -14
- package/src/rules/agent/20-skip-protocol.md +3 -3
- package/src/rules/agent/30-explorer.md +56 -48
- package/src/rules/agent/40-cycle1-agent.md +15 -24
- package/src/rules/agent/41-cycle2-agent.md +33 -57
- package/src/rules/agent/42-cycle3-agent.md +28 -42
- package/src/rules/lead/01-general.md +7 -10
- package/src/rules/lead/lead-brief.md +11 -14
- package/src/rules/lead/lead-tool.md +6 -5
- package/src/rules/shared/01-tool.md +44 -41
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +37 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +20 -5
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +31 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +13 -7
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +21 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +17 -38
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +5 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +119 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +17 -8
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +270 -78
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +18 -52
- package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +41 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +339 -63
- package/src/runtime/agent/orchestrator/stall-policy.mjs +37 -0
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -20
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +118 -53
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +106 -29
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +151 -64
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +37 -13
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +84 -49
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +172 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +68 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +17 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +24 -10
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -7
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -4
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +4 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +12 -2
- package/src/runtime/shared/tool-primitives.mjs +4 -1
- package/src/runtime/shared/tool-status.mjs +27 -0
- package/src/runtime/shared/tool-surface.mjs +6 -3
- package/src/session-runtime/config-helpers.mjs +14 -0
- package/src/session-runtime/context-status.mjs +1 -0
- package/src/session-runtime/effort.mjs +6 -2
- package/src/session-runtime/model-recency.mjs +5 -2
- package/src/session-runtime/provider-models.mjs +3 -3
- package/src/session-runtime/runtime-core.mjs +78 -10
- package/src/session-runtime/tool-catalog.mjs +34 -0
- package/src/session-runtime/warmup-schedulers.mjs +7 -1
- package/src/standalone/agent-tool/notify.mjs +13 -0
- package/src/standalone/agent-tool.mjs +45 -69
- package/src/standalone/explore-tool.mjs +6 -7
- package/src/tui/App.jsx +31 -0
- package/src/tui/app/model-options.mjs +5 -3
- package/src/tui/app/model-picker.mjs +12 -24
- package/src/tui/app/transcript-window.mjs +1 -0
- package/src/tui/components/ToolExecution.jsx +11 -6
- package/src/tui/components/TranscriptItem.jsx +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +24 -10
- package/src/tui/components/tool-execution/text-format.mjs +10 -19
- package/src/tui/dist/index.mjs +533 -143
- package/src/tui/engine/agent-job-feed.mjs +153 -16
- package/src/tui/engine/agent-response-tail.mjs +68 -0
- package/src/tui/engine/notification-plan.mjs +16 -0
- package/src/tui/engine/queue-helpers.mjs +8 -0
- package/src/tui/engine/session-api.mjs +8 -2
- package/src/tui/engine/session-flow.mjs +34 -2
- package/src/tui/engine/tool-card-results.mjs +54 -32
- package/src/tui/engine/tool-result-status.mjs +75 -21
- package/src/tui/engine/turn.mjs +83 -43
- package/src/tui/engine.mjs +63 -2
- package/src/workflows/bench/WORKFLOW.md +25 -35
- package/src/workflows/default/WORKFLOW.md +38 -32
- package/src/workflows/solo/WORKFLOW.md +19 -22
- package/scripts/_jitter-fuzz.mjs +0 -44410
- package/scripts/_jitter-fuzz2.mjs +0 -44400
- package/scripts/_jitter-probe.mjs +0 -44397
- package/scripts/_jp2.mjs +0 -45614
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
CHILD_RESULT_END,
|
|
11
|
+
CHILD_RESULT_START,
|
|
12
|
+
DEFAULT_LEAD_APPROVAL_PROMPT,
|
|
13
|
+
buildLiveLeadDriver,
|
|
14
|
+
leadModeExitCode,
|
|
15
|
+
parseLiveLeadResult,
|
|
16
|
+
runLiveLeadDelegation,
|
|
17
|
+
validateLeadRun,
|
|
18
|
+
} from './internal-comms-bench.mjs';
|
|
19
|
+
|
|
20
|
+
function writeRuntime(root, managerSource) {
|
|
21
|
+
const runtimePath = join(root, 'runtime.mjs');
|
|
22
|
+
const tracePath = join(root, 'agent-trace-io.mjs');
|
|
23
|
+
writeFileSync(runtimePath, managerSource);
|
|
24
|
+
writeFileSync(tracePath, 'export async function drainAgentTrace() {}\n');
|
|
25
|
+
return {
|
|
26
|
+
runtimeUrl: pathToFileURL(runtimePath).href,
|
|
27
|
+
traceUrl: pathToFileURL(tracePath).href,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeSandboxRuntime(root, marker) {
|
|
32
|
+
const runtimePath = join(root, 'mixdog-session-runtime.mjs');
|
|
33
|
+
const tracePath = join(root, 'runtime', 'agent', 'orchestrator', 'agent-trace-io.mjs');
|
|
34
|
+
mkdirSync(join(root, 'runtime', 'agent', 'orchestrator'), { recursive: true });
|
|
35
|
+
writeFileSync(runtimePath, `
|
|
36
|
+
import { writeFileSync } from 'node:fs';
|
|
37
|
+
export async function createMixdogSessionRuntime() {
|
|
38
|
+
writeFileSync(process.env.RUNTIME_MARKER_PATH, ${JSON.stringify(marker)});
|
|
39
|
+
return {
|
|
40
|
+
id: 'sess_default_resolution', sessionId: 'sess_default_resolution',
|
|
41
|
+
async setFast() {},
|
|
42
|
+
toolsStatus() { return { activeTools: ['agent'] }; },
|
|
43
|
+
onNotification() {},
|
|
44
|
+
agentStatus() { return { agentJobs: [] }; },
|
|
45
|
+
async ask() { return { result: { text: 'done' } }; },
|
|
46
|
+
async close() {},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
`);
|
|
50
|
+
writeFileSync(tracePath, 'export async function drainAgentTrace() {}\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
test('lead delegation resolves default runtime and trace imports from the variant plugin root', () => {
|
|
54
|
+
const root = mkdtempSync(join(tmpdir(), 'mixdog-internal-comms-bench-test-'));
|
|
55
|
+
const pluginA = join(root, 'plugin-A');
|
|
56
|
+
const pluginB = join(root, 'plugin-B');
|
|
57
|
+
const markerPath = join(root, 'runtime-marker');
|
|
58
|
+
const priorMarkerPath = process.env.RUNTIME_MARKER_PATH;
|
|
59
|
+
try {
|
|
60
|
+
process.env.RUNTIME_MARKER_PATH = markerPath;
|
|
61
|
+
writeSandboxRuntime(pluginA, 'A');
|
|
62
|
+
writeSandboxRuntime(pluginB, 'B');
|
|
63
|
+
for (const [pluginRoot, expected] of [[pluginA, 'A'], [pluginB, 'B']]) {
|
|
64
|
+
const run = runLiveLeadDelegation({
|
|
65
|
+
pluginRoot,
|
|
66
|
+
dataDir: root,
|
|
67
|
+
taskCwd: root,
|
|
68
|
+
prompt: 'Use the variant runtime.',
|
|
69
|
+
provider: 'test-provider',
|
|
70
|
+
model: 'test-model',
|
|
71
|
+
fast: false,
|
|
72
|
+
});
|
|
73
|
+
assert.equal(run.ok, true, run.stderr || run.stdout);
|
|
74
|
+
assert.equal(readFileSync(markerPath, 'utf8'), expected);
|
|
75
|
+
}
|
|
76
|
+
} finally {
|
|
77
|
+
if (priorMarkerPath === undefined) delete process.env.RUNTIME_MARKER_PATH;
|
|
78
|
+
else process.env.RUNTIME_MARKER_PATH = priorMarkerPath;
|
|
79
|
+
rmSync(root, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('lead driver plans before explicit approval, drains traces, and exits despite open handles', () => {
|
|
84
|
+
const root = mkdtempSync(join(tmpdir(), 'mixdog-internal-comms-bench-test-'));
|
|
85
|
+
const promptsPath = join(root, 'prompts.jsonl');
|
|
86
|
+
const tracePath = join(root, 'trace-drained');
|
|
87
|
+
const activeToolsPath = join(root, 'active-tools.json');
|
|
88
|
+
try {
|
|
89
|
+
const runtimeUrls = writeRuntime(root, `
|
|
90
|
+
import { appendFileSync, writeFileSync } from 'node:fs';
|
|
91
|
+
let calls = 0;
|
|
92
|
+
let fastApplied = false;
|
|
93
|
+
setInterval(() => {}, 1000);
|
|
94
|
+
export async function createMixdogSessionRuntime() {
|
|
95
|
+
return {
|
|
96
|
+
id: 'sess_bench_test', sessionId: 'sess_bench_test',
|
|
97
|
+
async setFast(value) {
|
|
98
|
+
if (typeof value !== 'boolean') throw new Error('fast must be boolean');
|
|
99
|
+
fastApplied = true;
|
|
100
|
+
writeFileSync(process.env.FAST_PATH, JSON.stringify(value));
|
|
101
|
+
},
|
|
102
|
+
toolsStatus() {
|
|
103
|
+
writeFileSync(process.env.ACTIVE_TOOLS_PATH, JSON.stringify(['agent']));
|
|
104
|
+
return { activeTools: ['agent'] };
|
|
105
|
+
},
|
|
106
|
+
onNotification() {},
|
|
107
|
+
agentStatus() { return { agentJobs: [] }; },
|
|
108
|
+
async ask(prompt) {
|
|
109
|
+
if (!fastApplied) throw new Error('fast was not applied before prompting');
|
|
110
|
+
appendFileSync(process.env.PROMPTS_PATH, JSON.stringify(prompt) + '\\n');
|
|
111
|
+
calls += 1;
|
|
112
|
+
return { result: calls === 1 ? { text: 'plan' } : { text: 'completed {math.js:1}' } };
|
|
113
|
+
},
|
|
114
|
+
async close() { this.id = ''; this.sessionId = ''; },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
`);
|
|
118
|
+
writeFileSync(join(root, 'agent-trace-io.mjs'), `
|
|
119
|
+
import { writeFileSync } from 'node:fs';
|
|
120
|
+
export async function drainAgentTrace() {
|
|
121
|
+
await new Promise((resolve) => setTimeout(resolve, 75));
|
|
122
|
+
writeFileSync(process.env.TRACE_PATH, 'drained');
|
|
123
|
+
}
|
|
124
|
+
`);
|
|
125
|
+
const driver = buildLiveLeadDriver({
|
|
126
|
+
...runtimeUrls,
|
|
127
|
+
taskCwd: root,
|
|
128
|
+
prompt: 'Plan the benchmark task.',
|
|
129
|
+
provider: 'test-provider',
|
|
130
|
+
model: 'test-model',
|
|
131
|
+
fast: false,
|
|
132
|
+
});
|
|
133
|
+
const child = spawnSync(process.execPath, ['--input-type=module', '-e', driver], {
|
|
134
|
+
encoding: 'utf8',
|
|
135
|
+
env: { ...process.env, PROMPTS_PATH: promptsPath, ACTIVE_TOOLS_PATH: activeToolsPath, TRACE_PATH: tracePath, FAST_PATH: join(root, 'fast.json') },
|
|
136
|
+
timeout: 2000,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
assert.equal(child.status, 0, child.stderr || child.stdout);
|
|
140
|
+
assert.deepEqual(
|
|
141
|
+
readFileSync(promptsPath, 'utf8').trim().split(/\r?\n/).map(JSON.parse),
|
|
142
|
+
['Plan the benchmark task.', DEFAULT_LEAD_APPROVAL_PROMPT],
|
|
143
|
+
);
|
|
144
|
+
assert.deepEqual(JSON.parse(readFileSync(activeToolsPath, 'utf8')), ['agent']);
|
|
145
|
+
assert.equal(JSON.parse(readFileSync(join(root, 'fast.json'), 'utf8')), false);
|
|
146
|
+
assert.equal(readFileSync(tracePath, 'utf8'), 'drained');
|
|
147
|
+
assert.deepEqual(parseLiveLeadResult(child.stdout), {
|
|
148
|
+
text: 'completed {math.js:1}',
|
|
149
|
+
sessionId: 'sess_bench_test',
|
|
150
|
+
});
|
|
151
|
+
assert.match(child.stdout, new RegExp(`${CHILD_RESULT_START}.*${CHILD_RESULT_END}`));
|
|
152
|
+
} finally {
|
|
153
|
+
rmSync(root, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('lead validation rejects successful child output without worker and reviewer participation', () => {
|
|
158
|
+
const root = mkdtempSync(join(tmpdir(), 'mixdog-internal-comms-bench-test-'));
|
|
159
|
+
try {
|
|
160
|
+
writeFileSync(join(root, 'math.js'), `export function mul(a, b) { return a * b; }\nexport function add(a, b) { return a + b; }\n`);
|
|
161
|
+
const split = {
|
|
162
|
+
byRole: {
|
|
163
|
+
lead: { usage_rows: 1 },
|
|
164
|
+
worker: { usage_rows: 0 },
|
|
165
|
+
reviewer: { usage_rows: 0 },
|
|
166
|
+
other: { usage_rows: 0 },
|
|
167
|
+
},
|
|
168
|
+
total: { usage_rows: 1 },
|
|
169
|
+
};
|
|
170
|
+
const validation = validateLeadRun({ taskCwd: root, run: { ok: true }, split });
|
|
171
|
+
assert.equal(validation.valid, false);
|
|
172
|
+
assert.deepEqual(validation.reasons, [
|
|
173
|
+
'worker participation/usage is missing',
|
|
174
|
+
'reviewer participation/usage is missing',
|
|
175
|
+
]);
|
|
176
|
+
assert.equal(leadModeExitCode(
|
|
177
|
+
{ A: [{ valid: false }], B: [{ valid: true }] },
|
|
178
|
+
{ A: [], B: [split] },
|
|
179
|
+
), 1);
|
|
180
|
+
} finally {
|
|
181
|
+
rmSync(root, { recursive: true, force: true });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('child failures retain stdout and stderr diagnostics and cannot succeed', () => {
|
|
186
|
+
const root = mkdtempSync(join(tmpdir(), 'mixdog-internal-comms-bench-test-'));
|
|
187
|
+
try {
|
|
188
|
+
const runtimeUrls = writeRuntime(root, `
|
|
189
|
+
export async function createMixdogSessionRuntime() {
|
|
190
|
+
return {
|
|
191
|
+
id: 'sess_bench_failure', sessionId: 'sess_bench_failure',
|
|
192
|
+
async setFast() {},
|
|
193
|
+
toolsStatus() { return { activeTools: ['agent'] }; },
|
|
194
|
+
onNotification() {},
|
|
195
|
+
agentStatus() { return { agentJobs: [] }; },
|
|
196
|
+
async ask() {
|
|
197
|
+
console.log('provider stdout {diagnostic}');
|
|
198
|
+
console.error('provider stderr {diagnostic}');
|
|
199
|
+
throw new Error('provider failure');
|
|
200
|
+
},
|
|
201
|
+
async close() {},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
`);
|
|
205
|
+
const run = runLiveLeadDelegation({
|
|
206
|
+
pluginRoot: root,
|
|
207
|
+
dataDir: root,
|
|
208
|
+
taskCwd: root,
|
|
209
|
+
prompt: 'Plan the benchmark task.',
|
|
210
|
+
provider: 'test-provider',
|
|
211
|
+
model: 'test-model',
|
|
212
|
+
fast: false,
|
|
213
|
+
runtimeUrls,
|
|
214
|
+
});
|
|
215
|
+
assert.equal(run.ok, false);
|
|
216
|
+
assert.equal(run.sessionId, null);
|
|
217
|
+
assert.match(run.stdout, /provider stdout \{diagnostic\}/);
|
|
218
|
+
assert.match(run.stderr, /provider stderr \{diagnostic\}/);
|
|
219
|
+
assert.equal(leadModeExitCode(
|
|
220
|
+
{ A: [{ valid: false }], B: [{ valid: false }] },
|
|
221
|
+
{ A: [], B: [] },
|
|
222
|
+
), 1);
|
|
223
|
+
} finally {
|
|
224
|
+
rmSync(root, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// node scripts/internal-comms-bench.mjs
|
|
9
9
|
// node scripts/internal-comms-bench.mjs --run [--model grok] [--provider P] [--json]
|
|
10
|
-
import { execFileSync } from 'node:child_process';
|
|
10
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
11
11
|
import {
|
|
12
12
|
copyFileSync,
|
|
13
13
|
cpSync,
|
|
@@ -31,11 +31,16 @@ const HEADLESS = pathToFileURL(resolve(__dir, '../src/headless-role.mjs')).href;
|
|
|
31
31
|
const RULE_FILES = [
|
|
32
32
|
'rules/agent/00-core.md',
|
|
33
33
|
'rules/agent/00-common.md',
|
|
34
|
+
'rules/agent/20-skip-protocol.md',
|
|
34
35
|
'agents/worker/AGENT.md',
|
|
35
36
|
'agents/heavy-worker/AGENT.md',
|
|
36
37
|
'agents/reviewer/AGENT.md',
|
|
37
38
|
'agents/debugger/AGENT.md',
|
|
38
39
|
'workflows/default/WORKFLOW.md',
|
|
40
|
+
'workflows/solo/WORKFLOW.md',
|
|
41
|
+
'workflows/bench/WORKFLOW.md',
|
|
42
|
+
'rules/lead/01-general.md',
|
|
43
|
+
'rules/lead/02-channels.md',
|
|
39
44
|
'rules/lead/lead-tool.md',
|
|
40
45
|
'rules/lead/lead-brief.md',
|
|
41
46
|
];
|
|
@@ -48,11 +53,12 @@ const DEFAULT_WORKER_PROMPT =
|
|
|
48
53
|
// Identical across variants A/B; only the on-disk rule files differ.
|
|
49
54
|
const DEFAULT_LEAD_PROMPT = [
|
|
50
55
|
'You are the Lead in an automation benchmark. A file named math.js already exists in your working directory.',
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'Step 2: after the worker hands off, call the agent tool with agent "reviewer" and give it a brief to verify that math.js exports both add and mul.',
|
|
54
|
-
'Step 3: stop and reply with a one-line outcome plus math.js:line. Do exactly these three steps and nothing else.',
|
|
56
|
+
'This is the planning turn: give a concise plan only, then wait for a later explicit approval. Do not edit, mutate state, or delegate in this turn.',
|
|
57
|
+
'After approval, do NOT edit any file yourself or call apply_patch yourself. Delegate every implementation step: first a worker adds exported add(a, b) returning a + b to math.js while preserving mul, then a reviewer verifies both exports. Finally reply with a one-line outcome plus math.js:line.',
|
|
55
58
|
].join(' ');
|
|
59
|
+
export const DEFAULT_LEAD_APPROVAL_PROMPT = 'Proceed with the latest plan.';
|
|
60
|
+
export const CHILD_RESULT_START = '__INTERNAL_COMMS_BENCH_RESULT_START__';
|
|
61
|
+
export const CHILD_RESULT_END = '__INTERNAL_COMMS_BENCH_RESULT_END__';
|
|
56
62
|
|
|
57
63
|
const MODEL_ALIASES = {
|
|
58
64
|
opus: { provider: 'anthropic-oauth', model: 'claude-opus-4-8' },
|
|
@@ -383,6 +389,18 @@ function emptyRoleSplit(dataDir) {
|
|
|
383
389
|
};
|
|
384
390
|
}
|
|
385
391
|
|
|
392
|
+
function mathTaskCompleted(taskCwd) {
|
|
393
|
+
let source = '';
|
|
394
|
+
try {
|
|
395
|
+
source = readFileSync(join(taskCwd, 'math.js'), 'utf8');
|
|
396
|
+
} catch {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
return source !== INITIAL_MATH_JS
|
|
400
|
+
&& /export\s+function\s+add\s*\(\s*a\s*,\s*b\s*\)\s*\{\s*return\s+a\s*\+\s*b\s*;?\s*\}/.test(source)
|
|
401
|
+
&& /export\s+function\s+mul\s*\(\s*a\s*,\s*b\s*\)\s*\{\s*return\s+a\s*\*\s*b\s*;?\s*\}/.test(source);
|
|
402
|
+
}
|
|
403
|
+
|
|
386
404
|
function splitTokensByRole(dataDir, rootSessionId) {
|
|
387
405
|
const tracePath = join(dataDir, 'history', 'agent-trace.jsonl');
|
|
388
406
|
const rows = readRows(tracePath);
|
|
@@ -420,6 +438,21 @@ function splitTokensByRole(dataDir, rootSessionId) {
|
|
|
420
438
|
return { tracePath, session_ids: [...treeIds], byRole, total };
|
|
421
439
|
}
|
|
422
440
|
|
|
441
|
+
export function validateLeadRun({ taskCwd, run, split }) {
|
|
442
|
+
const reasons = [];
|
|
443
|
+
if (!run.ok) reasons.push('child did not return a successful benchmark result');
|
|
444
|
+
if (!mathTaskCompleted(taskCwd)) reasons.push('math.js does not contain the approved add and preserved mul implementation');
|
|
445
|
+
if ((split?.byRole?.worker?.usage_rows || 0) < 1) reasons.push('worker participation/usage is missing');
|
|
446
|
+
if ((split?.byRole?.reviewer?.usage_rows || 0) < 1) reasons.push('reviewer participation/usage is missing');
|
|
447
|
+
return { valid: reasons.length === 0, reasons };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export function leadModeExitCode(runsMeta, perVariant) {
|
|
451
|
+
const allValid = ['A', 'B'].every((variant) => runsMeta[variant].length > 0 && runsMeta[variant].every((run) => run.valid));
|
|
452
|
+
const anyUsage = ['A', 'B'].some((variant) => perVariant[variant].some((split) => split.total.usage_rows > 0));
|
|
453
|
+
return allValid && anyUsage ? 0 : 1;
|
|
454
|
+
}
|
|
455
|
+
|
|
423
456
|
function median(values) {
|
|
424
457
|
const arr = values.filter((v) => Number.isFinite(v)).sort((a, b) => a - b);
|
|
425
458
|
if (!arr.length) return 0;
|
|
@@ -443,54 +476,120 @@ function aggregateVariant(splits) {
|
|
|
443
476
|
return out;
|
|
444
477
|
}
|
|
445
478
|
|
|
446
|
-
// Drives a REAL Lead session in the variant sandbox
|
|
447
|
-
//
|
|
448
|
-
// The variant rule files take effect through
|
|
449
|
-
//
|
|
450
|
-
function
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
`
|
|
463
|
-
|
|
464
|
-
`const
|
|
465
|
-
`
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
`
|
|
479
|
+
// Drives a REAL Lead session in the variant sandbox through the public session
|
|
480
|
+
// runtime, including its deferred standalone tool surface.
|
|
481
|
+
// The variant rule files and public runtime modules take effect through the
|
|
482
|
+
// sandboxed MIXDOG_ROOT.
|
|
483
|
+
export function buildLiveLeadDriver({
|
|
484
|
+
runtimeUrl,
|
|
485
|
+
traceUrl = pathToFileURL(join(PLUGIN_ROOT, 'runtime/agent/orchestrator/agent-trace-io.mjs')).href,
|
|
486
|
+
taskCwd,
|
|
487
|
+
prompt,
|
|
488
|
+
approvalPrompt = DEFAULT_LEAD_APPROVAL_PROMPT,
|
|
489
|
+
provider,
|
|
490
|
+
model,
|
|
491
|
+
effort,
|
|
492
|
+
fast,
|
|
493
|
+
}) {
|
|
494
|
+
return [
|
|
495
|
+
`import { createMixdogSessionRuntime } from ${JSON.stringify(runtimeUrl)};`,
|
|
496
|
+
`import { drainAgentTrace } from ${JSON.stringify(traceUrl)};`,
|
|
497
|
+
`const rt = await createMixdogSessionRuntime({ provider: ${JSON.stringify(provider)}, model: ${JSON.stringify(model)},`,
|
|
498
|
+
` cwd: ${JSON.stringify(taskCwd)}, toolMode: 'full' });`,
|
|
499
|
+
effort ? `await rt.setEffort(${JSON.stringify(effort)});` : '',
|
|
500
|
+
fast !== undefined ? `await rt.setFast(${JSON.stringify(fast)});` : '',
|
|
501
|
+
`const prePromptTools = rt.toolsStatus?.()?.activeTools || [];`,
|
|
502
|
+
`if (!prePromptTools.includes('agent')) throw new Error('Lead runtime agent tool is not active');`,
|
|
503
|
+
`let busy = false; let kickDeferred = false; let wake = null;`,
|
|
504
|
+
`rt.onNotification(() => { queueMicrotask(() => { kickDeferred = true; if (!busy && wake) wake(); }); return false; });`,
|
|
505
|
+
`const askOnce = async (msg) => { busy = true; try { const { result } = await rt.ask(msg); return result; } finally { busy = false; } };`,
|
|
506
|
+
`const agentsBusy = () => { const jobs = rt.agentStatus?.()?.agentJobs || []; return jobs.some((j) => /running|pending|spawn|queued/i.test(String(j?.status || j?.state || ''))); };`,
|
|
507
|
+
`let result; let sessionId = '';`,
|
|
508
|
+
`try {`,
|
|
509
|
+
` await askOnce(${JSON.stringify(prompt)});`,
|
|
510
|
+
` result = await askOnce(${JSON.stringify(approvalPrompt)});`,
|
|
511
|
+
` const deadline = Date.now() + 30 * 60_000;`,
|
|
512
|
+
` while (Date.now() < deadline) {`,
|
|
513
|
+
` if (!kickDeferred) {`,
|
|
514
|
+
` if (!agentsBusy()) break;`,
|
|
515
|
+
` const kicked = await new Promise((resolve) => { wake = () => resolve(true); setTimeout(() => resolve(false), 10_000); });`,
|
|
516
|
+
` wake = null;`,
|
|
517
|
+
` if (!kicked && !kickDeferred && !agentsBusy()) break;`,
|
|
518
|
+
` }`,
|
|
519
|
+
` if (!kickDeferred && !agentsBusy()) break;`,
|
|
520
|
+
` kickDeferred = false; result = (await askOnce('')) || result;`,
|
|
521
|
+
` }`,
|
|
522
|
+
` sessionId = rt.sessionId || rt.id || rt.session?.id || '';`,
|
|
523
|
+
`}`,
|
|
524
|
+
`finally { try { await rt.close('internal-comms-bench-lead'); } catch {} }`,
|
|
525
|
+
`await drainAgentTrace();`,
|
|
526
|
+
`const benchResult = JSON.stringify({ text: String(result?.text || result?.content || '').trim(), sessionId });`,
|
|
527
|
+
`process.stdout.write(${JSON.stringify(CHILD_RESULT_START)} + benchResult + ${JSON.stringify(CHILD_RESULT_END)} + '\\n', () => process.exit(0));`,
|
|
469
528
|
].filter(Boolean).join('\n');
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export function parseLiveLeadResult(stdout) {
|
|
532
|
+
const source = String(stdout || '');
|
|
533
|
+
const start = source.lastIndexOf(CHILD_RESULT_START);
|
|
534
|
+
if (start < 0) return null;
|
|
535
|
+
const jsonStart = start + CHILD_RESULT_START.length;
|
|
536
|
+
const end = source.indexOf(CHILD_RESULT_END, jsonStart);
|
|
537
|
+
if (end < 0) return null;
|
|
474
538
|
try {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
...process.env,
|
|
480
|
-
MIXDOG_ROOT: pluginRoot,
|
|
481
|
-
MIXDOG_DATA_DIR: dataDir,
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
const j = raw.lastIndexOf('{');
|
|
485
|
-
if (j >= 0) { try { sid = JSON.parse(raw.slice(j)).sessionId || null; } catch { /* tail */ } }
|
|
486
|
-
ok = !!sid;
|
|
487
|
-
} catch (e) {
|
|
488
|
-
raw = String(e.stdout || '') + String(e.stderr || '');
|
|
489
|
-
const j = raw.lastIndexOf('{');
|
|
490
|
-
if (j >= 0) { try { sid = JSON.parse(raw.slice(j)).sessionId || null; } catch { /* tail */ } }
|
|
491
|
-
ok = false;
|
|
539
|
+
const result = JSON.parse(source.slice(jsonStart, end));
|
|
540
|
+
return typeof result?.sessionId === 'string' && result.sessionId ? result : null;
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
492
543
|
}
|
|
493
|
-
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function runLiveLeadDelegation({
|
|
547
|
+
pluginRoot,
|
|
548
|
+
dataDir,
|
|
549
|
+
taskCwd,
|
|
550
|
+
prompt,
|
|
551
|
+
provider,
|
|
552
|
+
model,
|
|
553
|
+
effort,
|
|
554
|
+
fast,
|
|
555
|
+
runtimeUrls = null,
|
|
556
|
+
}) {
|
|
557
|
+
const runtimeUrl = runtimeUrls?.runtimeUrl || pathToFileURL(join(pluginRoot, 'mixdog-session-runtime.mjs')).href;
|
|
558
|
+
const traceUrl = runtimeUrls?.traceUrl || pathToFileURL(join(pluginRoot, 'runtime/agent/orchestrator/agent-trace-io.mjs')).href;
|
|
559
|
+
const driver = buildLiveLeadDriver({
|
|
560
|
+
runtimeUrl,
|
|
561
|
+
traceUrl,
|
|
562
|
+
taskCwd,
|
|
563
|
+
prompt,
|
|
564
|
+
provider,
|
|
565
|
+
model,
|
|
566
|
+
effort,
|
|
567
|
+
fast,
|
|
568
|
+
});
|
|
569
|
+
const started = Date.now();
|
|
570
|
+
const child = spawnSync(process.execPath, ['--input-type=module', '-e', driver], {
|
|
571
|
+
encoding: 'utf8',
|
|
572
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
573
|
+
env: {
|
|
574
|
+
...process.env,
|
|
575
|
+
MIXDOG_ROOT: pluginRoot,
|
|
576
|
+
MIXDOG_DATA_DIR: dataDir,
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
const stdout = String(child.stdout || '');
|
|
580
|
+
const stderr = String(child.stderr || '');
|
|
581
|
+
const result = parseLiveLeadResult(stdout);
|
|
582
|
+
return {
|
|
583
|
+
sessionId: result?.sessionId || null,
|
|
584
|
+
ms: Date.now() - started,
|
|
585
|
+
ok: child.status === 0 && !child.error && !!result,
|
|
586
|
+
raw: stdout + stderr,
|
|
587
|
+
stdout,
|
|
588
|
+
stderr,
|
|
589
|
+
exitCode: child.status,
|
|
590
|
+
signal: child.signal || null,
|
|
591
|
+
error: child.error ? String(child.error.message || child.error) : null,
|
|
592
|
+
};
|
|
494
593
|
}
|
|
495
594
|
|
|
496
595
|
function fmtDeltaEntry(entry) {
|
|
@@ -505,12 +604,13 @@ function runLeadMode({ route, effort, fast, repeat, jsonMode, leadPrompt }) {
|
|
|
505
604
|
const sandboxRoot = mkdtempSync(join(REPO_ROOT, '.tmp-internal-comms-bench-lead-'));
|
|
506
605
|
const perVariant = { A: [], B: [] };
|
|
507
606
|
const runsMeta = { A: [], B: [] };
|
|
607
|
+
let invalidRun = null;
|
|
508
608
|
try {
|
|
509
609
|
const pluginRoots = {
|
|
510
610
|
A: materializePluginRoot('A', sandboxRoot),
|
|
511
611
|
B: materializePluginRoot('B', sandboxRoot),
|
|
512
612
|
};
|
|
513
|
-
for (let i = 0; i < repeat; i += 1) {
|
|
613
|
+
runLoop: for (let i = 0; i < repeat; i += 1) {
|
|
514
614
|
for (const variant of ['A', 'B']) {
|
|
515
615
|
const taskCwd = prepareTaskCwd(sandboxRoot);
|
|
516
616
|
const dataDir = prepareDataDir(sandboxRoot, `${variant}-r${i}`, realDataDir, userUnified, route.provider);
|
|
@@ -526,15 +626,44 @@ function runLeadMode({ route, effort, fast, repeat, jsonMode, leadPrompt }) {
|
|
|
526
626
|
fast,
|
|
527
627
|
});
|
|
528
628
|
const split = run.sessionId ? splitTokensByRole(dataDir, run.sessionId) : emptyRoleSplit(dataDir);
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
629
|
+
const validation = validateLeadRun({ taskCwd, run, split });
|
|
630
|
+
if (validation.valid) perVariant[variant].push(split);
|
|
631
|
+
const meta = {
|
|
632
|
+
ok: run.ok,
|
|
633
|
+
valid: validation.valid,
|
|
634
|
+
reasons: validation.reasons,
|
|
635
|
+
ms: run.ms,
|
|
636
|
+
sessionId: run.sessionId,
|
|
637
|
+
total: split.total.total_tokens,
|
|
638
|
+
diagnostics: {
|
|
639
|
+
stdout: run.stdout,
|
|
640
|
+
stderr: run.stderr,
|
|
641
|
+
exitCode: run.exitCode,
|
|
642
|
+
signal: run.signal,
|
|
643
|
+
error: run.error,
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
runsMeta[variant].push(meta);
|
|
647
|
+
process.stderr.write(`[internal-comms-bench] -> ${validation.valid ? 'ok' : 'FAIL'} ${Math.round(run.ms / 1000)}s session=${run.sessionId || '(none)'} total=${split.total.total_tokens} lead=${split.byRole.lead.total_tokens} worker=${split.byRole.worker.total_tokens} reviewer=${split.byRole.reviewer.total_tokens} other=${split.byRole.other.total_tokens}${validation.valid ? '' : ` reasons=${validation.reasons.join('; ')}`}\n`);
|
|
648
|
+
if (!validation.valid && (run.stdout || run.stderr || run.error)) {
|
|
649
|
+
process.stderr.write(`[internal-comms-bench] child diagnostics variant=${variant} stdout=${JSON.stringify(run.stdout)} stderr=${JSON.stringify(run.stderr)} error=${JSON.stringify(run.error)}\n`);
|
|
650
|
+
}
|
|
651
|
+
if (!validation.valid) {
|
|
652
|
+
invalidRun = { variant, index: i, reasons: validation.reasons };
|
|
653
|
+
break runLoop;
|
|
654
|
+
}
|
|
532
655
|
}
|
|
533
656
|
}
|
|
534
657
|
} finally {
|
|
535
658
|
rmSync(sandboxRoot, { recursive: true, force: true });
|
|
536
659
|
}
|
|
537
660
|
|
|
661
|
+
if (invalidRun) {
|
|
662
|
+
process.stderr.write(`[internal-comms-bench] aborting before aggregation: invalid variant=${invalidRun.variant} run=${invalidRun.index + 1} reasons=${invalidRun.reasons.join('; ')}\n`);
|
|
663
|
+
process.exit(1);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
538
667
|
const aggA = aggregateVariant(perVariant.A);
|
|
539
668
|
const aggB = aggregateVariant(perVariant.B);
|
|
540
669
|
const deltaMedian = {};
|
|
@@ -574,9 +703,7 @@ function runLeadMode({ route, effort, fast, repeat, jsonMode, leadPrompt }) {
|
|
|
574
703
|
console.log('delta B-vs-A (mean): ' + [...ROLES, 'total'].map((r) => `${r}=${fmtDeltaEntry(deltaMean[r])}`).join(' '));
|
|
575
704
|
}
|
|
576
705
|
|
|
577
|
-
|
|
578
|
-
const anyUsage = perVariant.A.some((s) => s.total.usage_rows > 0) || perVariant.B.some((s) => s.total.usage_rows > 0);
|
|
579
|
-
process.exit(allOk && anyUsage ? 0 : 1);
|
|
706
|
+
process.exit(leadModeExitCode(runsMeta, perVariant));
|
|
580
707
|
}
|
|
581
708
|
|
|
582
709
|
function pctChange(b, a) {
|
|
@@ -724,4 +851,4 @@ function main() {
|
|
|
724
851
|
process.exit(ok ? 0 : 1);
|
|
725
852
|
}
|
|
726
853
|
|
|
727
|
-
main();
|
|
854
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();
|