UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

113 lines (112 loc) • 6.43 kB
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 }; }