UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

64 lines (63 loc) • 2.28 kB
import { createHash } from "node:crypto"; export const SESSION_LOG_DIGEST_SCHEME = "sema-log-v3"; function wireForm(value, seen) { try { return JSON.parse(JSON.stringify(value) ?? "null"); } catch { } if (value === null || typeof value !== "object") return { __sema_unserializable__: true }; const guard = seen ?? new WeakSet(); if (guard.has(value)) return { __sema_unserializable__: true }; guard.add(value); try { if (Array.isArray(value)) return value.map((v) => wireForm(v, guard)); const out = {}; for (const k of Object.keys(value)) { try { out[k] = wireForm(value[k], guard); } catch { out[k] = { __sema_unserializable__: true }; } } return out; } catch { return { __sema_unserializable__: true }; } finally { guard.delete(value); } } function stableJson(v) { if (v === null || typeof v !== "object") return JSON.stringify(v) ?? "null"; if (Array.isArray(v)) return `[${v.map(stableJson).join(",")}]`; const o = v; return `{${Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${stableJson(o[k])}`).join(",")}}`; } export function sessionEntryDigest(entry) { return `${SESSION_LOG_DIGEST_SCHEME}:sha256:${createHash("sha256").update(`entry\n${stableJson(wireForm(entry))}`, "utf8").digest("hex")}`; } export function sessionLogDigest(entries) { const list = entries ?? []; if (!Array.isArray(list)) throw new TypeError("sessionLogDigest expects an array of session entries (or null/undefined for an empty log)"); const h = createHash("sha256"); h.update(`${SESSION_LOG_DIGEST_SCHEME}\nlog\n${list.length}\n`, "utf8"); for (const e of list) { const c = stableJson(wireForm(e)); h.update(`${Buffer.byteLength(c, "utf8")}:`, "utf8"); h.update(c, "utf8"); } return `${SESSION_LOG_DIGEST_SCHEME}:sha256:${h.digest("hex")}`; } export function sessionLogDigestsComparable(a, b) { const wellFormed = (d) => typeof d === "string" && new RegExp(`^${SESSION_LOG_DIGEST_SCHEME}:sha256:[0-9a-f]{64}$`).test(d); return wellFormed(a) && wellFormed(b); }