import assert from "node:assert/strict"; import * as path from "node:path"; import test from "node:test"; import { fileURLToPath } from "node:url"; const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const extensionPath = path.join(packageRoot, "extensions", "subagent-timeout-safety", "index.ts"); async function loadHarness(environment: Record = {}) { const prior = new Map(Object.entries(environment).map(([key]) => [key, process.env[key]])); for (const [key, value] of Object.entries(environment)) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } try { const { default: register } = await import(`${extensionPath}?test=${Math.random()}`); const handlers = new Map(); const commands = new Map(); const messages: unknown[] = []; register({ on(name: string, handler: Function) { handlers.set(name, handler); }, registerCommand(name: string, definition: unknown) { commands.set(name, definition); }, sendMessage(message: unknown) { messages.push(message); }, }); return { handlers, commands, messages }; } finally { for (const [key, value] of prior) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } } } const ctx = { cwd: packageRoot, hasUI: false, ui: { notify() {} } }; test("package extension registers exactly the tool policy and command", async () => { const harness = await loadHarness({ PI_SUBAGENT_CHILD: undefined }); assert.equal(typeof harness.handlers.get("tool_call"), "function"); assert.equal(harness.commands.has("subagent-timeout-safety"), true); }); test("tool_call mutates Pi's validated writer input before execution", async () => { const harness = await loadHarness({ PI_SUBAGENT_CHILD: undefined }); const input = { agent: "worker", task: "Implement the change.", timeoutMs: 99, maxRuntimeMs: 99, turnBudget: { maxTurns: 2 } }; await harness.handlers.get("tool_call")?.({ toolName: "subagent", toolCallId: "writer", input }, ctx); assert.equal("timeoutMs" in input, false); assert.equal("maxRuntimeMs" in input, false); assert.equal("turnBudget" in input, false); assert.match(input.task, /Mutation-safe deadline policy is active/); assert.equal(harness.messages.length, 1); assert.deepEqual((harness.messages[0] as { details: unknown }).details, { removed: ["timeoutMs", "maxRuntimeMs", "turnBudget"], checkpointPromptsAdded: 1, reason: "worker has mutation-capable tools", action: "execute", }); }); test("tool_call still protects a writer whose task says read-only", async () => { const harness = await loadHarness({ PI_SUBAGENT_CHILD: undefined }); const readOnly = { agent: "worker", task: "Read-only review. Do not modify files.", timeoutMs: 99 }; const management = { action: "status", id: "run", timeoutMs: 99 }; await harness.handlers.get("tool_call")?.({ toolName: "subagent", toolCallId: "read-only", input: readOnly }, ctx); await harness.handlers.get("tool_call")?.({ toolName: "subagent", toolCallId: "status", input: management }, ctx); assert.equal("timeoutMs" in readOnly, false); assert.equal(management.timeoutMs, 99); assert.equal(harness.messages.length, 1); }); test("child process exclusion registers no hooks or command", async () => { const harness = await loadHarness({ PI_SUBAGENT_CHILD: "1" }); assert.equal(harness.handlers.size, 0); assert.equal(harness.commands.size, 0); });