@sema-agent/core
Version:
Stateless, task-oriented AI agent core
632 lines (631 loc) • 29.3 kB
JavaScript
import { createHash } from "node:crypto";
import { Type } from "typebox";
import { getToolContract } from "../prompt-assembly/tool-catalog.js";
import { protocolOf } from "./protocol-table.js";
import { describeToolCatalog } from "./tool-registry.js";
import { RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_CHARS, RENDER_HINT_MAX_LIST, RESERVED_APPROVAL_CARDS, isToolApprovalCard, TOOL_APPROVAL_CARDS, TOOL_CARD_ID_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_FAMILIES, TOOL_KEYS_MAX, TOOL_KEY_MAX_CHARS, TOOL_MOUNT_TAGS, TOOL_WIRE_NAME_MAX_CHARS, TOOL_PATH_ABSENCES, TOOL_PATH_BASES, } from "./tool-face.js";
import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-question.js";
import { PRESENT_PLAN_TOOL_NAME } from "./present-plan-tool.js";
import { wireOf } from "./tool-name-readings.js";
function lit(values) {
return Type.Union(values.map((v) => Type.Literal(v)));
}
const Name = Type.String({ minLength: 1, maxLength: TOOL_WIRE_NAME_MAX_CHARS });
const Short = Type.String({ maxLength: TOOL_CONTRACT_MAX_CHARS });
const Key = Type.String({ maxLength: TOOL_KEY_MAX_CHARS });
const Digest16 = Type.String({ minLength: 16, maxLength: 16 });
export const ROSTER_SOURCES = Object.freeze(["builtin", "caller", "host", "mcp", "a2a", "external", "synthetic"]);
export const ToolRosterRenderHints = Type.Object({
userFacingName: Type.Optional(Type.String({ maxLength: RENDER_HINT_MAX_CHARS })),
activity: Type.Optional(Type.String({ maxLength: RENDER_HINT_ACTIVITY_MAX_CHARS })),
summaryParams: Type.Optional(Type.Array(Key, { maxItems: RENDER_HINT_MAX_LIST })),
resultCards: Type.Optional(Type.Array(Key, { maxItems: RENDER_HINT_MAX_LIST })),
approvalCard: Type.Optional(lit(TOOL_APPROVAL_CARDS)),
}, { additionalProperties: false });
export const ToolRosterEntry = Type.Object({
name: Name,
aliases: Type.Array(Name, { uniqueItems: true }),
source: lit(ROSTER_SOURCES),
origin: Type.Optional(Type.Object({ peer: Name, declaredBy: Type.Optional(lit(["center", "file", "request"])) }, { additionalProperties: false })),
contract: Type.Object({ contractId: Short, implementationRevision: Short }, { additionalProperties: false }),
shapeDigest: Digest16,
wireSchemaDigest: Digest16,
cardId: Type.String({ maxLength: TOOL_CARD_ID_MAX_CHARS }),
capabilityId: Digest16,
inputKeys: Type.Array(Type.Object({ name: Key, type: lit(["string", "number", "integer", "boolean", "object", "array", "null", "mixed"]) }, { additionalProperties: false }), { maxItems: TOOL_KEYS_MAX }),
effect: lit(["read", "write", "idempotent"]),
effectProvenance: lit(["declared", "caller", "synthetic", "default"]),
offload: Type.Boolean(),
offloadThresholdChars: Type.Optional(Type.Integer({ minimum: 0 })),
compactable: Type.Boolean(),
budgetExempt: Type.Boolean(),
egress: Type.Boolean(),
egressProvenance: lit(["declared", "caller", "default"]),
irreversibility: Type.Optional(lit(["never", "maybe", "always"])),
reversibilityProbe: Type.Boolean(),
contentOrigin: lit(["local", "execution", "external"]),
contentOriginProvenance: lit(["declared", "server", "exempted", "default"]),
family: lit(TOOL_FAMILIES),
pathTarget: Type.Optional(Type.Object({
param: Key,
aliases: Type.Array(Key),
access: lit(["read", "create", "edit"]),
skillScopeEligible: Type.Boolean(),
base: Type.Optional(lit(TOOL_PATH_BASES)),
absent: Type.Optional(lit(TOOL_PATH_ABSENCES)),
patternParam: Type.Optional(Key),
}, { additionalProperties: false })),
ruleFace: Type.Object({ primaryParams: Type.Array(Key), params: Type.Array(Key) }, { additionalProperties: false }),
approvalPreview: Type.Boolean(),
validateInput: Type.Boolean(),
deferred: Type.Boolean(),
alwaysLoad: Type.Boolean(),
modelGate: Type.Optional(Short),
renderHints: Type.Optional(ToolRosterRenderHints),
mountedBy: lit(TOOL_MOUNT_TAGS),
}, { additionalProperties: false });
export const ToolRoster = Type.Object({
schemaVersion: Type.Literal(1),
count: Type.Integer({ minimum: 0 }),
entries: Type.Array(ToolRosterEntry, { maxItems: 2048 }),
digest: Digest16,
}, { additionalProperties: false });
export const ToolRosterDelta = Type.Object({
fromDigest: Digest16,
roster: ToolRoster,
summary: Type.Object({ added: Type.Array(Name), removed: Type.Array(Name), changed: Type.Array(Name) }, { additionalProperties: false }),
}, { additionalProperties: false });
export function rosterEntryNamed(roster, toolName) {
return roster.entries.find((r) => r.name === toolName) ?? roster.entries.find((r) => r.aliases.includes(toolName));
}
export function rosterToolName(roster, name) {
return (roster === undefined ? undefined : rosterEntryNamed(roster, name)?.name) ?? name;
}
function namesResolvingTo(roster, e) {
return [e.name, ...e.aliases.filter((a) => a !== e.name && rosterToolName(roster, a) === e.name)];
}
export { ruleNamesOf } from "./tool-name-readings.js";
const RESERVED_CARD_OWNER = {
question: () => ASK_USER_QUESTION_TOOL_NAME,
plan: () => PRESENT_PLAN_TOOL_NAME,
};
export function engineCardOf(call) {
const wire = wireOf("card.reserved", call);
return RESERVED_APPROVAL_CARDS.find((card) => RESERVED_CARD_OWNER[card]() === wire);
}
export function approvalCardOf(call, declared) {
return engineCardOf(call) ?? (declared !== undefined && !RESERVED_APPROVAL_CARDS.includes(declared) ? declared : "generic");
}
export function approvalCardMemberOf(call) {
const card = call.face?.approvalCard;
return card !== undefined ? { approvalCard: card } : {};
}
export function toolCallFaceOf(roster, toolName) {
if (roster === undefined)
return undefined;
const e = rosterEntryNamed(roster, toolName);
if (e === undefined)
return undefined;
const resolving = namesResolvingTo(roster, e);
return {
names: resolving.includes(toolName) ? [toolName, ...resolving.filter((n) => n !== toolName)] : resolving,
family: e.family,
...(e.pathTarget !== undefined ? { pathTarget: e.pathTarget } : {}),
capabilityId: e.capabilityId,
contractId: e.contract.contractId,
shapeDigest: e.shapeDigest,
implementationRevision: e.contract.implementationRevision,
approvalCard: approvalCardOf({ toolName: e.name }, e.renderHints?.approvalCard),
};
}
export function parkedToolIdentity(roster, toolName) {
if (roster === undefined)
return {};
const e = rosterEntryNamed(roster, toolName);
if (e === undefined)
return {};
return { capabilityId: e.capabilityId, contractId: e.contract.contractId, shapeDigest: e.shapeDigest, implementationRevision: e.contract.implementationRevision, rosterDigest: roster.digest };
}
export function parkedToolFace(roster, req) {
if (roster === undefined)
return {};
const e = rosterEntryNamed(roster, req.toolName);
if (e === undefined)
return {};
const judged = req.face?.names;
return { family: e.family, ...(e.pathTarget !== undefined ? { pathTarget: e.pathTarget } : {}), names: judged !== undefined && judged.includes(req.toolName) ? [...judged] : namesResolvingTo(roster, e) };
}
export function parkedRowLiveFace(roster, row) {
const spelled = row.names?.[0];
const under = spelled !== undefined && roster !== undefined && rosterToolName(roster, spelled) === row.toolName ? spelled : row.toolName;
return toolCallFaceOf(roster, under);
}
export function parkedCallFaceOf(pending) {
if (pending.family === undefined || pending.capabilityId === undefined || pending.contractId === undefined || pending.shapeDigest === undefined || pending.implementationRevision === undefined)
return undefined;
return {
...(pending.names !== undefined ? { names: pending.names } : {}),
family: pending.family,
...(pending.pathTarget !== undefined ? { pathTarget: pending.pathTarget } : {}),
capabilityId: pending.capabilityId,
contractId: pending.contractId,
shapeDigest: pending.shapeDigest,
implementationRevision: pending.implementationRevision,
};
}
export function callFaceSeat(face) {
return face !== undefined ? { face } : {};
}
export function toolIdentityOf(entry) {
return { capabilityId: entry.capabilityId, contractId: entry.contract.contractId, shapeDigest: entry.shapeDigest, implementationRevision: entry.contract.implementationRevision };
}
function canonical(node) {
if (Array.isArray(node))
return `[${node.map(canonical).join(",")}]`;
if (node === null || typeof node !== "object")
return JSON.stringify(node) ?? "null";
const keys = Object.keys(node).sort();
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(node[k])}`).join(",")}}`;
}
function sha16(text) {
return createHash("sha256").update(text).digest("hex").slice(0, 16);
}
export function mintCapabilityId(t) {
return sha16(canonical({ source: t.source, peer: t.peer, name: t.name, contractId: t.contractId }));
}
export function rosterDigestOf(entries) {
return sha16(canonical(entries));
}
const SCALAR_TYPES = new Set(["string", "number", "integer", "boolean"]);
function schemaTypeOf(node) {
if (node === null || typeof node !== "object")
return { type: "mixed", scalar: false };
const s = node;
const branches = s["anyOf"] ?? s["oneOf"];
if (Array.isArray(branches) && branches.length > 0) {
const parts = branches.map(schemaTypeOf);
const scalar = parts.every((p) => p.scalar);
const first = parts[0].type;
return parts.every((p) => p.type === first) ? { type: first, scalar } : { type: "mixed", scalar };
}
if (s["const"] !== undefined) {
const t = typeof s["const"];
return t === "string" || t === "number" || t === "boolean" ? { type: t, scalar: true } : { type: "mixed", scalar: false };
}
const en = s["enum"];
if (Array.isArray(en) && en.length > 0) {
const kinds = new Set(en.map((v) => typeof v));
if (kinds.size === 1) {
const t = [...kinds][0];
if (t === "string" || t === "number" || t === "boolean")
return { type: t, scalar: true };
}
return { type: "mixed", scalar: [...kinds].every((k) => k === "string" || k === "number" || k === "boolean") };
}
const t = s["type"];
if (typeof t === "string") {
if (SCALAR_TYPES.has(t))
return { type: t, scalar: true };
if (t === "object" || t === "array" || t === "null")
return { type: t, scalar: false };
}
if (Array.isArray(t))
return { type: "mixed", scalar: t.every((x) => typeof x === "string" && SCALAR_TYPES.has(x)) };
if (s["properties"] !== undefined)
return { type: "object", scalar: false };
if (s["items"] !== undefined)
return { type: "array", scalar: false };
return { type: "mixed", scalar: false };
}
export function inputKeysOf(schema) {
const keys = [];
const scalar = new Set();
const props = schema?.properties;
if (props !== null && typeof props === "object" && !Array.isArray(props)) {
for (const [name, sub] of Object.entries(props)) {
const t = schemaTypeOf(sub);
keys.push({ name, type: t.type });
if (t.scalar)
scalar.add(name);
}
}
return { keys, scalar };
}
export function schemaKeyBoundProblem(schema) {
const props = schema?.properties;
if (props === null || typeof props !== "object" || Array.isArray(props))
return undefined;
const names = Object.keys(props);
if (names.length > TOOL_KEYS_MAX)
return `the schema declares ${names.length} top-level properties; a roster row carries at most ${TOOL_KEYS_MAX}`;
const long = names.find((n) => n.length > TOOL_KEY_MAX_CHARS);
if (long !== undefined)
return `the schema property ${JSON.stringify(long.slice(0, 40) + "…")} is ${long.length} characters; a roster key is at most ${TOOL_KEY_MAX_CHARS}`;
return undefined;
}
export function rosterMemberBoundProblem(m) {
if (typeof m.name !== "string")
return { code: "config.tool_roster_bound", message: `tool has no string name (got ${m.name === null ? "null" : typeof m.name}); every roster row is keyed by a string \`name\`` };
const over = (label, value, bound) => `${label} ${JSON.stringify(value.slice(0, 40) + "…")} is ${value.length} characters; the roster's bound is ${bound}`;
const of = (text) => `tool ${JSON.stringify(m.name.slice(0, 40))}: ${text}`;
if (m.name.length > TOOL_WIRE_NAME_MAX_CHARS)
return { code: "config.tool_name_too_long", message: over("tool name", m.name, TOOL_WIRE_NAME_MAX_CHARS) };
const alias = m.aliases?.find((a) => a.length > TOOL_WIRE_NAME_MAX_CHARS);
if (alias !== undefined)
return { code: "config.tool_name_too_long", message: of(over("alias", alias, TOOL_WIRE_NAME_MAX_CHARS)) };
if (m.peer !== undefined && m.peer.length > TOOL_WIRE_NAME_MAX_CHARS)
return { code: "config.tool_name_too_long", message: of(over("peer", m.peer, TOOL_WIRE_NAME_MAX_CHARS)) };
for (const [label, v] of [["contractId", m.contractId], ["implementationRevision", m.implementationRevision]]) {
if (v !== undefined && v.length > TOOL_CONTRACT_MAX_CHARS)
return { code: "config.tool_contract_too_long", message: of(over(`contract ${label}`, v, TOOL_CONTRACT_MAX_CHARS)) };
}
if (m.modelGate !== undefined && m.modelGate.length > TOOL_CONTRACT_MAX_CHARS)
return { code: "config.tool_roster_bound", message: of(over("modelGate", m.modelGate, TOOL_CONTRACT_MAX_CHARS)) };
if (m.cardId !== undefined && m.cardId.length > TOOL_CARD_ID_MAX_CHARS)
return { code: "config.tool_roster_bound", message: of(over("cardId", m.cardId, TOOL_CARD_ID_MAX_CHARS)) };
return undefined;
}
export function ruleFaceOfInstance(schema, declared, pathTarget) {
const { scalar } = inputKeysOf(schema);
const derived = new Set(scalar);
if (pathTarget !== undefined) {
derived.add(pathTarget.param);
for (const a of pathTarget.aliases ?? [])
derived.add(a);
}
const primary = declared?.primaryParams !== undefined ? [...declared.primaryParams] : pathTarget !== undefined ? [pathTarget.param, ...(pathTarget.aliases ?? [])] : [];
for (const p of primary)
derived.add(p);
const params = declared?.params !== undefined ? declared.params.filter((p) => derived.has(p)) : [...derived];
const ordered = [...new Set([...params, ...primary.filter((p) => params.includes(p) || declared?.params === undefined)])];
return { primaryParams: primary, params: ordered };
}
function capRenderHints(h) {
const out = {};
if (typeof h.userFacingName === "string")
out.userFacingName = h.userFacingName;
if (typeof h.activity === "string")
out.activity = h.activity;
if (Array.isArray(h.summaryParams))
out.summaryParams = h.summaryParams.filter((s) => typeof s === "string");
if (Array.isArray(h.resultCards))
out.resultCards = h.resultCards.filter((s) => typeof s === "string");
if (isToolApprovalCard(h.approvalCard))
out.approvalCard = h.approvalCard;
return Object.keys(out).length > 0 ? out : undefined;
}
export function survivorsByName(mounted) {
const winner = new Map();
for (const t of mounted)
winner.set(t.name, t);
return winner;
}
export const faceOf = (t) => t;
function configError(code, message) {
const e = new Error(message);
e.code = code;
return e;
}
export class RosterBuilder {
#tools = [];
#engineLane = [];
#meta = new WeakMap();
#effects;
constructor(effects) {
this.#effects = effects;
}
get tools() {
return this.#tools;
}
get length() {
return this.#tools.length;
}
metaOf(tool) {
return this.#meta.get(tool);
}
mount(tool, meta) {
this.#admit(tool, meta);
this.#tools.push(tool);
}
mountEngineLane(tool, meta) {
this.#admit(tool, meta);
this.#engineLane.push(tool);
}
#admit(tool, meta) {
if ((meta.source === "mcp" || meta.source === "a2a" || meta.source === "external") !== (meta.origin !== undefined)) {
throw configError("config.tool_roster_origin", `tool ${JSON.stringify(tool.name)}: a ${meta.source} mount ${meta.origin === undefined ? "requires" : "must not carry"} an origin`);
}
if (!TOOL_MOUNT_TAGS.includes(meta.mountedBy))
throw configError("config.tool_roster_mount_tag", `tool ${JSON.stringify(tool.name)}: mount tag ${JSON.stringify(meta.mountedBy)} is outside the closed set`);
this.#meta.set(tool, meta);
const eff = faceOf(tool).effect;
if (eff !== undefined && meta.mountedBy !== "scenario")
this.#effects.set(tool.name, eff);
}
mountAll(tools, meta) {
for (const t of tools)
this.mount(t, meta);
}
unmountWhere(pred) {
const removed = [];
for (let i = this.#tools.length - 1; i >= 0; i--) {
const t = this.#tools[i];
if (pred(t, i)) {
this.#tools.splice(i, 1);
removed.unshift(t);
}
}
return removed;
}
replaceAt(index, tool) {
const prior = this.#tools[index];
if (prior === undefined)
throw new Error(`roster: no tool at index ${index}`);
const meta = this.#meta.get(prior);
this.#tools[index] = tool;
if (meta !== undefined)
this.#meta.set(tool, meta);
}
replaceDomain(prefix, fresh, meta) {
let anchor = -1;
for (let i = this.#tools.length - 1; i >= 0; i--) {
if (this.#tools[i].name.startsWith(prefix)) {
anchor = i;
this.#tools.splice(i, 1);
}
}
for (const t of fresh) {
this.#meta.set(t, meta);
const eff = faceOf(t).effect;
if (eff !== undefined)
this.#effects.set(t.name, eff);
}
if (anchor >= 0)
this.#tools.splice(anchor, 0, ...fresh);
else
this.#tools.push(...fresh);
return { anchor };
}
snapshot(facts) {
const all = [...this.#tools, ...this.#engineLane];
const winner = survivorsByName(all);
const entries = [];
const emitted = new Set();
for (const t of all) {
if (emitted.has(t.name))
continue;
emitted.add(t.name);
const survivor = winner.get(t.name);
const meta = this.#meta.get(survivor) ?? { source: "caller", mountedBy: "scenario" };
entries.push(mintRosterEntry(survivor, meta, facts));
}
const roster = { schemaVersion: 1, count: entries.length, entries, digest: rosterDigestOf(entries) };
const worn = rosterEngineNamesProblem(roster);
if (worn !== undefined)
throw configError("config.tool_face_invalid", worn);
return roster;
}
}
let catalogOwners;
function catalogOwnerOf(name) {
if (catalogOwners === undefined) {
const owners = new Map();
for (const e of describeToolCatalog()) {
owners.set(e.name, e.name);
for (const a of e.face.aliases ?? [])
if (!owners.has(a))
owners.set(a, e.name);
}
catalogOwners = owners;
}
return catalogOwners.get(name);
}
export function rosterEngineNamesProblem(roster) {
for (const e of roster.entries) {
const resolving = namesResolvingTo(roster, e);
const worn = new Map();
for (const n of resolving) {
const owner = catalogOwnerOf(n);
if (owner !== undefined && !worn.has(owner))
worn.set(owner, n);
}
if (worn.size >= 2) {
return `tool ${JSON.stringify(e.name)} answers to the names of ${worn.size} engine tools (${[...worn.values()].map((n) => JSON.stringify(n)).join(", ")}): a tool may answer to at most one engine tool's names — keep one of those aliases`;
}
}
return undefined;
}
export function callerMountSource(tool) {
return getToolContract(tool, "caller").origin === "core" ? "builtin" : "caller";
}
export function rosterCardId(source, toolName, peer, protocolTool, contractId) {
return source === "builtin" || source === "synthetic"
? `builtin:${toolName}`
: source === "mcp" || source === "a2a"
? `${source}:${peer}/${protocolTool}`
: `${source === "external" ? "external" : "caller"}:${contractId}`;
}
export function callerCardIdOf(tool) {
const t = tool;
return rosterCardId(callerMountSource(t), t.name, null, t.name, getToolContract(t, "caller").contractId);
}
export function mintRosterEntry(tool, meta, facts) {
const face = faceOf(tool);
const fallbackOrigin = meta.source === "builtin" ? "core" : meta.source === "synthetic" ? "synthetic" : meta.source === "mcp" || meta.source === "a2a" ? meta.source : "caller";
const d = getToolContract(tool, fallbackOrigin);
const peer = meta.origin?.peer ?? null;
const protocolTool = peer !== null ? protocolOf(tool.name)?.parse(tool.name)?.tool ?? tool.name : tool.name;
const cardId = rosterCardId(meta.source, tool.name, peer, protocolTool, d.contractId);
const { keys: inputKeys } = inputKeysOf(tool.parameters);
const declaredEffect = face.effect;
const foldedEffect = facts.effects.get(tool.name);
const effect = declaredEffect ?? foldedEffect ?? "write";
const effectProvenance = declaredEffect !== undefined ? (meta.source === "synthetic" ? "synthetic" : "declared") : foldedEffect !== undefined && (meta.source === "mcp" || meta.source === "a2a") ? "caller" : "default";
const egress = face.egress === true || facts.egress.has(tool.name);
const egressProvenance = face.egress === true ? "declared" : egress ? "caller" : "default";
const irreversibility = face.irreversibility ?? facts.irreversibilityTier.get(tool.name);
const reversibilityProbe = face.reversibilityProbe !== undefined || facts.reversibilityProbes.has(tool.name);
const contentOrigin = face.contentOrigin ?? (facts.trustedTools.has(tool.name) ? "local" : "external");
const contentOriginProvenance = face.contentOrigin !== undefined ? (meta.source === "mcp" || meta.source === "a2a" ? "server" : "declared") : facts.trustedTools.has(tool.name) ? "exempted" : "default";
const pathTarget = face.pathTarget;
const rf = ruleFaceOfInstance(tool.parameters, face.ruleFace, pathTarget);
const hints = face.renderHints !== undefined ? capRenderHints(face.renderHints) : undefined;
const bound = rosterMemberBoundProblem({
name: tool.name,
aliases: tool.aliases,
...(peer !== null ? { peer } : {}),
contractId: d.contractId,
implementationRevision: d.implementationRevision,
...(typeof face.modelGate === "string" ? { modelGate: face.modelGate } : {}),
cardId,
});
if (bound !== undefined)
throw configError(bound.code, bound.message);
return {
name: tool.name,
aliases: [...new Set(tool.aliases ?? [])],
source: meta.source,
...(meta.origin !== undefined ? { origin: { peer: meta.origin.peer, ...(meta.origin.declaredBy !== undefined ? { declaredBy: meta.origin.declaredBy } : {}) } } : {}),
contract: { contractId: d.contractId, implementationRevision: d.implementationRevision },
shapeDigest: d.shapeDigest,
wireSchemaDigest: d.wireSchemaDigest,
cardId,
capabilityId: mintCapabilityId({ source: meta.source, peer, name: tool.name, contractId: d.contractId }),
inputKeys,
effect,
effectProvenance,
offload: face.offload !== false,
...(typeof face.offloadThresholdChars === "number" && Number.isFinite(face.offloadThresholdChars) && face.offloadThresholdChars >= 0 ? { offloadThresholdChars: Math.floor(face.offloadThresholdChars) } : {}),
compactable: face.compactable === true,
budgetExempt: face.budgetExempt === true,
egress,
egressProvenance,
...(irreversibility !== undefined ? { irreversibility } : {}),
reversibilityProbe,
contentOrigin,
contentOriginProvenance,
family: face.family !== undefined && TOOL_FAMILIES.includes(face.family) ? face.family : "other",
...(pathTarget !== undefined
? {
pathTarget: {
param: pathTarget.param,
aliases: [...(pathTarget.aliases ?? [])],
access: pathTarget.access,
skillScopeEligible: pathTarget.skillScopeEligible === true,
...(pathTarget.base !== undefined ? { base: pathTarget.base } : {}),
...(pathTarget.absent !== undefined ? { absent: pathTarget.absent } : {}),
...(pathTarget.patternParam !== undefined ? { patternParam: pathTarget.patternParam } : {}),
},
}
: {}),
ruleFace: rf,
approvalPreview: typeof tool.approvalPreview === "function",
validateInput: typeof tool.validateInput === "function",
deferred: facts.deferred.has(tool.name),
alwaysLoad: face.alwaysLoad === true || face.mcpAlwaysLoad === true,
...(typeof face.modelGate === "string" ? { modelGate: face.modelGate } : {}),
...(hints !== undefined ? { renderHints: hints } : {}),
mountedBy: meta.mountedBy,
};
}
export class ToolRosterDeltaSeat {
#current;
#snapshot;
#pending = [];
#sink;
arm(current, snapshot) {
this.#current = current;
this.#snapshot = snapshot;
}
get current() {
return this.#current;
}
republish() {
if (this.#current === undefined || this.#snapshot === undefined)
return undefined;
const next = this.#snapshot();
const delta = diffToolRoster(this.#current, next);
this.#current = next;
if (this.#sink !== undefined)
this.#sink(delta);
else {
this.#pending.push(delta);
if (this.#pending.length > 64)
this.#pending.shift();
}
return delta;
}
subscribe(sink) {
this.#sink = sink;
const pending = this.#pending;
this.#pending = [];
for (const d of pending)
sink(d);
}
unsubscribe() {
this.#sink = undefined;
}
drain() {
const pending = this.#pending;
this.#pending = [];
return pending;
}
}
export function ruleFacesFromRoster(roster) {
const byName = new Map(roster.entries.map((e) => [e.name, e]));
for (const e of roster.entries)
for (const a of e.aliases)
if (!byName.has(a))
byName.set(a, e);
return (toolName) => {
const e = byName.get(toolName);
if (e === undefined)
return undefined;
const params = new Set(e.ruleFace.params);
return {
primaryParams: e.ruleFace.primaryParams,
params: e.ruleFace.params,
nonScalar: e.inputKeys.filter((k) => !params.has(k.name) && (k.type === "object" || k.type === "array")).map((k) => k.name),
pathTargeting: e.pathTarget !== undefined,
};
};
}
export function diffToolRoster(prev, next) {
const before = new Map(prev.entries.map((e) => [e.name, canonical(e)]));
const after = new Map(next.entries.map((e) => [e.name, canonical(e)]));
const added = [];
const removed = [];
const changed = [];
for (const [name, row] of after) {
const old = before.get(name);
if (old === undefined)
added.push(name);
else if (old !== row)
changed.push(name);
}
for (const name of before.keys())
if (!after.has(name))
removed.push(name);
return { fromDigest: prev.digest, roster: next, summary: { added, removed, changed } };
}
export function judgeParkedToolIdentity(pending, row) {
const carried = pending.capabilityId !== undefined || pending.contractId !== undefined || pending.shapeDigest !== undefined || pending.implementationRevision !== undefined;
if (row === undefined)
return "absent";
if (!carried)
return "name_only";
const id = toolIdentityOf(row);
return pending.capabilityId === id.capabilityId && pending.contractId === id.contractId && pending.shapeDigest === id.shapeDigest && pending.implementationRevision === id.implementationRevision ? "match" : "mismatch";
}
export function offendingResultCard(hints, cardId, engineCards) {
for (const c of hints?.resultCards ?? []) {
if (typeof c !== "string")
continue;
if (c.startsWith(`${cardId}/`))
continue;
if (c.startsWith("builtin:") || engineCards.has(c) || !c.includes("/"))
return c;
return c;
}
return undefined;
}