@sema-agent/core
Version:
Stateless, task-oriented AI agent core
189 lines (188 loc) • 11 kB
JavaScript
import { uuidv7 } from "../../internal/harness.js";
import { snapshotActorAssertion } from "../../internal/llm.js";
import { MAX_STEER_INPUT_ID_CHARS } from "../checkpoint-store.js";
import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
import { buildHumanInputEvent, frameMidTurnUserInput, projectHumanInput } from "../human-input-projection.js";
import { isSystemInjectionPriority, steerPriorityOf, SYSTEM_INJECTION_PRIORITIES } from "../task-notification.js";
import { deliverEngineNotice } from "../types.js";
import { inlineUntrusted, sanitizeUntrustedText } from "../untrusted-text.js";
import { announceAcceptance, armAcceptance, nextHumanInputSeq, sameAcceptedSteerInput } from "./steer-admission.js";
import { asThrownError } from "../thrown-value.js";
export function streamSteerVerb(input) {
const { spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner } = input;
let steerChain = Promise.resolve();
const steer = async (text, options) => {
const trusted = options?.trusted ? true : false;
if (trusted && sanitizeUntrustedText(text) !== text) {
throw steeringError("trusted steering text must not contain a </system-reminder> tag", "steering.invalid_content");
}
const inputId = options?.inputId;
if (inputId !== undefined) {
if (typeof inputId !== "string") {
throw steeringError("inputId must be a string when supplied", "steering.invalid_content");
}
if (inputId === "" || inputId.length > MAX_STEER_INPUT_ID_CHARS) {
throw steeringError(`inputId must be a non-empty string of at most ${MAX_STEER_INPUT_ID_CHARS} characters`, "steering.invalid_content");
}
}
const priorityIn = options?.priority;
if (priorityIn !== undefined && !isSystemInjectionPriority(priorityIn)) {
throw steeringError(`priority must be one of ${SYSTEM_INJECTION_PRIORITIES.join("/")} when supplied`, "steering.invalid_content");
}
const priority = steerPriorityOf({ priority: priorityIn });
const actorIn = options?.actor;
const actor = actorIn === undefined ? undefined : snapshotActorAssertion(actorIn);
const projected = projectHumanInput({ text, actor, source: "steer" });
const effectiveInputId = typeof inputId === "string" ? inputId : uuidv7();
const parkRecord = {
text,
trusted,
inputId: effectiveInputId,
...(priorityIn !== undefined ? { priority } : {}),
...(actor !== undefined ? { actor } : {}),
};
let payload;
let mintsAFrame;
let replay;
const publishQueued = (h) => {
const identity = internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId, ...(spec.taskId !== undefined ? { sourceTaskId: spec.taskId } : {}) } : {};
queue.push({
...buildHumanInputEvent({
carrier: "steer",
source: "steer",
delivery: "queued",
sessionSeq: nextHumanInputSeq(h.harness),
inputId: effectiveInputId,
...(actor !== undefined ? { actor } : {}),
...(actor?.issuer !== undefined ? { issuer: actor.issuer } : {}),
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
}),
eventId: uuidv7(),
...identity,
});
return identity;
};
const noteAccepted = () => {
if (!mintsAFrame)
return;
if (typeof inputId === "string")
acceptedSteerInputs.set(inputId, replay);
announceAcceptance(parkRecord);
};
const deliver = async () => {
if (live.resultValue)
throw steeringError("the task has already finished");
const h = live.handle ?? (await orTimeout(ready));
if (!h)
throw steeringError("the task is not running");
payload = trusted ? formatHookFeedback(projected, h.reminderMark) : frameMidTurnUserInput(projected);
mintsAFrame = payload.trim().length !== 0;
replay = { payload, trusted, priority, ...(actor !== undefined ? { actor } : {}) };
if (typeof inputId === "string") {
const prior = acceptedSteerInputs.get(inputId);
if (prior !== undefined) {
if (live.resultValue !== undefined || h.loop.ended)
throw steeringError("the task is no longer running");
if (!sameAcceptedSteerInput(prior, replay)) {
throw steeringError("a different steering instruction was already accepted under this inputId — re-issue this one with a fresh inputId " +
"(an identical payload would have been an idempotent retry)", "steering.duplicate_input_id");
}
return;
}
}
if (live.resultValue !== undefined || h.loop.ended)
throw steeringError("the task is no longer running");
if (mintsAFrame) {
const screen = (spec.hooks ?? runner.deps.hooks)?.userPromptSubmit;
if (screen !== undefined) {
let decision;
try {
const seat = await runHookSeat("userPromptSubmit", { timeoutMs: h.hookTimeoutMs, signal: h.abortController.signal, abortEnds: true }, (sig) => screen(text, { identity: h.hookIdentity, permissionMode: h.permissionMode.observed, signal: sig, source: "steer", inputId: effectiveInputId, ...(actor !== undefined ? { actor: snapshotActorAssertion(actor) } : {}) }));
if (seat.expired) {
if (seat.cause === "timeout") {
try {
runner.deps.onError?.(hookSeatExpiredError("userPromptSubmit", h.hookTimeoutMs, seat.cause, "the steering input was NOT accepted (fail-closed) and the caller was refused typed"), { phase: "hook", sessionId: h.sessionId });
}
catch {
}
}
throw steeringError(seat.cause === "timeout"
? `the deployment's userPromptSubmit hook did not answer within its ${h.hookTimeoutMs}ms bound while screening this steering input; the input was NOT accepted (fail-closed)`
: `the task was cancelled while the deployment's userPromptSubmit hook was still screening this steering input; the input was NOT accepted (fail-closed)`, "steering.blocked_by_hook");
}
decision = seat.value;
}
catch (hookErr) {
if (hookErr instanceof Error && hookErr.code === "steering.blocked_by_hook")
throw hookErr;
const err = asThrownError(hookErr);
try {
runner.deps.onError?.(err, { phase: "hook", sessionId: h.sessionId });
}
catch {
}
throw steeringError(`the deployment's userPromptSubmit hook crashed while screening this steering input (${inlineUntrusted(err.message)}); the input was NOT accepted (fail-closed)`, "steering.blocked_by_hook");
}
if (decision?.block !== undefined && decision.block !== "") {
throw steeringError(`the deployment's userPromptSubmit hook blocked this steering input: ${inlineUntrusted(decision.block)}`, "steering.blocked_by_hook");
}
if (decision?.additionalContext !== undefined && decision.additionalContext !== "") {
payload = `${formatHookFeedback(decision.additionalContext, h.reminderMark)}\n\n${payload}`;
}
}
}
const injectFramed = async () => {
const noteOptions = { provenance: "engine-note", callerAuthored: true, parkRecord, ...(actor !== undefined ? { actor } : {}) };
if (mintsAFrame)
armAcceptance(parkRecord, () => publishQueued(h));
if (priority === "later") {
await h.harness.followUp(payload, noteOptions);
noteAccepted();
return;
}
const frame = await h.harness.steer(payload, { ...noteOptions, ...(priority === "now" ? { immediate: true } : {}) });
noteAccepted();
if (priority === "now" && frame !== undefined && h.harness.interruptTurn(frame)) {
deliverEngineNotice(runner.deps.onNotice, {
code: "task.turn_interrupted",
message: "a caller-provenance steer with priority \"now\" interrupted the running turn: in-flight work was cut at " +
"a manufactured boundary (finished tool calls keep their real results; never-started ones settle as " +
"interrupted) and the run continues with the steer at the queue head.",
detail: {
inputId: effectiveInputId,
sessionId: h.sessionId,
runId: h.runId,
...(actor?.id !== undefined ? { actorId: actor.id } : {}),
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
},
});
}
};
try {
await injectFramed();
return;
}
catch (e) {
if (!(e instanceof Error && e.code === "invalid_state"))
throw e;
}
const birthDeadline = Date.now() + READY_TIMEOUT_MS;
while (live.resultValue === undefined && !h.loop.ended && Date.now() < birthDeadline) {
try {
await injectFramed();
return;
}
catch (e2) {
if (!(e2 instanceof Error && e2.code === "invalid_state"))
throw e2;
}
await new Promise((r) => setTimeout(r, 10));
}
throw steeringError("the task is no longer running");
};
const p = steerChain.then(deliver);
steerChain = p.then(() => undefined, () => undefined);
return p;
};
return { steer };
}