@sema-agent/core
Version:
Stateless, task-oriented AI agent core
288 lines (287 loc) • 15.9 kB
JavaScript
import { escapeForDisclosure, parseRuleText, programRunReachOf, ruleReachesProgramRun } from "./permission-rule-model.js";
import { writerOf } from "./permission-rule-store.js";
export const ORG_UNAVAILABLE_DECISION_REASON = "org_unavailable";
export const ORG_ADJUDICATION_TIMEOUT_MS = 15_000;
export function settleOrgVerdictWithin(p, fallback, opts) {
return new Promise((resolve) => {
let settled = false;
const finish = (v, cause) => {
if (settled)
return;
settled = true;
clearTimeout(timer);
opts.signal?.removeEventListener("abort", onAbort);
if (cause !== undefined) {
try {
opts.onFallback?.(cause);
}
catch {
}
}
resolve(v);
};
const onAbort = () => finish(fallback, "aborted");
const timer = setTimeout(() => finish(fallback, "timeout"), opts.timeoutMs);
if (opts.signal?.aborted === true) {
finish(fallback, "aborted");
return;
}
opts.signal?.addEventListener("abort", onAbort);
p.then((v) => finish(v), () => finish(fallback));
});
}
export const ORG_RULE_DECISION_REASON = "org_rule";
export const ORG_FETCHED_AT_SKEW_ALLOWANCE_MS = 5 * 60_000;
export const UNGOVERNED_ORG_RESOLUTION = Object.freeze({ source: "org", status: "ungoverned", rules: [], disclosures: [] });
export function createOrgRulePartition(cfg) {
if (cfg === null || typeof cfg !== "object") {
throw new Error("the org partition config must be an object naming a snapshot source and a staleness bound");
}
if (cfg.source === undefined || typeof cfg.source.current !== "function") {
throw new Error("org-governed is declared but no OrgRuleSnapshotProvider is wired as `org.source` — a governed deployment without an org source is a configuration contradiction; refusing to boot rather than running silently ungoverned");
}
if (typeof cfg.stalenessBoundMs !== "number" || !Number.isFinite(cfg.stalenessBoundMs) || cfg.stalenessBoundMs <= 0) {
throw new Error(`org stalenessBoundMs must be a finite positive number of milliseconds (got ${escapeForDisclosure(cfg.stalenessBoundMs)})`);
}
const provider = cfg.source;
const now = cfg.now ?? Date.now;
let memoryState;
const readState = async () => {
const state = cfg.persistence !== undefined ? await cfg.persistence.readOrgState() : memoryState;
return state === undefined ? undefined : structuredClone(state);
};
const writeState = async (state) => {
if (cfg.persistence !== undefined)
await cfg.persistence.installOrgState(structuredClone(state));
else
memoryState = structuredClone(state);
};
let chain = Promise.resolve();
const serialize = (fn) => {
const run = chain.then(fn, fn);
chain = run.then(() => undefined, () => undefined);
return run;
};
const resolve = async () => {
const disclosures = [];
if (cfg.persistence === undefined) {
disclosures.push("org rule state has no durable persistence wired — the last-known-good snapshot and the anti-rollback mark do not survive a restart");
}
let fetched;
let fetchFailed = false;
const settled = await settleOrgVerdictWithin(provider.current().then((v) => ({ v }), (e) => ({ e })), { timedOut: true }, { timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
try {
if ("timedOut" in settled)
throw new Error(`the org snapshot source did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms`);
if ("e" in settled)
throw settled.e;
fetched = structuredClone(settled.v);
}
catch (err) {
fetchFailed = true;
disclosures.push(`org snapshot source failed: ${escapeForDisclosure(err instanceof Error ? err.message : err)}`);
}
return await settleOrgVerdictWithin(serialize(async () => {
const nowMs = now();
const guardedRead = async (context) => {
try {
return await readState();
}
catch (err) {
disclosures.push(`org state ${context} read failed: ${escapeForDisclosure(err instanceof Error ? err.message : err)}`);
return undefined;
}
};
const installed = await (async () => {
const persisted = await guardedRead("persistence");
if (fetched === undefined)
return persisted;
const invalid = validateOrgSnapshot(fetched, nowMs);
if (invalid !== undefined) {
disclosures.push(`org snapshot refused: ${invalid}; the previously installed snapshot (if any) remains in force`);
return persisted;
}
const persistedFloor = persisted === undefined ? undefined : antiRollbackFloor(persisted);
if (persisted !== undefined && !persistedMarkIsCoherent(persisted)) {
disclosures.push(`the durable org state carries a high-water mark (${escapeForDisclosure(persisted.revisionHighWater)}) that is not a finite number at or above its own snapshot revision (${escapeForDisclosure(persisted.snapshot?.revision)}) — the anti-rollback fence is being held at ${escapeForDisclosure(persistedFloor)} instead, and the durable state should be repaired`);
}
if (persistedFloor !== undefined && fetched.revision < persistedFloor) {
disclosures.push(`org snapshot revision ${fetched.revision} is below the installed high-water mark ${escapeForDisclosure(persistedFloor)} — refusing the rollback; the current deny set is unchanged`);
return persisted;
}
if (persisted !== undefined && fetched.revision === persisted.snapshot.revision && sameOrgPolicyContent(fetched, persisted.snapshot) && fetched.fetchedAtMs <= persisted.snapshot.fetchedAtMs) {
return persisted;
}
if (persisted !== undefined && fetched.revision === persisted.snapshot.revision && !sameOrgPolicyContent(fetched, persisted.snapshot)) {
disclosures.push(`org snapshot revision ${fetched.revision} equals the installed revision but carries DIFFERENT policy content — refusing the swap; the current deny set is unchanged`);
return persisted;
}
const state = {
revisionHighWater: Math.max(persistedFloor !== undefined && Number.isFinite(persistedFloor) ? persistedFloor : 0, fetched.revision),
snapshot: fetched,
installedAtMs: nowMs,
};
try {
await writeState(state);
}
catch (err) {
const reread = await guardedRead("reconciliation");
if (reread !== undefined && antiRollbackFloor(reread) >= state.revisionHighWater) {
disclosures.push(`org snapshot install was superseded by a concurrent install at revision ${escapeForDisclosure(reread.snapshot.revision)} — continuing on the newer installed state`);
return reread;
}
disclosures.push(`org snapshot install failed (${escapeForDisclosure(err instanceof Error ? err.message : err)}) and no superseding installed state exists — org adjudication is UNAVAILABLE`);
return undefined;
}
return state;
})();
const decisionMs = now();
const withinBound = (snap) => decisionMs - snap.fetchedAtMs <= cfg.stalenessBoundMs && snap.fetchedAtMs <= decisionMs + ORG_FETCHED_AT_SKEW_ALLOWANCE_MS;
if (installed !== undefined) {
if (installed.snapshot.fetchedAtMs > decisionMs + ORG_FETCHED_AT_SKEW_ALLOWANCE_MS) {
disclosures.push(`the installed org snapshot claims a FUTURE observation time (${escapeForDisclosure(installed.snapshot.fetchedAtMs)} vs now ${decisionMs}) — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask`);
return { source: "org", status: "unavailable", rules: [], disclosures };
}
const servingRefusal = validateOrgSnapshot(installed.snapshot, decisionMs);
if (servingRefusal !== undefined) {
disclosures.push(`the installed org snapshot (revision ${escapeForDisclosure(installed.snapshot.revision)}) does not meet this version's validator: ${servingRefusal} — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask`);
return { source: "org", status: "unavailable", rules: [], disclosures };
}
if (withinBound(installed.snapshot)) {
const freshlyInstalled = fetched !== undefined && installed.snapshot === fetched;
if (!freshlyInstalled && !fetchFailed && fetched === undefined) {
disclosures.push("org snapshot source returned no snapshot — continuing on the last-known-good");
}
return {
source: "org",
status: freshlyInstalled ? "fresh" : "last-known-good",
rules: structuredClone(installed.snapshot.rules),
revision: installed.snapshot.revision,
disclosures,
};
}
disclosures.push(`the newest org snapshot was observed ${decisionMs - installed.snapshot.fetchedAtMs}ms ago (bound ${cfg.stalenessBoundMs}ms) — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask`);
return { source: "org", status: "unavailable", rules: [], disclosures };
}
disclosures.push("no org snapshot has ever been installed — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask");
return { source: "org", status: "unavailable", rules: [], disclosures };
}), {
source: "org",
status: "unavailable",
rules: [],
disclosures: [...disclosures, `org state persistence did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms — org adjudication is UNAVAILABLE; the consuming gate must tighten every terminal allow to a real-approval ask`],
}, { timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
};
return { resolve };
}
function antiRollbackFloor(state) {
const mark = typeof state.revisionHighWater === "number" && Number.isFinite(state.revisionHighWater) ? state.revisionHighWater : undefined;
const own = typeof state.snapshot?.revision === "number" && Number.isFinite(state.snapshot.revision) ? state.snapshot.revision : undefined;
if (mark === undefined)
return own ?? Number.NEGATIVE_INFINITY;
return own === undefined ? mark : Math.max(mark, own);
}
function persistedMarkIsCoherent(state) {
return (typeof state.revisionHighWater === "number" &&
Number.isFinite(state.revisionHighWater) &&
typeof state.snapshot?.revision === "number" &&
Number.isFinite(state.snapshot.revision) &&
state.revisionHighWater >= state.snapshot.revision);
}
function sameOrgPolicyContent(a, b) {
if (a.rules.length !== b.rules.length)
return false;
return a.rules.every((r, i) => b.rules[i]?.rule === r.rule && b.rules[i]?.behavior === r.behavior);
}
const DISCLOSED_UNENFORCEABLE_RULES = 20;
export function unenforceableOrgRules(rules) {
const out = [];
for (const r of rules) {
if (typeof r?.rule !== "string" || r.rule === "")
continue;
const parsed = parseRuleText(r.rule, r.behavior);
if ("reject" in parsed) {
out.push({ rule: r.rule, code: parsed.reject.code });
continue;
}
if (parsed.rule.tool !== "Bash")
out.push({ rule: r.rule, code: "unsupported.tool" });
}
return out;
}
function describeUnenforceable(unenforceable) {
const named = unenforceable
.slice(0, DISCLOSED_UNENFORCEABLE_RULES)
.map((u) => `"${escapeForDisclosure(u.rule)}" (${u.code})`)
.join(", ");
const more = unenforceable.length - DISCLOSED_UNENFORCEABLE_RULES;
return more > 0 ? `${named} and ${more} more` : named;
}
function validateOrgSnapshot(s, nowMs) {
if (typeof s.revision !== "number" || !Number.isFinite(s.revision))
return `revision is not a finite number (${escapeForDisclosure(s.revision)})`;
if (typeof s.fetchedAtMs !== "number" || !Number.isFinite(s.fetchedAtMs))
return `fetchedAtMs is not a finite number (${escapeForDisclosure(s.fetchedAtMs)})`;
if (s.fetchedAtMs > nowMs + ORG_FETCHED_AT_SKEW_ALLOWANCE_MS) {
return `fetchedAtMs is ${s.fetchedAtMs - nowMs}ms in the future (allowance ${ORG_FETCHED_AT_SKEW_ALLOWANCE_MS}ms) — a future-dated observation would satisfy the staleness bound indefinitely`;
}
if (!Array.isArray(s.rules))
return "rules is not an array";
for (const r of s.rules) {
if (typeof r?.rule !== "string" || r.rule === "")
return "a rule entry carries no rule text";
if (r.behavior !== "deny" && r.behavior !== "ask") {
return `rule "${escapeForDisclosure(r.rule)}" carries behavior "${escapeForDisclosure(r.behavior)}" — the org layer has no allow bucket`;
}
}
const unenforceable = unenforceableOrgRules(s.rules);
const first = unenforceable[0];
if (first !== undefined) {
const rest = unenforceable.length > 1 ? ` (${unenforceable.length} in all — ${describeUnenforceable(unenforceable)})` : "";
return `rule "${escapeForDisclosure(first.rule)}" does not parse (${first.code}) — an unenforceable deny/ask must not stand as policy${rest}`;
}
return undefined;
}
export function orgRuleVerdictFor(rules, call) {
let ask;
let unreadable;
for (const r of rules) {
const parsed = parseRuleText(r.rule, r.behavior);
if ("reject" in parsed)
continue;
if (parsed.rule.tool !== call.tool)
continue;
const outcome = programRunReachOf(parsed.rule, call.command);
if (outcome.reach === "not_reached")
continue;
if (outcome.reach === "unreadable") {
unreadable ??= { behavior: "ask", rule: r.rule, unreadable: outcome.reason };
continue;
}
if (r.behavior === "deny")
return { behavior: "deny", rule: r.rule };
ask ??= { behavior: "ask", rule: r.rule };
}
return unreadable ?? ask;
}
export function orgRuleShadows(orgRules, rule) {
return compileOrgShadowPredicate(orgRules)(rule);
}
export function compileOrgShadowPredicate(orgRules) {
const denies = [];
for (const d of orgRules) {
if (d.behavior !== "deny")
continue;
const parsed = parseRuleText(d.rule, "deny");
if (!("reject" in parsed))
denies.push(parsed.rule);
}
return (rule) => {
if (rule.tool !== "Bash" || denies.length === 0)
return false;
return denies.some((d) => d.tool === rule.tool && ruleReachesProgramRun(d, rule.command));
};
}
export function isWritablePermissionRuleStore(store) {
return writerOf(store) !== undefined;
}