@sema-agent/core
Version:
Stateless, task-oriented AI agent core
137 lines (136 loc) • 5.16 kB
JavaScript
import { createHash } from "node:crypto";
const PRESENTATION_KEYWORDS = new Set(["description", "title", "examples", "$id"]);
const SCHEMA_NAME_MAPS = new Set([
"properties",
"patternProperties",
"definitions",
"$defs",
"dependentSchemas",
]);
function stripPresentation(node) {
if (Array.isArray(node))
return node.map(stripPresentation);
if (node === null || typeof node !== "object")
return node;
const out = {};
for (const [k, v] of Object.entries(node)) {
if (PRESENTATION_KEYWORDS.has(k))
continue;
if (SCHEMA_NAME_MAPS.has(k) && v !== null && typeof v === "object" && !Array.isArray(v)) {
const named = {};
for (const [name, sub] of Object.entries(v))
named[name] = stripPresentation(sub);
out[k] = named;
continue;
}
out[k] = k === "required" && Array.isArray(v) ? [...v].sort() : stripPresentation(v);
}
return out;
}
function canonical(node) {
if (Array.isArray(node))
return `[${node.map(canonical).join(",")}]`;
if (node === null || typeof node !== "object")
return JSON.stringify(node);
const keys = Object.keys(node).sort();
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(node[k])}`).join(",")}}`;
}
function sha(text) {
return createHash("sha256").update(text).digest("hex").slice(0, 16);
}
export function computeShapeDigest(schema) {
return sha(canonical(stripPresentation(schema ?? null)));
}
export function computeWireSchemaDigest(schema, description) {
return sha(`${canonical(schema ?? null)}\u0000${description}`);
}
const TOOL_CONTRACT = Symbol("sema.toolContract");
export function attachToolContract(tool, input) {
const descriptor = {
contractId: input.contractId,
implementationRevision: input.implementationRevision,
wireName: tool.name,
...(input.aliases && input.aliases.length > 0 ? { aliases: input.aliases } : {}),
origin: input.origin,
shapeDigest: computeShapeDigest(tool.parameters),
wireSchemaDigest: computeWireSchemaDigest(tool.parameters, tool.description ?? ""),
cardId: `builtin:${tool.name}`,
};
tool[TOOL_CONTRACT] = descriptor;
}
export function getToolContract(tool, fallbackOrigin = "caller") {
const attached = tool[TOOL_CONTRACT];
if (attached) {
const mountedShape = computeShapeDigest(tool.parameters);
if (mountedShape !== attached.shapeDigest) {
return {
contractId: `legacy:${mountedShape}`,
implementationRevision: "unversioned",
wireName: tool.name,
...(tool.aliases && tool.aliases.length > 0 ? { aliases: tool.aliases } : {}),
origin: attached.origin,
shapeDigest: mountedShape,
wireSchemaDigest: computeWireSchemaDigest(tool.parameters, tool.description ?? ""),
cardId: `legacy:${tool.name}`,
};
}
return {
...attached,
wireName: tool.name,
shapeDigest: mountedShape,
wireSchemaDigest: computeWireSchemaDigest(tool.parameters, tool.description ?? ""),
};
}
const shapeDigest = computeShapeDigest(tool.parameters);
return {
contractId: `legacy:${shapeDigest}`,
implementationRevision: "unversioned",
wireName: tool.name,
...(tool.aliases && tool.aliases.length > 0 ? { aliases: tool.aliases } : {}),
origin: fallbackOrigin,
shapeDigest,
wireSchemaDigest: computeWireSchemaDigest(tool.parameters, tool.description ?? ""),
cardId: `legacy:${tool.name}`,
};
}
export function hasToolContract(tool) {
return tool[TOOL_CONTRACT] !== undefined;
}
export function projectToolManifest(tools, originOf) {
return tools.map((t) => {
const d = getToolContract(t, originOf?.(t) ?? "caller");
return {
wireName: d.wireName,
aliases: d.aliases ?? [],
contractId: d.contractId,
implementationRevision: d.implementationRevision,
cardId: d.cardId,
shapeDigest: d.shapeDigest,
wireSchemaDigest: d.wireSchemaDigest,
};
});
}
export function auditToolCollisions(tools) {
const canonical = new Map();
const aliasOwner = new Map();
const yields = [];
const aliasCollisions = [];
for (const t of tools) {
if (canonical.has(t.name))
yields.push({ name: t.name });
canonical.set(t.name, t);
}
for (const t of tools) {
for (const a of t.aliases ?? []) {
if (canonical.has(a) && canonical.get(a) !== t) {
aliasCollisions.push({ alias: a, owners: [canonical.get(a).name, t.name] });
}
const prior = aliasOwner.get(a);
if (prior !== undefined && prior !== t.name) {
aliasCollisions.push({ alias: a, owners: [prior, t.name] });
}
aliasOwner.set(a, t.name);
}
}
return { yields, aliasCollisions };
}