openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
128 lines (127 loc) • 5.29 kB
JavaScript
import { r as isMissingPathError } from "./errno-CkbDOfLk.js";
import "./errors-Db3Ymjlb.js";
import { t as createCorePluginStateSyncKeyedStore } from "./plugin-state-store-C6hmUuuk.js";
import { realpathSync } from "node:fs";
import path from "node:path";
import { createHash, randomUUID } from "node:crypto";
//#region src/memory/memory-artifact-provenance.ts
const MEMORY_ARTIFACT_PROVENANCE_OWNER_ID = "core:memory-artifact-provenance";
const MEMORY_ARTIFACT_PROVENANCE_NAMESPACE = "workspace-files";
const MEMORY_ARTIFACT_PROVENANCE_MAX_ENTRIES = 5e4;
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function normalizeWorkspaceKey(workspaceDir) {
const resolved = path.resolve(workspaceDir);
let canonical = resolved;
try {
canonical = realpathSync.native(resolved);
} catch (error) {
if (!isMissingPathError(error)) throw error;
}
const normalized = canonical.replaceAll("\\", "/");
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
}
function normalizeMemoryArtifactRelativePath(relativePath) {
const normalized = relativePath.replaceAll("\\", "/");
if (!normalized || normalized.startsWith("/") || normalized.split("/").some((segment) => segment === "..")) return;
if ([
"MEMORY.md",
"memory.md",
"USER.md"
].includes(normalized)) return normalized;
if (!normalized.startsWith("memory/") || !normalized.endsWith(".md")) return;
if (normalized.startsWith("memory/dreaming/") || normalized.startsWith("memory/.dreams/")) return;
return normalized;
}
function resolveAddress(params) {
const relativePath = normalizeMemoryArtifactRelativePath(params.relativePath);
if (!relativePath) return;
const workspaceKey = sha256(normalizeWorkspaceKey(params.workspaceDir));
return {
workspaceKey,
relativePath,
storeKey: `${workspaceKey}:${sha256(relativePath)}`
};
}
function openStore() {
return createCorePluginStateSyncKeyedStore({
ownerId: MEMORY_ARTIFACT_PROVENANCE_OWNER_ID,
namespace: MEMORY_ARTIFACT_PROVENANCE_NAMESPACE,
maxEntries: MEMORY_ARTIFACT_PROVENANCE_MAX_ENTRIES,
overflowPolicy: "reject-new"
});
}
function normalizeStoredProvenance(value, address) {
if (value?.version !== 1 || value.workspaceKey !== address.workspaceKey || value.relativePath !== address.relativePath || !/^[a-f0-9]{64}$/u.test(value.fileHash) || value.originClass !== "agent" && value.originClass !== "untrusted" || !Number.isSafeInteger(value.observedAt) || typeof value.reservationId !== "string" || value.reservationId.length === 0) return;
return value;
}
function toPublicProvenance(stored) {
return {
fileHash: stored.fileHash,
originClass: stored.originClass,
observedAt: stored.observedAt,
...stored.sessionId ? { sessionId: stored.sessionId } : {},
...stored.sessionKey ? { sessionKey: stored.sessionKey } : {}
};
}
async function recordMemoryArtifactWriteProvenance(params) {
const address = resolveAddress(params);
if (!address) return;
const store = openStore();
const reservationId = randomUUID();
let previous;
store.update(address.storeKey, (current) => {
previous = normalizeStoredProvenance(current, address);
const originClass = params.originClass === "agent" && (!previous || previous.originClass === "agent" && previous.fileHash === sha256(params.contentBefore)) ? "agent" : "untrusted";
return {
version: 1,
workspaceKey: address.workspaceKey,
relativePath: address.relativePath,
fileHash: sha256(params.contentAfter),
originClass,
observedAt: params.observedAt,
...params.sessionId ? { sessionId: params.sessionId } : {},
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
reservationId
};
});
return async () => {
const rollbackStore = openStore();
if (previous) {
rollbackStore.update(address.storeKey, (current) => current?.reservationId === reservationId ? previous : void 0);
return;
}
rollbackStore.deleteIf(address.storeKey, (current) => current.reservationId === reservationId);
};
}
async function clearMemoryArtifactProvenance(params) {
const address = resolveAddress(params);
if (!address) return;
const expectedHash = sha256(params.contentBefore);
openStore().deleteIf(address.storeKey, (current) => current.fileHash === expectedHash);
}
async function readMemoryArtifactProvenance(params) {
const address = resolveAddress(params);
if (!address) return;
const stored = normalizeStoredProvenance(openStore().lookup(address.storeKey), address);
return stored ? toPublicProvenance(stored) : void 0;
}
async function listMemoryArtifactProvenance(params) {
const workspaceKey = sha256(normalizeWorkspaceKey(params.workspaceDir));
const prefix = `${workspaceKey}:`;
return openStore().entries().filter((entry) => entry.key.startsWith(prefix)).flatMap((entry) => {
const address = {
workspaceKey,
relativePath: entry.value.relativePath,
storeKey: entry.key
};
const stored = normalizeStoredProvenance(entry.value, address);
return stored ? [{
relativePath: stored.relativePath,
provenance: toPublicProvenance(stored)
}] : [];
});
}
//#endregion
export { recordMemoryArtifactWriteProvenance as a, readMemoryArtifactProvenance as i, listMemoryArtifactProvenance as n, normalizeMemoryArtifactRelativePath as r, clearMemoryArtifactProvenance as t };