@sema-agent/core
Version:
Stateless, task-oriented AI agent core
136 lines (135 loc) • 7.9 kB
JavaScript
import { canonicalizeTarget, lexicalViewOf, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
import { isWithin } from "./runner/session-rule-policy.js";
import { swallowedFenceRoot } from "./runner/advertised-writable-dirs.js";
import { declaredPathTargetOf, isProtectedWrite } from "./tool-registry.js";
import { pathTargetBaseOf } from "./effective-path-target.js";
import { judgePathRows } from "./path-guard-rows.js";
export function domainRow(source, dirs) {
return {
name: `write domain (${source})`,
grammar: { kind: "domain", dirs, source },
polarity: "allow",
faces: ["write"],
placement: "anywhere",
fold: "exact",
exemptible: false,
origin: source === "advertised" ? "builtin" : "addition",
reach: (view) => (dirs.some((dir) => isWithin(dir, view.path)) ? { reach: "reached" } : { reach: "not_reached" }),
};
}
const ask = (message) => ({ action: "ask", message, decisionReason: "rule" });
function absoluteRootOf(rootPath, envCwd) {
return rootPath === undefined ? undefined : lexicalViewOf(rootPath, envCwd);
}
export const WRITE_EXEMPT_DIR_COVERS_ROOT_CODE = "config.write_exempt_dir_covers_root";
function assertExemptDirsDoNotCoverRoot(exemptDirs, rootLexical) {
if (exemptDirs === undefined || rootLexical === undefined)
return;
for (const raw of exemptDirs) {
if (!raw)
continue;
const dirLexical = lexicalViewOf(raw, rootLexical);
const swallowed = swallowedFenceRoot(dirLexical, [rootLexical]);
if (swallowed === undefined)
continue;
const e = new Error(`createFsWriteGatePolicy: exemptDirs entry "${raw}" resolves to ${dirLexical}, which covers this gate's root ${swallowed} — ` +
`an exemption that holds the root auto-approves every write under it, which is the whole workspace, so the gate refuses to be built rather than run with write governance silently off. ` +
`If that IS what this deployment wants, say it with the knob that means it: defaultWrite: "allow", an open write face, or a permission rule.`);
e.code = WRITE_EXEMPT_DIR_COVERS_ROOT_CODE;
e.detail = { exemptDir: raw, exemptDirResolved: dirLexical, root: swallowed };
throw e;
}
}
export function createFsWriteGatePolicy(opts) {
const { env, rootPath, defaultWrite } = opts;
const acceptDirs = opts.acceptDirs && opts.acceptDirs.length > 0 ? opts.acceptDirs : undefined;
const exemptDirs = opts.exemptDirs && opts.exemptDirs.length > 0 ? opts.exemptDirs : undefined;
const rootLexical = absoluteRootOf(rootPath, env.cwd);
assertExemptDirsDoNotCoverRoot(exemptDirs, rootLexical);
const guardedCapable = env.writeFileGuarded !== undefined;
const advisory = guardedCapable
? ""
: " (advisory adjudication: this environment provides no guarded write, so approval is judged before a separate, non-atomic write step)";
return {
async check(req, signal) {
if (!isProtectedWrite(req))
return { action: "allow" };
if (opts.requireGuardedWrite === true && !guardedCapable) {
return {
action: "deny",
message: `write tool "${req.toolName}" refused: this deployment requires guarded (atomic precondition-verified) writes (requireGuardedWrite), but the execution environment provides no writeFileGuarded capability — refusing rather than falling back to a non-atomic write`,
decisionReason: "rule",
};
}
const declared = declaredPathTargetOf(req);
const path = declared === undefined ? undefined : writeTargetPath(req, declared);
if (path === undefined) {
return ask(`write tool "${req.toolName}" requires approval: the call has no resolvable path target to confine${declared === undefined ? " (the tool declares no path target)" : ""}${advisory}`);
}
const canon = await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootPath, cwd: req.cwd ?? env.cwd }), pathTargetBaseOf(declared, { root: rootPath, cwd: req.cwd }));
if (!canon.ok) {
return ask(`write to "${path}" requires approval: its real target could not be resolved (${canon.message})${advisory}`);
}
const advertised = req.advertisedWritableRoots ?? [];
const targetViews = { views: [{ path: canon.key, kind: "canonical" }] };
const admitted = { configured: [], accepted: [] };
const admits = () => judgePathRows([domainRow("advertised", advertised), domainRow("configured", admitted.configured), domainRow("accepted", admitted.accepted)], targetViews, "allow") !== undefined;
if (admits())
return { action: "allow" };
let fenceRootsCanonical;
let refusedDomain;
for (const [dirs, structural] of [
[exemptDirs, true],
[acceptDirs, false],
]) {
if (!dirs)
continue;
for (const raw of dirs) {
const dir = await canonicalizeTarget(env, raw, signal, pathFamilyOf({ root: rootPath ?? env.cwd }), rootPath);
if (!dir.ok)
continue;
if (structural) {
if (fenceRootsCanonical === undefined) {
const r = rootPath === undefined ? undefined : await canonicalizeTarget(env, rootPath, signal, pathFamilyOf({ root: rootPath }), undefined);
fenceRootsCanonical = r === undefined ? [] : r.ok ? [r.key] : "unresolved";
}
if (fenceRootsCanonical === "unresolved") {
refusedDomain ??= `"${raw}" (this task's root ${rootPath} could not be resolved, so a structural domain that may cover it grants nothing)`;
continue;
}
const swallowed = swallowedFenceRoot(dir.key, fenceRootsCanonical);
if (swallowed !== undefined) {
refusedDomain ??= `"${raw}" (resolves to ${dir.key}, which contains this task's root ${swallowed})`;
continue;
}
}
(structural ? admitted.configured : admitted.accepted).push(dir.key);
if (admits())
return { action: "allow" };
}
}
if (opts.isExempt) {
let exempt = false;
try {
exempt = (await opts.isExempt(req.toolName, canon.key, req.face !== undefined ? { capabilityId: req.face.capabilityId, contractId: req.face.contractId, shapeDigest: req.face.shapeDigest } : undefined)) === true;
}
catch {
exempt = false;
}
if (exempt) {
return {
action: "allow",
message: `write to "${path}" allowed by session exemption (previously approved for this session)`,
decisionReason: "rule",
};
}
}
if (defaultWrite === "allow")
return { action: "allow" };
const note = refusedDomain === undefined ? undefined : ` — note: the configured write domain ${refusedDomain} was not honored; a domain that holds the root would exempt every write under it`;
if (opts.defaultAsk !== undefined)
return opts.defaultAsk(path, note);
return ask(`approve write to "${path}"?${advisory}${note ?? ""}`);
},
};
}