@sema-agent/core
Version:
Stateless, task-oriented AI agent core
77 lines (76 loc) • 4.55 kB
JavaScript
import { strict as assert } from "node:assert";
import { beginContract } from "./contract-harness.js";
const SESSION_ENTRY = (id, parentId) => ({
id,
parentId,
type: "message",
timestamp: "2026-07-30T00:00:00.000Z",
message: { role: "user", content: [{ type: "text", text: `t-${id}` }], timestamp: 1785340800000 },
});
export async function sessionRepoContract(make, runAssertion) {
const { run, settle } = beginContract(runAssertion);
const A = "0198fa00-0000-7000-8000-00000000000a";
run("RB-360 create({id}) on an id that ALREADY has history is an idempotent REOPEN — never a wipe", async () => {
const repo = make();
const sess = await repo.create({ id: A });
await sess.getStorage().appendEntry(SESSION_ENTRY("e1", null));
await sess.getStorage().appendEntry(SESSION_ENTRY("e2", "e1"));
const again = await repo.create({ id: A });
const entries = await again.getStorage().getEntries();
assert.deepEqual(entries.map((e) => e.id), ["e1", "e2"]);
const exported = (await repo.exportEntries?.(A)) ?? [];
assert.deepEqual(exported.map((e) => e.id), ["e1", "e2"]);
});
run("create({id}) fresh → open() sees appended entries (round-trip floor)", async () => {
const repo = make();
const sess = await repo.create({ id: A });
await sess.getStorage().appendEntry(SESSION_ENTRY("e1", null));
const opened = await repo.open({ id: A, createdAt: "" });
assert.deepEqual((await opened.getStorage().getEntries()).map((e) => e.id), ["e1"]);
});
run("open(missing) throws not_found — never a silent create", async () => {
const repo = make();
await assert.rejects(repo.open({ id: "0198fa00-0000-7000-8000-0000000000ff", createdAt: "" }), /not.?found/i);
});
run("delete() makes the id not_found — the history is gone, not merely hidden from open()", async () => {
const repo = make();
const sess = await repo.create({ id: A });
await sess.getStorage().appendEntry(SESSION_ENTRY("e1", null));
await repo.delete({ id: A, createdAt: "" });
await assert.rejects(repo.open({ id: A, createdAt: "" }), /not.?found/i);
const fresh = await repo.create({ id: A });
assert.deepEqual((await fresh.getStorage().getEntries()).map((e) => e.id), []);
});
run("fork() copies the log, stamps forkedFrom, and leaves the SOURCE untouched", async () => {
const repo = make();
const src = await repo.create({ id: A });
await src.getStorage().appendEntry(SESSION_ENTRY("e1", null));
await src.getStorage().appendEntry(SESSION_ENTRY("e2", "e1"));
const B = "0198fa00-0000-7000-8000-00000000000b";
const forked = await repo.fork({ id: A, createdAt: "" }, { id: B });
assert.deepEqual((await forked.getStorage().getEntries()).map((e) => e.id), ["e1", "e2"]);
assert.equal((await forked.getMetadata()).forkedFrom, A, "fork lineage must be stamped (a reap walks it)");
await src.getStorage().appendEntry(SESSION_ENTRY("e3", "e2"));
assert.deepEqual((await (await repo.open({ id: A, createdAt: "" })).getStorage().getEntries()).map((e) => e.id), ["e1", "e2", "e3"]);
assert.deepEqual((await (await repo.open({ id: B, createdAt: "" })).getStorage().getEntries()).map((e) => e.id), ["e1", "e2"]);
});
run("exportEntries → importEntries round-trips the log verbatim, and the imported session REPLACES what was there", async () => {
const repo = make();
if (repo.exportEntries === undefined || repo.importEntries === undefined)
return;
const src = await repo.create({ id: A });
await src.getStorage().appendEntry(SESSION_ENTRY("e1", null));
await src.getStorage().appendEntry(SESSION_ENTRY("e2", "e1"));
const donor = await repo.exportEntries(A);
assert.deepEqual(donor.map((e) => e.id), ["e1", "e2"]);
const C = "0198fa00-0000-7000-8000-00000000000c";
const target = await repo.create({ id: C });
await target.getStorage().appendEntry(SESSION_ENTRY("old", null));
await repo.importEntries(C, undefined, donor);
const after = await repo.exportEntries(C);
assert.deepEqual(after.map((e) => e.id), ["e1", "e2"]);
assert.deepEqual(after.map((e) => e.parentId), [null, "e1"]);
assert.equal((await repo.exportEntries(A)).length, 2, "importing into C must not disturb the donor session");
});
await settle();
}