@sema-agent/core
Version:
Stateless, task-oriented AI agent core
398 lines (397 loc) • 26.3 kB
JavaScript
import { uuidv7 } from "../../engine/session/uuid.js";
import { Value } from "typebox/value";
import { ASK_USER_QUESTION_TOOL_NAME, classifyQuestionOutcome, validateAskQuestions } from "../ask-question.js";
import { hasBackgroundShell, sweepBackgroundShells, sweepRunBoundaryBackgroundShells } from "../background-shell.js";
import { boundInputHashOf } from "../canonical-json.js";
import { buildRiskDescriptor, checkpointVersionFor, debitLedger, encodeAtFidelity, mintCheckpointId, mintCheckpointToken, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
import { pauseOf } from "../pause-registry.js";
import { askCarryRowMembers } from "../hooks.js";
import { askMandateMember, ownedClearanceFacts } from "../ask-origin.js";
import { defaultTaskRegistry } from "../task-registry.js";
import { advertisedWritableRootsSeat, isLiveApproverSeat, carriesBidiControls, describeThrown, refuseOutOfContractDecision, tryCloneArgs } from "../tool-policy.js";
import { InMemoryToolResultStore, ScopedToolResultStore, isVolatileOffloadStore } from "../tool-result-store.js";
import { raceAbort, raceSettlementAgainstSignal } from "./abort-race.js";
import { checkpointScopeOf } from "./checkpoint-scope.js";
import { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
import { DEFAULT_UNATTENDED_APPROVAL_TTL_MS, publishCommittedSuspend, sanitizedTtlMs } from "./park-commit.js";
import { remoteEnvFailureNote } from "./remote-env-retry.js";
import { callFaceSeat, parkedToolIdentity, parkedToolFace } from "../tool-roster.js";
import { seatOf, wireOf } from "../tool-name-readings.js";
import { skillCallGrantDigest } from "../skill-grant.js";
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
class ParkRefusal extends Error {
constructor(message, options) {
super(message, options);
this.name = "ParkRefusal";
}
}
const wordlessDemand = (toolName) => {
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
};
export function prepareParkAsk(input) {
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, skillSpecs, now, humanReviewRef, abortController, harness, pausedRef } = input;
if (askLane === undefined || saga === undefined)
return { parkAsk: undefined };
const { composedCallSignal, approvalPreviewOf } = askLane;
const { inFlightSpendMicroUsd, serializeCheckpointState, compensatePausedVM, commitSuspendSaga, suspendLoopCapHit, suspendableEnv, parkOnlyRemoteEnv, parkOnlyHandle } = saga;
const resolveContentAsk = async (req, callSignal) => {
const contentAskSignal = composedCallSignal(callSignal);
if (!contentAskRoutable(req.toolCallId) || liveQuestionFace === undefined || mountedQuestionTool === undefined) {
return { kind: "unavailable", parkDeclined: false };
}
const deliveryId = uuidv7();
const bindOutcome = (entry, questionsHash) => {
if (contentAskBindings.size >= CONTENT_ASK_BINDING_CAP && !contentAskBindings.has(req.toolCallId))
return;
contentAskBindings.set(req.toolCallId, { ...entry, questionsHash, deliveryId });
};
const refuseBeforeDelivery = (code, presentedInput) => ({
kind: "delivery_failure",
code,
...(presentedInput !== undefined ? { presentedInput } : {}),
});
let retained;
let presented;
try {
retained = structuredClone(req.args);
presented = structuredClone(retained.questions);
}
catch {
return refuseBeforeDelivery("question.malformed_request");
}
const retainedQuestionsHash = boundInputHashOf(retained.questions);
if (!Value.Check(mountedQuestionTool.parameters, retained) || validateAskQuestions(presented) !== undefined) {
return refuseBeforeDelivery("question.malformed_request", retained);
}
if (contentAskBindings.size >= CONTENT_ASK_BINDING_CAP && !contentAskBindings.has(req.toolCallId)) {
return { kind: "unavailable", parkDeclined: true };
}
if (contentAskSignal.aborted) {
return { kind: "delivery_failure", code: "question.aborted", presentedInput: retained };
}
try {
const facePromise = (async () => liveQuestionFace({
toolCallId: req.toolCallId,
questions: presented,
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
sourceTaskId: sessionId,
boundInputHash: boundInputHashOf(retained),
deliveryId,
}, contentAskSignal))();
const settlement = await raceAbort(facePromise.then((outcome) => ({ tag: "outcome", outcome }), (error) => ({ tag: "threw", error })), contentAskSignal, () => ({ tag: "aborted" }));
if (settlement.tag === "aborted") {
void facePromise.then((late) => {
if (classifyQuestionOutcome(late).shape !== "answered")
return;
lateStrandedAnswers.push({ deliveryId, toolCallId: req.toolCallId });
discloseStrandedAnswers([{ deliveryId, toolCallId: req.toolCallId }], "the task had already ended when the answer arrived");
}, () => undefined).catch(() => undefined);
}
if (settlement.tag === "outcome") {
const outcome = settlement.outcome;
const reading = classifyQuestionOutcome(outcome);
if (reading.shape === "contradictory") {
bindOutcome({ kind: "failed", error: new Error("the question channel returned a contradictory outcome (an unavailable answer)") }, retainedQuestionsHash);
return { kind: "delivery_failure", code: "question.human_channel_failed", presentedInput: retained };
}
if (reading.shape === "unavailable")
return { kind: "unavailable", parkDeclined: true, presentedInput: retained };
const answerSnapshot = reading.answer;
bindOutcome({ kind: "answered", answer: answerSnapshot }, retainedQuestionsHash);
return { kind: "answered", presentedInput: retained };
}
const error = settlement.tag === "threw"
? settlement.error
: Object.assign(new Error("the task ended while this question was still out"), { name: "AbortError" });
bindOutcome({ kind: "failed", error }, retainedQuestionsHash);
return {
kind: "delivery_failure",
code: settlement.tag === "aborted" || contentAskSignal.aborted ? "question.aborted" : "question.human_channel_failed",
presentedInput: retained,
};
}
catch (err) {
bindOutcome({ kind: "failed", error: err }, retainedQuestionsHash);
return { kind: "delivery_failure", code: "question.human_channel_failed", presentedInput: retained };
}
};
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, allowRuleShadowed, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated, carry) => {
const syncFirstEligible = wireOf("park.syncFirstRoute", req) === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
if (syncFirstEligible &&
runtimeCaps?.forceDurableGate !== true &&
liveFaceUnavailable !== true &&
!inheritedUnavailableAsks.has(req.toolCallId)) {
return undefined;
}
const cutSignal = composedCallSignal(carry?.signal);
let token;
let gate;
let cp;
let remoteHandle;
const remoteEnv = suspendableEnv;
try {
const parked = tryCloneArgs(postHookArgs);
if (!parked.ok) {
throw new ParkRefusal(`the arguments of "${req.toolName}" cannot be captured onto an approval checkpoint (unclonable value: ${parked.reason}) — ` +
`a durable approval row must hold an inert snapshot of exactly what a human approves and a resume executes`, { cause: parked.cause });
}
let backendFidelity;
try {
backendFidelity = resolveDeclaredFidelity(checkpointStore, "checkpointStore");
}
catch (err) {
throw new ParkRefusal(`the checkpoint backend declares an unrecognized encoding fidelity, so the width of the snapshot this row may hold is unknown (${describeThrown(err)}) — ` +
`refusing to park rather than guess which values would survive it`, { cause: err });
}
const captured = encodeAtFidelity(backendFidelity, parked.value);
if (!captured.ok) {
throw new ParkRefusal(`the arguments of "${req.toolName}" cannot be encoded by this checkpoint backend (${describeThrown(captured.cause)}) — ` +
`a durable approval row must hold an inert snapshot of exactly what a human approves and a resume executes`, { cause: captured.cause });
}
const parkedArgs = captured.value;
const projectionMovedArgs = backendFidelity !== "structured-clone" && !samePlainValue(parked.value, parkedArgs);
if (projectionMovedArgs && basePolicyForResumeEdit === undefined) {
throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and no policy is wired to re-adjudicate the stored shape ` +
`(no basePolicyForResumeEdit override and no caller toolPolicy) — refusing to file a row whose executable form nothing adjudicated`);
}
let storedShapeFacts;
if (projectionMovedArgs && basePolicyForResumeEdit !== undefined) {
const presented = encodeAtFidelity(backendFidelity, parkedArgs);
if (!presented.ok) {
throw new ParkRefusal(`the stored form of "${req.toolName}"'s arguments could not be presented for re-adjudication (${describeThrown(presented.cause)})`, { cause: presented.cause });
}
const reprojectedRaced = await raceSettlementAgainstSignal(Promise.resolve(basePolicyForResumeEdit.check({
toolName: req.toolName,
args: presented.value,
toolCallId: req.toolCallId,
budget: budgetSnapshot,
...callFaceSeat(req.face),
...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}),
...advertisedWritableRootsSeat(advertisedWritableDirs),
}, cutSignal)), cutSignal);
if (reprojectedRaced.tag === "aborted")
return undefined;
if (reprojectedRaced.tag === "threw")
throw reprojectedRaced.error;
const reprojected = refuseOutOfContractDecision(reprojectedRaced.value);
const rewroteTheFiledValue = reprojected.updatedInput !== undefined && !samePlainValue(reprojected.updatedInput, parkedArgs);
if (reprojected.action === "deny" || rewroteTheFiledValue) {
throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and the deployment's tool policy does not accept the stored shape` +
`${reprojected.action === "deny" && reprojected.message ? `: ${reprojected.message}` : ""} — ` +
`refusing to file a row whose executable form nothing adjudicated`);
}
if (reprojected.action === "ask")
storedShapeFacts = ownedClearanceFacts(reprojected);
}
const rowRealApproval = realApproval ?? (storedShapeFacts?.requiresRealApproval === true ? { origin: carry?.origin ?? wordlessDemand(req.toolName) } : undefined);
if (offloadStore && isVolatileOffloadStore(offloadStore) && !offloadStore.isEmpty()) {
throw new ParkRefusal("durable suspend requires a durable toolResultStore (the default InMemoryToolResultStore " +
"holds offloaded results that are lost across a cross-replica resume); inject " +
"RunnerDeps.toolResultStore or disable offload.");
}
if (ownedEnv !== undefined && remoteEnv === undefined && parkOnlyRemoteEnv === undefined) {
throw new ParkRefusal(incompleteSuspendAdapter !== undefined
? `durable suspend is not supported with this per-task executionEnvFactory env: it declares capabilities.suspendable but its adapter does not implement ${incompleteSuspendAdapter.join(" or ")}, so a snapshot taken now could never be restored. Implement the full RemoteExecutionEnv restore surface, or declare capabilities.suspendable:false if the workspace is externally durable (the park-only lane).`
: "durable suspend is not supported with a non-remote per-task executionEnvFactory env: the " +
"minted env is destroyed on suspend, so a resumed file/shell tool would act on a fresh " +
"(empty) env. Use a RemoteExecutionEnv factory (it is paused, not destroyed) or a static, " +
"caller-owned RunnerDeps.executionEnv.");
}
const leafId = await session.getLeafId();
if (!leafId) {
throw new ParkRefusal("cannot suspend: session has no committed leaf to resume from");
}
const { messages } = await session.buildContext();
const { batchToolCallIds, completedCallIds } = batchContextAt(messages, req.toolCallId);
if (cutSignal.aborted) {
return undefined;
}
if (suspendLoopCapHit(suspendChainBase(), maxSuspends, ` for tool "${req.toolName}" — likely a resume/restart loop.`))
return undefined;
if (remoteEnv !== undefined) {
if (hasBackgroundShell(remoteEnv))
await sweepBackgroundShells(remoteEnv, defaultTaskRegistry);
const snap = await remoteEnv.suspendVM({ abortSignal: cutSignal });
if (!snap.ok) {
remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1));
throw new Error(`suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error });
}
remoteHandle = { ...remoteEnv.workspaceHandle(), snapshotId: snap.value };
if (cutSignal.aborted) {
const back = await compensatePausedVM(remoteEnv, snap.value);
if (!back.ok) {
abortController.abort();
void harness.abort();
}
return undefined;
}
}
else if (parkOnlyRemoteEnv !== undefined) {
if (hasBackgroundShell(parkOnlyRemoteEnv))
await sweepRunBoundaryBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry, { task: spec.retainBackgroundProcesses === true });
remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
}
token = mintCheckpointToken();
const tierSeat = seatOf("park.riskDescriptor", irreversibilityTier, req);
const riskDescriptor = buildRiskDescriptor({
toolName: req.toolName,
args: parkedArgs,
safety,
...(allowRuleShadowed !== undefined ? { allowRuleShadowed } : {}),
...(probeReason !== undefined ? { probeReason } : {}),
...(probeCause !== undefined ? { probeCause } : {}),
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
...(effectiveShellGate !== undefined ? { shellGateDoctrine: effectiveShellGate } : {}),
});
gate =
safety !== undefined || rowRealApproval !== undefined
? {
kind: "irreversible_ask",
reason: safety !== undefined
? `human approval required before safety-tightened tool "${req.toolName}"`
: `real human approval required for tool "${req.toolName}" (non-budgetable: ${rowRealApproval.origin})`,
toolName: req.toolName,
...(safety !== undefined ? { safetyAxis: safety } : {}),
...(rowRealApproval !== undefined ? { realApproval: rowRealApproval } : {}),
riskDescriptor,
}
: durableApproval
? {
kind: "human",
reason: `human approval required for tool "${req.toolName}"`,
toolName: req.toolName,
riskDescriptor,
}
: {
kind: "irreversible_ask",
reason: `human approval required before irreversible tool "${req.toolName}"`,
toolName: req.toolName,
riskDescriptor,
};
const scope = durableApproval?.scope || checkpointScopeOf({ principal: spec.principal });
const ttlMs = sanitizedTtlMs(durableApproval?.ttlMs);
const checkpointState = serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd());
if (rowRealApproval !== undefined && checkpointState.readFace !== undefined) {
checkpointState.readFace = { ...checkpointState.readFace, realApproval: true };
}
const approvalLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
const rowMatchedAskRule = matchedAskRule ?? storedShapeFacts?.matchedAskRule;
const storedShapeMandated = askMandateMember(storedShapeFacts).mandated === true;
const clearanceFacts = {
...(carry?.mandated === true || storedShapeMandated ? { mandated: true } : {}),
...(carry?.mandate !== undefined && !storedShapeMandated ? { mandate: carry.mandate } : {}),
...(carry?.authoredBy === "deployment" ? { authoredBy: "deployment" } : {}),
...(rowRealApproval !== undefined ? { requiresRealApproval: true } : {}),
...(allowRuleShadowed !== undefined ? { allowRuleShadowed } : {}),
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
...(rowMatchedAskRule !== undefined ? { matchedAskRule: rowMatchedAskRule } : {}),
...(probeMandated === true ? { probeMandated: true } : {}),
...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
...(segmentCoverage !== undefined ? { segmentCoverage } : {}),
};
const mintedAt = Date.now();
cp = {
token,
checkpointId: mintCheckpointId(),
version: checkpointVersionFor({ resource: false, binding: true, token: approvalLedger.totalTokens !== undefined, orgAdmission: orgAdmissionCheckpointState(), f012: f012CheckpointState(), realApproval: rowRealApproval !== undefined, face: faceCheckpointState() }),
scope,
sessionId,
leafId,
...pauseOf(gate, {
kind: "tool_approval",
toolCallId: req.toolCallId,
toolName: req.toolName,
...parkedToolIdentity(toolRosterDeltas.current, req.toolName), ...parkedToolFace(toolRosterDeltas.current, req),
args: parkedArgs,
...(() => {
const p = approvalPreviewOf(req.toolName, parkedArgs);
const bidi = carriesBidiControls(parkedArgs) || carriesBidiControls(p.preview);
return {
...(p.preview !== undefined ? { preview: p.preview } : {}),
...(p.withheld !== undefined ? { previewWithheld: p.withheld } : {}),
...(bidi ? { hasBidiControls: true } : {}),
};
})(),
...(rowRealApproval !== undefined ? { requiresRealApproval: true } : {}),
...askMandateMember(clearanceFacts),
...askCarryRowMembers(carry),
...ruleOffersOf(req, parkedArgs, clearanceFacts),
boundInputHash: boundInputHashOf(parkedArgs),
...((grantDigest) => (grantDigest !== undefined ? { boundGrantDigest: grantDigest } : {}))(skillCallGrantDigest(req, parkedArgs, skillSpecs, SKILL_TOOL_NAME)),
batchToolCallIds,
completedCallIds,
}),
state: checkpointState,
deadline: ttlMs !== undefined
? mintedAt + ttlMs
: durableApproval !== undefined
? undefined
: mintedAt + DEFAULT_UNATTENDED_APPROVAL_TTL_MS,
status: "pending",
createdAt: mintedAt,
suspendedAt: now(),
humanReview: humanReviewRef.count > 0
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
: undefined,
suspendCount: suspendChainBase() + 1,
resourceLedger: approvalLedger,
sourceTaskId: sessionId,
...(spec.principal ? { principal: spec.principal } : {}),
...(durableApproval !== undefined ? { durableApproval: { ...durableApproval } } : {}),
};
}
catch (err) {
const cutWon = cutSignal.aborted;
const preselected = cutWon
? undefined
: {
parkFailed: err instanceof ParkRefusal
? err.message
: "the durable approval park could not be prepared (the deployment's error face carries the exception)",
};
try {
deps.onError?.(err, { phase: "config", sessionId });
}
catch {
}
return preselected;
}
const committed = await commitSuspendSaga(token, cp, remoteEnv, remoteHandle, cutSignal);
if (committed.tag !== "committed") {
if (committed.tag === "cut")
return undefined;
if (committed.tag === "absent") {
if (cutSignal.aborted)
return undefined;
return committed.reason !== undefined ? { parkFailed: committed.reason } : undefined;
}
return { parkFailed: committed.reason };
}
publishCommittedSuspend({ pausedRef }, token, gate, cp.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 { token, gate };
}
: undefined;
return { parkAsk: { resolveContentAsk, suspendAsk } };
}
export function batchContextAt(messages, currentId) {
let batch = [];
for (const m of messages) {
if (m.role === "assistant") {
const ids = [...new Set(m.content.filter((c) => c.type === "toolCall").map((c) => c.id))];
if (ids.includes(currentId))
batch = ids;
}
}
const resolved = new Set(messages.filter((m) => m.role === "toolResult").map((m) => m.toolCallId));
const completedCallIds = batch.filter((id) => id !== currentId && resolved.has(id));
return { batchToolCallIds: batch, completedCallIds };
}