@sema-agent/core
Version:
Stateless, task-oriented AI agent core
307 lines (306 loc) • 18.3 kB
JavaScript
import { defaultTaskRegistry } from "../task-registry.js";
import { isRemoteExecutionEnv, isSuspendable, RemoteExecutionError } from "../remote-env.js";
import { constraintChainDigest, constraintChainEntryOfLayer } from "../tool-policy.js";
import { raceSettlementAgainstSignal } from "./abort-race.js";
import { placementValueOrAbsent } from "./checkpoint-scope.js";
import { serializeAnnouncedListings } from "./announce-once-ledger.js";
import { remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./remote-env-retry.js";
import { thrownText } from "../thrown-value.js";
const PARK_COMPENSATION_TIMEOUT_MS = 30_000;
export async function compensateUnparkedPause(remoteEnv, snapshotId, io) {
const boundCtl = new AbortController();
const boundTimer = setTimeout(() => boundCtl.abort(), io.boundMs);
const bound = boundCtl.signal;
let phase = "resumeVM";
let restoreAttempts = 1;
let vmRunning = false;
const work = (async () => {
const { outcome: back, attempts: backAttempts } = await restoreWorkspaceWithRetry(remoteEnv, snapshotId, { abortSignal: bound }, (attempt) => (restoreAttempts = attempt));
if (!back.ok) {
io.noteFailure(remoteEnvFailureNote("resumeVM", back.error, backAttempts));
io.disclose(back.error);
return { ok: false, reason: `resumeVM failed (${back.error.code}) while compensating an unparked pause` };
}
vmRunning = true;
if (bound.aborted) {
return { ok: false, reason: "resumeVM settled only after the decision bound" };
}
phase = "postResumeInit";
const init = await remoteEnv.postResumeInit({ abortSignal: bound });
if (!init.ok) {
io.noteFailure(remoteEnvFailureNote("postResumeInit", init.error, 1));
io.disclose(init.error);
return { ok: false, reason: `postResumeInit failed (${init.error.code}) while compensating an unparked pause` };
}
return { ok: true };
})();
const raced = await raceSettlementAgainstSignal(work, bound);
clearTimeout(boundTimer);
if (raced.tag === "value")
return raced.value;
if (raced.tag === "threw") {
io.noteFailure(remoteEnvFailureNote(phase, raced.error instanceof RemoteExecutionError
? raced.error
: new RemoteExecutionError("unknown", `the pause compensation's ${phase} call threw: ${thrownText(raced.error)}`), phase === "resumeVM" ? restoreAttempts : 1));
io.disclose(raced.error);
return {
ok: false,
reason: "the pause compensation threw (adapter contract violation; the deployment's error face carries the exception)",
};
}
const timedOut = new RemoteExecutionError("timeout", `the pause compensation's ${phase} call did not settle within the ${io.boundMs}ms decision bound`);
io.noteFailure(remoteEnvFailureNote(phase, timedOut, phase === "resumeVM" ? restoreAttempts : 1));
io.disclose(timedOut);
const destroyOwnerlessLate = () => {
io.disclose(new Error(`park compensation settled after its ${io.boundMs}ms decision bound: the VM is running with no run ` +
`left to own it — destroying the env (an ownerless running VM must not linger; the run already took ` +
`the fail-closed arm when the bound fired)`));
void Promise.resolve()
.then(() => remoteEnv.destroy())
.catch((destroyErr) => io.disclose(destroyErr));
};
void work.then((late) => {
if (vmRunning) {
destroyOwnerlessLate();
return;
}
void late;
}, (lateErr) => {
io.disclose(lateErr);
if (vmRunning)
destroyOwnerlessLate();
});
return {
ok: false,
reason: `the pause compensation did not settle within ${io.boundMs}ms — treated as failed (a late-restored VM is destroyed; a late restore failure is disclosed)`,
};
}
function stampPlacementRootSessionId(placementRootResolved) {
return placementValueOrAbsent(placementRootResolved);
}
export function prepareSuspendSaga(input) {
const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, skillGrantCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
if (!gateMachineryActive)
return { saga: undefined };
const inFlightSpendMicroUsd = () => {
const own = liveSpendRef.get?.().costMicroUsd ?? 0;
const seededNested = resume?.seed.nestedStats.costMicroUsd;
const nestedDelta = nestedStats.costMicroUsd - (typeof seededNested === "number" && Number.isFinite(seededNested) ? seededNested : 0);
return (Number.isFinite(own) ? own : 0) + Math.max(0, Number.isFinite(nestedDelta) ? nestedDelta : 0);
};
const repairBundleForCheckpoint = (parkedSpendMicroUsd) => {
const carried = internals?.repairBundle !== undefined
? structuredClone(internals.repairBundle)
: resume?.seed.repairBundle !== undefined
? structuredClone(resume.seed.repairBundle)
: undefined;
if (carried === undefined)
return undefined;
if (parkedSpendMicroUsd === undefined || !Number.isFinite(parkedSpendMicroUsd) || parkedSpendMicroUsd <= 0)
return carried;
const prior = typeof carried.spentMicroUsd === "number" && Number.isFinite(carried.spentMicroUsd) ? Math.max(0, carried.spentMicroUsd) : 0;
carried.spentMicroUsd = prior + parkedSpendMicroUsd;
return carried;
};
const screeningParkDisclosedRef = { done: false };
const serializeCheckpointState = (workspaceHandle, parkedSpendMicroUsd) => ({
activeTools: [...activeTools],
outputRef: { value: outputRef.value, set: outputRef.set },
nestedStats: { ...nestedStats },
readFileState: readFileStateForCheckpoint ? [...readFileStateForCheckpoint.entries()] : undefined,
readFace: faceCheckpointSection(),
writeFace: writeFaceCheckpointSection(),
skillGrants: skillGrantCheckpointSection(),
reminderMark,
repairBundle: repairBundleForCheckpoint(parkedSpendMicroUsd),
workspaceHandle,
handsCwd: handsCwdRef?.current,
activeWorktree: worktreeSessionRef?.current ? { ...worktreeSessionRef.current } : undefined,
runningBackgroundTasks: (() => {
const live = defaultTaskRegistry
.list({ owner: hostTaskId, scope: taskScope, sessionId })
.filter((t) => t.status === "pending" || t.status === "running")
.map((t) => ({ id: t.task_id, ...(t.description !== undefined ? { description: t.description } : {}) }));
const owed = undeliveredContinuationRef.current.orphanTasks.filter((o) => !live.some((t) => t.id === o.id));
const all = [...owed, ...live];
return all.length > 0 ? all : undefined;
})(),
pendingSteerQueue: undefined,
inheritedGate: (() => {
const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
const constraintChain = (inheritedParentConstraints?.length ?? 0) > 0
? inheritedParentConstraints.map((pc) => constraintChainEntryOfLayer(pc))
: seedInheritedGate?.constraintChain;
const constraintDigest = (inheritedParentConstraints?.length ?? 0) > 0
? constraintChainDigest(constraintChain)
: seedInheritedGate?.constraintDigest;
const declaredMode = permissionMode.declared;
const modeRecorded = declaredMode !== "default" || permissionMode.requested !== undefined || seedInheritedGate?.permissionMode !== undefined;
const ancestorRulesAtMint = ancestorRulesNow();
return ancestorRulesAtMint !== undefined ||
inheritedShellGate !== undefined ||
modeRecorded ||
inheritedAdmittedOrgScopes !== undefined ||
ownOrgVerdictRef.current !== undefined ||
orgGovernedProvenance ||
requiresParentConstraint
? {
...(ancestorRulesAtMint !== undefined ? { ancestorRules: structuredClone(ancestorRulesAtMint) } : {}),
...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
...(modeRecorded ? { permissionMode: declaredMode } : {}),
...(permissionMode.prePlanMode !== undefined ? { prePlanMode: permissionMode.prePlanMode } : {}),
...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
...(ownOrgVerdictRef.current !== undefined
? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
: {}),
...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
requiresParentConstraint,
...(requiresParentConstraint && parentConstraintCount !== undefined ? { parentConstraintCount } : {}),
...(requiresParentConstraint && constraintChain !== undefined
? { constraintChain: structuredClone(constraintChain), ...(constraintDigest !== undefined ? { constraintDigest } : {}) }
: {}),
}
: undefined;
})(),
announcedListings: serializeAnnouncedListings(announcedListingsRef),
gitAnnouncement: gitStatusRef.announced !== undefined ? { ...gitStatusRef.announced } : undefined,
delegationProvenance: internals?.delegationProvenance !== undefined ? { ...internals.delegationProvenance.ref.current } : undefined,
isDelegatedChild: hookIdentity.isDelegatedChild ? true : undefined,
placementRootSessionId: stampPlacementRootSessionId(placementRootResolved),
externalContentTarget: externalContentTargetActive ? true : undefined,
});
const compensatePausedVM = (remoteEnv, snapshotId) => compensateUnparkedPause(remoteEnv, snapshotId, {
boundMs: PARK_COMPENSATION_TIMEOUT_MS,
noteFailure: (note) => remoteEnvFailures.push(note),
disclose: (err) => {
try {
deps.onError?.(err, { phase: "config", sessionId });
}
catch {
}
},
});
const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle, cutSignal) => {
if (!checkpointStore)
return { tag: "absent" };
if (memoryEngineSession)
await memoryEngineSession.harvest("checkpoint");
if (cutSignal?.aborted) {
if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
const back = await compensatePausedVM(remoteEnv, remoteHandle.snapshotId);
if (!back.ok) {
abortController.abort();
void harness.abort();
return { tag: "compensation_failed", reason: back.reason };
}
}
return { tag: "cut" };
}
const announceCommittedScreeningPark = () => {
const committedCount = cp.state.inheritedGate?.parentConstraintCount;
if (cp.state.inheritedGate?.requiresParentConstraint === true &&
!screeningParkDisclosedRef.done &&
(inheritedParentConstraints ?? []).some((pc) => pc.preToolUse !== undefined)) {
screeningParkDisclosedRef.done = true;
try {
deps.onError?.(new Error(`durable park under an inherited PreToolUse SCREENING constraint: this checkpoint records ` +
`requiresParentConstraint with parentConstraintCount=${committedCount ?? "(unrecorded)"}, and the live ` +
`closures cannot be persisted. A resume on THIS Runner re-supplies them automatically; a resume on a fresh ` +
`Runner (restart / another replica) must hand back the whole chain via resumeStream(..., internals), ` +
`rebuilding the screening entry with createPreToolUseConstraintPolicy(hook, env) — otherwise the row stays ` +
`pending. If the face only observes, declare Hooks.preToolUseObservational and it stops entering the chain.`), { phase: "degraded", sessionId, classification: "screening-constraint-in-durable-chain" });
}
catch {
}
}
};
const confirmPutOutcome = async (putErr) => {
if (putErr?.code === "checkpoint.already_exists")
return "absent";
try {
const row = await checkpointStore.get(token);
if (row == null)
return "absent";
const sameCall = (a, b) => a.kind === b.kind && (a.kind !== "tool_approval" || b.kind !== "tool_approval" || a.toolCallId === b.toolCallId);
const isOurs = row.scope === cp.scope && row.sessionId === cp.sessionId && row.leafId === cp.leafId && sameCall(row.pendingAction, cp.pendingAction);
return isOurs ? "committed" : "absent";
}
catch (readErr) {
deps.onError?.(readErr, { phase: "config", sessionId });
return "unknown";
}
};
try {
await checkpointStore.put(token, cp);
announceCommittedScreeningPark();
return { tag: "committed" };
}
catch (putErr) {
deps.onError?.(putErr, { phase: "config", sessionId });
const confirmed = await confirmPutOutcome(putErr);
if (confirmed === "committed") {
try {
deps.onError?.(new Error(`durable approval checkpoint: the store's write reported a failure but the row EXISTS — the commit ` +
`landed and its acknowledgement was lost (a network backend can complete the INSERT and drop the ` +
`connection before replying). The run suspends on that row rather than continuing, which would leave ` +
`a redeemable pending checkpoint behind. This backend's acknowledgement path is lossy — the operator ` +
`face carries the store's own message.`), { phase: "degraded", sessionId, classification: "checkpoint-put-confirmation-lost" });
}
catch {
}
announceCommittedScreeningPark();
return { tag: "committed" };
}
if (confirmed === "unknown") {
const unknownReason = "the approval checkpoint's state cannot be established (the store rejected the write and then could not be read back; a committed-but-unacknowledged row may exist) — the run is stopped rather than continued past an approval whose durable record is unknown";
try {
deps.onError?.(new Error(`durable approval checkpoint: ${unknownReason}`), {
phase: "degraded",
sessionId,
classification: "checkpoint-put-outcome-unknown",
});
}
catch {
}
abortController.abort();
void harness.abort();
return { tag: "unknown", reason: unknownReason };
}
const reason = "the approval checkpoint could not be persisted (the store rejected the write; the deployment's error face carries the store's own message)";
if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
const back = await compensatePausedVM(remoteEnv, remoteHandle.snapshotId);
if (back.ok)
return { tag: "absent", reason };
abortController.abort();
void harness.abort();
return { tag: "compensation_failed", reason };
}
return { tag: "absent", reason };
}
};
const suspendLoopCapHit = (count, cap, detail) => {
if (cap === undefined)
return false;
if (count + 1 <= cap)
return false;
suspendLoopRef.hit = true;
abortController.abort();
void harness.abort();
try {
deps.onError?.(new Error(`suspend loop: task already suspended ${count} time(s) (max ${cap}); refusing to suspend again${detail}`), { phase: "config", sessionId });
}
catch {
}
return true;
};
const suspendableEnv = ownedEnv !== undefined && isSuspendable(ownedEnv) ? ownedEnv : undefined;
const parkOnlyRemoteEnv = suspendableEnv === undefined && ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && incompleteSuspendAdapter === undefined
? ownedEnv
: undefined;
const parkOnlyHandle = (env) => {
const { snapshotId: _lineage, ...identity } = env.workspaceHandle();
return { ...identity, restoreMode: "park_only" };
};
return { saga: { inFlightSpendMicroUsd, serializeCheckpointState, compensatePausedVM, commitSuspendSaga, suspendLoopCapHit, suspendableEnv, parkOnlyRemoteEnv, parkOnlyHandle } };
}