@sema-agent/core
Version:
Stateless, task-oriented AI agent core
191 lines (190 loc) • 12.7 kB
JavaScript
import { strict as assert } from "node:assert";
import { summarizeWorkflowRun } from "./workflow-run-store.js";
export function createWorkflowRun(over = {}) {
const createdAt = over.createdAt ?? 1_000;
return {
id: over.id ?? "run-1",
scope: "tenant-a",
status: "completed",
phases: [{ title: "build", status: "completed", startedAt: createdAt, endedAt: createdAt + 5 }],
agents: [
{ label: "agent-1", callKey: "0:abc", phase: "build", status: "completed", taskStatus: "completed", queuedAt: createdAt, startedAt: createdAt, endedAt: createdAt + 4, stats: { tokens: 100, turns: 1, costMicroUsd: 10 } },
],
groups: [],
stats: { tokens: 100, turns: 1, costMicroUsd: 10, nested: { tokens: 50, turns: 1, tasks: 1, costMicroUsd: 5 } },
startedAt: createdAt,
endedAt: createdAt + 10,
createdAt,
...over,
};
}
export async function workflowRunStoreContract(make, runAssertion = defaultSequentialRunner) {
const pending = [];
const run = (name, fn) => {
const result = runAssertion(name, fn);
if (result instanceof Promise)
pending.push(result);
};
run("put create-once → already_exists", async () => {
const store = make();
const r = createWorkflowRun();
await store.put(r.id, r);
await assert.rejects(store.put(r.id, r), (e) => e.code === "workflow_run.already_exists");
});
run("get returns a deep copy (mutating the result must not corrupt the store)", async () => {
const store = make();
const r = createWorkflowRun();
await store.put(r.id, r);
const got = await store.get(r.id);
assert.ok(got);
got.agents.push({ label: "x", callKey: "0:x", status: "running", queuedAt: 0, startedAt: 0 });
const again = await store.get(r.id);
assert.equal(again.agents.length, 1);
assert.equal(await store.get("nope"), null);
});
run("update CAS: bumps rev, scope-mismatch loses, stale-rev loses, current-rev wins", async () => {
const store = make();
const r = createWorkflowRun({ status: "running" });
await store.put(r.id, r);
assert.equal(await store.update(r.id, r.scope, { ...r, status: "running", stats: { ...r.stats, tokens: 200 } }), true);
assert.equal((await store.get(r.id)).rev, 1);
assert.equal((await store.get(r.id)).stats.tokens, 200);
assert.equal(await store.update(r.id, "tenant-b", { ...r, status: "completed" }), false);
assert.equal(await store.update(r.id, r.scope, { ...r, status: "completed" }, { rev: 0 }), false);
assert.equal(await store.update(r.id, r.scope, { ...r, status: "completed" }, { rev: 1 }), true);
assert.equal((await store.get(r.id)).rev, 2);
assert.equal((await store.get(r.id)).status, "completed");
assert.equal(await store.update("ghost", r.scope, createWorkflowRun({ id: "ghost" })), false);
});
run("the key id is authoritative — a payload with a mismatched id never poisons the projection", async () => {
const store = make();
const r = createWorkflowRun({ id: "real", status: "running" });
await store.put("real", { ...r, id: "spoofed" });
assert.equal((await store.get("real")).id, "real");
assert.equal(await store.get("spoofed"), null);
await store.update("real", r.scope, { ...r, id: "spoofed-again", status: "completed" });
assert.equal((await store.get("real")).id, "real");
const listed = await store.listByScope(r.scope);
assert.ok(listed.every((s) => s.id === "real"));
});
run("listByScope: history included (completed + failed + running), createdAt DESC, status filter + limit", async () => {
const store = make();
assert.deepEqual(await store.listByScope("tenant-a"), []);
const running = createWorkflowRun({ id: "r-run", status: "running", createdAt: 3_000, endedAt: undefined });
const done = createWorkflowRun({ id: "r-done", status: "completed", createdAt: 2_000 });
const failed = createWorkflowRun({ id: "r-fail", status: "failed", createdAt: 1_000 });
const other = createWorkflowRun({ id: "r-other", scope: "tenant-b", createdAt: 9_000 });
for (const r of [running, done, failed, other])
await store.put(r.id, r);
const all = await store.listByScope("tenant-a");
assert.deepEqual(all.map((s) => s.id), ["r-run", "r-done", "r-fail"]);
assert.deepEqual(all.map((s) => s.status), ["running", "completed", "failed"]);
assert.deepEqual((await store.listByScope("tenant-a", { status: "completed" })).map((s) => s.id), ["r-done"]);
assert.deepEqual((await store.listByScope("tenant-a", { status: "failed" })).map((s) => s.id), ["r-fail"]);
assert.deepEqual((await store.listByScope("tenant-a", { limit: 2 })).map((s) => s.id), ["r-run", "r-done"]);
assert.deepEqual(all.find((s) => s.id === "r-done"), summarizeWorkflowRun(done));
assert.equal(all.find((s) => s.id === "r-done").tokens, done.stats.tokens + done.stats.nested.tokens);
const doneSummary = all.find((s) => s.id === "r-done");
assert.equal(doneSummary.currentPhase, "build");
assert.equal(doneSummary.name, undefined);
assert.equal(doneSummary.description, undefined);
const named = createWorkflowRun({
id: "r-named", name: "find-flaky", description: "find flaky tests", createdAt: 4_000,
phases: [
{ title: "scan", status: "completed", startedAt: 4_000, endedAt: 4_001 },
{ title: "fix", status: "running", startedAt: 4_001 },
],
});
await store.put(named.id, named);
const namedSummary = (await store.listByScope("tenant-a")).find((s) => s.id === "r-named");
assert.equal(namedSummary.name, "find-flaky");
assert.equal(namedSummary.description, "find flaky tests");
assert.equal(namedSummary.currentPhase, "fix");
});
run("the ADDITIVE terminal disclosures reach the LIST row: agentFailures + budgetOvershoot project, and a clean run keeps both keys ABSENT", async () => {
const store = make();
const over = createWorkflowRun({ id: "d-over", createdAt: 5_000, agentFailures: 2, budgetOvershoot: { budgetTokens: 150, spentTokens: 1_200 } });
const unsettled = createWorkflowRun({ id: "d-unsettled", createdAt: 6_000, budgetOvershoot: { budgetTokens: 100, spentTokens: 0, unsettledTokens: 500 } });
const clean = createWorkflowRun({ id: "d-clean", createdAt: 7_000 });
for (const r of [over, unsettled, clean])
await store.put(r.id, r);
const byId = new Map((await store.listByScope("tenant-a")).map((s) => [s.id, s]));
assert.equal(byId.get("d-over").agentFailures, 2);
assert.deepEqual(byId.get("d-over").budgetOvershoot, { budgetTokens: 150, spentTokens: 1_200 });
assert.deepEqual(byId.get("d-unsettled").budgetOvershoot, { budgetTokens: 100, spentTokens: 0, unsettledTokens: 500 });
assert.equal("agentFailures" in byId.get("d-clean"), false, "no failures ⇒ the key is OMITTED, not 0/null");
assert.equal("budgetOvershoot" in byId.get("d-clean"), false, "within (or without) a ceiling ⇒ the key is OMITTED, not null/zeroed");
assert.equal(byId.get("d-over").tokens, over.stats.tokens + over.stats.nested.tokens);
byId.get("d-over").budgetOvershoot.spentTokens = 7;
assert.equal((await store.get("d-over")).budgetOvershoot.spentTokens, 1_200);
});
run("the failure CODE reaches the LIST row verbatim, and a run without one keeps the key ABSENT", async () => {
const store = make();
const coded = createWorkflowRun({ id: "f-coded", status: "failed", createdAt: 5_000, error: "the body gave up", errorCode: "workflow.agent_blocked" });
const codeless = createWorkflowRun({ id: "f-codeless", status: "failed", createdAt: 6_000, error: "the body gave up" });
const ok = createWorkflowRun({ id: "f-ok", status: "completed", createdAt: 7_000 });
for (const r of [coded, codeless, ok])
await store.put(r.id, r);
const byId = new Map((await store.listByScope("tenant-a")).map((s) => [s.id, s]));
assert.equal(byId.get("f-coded").errorCode, "workflow.agent_blocked");
assert.equal("errorCode" in byId.get("f-codeless"), false, "a failed run with no code ⇒ the key is OMITTED, not an `unknown` stand-in");
assert.equal("errorCode" in byId.get("f-ok"), false, "a completed run never carries one ⇒ the key is OMITTED");
assert.deepEqual(byId.get("f-coded"), summarizeWorkflowRun(coded));
});
run("the TOTAL-TIMEOUT disclosure reaches the LIST row: timeoutInterruption projects (de-aliased), and a run without it keeps the key ABSENT", async () => {
const store = make();
const seat = { timeoutMs: 600_000, agentsCompleted: 3, agentsFailed: 1, agentsInFlight: 2, approvalWaitedMs: 540_000 };
const cut = createWorkflowRun({ id: "d-timeout", createdAt: 8_000, status: "failed", error: "workflow total timeout", timeoutInterruption: seat });
const noWait = { timeoutMs: 600_000, agentsCompleted: 1, agentsFailed: 0, agentsInFlight: 1 };
const slow = createWorkflowRun({ id: "d-slow", createdAt: 8_050, status: "failed", error: "workflow total timeout", timeoutInterruption: noWait });
const threw = createWorkflowRun({ id: "d-threw", createdAt: 8_100, status: "failed", error: "script blew up" });
for (const r of [cut, slow, threw])
await store.put(r.id, r);
const byId = new Map((await store.listByScope("tenant-a")).map((s) => [s.id, s]));
assert.deepEqual(byId.get("d-timeout").timeoutInterruption, seat);
assert.deepEqual(byId.get("d-slow").timeoutInterruption, noWait);
assert.equal("approvalWaitedMs" in byId.get("d-slow").timeoutInterruption, false, "a run nobody reported a wait for ⇒ the member is OMITTED, never stored as 0 (absence is 'not supplied', not 'nobody waited')");
assert.equal("timeoutInterruption" in byId.get("d-threw"), false, "a run its own error ended ⇒ the key is OMITTED, not null/zeroed");
byId.get("d-timeout").timeoutInterruption.agentsCompleted = 7;
assert.equal((await store.get("d-timeout")).timeoutInterruption.agentsCompleted, 3);
});
run("reap: only terminal runs, NEVER running; maxAgeMs + keep retention; no policy → no-op", async () => {
const store = make();
const running = createWorkflowRun({ id: "k-run", status: "running", createdAt: 100, endedAt: undefined });
const oldDone = createWorkflowRun({ id: "k-old", status: "completed", createdAt: 100, endedAt: 100 });
const newDone = createWorkflowRun({ id: "k-new", status: "completed", createdAt: 9_000, endedAt: 9_000 });
const oldFail = createWorkflowRun({ id: "k-fail", status: "failed", createdAt: 200, endedAt: 200 });
for (const r of [running, oldDone, newDone, oldFail])
await store.put(r.id, r);
assert.equal(await store.reap("tenant-a", 1_000_000), 0);
assert.equal(store2size(store) >= -1, true);
const reaped = await store.reap("tenant-a", 10_000, { maxAgeMs: 5_000 });
assert.equal(reaped, 2);
assert.equal(await store.get("k-old"), null);
assert.equal(await store.get("k-fail"), null);
assert.ok(await store.get("k-new"));
assert.ok(await store.get("k-run"));
});
run("reap keep-newest-N: deletes all but the newest N terminal runs; running excluded from the count", async () => {
const store = make();
const running = createWorkflowRun({ id: "p-run", status: "running", createdAt: 50, endedAt: undefined });
const t1 = createWorkflowRun({ id: "p-1", status: "completed", createdAt: 1_000 });
const t2 = createWorkflowRun({ id: "p-2", status: "completed", createdAt: 2_000 });
const t3 = createWorkflowRun({ id: "p-3", status: "failed", createdAt: 3_000 });
for (const r of [running, t1, t2, t3])
await store.put(r.id, r);
assert.equal(await store.reap("tenant-a", 99_999, { keep: 1 }), 2);
assert.equal(await store.get("p-1"), null);
assert.equal(await store.get("p-2"), null);
assert.ok(await store.get("p-3"));
assert.ok(await store.get("p-run"));
});
await Promise.all(pending);
}
function defaultSequentialRunner(_name, fn) {
return fn();
}
function store2size(store) {
const s = store.size;
return typeof s === "number" ? s : -1;
}