@sema-agent/core
Version:
Stateless, task-oriented AI agent core
135 lines (134 loc) • 7.57 kB
JavaScript
import { createPreToolUseScreening, formatHookFeedback } from "../hooks.js";
import { classicEventBase, dispatchClassic, stringMembersAnswer, stringMembersOf } from "../mods/stations.js";
import { policyCannotAnswer } from "../real-approval.js";
import { rosterToolName } from "../tool-roster.js";
import { wireOf } from "../tool-name-readings.js";
import { noteHostRewrite } from "../frame-settlement.js";
function postToolUseAnswer(answer) {
const strings = stringMembersAnswer("classic.PostToolUse", ["additionalContext"])(answer);
if (strings !== undefined || typeof answer !== "object" || answer === null)
return strings;
const out = Reflect.get(answer, "updatedOutput");
return out === undefined || Array.isArray(out) ? undefined : "answered a non-array \"updatedOutput\"; it replaces the content array";
}
function updatedOutputOf(answer) {
if (typeof answer !== "object" || answer === null)
return undefined;
const out = Reflect.get(answer, "updatedOutput");
return Array.isArray(out) ? out.filter(isContentBlock) : undefined;
}
function isContentBlock(c) {
return typeof c === "object" && c !== null && typeof Reflect.get(c, "type") === "string";
}
export function createToolResultStation(input) {
const { modBus, blockedToolCalls, toolRosterDeltas, preToolContexts, abortController, spec, handsCwdRef, hookEnvFace, hookIdentity, permissionMode, reminderMark, frameSettlement } = input;
const toolResultStationArmed = modBus.hasHandlers("classic.PostToolUse") || modBus.hasHandlers("classic.PreToolUse") || modBus.hasHandlers("classic.PostToolUseFailure");
if (toolResultStationArmed) {
return async (e) => {
if (blockedToolCalls.delete(e.toolCallId)) {
return undefined;
}
let content = e.content;
let changed = false;
const post = { toolName: rosterToolName(toolRosterDeltas.current, e.toolName) };
const callFacts = (tool) => ({
...classicEventBase(hookIdentity, permissionMode.observed),
tool,
tool_use_id: e.toolCallId,
input: e.input,
...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}),
});
if (e.isError) {
if (modBus.hasHandlers("classic.PostToolUseFailure")) {
let clonedDetails = e.details;
try {
clonedDetails = structuredClone(e.details);
}
catch {
}
const failure = {
error: e.content
.filter((c) => c.type === "text")
.map((c) => c.text)
.join("\n"),
isInterrupt: abortController.signal.aborted || spec.signal?.aborted === true,
content: e.content.map((c) => ({ ...c })),
details: clonedDetails,
};
const outcome = await dispatchClassic(modBus, "classic.PostToolUseFailure", { ...callFacts(wireOf("hook.postToolUseFailure", post)), failure }, { signal: abortController.signal, checkAnswer: stringMembersAnswer("classic.PostToolUseFailure", ["additionalContext"]) }).catch(() => undefined);
const patch = outcome?.kind === "answered" ? stringMembersOf(outcome.answer, ["additionalContext"]) : undefined;
if (patch?.additionalContext) {
content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext, reminderMark) }];
changed = true;
}
}
}
else if (modBus.hasHandlers("classic.PostToolUse")) {
const outcome = await dispatchClassic(modBus, "classic.PostToolUse", { ...callFacts(wireOf("hook.postToolUse", post)), output: { content: e.content, details: e.details, isError: e.isError } }, { signal: abortController.signal, checkAnswer: postToolUseAnswer }).catch(() => undefined);
const answer = outcome?.kind === "answered" ? outcome.answer : undefined;
const updatedOutput = updatedOutputOf(answer);
const patch = stringMembersOf(answer, ["additionalContext"]);
if (updatedOutput) {
content = updatedOutput;
changed = true;
noteHostRewrite(frameSettlement, e.toolCallId);
}
if (patch.additionalContext) {
content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext, reminderMark) }];
changed = true;
}
}
const pre = preToolContexts.get(e.toolCallId);
if (pre) {
preToolContexts.delete(e.toolCallId);
content = [...content, ...pre.map((t) => ({ type: "text", text: formatHookFeedback(t, reminderMark) }))];
changed = true;
}
return changed ? { content } : undefined;
};
}
return undefined;
}
export function createApprovedCallScreen(input) {
const { modBus, permissionMode, abortController, handsCwdRef, hookEnvFace, hookIdentity, preToolContexts } = input;
if (!modBus.hasHandlers("classic.PreToolUse"))
return undefined;
return async ({ toolCallId, toolName, args, decidedBy }) => {
const screening = createPreToolUseScreening({
event: { toolCallId, toolName, input: {} },
modBus,
observedPermissionMode: () => permissionMode.observed,
...(handsCwdRef !== undefined ? { trackedCwd: () => handsCwdRef.current } : {}),
...(hookEnvFace !== undefined ? { hookEnv: hookEnvFace } : {}),
identity: hookIdentity,
abortSignal: abortController.signal,
});
if (screening === undefined)
return { args, admit: () => undefined };
const subject = `this call to "${toolName}"`;
const screened = await screening.screen(args, subject);
if (screened.kind === "refused")
return { refused: screened.reason };
const r = screened.answer;
if (r?.action === "deny")
return { refused: screening.deniedReason(r) };
if (r !== undefined && policyCannotAnswer(r, decidedBy))
return { refused: `a PreToolUse hook requires a person's approval for ${subject}, and this approval was the deployment's policy — not executed` };
const context = r?.additionalContext;
return { args: r?.updatedInput !== undefined ? r.updatedInput : args, admit: () => {
if (context)
preToolContexts.set(toolCallId, [context]);
} };
};
}
export function chainCannotJudge(ig) {
if (ig?.requiresParentConstraint !== true)
return undefined;
const chain = ig.constraintChain;
if (chain === undefined)
return "the checkpoint predates the frozen projection chain (no constraintChain recorded)";
if (chain.some((e) => "opaque" in e))
return "the inherited chain carries an OPAQUE layer (no projection exported)";
return chain.some((e) => !("opaque" in e) && e.requiresLiveRemainder) ? "an inherited chain layer declares a live remainder beyond its projection" : undefined;
}