UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

83 lines (82 loc) • 4.99 kB
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" }); }