@sema-agent/core
Version:
Stateless, task-oriented AI agent core
145 lines (144 loc) • 6.2 kB
JavaScript
import { boundedRedactedSummary } from "./untrusted-egress.js";
import { delimitUntrusted } from "./untrusted-text.js";
import { thrownText } from "./thrown-value.js";
import { assertOwnership, canAccessWorkflowRun, formatWorkflowRun, sleepPollStep, } from "./task-registry-shared.js";
export function registerWorkflowLane(core, input) {
assertOwnership(input, "registerWorkflow");
const id = input.runId;
if (core.handles.has(id))
throw new Error(`TaskRegistry: task id already registered: ${id}`);
const now = input.now ?? Date.now();
const originatingSessionId = input.originatingSessionId;
const handle = {
id,
type: "workflow",
status: "running",
...(input.description !== undefined ? { description: input.description } : {}),
...(input.owner !== undefined ? { owner: input.owner } : {}),
...(input.scope !== undefined ? { scope: input.scope } : {}),
...(input.toolUseId !== undefined ? { toolUseId: input.toolUseId } : {}),
createdAt: now,
updatedAt: now,
runId: id,
handle: input.handle,
...(input.store !== undefined ? { store: input.store } : {}),
...(originatingSessionId !== undefined ? { originatingSessionId, sessionScoped: true } : {}),
...(input.onServedTerminal !== undefined ? { onServedTerminal: input.onServedTerminal } : {}),
};
core.handles.set(id, handle);
void input.handle.done.then((res) => {
handle.status = "completed";
const run = res?.run;
const r = run?.resultFull ?? run?.result;
if (typeof r === "string")
handle.result = r;
if (run?.completionId !== undefined)
handle.completionId = run.completionId;
handle.updatedAt = Date.now();
}, (err) => {
if (handle.status !== "cancelled")
handle.status = "failed";
handle.error = boundedRedactedSummary(thrownText(err), 500);
const rejectedRun = err !== null && (typeof err === "object" || typeof err === "function")
? err.workflowRun
: undefined;
if (rejectedRun?.completionId !== undefined)
handle.completionId = rejectedRun.completionId;
handle.updatedAt = Date.now();
});
return id;
}
function fireServedTerminalLane(handle) {
const cb = handle.onServedTerminal;
if (!cb)
return;
handle.onServedTerminal = undefined;
try {
cb();
}
catch {
}
}
export async function pollWorkflowLane(handle, access, deadline, signal) {
const store = handle.store;
if (store) {
for (;;) {
const run = await store.get(handle.runId);
if (!run || !canAccessWorkflowRun(run, access))
break;
if (run.status !== "running" || deadline === undefined || Date.now() >= deadline || signal?.aborted) {
const out = formatWorkflowRun(run);
if (run.status === "running" && deadline !== undefined && Date.now() >= deadline)
out.details.retrieval_status = "timeout";
else if (run.status !== "running")
fireServedTerminalLane(handle);
return out;
}
await sleepPollStep(deadline, signal);
}
}
while (handle.status === "running" && deadline !== undefined && Date.now() < deadline && !signal?.aborted) {
await sleepPollStep(deadline, signal);
}
if (handle.status !== "running")
fireServedTerminalLane(handle);
return {
content: JSON.stringify({
task_id: handle.id,
runId: handle.id,
type: "workflow",
status: handle.status,
...(handle.status === "completed" && handle.result !== undefined
? { result: delimitUntrusted("workflow result", handle.result) }
: {}),
...(handle.status === "failed" && handle.error !== undefined
? { error: delimitUntrusted("workflow error", handle.error) }
: {}),
note: handle.status === "running"
? "still running — poll again shortly."
: handle.status === "completed"
? handle.result !== undefined
? "completed — `result` above is the workflow's return value."
: "completed — its result was delivered on the completion notification."
: handle.status === "failed" && handle.error !== undefined
? "failed — see error."
: "workflow reached a terminal state.",
}),
details: {
task_id: handle.id,
type: "workflow",
status: handle.status,
retrieval_status: handle.status === "running" && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success",
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
},
};
}
export async function stopWorkflowLane(handle) {
if (handle.status !== "running") {
return {
content: JSON.stringify({
task_id: handle.id,
runId: handle.id,
type: "workflow",
status: handle.status,
stopped: false,
note: `workflow already ${handle.status} — nothing to stop; poll TaskOutput for its final state.`,
}),
details: { task_id: handle.id, type: "workflow", status: handle.status, retrieval_status: "success" },
};
}
handle.status = "cancelled";
handle.updatedAt = Date.now();
handle.handle.cancel("TaskStop requested");
return {
content: JSON.stringify({
task_id: handle.id,
runId: handle.id,
type: "workflow",
status: "cancelled",
stopped: true,
note: "workflow cancellation requested; durable run status maps cancellation to failed when it finalizes.",
}),
details: { task_id: handle.id, type: "workflow", status: "cancelled", retrieval_status: "success" },
};
}