@sema-agent/core
Version:
Stateless, task-oriented AI agent core
170 lines (169 loc) • 8.15 kB
JavaScript
import { createHash } from "node:crypto";
import { canonical } from "./epoch.js";
export const ARTIFACT_LIMITS = {
maxSections: 64,
maxSectionBytes: 32_000,
maxTotalBytes: 131_072,
maxIdLength: 128,
maxPackIdLength: 200,
};
const ID_RE = /^[a-z0-9][a-z0-9/._-]*$/;
const DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
const SLOTS = new Set(["identity", "scenario", "behavior"]);
export function sha256Hex(text) {
return `sha256:${createHash("sha256").update(text, "utf8").digest("hex")}`;
}
export function computeCatalogDigest(catalog) {
return sha256Hex(canonical(catalog));
}
export function computeArtifactDigest(payload) {
return sha256Hex(canonical(payload));
}
function triple(v) {
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v.trim());
if (!m)
return undefined;
return [Number(m[1]), Number(m[2]), Number(m[3])];
}
function tripleCmp(a, b) {
for (let i = 0; i < 3; i++) {
if (a[i] !== b[i])
return a[i] < b[i] ? -1 : 1;
}
return 0;
}
export function verifyPromptArtifact(raw, opts) {
const fail = (code, reason) => ({ ok: false, code, reason });
if (raw === null || typeof raw !== "object")
return fail("shape", "envelope is not an object");
const env = raw;
if (typeof env.sourceRevision !== "string" || !env.sourceRevision || env.sourceRevision.length > 500) {
return fail("shape", "sourceRevision missing/oversized");
}
const art = env.artifact;
if (art === null || typeof art !== "object")
return fail("shape", "artifact is not an object");
const payload = art.payload;
if (payload === null || typeof payload !== "object")
return fail("shape", "payload is not an object");
if (payload.schemaVersion !== 1)
return fail("schema", `unsupported schemaVersion ${String(payload.schemaVersion)}`);
if (payload.assemblyApi !== 1)
return fail("schema", `unsupported assemblyApi ${String(payload.assemblyApi)}`);
if (typeof payload.catalogDigest !== "string" || !DIGEST_RE.test(payload.catalogDigest))
return fail("shape", "catalogDigest malformed");
let compatibleCore;
if (payload.compatibleCore !== undefined) {
const cc = payload.compatibleCore;
if (cc === null || typeof cc !== "object")
return fail("shape", "compatibleCore malformed");
compatibleCore = {};
for (const key of ["minCore", "maxCoreExclusive"]) {
const v = cc[key];
if (v === undefined)
continue;
if (typeof v !== "string" || triple(v) === undefined)
return fail("shape", `compatibleCore.${key} is not a dotted triple`);
compatibleCore[key] = v;
}
const engine = triple(opts.engineVersion);
if (engine === undefined)
return fail("core-range", `engine version "${opts.engineVersion}" is not a dotted triple`);
if (compatibleCore.minCore !== undefined && tripleCmp(engine, triple(compatibleCore.minCore)) < 0) {
return fail("core-range", `engine ${opts.engineVersion} < minCore ${compatibleCore.minCore}`);
}
if (compatibleCore.maxCoreExclusive !== undefined && tripleCmp(engine, triple(compatibleCore.maxCoreExclusive)) >= 0) {
return fail("core-range", `engine ${opts.engineVersion} >= maxCoreExclusive ${compatibleCore.maxCoreExclusive}`);
}
}
const closure = payload.closure;
if (closure === null || typeof closure !== "object")
return fail("shape", "closure is not an object");
if (closure.basePackId !== "sema-default@1") {
return fail("base-pack", `unsupported basePackId ${JSON.stringify(closure.basePackId).slice(0, 120)} (v1: bundled base only)`);
}
const catalog = closure.promptCatalog;
if (catalog === null || typeof catalog !== "object")
return fail("shape", "promptCatalog is not an object");
if (typeof catalog.packId !== "string" || !catalog.packId.trim() || catalog.packId.length > ARTIFACT_LIMITS.maxPackIdLength) {
return fail("shape", "promptCatalog.packId missing/oversized");
}
if (!Array.isArray(catalog.sections))
return fail("shape", "promptCatalog.sections is not an array");
if (catalog.sections.length === 0)
return fail("shape", "promptCatalog.sections is empty — publish at least one section, or disable the source instead");
if (catalog.sections.length > ARTIFACT_LIMITS.maxSections) {
return fail("limits", `${catalog.sections.length} sections > max ${ARTIFACT_LIMITS.maxSections}`);
}
const ids = new Set();
let totalBytes = 0;
const sections = [];
for (const rawSection of catalog.sections) {
if (rawSection === null || typeof rawSection !== "object")
return fail("shape", "section is not an object");
const s = rawSection;
if (typeof s.id !== "string" || s.id.startsWith("core/") || s.id.length > ARTIFACT_LIMITS.maxIdLength || !ID_RE.test(s.id)) {
return fail("shape", `invalid section id ${JSON.stringify(s.id).slice(0, 120)}`);
}
if (ids.has(s.id))
return fail("shape", `duplicate section id "${s.id}"`);
ids.add(s.id);
if (typeof s.slot !== "string" || !SLOTS.has(s.slot))
return fail("shape", `unknown slot ${JSON.stringify(s.slot).slice(0, 60)} ("${s.id}")`);
if (typeof s.text !== "string" || s.text.length === 0)
return fail("shape", `empty/missing text ("${s.id}")`);
const bytes = Buffer.byteLength(s.text, "utf8");
if (bytes > ARTIFACT_LIMITS.maxSectionBytes)
return fail("limits", `section "${s.id}" ${bytes} bytes > max ${ARTIFACT_LIMITS.maxSectionBytes}`);
totalBytes += bytes;
if (typeof s.contentHash !== "string" || !DIGEST_RE.test(s.contentHash))
return fail("shape", `contentHash missing/malformed ("${s.id}")`);
if (s.contentHash !== sha256Hex(s.text))
return fail("content-hash", `contentHash does not match text ("${s.id}")`);
sections.push({ id: s.id, slot: s.slot, text: s.text, contentHash: s.contentHash });
}
if (totalBytes > ARTIFACT_LIMITS.maxTotalBytes)
return fail("limits", `catalog ${totalBytes} bytes > max ${ARTIFACT_LIMITS.maxTotalBytes}`);
const normalizedCatalog = { packId: catalog.packId, sections };
const normalizedPayload = {
schemaVersion: 1,
assemblyApi: 1,
catalogDigest: payload.catalogDigest,
...(compatibleCore !== undefined ? { compatibleCore } : {}),
closure: { basePackId: "sema-default@1", promptCatalog: normalizedCatalog },
};
if (computeCatalogDigest(normalizedCatalog) !== payload.catalogDigest) {
return fail("digest", "catalogDigest does not match canonical(promptCatalog)");
}
if (typeof art.artifactDigest !== "string" || !DIGEST_RE.test(art.artifactDigest))
return fail("shape", "artifactDigest malformed");
if (computeArtifactDigest(normalizedPayload) !== art.artifactDigest) {
return fail("digest", "artifactDigest does not match canonical(payload)");
}
return { ok: true, artifact: { payload: normalizedPayload, artifactDigest: art.artifactDigest } };
}
export function artifactDeclarations(artifact) {
return artifact.payload.closure.promptCatalog.sections.map((s) => ({
id: s.id,
slot: s.slot,
text: s.text,
contentHash: s.contentHash,
}));
}
export function buildPromptArtifactEnvelope(opts) {
const catalog = {
packId: opts.packId,
sections: opts.sections.map((s) => ({ ...s, contentHash: sha256Hex(s.text) })),
};
const payload = {
schemaVersion: 1,
assemblyApi: 1,
catalogDigest: computeCatalogDigest(catalog),
...(opts.compatibleCore !== undefined ? { compatibleCore: opts.compatibleCore } : {}),
closure: { basePackId: "sema-default@1", promptCatalog: catalog },
};
return {
artifact: { payload, artifactDigest: computeArtifactDigest(payload) },
sourceRevision: opts.sourceRevision,
};
}