@sema-agent/core
Version:
Stateless, task-oriented AI agent core
125 lines (124 loc) • 7.29 kB
JavaScript
import { isToolContentOrigin } from "../core/memory-engine/content-origin.js";
import { inlineUntrusted } from "../core/untrusted-text.js";
import { readToolNameList, valueTypeLabel } from "../core/tool-name-list.js";
export const EXTRA_TOOL_NAMES_CODE = "config.extra_tool_names";
export function extraToolNamesError(message, reason) {
const e = new Error(message);
e.code = EXTRA_TOOL_NAMES_CODE;
e.detail = { reason };
return e;
}
const shape = (what) => extraToolNamesError(`createSubagentTool: extraToolNames ${what} — it declares the tools the extraTools factory may inject, each { name, aliases?, contentOrigin? }. Fix the declaration.`, "shape");
function own(entry, key) {
try {
return { ok: true, value: entry[key] };
}
catch {
return { ok: false };
}
}
export function readExtraToolNames(value, hasFactory) {
if (!hasFactory)
throw extraToolNamesError("createSubagentTool: extraToolNames declares the tools an extraTools factory may inject, and this delegation tool has no extraTools factory. Remove the declaration or add the factory.", "shape");
const list = readToolNameList(value);
if (list.kind === "not_a_list")
throw shape(`is a value that is ${list.type}, not a list`);
if (list.kind === "unreadable")
throw shape("is a list whose entries cannot be read (an accessor or a proxy trap threw)");
const out = [];
const owner = new Map();
list.entries.forEach((entry, index) => {
const at = `entry ${index + 1}`;
if (entry === null || typeof entry !== "object" || valueTypeLabel(entry) !== "an object")
throw shape(`${at} is a value that is ${valueTypeLabel(entry)}, not an object`);
const name = own(entry, "name");
if (!name.ok)
throw shape(`${at}: its name cannot be read`);
if (typeof name.value !== "string" || name.value === "")
throw shape(`${at}: its name is ${typeof name.value === "string" ? "empty" : `a value that is ${valueTypeLabel(name.value)}`}, not a tool name`);
const aliasesRead = own(entry, "aliases");
if (!aliasesRead.ok)
throw shape(`${at}: its aliases cannot be read`);
let aliases;
if (aliasesRead.value !== undefined) {
const al = readToolNameList(aliasesRead.value);
if (al.kind !== "entries")
throw shape(`${at}: its aliases are ${al.kind === "not_a_list" ? `a value that is ${al.type}` : "a list that cannot be read"}, not a list of names`);
aliases = [];
for (const a of al.entries) {
if (typeof a !== "string" || a === "")
throw shape(`${at}: an alias is ${typeof a === "string" ? "empty" : `a value that is ${valueTypeLabel(a)}`}, not a tool name`);
aliases.push(a);
}
}
const originRead = own(entry, "contentOrigin");
if (!originRead.ok)
throw shape(`${at}: its contentOrigin cannot be read`);
if (originRead.value !== undefined && !isToolContentOrigin(originRead.value))
throw shape(`${at}: its contentOrigin is not one of "local", "execution", "external"`);
for (const spelling of new Set([name.value, ...(aliases ?? [])])) {
const first = owner.get(spelling);
if (first !== undefined)
throw shape(`entries ${first + 1} and ${index + 1} both answer to ${JSON.stringify(inlineUntrusted(spelling, 80))}`);
owner.set(spelling, index);
}
out.push(Object.freeze({
name: name.value,
...(aliases !== undefined ? { aliases: Object.freeze(aliases) } : {}),
...(originRead.value !== undefined ? { contentOrigin: originRead.value } : {}),
}));
});
return Object.freeze(out);
}
const REACH = { local: 0, execution: 1, external: 2 };
const quoted = (name) => `"${inlineUntrusted(name, 80)}"`;
function rosterPhrase(roster) {
const shown = roster.slice(0, 8).map((e) => quoted(e.name));
return roster.length === 0 ? "none" : shown.join(", ") + (roster.length > shown.length ? `, … (${roster.length - shown.length} more)` : "");
}
export function undeclaredExtraTool(roster, product, toolName) {
const where = `the extraTools factory of delegation tool ${quoted(toolName)}`;
const tail = "the declaration is the upper bound of what the factory may inject. Fix the declaration or the factory.";
const name = product.name;
if (typeof name !== "string")
return { reason: "undeclared_name", sentence: `${where} injected a tool whose name is a value that is ${valueTypeLabel(name)}, which its declared extra tools (extraToolNames: ${rosterPhrase(roster)}) cannot name — ${tail}` };
const entry = roster.find((e) => e.name === name || (e.aliases?.includes(name) ?? false));
if (entry === undefined)
return { reason: "undeclared_name", sentence: `${where} injected tool ${quoted(name)}, which its declared extra tools (extraToolNames: ${rosterPhrase(roster)}) do not name — ${tail}` };
const spellings = new Set([entry.name, ...(entry.aliases ?? [])]);
const aliases = product.aliases;
const aliasList = Array.isArray(aliases) ? aliases : [];
for (const alias of aliasList) {
if (typeof alias !== "string" || !spellings.has(alias)) {
const said = typeof alias === "string" ? quoted(alias) : `a value that is ${valueTypeLabel(alias)}`;
return { reason: "undeclared_name", sentence: `${where} injected tool ${quoted(name)} answering to ${said}, which its declared extra tools (extraToolNames: ${rosterPhrase(roster)}) do not name — ${tail}` };
}
}
const answers = new Set([name, ...aliasList]);
const missing = [...spellings].find((n) => !answers.has(n));
if (missing !== undefined)
return { reason: "undeclared_name", sentence: `${where} injected tool ${quoted(name)}, which does not answer to ${quoted(missing)} as its declared extra tools (extraToolNames: ${rosterPhrase(roster)}) say it does — ${tail}` };
const stated = product.contentOrigin;
const productClass = isToolContentOrigin(stated) ? stated : "external";
const declaredClass = entry.contentOrigin ?? "external";
if (REACH[productClass] > REACH[declaredClass]) {
return { reason: "undeclared_origin", sentence: `${where} injected tool ${quoted(name)} of content class "${productClass}", which its declared extra tools (extraToolNames: ${rosterPhrase(roster)}) state as "${declaredClass}" — ${tail}` };
}
return undefined;
}
export function delegationPoolFace(staticPool, hasFactory, roster) {
if (hasFactory && roster === undefined)
return undefined;
return [...staticPool, ...(roster ?? [])].map((t) => {
const origin = t.contentOrigin;
return { name: t.name, ...(t.aliases !== undefined ? { aliases: t.aliases } : {}), ...(origin !== undefined ? { contentOrigin: origin } : {}) };
});
}
export function firstUndeclaredExtraTool(roster, products, toolName) {
for (const product of products) {
const flaw = undeclaredExtraTool(roster, product, toolName);
if (flaw !== undefined)
return flaw;
}
return undefined;
}