@sema-agent/core
Version:
Stateless, task-oriented AI agent core
901 lines (900 loc) • 47.3 kB
JavaScript
import { createHash } from "node:crypto";
import { thrownText } from "./thrown-value.js";
import { isAbsoluteForFamily, isWinFormPath, pathFamilyOf, relativeInEveryFamily } from "../tools/fs/safety.js";
import { fileHistoryBoundariesToKeep, resolveFileHistoryRetention } from "./file-history-retention.js";
const normSlash = (p) => p.replace(/\\/g, "/");
const stripTrailSep = (p) => (isWinFormPath(p) ? p.replace(/[\\/]+$/, "") : p.replace(/\/+$/, ""));
const foldDrive = (p) => (/^[A-Za-z]:(\/|$)/.test(p) ? p[0].toLowerCase() + p.slice(1) : p);
function relUnder(root, child) {
const fold = isWinFormPath(root) ? normSlash : (p) => p;
const r = foldDrive(fold(stripTrailSep(root)));
const c = foldDrive(fold(child));
if (c === r)
return "";
if (!c.startsWith(r + "/"))
return null;
return fold(child).slice(fold(stripTrailSep(root)).length + 1);
}
function joinUnder(root, rel) {
const win = isWinFormPath(root);
return stripTrailSep(root) + (win ? "\\" : "/") + (win ? rel.replace(/\//g, "\\") : rel);
}
function isUnsafeRelPathLike(relN) {
return !relativeInEveryFamily(relN) || relN.split("/").includes("..");
}
function parentDirOf(absPath) {
const win = isWinFormPath(absPath);
const norm = win ? normSlash(absPath) : absPath;
const cut = norm.replace(/\/+$/, "");
const i = cut.lastIndexOf("/");
if (i <= 0)
return undefined;
const parent = cut.slice(0, i);
return win ? parent.replace(/\//g, "\\") : parent;
}
export function trackKeyFamilyOf(root) {
return isWinFormPath(root) ? "win" : "posix";
}
export function describeTrackKey(root, absPath) {
const key = trackKeyOf(root, absPath);
return { key, kind: key === absPath ? "absolute" : "root-relative", family: trackKeyFamilyOf(root) };
}
export function trackKeyOf(root, absPath) {
const rel = relUnder(root, absPath);
if (rel === null || rel === "")
return absPath;
const win = isWinFormPath(root);
const relN = win ? normSlash(rel) : rel;
if (win ? isUnsafeRelPathLike(relN) : isAbsoluteForFamily("posix", relN) || relN.split("/").includes(".."))
return absPath;
return rel;
}
export function resolveTrackKey(root, key, minted) {
const win = isWinFormPath(root);
if (minted !== undefined && minted.family !== trackKeyFamilyOf(root)) {
if (minted.kind === "absolute") {
return { ok: false, reason: `foreign-family absolute key cannot be restored here (recorded under a ${minted.family} root, this root is ${trackKeyFamilyOf(root)}): ${key}` };
}
if (isUnsafeRelPathLike(normSlash(key)) || key.includes("\\")) {
return { ok: false, reason: `foreign-family key is ambiguous across path families (recorded under a ${minted.family} root, this root is ${trackKeyFamilyOf(root)}): ${key}` };
}
}
const keyN = win ? normSlash(key) : key;
if (isAbsoluteForFamily(pathFamilyOf({ root }), keyN) || (win && /^[A-Za-z]:/.test(keyN)))
return { ok: true, abs: key };
if (win ? isUnsafeRelPathLike(keyN) : keyN.split("/").includes(".."))
return { ok: false, reason: `unsafe recorded key escapes root: ${key}` };
return { ok: true, abs: joinUnder(root, keyN) };
}
export const FILE_HISTORY_DIFF_LINE_BUDGET = 2000;
export function countRestoreLineDiff(current, target) {
const isBinary = (b) => b !== undefined && b.includes(0);
if (isBinary(current) || isBinary(target))
return { insertions: 0, deletions: 0 };
const linesOf = (b) => {
if (b === undefined)
return [];
const text = new TextDecoder().decode(b);
if (text === "")
return [];
return text.endsWith("\n") ? text.slice(0, -1).split("\n") : text.split("\n");
};
const a = linesOf(current);
const bLines = linesOf(target);
const common = longestCommonSubsequenceLength(a, bLines);
return { deletions: a.length - common, insertions: bLines.length - common };
}
function longestCommonSubsequenceLength(a, b) {
const n = a.length;
const m = b.length;
if (n === 0 || m === 0)
return 0;
const max = Math.min(FILE_HISTORY_DIFF_LINE_BUDGET, n + m);
const offset = max;
const v = new Int32Array(2 * max + 1);
for (let d = 0; d <= max; d++) {
for (let k = -d; k <= d; k += 2) {
let x;
if (k === -d || (k !== d && v[offset + k - 1] < v[offset + k + 1]))
x = v[offset + k + 1];
else
x = v[offset + k - 1] + 1;
let y = x - k;
while (x < n && y < m && a[x] === b[y]) {
x++;
y++;
}
v[offset + k] = x;
if (x >= n && y >= m) {
return (n + m - d) / 2;
}
}
}
return 0;
}
export function fileHistoryExportDigest(data) {
const canonical = {
formatVersion: data.formatVersion,
tracked: [...data.tracked]
.map((t) => ({
relOrAbsPath: t.relOrAbsPath,
keyKind: t.keyKind,
keyFamily: t.keyFamily,
baseAvailable: t.baseAvailable,
versions: [...t.versions]
.map((v) => ({ version: v.version, blobHash: v.blobHash, mode: v.mode ?? null, parentIdent: v.parentIdent ?? null, backupTime: v.backupTime }))
.sort((a, b) => a.version - b.version),
}))
.sort((a, b) => (a.relOrAbsPath < b.relOrAbsPath ? -1 : a.relOrAbsPath > b.relOrAbsPath ? 1 : 0)),
boundaries: [...data.boundaries]
.map((b) => ({
entryId: b.entryId,
files: [...b.files].map((f) => ({ relOrAbsPath: f.relOrAbsPath, version: f.version })).sort((x, y) => (x.relOrAbsPath < y.relOrAbsPath ? -1 : x.relOrAbsPath > y.relOrAbsPath ? 1 : 0)),
}))
.sort((a, b) => (a.entryId < b.entryId ? -1 : a.entryId > b.entryId ? 1 : 0)),
};
return createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
}
export function validateFileHistoryExport(data) {
const bad = (message) => ({ ok: false, error: { code: "unsupported_format", message } });
if (data === null || typeof data !== "object")
return bad("file-history import payload is not an envelope");
if (data.formatVersion !== "file-history/1")
return bad(`unsupported file-history envelope format ${JSON.stringify(String(data.formatVersion))} — this engine reads "file-history/1" and refuses rather than reinterpreting a foreign epoch`);
if (!Array.isArray(data.tracked) || !Array.isArray(data.boundaries))
return bad("file-history envelope is missing its tracked/boundaries arrays");
const known = new Set();
for (const t of data.tracked) {
if (typeof t?.relOrAbsPath !== "string" || t.relOrAbsPath === "")
return bad("file-history envelope carries a tracked row with no path");
if (t.keyKind !== "root-relative" && t.keyKind !== "absolute")
return bad(`tracked row ${t.relOrAbsPath} declares an unknown key kind ${JSON.stringify(String(t.keyKind))} — a key whose shape cannot be judged is refused, never guessed`);
if (t.keyKind === "absolute") {
return bad(`tracked row ${t.relOrAbsPath} is an ABSOLUTE key — an imported graph may not carry paths outside the importing root (it would grant this envelope restore authority over an arbitrary path); same-store carry across sessions is adoptScope's seat`);
}
if (isUnsafeRelPathLike(normSlash(t.relOrAbsPath)) || t.relOrAbsPath.includes("\\")) {
return bad(`tracked row ${t.relOrAbsPath} is not a safe root-relative key (absolute form, drive spelling, '..' segment, or a separator that means different things across path families) — refused, never coerced`);
}
if (t.keyFamily !== "posix" && t.keyFamily !== "win")
return bad(`tracked row ${t.relOrAbsPath} declares an unknown key family ${JSON.stringify(String(t.keyFamily))} — a key whose provenance cannot be judged is refused, never guessed`);
if (typeof t.baseAvailable !== "boolean" || !Array.isArray(t.versions))
return bad(`tracked row ${t.relOrAbsPath} is missing its base-availability state or version chain`);
for (const v of t.versions) {
if (typeof v?.version !== "number" || !Number.isInteger(v.version) || v.version < 1)
return bad(`tracked row ${t.relOrAbsPath} carries a non-integer version number`);
if (v.blobHash !== null && (typeof v.blobHash !== "string" || !/^[0-9a-f]{64}$/.test(v.blobHash)))
return bad(`tracked row ${t.relOrAbsPath} carries a blob reference that is not a sha256 digest`);
if (v.mode !== undefined) {
if (typeof v.mode !== "number" || !Number.isInteger(v.mode) || v.mode < 0 || v.mode > 0o7777) {
return bad(`tracked row ${t.relOrAbsPath} carries a mode outside the permission-bit domain [0, 0o7777] — refused, never coerced`);
}
if ((v.mode & 0o6000) !== 0) {
return bad(`tracked row ${t.relOrAbsPath} carries a set-ID mode (0o${v.mode.toString(8)}) — a restore replaying setuid/setgid bits would mint a privilege-escalation primitive; refused`);
}
}
}
if (new Set(t.versions.map((v) => v.version)).size !== t.versions.length)
return bad(`tracked row ${t.relOrAbsPath} carries the same version number twice — a chain whose versions are ambiguous cannot be published`);
for (let i = 1; i < t.versions.length; i++) {
if (t.versions[i].version <= t.versions[i - 1].version) {
return bad(`tracked row ${t.relOrAbsPath} carries a chain whose versions are not strictly increasing in order (…${t.versions[i - 1].version}, ${t.versions[i].version}…) — a later local boundary would then reference an ambiguous version`);
}
}
if (known.has(t.relOrAbsPath)) {
return bad(`file-history envelope carries the tracked row ${t.relOrAbsPath} twice — duplicate keys make the published graph differ from the validated one`);
}
if (t.baseAvailable && t.versions.length > 0 && t.versions[0].version !== 1)
return bad(`tracked row ${t.relOrAbsPath} claims an available base but its chain does not start at v1`);
if (!t.baseAvailable && t.versions.some((v) => v.version === 1))
return bad(`tracked row ${t.relOrAbsPath} carries a v1 record while declaring its base UNAVAILABLE — the DV-14 refusal state and a pristine baseline are mutually exclusive`);
known.add(t.relOrAbsPath);
}
const seen = new Set();
for (const b of data.boundaries) {
if (typeof b?.entryId !== "string" || b.entryId === "")
return bad("file-history envelope carries a boundary with no entry id");
if (seen.has(b.entryId))
return bad(`file-history envelope carries the boundary ${b.entryId} twice`);
seen.add(b.entryId);
if (!Array.isArray(b.files))
return bad(`boundary ${b.entryId} carries no file mapping`);
if (new Set(b.files.map((f) => f?.relOrAbsPath)).size !== b.files.length)
return bad(`boundary ${b.entryId} maps the same path twice — the published mapping would differ from the validated one`);
for (const f of b.files) {
if (typeof f?.relOrAbsPath !== "string" || typeof f.version !== "number")
return bad(`boundary ${b.entryId} carries a malformed file mapping`);
if (!known.has(f.relOrAbsPath))
return bad(`boundary ${b.entryId} references ${f.relOrAbsPath}, which the envelope's tracked set does not carry`);
const chain = data.tracked.find((t) => t.relOrAbsPath === f.relOrAbsPath);
if (!chain.versions.some((v) => v.version === f.version))
return bad(`boundary ${b.entryId} references ${f.relOrAbsPath}@v${f.version}, which the envelope's version chain does not carry`);
}
}
return { ok: true };
}
export function boundaryPublishVerdict(attempt) {
return attempt.aborted ? { publish: false, reason: "aborted" } : { publish: true };
}
async function parentIdentityViaEnv(env, absPath, root, signal) {
const parent = parentDirOf(absPath);
if (parent === undefined)
return undefined;
const canon = await env.canonicalPath(parent, signal);
if (!canon.ok)
return undefined;
let rel = relUnder(root, canon.value);
if (rel === null) {
const canonRoot = await env.canonicalPath(root, signal);
if (canonRoot.ok)
rel = relUnder(canonRoot.value, canon.value);
}
return rel === null ? canon.value : rel === "" ? "." : rel;
}
export async function captureFileBackupViaEnv(env, absPath, root, signal) {
if (signal?.aborted)
return { state: "aborted" };
const parentIdent = await parentIdentityViaEnv(env, absPath, root, signal);
const rb = await env.readBinaryFile(absPath, signal);
if (!rb.ok) {
if (signal?.aborted)
return { state: "aborted" };
if (rb.error.code === "not_found")
return { state: "absent", ...(parentIdent !== undefined ? { parentIdent } : {}) };
return { state: "unreadable", cause: `read ${absPath}: ${rb.error.code}` };
}
const hash = createHash("sha256").update(rb.value).digest("hex");
const mode = await (async () => {
const info = await env.fileInfo(absPath, signal);
return info.ok && typeof info.value.mode === "number" ? info.value.mode & 0o7777 : undefined;
})();
return { state: "file", bytes: rb.value, hash, ...(mode !== undefined ? { mode } : {}), ...(parentIdent !== undefined ? { parentIdent } : {}) };
}
export async function worldStillMatchesMint(env, absPath, root, baseAvailable, mintedBlobHash, signal) {
if (!baseAvailable) {
return { unchanged: false, reason: "the first touch recorded a base-UNAVAILABLE marker, whose whole purpose is to refuse a fake pristine later — an ambiguous failure may not erase it" };
}
const cap = await captureFileBackupViaEnv(env, absPath, root, signal);
if (cap.state === "absent") {
return mintedBlobHash === null ? { unchanged: true } : { unchanged: false, reason: "the file recorded at the first touch is gone now" };
}
if (cap.state === "file") {
if (mintedBlobHash === null)
return { unchanged: false, reason: "the path was recorded as NOT EXISTING at the first touch but holds bytes now" };
return cap.hash === mintedBlobHash ? { unchanged: true } : { unchanged: false, reason: "the file's bytes differ from the ones recorded at the first touch" };
}
return { unchanged: false, reason: `the path could not be re-read to prove it is unchanged (${cap.state === "aborted" ? "aborted" : cap.cause})` };
}
export function modeProvenDifferent(recorded, current) {
return recorded !== undefined && current !== undefined && recorded !== current;
}
export async function applyFileRestoreViaEnv(env, absPath, target, opts) {
const signal = opts.signal;
if (signal?.aborted)
return { outcome: "failed", reason: "aborted" };
const guard = await restorePreflight(env, absPath, opts);
if ("refused" in guard)
return guard.refused;
const cur = await env.readBinaryFile(absPath, signal);
const curAbsent = !cur.ok && cur.error.code === "not_found";
if ("absent" in target) {
if (curAbsent)
return { outcome: "identical" };
const rm = await env.remove(absPath, { force: true, ...(signal !== undefined ? { abortSignal: signal } : {}) });
if (!rm.ok)
return { outcome: "failed", reason: `remove: ${rm.error.code}` };
return { outcome: "applied" };
}
if (cur.ok && createHash("sha256").update(cur.value).digest("hex") === target.hash) {
const modeOnly = await applyRecordedMode(env, absPath, opts, guard.currentMode);
if (modeOnly === "unchanged")
return { outcome: "identical" };
if (modeOnly === "applied")
return { outcome: "applied" };
if (modeOnly === "applied_unpreserved")
return { outcome: "applied", modeNotPreserved: true };
return { outcome: "identical", modeNotPreserved: true };
}
const w = await writeRestoredBytes(env, absPath, target.bytes, signal);
if (!w.ok)
return { outcome: w.refused ? "refused" : "failed", reason: `write: ${w.code}` };
const modeAfter = await applyRecordedMode(env, absPath, opts, undefined);
return { outcome: "applied", ...(modeAfter === "unpreserved" || modeAfter === "applied_unpreserved" ? { modeNotPreserved: true } : {}) };
}
async function restorePreflight(env, absPath, opts) {
const info = await env.fileInfo(absPath, opts.signal);
if (info.ok && info.value.kind !== "file") {
return {
refused: {
outcome: "refused",
reason: info.value.kind === "symlink"
? "restore target is a SYMLINK — the recorded bytes belong to the file that was edited, not to whatever the link now points at"
: `restore target is a ${info.value.kind}, not a regular file — the recorded bytes are not this object's`,
},
};
}
const currentMode = info.ok ? info.value.mode : undefined;
if (opts.parentIdent === undefined)
return { currentMode };
const now = await parentIdentityViaEnv(env, absPath, opts.root, opts.signal);
if (now === undefined)
return { currentMode };
if (now === opts.parentIdent)
return { currentMode };
return {
refused: {
outcome: "refused",
reason: `restore target's parent chain changed identity since it was recorded (recorded ${JSON.stringify(opts.parentIdent)}, now ${JSON.stringify(now)}) — writing here could land outside the task root`,
},
};
}
async function writeRestoredBytes(env, absPath, bytes, signal) {
if (env.writeFileGuarded !== undefined) {
const r = await env.writeFileGuarded(absPath, bytes, { canonicalPath: absPath, noFollow: true }, signal);
return r.ok ? { ok: true } : { ok: false, code: r.error.code, refused: r.error.code === "precondition_failed" };
}
const w = await env.writeFile(absPath, bytes, signal);
return w.ok ? { ok: true } : { ok: false, code: w.error.code, refused: false };
}
export async function previewFileDelta(env, absPath, targetHash, targetBytes, targetMode) {
const cur = await env.readBinaryFile(absPath);
const curBytes = cur.ok ? cur.value : undefined;
if (curBytes === undefined && targetBytes === undefined)
return undefined;
if (curBytes !== undefined && targetHash !== null && createHash("sha256").update(curBytes).digest("hex") === targetHash) {
if (targetMode === undefined)
return undefined;
const info = await env.fileInfo(absPath);
const currentMode = info.ok ? info.value.mode : undefined;
const replayMode = (targetMode & 0o6000) !== 0 ? targetMode & 0o1777 : targetMode;
return modeProvenDifferent(targetMode, currentMode) && modeProvenDifferent(replayMode, currentMode)
? { insertions: 0, deletions: 0 }
: undefined;
}
return countRestoreLineDiff(curBytes, targetBytes);
}
async function applyRecordedMode(env, absPath, opts, currentMode) {
const want = opts.mode;
if (want === undefined)
return "unchanged";
if (currentMode === (want & 0o7777))
return "unchanged";
if ((want & 0o6000) !== 0) {
const stripped = want & 0o1777;
if (currentMode === stripped)
return "unpreserved";
if (env.setFileMode === undefined)
return "unpreserved";
const r = await env.setFileMode(absPath, stripped, opts.signal);
return r.ok ? "applied_unpreserved" : "unpreserved";
}
if (env.setFileMode === undefined)
return "unpreserved";
const r = await env.setFileMode(absPath, want & 0o7777, opts.signal);
return r.ok ? "applied" : "unpreserved";
}
export class InMemoryFileHistoryStore {
scopes = new Map();
boundaryKeep;
blobs = new Map();
inFlightBlobs = new Map();
constructor(opts) {
this.boundaryKeep = resolveFileHistoryRetention("InMemoryFileHistoryStore", opts?.retention);
}
applyBoundaryRetention(scope, justCommitted) {
if (this.boundaryKeep === null)
return;
try {
const sc = this.scopes.get(scope);
if (sc === undefined)
return;
const keepIds = fileHistoryBoundariesToKeep([...sc.boundaries.keys()], justCommitted, this.boundaryKeep);
if (keepIds === null)
return;
this.reap(scope, keepIds).catch(() => { });
}
catch {
}
}
leaseBlob(hash) {
this.inFlightBlobs.set(hash, (this.inFlightBlobs.get(hash) ?? 0) + 1);
}
releaseBlob(hash) {
const n = this.inFlightBlobs.get(hash);
if (n === undefined)
return;
if (n <= 1)
this.inFlightBlobs.delete(hash);
else
this.inFlightBlobs.set(hash, n - 1);
}
scope(scope) {
let sc = this.scopes.get(scope);
if (sc === undefined) {
sc = { tracked: new Map(), boundaries: new Map(), spentAttempts: new Set(), activeAttempts: new Set(), reapedEntries: new Set() };
this.scopes.set(scope, sc);
}
return sc;
}
persistFirstTouch(scope, key, state) {
this.scope(scope).tracked.set(key, state);
}
publishBoundary(scope, entryId, staged) {
const sc = this.scope(scope);
for (const [key, cap] of staged) {
if (cap.kind !== "unchanged")
continue;
const still = sc.tracked.get(key)?.versions.some((v) => v.version === cap.version) ?? false;
if (!still)
throw new Error(`staged reference ${key}@v${cap.version} no longer exists (reaped mid-capture)`);
}
const mapping = new Map();
const now = new Date().toISOString();
for (const [key, cap] of staged) {
if (cap.kind === "unchanged") {
mapping.set(key, cap.version);
continue;
}
const t = sc.tracked.get(key);
if (t === undefined)
continue;
const last = t.versions[t.versions.length - 1];
const version = last !== undefined ? last.version + 1 : t.baseAvailable ? 1 : 2;
t.versions.push({ version, blobHash: cap.blobHash, backupTime: now, ...(cap.mode !== undefined ? { mode: cap.mode } : {}), ...(cap.parentIdent !== undefined ? { parentIdent: cap.parentIdent } : {}) });
mapping.set(key, version);
}
sc.boundaries.set(entryId, mapping);
}
async trackEdit(scope, absPath, env, root, signal) {
try {
const { key, ...keyDesc } = describeTrackKey(root, absPath);
const sc = this.scope(scope);
const already = sc.tracked.get(key);
if (already !== undefined) {
already.observedByLaterTouch = true;
return { ok: true, minted: false };
}
const cap = await captureFileBackupViaEnv(env, absPath, root, signal);
const raced = sc.tracked.get(key);
if (raced !== undefined) {
raced.observedByLaterTouch = true;
return { ok: true, minted: false };
}
if (cap.state === "aborted") {
return { ok: false, error: { code: "aborted", message: `first-touch backup of ${absPath} aborted` } };
}
if (cap.state === "unreadable") {
this.persistFirstTouch(scope, key, { baseAvailable: false, keyDesc, versions: [] });
return { ok: true, minted: true };
}
if (cap.state === "file") {
this.leaseBlob(cap.hash);
try {
if (!this.blobs.has(cap.hash))
this.blobs.set(cap.hash, cap.bytes);
this.persistFirstTouch(scope, key, {
baseAvailable: true,
keyDesc,
versions: [{ version: 1, blobHash: cap.hash, backupTime: new Date().toISOString(), ...(cap.mode !== undefined ? { mode: cap.mode } : {}), ...(cap.parentIdent !== undefined ? { parentIdent: cap.parentIdent } : {}) }],
});
}
finally {
this.releaseBlob(cap.hash);
}
return { ok: true, minted: true };
}
this.persistFirstTouch(scope, key, {
baseAvailable: true,
keyDesc,
versions: [{ version: 1, blobHash: null, backupTime: new Date().toISOString(), ...(cap.parentIdent !== undefined ? { parentIdent: cap.parentIdent } : {}) }],
});
return { ok: true, minted: true };
}
catch (err) {
return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} could not be persisted: ${thrownText(err)}` } };
}
}
async annulTrack(scope, absPath, root, verify) {
try {
const key = trackKeyOf(root, absPath);
const sc = this.scopes.get(scope);
const t = sc?.tracked.get(key);
if (sc === undefined || t === undefined)
return { ok: true };
const ineligible = () => {
const live = sc.tracked.get(key);
if (live === undefined)
return undefined;
if (live !== t)
return `first-touch record for ${absPath} was replaced while this retraction was being proven`;
for (const mapping of sc.boundaries.values()) {
if (mapping.has(key))
return `first-touch record for ${absPath} is referenced by a committed boundary — it is history now, not a retractable mint`;
}
if (live.versions.length > 1)
return `first-touch record for ${absPath} has grown past its first version — only the mint itself is retractable`;
if (live.observedByLaterTouch === true) {
return `first-touch record for ${absPath} has since been served to another edit of the same path — it is that edit's baseline now, not a retractable mint`;
}
return undefined;
};
const before = ineligible();
if (before !== undefined)
return { ok: false, error: { code: "conflict", message: before } };
const proof = verify === undefined ? undefined : await worldStillMatchesMint(verify.env, absPath, root, t.baseAvailable, t.versions[0]?.blobHash ?? null, verify.signal);
if (proof !== undefined && !proof.unchanged) {
return { ok: false, error: { code: "conflict", message: `first-touch record for ${absPath} is NOT retractable: ${proof.reason} — the write this retraction compensates for may have changed the file, and deleting the only baseline of a modified file is worse than keeping a stale record` } };
}
const after = ineligible();
if (after !== undefined)
return { ok: false, error: { code: "conflict", message: after } };
sc.tracked.delete(key);
return { ok: true };
}
catch (err) {
return { ok: false, error: { code: "track_failed", message: `annulling the first-touch record for ${absPath} failed: ${thrownText(err)}` } };
}
}
async snapshot(scope, entryId, env, root, signal) {
const sc = this.scope(scope);
if (sc.boundaries.has(entryId))
return { ok: true };
if (sc.reapedEntries.has(entryId)) {
return { ok: false, error: { code: "snapshot_failed", message: `entry "${entryId}" already had a boundary and it was reaped by retention — a fresh capture now would record post-turn bytes under a pre-turn key, so this entry has no boundary and cannot get one back` } };
}
if (sc.spentAttempts.has(entryId)) {
return { ok: false, error: { code: "snapshot_failed", message: `capture lease for entry "${entryId}" is closed (a prior attempt failed); no complete pre-close staging exists, so this turn has no boundary` } };
}
if (sc.activeAttempts.has(entryId)) {
return { ok: false, error: { code: "snapshot_failed", message: `a capture attempt for entry "${entryId}" is already in flight — concurrent same-key capture is refused (the in-flight attempt owns the lease)` } };
}
const stagedBlobHashes = new Set();
sc.activeAttempts.add(entryId);
try {
const staged = new Map();
for (const [key, t] of [...sc.tracked]) {
if (signal?.aborted) {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "aborted", message: `boundary capture for entry "${entryId}" aborted` } };
}
const resolved = resolveTrackKey(root, key, t.keyDesc);
if (!resolved.ok) {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "snapshot_failed", message: resolved.reason } };
}
const cap = await captureFileBackupViaEnv(env, resolved.abs, root, signal);
if (cap.state === "aborted") {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "aborted", message: `boundary capture for entry "${entryId}" aborted` } };
}
if (cap.state === "unreadable") {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "snapshot_failed", message: cap.cause } };
}
const last = t.versions[t.versions.length - 1];
if (cap.state === "absent") {
if (last !== undefined && last.blobHash === null)
staged.set(key, { kind: "unchanged", version: last.version });
else
staged.set(key, { kind: "changed", blobHash: null, ...(cap.parentIdent !== undefined ? { parentIdent: cap.parentIdent } : {}) });
continue;
}
if (last !== undefined && last.blobHash === cap.hash && !modeProvenDifferent(last.mode, cap.mode)) {
staged.set(key, { kind: "unchanged", version: last.version });
continue;
}
if (!stagedBlobHashes.has(cap.hash)) {
stagedBlobHashes.add(cap.hash);
this.leaseBlob(cap.hash);
}
if (!this.blobs.has(cap.hash))
this.blobs.set(cap.hash, cap.bytes);
staged.set(key, { kind: "changed", blobHash: cap.hash, ...(cap.mode !== undefined ? { mode: cap.mode } : {}), ...(cap.parentIdent !== undefined ? { parentIdent: cap.parentIdent } : {}) });
}
const verdict = boundaryPublishVerdict({ aborted: signal?.aborted === true });
if (!verdict.publish) {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "aborted", message: `boundary capture for entry "${entryId}" aborted (fenced — never committed)` } };
}
try {
this.publishBoundary(scope, entryId, staged);
}
catch (err) {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "snapshot_failed", message: `boundary for entry "${entryId}" could not be committed: ${thrownText(err)}` } };
}
this.applyBoundaryRetention(scope, entryId);
return { ok: true };
}
catch (err) {
sc.spentAttempts.add(entryId);
return { ok: false, error: { code: "snapshot_failed", message: thrownText(err) } };
}
finally {
sc.activeAttempts.delete(entryId);
for (const h of stagedBlobHashes)
this.releaseBlob(h);
}
}
async restore(scope, entryId, env, root, signal) {
const empty = { filesChanged: [], identicalSkipped: [], refused: [], failed: [] };
try {
const sc = this.scopes.get(scope);
const boundary = sc?.boundaries.get(entryId);
if (sc === undefined || boundary === undefined) {
return { outcome: "failed", ...empty, error: { code: "not_found", message: `no history boundary for entry "${entryId}"` } };
}
const ledger = empty;
let modeNotPreserved = 0;
for (const key of [...sc.tracked.keys()].sort()) {
const t = sc.tracked.get(key);
if (signal?.aborted) {
ledger.failed.push({ path: key, reason: "aborted before attempt" });
continue;
}
let targetVersion = boundary.get(key);
if (targetVersion === undefined) {
if (!t.baseAvailable) {
ledger.refused.push({ path: key, reason: "base_unavailable" });
continue;
}
targetVersion = 1;
}
const rec = t.versions.find((v) => v.version === targetVersion);
if (rec === undefined) {
ledger.failed.push({ path: key, reason: `version ${targetVersion} record missing (store integrity)` });
continue;
}
const resolved = resolveTrackKey(root, key, t.keyDesc);
if (!resolved.ok) {
ledger.refused.push({ path: key, reason: resolved.reason });
continue;
}
let target;
if (rec.blobHash === null)
target = { absent: true };
else {
const bytes = this.blobs.get(rec.blobHash);
if (bytes === undefined) {
ledger.failed.push({ path: key, reason: `blob ${rec.blobHash} missing (store integrity)` });
continue;
}
target = { bytes, hash: rec.blobHash };
}
let applied;
try {
applied = await applyFileRestoreViaEnv(env, resolved.abs, target, {
root,
...(rec.mode !== undefined ? { mode: rec.mode } : {}),
...(rec.parentIdent !== undefined ? { parentIdent: rec.parentIdent } : {}),
...(signal !== undefined ? { signal } : {}),
});
}
catch (err) {
applied = { outcome: "failed", reason: thrownText(err) };
}
if (applied.outcome === "applied")
ledger.filesChanged.push(key);
else if (applied.outcome === "identical")
ledger.identicalSkipped.push(key);
else if (applied.outcome === "refused")
ledger.refused.push({ path: key, reason: applied.reason });
else
ledger.failed.push({ path: key, reason: applied.reason });
if ((applied.outcome === "applied" || applied.outcome === "identical") && applied.modeNotPreserved === true)
modeNotPreserved++;
}
return { outcome: ledger.refused.length > 0 || ledger.failed.length > 0 ? "partial" : "complete", ...ledger, ...(modeNotPreserved > 0 ? { modeNotPreserved } : {}) };
}
catch (err) {
const touched = empty.filesChanged.length > 0 || empty.identicalSkipped.length > 0 || empty.refused.length > 0 || empty.failed.length > 0;
return { outcome: touched ? "partial" : "failed", ...empty, error: { code: "restore_failed", message: thrownText(err) } };
}
}
async canRestore(scope, entryId) {
return this.scopes.get(scope)?.boundaries.has(entryId) ?? false;
}
async diffStats(scope, entryId, env, root) {
try {
const sc = this.scopes.get(scope);
const boundary = sc?.boundaries.get(entryId);
if (sc === undefined || boundary === undefined)
return null;
const filesChanged = [];
let insertions = 0;
let deletions = 0;
for (const key of [...sc.tracked.keys()].sort()) {
const t = sc.tracked.get(key);
let targetVersion = boundary.get(key);
if (targetVersion === undefined) {
if (!t.baseAvailable)
continue;
targetVersion = 1;
}
const rec = t.versions.find((v) => v.version === targetVersion);
const resolved = resolveTrackKey(root, key, t.keyDesc);
if (rec === undefined || !resolved.ok)
continue;
const targetBytes = rec.blobHash === null ? undefined : this.blobs.get(rec.blobHash);
if (rec.blobHash !== null && targetBytes === undefined)
continue;
const delta = await previewFileDelta(env, resolved.abs, rec.blobHash, targetBytes, rec.mode);
if (delta === undefined)
continue;
filesChanged.push(key);
insertions += delta.insertions;
deletions += delta.deletions;
}
return { filesChanged, insertions, deletions };
}
catch {
return null;
}
}
async reap(scope, keepEntryIds) {
const sc = this.scopes.get(scope);
if (sc === undefined)
return 0;
const keep = new Set(keepEntryIds);
let removed = 0;
for (const id of [...sc.boundaries.keys()]) {
if (!keep.has(id)) {
sc.boundaries.delete(id);
sc.reapedEntries.add(id);
removed++;
}
}
const referenced = new Map();
for (const mapping of sc.boundaries.values()) {
for (const [key, v] of mapping) {
let s = referenced.get(key);
if (s === undefined)
referenced.set(key, (s = new Set()));
s.add(v);
}
}
for (const [key, t] of sc.tracked) {
const refs = referenced.get(key);
t.versions = t.versions.filter((v) => v.version === 1 || (refs?.has(v.version) ?? false));
}
const live = new Set(this.inFlightBlobs.keys());
for (const s of this.scopes.values()) {
for (const t of s.tracked.values())
for (const v of t.versions)
if (v.blobHash !== null)
live.add(v.blobHash);
}
for (const h of [...this.blobs.keys()])
if (!live.has(h))
this.blobs.delete(h);
return removed;
}
async adoptScope(fromScope, toScope) {
try {
if (fromScope === toScope)
return { ok: true };
const from = this.scopes.get(fromScope);
const to = this.scopes.get(toScope);
if (to !== undefined && (to.tracked.size > 0 || to.boundaries.size > 0)) {
return { ok: false, error: { code: "conflict", message: `adoptScope target "${toScope}" already has history of its own — adoption is legal only onto an EMPTY scope (the fork/resume boundary before the new session's first turn)` } };
}
if (from === undefined || (from.tracked.size === 0 && from.boundaries.size === 0)) {
if (from !== undefined && from.reapedEntries.size > 0) {
const target = this.scope(toScope);
for (const id of from.reapedEntries)
target.reapedEntries.add(id);
}
return { ok: true };
}
const copy = { tracked: new Map(), boundaries: new Map(), spentAttempts: new Set(), activeAttempts: new Set(), reapedEntries: new Set([...from.reapedEntries, ...(to?.reapedEntries ?? [])]) };
for (const [key, t] of from.tracked)
copy.tracked.set(key, { baseAvailable: t.baseAvailable, keyDesc: { ...t.keyDesc }, ...(t.observedByLaterTouch === true ? { observedByLaterTouch: true } : {}), versions: t.versions.map((v) => ({ ...v })) });
for (const [id, mapping] of from.boundaries)
copy.boundaries.set(id, new Map(mapping));
this.scopes.set(toScope, copy);
return { ok: true };
}
catch (err) {
return { ok: false, error: { code: "adopt_failed", message: `adoptScope failed: ${thrownText(err)}` } };
}
}
async exportHistory(scope) {
const sc = this.scopes.get(scope);
if (sc === undefined || (sc.tracked.size === 0 && sc.boundaries.size === 0))
return null;
return this.exportOf(sc);
}
exportOf(sc) {
return {
formatVersion: "file-history/1",
tracked: [...sc.tracked.entries()]
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
.map(([key, t]) => ({
relOrAbsPath: key,
keyKind: t.keyDesc.kind,
keyFamily: t.keyDesc.family,
baseAvailable: t.baseAvailable,
versions: t.versions.map((v) => ({ version: v.version, blobHash: v.blobHash, ...(v.mode !== undefined ? { mode: v.mode } : {}), ...(v.parentIdent !== undefined ? { parentIdent: v.parentIdent } : {}), backupTime: v.backupTime })),
})),
boundaries: [...sc.boundaries.entries()]
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
.map(([entryId, mapping]) => ({ entryId, files: [...mapping.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([relOrAbsPath, version]) => ({ relOrAbsPath, version })) })),
};
}
async importHistory(scope, data, srcGetBlob) {
try {
const shaped = validateFileHistoryExport(data);
if (!shaped.ok)
return shaped;
const existing = await this.exportHistory(scope);
if (existing !== null) {
if (fileHistoryExportDigest(existing) === fileHistoryExportDigest(data))
return { ok: true };
return { ok: false, error: { code: "conflict", message: `importHistory target "${scope}" already holds a DIFFERENT history — a first import requires an empty target and a retry is legal only for the identical envelope (atomic visibility prevents half-graphs; this law prevents publishing the wrong complete graph over locally-grown history)` } };
}
const needed = new Set();
for (const t of data.tracked)
for (const v of t.versions)
if (v.blobHash !== null)
needed.add(v.blobHash);
for (const hash of needed)
this.leaseBlob(hash);
try {
return await this.importStaged(scope, data, srcGetBlob, needed);
}
finally {
for (const hash of needed)
this.releaseBlob(hash);
}
}
catch (err) {
return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}` } };
}
}
async importStaged(scope, data, srcGetBlob, needed) {
try {
const staged = new Map();
for (const hash of needed) {
if (this.blobs.has(hash))
continue;
let bytes;
try {
bytes = await srcGetBlob(hash);
}
catch (err) {
return { ok: false, error: { code: "unsupported_format", message: `source blob ${hash} fetch failed: ${thrownText(err)}` } };
}
if (bytes === undefined)
return { ok: false, error: { code: "not_found", message: `source blob ${hash} is missing — the envelope's graph cannot be completed, so nothing is published` } };
if (createHash("sha256").update(bytes).digest("hex") !== hash) {
return { ok: false, error: { code: "unsupported_format", message: `source blob ${hash} fails content-address integrity — refused, nothing published` } };
}
staged.set(hash, bytes);
}
const live = this.scopes.get(scope);
if (live !== undefined && (live.tracked.size > 0 || live.boundaries.size > 0)) {
const now = this.exportOf(live);
if (fileHistoryExportDigest(now) === fileHistoryExportDigest(data))
return { ok: true };
return { ok: false, error: { code: "conflict", message: `importHistory target "${scope}" gained a DIFFERENT history while this import was fetching blobs — nothing was published` } };
}
for (const [hash, bytes] of staged)
this.blobs.set(hash, bytes);
const built = { tracked: new Map(), boundaries: new Map(), spentAttempts: new Set(), activeAttempts: new Set(), reapedEntries: new Set(live?.reapedEntries ?? []) };
for (const t of data.tracked) {
built.tracked.set(t.relOrAbsPath, {
baseAvailable: t.baseAvailable,
keyDesc: { kind: t.keyKind, family: t.keyFamily },
versions: t.versions.map((v) => ({ version: v.version, blobHash: v.blobHash, ...(v.mode !== undefined ? { mode: v.mode } : {}), ...(v.parentIdent !== undefined ? { parentIdent: v.parentIdent } : {}), backupTime: v.backupTime })),
});
}
for (const b of data.boundaries)
built.boundaries.set(b.entryId, new Map(b.files.map((f) => [f.relOrAbsPath, f.version])));
this.scopes.set(scope, built);
return { ok: true };
}
catch (err) {
return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}` } };
}
}
}
export const __testables = { joinUnder, relUnder, stripTrailSep, trackKeyOf, resolveTrackKey, parentDirOf };