@sema-agent/core
Version:
Stateless, task-oriented AI agent core
230 lines (229 loc) • 11.4 kB
JavaScript
import { readCompactionActiveTools } from "../../internal/harness.js";
import { toolsToFingerprintInputs } from "../cache-break-detector.js";
import { deliverEngineNotice } from "../types.js";
import { TOOL_SEARCH_NAME, buildDeferredRegistry, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, staticSchemaRenderable } from "./tool-disclosure.js";
import { createDeferralCensus } from "./deferral-census.js";
let announcedMaterializeEnvBySink = new WeakMap();
const announcedMaterializeEnvConsole = new Set();
function materializeEnvLedger(onNotice) {
if (typeof onNotice !== "function")
return announcedMaterializeEnvConsole;
let lines = announcedMaterializeEnvBySink.get(onNotice);
if (lines === undefined) {
lines = new Set();
announcedMaterializeEnvBySink.set(onNotice, lines);
}
return lines;
}
export function __resetMaterializeEnvAnnouncements() {
announcedMaterializeEnvBySink = new WeakMap();
announcedMaterializeEnvConsole.clear();
}
function emitMaterializeEnvNotice(onNotice, message, detail) {
const ledger = materializeEnvLedger(onNotice);
if (ledger.has(message))
return;
ledger.add(message);
deliverEngineNotice(onNotice, { code: "config.materialize_env_discarded", message, detail });
}
export async function prepareToolDisclosureMount(input) {
const { spec, deps, session, resume, deferred, tools, onceLedger, mcp, promptManifest, offloadReachableToolsRef, contentOriginWrapRef, rebuildHarnessToolsRef, harnessRef, toolSearchPolicy } = input;
let toolDisclosureSeat;
const activeTools = new Set();
const fpRef = {};
const turnSnapshotRef = {};
let harnessTools = tools;
let toolSearchMounted;
const failedMcpServers = onceLedger.mcpFailuresToAnnounce(mcp.statuses);
let toolsDeltaRef;
let toolMaterializeStatic = false;
let deferDirectCall = false;
const staticFaceForRef = {};
if (deferred.size > 0 || failedMcpServers.length > 0) {
toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
}
const listingRideRef = {};
{
const raw = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
if (raw !== undefined && raw !== "swap" && raw !== "static" && deferred.size === 0) {
const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(raw)} is not "swap" or "static" — inert on this task (no deferred tools), but a deferring task WITHOUT an explicit spec strategy will refuse to prepare under it (an explicit legal spec outranks and discards it, loudly). Fix or unset the flag.`;
emitMaterializeEnvNotice(deps.onNotice, line, { raw });
}
}
if (deferred.size > 0) {
if (deferred.has(TOOL_SEARCH_NAME) || tools.some((t) => t.name === TOOL_SEARCH_NAME)) {
const e = new Error(`Tool name "${TOOL_SEARCH_NAME}" is reserved when deferred tools are present.`);
e.code = "config.reserved_tool_name";
throw e;
}
const registry = buildDeferredRegistry(deferred, tools);
const rawEnvStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
const envStrategyInvalid = rawEnvStrategy !== undefined && rawEnvStrategy !== "swap" && rawEnvStrategy !== "static";
if (envStrategyInvalid && spec.toolMaterializeStrategy === undefined) {
const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(rawEnvStrategy)}).`);
e.code = "config.tool_materialize_invalid";
throw e;
}
if (envStrategyInvalid) {
const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(rawEnvStrategy)} was ignored — not "swap" or "static", and the task spec pins toolMaterializeStrategy=${JSON.stringify(spec.toolMaterializeStrategy)} which outranks it. Fix or unset the env flag.`;
emitMaterializeEnvNotice(deps.onNotice, line, { raw: rawEnvStrategy, specStrategy: spec.toolMaterializeStrategy });
}
const envStrategy = envStrategyInvalid ? undefined : rawEnvStrategy;
const requestedStrategy = spec.toolMaterializeStrategy ?? envStrategy ?? "swap";
const laneDegrade = requestedStrategy === "static" && spec.deferSelfResolve === false;
const materializeStatic = requestedStrategy === "static" && !laneDegrade;
toolMaterializeStatic = materializeStatic;
deferDirectCall = spec.deferSelfResolve !== false;
promptManifest.toolDisclosure = {
deferredTools: deferred.size,
strategy: materializeStatic ? "static" : "swap",
source: laneDegrade ? "degraded_no_direct_lane" : spec.toolMaterializeStrategy !== undefined ? "spec" : envStrategy !== undefined ? "env" : "default",
};
const census = createDeferralCensus(deferred, toolSearchPolicy);
toolDisclosureSeat = census.seat;
const publishCensus = () => census.publish(activeTools);
const everExempt = new Set();
const staticFaceFor = (name) => {
if (!materializeStatic)
return false;
if (everExempt.has(name))
return false;
if (staticSchemaRenderable(tools.find((t) => t.name === name)?.parameters))
return true;
everExempt.add(name);
return false;
};
staticFaceForRef.current = staticFaceFor;
let activationChain = Promise.resolve();
const serializeActivation = (section) => {
const p = activationChain.then(section);
activationChain = p.then(() => undefined, () => undefined);
return p;
};
const directCallFor = (name) => {
if (spec.deferSelfResolve === false)
return undefined;
const executionMode = tools.find((t) => t.name === name)?.executionMode;
return {
resolveReal: () => {
const real = tools.find((t) => t.name === name);
if (real === undefined)
return undefined;
return {
parameters: real.parameters,
invoke: (toolCallId, params, signal, onUpdate) => real.execute(toolCallId, params, signal, onUpdate),
};
},
...(executionMode !== undefined ? { executionMode } : {}),
staticFace: () => staticFaceFor(name),
activate: async () => serializeActivation(async () => {
if (activeTools.has(name))
return undefined;
activeTools.add(name);
try {
await rematerialize(activeTools);
}
catch (e) {
activeTools.delete(name);
throw e;
}
publishCensus();
return listingRideRef.current?.([name]);
}),
};
};
const placeholders = new Map([...registry.values()].map((i) => [i.name, createPlaceholderTool(i, directCallFor(i.name))]));
const { messages } = await session.buildContext();
for (const n of extractDiscoveredToolNames(messages, registry)) {
if (deferred.has(n))
activeTools.add(n);
}
for (const entry of await session.getBranch()) {
if (entry.type !== "compaction" || entry.fromHook === true)
continue;
for (const n of readCompactionActiveTools(entry.details)) {
if (deferred.has(n))
activeTools.add(n);
}
}
if (resume) {
for (const n of resume.seed.activeTools)
if (deferred.has(n))
activeTools.add(n);
}
const callableToolNames = () => {
const s = new Set(tools.map((t) => t.name));
for (const n of deferred)
if (!activeTools.has(n))
s.delete(n);
s.add(TOOL_SEARCH_NAME);
return s;
};
offloadReachableToolsRef.current = callableToolNames;
let toolSearch;
const buildToolList = (active) => {
const list = tools.map((t) => (deferred.has(t.name) && (staticFaceFor(t.name) || !active.has(t.name)) ? placeholders.get(t.name) : t));
list.push(toolSearch);
return list;
};
const announcedTools = new Set(activeTools);
const withdrawnTools = new Set();
const deltaRef = toolsDeltaRef;
const rematerialize = async (active) => {
contentOriginWrapRef.current?.();
const list = buildToolList(active);
await harnessRef.current.setTools(list, list.map((t) => t.name));
if (fpRef.current)
fpRef.current.tools = toolsToFingerprintInputs(list);
turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
const live = new Set(list.map((t) => t.name));
for (const n of [...announcedTools]) {
if (!live.has(n)) {
announcedTools.delete(n);
withdrawnTools.add(n);
deltaRef.pendingRemoved.push(n);
}
}
for (const n of active) {
if (!live.has(n))
continue;
if (!announcedTools.has(n)) {
announcedTools.add(n);
if (withdrawnTools.delete(n))
deltaRef.pendingReadded.push(n);
else
deltaRef.pending.push(n);
}
}
};
rebuildHarnessToolsRef.current = () => rematerialize(activeTools);
toolSearch = createToolSearchTool({
registry,
active: activeTools,
rematerialize,
listingRide: (newly) => listingRideRef.current?.(newly),
mountedNames: callableToolNames,
directCallEnabled: spec.deferSelfResolve !== false,
isMounted: (name) => tools.some((t) => t.name === name),
...(materializeStatic
? { staticSchemaFor: (name) => (staticFaceFor(name) ? tools.find((t) => t.name === name)?.parameters : undefined) }
: {}),
onActivationCommitted: publishCensus,
serializeActivation,
});
harnessTools = buildToolList(activeTools);
publishCensus();
toolSearchMounted = toolSearch;
}
else {
rebuildHarnessToolsRef.current = async () => {
contentOriginWrapRef.current?.();
const list = [...tools];
await harnessRef.current.setTools(list, list.map((t) => t.name));
if (fpRef.current)
fpRef.current.tools = toolsToFingerprintInputs(list);
turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
};
}
return { activeTools, fpRef, turnSnapshotRef, harnessTools, toolsDeltaRef, toolMaterializeStatic, deferDirectCall, staticFaceForRef, listingRideRef, ...(toolSearchMounted !== undefined ? { toolSearch: toolSearchMounted } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosureSeat } : {}) };
}