@sema-agent/core
Version:
Stateless, task-oriented AI agent core
83 lines (82 loc) • 4.99 kB
JavaScript
import { bandOf } from "../prompt-assembly/bands.js";
import { lockedTextIn, receivePromptData } from "../prompt-assembly/overlay.js";
import { inlineUntrusted } from "./untrusted-text.js";
import { egressThrownText, extensionWords } from "./untrusted-egress.js";
export const DISPOSABLE_PROMPT_SECTIONS = Object.freeze(["core/discovery.mcp-instructions"]);
const DEFECT_MAX = 600;
function readAnswer(answer, seat) {
if (typeof answer !== "object" || answer === null || Array.isArray(answer))
return `answered ${answer === null ? "null" : Array.isArray(answer) ? "an array" : typeof answer}; prompt.section takes { text: string | null }`;
const received = receivePromptData(answer);
if ("notData" in received)
return `a prompt.section answer is data (plain objects, arrays, strings, booleans); it holds ${received.notData}`;
const data = received.value;
const keys = Object.keys(data);
const stray = keys.filter((k) => k !== "text");
if (stray.length > 0)
return `the answer carries ${stray.slice(0, 5).map((k) => JSON.stringify(extensionWords(k, 40))).join(", ")} (text only)`;
if (!keys.includes("text"))
return "the answer has no text (a string to place, null to leave the entry out)";
const text = data["text"];
const op = text === null ? "drop" : "replace";
if (text !== null && (typeof text !== "string" || text.trim() === ""))
return `text is ${typeof text === "string" ? "blank" : typeof text}; it is a non-blank string or null`;
const source = seat === "deployment" ? "deploy" : "task";
if (!bandOf("discovery").ops[source].includes(op))
return `a ${source} answer may not ${op} a discovery entry`;
if (typeof text === "string") {
const hits = lockedTextIn(text);
if (hits.length > 0)
return `the text contains, verbatim, the locked text of ${[...new Set(hits.map((h) => h.split("#")[0]))].join(", ")}`;
}
return { text };
}
export function sectionDisposerOf(bus, signal) {
if (bus === undefined || !bus.hasHandlers("prompt.section"))
return undefined;
return async (entry) => {
const computed = { text: entry.text, author: "server" };
const where = `prompt section "${entry.section}" entry "${inlineUntrusted(entry.server, 160)}"`;
const e = { name: entry.section, key: entry.server, mountedTools: Object.freeze([...entry.mountedTools]), text: entry.text };
const bottom = Object.freeze({ text: entry.text });
const taken = new WeakMap([[bottom, entry.text]]);
let outcome;
try {
outcome = await bus.dispatch("prompt.section", e, {
bottom: () => bottom,
...(signal !== undefined ? { signal } : {}),
checkAnswer: (a, _e, by) => {
if (typeof a === "object" && a !== null && taken.has(a))
return undefined;
const read = readAnswer(a, by.seat);
if (typeof read === "string")
return read;
taken.set(a, read.text);
return undefined;
},
});
}
catch (err) {
return { kept: computed, defect: inlineUntrusted(`${where}: the station could not be raised (${inlineUntrusted(egressThrownText("same-principal", err), 200)}) — the entry is included as the engine computed it`, DEFECT_MAX) };
}
if (outcome.kind === "aborted")
return { kept: computed };
const skipped = outcome.skipped.map((s) => `extension "${extensionWords(s.registration, 80)}" ${s.cause === "violation" ? "gave an answer the station cannot take" : s.cause}: ${extensionWords(s.reason, 200)}`);
const defect = skipped.length > 0 ? inlineUntrusted(`${where}: ${skipped.join("; ")} — that answer is skipped; what is below it stands`, DEFECT_MAX) : undefined;
if (outcome.kind === "failed")
return { kept: computed, ...(defect !== undefined ? { defect } : {}) };
const answer = outcome.answer;
const text = typeof answer === "object" && answer !== null ? taken.get(answer) : undefined;
if (text === undefined) {
return { kept: computed, defect: inlineUntrusted(`${where}: the chain's answer is not one the station took — the entry is included as the engine computed it`, DEFECT_MAX) };
}
const disposed = text === null ? {} : typeof text === "string" && text !== entry.text ? { kept: { text, author: "host" } } : { kept: computed };
return { ...disposed, ...(defect !== undefined ? { defect } : {}) };
};
}
export function disposeSectionEntry(disposer, entry) {
return disposer === undefined ? Promise.resolve({ kept: { text: entry.text, author: "server" } }) : disposer(entry);
}
export function sectionAnswerWarning(defect) {
return Object.assign(new Error(defect), { code: "prompt.section_answer_invalid" });
}