UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

170 lines (169 loc) • 8.15 kB
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, }; }