UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

230 lines (229 loc) • 11.4 kB
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 } : {}) }; }