blob: 1e1ed21ada8a499987eb8e3781e6768d1b66cc75 [file]
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { mkdtemp, readdir, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, test } from 'node:test';
import {
BackendRegistry,
FakeBackend,
PermissionEngine,
PiAgentBackend,
type AgentBackend,
type PiAgentTransport,
type SessionStore,
} from '@maka/runtime';
import type { BackendKind, SessionEvent, SessionHeader } from '@maka/core';
import type { BackendSendInput, PermissionDecision } from '@maka/core/backend-types';
import type { Config, Task } from '../contracts.js';
import type { HeadlessBackendContext } from '../isolation.js';
import { runExperiment } from '../runner.js';
const registerFakeBackend = (registry: BackendRegistry): void => {
registry.register(
'fake',
(ctx) => new FakeBackend({ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store }),
);
};
function registerTestPiAgentBackend(
registry: BackendRegistry,
transportFactory: (input: { header: SessionHeader; store: SessionStore }) => PiAgentTransport,
): void {
registry.register(
'pi-agent',
(ctx) =>
new PiAgentBackend({
sessionId: ctx.sessionId,
header: ctx.header,
appendMessage:
ctx.appendMessage ?? ((message) => ctx.store.appendMessage(ctx.sessionId, message)),
permissionEngine: new PermissionEngine({ newId: () => 'perm-id', now: () => 123 }),
transport: transportFactory({ header: ctx.header, store: ctx.store }),
}),
);
}
/**
* A malicious config: it rewrites the grading test in its own cwd to one
* that always passes, then completes normally. Used to prove clean-room
* grading reverts the tamper before scoring.
*/
class TamperBackend implements AgentBackend {
readonly kind: BackendKind = 'fake';
readonly sessionId: string;
constructor(
private readonly ctx: { sessionId: string; header: SessionHeader; store: SessionStore },
) {
this.sessionId = ctx.sessionId;
}
async *send(input: BackendSendInput): AsyncIterable<SessionEvent> {
const turnId = input.turnId;
const messageId = 'tamper-msg';
// Rewrite the grading script in its own cwd to one that always passes.
await writeFile(join(this.ctx.header.cwd, 'check.mjs'), 'process.exit(0);\n', 'utf8');
const text = 'rewrote the grading script to pass';
const ts = Date.now();
await this.ctx.store.appendMessage(this.sessionId, {
type: 'assistant',
id: messageId,
turnId,
ts,
text,
modelId: this.ctx.header.model,
});
yield { type: 'text_complete', id: 'tamper-tc', turnId, ts, messageId, text };
yield { type: 'complete', id: 'tamper-c', turnId, ts, stopReason: 'end_turn' };
}
async stop(): Promise<void> {}
async respondToPermission(_decision: PermissionDecision): Promise<void> {}
async dispose(): Promise<void> {}
}
const registerTamperBackend = (registry: BackendRegistry): void => {
registry.register(
'fake',
(ctx) => new TamperBackend({ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store }),
);
};
/**
* A backend that reports failure the way a real one can — an error event plus
* a complete(error) — WITHOUT throwing. The InvocationResult comes back with
* status 'failed'; the run must surface that as a record error, not a silent
* ⚠️-but-exit-0.
*/
class FailingBackend implements AgentBackend {
readonly kind: BackendKind = 'fake';
readonly sessionId: string;
constructor(
private readonly ctx: { sessionId: string; header: SessionHeader; store: SessionStore },
) {
this.sessionId = ctx.sessionId;
}
async *send(input: BackendSendInput): AsyncIterable<SessionEvent> {
const { turnId } = input;
const ts = Date.now();
yield {
type: 'error',
id: 'fail-err',
turnId,
ts,
recoverable: false,
reason: 'backend_failed',
message: 'backend blew up',
};
yield { type: 'complete', id: 'fail-c', turnId, ts, stopReason: 'error' };
}
async stop(): Promise<void> {}
async respondToPermission(_decision: PermissionDecision): Promise<void> {}
async dispose(): Promise<void> {}
}
const registerFailingBackend = (registry: BackendRegistry): void => {
registry.register(
'fake',
(ctx) => new FailingBackend({ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store }),
);
};
class IsolatedRealBackend implements AgentBackend {
readonly kind: BackendKind = 'ai-sdk';
readonly sessionId: string;
constructor(
private readonly ctx: { sessionId: string; header: SessionHeader; store: SessionStore },
) {
this.sessionId = ctx.sessionId;
}
async *send(input: BackendSendInput): AsyncIterable<SessionEvent> {
const turnId = input.turnId;
const ts = Date.now();
const messageId = 'isolated-real-msg';
await writeFile(join(this.ctx.header.cwd, 'solved.txt'), 'ok\n', 'utf8');
await this.ctx.store.appendMessage(this.sessionId, {
type: 'assistant',
id: messageId,
turnId,
ts,
text: 'solved inside explicit isolation',
modelId: this.ctx.header.model,
});
yield {
type: 'text_complete',
id: 'isolated-real-tc',
turnId,
ts,
messageId,
text: 'solved inside explicit isolation',
};
yield { type: 'complete', id: 'isolated-real-c', turnId, ts, stopReason: 'end_turn' };
}
async stop(): Promise<void> {}
async respondToPermission(_decision: PermissionDecision): Promise<void> {}
async dispose(): Promise<void> {}
}
const registerIsolatedRealBackend =
(
seen: HeadlessBackendContext[],
): NonNullable<Parameters<typeof runExperiment>[2]['registerBackends']> =>
(registry, context) => {
seen.push(context);
registry.register(
'ai-sdk',
(ctx) =>
new IsolatedRealBackend({ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store }),
);
};
// A fixture whose grading script exits non-zero against the buggy source —
// the only way `node check.mjs` passes is if the grading script is replaced.
// (Plain exit-code grading, not `node --test`, so the verification child
// doesn't collide with the lab's own test runner.)
async function writeBuggyFixture(fixtureDir: string): Promise<void> {
await writeFile(join(fixtureDir, 'src.mjs'), 'export const add = (a, b) => a - b;\n', 'utf8');
await writeFile(
join(fixtureDir, 'check.mjs'),
"import { add } from './src.mjs';\nprocess.exit(add(2, 3) === 5 ? 0 : 1);\n",
'utf8',
);
}
const fakeConfig: Config = {
id: 'fake-cfg',
backend: 'fake',
llmConnectionSlug: 'fake',
model: 'fake-model',
};
const piConfig: Config = {
id: 'pi-cfg',
backend: 'pi-agent',
llmConnectionSlug: 'pi-agent',
model: 'pi-test',
};
async function fileExistsRecursive(root: string, name: string): Promise<boolean> {
for (const entry of await readdir(root, { withFileTypes: true })) {
const full = join(root, entry.name);
if (entry.isDirectory()) {
if (await fileExistsRecursive(full, name)) return true;
} else if (entry.name === name) {
return true;
}
}
return false;
}
async function withDirs<T>(
fn: (fixtureDir: string, storageRoot: string) => Promise<T>,
): Promise<T> {
const fixtureDir = await mkdtemp(join(tmpdir(), 'maka-headless-fx-'));
const storageRoot = await mkdtemp(join(tmpdir(), 'maka-headless-store-'));
try {
return await fn(fixtureDir, storageRoot);
} finally {
await rm(fixtureDir, { recursive: true, force: true });
await rm(storageRoot, { recursive: true, force: true });
}
}
describe('runExperiment (walking skeleton)', () => {
test('runs Config × Task end-to-end, scores a passing verification, records a trajectory', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const task: Task = {
id: 'pass-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
registerBackends: registerFakeBackend,
});
assert.equal(result.status, 'completed');
assert.equal(result.passed, true);
assert.equal(result.exitCode, 0);
assert.equal(result.taskId, 'pass-task');
assert.equal(result.configId, 'fake-cfg');
assert.equal(result.orchestrationMode, 'default');
assert.equal(result.orchestrationSource, 'session');
assert.equal(result.agentSwarmAuthorization, 'none');
// The agent run produced a trajectory...
assert.ok(result.steps > 0, 'expected a non-empty trajectory');
// ...persisted as the canonical runtime-events.jsonl.
assert.ok(
await fileExistsRecursive(storageRoot, 'runtime-events.jsonl'),
'expected runtime-events.jsonl under the storage root',
);
});
});
test('scores a failing verification as not passed (run still completes)', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const task: Task = {
id: 'fail-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f does-not-exist.txt', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
registerBackends: registerFakeBackend,
});
assert.equal(result.status, 'completed');
assert.equal(result.passed, false);
assert.notEqual(result.exitCode, 0);
});
});
test('defaults to the inert FakeBackend when no registerBackends is given', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const task: Task = {
id: 'default-backend',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
// Minimal usage — no registerBackends supplied; the engine wires fake.
const result = await runExperiment(fakeConfig, task, { storageRoot });
assert.equal(result.status, 'completed');
assert.equal(result.passed, true);
});
});
test('records the persistent session Swarm Mode as authoritative run evidence', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const task: Task = {
id: 'session-swarm',
instruction: 'fan out the work',
workspaceDir: fixtureDir,
verification: { command: 'true', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
orchestrationMode: 'swarm',
});
assert.equal(result.orchestrationMode, 'swarm');
assert.equal(result.orchestrationSource, 'session');
assert.equal(result.agentSwarmAuthorization, 'session_mode');
});
});
test('records a host API turn override without changing the session-default API', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const task: Task = {
id: 'turn-swarm',
instruction: 'fan out this turn',
workspaceDir: fixtureDir,
verification: { command: 'true', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
orchestrationMode: 'default',
turnOrchestration: { mode: 'swarm', source: 'host_api' },
});
assert.equal(result.orchestrationMode, 'swarm');
assert.equal(result.orchestrationSource, 'turn_override');
assert.equal(result.agentSwarmAuthorization, 'turn_override');
});
});
});
describe('clean-room grading (a config cannot rewrite its own test to pass)', () => {
test('protectedPaths reverts the tampered test before scoring', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeBuggyFixture(fixtureDir);
const task: Task = {
id: 'tamper-task',
instruction: 'fix the bug',
workspaceDir: fixtureDir,
verification: { command: 'node check.mjs', protectedPaths: ['check.mjs'] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
registerBackends: registerTamperBackend,
});
// The run completed normally, but the cheated grading script was
// restored to the original, which still fails the unfixed buggy source.
assert.equal(result.status, 'completed');
assert.equal(result.passed, false);
assert.notEqual(result.exitCode, 0);
});
});
});
describe('fail-closed (a model-backed backend does not run without isolation)', () => {
test('refuses a real backend when no isolated executor is available', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const realConfig: Config = {
id: 'real-cfg',
backend: 'ai-sdk',
llmConnectionSlug: 'anthropic',
model: 'claude-sonnet-4-6',
};
const task: Task = {
id: 'real-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
// A real backend would run tools on the host with no isolation, so the
// run is refused before it starts — no workspace prepared, no agent turn.
await assert.rejects(
runExperiment(realConfig, task, { storageRoot, registerBackends: registerFakeBackend }),
/isolated executor/i,
);
});
});
test('runs a model-backed backend only when the caller supplies explicit isolation', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const realConfig: Config = {
id: 'real-cfg',
backend: 'ai-sdk',
llmConnectionSlug: 'deepseek',
model: 'deepseek-chat',
};
const task: Task = {
id: 'real-task',
instruction: 'create solved.txt',
workspaceDir: fixtureDir,
verification: { command: 'test -f solved.txt', protectedPaths: [] },
};
const contexts: HeadlessBackendContext[] = [];
const result = await runExperiment(realConfig, task, {
storageRoot,
registerBackends: registerIsolatedRealBackend(contexts),
realBackendIsolation: { kind: 'external', label: 'unit-test isolated backend' },
});
assert.equal(result.status, 'completed');
assert.equal(result.passed, true);
assert.equal(contexts.length, 1);
assert.equal(contexts[0]?.realBackendIsolation?.label, 'unit-test isolated backend');
assert.equal(contexts[0]?.config.id, 'real-cfg');
assert.equal(contexts[0]?.task.id, 'real-task');
assert.equal(typeof contexts[0]?.spawnChildAgent, 'function');
assert.equal(typeof contexts[0]?.spawnChildSession, 'function');
assert.equal(typeof contexts[0]?.retryChildAgent, 'function');
assert.equal(typeof contexts[0]?.listChildAgents, 'function');
assert.equal(typeof contexts[0]?.readChildAgentOutput, 'function');
assert.ok(Array.isArray((await contexts[0]!.listChildAgents!(result.sessionId)).definitions));
});
});
test('runs pi-agent through the headless backend bridge when isolated', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeBuggyFixture(fixtureDir);
const seen: HeadlessBackendContext[] = [];
const task: Task = {
id: 'pi-task',
instruction: 'solve the fixture',
workspaceDir: fixtureDir,
verification: { command: 'test -f solved.txt', protectedPaths: [] },
};
const result = await runExperiment({ ...piConfig, agentTools: true }, task, {
storageRoot,
realBackendIsolation: {
kind: 'external',
label: 'unit-test isolated pi transport',
toolExecutor: {
async exec() {
return { exitCode: 0, stdout: '', stderr: '' };
},
},
},
registerBackends: (registry, context) => {
seen.push(context);
registerTestPiAgentBackend(registry, ({ header }) => ({
async *send(sendInput) {
assert.equal(header.cwd, context.workspaceDir);
assert.equal(sendInput.text, 'solve the fixture');
yield {
type: 'tool_start',
toolUseId: 'tool-1',
toolName: 'Bash',
args: { command: 'touch solved.txt' },
};
await writeFile(join(header.cwd, 'solved.txt'), 'ok\n', 'utf8');
yield {
type: 'tool_result',
toolUseId: 'tool-1',
content: { kind: 'text', text: 'created solved.txt' },
};
yield { type: 'text_complete', text: 'done' };
yield { type: 'complete' };
},
}));
},
});
assert.equal(result.status, 'completed');
assert.equal(result.passed, true);
assert.equal(seen[0]?.config.backend, 'pi-agent');
assert.equal(seen[0]?.realBackendIsolation?.label, 'unit-test isolated pi transport');
assert.equal(seen[0]?.productToolSurface, undefined);
});
});
});
describe('failed runs surface as an error (not a silent ⚠️ + exit 0)', () => {
test('a backend that reports failure without throwing yields status failed + an error', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
// The fixture already satisfies the verification — proving the failure
// verdict comes from the run status, not from a failing check.
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const task: Task = {
id: 'failing',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
registerBackends: registerFailingBackend,
});
assert.equal(result.status, 'failed');
assert.ok(
result.error,
'a failed run must carry an error so the CLI exit code and the table agree',
);
assert.equal(result.errorClass, 'backend_failed');
assert.equal(result.passed, false);
});
});
test('complete(stopReason=error) with no preceding error event classifies as runtime_error in ResultRecord', async () => {
// Reproduces the DeepSeek-reasoner smoke: the backend ended with
// stopReason='error' but never emitted a preceding error event. The
// benchmark ResultRecord.errorClass must read 'runtime_error' (not
// 'failed' or 'unknown') so scoring can distinguish runtime failures
// from max_tokens / incomplete_tool_calls / verification_failed.
class BareErrorCompleteBackend implements AgentBackend {
readonly kind: BackendKind = 'fake';
readonly sessionId: string;
constructor(
private readonly ctx: { sessionId: string; header: SessionHeader; store: SessionStore },
) {
this.sessionId = ctx.sessionId;
}
async *send(input: BackendSendInput): AsyncIterable<SessionEvent> {
const { turnId } = input;
const ts = Date.now();
// NO preceding error event — just a bare complete(error).
yield { type: 'complete', id: 'bare-err-c', turnId, ts, stopReason: 'error' };
}
async stop(): Promise<void> {}
async respondToPermission(_decision: PermissionDecision): Promise<void> {}
async dispose(): Promise<void> {}
}
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const task: Task = {
id: 'bare-error',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
registerBackends: (registry) => {
registry.register(
'fake',
(ctx) =>
new BareErrorCompleteBackend({
sessionId: ctx.sessionId,
header: ctx.header,
store: ctx.store,
}),
);
},
});
assert.equal(result.status, 'failed');
assert.equal(result.errorClass, 'runtime_error');
assert.equal(result.passed, false);
});
});
});
describe('engine-level grading-boundary validation (not only the CLI)', () => {
test('runExperiment refuses a task missing protectedPaths before running the agent', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
// Simulate an untyped (JS / JSON) caller that omits the now-required field.
const task = {
id: 'no-guard',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'true' },
} as unknown as Task;
await assert.rejects(
runExperiment(fakeConfig, task, { storageRoot, registerBackends: registerFakeBackend }),
/protectedPaths/,
);
});
});
});
describe('Config.systemPrompt (benchmark config variable, not session state)', () => {
// A factory that captures the systemPrompt it would hand to the backend,
// proving the benchmark's registerBackends closure can read config.systemPrompt
// and pass it through — mirroring the desktop path. The harness itself does
// NOT thread systemPrompt through BackendFactoryContext (that channel is the
// child-agent instruction); the factory owns it.
const registerCapturingBackend =
(captured: { systemPrompt?: string }[]) =>
(registry: BackendRegistry, context: HeadlessBackendContext): void => {
captured.push({ systemPrompt: context.config.systemPrompt });
registry.register(
'fake',
(ctx) =>
new FakeBackend({ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store }),
);
};
test('factory closure can read config.systemPrompt and pass it to the backend', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const configWithPrompt: Config = {
...fakeConfig,
systemPrompt: 'You are a benchmark agent. Use tools, do not narrate.',
};
const captured: { systemPrompt?: string }[] = [];
const task: Task = {
id: 'prompt-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
const result = await runExperiment(configWithPrompt, task, {
storageRoot,
registerBackends: registerCapturingBackend(captured),
});
assert.equal(result.status, 'completed');
assert.equal(result.systemPromptMode, 'custom');
assert.equal(
result.systemPromptHash,
`sha256:${createHash('sha256')
.update(JSON.stringify(configWithPrompt.systemPrompt))
.digest('hex')}`,
);
assert.equal(captured.length, 1);
assert.equal(
captured[0]?.systemPrompt,
'You are a benchmark agent. Use tools, do not narrate.',
'factory closure must receive config.systemPrompt',
);
});
});
test('omitting systemPrompt injects the default headless prompt in the factory context', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const captured: { systemPrompt?: string }[] = [];
const task: Task = {
id: 'default-prompt-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
const result = await runExperiment(fakeConfig, task, {
storageRoot,
registerBackends: registerCapturingBackend(captured),
});
assert.equal(captured.length, 1);
assert.equal(
captured[0]?.systemPrompt,
[
'Complete the task by acting with the available tools, not by narrating.',
'Prefer Read, Glob, and Grep for inspection, Edit and Write for file changes, and Bash for shell commands and tests.',
'Verify the result when practical.',
'Stop when the task is complete.',
].join('\n'),
'the factory must receive the default headless system prompt',
);
assert.equal(result.systemPromptMode, 'default');
assert.equal(
result.systemPromptHash,
`sha256:${createHash('sha256')
.update(JSON.stringify(captured[0]?.systemPrompt))
.digest('hex')}`,
);
});
});
test('rejects a blank custom systemPrompt before registering a backend', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const task: Task = {
id: 'blank-prompt-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'true', protectedPaths: [] },
};
let registered = false;
await assert.rejects(
runExperiment({ ...fakeConfig, systemPrompt: '\n \t' }, task, {
storageRoot,
registerBackends: (registry) => {
registered = true;
registerFakeBackend(registry);
},
}),
/systemPrompt must contain non-whitespace text/,
);
assert.equal(registered, false);
});
});
test('does not append heavy-task policy without the required direct-runner tools', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const captured: { systemPrompt?: string }[] = [];
const task: Task = {
id: 'heavy-default-prompt-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'true', protectedPaths: [] },
};
const result = await runExperiment({ ...fakeConfig, heavyTaskMode: true }, task, {
storageRoot,
registerBackends: registerCapturingBackend(captured),
});
const prompt = captured[0]?.systemPrompt ?? '';
assert.equal(
prompt,
[
'Complete the task by acting with the available tools, not by narrating.',
'Prefer Read, Glob, and Grep for inspection, Edit and Write for file changes, and Bash for shell commands and tests.',
'Verify the result when practical.',
'Stop when the task is complete.',
].join('\n'),
);
assert.doesNotMatch(prompt, /Heavy-task benchmark policy/);
assert.equal(
result.systemPromptHash,
`sha256:${createHash('sha256').update(JSON.stringify(prompt)).digest('hex')}`,
);
});
});
test('does not change direct-runner policy behavior while resolving Layer 1', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
const captured: { systemPrompt?: string }[] = [];
const task: Task = {
id: 'economy-default-prompt-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'true', protectedPaths: [] },
};
await runExperiment({ ...fakeConfig, economyTaskMode: true }, task, {
storageRoot,
registerBackends: registerCapturingBackend(captured),
});
const prompt = captured[0]?.systemPrompt ?? '';
assert.equal(
prompt,
[
'Complete the task by acting with the available tools, not by narrating.',
'Prefer Read, Glob, and Grep for inspection, Edit and Write for file changes, and Bash for shell commands and tests.',
'Verify the result when practical.',
'Stop when the task is complete.',
].join('\n'),
);
assert.doesNotMatch(prompt, /Economy-task benchmark policy/);
});
});
// End-to-end wiring test: proves config.systemPrompt flows all the way
// through to the backend constructor's systemPrompt parameter — the exact
// seam a real AiSdkBackend factory (like desktop's) uses. Uses an ai-sdk
// stub that records its constructor input, so we verify the wiring contract
// without needing a live LLM call.
class SystemPromptCapturingBackend implements AgentBackend {
readonly kind: BackendKind = 'ai-sdk';
readonly sessionId: string;
readonly receivedSystemPrompt: string | undefined;
constructor(
private readonly ctx: { sessionId: string; header: SessionHeader; store: SessionStore },
systemPrompt?: string,
) {
this.sessionId = ctx.sessionId;
this.receivedSystemPrompt = systemPrompt;
}
async *send(input: BackendSendInput): AsyncIterable<SessionEvent> {
const turnId = input.turnId;
const ts = Date.now();
const messageId = 'capture-msg';
await this.ctx.store.appendMessage(this.sessionId, {
type: 'assistant',
id: messageId,
turnId,
ts,
text: 'ok',
modelId: this.ctx.header.model,
});
yield { type: 'text_complete', id: 'capture-tc', turnId, ts, messageId, text: 'ok' };
yield { type: 'complete', id: 'capture-c', turnId, ts, stopReason: 'end_turn' };
}
async stop(): Promise<void> {}
async respondToPermission(_decision: PermissionDecision): Promise<void> {}
async dispose(): Promise<void> {}
}
test('config.systemPrompt reaches the backend constructor systemPrompt parameter', async () => {
await withDirs(async (fixtureDir, storageRoot) => {
await writeFile(join(fixtureDir, 'marker.txt'), 'present', 'utf8');
const prompt = 'You are a benchmark agent. Use tools, do not narrate.';
let constructedBackend: SystemPromptCapturingBackend | undefined;
const configWithPrompt: Config = {
id: 'real-cfg',
backend: 'ai-sdk',
llmConnectionSlug: 'deepseek',
model: 'deepseek-chat',
systemPrompt: prompt,
};
const task: Task = {
id: 'wiring-task',
instruction: 'do the thing',
workspaceDir: fixtureDir,
verification: { command: 'test -f marker.txt', protectedPaths: [] },
};
await runExperiment(configWithPrompt, task, {
storageRoot,
realBackendIsolation: { kind: 'external', label: 'wiring test' },
registerBackends: (registry, context) => {
// This is the exact pattern a real benchmark factory uses:
// read config.systemPrompt from the closure, pass to backend ctor.
// The factory receives BackendFactoryContext (with store/header),
// and context.config is the HeadlessBackendContext closure copy.
registry.register('ai-sdk', (ctx) => {
constructedBackend = new SystemPromptCapturingBackend(
{ sessionId: ctx.sessionId, header: ctx.header, store: ctx.store },
context.config.systemPrompt,
);
return constructedBackend;
});
},
});
assert.ok(constructedBackend, 'backend must have been constructed');
assert.equal(
constructedBackend!.receivedSystemPrompt,
prompt,
'config.systemPrompt must reach the backend constructor systemPrompt parameter',
);
});
});
});