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