@sema-agent/core
Version:
Stateless, task-oriented AI agent core
201 lines (200 loc) • 11.8 kB
JavaScript
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;
},
},
};
}