feat: add process runner for subagents
This commit is contained in:
113
src/process-runner.test.ts
Normal file
113
src/process-runner.test.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { createRunArtifacts } from "./artifacts.ts";
|
||||
import { monitorRun } from "./monitor.ts";
|
||||
import { createProcessSingleRunner } from "./process-runner.ts";
|
||||
|
||||
class FakeChild extends EventEmitter {}
|
||||
|
||||
test("createProcessSingleRunner launches wrapper without tmux and returns monitored result", async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "pi-subagents-process-"));
|
||||
let metaPathSeen = "";
|
||||
|
||||
const runSingleTask = createProcessSingleRunner({
|
||||
createArtifacts: createRunArtifacts,
|
||||
buildWrapperSpawn(metaPath: string) {
|
||||
metaPathSeen = metaPath;
|
||||
return { command: process.execPath, args: ["-e", "process.exit(0)"] };
|
||||
},
|
||||
spawnChild() {
|
||||
const child = new FakeChild() as any;
|
||||
process.nextTick(async () => {
|
||||
const meta = JSON.parse(await readFile(metaPathSeen, "utf8"));
|
||||
await writeFile(
|
||||
meta.resultPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
runId: meta.runId,
|
||||
mode: meta.mode,
|
||||
agent: meta.agent,
|
||||
agentSource: meta.agentSource,
|
||||
task: meta.task,
|
||||
requestedModel: meta.requestedModel,
|
||||
resolvedModel: meta.resolvedModel,
|
||||
sessionPath: meta.sessionPath,
|
||||
exitCode: 0,
|
||||
finalText: "done",
|
||||
stdoutPath: meta.stdoutPath,
|
||||
stderrPath: meta.stderrPath,
|
||||
transcriptPath: meta.transcriptPath,
|
||||
resultPath: meta.resultPath,
|
||||
eventsPath: meta.eventsPath,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
child.emit("close", 0);
|
||||
});
|
||||
return child;
|
||||
},
|
||||
monitorRun: (input) => monitorRun({ ...input, pollMs: 1 }),
|
||||
});
|
||||
|
||||
const result = await runSingleTask({
|
||||
cwd,
|
||||
meta: {
|
||||
mode: "single",
|
||||
agent: "scout",
|
||||
agentSource: "builtin",
|
||||
task: "inspect auth",
|
||||
requestedModel: "openai/gpt-5",
|
||||
resolvedModel: "openai/gpt-5",
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.finalText, "done");
|
||||
assert.equal(result.exitCode, 0);
|
||||
assert.match(result.resultPath ?? "", /\.pi\/subagents\/runs\//);
|
||||
});
|
||||
|
||||
test("createProcessSingleRunner writes error result.json when wrapper launch fails", async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "pi-subagents-process-"));
|
||||
|
||||
const runSingleTask = createProcessSingleRunner({
|
||||
createArtifacts: createRunArtifacts,
|
||||
buildWrapperSpawn() {
|
||||
return { command: process.execPath, args: ["-e", "process.exit(0)"] };
|
||||
},
|
||||
spawnChild() {
|
||||
const child = new FakeChild() as any;
|
||||
process.nextTick(() => {
|
||||
child.emit("error", new Error("spawn boom"));
|
||||
});
|
||||
return child;
|
||||
},
|
||||
monitorRun: (input) => monitorRun({ ...input, pollMs: 1 }),
|
||||
});
|
||||
|
||||
const result = await runSingleTask({
|
||||
cwd,
|
||||
meta: {
|
||||
mode: "single",
|
||||
agent: "scout",
|
||||
agentSource: "builtin",
|
||||
task: "inspect auth",
|
||||
requestedModel: "openai/gpt-5",
|
||||
resolvedModel: "openai/gpt-5",
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result.exitCode, 1);
|
||||
assert.equal(result.stopReason, "error");
|
||||
assert.match(result.errorMessage ?? "", /spawn boom/);
|
||||
|
||||
const saved = JSON.parse(await readFile(result.resultPath!, "utf8"));
|
||||
assert.equal(saved.exitCode, 1);
|
||||
assert.match(saved.errorMessage ?? "", /spawn boom/);
|
||||
});
|
||||
Reference in New Issue
Block a user