Menu
popagent
publicLatest change c5300493b2cdef9ff6d10de92b8a15067eac3e8b - Permit autonomous skill instruction reads by AkurAI Build
import { describe, expect, test } from "bun:test";
import type { Agent } from "@mastra/core/agent";
import type { AgentTask } from "./api-types";
import type { AgentExecutionRuntime } from "./agent-execution";
import {
isContainmentDeniedEvidence,
runSelfUpdateWorkflow,
type SelfUpdateWorkflowAgents,
type SelfUpdateWorkflowDependencies,
} from "./self-update-workflow";
import type { SelfUpdateRun } from "./self-update-policy";
const DIFF_DIGEST = "1".repeat(64);
const run: SelfUpdateRun = {
taskId: "workflow-task",
workspaceId: "workflow-workspace",
repositoryPath: "/registered",
path: "/contained/self-update",
branch: "autonomous/self-update/test",
snapshot: { head: "a".repeat(40), branch: "main", remotes: "remote-hash" },
cloneRemotes: "clone-remote-hash",
delegations: new Set<string>(),
deploymentEnabled: false,
};
function task(): AgentTask {
return {
id: run.taskId,
sessionId: "workflow-session",
scheduleId: "workflow-schedule",
source: "self-update",
workspaceId: run.workspaceId,
prompt: "Improve the contained repository",
model: "test/model",
status: "running",
output: null,
error: null,
stepsCompleted: 0,
progress: null,
recoveryCount: 0,
attemptCount: 1,
maxAttempts: 1,
nextAttemptAt: null,
lastErrorClass: null,
deadLetteredAt: null,
createdAt: "",
startedAt: "",
completedAt: null,
};
}
function fakeAgent(
onGenerate: (phase: string) => unknown | Promise<unknown>,
): Agent {
const fake = {
__fork: () => fake,
setBrowser: () => undefined,
getModel: () => ({ provider: "test", modelId: "structurer" }),
stream: async (_prompt: string, options: { runId?: string }) => ({
object: Promise.resolve(await onGenerate(options.runId?.split("-").at(-1) ?? "")),
}),
};
return fake as unknown as Agent;
}
function agents(
onGenerate: (phase: string) => unknown | Promise<unknown>,
): SelfUpdateWorkflowAgents {
return {
researcher: fakeAgent(onGenerate),
orchistrator: fakeAgent(onGenerate),
implementer: fakeAgent(onGenerate),
reviewer: fakeAgent(onGenerate),
};
}
function dependencies(
signal: AbortSignal,
workflowAgents: SelfUpdateWorkflowAgents,
calls: string[],
reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }>,
): SelfUpdateWorkflowDependencies {
let inspections = 0;
return {
execution: {
prepare: async (input: { prompt: string }) => ({
prompt: input.prompt,
options: {},
}),
} as unknown as AgentExecutionRuntime,
policy: {
begin: async () => run,
observeDelegation: () => undefined,
inspect: async () => {
inspections += 1;
return inspections === 1
? {
changedPaths: [],
diffCheck: "clean",
diffEvidence: "",
diffDigest: DIFF_DIGEST,
clean: true,
}
: {
changedPaths: ["src/change.ts"],
diffCheck: "checked",
diffEvidence: "diff --git a/src/change.ts b/src/change.ts",
diffDigest: DIFF_DIGEST,
clean: false,
};
},
verify: async () => {
calls.push("verify");
return "verified";
},
commit: async () => {
calls.push("commit");
return "commit-id";
},
accept: async () => {
calls.push("accept");
},
reject: async () => {
calls.push("reject");
},
},
agents: workflowAgents,
report: async (detail) => {
reports.push({
state: detail.state,
phases: detail.phases.map((phase) => ({ id: phase.id, state: phase.state })),
});
},
signal,
};
}
describe("self-update workflow contracts", () => {
test("recognizes varied containment-denial evidence", () => {
expect(isContainmentDeniedEvidence("Self-update containment denied a protected path")).toBe(true);
expect(isContainmentDeniedEvidence("The operation was denied at the containment level")).toBe(true);
expect(isContainmentDeniedEvidence("Repository inspection completed inside containment")).toBe(false);
});
test("no-op completes without implementation, review, or commit", async () => {
const calls: string[] = [];
const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
const result = await runSelfUpdateWorkflow(
task(),
"workflow-turn",
"workflow-trace",
dependencies(
new AbortController().signal,
agents((phase) => {
if (phase === "research") {
calls.push("research");
return { summary: "Nothing requires a source change", evidence: [], relevantPaths: [], noOp: true };
}
calls.push("decide");
return { rationale: "The requested state is already satisfied", noOp: true, verificationCommand: null };
}),
calls,
reports,
),
);
expect(result).toBe("Self-update verified no-op with no source changes");
expect(calls).toEqual(["research", "decide", "accept"]);
expect(reports.at(-1)?.state).toBe("completed");
expect(reports.at(-1)?.phases.filter((phase) => phase.state === "skipped").map((phase) => phase.id)).toEqual([
"implement",
"inspect-change",
"verify",
"review",
"commit",
]);
});
test("uses the orchestrator model to structure local specialist output", async () => {
const calls: string[] = [];
const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
const modes: unknown[] = [];
const structurer = { provider: "test", modelId: "structurer" };
const agent = {
__fork: () => agent,
setBrowser: () => undefined,
getModel: () => structurer,
stream: async (_prompt: string, options: {
runId?: string;
structuredOutput?: { jsonPromptInjection?: unknown; model?: unknown };
}) => {
modes.push([options.structuredOutput?.jsonPromptInjection, options.structuredOutput?.model]);
const phase = options.runId?.split("-").at(-1);
return {
object: Promise.resolve(phase === "research"
? { summary: "No change", evidence: [], relevantPaths: [], noOp: true }
: { rationale: "Already satisfied", noOp: true, verificationCommand: null }),
};
},
} as unknown as Agent;
await runSelfUpdateWorkflow(
task(),
"workflow-turn",
"workflow-trace",
dependencies(new AbortController().signal, {
researcher: agent,
orchistrator: agent,
implementer: agent,
reviewer: agent,
}, calls, reports),
);
expect(modes).toEqual([["auto", structurer], ["auto", structurer]]);
});
test("recovers structured handoffs from the streamed JSON text", async () => {
const calls: string[] = [];
const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
const agent = {
__fork: () => agent,
setBrowser: () => undefined,
getModel: () => ({ provider: "test", modelId: "structurer" }),
stream: async (_prompt: string, options: { runId?: string }) => {
const phase = options.runId?.split("-").at(-1);
return {
object: Promise.resolve(phase === "research"
? { result: { summary: "No change", evidence: { path: "src/agent.ts", finding: "No implementation needed" }, relevantPaths: [], noOp: true } }
: undefined),
text: Promise.resolve(`handoff follows\n${JSON.stringify({ rationale: "Already satisfied", noOp: true, verificationCommand: null })}\nfinished`),
};
},
} as unknown as Agent;
const result = await runSelfUpdateWorkflow(
task(),
"workflow-turn",
"workflow-trace",
dependencies(new AbortController().signal, {
researcher: agent,
orchistrator: agent,
implementer: agent,
reviewer: agent,
}, calls, reports),
);
expect(result).toBe("Self-update verified no-op with no source changes");
expect(reports.at(-1)?.state).toBe("completed");
});
test("retries one transient phase failure", async () => {
const calls: string[] = [];
const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
let attempts = 0;
const agent = fakeAgent((phase) => {
if (phase === "research" && attempts++ === 0) throw new Error("socket connection was closed unexpectedly");
return phase === "research"
? { summary: "No change", evidence: [], relevantPaths: [], noOp: true }
: { rationale: "Already satisfied", noOp: true, verificationCommand: null };
});
await expect(runSelfUpdateWorkflow(
task(),
"workflow-turn",
"workflow-trace",
dependencies(new AbortController().signal, {
researcher: agent,
orchistrator: agent,
implementer: agent,
reviewer: agent,
}, calls, reports),
)).resolves.toBe("Self-update verified no-op with no source changes");
expect(attempts).toBe(2);
});
test("research failure prevents commit", async () => {
const calls: string[] = [];
const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
await expect(runSelfUpdateWorkflow(
task(),
"workflow-turn",
"workflow-trace",
dependencies(
new AbortController().signal,
agents((phase) => {
calls.push(phase);
if (phase === "research") throw new Error("research failed");
throw new Error(`unexpected phase ${phase}`);
}),
calls,
reports,
),
)).rejects.toThrow("Research phase failed: research failed");
expect(calls).toEqual(["research", "reject"]);
expect(calls).not.toContain("commit");
expect(reports.at(-1)?.state).toBe("failed");
expect(reports.at(-1)?.phases.find((phase) => phase.id === "research")?.state).toBe("failed");
});
test("cancellation prevents later phases", async () => {
const calls: string[] = [];
const reports: Array<{ state: string; phases: Array<{ id: string; state: string }> }> = [];
const controller = new AbortController();
await expect(runSelfUpdateWorkflow(
task(),
"workflow-turn",
"workflow-trace",
dependencies(
controller.signal,
agents((phase) => {
calls.push(phase);
if (phase === "research") {
controller.abort();
return { summary: "Research complete", evidence: [], relevantPaths: [], noOp: false };
}
throw new Error(`unexpected phase ${phase}`);
}),
calls,
reports,
),
)).rejects.toThrow();
expect(calls).toEqual(["research", "reject"]);
expect(calls).not.toContain("commit");
expect(reports.at(-1)?.state).toBe("cancelled");
expect(reports.at(-1)?.phases.filter((phase) => phase.state === "skipped").map((phase) => phase.id)).toEqual([
"implement",
"inspect-change",
"verify",
"review",
"commit",
]);
});
});