UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

115 lines (114 loc) • 6.55 kB
import { errorCodeOf } from "./assemble-result.js"; import { resumeDecisionWasNegative } from "./decide-continuation.js"; import { drainForwardedFramesBeforeDone, forwardsSubagentEvents } from "./prepare-run-refs.js"; import { hasDestroy } from "../remote-env.js"; import { createSafeNotifier } from "../safe-notify.js"; import { settleTeardownLeg } from "./teardown-bounded.js"; import { emitTrace } from "../trace.js"; import { deliverDelegationLifecycle } from "../types.js"; import { asThrownError, readMember, thrownText } from "../thrown-value.js"; export function streamSettleBackstop(input) { const { spec, resume, queue, taskIdRef, entryTracer, runStartedAt, live, runner, sessions, locallyClaimedTokens, parentConstraintRegistry, suspendedEnvReaps } = input; const onRunRejected = async (err) => { let code = errorCodeOf(err); const reopenReason = code === "resume.tool_unavailable" || code === "resume.tool_contract_mismatch" ? "tool_unavailable" : "env_failed"; const reopenable = resume !== undefined && resume.pendingActionStarted !== true && reopenReason !== undefined && !(resumeDecisionWasNegative(resume) && resume.decisionDelivered === true); let reopenCommitted = false; let reopenStateUnknown = false; if (reopenable && resume?.onEnvRestoreFailed) { try { await resume.onEnvRestoreFailed(reopenReason); reopenCommitted = true; } catch (reopenErr) { parentConstraintRegistry.delete(resume.cp.token); const original = asThrownError(err); const reopenMsg = thrownText(reopenErr); const definitive = errorCodeOf(reopenErr) === "checkpoint.reopen_failed"; reopenStateUnknown = !definitive; err = Object.assign(new Error(definitive ? `${reopenMsg} (original resume failure: ${original.message})` : `checkpoint reopen threw mid-flight — state UNKNOWN, confirm via the checkpoint store before retrying: ${reopenMsg} (original resume failure: ${original.message})`, { cause: original }), { code: "checkpoint.reopen_failed" }); code = "checkpoint.reopen_failed"; } } if (resume) locallyClaimedTokens.delete(resume.cp.token); if (resume && !reopenCommitted) { parentConstraintRegistry.delete(resume.cp.token); await settleTeardownLeg(() => sessions.unpin?.(resume.cp.sessionId), "sessions.unpin (resume-failure leg)", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: resume.cp.sessionId })); if (!reopenStateUnknown) { const failedReap = suspendedEnvReaps.get(resume.cp.token); suspendedEnvReaps.delete(resume.cp.token); if (failedReap?.env !== undefined && hasDestroy(failedReap.env)) { const failedEnv = failedReap.env; await settleTeardownLeg(() => failedEnv.destroy(), "terminal resume-failure env destroy", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: resume.cp.sessionId })); } } } if (live.resultValue !== undefined) { try { runner.deps.onError?.(asThrownError(err), { phase: "config", sessionId: live.resultValue.sessionId }); } catch { } queue.close(); return; } const remoteEnvFailure = (() => { let cur = err; for (let depth = 0; cur !== null && cur !== undefined && typeof cur === "object" && depth < 8; depth++) { const note = readMember(cur, "remoteEnvFailure"); if (note !== undefined) return [note]; cur = readMember(cur, "cause"); } return undefined; })(); live.resultValue = { taskId: taskIdRef.current ?? "unknown", sessionId: taskIdRef.sessionId ?? "unknown", ...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}), terminal: { kind: "failed", ...(code !== undefined ? { code } : {}), message: thrownText(err) }, result: "", ...(remoteEnvFailure !== undefined ? { remoteEnvFailures: remoteEnvFailure } : {}), ...(taskIdRef.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: taskIdRef.effectiveMemoryScopes } : {}), ...(taskIdRef.effectiveReasoning !== undefined ? { effectiveReasoning: taskIdRef.effectiveReasoning } : {}), ...(() => { const observed = taskIdRef.editedFiles?.(); return observed !== undefined && observed.length > 0 ? { editedFiles: observed } : {}; })(), ...(() => { const hinted = readMember(err, "retryAfterMs"); return code === "memory.admission_required" && typeof hinted === "number" && Number.isFinite(hinted) && hinted > 0 ? { retryAfterMs: hinted } : {}; })(), stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costMicroUsd: 0, usageMissing: true }, }; emitTrace(entryTracer.tracer, () => ({ kind: "task.end", version: 1, taskId: taskIdRef.current ?? "unknown", ...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}), status: "failed", errorCode: code, turns: 0, tokens: 0, durationMs: Date.now() - runStartedAt, ts: Date.now(), })); const owedTerminal = taskIdRef.delegationTerminalOwed; if (owedTerminal !== undefined) { taskIdRef.delegationTerminalOwed = undefined; deliverDelegationLifecycle(runner.deps.onDelegationLifecycle, { phase: "terminal", identity: owedTerminal, status: "failed", turns: 0, ...(code !== undefined ? { errorCode: code } : {}) }, createSafeNotifier({ onError: (f) => console.warn(`[sema-core] ${f.site}: delegation-lifecycle observer threw (contained; further failures counted, not re-disclosed): ${f.error.message}`), }), "runtask.onDelegationLifecycle"); } if (forwardsSubagentEvents(spec)) await drainForwardedFramesBeforeDone(); queue.push({ type: "done", result: live.resultValue }); queue.close(); }; return { onRunRejected }; }