@sema-agent/core
Version:
Stateless, task-oriented AI agent core
86 lines (85 loc) • 5.43 kB
JavaScript
import { createAskUserQuestionTool, isLiveQuestionFace } from "../ask-question.js";
import { boundInputHashOf } from "../canonical-json.js";
import { durableParkOptInDeclared } from "../tool-policy.js";
import { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
import { RosterBuilder } from "../tool-roster.js";
export { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
export function prepareQuestionFace(input) {
const { frozenOnQuestion, spec, deps, checkpointStore, runtimeCaps, resolvedInteractionPosture, resume, sessionId, roster, inheritedUnavailableAsks } = input;
const tools = roster.tools;
const onQuestion = frozenOnQuestion;
const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
const contentAskBindings = new Map();
const questionParkStoreWired = checkpointStore !== undefined;
const questionDurableMandateBinding = runtimeCaps?.forceDurableGate === true && questionParkStoreWired;
const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
mountedQuestionTool !== undefined &&
tools.includes(mountedQuestionTool) &&
!questionDurableMandateBinding &&
!inheritedUnavailableAsks().has(toolCallId);
const lateStrandedAnswers = [];
const discloseStrandedAnswers = (records, why) => {
if (records.length === 0)
return;
try {
deps.onError?.(new Error(`AskUserQuestion: ${records.length} question(s) were answered by a person but the call never ` +
`executed to collect the answer (${records.map((r) => `${r.toolCallId} [delivery ${r.deliveryId}]`).join(", ")}) — ${why}. The answer(s) were NOT ` +
`delivered to the model and are gone with this leg; re-ask if the decision is still needed.`), { phase: "degraded", sessionId, classification: "unconsumed-human-answer" });
}
catch {
}
};
const settleContentAskBindings = () => {
const stranded = [];
for (const [callId, bound] of contentAskBindings) {
if (bound.kind === "answered")
stranded.push({ deliveryId: bound.deliveryId, toolCallId: callId });
}
contentAskBindings.clear();
discloseStrandedAnswers(stranded, "the leg ended first (abort, batch teardown, or a loop failure)");
const byDelivery = new Map();
for (const r of [...stranded, ...lateStrandedAnswers])
byDelivery.set(r.deliveryId, r);
return [...byDelivery.values()];
};
const durableQuestionFace = questionParkStoreWired && durableParkOptInDeclared({ forceDurableGate: runtimeCaps?.forceDurableGate === true, durableApprovalDeclared: spec.durableApproval !== undefined });
const mountedQuestionFace = liveQuestionFace !== undefined
? async (req, signal) => {
const bound = contentAskBindings.get(req.toolCallId);
if (bound !== undefined) {
if (bound.questionsHash === boundInputHashOf(req.questions)) {
contentAskBindings.delete(req.toolCallId);
if (bound.kind === "answered")
return bound.answer;
throw bound.error;
}
contentAskBindings.delete(req.toolCallId);
}
return liveQuestionFace(req, signal);
}
: onQuestion;
const questionToolMounted = spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace));
let mountedQuestionTool;
if (questionToolMounted)
roster.mount((mountedQuestionTool =
createAskUserQuestionTool(mountedQuestionFace, { principal: spec.principal, sourceTaskId: sessionId }, {
...(resume?.redeemedContentAskCallId !== undefined
? {
redeemedApprovalCallId: resume.redeemedContentAskCallId,
...(resume.redeemedContentAskQuestionsHash !== undefined ? { redeemedApprovalQuestionsHash: resume.redeemedContentAskQuestionsHash } : {}),
}
: {}),
...(resolvedInteractionPosture !== undefined ? { posture: resolvedInteractionPosture } : {}),
...(spec.interactiveQuestionFallback === true ? { interactiveFallback: true } : {}),
onSyntheticContinuation: ({ questionId, reason }) => {
deps.onError?.(new Error(`AskUserQuestion ${questionId}: no human answer was obtainable (` +
(reason === "seam_absent"
? "no onQuestion seam is wired"
: reason === "declined_unavailable"
? "the wired question channel reported nobody was reachable"
: "the wired question channel failed") +
`) — the model was instructed to self-answer and the run CONTINUES (warning, not a failure).`), { phase: "degraded", sessionId, classification: "no-human-autoanswered" });
},
})), { source: "builtin", mountedBy: "optional" });
return { checkpointStore, liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, mountedQuestionFace, questionToolMounted, mountedQuestionTool };
}