@sema-agent/core
Version:
Stateless, task-oriented AI agent core
141 lines (140 loc) • 8.3 kB
JavaScript
import { readRowBehavior } from "./permission-rule-model.js";
import { isValidConsentScope, normalizePersistedRule } from "./permission-rule-store.js";
function isValidOverlayAdd(add) {
const a = add;
if (typeof a?.dot?.actor !== "string" || a.dot.actor === "" || typeof a.dot.counter !== "number" || !Number.isFinite(a.dot.counter))
return false;
if (a.origin !== "user" && a.origin !== "imported-cc" && a.origin !== "starter")
return false;
return typeof a.createdAt === "string";
}
export class InMemorySessionRulePartition {
rows = new Map();
sealed = new Set();
epochs = new Map();
bumpEpoch(sessionId) {
this.epochs.set(sessionId, (this.epochs.get(sessionId) ?? 0) + 1);
}
async read(sessionId) {
return structuredClone(this.rows.get(sessionId) ?? []);
}
async apply(sessionId, add) {
if (this.sealed.has(sessionId))
return { refused: "session_ended" };
add = structuredClone(add);
if (this.sealed.has(sessionId))
return { refused: "session_ended" };
if (!isValidOverlayAdd(add?.add)) {
throw new Error("SessionRulePartition.apply was handed a malformed add (dot/origin/createdAt) — evidence this shape cannot vouch for must not install");
}
const scope = { kind: "session", sessionId };
if (!isValidConsentScope(scope)) {
throw new Error("SessionRulePartition.apply was addressed with an unusable session id — a session nobody can name is not a scope a grant can live under");
}
const projected = { behavior: add.behavior, rule: add.rule, tool: add.tool, match: add.match, command: add.command, scope, adds: [add.add] };
if ("reject" in normalizePersistedRule(projected)) {
throw new Error("SessionRulePartition.apply was handed a row whose text/metadata do not survive the canonical re-projection — a row the engine cannot re-project can never adjudicate, and would refuse its own restore");
}
const rows = this.rows.get(sessionId) ?? [];
if (!this.rows.has(sessionId))
this.rows.set(sessionId, rows);
const sameDotAs = (a) => a.dot.actor === add.add.dot.actor && a.dot.counter === add.add.dot.counter;
for (const [heldSession, heldRows] of this.rows) {
const holder = heldRows.find((r) => r.adds.some(sameDotAs));
if (holder !== undefined && (heldSession !== sessionId || holder.rule !== add.rule || holder.behavior !== add.behavior)) {
throw new Error(`SessionRulePartition.apply: dot ${add.add.dot.actor}#${add.add.dot.counter} already vouches for another grant — a dot identifies one add of one (behavior, rule, session), ever`);
}
}
const existing = rows.find((r) => r.rule === add.rule && r.behavior === add.behavior);
if (existing === undefined) {
rows.push({ behavior: add.behavior, rule: add.rule, tool: add.tool, match: add.match, command: add.command, scope, adds: [structuredClone(add.add)] });
this.bumpEpoch(sessionId);
return { landed: true };
}
if (!existing.adds.some((a) => a.dot.actor === add.add.dot.actor && a.dot.counter === add.add.dot.counter)) {
existing.adds.push(structuredClone(add.add));
this.bumpEpoch(sessionId);
}
return { landed: true };
}
endSession(sessionId) {
this.sealed.add(sessionId);
this.rows.delete(sessionId);
this.bumpEpoch(sessionId);
}
async snapshotSession(sessionId) {
return this.read(sessionId);
}
async restoreSession(sessionId, rows) {
if (this.sealed.has(sessionId)) {
throw new Error(`session "${sessionId}" has ended — a sealed session's authorizations cannot be restored (termination is one-way)`);
}
const epochBeforeRead = this.epochs.get(sessionId) ?? 0;
rows = structuredClone(rows);
if (this.sealed.has(sessionId)) {
throw new Error(`session "${sessionId}" has ended — a sealed session's authorizations cannot be restored (termination is one-way)`);
}
if ((this.epochs.get(sessionId) ?? 0) !== epochBeforeRead) {
throw new Error(`restoreSession("${sessionId}") raced a landing on the same session — refusing the whole restore (this door replaces the session's rows, and a grant that already answered landed must not be erased by a resume)`);
}
for (const r of rows) {
const behavior = readRowBehavior(r?.behavior);
if (behavior !== undefined && r !== null && typeof r === "object")
r.behavior = behavior;
const scope = r?.scope;
if (scope?.kind !== "session" || scope.sessionId !== sessionId || !isValidConsentScope(scope)) {
throw new Error(`restoreSession("${sessionId}") was handed a row that is not a session row of that session — refusing the whole restore (a foreign-scope row through this door would reach adjudication)`);
}
if ("reject" in normalizePersistedRule(r)) {
throw new Error(`restoreSession("${sessionId}") was handed a row whose text/metadata do not survive the canonical re-projection — refusing the whole restore (a row the engine cannot re-project must not reach adjudication)`);
}
}
const identityOf = (r) => `${r.behavior}|${r.rule}`;
const ownDotRule = new Map();
for (const held of this.rows.get(sessionId) ?? []) {
for (const h of held.adds)
ownDotRule.set(`${h.dot.actor}#${h.dot.counter}`, identityOf(held));
}
const seenDots = new Map();
for (const r of rows) {
if (!Array.isArray(r.adds) || r.adds.length === 0 || !r.adds.every(isValidOverlayAdd)) {
throw new Error(`restoreSession("${sessionId}") was handed a row with malformed add evidence — refusing the whole restore (an add whose dot/origin cannot be read is not a consent anyone can audit)`);
}
for (const a of r.adds) {
const key = `${a.dot.actor}#${a.dot.counter}`;
const holder = seenDots.get(key);
if (holder !== undefined && holder !== identityOf(r)) {
throw new Error(`restoreSession("${sessionId}"): dot ${key} vouches for two different rules — refusing the whole restore`);
}
seenDots.set(key, identityOf(r));
const ownHolder = ownDotRule.get(key);
if (ownHolder !== undefined && ownHolder !== identityOf(r)) {
throw new Error(`restoreSession("${sessionId}"): dot ${key} is live in this session under a different rule — a restore may replay a grant, never reassign its dot; refusing the whole restore`);
}
for (const [heldSession, heldRows] of this.rows) {
if (heldSession === sessionId)
continue;
if (heldRows.some((held) => held.adds.some((h) => h.dot.actor === a.dot.actor && h.dot.counter === a.dot.counter))) {
throw new Error(`restoreSession("${sessionId}"): dot ${key} already vouches for another live session's grant — refusing the whole restore`);
}
}
}
}
const restoredDots = new Set(rows.flatMap((r) => r.adds.map((a) => `${a.dot.actor}#${a.dot.counter}`)));
const carried = [];
for (const held of this.rows.get(sessionId) ?? []) {
if (held.behavior === "allow")
continue;
const adds = held.adds.filter((a) => !restoredDots.has(`${a.dot.actor}#${a.dot.counter}`));
if (adds.length === 0)
continue;
const same = rows.find((r) => r.behavior === held.behavior && r.rule === held.rule);
if (same !== undefined)
same.adds.push(...structuredClone(adds));
else
carried.push({ ...structuredClone(held), adds: structuredClone(adds) });
}
this.rows.set(sessionId, [...rows, ...carried]);
this.bumpEpoch(sessionId);
}
}