@sema-agent/core
Version:
Stateless, task-oriented AI agent core
167 lines (166 loc) • 8.82 kB
JavaScript
import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
import { boundInputHashOf } from "../canonical-json.js";
import { createLiveNotificationLane, DurableAppendObligations, injectionLaneOf, isDelegatedAgentTerminal, isTerminalTaskNotification, renderTaskNotificationXml, routeOrParkNotification, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
import { ToolDetachHub } from "../tool-detach.js";
import { pendingContentAskCallId } from "./decide-continuation.js";
export function runNotificationLane(input) {
const obligations = new DurableAppendObligations();
return obligations.run(() => runNotificationLaneAs(input, obligations));
}
async function runNotificationLaneAs(input, obligations) {
const { spec, queue, resume, internals, taskIdRef, holdSessionLock, runner, self, sessions, sessionReadStates, sessionModeSeats, pendingSessionNotifications, prepareTask, next } = input;
const prepareResume = resume
? {
leafId: resume.cp.leafId,
suspendedBatch: new Set(resume.cp.pendingAction.kind === "tool_approval" ? resume.cp.pendingAction.batchToolCallIds : []),
seed: resume.cp.state,
outcome: resume.outcome,
...(resume.legMods !== undefined ? { legMods: resume.legMods } : {}),
priorSuspendCount: resume.cp.suspendCount,
priorLedger: resume.cp.resourceLedger,
priorHumanReview: resume.cp.humanReview,
workspaceHandle: resume.cp.state.workspaceHandle,
executesApprovedAction: resume.cp.pendingAction.kind === "tool_approval" &&
resume.outcome.gate !== "wake" &&
resume.outcome.decision === "allow",
...(resume.outcome.gate !== "wake" && resume.outcome.decision === "allow" && pendingContentAskCallId(resume.cp) !== undefined
? {
redeemedContentAskCallId: pendingContentAskCallId(resume.cp),
redeemedContentAskQuestionsHash: boundInputHashOf(resume.cp.pendingAction.args?.questions),
}
: {}),
}
: undefined;
const taskNotificationQueue = new SystemInjectionQueue();
const detachHub = internals?.detachHub ?? new ToolDetachHub();
let notificationHarness;
let notificationIdent = () => ({});
let notificationLaneLive = true;
let notificationSessionId;
let descendantAnchorDisclosed = false;
const parkTaskNotification = (payload, priority, onDisposition) => {
laneSeat.handOff(payload);
const ownSession = notificationSessionId;
if (ownSession !== undefined)
routeOrParkNotification(pendingSessionNotifications, ownSession, payload, priority, onDisposition);
else
onDisposition?.("parked");
if (!isDelegatedAgentTerminal(payload))
return;
const uplink = internals?.parentNotify;
if (uplink !== undefined) {
try {
uplink(payload, { priority });
}
catch (e) {
runner.deps.onError?.(e, { phase: "degraded", sessionId: notificationSessionId ?? "", classification: "descendant-terminal-uplink" });
}
return;
}
const rootAnchor = internals?.rootSessionId;
if (rootAnchor !== undefined && rootAnchor !== notificationSessionId) {
routeOrParkNotification(pendingSessionNotifications, rootAnchor, payload, priority);
return;
}
if (internals?.parentSessionId !== undefined && !descendantAnchorDisclosed) {
descendantAnchorDisclosed = true;
runner.deps.onError?.(new Error(`background-agent terminal ${payload.task_id} stayed on this run's own session — the delegation tree's root session is not reachable from this run (no uplink, no root anchor)`), { phase: "degraded", sessionId: notificationSessionId ?? "", classification: "descendant-terminal-unrouted" });
}
};
const unsubscribeTaskNotifications = taskNotificationQueue.subscribe((item) => {
queue.push({ type: "task_notification", notification: item.payload, priority: item.priority, ...notificationIdent() });
if (notificationHarness && prepared.batchHaltRef.current === undefined) {
const xml = renderTaskNotificationXml(item.payload);
const noteOptions = {
provenance: "engine-note",
enginePayload: item.payload,
engineNoteKey: taskNotificationDedupKey(item.payload),
...(item.payload.task_type !== "external" && isTerminalTaskNotification(item.payload) ? { capPreferred: true } : {}),
};
const deliver = injectionLaneOf(item.priority) === "followUp"
? notificationHarness.followUp(xml, noteOptions)
: notificationHarness.steer(xml, { ...noteOptions, ...(item.priority === "now" ? { immediate: true } : {}) });
void deliver.then(() => item.onDisposition?.("queued"), () => {
parkTaskNotification(item.payload, item.priority, item.onDisposition);
});
}
else {
parkTaskNotification(item.payload, item.priority, item.onDisposition);
}
});
const upstreamTaskNotification = internals?.onTaskNotification;
const deliveredAtTurnOpen = new Set();
const laneSeat = createLiveNotificationLane({
queue: taskNotificationQueue,
canTakeNow: () => notificationLaneLive && notificationHarness?.acceptsInjections === true,
isLive: () => notificationLaneLive,
alreadyDelivered: (key) => deliveredAtTurnOpen.has(key),
});
const takeOnThisLane = laneSeat.take;
const takeAsDestination = laneSeat.takeAsDestination;
const injectTaskNotification = (notification, opts) => {
const priority = opts?.priority ?? "later";
return new Promise((resolve) => {
const own = takeOnThisLane(notification, priority, resolve);
if (own === "duplicate")
resolve("dropped_duplicate");
else if (own === "pending")
resolve("queued");
else if (own === "unavailable")
parkTaskNotification(notification, priority, resolve);
});
};
const peerSelfRef = internals?.peerSelfRef ?? createPeerSelfRef(internals?.registryScope ?? spec.principal ?? "default");
const peerInboundChainRef = internals?.peerInboundChainRef ?? createPeerInboundChainRef();
const modelCatalog = runner.deps.models;
const prepared = await prepareTask(spec, runner.deps, sessions, prepareResume, {
...(internals ?? {}),
peerSelfRef,
peerInboundChainRef,
detachHub,
sessionReadStates,
sessionModeSeats,
onTaskNotification: (notification, opts) => {
try {
upstreamTaskNotification?.(notification, opts);
}
catch {
}
return injectTaskNotification(notification, opts);
},
}, self, taskIdRef, holdSessionLock);
pendingSessionNotifications.bindSessionBox(prepared.sessionId, prepared.sessionBox);
const notificationLane = {
get harness() { return notificationHarness; }, set harness(h) { notificationHarness = h; },
get sessionId() { return notificationSessionId; },
set sessionId(s) {
if (notificationSessionId !== undefined)
pendingSessionNotifications.liveLanes.unregister(notificationSessionId, takeAsDestination);
notificationSessionId = s;
if (s !== undefined && notificationLaneLive)
pendingSessionNotifications.liveLanes.register(s, takeAsDestination);
},
get ident() { return notificationIdent; }, set ident(f) { notificationIdent = f; },
get live() { return notificationLaneLive; },
set live(v) {
notificationLaneLive = v;
if (notificationSessionId === undefined)
return;
if (v) {
pendingSessionNotifications.liveLanes.register(notificationSessionId, takeAsDestination);
}
else {
pendingSessionNotifications.liveLanes.unregister(notificationSessionId, takeAsDestination);
laneSeat.release();
}
},
};
try {
return await next({ prepared, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, modelCatalog, notificationLane, unsubscribeTaskNotifications });
}
finally {
if (notificationSessionId !== undefined)
pendingSessionNotifications.liveLanes.unregister(notificationSessionId, takeAsDestination);
await obligations.settle();
}
}