UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

120 lines (119 loc) • 11.4 kB
import { persistedReadDenyEntryProblem } from "../../tools/fs/read-deny.js"; import { isAskOrigin } from "../ask-origin.js"; import { CHECKPOINT_FEATURES, CheckpointError, checkpointVersionOf, MAX_SUPPORTED_CHECKPOINT_VERSION, realApprovalOrgFact } from "../checkpoint-store.js"; import { constraintChainDigest, constraintChainEntryOfSuppliedLayer } from "../tool-policy.js"; import { readFrozenSkillGrants } from "../skill-grant.js"; export function resumeCheckpointScreen(input) { const { cp, resumeSignal, internals, runner } = input; const { realApproval: realApprovalRow, face: faceRow, f012: f012Row } = CHECKPOINT_FEATURES; if (checkpointVersionOf(cp) > MAX_SUPPORTED_CHECKPOINT_VERSION) { throw new CheckpointError("checkpoint.unsupported_version", `checkpoint version ${checkpointVersionOf(cp)} is newer than this worker supports (max ${MAX_SUPPORTED_CHECKPOINT_VERSION})`, { reason: "version_newer" }); } const preCasGateBit = cp.gate.kind === "irreversible_ask" ? cp.gate.realApproval : undefined; const preCasBitWellFormed = preCasGateBit !== undefined && typeof preCasGateBit === "object" && isAskOrigin(preCasGateBit.origin); const faceSaysGoverned = (cp.state.readFace?.realApproval) === true; const preCasBitViolation = checkpointVersionOf(cp) === realApprovalRow.version ? !preCasBitWellFormed : checkpointVersionOf(cp) < realApprovalRow.version ? preCasGateBit !== undefined : faceSaysGoverned ? !preCasBitWellFormed : preCasGateBit !== undefined && !preCasBitWellFormed; if (preCasBitViolation) { throw new CheckpointError("checkpoint.invalid_outcome", checkpointVersionOf(cp) >= realApprovalRow.version ? `a v${checkpointVersionOf(cp)} checkpoint's realApproval gate bit must be well-formed (origin a member of the closed ask-origin set)${checkpointVersionOf(cp) === realApprovalRow.version ? " and present on an irreversible_ask gate" : ""} — this row's is not; refusing to resume a damaged real-approval row (corruption / downgrade guard), the checkpoint stays pending` : `a v${checkpointVersionOf(cp)} checkpoint carries a realApproval gate bit no release of that version ever minted — refusing to honor a fabricated origin (corruption / forgery guard), the checkpoint stays pending`, { reason: checkpointVersionOf(cp) >= realApprovalRow.version ? "real_approval_damaged" : "real_approval_forged" }); } { const faceSectionRaw = cp.state.readFace; const faceCandidate = faceSectionRaw !== undefined && typeof faceSectionRaw === "object" && faceSectionRaw !== null ? faceSectionRaw : undefined; const faceWellFormed = faceCandidate !== undefined && (faceCandidate.face === "open" || faceCandidate.face === "roots") && (faceCandidate.realApproval === undefined || faceCandidate.realApproval === true) && (faceCandidate.denyEntries === undefined || (Array.isArray(faceCandidate.denyEntries) && faceCandidate.denyEntries.every((e) => persistedReadDenyEntryProblem(e) === null))); if (checkpointVersionOf(cp) >= faceRow.version ? !faceWellFormed : faceSectionRaw !== undefined) { throw new CheckpointError("checkpoint.invalid_outcome", checkpointVersionOf(cp) >= faceRow.version ? `a v${checkpointVersionOf(cp)} checkpoint must carry a well-formed read-face section (state.readFace) — this row's is missing or malformed; refusing to resume a damaged read-face row (corruption / downgrade guard), the checkpoint stays pending` : `a v${checkpointVersionOf(cp)} checkpoint carries a read-face section no release of that version ever minted — refusing to honor a fabricated read-face state (corruption / forgery guard), the checkpoint stays pending`, { reason: checkpointVersionOf(cp) >= faceRow.version ? "read_face_damaged" : "read_face_forged" }); } const writeFaceSectionRaw = cp.state.writeFace; if (writeFaceSectionRaw !== undefined) { const c = typeof writeFaceSectionRaw === "object" && writeFaceSectionRaw !== null ? writeFaceSectionRaw : undefined; if (c === undefined || !(c.face === "open" || c.face === "roots")) { throw new CheckpointError("checkpoint.invalid_outcome", `this checkpoint carries a write-face section (state.writeFace) that is not well-formed — refusing to resume a damaged write-face row (corruption guard), the checkpoint stays pending`, { reason: "write_face_damaged" }); } } if (readFrozenSkillGrants(cp.state.skillGrants) === undefined) throw new CheckpointError("checkpoint.invalid_outcome", "this checkpoint carries a skill-grant section (state.skillGrants) that does not read as grant rows — refusing to resume without the grant rows and refusals it carries (corruption guard), the checkpoint stays pending", { reason: "skill_grants_damaged" }); } if (realApprovalOrgFact(preCasGateBit) !== undefined && runner.deps.permissionRuleStore?.partitions.org !== true) { throw new CheckpointError("checkpoint.unsupported_version", `this checkpoint's approval was minted under organization governance (org ${String(realApprovalOrgFact(preCasGateBit))}, routed as ${String(preCasGateBit?.origin)}) and this worker has no org adjudication wiring (its permission-rule store carries no org partition) — a governed approval may only be redeemed where governance can be enforced; the checkpoint stays pending, resume it on an org-wired worker`, { reason: "governed_unwired" }); } const retiredWalltimeTotal = cp.resourceLedger?.totalWalltimeSec; if (retiredWalltimeTotal !== undefined) { throw new CheckpointError("checkpoint.walltime_axis_retired", "this checkpoint's ledger carries a cross-slice WALL-CLOCK allocation (resourceLedger.totalWalltimeSec), an axis this engine version retired — " + "resuming here would run the leg with that ceiling unenforced. Rejected pre-CAS (the checkpoint stays pending): finish it on a worker of the " + "previous release, or start a fresh task bounded by resourceSuspend.totalTokens / totalBudgetUsd."); } if (resumeSignal?.aborted === true && cp.status === "pending") { throw new CheckpointError("checkpoint.resume_aborted", "the resume was handed an ALREADY-ABORTED signal — refusing to consume the approval on a leg that cannot run it (the checkpoint stays pending and is resumable with a live signal)"); } if (cp.state.workspaceHandle !== undefined && runner.deps.executionEnvFactory === undefined) { throw new CheckpointError("checkpoint.unsupported_version", "checkpoint has a remote workspaceHandle but no RunnerDeps.executionEnvFactory is wired to rebuild the env", { reason: "env_factory_missing" }); } if (cp.state.workspaceHandle !== undefined && internals?.requestedCwd !== undefined) { throw new CheckpointError("checkpoint.cwd_conflicts_restore", `internals.requestedCwd ("${internals.requestedCwd}") cannot be combined with a checkpoint workspace restore — ` + "the restored workspace's own mount path is authoritative for the task root. Drop requestedCwd on resume legs " + "(the checkpoint stays pending and is resumable without it)."); } if (cp.state.inheritedGate?.requiresParentConstraint === true) { const supplied = internals?.inheritedGate?.parentConstraints?.length ?? 0; if (supplied === 0) { throw new CheckpointError("resume.parent_constraint_missing", "this checkpoint's task ran under inherited parent-policy constraints (state.inheritedGate.requiresParentConstraint) " + "— the live policy/onAsk closures cannot be persisted, so the resume must re-supply them via " + "resumeStream(token, outcome, taskConfig, internals) with internals.inheritedGate.parentConstraints; " + "rejected pre-CAS (the checkpoint stays pending) rather than silently dropping the ancestors' gate"); } const expected = cp.state.inheritedGate.parentConstraintCount; if (expected !== undefined && supplied !== expected) { throw new CheckpointError("resume.parent_constraint_mismatch", `re-supplied ${supplied} parent constraint(s) but the checkpoint recorded ${expected} — a partial/mismatched ` + "re-supply would run the resumed leg under a different ancestor chain than it suspended with; " + "rejected pre-CAS (the checkpoint stays pending) — re-resume with the full original chain"); } if (checkpointVersionOf(cp) >= f012Row.version && (cp.state.inheritedGate.constraintChain === undefined || cp.state.inheritedGate.constraintDigest === undefined)) { throw new CheckpointError("checkpoint.invalid_outcome", `a v${checkpointVersionOf(cp)} checkpoint that requires parent constraints must carry BOTH the frozen constraintChain and its constraintDigest (they are minted in one write with the version stamp) — this row carries neither or only one; refusing to fall back to the count-only contract on a damaged row (corruption / downgrade guard), the checkpoint stays pending`, { reason: "constraint_chain_missing" }); } const expectedDigest = cp.state.inheritedGate.constraintDigest; if (expectedDigest !== undefined) { const persistedChain = cp.state.inheritedGate.constraintChain; if (persistedChain === undefined || constraintChainDigest(persistedChain) !== expectedDigest) { throw new CheckpointError("resume.parent_constraint_mismatch", "the checkpoint's OWN recorded constraint chain does not match its recorded digest " + "(corrupt / inconsistently-minted row) — the frozen deny data the resume would execute is not the data the " + "digest authenticates; rejected pre-CAS (the checkpoint stays pending)"); } const suppliedDigest = constraintChainDigest((internals?.inheritedGate?.parentConstraints ?? []).map((pc, i) => constraintChainEntryOfSuppliedLayer(pc, persistedChain[i]))); if (suppliedDigest !== expectedDigest) { throw new CheckpointError("resume.parent_constraint_mismatch", "the re-supplied parent-constraint chain's projection content does not match what the checkpoint recorded " + `(recorded ${expectedDigest}, re-supplied ${suppliedDigest}) — a same-shape chain with different frozen ` + "deny data would run the resumed leg under a different ancestor gate than it suspended with; " + "rejected pre-CAS (the checkpoint stays pending) — re-resume with the original chain's policies " + "(including, when the row recorded one, the ancestor's auto-mode arming recipe verbatim)"); } } } const resumeInternals = internals?.inheritedGate?.parentConstraints !== undefined ? { ...internals, inheritedGate: { ...internals.inheritedGate, parentConstraints: Object.freeze(internals.inheritedGate.parentConstraints.map((e) => ({ ...e }))), }, } : internals; return { resumeInternals }; }