@sema-agent/core
Version:
Stateless, task-oriented AI agent core
113 lines (112 loc) • 6.43 kB
JavaScript
import { getToolContract } from "../../prompt-assembly/tool-catalog.js";
import { FULL_SHELL_CONTRACT_ID } from "../../tools/fs/index.js";
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
import { announceMemoryNotMounted } from "./announce-once-ledger.js";
import { protocolOf } from "../protocol-table.js";
import { classifyDeferredWithGate } from "./tool-defer-gate.js";
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
import { resolveToolSearch, toolSearchSettingOf } from "../tool-search-setting.js";
import { RosterBuilder } from "../tool-roster.js";
import { valveLeaves } from "../../agents/tool-filter.js";
function explicitlyDeferredMemoryTrio(mounted, roster, callerDeferNames) {
return mounted ? MEMORY_ENGINE_TOOL_NAMES.filter((n) => roster.some((t) => t.name === n) && (callerDeferNames ?? []).includes(n)) : [];
}
function memoryGroupRetractionSet(builtinDeferPairNames, engineTrioInPlay) {
return new Set([...builtinDeferPairNames, ...(engineTrioInPlay ? MEMORY_ENGINE_TOOL_NAMES : [])]);
}
export function prepareDeferClassify(input) {
const { spec, deps, roster, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryRecallSegment, onceLedger, sessionId } = input;
const tools = roster.tools;
let memoryBlock = input.memoryBlock;
const leaves = valveLeaves(spec, toolFaceSnapshot.selector);
roster.unmountWhere((t) => !leaves(t));
if (handsEnabled) {
const fullShellOnRoster = tools.some((t) => t.name === "Bash" && getToolContract(t, "core").contractId === FULL_SHELL_CONTRACT_ID);
if (fullShellOnRoster !== fullShellReachable) {
const e = new Error(`internal invariant: fullShellReachable=${String(fullShellReachable)} but the post-exclusion roster ` +
`${fullShellOnRoster ? "carries" : "does not carry"} the full shell (${FULL_SHELL_CONTRACT_ID}). ` +
`A new roster-affecting mechanism was added without updating the predicate.`);
e.code = "internal.full_shell_reachable_mismatch";
throw e;
}
}
if (promptProfile === "classic") {
for (let i = 0; i < tools.length; i++) {
const t = tools[i];
if (t.descriptionClassic !== undefined)
roster.replaceAt(i, { ...t, description: t.descriptionClassic });
}
}
const protocolToolNames = [...mcp.tools, ...a2a.tools].filter((t) => protocolOf(t.name) !== undefined).map((t) => t.name);
const mcpAlwaysLoadNames = mcp.tools
.filter((t) => t.mcpAlwaysLoad === true && !(toolFaceSnapshot.defer ?? []).includes(t.name))
.map((t) => t.name);
const resolvedToolSearch = resolveToolSearch(deps.toolSearch);
const toolSearchSeat = toolSearchSettingOf(resolvedToolSearch);
const toolSearchAdmitted = leaves({ name: TOOL_SEARCH_NAME });
const classifyDeferredOverFace = (face, builtinDeferNames) => {
const mounted = new Set(face.map((t) => t.name));
const classified = classifyDeferredWithGate({
specs: spec.tools ?? [],
protocolToolNames,
fullTools: face,
deferMode: deps.deferMode,
toolSearch: toolSearchSeat,
model,
callerDeferNames: (toolFaceSnapshot.defer ?? []).filter((n) => mounted.has(n)),
builtinDeferNames: builtinDeferNames.filter((n) => mounted.has(n)),
alwaysLoadNames: [
ASK_USER_QUESTION_TOOL_NAME,
...(toolFaceSnapshot.alwaysLoad ?? []),
...mcpAlwaysLoadNames,
],
});
const deferredSet = classified.deferred;
for (const n of [...deferredSet])
if (!mounted.has(n))
deferredSet.delete(n);
if (!toolSearchAdmitted)
deferredSet.clear();
return deferredSet;
};
const sharedMemoryPair = sharedMemoryPairMounted ? SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
const memoryEnginePair = explicitlyDeferredMemoryTrio(memoryEnginePairMounted, tools, toolFaceSnapshot.defer);
const builtinDeferPairNames = [...sharedMemoryPair, ...memoryEnginePair];
let deferred;
if (builtinDeferPairNames.length > 0) {
const withoutPairs = tools.filter((t) => !builtinDeferPairNames.some((n) => n === t.name));
const d1 = classifyDeferredOverFace(tools, builtinDeferPairNames);
const d0 = classifyDeferredOverFace(withoutPairs, []);
const soleCause = d0.size === 0 && d1.size > 0;
const supportNameTaken = tools.some((t) => t.name === TOOL_SEARCH_NAME || (t.aliases ?? []).includes(TOOL_SEARCH_NAME));
if (soleCause && supportNameTaken) {
const retractNames = memoryGroupRetractionSet(builtinDeferPairNames, memoryEnginePair.length > 0);
roster.unmountWhere((t) => retractNames.has(t.name));
if (sharedMemoryPair.length > 0) {
announceMemoryNotMounted(onceLedger, sessionId, "shared", SHARED_MEMORY_TOOL_NAMES, `mounting them would inject the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares`);
}
if (memoryEnginePair.length > 0) {
if (memoryBlock !== undefined && memoryRecallSegment !== undefined) {
const segment = memoryRecallSegment;
if (memoryBlock === segment)
memoryBlock = undefined;
else if (memoryBlock.includes(`\n\n${segment}`))
memoryBlock = memoryBlock.replace(`\n\n${segment}`, "");
else if (memoryBlock.startsWith(`${segment}\n\n`))
memoryBlock = memoryBlock.slice(segment.length + 2);
}
announceMemoryNotMounted(onceLedger, sessionId, "engine", MEMORY_ENGINE_TOOL_NAMES, `mounting them would inject the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares`);
}
deferred = d0;
}
else {
deferred = d1;
}
}
else {
deferred = classifyDeferredOverFace(tools, []);
}
return { deferred, memoryBlock, toolSearch: resolvedToolSearch };
}