UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

201 lines (200 loc) • 11.8 kB
import { createHash } from "node:crypto"; import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js"; import { assemblePrompt } from "../../prompt-assembly/assemble.js"; import { adoptCenter, centerOverlaysOf, verifyPromptArtifactBody } from "../../prompt-assembly/artifact.js"; import { refusalClause, storedArtifactLine } from "../../prompt-assembly/artifact-store.js"; import { catalogDigestOf, digestOf, prepareEpochRecord } from "../../prompt-assembly/epoch.js"; import { rendererVector } from "../../prompt-assembly/renderers.js"; import { SEMA_DEFAULT_CATALOG } from "../../prompt-assembly/catalog/index.js"; import { describeCatalog } from "../../prompt-assembly/explain.js"; import { selectorRecordOf, selectorsOf } from "../../prompt-assembly/select.js"; import { auditToolCollisions, projectToolManifest } from "../../prompt-assembly/tool-catalog.js"; import { buildEnvironmentContext } from "../../prompts/default.js"; import { raisePromptCompose } from "../mods/stations.js"; import { protocolOf } from "../protocol-table.js"; import { hasConversationContent } from "../session.js"; import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js"; const CATALOG_DIGEST = catalogDigestOf(SEMA_DEFAULT_CATALOG); function taskLayerOf(task) { return task.map(({ overlay: o, origin }) => ({ by: origin.by, id: o.id, op: o.op, target: o.target, ...(o.text !== undefined ? { chars: o.text.length } : {}), ...(o.message !== undefined ? { message: o.message } : {}), ...(o.when !== undefined ? { when: o.when } : {}), })); } export async function preparePromptAssembly(input) { const { session, deps, internals, resume, sugar, host, binding, modBus, featureFlags, mcpInstructionsBlock, memoryBlock, envFacts, promptProfile, fableMitigations, tools, model, sessionId, onceLedger } = input; const topLevel = internals?.parentTaskId === undefined && internals?.parentSessionId === undefined && internals?.parentCenterArtifactDigest === undefined; const stored = await session.getPromptEpoch(); let centerAdoption; { const failUnavailable = (what, refusal) => { const why = refusal !== undefined ? `the source's copy is ${refusalClause(refusal)}` : `artifact source miss / not verifiable here${deps.promptSource === undefined ? "; RunnerDeps.promptSource is not configured" : ""}`; throw Object.assign(new Error(`prompt snapshot unavailable: ${what} (${why}) — refusing to recompose a hybrid prompt`), { code: "prompt.snapshot_unavailable" }); }; const resolvePinned = async (digest, sourceRevision, via) => { const told = {}; const art = await deps.promptSource?.get(digest, (r) => void (told.current = r)); const adopted = art !== undefined ? adoptCenter(art, sourceRevision) : undefined; if (adopted !== undefined) return adopted; const checked = art !== undefined ? verifyPromptArtifactBody(art) : undefined; const refusal = told.current ?? (checked !== undefined && !checked.ok ? { code: checked.code, reason: checked.reason } : undefined); if (refusal !== undefined) deps.onError?.(new Error(storedArtifactLine(digest, refusal, `the session fails prompt.snapshot_unavailable (a hybrid prompt is never recomposed)`)), { phase: "prompt-constitution", sessionId }); return failUnavailable(`${via} center prompt artifact ${digest.slice(0, 23)}… has no verified resolvable copy`, refusal); }; const pinned = stored?.kind === "pin" ? stored.descriptor : undefined; const retiredCenterPin = stored?.kind === "legacy" && stored.centerPinned; if (pinned?.layers.center !== undefined) { centerAdoption = await resolvePinned(pinned.layers.center, pinned.sourceRevision, "this session is pinned to"); } else if (retiredCenterPin && topLevel) { const state = deps.promptSource?.sourceState() ?? "unavailable"; if (state === "unavailable") failUnavailable("this session's center pin is of a retired shape and the center source has no verified state; the pin is kept"); if (state === "active") { const cand = deps.promptSource.current(); centerAdoption = (cand !== undefined ? adoptCenter(cand, cand.sourceRevision) : undefined) ?? failUnavailable("this session's center pin is of a retired shape and the current candidate"); } } else if (stored === undefined || retiredCenterPin) { if (internals?.parentCenterArtifactDigest !== undefined) { centerAdoption = await resolvePinned(internals.parentCenterArtifactDigest, internals.parentCenterSourceRevision, "the spawning parent adopted"); } else if (stored === undefined && topLevel && resume === undefined) { const cand = deps.promptSource?.current(); if (cand !== undefined && !hasConversationContent(await session.getBranch())) { centerAdoption = adoptCenter(cand, cand.sourceRevision); if (centerAdoption === undefined) deps.onError?.(new Error(`center prompt candidate ${cand.artifactDigest.slice(0, 23)}… does not verify on this engine; the session starts with no center layer`), { phase: "prompt-constitution", sessionId }); } } } } const facts = { supervisorEnabled: false, teammateEnabled: internals?.explicitAgentName !== undefined && tools.some((t) => t.name === SEND_MESSAGE_TOOL_NAME), ...featureFlags, orchestrationEnabled: featureFlags.orchestrationEnabled && tools.some((t) => t.name === "Workflow"), orchestrationDeferred: featureFlags.orchestrationDeferred && tools.some((t) => t.name === "Workflow"), promptProfile, fableMitigations, }; const selectorRecord = selectorRecordOf(selectorsOf(facts, host, binding)); const centerOverlays = centerOverlaysOf(centerAdoption); const lowerAdds = new Map(); for (const { overlay } of [...sugar, ...centerOverlays]) if (overlay.op === "add") lowerAdds.set(overlay.id, overlay.target); const composed = await raisePromptCompose(modBus, { model: model.id, catalog: describeCatalog().sections, selectors: selectorRecord, }, { lowerAdds, ...(input.signal !== undefined ? { signal: input.signal } : {}) }); for (const w of composed.warnings) deps.onError?.(new Error(w), { phase: "prompt-constitution", sessionId }); const taskOverlays = [...sugar, ...composed.task]; const buildAssembleInputs = (center) => ({ taskOverlays, centerOverlays: centerOverlaysOf(center), deployOverlays: composed.deploy, host, ...(binding !== undefined ? { binding } : {}), tools, facts, ...(envFacts.cwd !== undefined ? { workspaceFolder: envFacts.cwd } : {}), mcpInstructionsBlock, memoryBlock, buildEnvBlock: (date) => buildEnvironmentContext({ ...envFacts, date }) || undefined, date: envFacts.date, ...(model.promptGuidance && model.promptGuidance.length > 0 ? { modelGuidance: model.promptGuidance.join("\n\n") } : {}), onWarn: (message, phase) => deps.onError?.(new Error(message), { phase, sessionId }), }); const deployLayer = composed.deploy.length > 0 ? digestOf(composed.deploy.map(({ overlay, origin }) => ({ by: origin.by, overlay }))) : undefined; const taskLayer = taskOverlays.length > 0 ? digestOf(taskLayerOf(taskOverlays)) : undefined; const bindingLayer = digestOf(selectorRecord); const renderers = rendererVector(); const identityFor = (center) => ({ layers: { catalog: CATALOG_DIGEST, renderers, ...(center !== undefined ? { center: center.artifact.artifactDigest } : {}), ...(deployLayer !== undefined ? { deploy: deployLayer } : {}), ...(taskLayer !== undefined ? { task: taskLayer } : {}), binding: bindingLayer, }, ...(center?.sourceRevision !== undefined ? { sourceRevision: center.sourceRevision } : {}), }); const assembled = assemblePrompt(buildAssembleInputs(centerAdoption)); let systemPrompt = assembled.systemPrompt; const renderWithDate = (date) => { systemPrompt = assembled.renderWithDate(date); return systemPrompt; }; const promptBlocks = assembled.blocks; const collisionAudit = auditToolCollisions(tools); for (const y of collisionAudit.yields) { onceLedger.onError(new Error(`tool mount: caller tool "${y.name}" shadows another tool with the same canonical name (deliberate last-write-wins yield — the later mount serves)`), { phase: "config", sessionId }); } for (const c of collisionAudit.aliasCollisions) { onceLedger.onError(new Error(`tool mount: alias "${c.alias}" is claimed by both "${c.owners[0]}" and "${c.owners[1]}" (design/141 warn face — name resolution serves the later mount)`), { phase: "config", sessionId }); } const promptIdentity = identityFor(centerAdoption); const priorConversation = stored === undefined ? hasConversationContent(await session.getBranch()) : true; const record = prepareEpochRecord(stored, promptIdentity.layers, { priorConversation, ...(promptIdentity.sourceRevision !== undefined ? { sourceRevision: promptIdentity.sourceRevision } : {}) }); if (record !== undefined) await session.appendPromptEpoch(record); const current = record ?? (stored?.kind === "pin" ? stored.descriptor : undefined); const saltedHash = (text) => createHash("sha256").update(PROMPT_HASH_SALT).update(text).digest("hex").slice(0, 12); const contentHashOf = (s) => s.renderer !== undefined || s.chain.some((l) => l.source === "task") ? `salted:${createHash("sha256").update(PROMPT_HASH_SALT).update(s.text).digest("hex")}` : `sha256:${createHash("sha256").update(s.text).digest("hex")}`; const promptManifest = { identity: { artifact: current.identity, layers: current.layers, epoch: current.epoch, activatedBy: current.activatedBy }, selectors: selectorRecord, blocks: promptBlocks.map((b) => ({ id: b.id, chars: b.text.length, hash: saltedHash(b.text) })), sections: assembled.sections.map((s) => ({ id: s.id, band: s.band, rank: s.rank, ...(s.variant !== undefined ? { variant: s.variant } : {}), ...(s.renderer !== undefined ? { renderer: { ...s.renderer } } : {}), chars: s.text.length, locale: s.chain.some((l) => l.source !== "bundled" && l.superseded !== true) ? "overlay" : "canonical", contentHash: contentHashOf(s), chain: s.chain.map((l) => ({ ...l })), })), ...(assembled.superseded.length > 0 ? { superseded: assembled.superseded.map((s) => ({ ...s })) } : {}), tools: projectToolManifest(tools, (t) => protocolOf(t.name)?.id ?? "caller"), }; return { systemPromptSeat: { get current() { return systemPrompt; }, set current(v) { systemPrompt = v; }, }, renderWithDate, systemBlocks: assembled.systemBlocks, promptManifest, promptIdentity, buildAssembleInputs, identityFor, adoptsCandidates: topLevel, centerAdoptionRef: { get current() { return centerAdoption; }, set current(v) { centerAdoption = v; }, }, }; }