UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

125 lines (124 loc) • 8.24 kB
import { isRemoteExecutionEnv, missingRestoreSurface } from "../remote-env.js"; import { remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./remote-env-retry.js"; import { rebaseWorkspacePathAcross } from "./workspace-path.js"; export { remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./remote-env-retry.js"; export async function prepareWorkspaceRestore(input) { const { ownedEnv, deps, spec, internals, resume, abortController, sessionId } = input; let taskRootPath = input.taskRootInitial; let restoredRootRebase; const rebaseRestoredPath = (p) => restoredRootRebase === undefined ? p : rebaseWorkspacePathAcross(p, restoredRootRebase.from, restoredRootRebase.to); if (resume?.workspaceHandle !== undefined) { const failResume = (message, cause, note) => { const e = new Error(message, cause ? { cause } : undefined); e.code = "resume.env_failed"; if (note !== undefined) e.remoteEnvFailure = note; throw e; }; const handle = resume.workspaceHandle; const refuseIdentityMismatch = (resumedHandle) => { if (resumedHandle.provider !== handle.provider) { failResume(`resume workspace identity mismatch: the checkpoint records provider "${handle.provider}" but the resumed env reports "${resumedHandle.provider}" — refusing to continue a workspace on a different backend (wire the factory to rebuild the checkpointed lane and re-resume)`); } if (handle.deviceId !== undefined && resumedHandle.deviceId !== handle.deviceId) { failResume(resumedHandle.deviceId === undefined ? `resume workspace identity mismatch: the checkpoint records deviceId "${handle.deviceId}" but the resumed env reports none — a device-bound workspace cannot be verified on a handle that does not state its device (absence is not a match)` : `resume workspace identity mismatch: the checkpoint records deviceId "${handle.deviceId}" but the resumed env reports "${resumedHandle.deviceId}" — refusing to continue on a different physical device`); } }; if (ownedEnv === undefined || !isRemoteExecutionEnv(ownedEnv)) { failResume("resume needs a RemoteExecutionEnv from executionEnvFactory to restore the workspace snapshot"); } else if (handle.snapshotId === undefined) { if (handle.restoreMode !== "park_only" && ownedEnv.capabilities.suspendable) { failResume("checkpoint workspaceHandle has no snapshotId and is not a park_only handle, but the resumed env is suspendable — refusing to resume on a possibly-unrestored workspace (corrupt checkpoint?)"); } refuseIdentityMismatch(ownedEnv.workspaceHandle()); if (handle.mountPath && handle.mountPath !== taskRootPath) { taskRootPath = handle.mountPath; } } else { const restoreSignal = spec.signal ? AbortSignal.any([abortController.signal, spec.signal]) : abortController.signal; const missingHere = missingRestoreSurface(ownedEnv); if (missingHere.length > 0) { failResume(`the resumed execution env cannot restore a workspace snapshot: its adapter does not implement ${missingHere.join(" or ")}. The checkpoint holds snapshot "${handle.snapshotId}" — wire an adapter that implements the full RemoteExecutionEnv restore surface and re-resume.`); } refuseIdentityMismatch(ownedEnv.workspaceHandle()); const { outcome: restored, attempts: restoreAttempts } = await restoreWorkspaceWithRetry(ownedEnv, handle.snapshotId, { abortSignal: restoreSignal, priorHandle: handle, }); if (!restored.ok) { failResume(`resumeVM failed after ${restoreAttempts} attempt(s) (${restored.error.code}): ${restored.error.message}`, restored.error, remoteEnvFailureNote("resumeVM", restored.error, restoreAttempts)); } else { refuseIdentityMismatch(restored.value); const restoredEnv = ownedEnv; const canonicalInEnv = async (p) => { try { const r = await restoredEnv.canonicalPath(p, restoreSignal); return r.ok ? r.value : undefined; } catch { return undefined; } }; let checkpointedCanonical; let sameRootUnderAlias = false; if (restored.value.mountPath !== handle.mountPath) { checkpointedCanonical = await canonicalInEnv(handle.mountPath); const restoredCanonical = await canonicalInEnv(restored.value.mountPath); sameRootUnderAlias = checkpointedCanonical !== undefined && restoredCanonical !== undefined && checkpointedCanonical === restoredCanonical; } if (restored.value.mountPath !== handle.mountPath && !sameRootUnderAlias) { if (resume.executesApprovedAction === true) { failResume(`resumeVM workspace-root divergence with a pending approved action: checkpointed mountPath "${handle.mountPath}" but the restored handle reports "${restored.value.mountPath}" — the approved args are bound to the checkpointed root; refusing to execute them against a moved workspace (adapter should honor priorHandle)`); } try { deps.onError?.(new Error(`resumeVM workspace-root divergence: checkpointed mountPath "${handle.mountPath}" but the restored handle reports "${restored.value.mountPath}" — the resumed task follows the restored root`), { phase: "config", sessionId }); } catch { } } if (restored.value.mountPath && restored.value.mountPath !== taskRootPath) { taskRootPath = restored.value.mountPath; } if (restored.value.mountPath && restored.value.mountPath !== handle.mountPath && !sameRootUnderAlias) { const from = checkpointedCanonical !== undefined && checkpointedCanonical !== handle.mountPath ? [handle.mountPath, checkpointedCanonical] : [handle.mountPath]; restoredRootRebase = { from, to: restored.value.mountPath }; try { deps.onError?.(new Error(from.length > 1 ? `resumeVM workspace-root rebase accepts both spellings of the checkpointed root (${from.map((f) => `"${f}"`).join(" and ")}) when migrating persisted paths to "${restored.value.mountPath}"` : `resumeVM workspace-root rebase is spelling-exact: it matches the checkpointed root "${handle.mountPath}" only, so persisted paths recorded under an equivalent alias of it are NOT migrated to "${restored.value.mountPath}" and stay as written`), { phase: "config", sessionId }); } catch { } } } const init = await ownedEnv.postResumeInit(); if (!init.ok) { failResume(`postResumeInit failed (${init.error.code}): ${init.error.message}`, init.error, remoteEnvFailureNote("postResumeInit", init.error, 1)); } } } if (internals?.onWorkspaceResolved !== undefined) { try { const workspaceEnv = ownedEnv ?? deps.executionEnv; internals.onWorkspaceResolved({ cwd: taskRootPath, isolated: internals.isolation === "worktree", remote: workspaceEnv !== undefined && (isRemoteExecutionEnv(workspaceEnv) || workspaceEnv.hostLocalPaths === false), }); } catch { } } return { taskRootFinal: taskRootPath, rebaseRestoredPath }; }