UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

170 lines (169 loc) • 10.2 kB
import { hasBackgroundShell, sweepBackgroundShells, sweepRunBoundaryBackgroundShells } from "../background-shell.js"; import { checkpointVersionFor, debitLedger, mintCheckpointId, mintCheckpointToken, } from "../checkpoint-store.js"; import { defaultTaskRegistry } from "../task-registry.js"; import { checkpointScopeOf } from "./checkpoint-scope.js"; import { DEFAULT_RESOURCE_TTL_MS, publishCommittedSuspend, sanitizedTtlMs, USAGE_WINDOW_REAP_MARGIN_MS } from "./park-commit.js"; import { remoteEnvFailureNote } from "./remote-env-retry.js"; export function prepareBoundaryParks(input) { const { saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter } = input; if (saga === undefined) return { suspendForResource: undefined, suspendForPlatformLimit: undefined, suspendForReview: undefined }; const { inFlightSpendMicroUsd, serializeCheckpointState, commitSuspendSaga, suspendLoopCapHit, suspendableEnv, parkOnlyRemoteEnv, parkOnlyHandle } = saga; const mintResourceSuspend = async (reason, sliceSpend, hint) => { const rs = spec.resourceSuspend; if (!checkpointStore) return false; const scope = rs?.scope ?? checkpointScopeOf(spec); if (abortController.signal.aborted) return false; if (suspendLoopCapHit(priorLedger?.sliceCount ?? 0, maxSlices, " (resource slice limit)")) return false; const leafId = await session.getLeafId(); if (!leafId) return false; if (abortController.signal.aborted) return false; let remoteHandle; if (suspendableEnv !== undefined) { if (hasBackgroundShell(suspendableEnv)) await sweepBackgroundShells(suspendableEnv, defaultTaskRegistry); const snap = await suspendableEnv.suspendVM({ abortSignal: abortController.signal }); if (!snap.ok) { remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1)); deps.onError?.(new Error(`resource suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error }), { phase: "config", sessionId }); return false; } remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value }; } else if (parkOnlyRemoteEnv !== undefined) { if (hasBackgroundShell(parkOnlyRemoteEnv)) await sweepRunBoundaryBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry, { task: spec.retainBackgroundProcesses === true }); remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv); } const token = mintCheckpointToken(); const gate = { kind: "resource_limit", reason, ...(hint !== undefined ? { resumeAfterMs: hint.resumeAfterMs } : {}) }; const resourceLedgerOut = debitLedger(priorLedger, sliceSpend, resourceTotal); const mintedAt = Date.now(); const cp = { token, checkpointId: mintCheckpointId(), version: checkpointVersionFor({ resource: true, binding: false, token: resourceLedgerOut.totalTokens !== undefined, orgAdmission: orgAdmissionCheckpointState(), f012: f012CheckpointState(), realApproval: false, face: faceCheckpointState() }), scope, sessionId, leafId, gate, pendingAction: { kind: "resource_limit", reason }, state: serializeCheckpointState(remoteHandle), status: "pending", createdAt: mintedAt, deadline: mintedAt + Math.max(sanitizedTtlMs(rs?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS, hint !== undefined ? hint.resumeAfterMs + USAGE_WINDOW_REAP_MARGIN_MS : 0), suspendCount: priorSuspendCount, humanReview: humanReviewRef.count > 0 ? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] } : undefined, resourceLedger: resourceLedgerOut, ...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}), ...(spec.principal ? { principal: spec.principal } : {}), }; if ((await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).tag !== "committed") return false; publishCommittedSuspend({ pausedRef }, token, gate, scope, remoteHandle, cp.checkpointId, cp.pendingAction); try { await sessions.pin?.(sessionId); } catch (pinErr) { deps.onError?.(pinErr, { phase: "config", sessionId }); } harness.requestStopAfterTurn(); return true; }; const suspendForResource = resourceSuspendEligible ? mintResourceSuspend : undefined; const suspendForPlatformLimit = durableSuspendInfraReady ? mintResourceSuspend : undefined; const suspendForReview = checkpointStore ? async (reason) => { if (!checkpointStore) return false; if (abortController.signal.aborted) return false; if (incompleteSuspendAdapter !== undefined) { try { deps.onError?.(new Error(`plan_review park skipped: the per-task execution env declares capabilities.suspendable but its adapter does not implement ${incompleteSuspendAdapter.join(" or ")}, so its workspace could not be restored on resume (the plan-review request was dropped; the run continues without pausing)`), { phase: "config", sessionId }); } catch { } return false; } if (suspendLoopCapHit(suspendChainBase(), maxSuspends, " for a plan_review (likely a resume/restart loop).")) return false; const leafId = await session.getLeafId(); if (!leafId) { try { deps.onError?.(new Error("plan_review park skipped: no committed session leaf (the plan-review request was dropped; the run continues without pausing)"), { phase: "config", sessionId }); } catch { } return false; } if (abortController.signal.aborted) return false; let remoteHandle; if (suspendableEnv !== undefined) { if (hasBackgroundShell(suspendableEnv)) await sweepBackgroundShells(suspendableEnv, defaultTaskRegistry); const snap = await suspendableEnv.suspendVM({ abortSignal: abortController.signal }); if (!snap.ok) { remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1)); deps.onError?.(new Error(`plan_review suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error }), { phase: "config", sessionId }); return false; } remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value }; } else if (parkOnlyRemoteEnv !== undefined) { if (hasBackgroundShell(parkOnlyRemoteEnv)) await sweepRunBoundaryBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry, { task: spec.retainBackgroundProcesses === true }); remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv); } const token = mintCheckpointToken(); const scope = checkpointScopeOf(spec); const gate = { kind: "plan_review", reason: reason ?? `plan review requested` }; const reviewLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false }); const mintedAt = Date.now(); const cp = { token, checkpointId: mintCheckpointId(), version: checkpointVersionFor({ resource: false, binding: true, token: reviewLedger.totalTokens !== undefined, orgAdmission: orgAdmissionCheckpointState(), f012: f012CheckpointState(), realApproval: false, face: faceCheckpointState() }), scope, sessionId, leafId, gate, pendingAction: { kind: "plan_review" }, state: serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd()), status: "pending", createdAt: mintedAt, suspendedAt: now(), deadline: mintedAt + (sanitizedTtlMs(spec.durableApproval?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS), suspendCount: suspendChainBase() + 1, humanReview: humanReviewRef.count > 0 ? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] } : undefined, resourceLedger: reviewLedger, sourceTaskId: sessionId, ...(spec.principal ? { principal: spec.principal } : {}), ...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}), }; if ((await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).tag !== "committed") return false; publishCommittedSuspend({ pausedRef }, token, gate, scope, remoteHandle, cp.checkpointId, cp.pendingAction); try { await sessions.pin?.(sessionId); } catch (pinErr) { deps.onError?.(pinErr, { phase: "config", sessionId }); } abortController.abort(); void harness.abort(); return true; } : undefined; return { suspendForResource, suspendForPlatformLimit, suspendForReview }; }