openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
215 lines (214 loc) • 11.9 kB
JavaScript
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { v as resolveSessionAgentIdStrict } from "./agent-scope-DbtJyKUL.js";
import { f as sessionDeliveryOrigin } from "./delivery-context.shared-CXmRgetN.js";
import { k as listSessionTranscriptArchivesReadOnly } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { t as resolveCanonicalMainSessionKey } from "./main-session-key-DOmwwwt0.js";
import { o as resolveEffectiveSessionToolsVisibility, r as createSessionVisibilityGuard, s as resolveSandboxSessionToolsVisibility, t as createAgentToAgentPolicy } from "./session-visibility-DNhpZ_1f.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import "./agent-scope-runtime-h57Nypqc.js";
import "./session-store-runtime-Btld46Zi.js";
import { i as resolveTranscriptStemToSessionKeys, r as loadCombinedSessionStoreForGateway, t as extractTranscriptIdentityFromSessionsMemoryHit } from "./session-transcript-hit-CtGwqFdZ.js";
import { r as buildSessionEntry } from "./memory-core-host-engine-sessions-Cn5pEjBS.js";
import "./memory-core-host-runtime-core-5HBGxdZe.js";
import { n as readSessionResetRecallCutoffMetadata, t as readSessionArchiveReasonFromHitPath } from "./session-reset-recall-metadata-_gATHFyh.js";
//#region extensions/memory-core/src/session-search-visibility.ts
function isGlobalSessionKeyForSharedScope(cfg, key) {
return cfg.session?.scope === "global" && key.trim().toLowerCase() === "global";
}
function isSameStoredTranscript(anchor, candidate) {
if (!anchor || !candidate) return false;
const anchorSessionId = anchor.sessionId?.trim();
const anchorSessionFile = normalizeOptionalString(anchor.sessionFile);
return Boolean(anchorSessionId && candidate.sessionId?.trim() === anchorSessionId || anchorSessionFile && normalizeOptionalString(candidate.sessionFile) === anchorSessionFile);
}
function isPrivateConversation(params) {
if (!params.entry) return false;
const key = params.key.trim().toLowerCase();
const chatTypes = [params.entry.chatType, sessionDeliveryOrigin(params.entry)?.chatType].filter((chatType) => chatType !== void 0);
if (chatTypes.some((chatType) => chatType === "group" || chatType === "channel") || /:active-memory:[a-f0-9]{12}$/i.test(key)) return false;
const prefix = `agent:${params.agentId.trim().toLowerCase()}:`;
if (key === "global" || key === `${prefix}global`) return false;
if (key.startsWith(`${prefix}explicit:`)) return chatTypes.length > 0 && chatTypes.every((chatType) => chatType === "direct");
if (key.includes(":group:") || key.includes(":channel:") || /:(?:active-memory|cron|heartbeat|hook|node|subagent)(?::|$)/.test(key)) return false;
if (chatTypes.length > 0) return chatTypes.every((chatType) => chatType === "direct");
if (key.includes(":direct:") || key.includes(":dm:")) return true;
return false;
}
function anchorAliasesArePrivate(params) {
for (const [key, entry] of Object.entries(params.store)) {
if (key === params.anchorSessionKey) continue;
if (!isSameStoredTranscript(params.anchorEntry, entry)) continue;
if (!isPrivateConversation({
agentId: params.agentId,
entry,
key
})) return false;
}
return true;
}
function isTrustedRecallRequester(params) {
const requesterSessionKey = params.requesterSessionKey?.trim();
if (!requesterSessionKey) return false;
if (requesterSessionKey === params.anchorSessionKey) return true;
if (!requesterSessionKey.startsWith(params.anchorSessionKey)) return false;
const recallSuffix = requesterSessionKey.slice(params.anchorSessionKey.length);
return /^:active-memory:[a-f0-9]{12}$/i.test(recallSuffix);
}
function filterSessionKeysByScopedAgent(params) {
const scopedAgentId = normalizeOptionalLowercaseString(params.scopedAgentId);
if (!scopedAgentId) return params.keys;
return params.keys.filter((key) => {
if (isGlobalSessionKeyForSharedScope(params.cfg, key)) return true;
const ownerAgentId = resolveSessionAgentIdStrict({
sessionKey: key,
config: params.cfg
});
return normalizeOptionalLowercaseString(ownerAgentId) === scopedAgentId;
});
}
async function filterMemorySearchHitsBySessionVisibility(params) {
if (!params.hits.some((hit) => hit.source === "sessions")) return params.conversationRecall?.corpus === "sessions" ? [] : params.hits;
const visibility = resolveEffectiveSessionToolsVisibility({
cfg: params.cfg,
sandboxed: params.sandboxed
});
const a2aPolicy = createAgentToAgentPolicy(params.cfg);
const requesterAgentId = params.requesterSessionKey ? resolveSessionAgentIdStrict({
sessionKey: params.requesterSessionKey,
config: params.cfg,
agentId: params.agentId
}) : void 0;
const scopedAgentId = params.agentId?.trim() || requesterAgentId;
const guard = params.requesterSessionKey ? await createSessionVisibilityGuard({
action: "history",
requesterSessionKey: params.requesterSessionKey,
requesterAgentId,
mainSessionKey: requesterAgentId && (!params.sandboxed || resolveSandboxSessionToolsVisibility(params.cfg) === "all") ? resolveCanonicalMainSessionKey({
agentId: requesterAgentId,
mainKey: params.cfg.session?.mainKey,
sessionScope: params.cfg.session?.scope
}) : void 0,
visibility,
a2aPolicy
}) : null;
const { store: combinedSessionStore, storePath } = loadCombinedSessionStoreForGateway(params.cfg, scopedAgentId ? { agentId: scopedAgentId } : {});
const archiveNames = [...new Set(params.hits.flatMap((hit) => {
const identity = hit.source === "sessions" ? extractTranscriptIdentityFromSessionsMemoryHit(hit.path) : void 0;
const archiveName = hit.path.replace(/\\/g, "/").split("/").at(-1);
return identity?.archived && archiveName ? [archiveName] : [];
}))];
const archivedSessionsByName = new Map(listSessionTranscriptArchivesReadOnly({
agentId: scopedAgentId,
archiveNames,
storePath
}).map((archive) => [archive.archiveName, archive]));
const conversationRecall = params.conversationRecall;
const trustedAgentScope = Boolean(params.trustedAgentScope && scopedAgentId && !params.requesterSessionKey && !conversationRecall);
const anchorSessionKey = conversationRecall?.anchorSessionKey.trim();
const recallAgentId = anchorSessionKey ? resolveSessionAgentIdStrict({
sessionKey: anchorSessionKey,
config: params.cfg
}) : void 0;
const anchorEntry = anchorSessionKey ? combinedSessionStore[anchorSessionKey] : void 0;
let anchorResetCutoffPromise;
const resolveAnchorResetCutoff = () => {
if (anchorResetCutoffPromise) return anchorResetCutoffPromise;
const sessionId = anchorEntry?.sessionId?.trim();
if (!recallAgentId || !sessionId || !anchorSessionKey) return Promise.resolve({ state: "invalid" });
anchorResetCutoffPromise = buildSessionEntry(`${sessionId}.jsonl`, {
agentId: recallAgentId,
sessionId,
sessionKey: anchorSessionKey,
storePath,
updatedAtMs: anchorEntry?.updatedAt
}).then(readSessionResetRecallCutoffMetadata).catch(() => ({ state: "invalid" }));
return anchorResetCutoffPromise;
};
const recallAuthorized = Boolean(conversationRecall && !params.sandboxed && conversationRecall.scope === "same-agent-private" && (conversationRecall.corpus === "sessions" || conversationRecall.corpus === "configured") && anchorSessionKey && isTrustedRecallRequester({
anchorSessionKey,
requesterSessionKey: params.requesterSessionKey
}) && normalizeOptionalLowercaseString(recallAgentId) === normalizeOptionalLowercaseString(scopedAgentId) && recallAgentId && isPrivateConversation({
agentId: recallAgentId,
entry: anchorEntry,
key: anchorSessionKey
}) && anchorAliasesArePrivate({
store: combinedSessionStore,
agentId: recallAgentId,
anchorSessionKey,
anchorEntry
}));
if (conversationRecall && !recallAuthorized) return conversationRecall.corpus === "configured" ? params.hits.filter((hit) => hit.source !== "sessions") : [];
const isSessionKeyAllowed = (key, allowAnchorTranscript = false) => {
if (!conversationRecall || !anchorSessionKey || !recallAgentId) {
const visibilityKey = scopedAgentId && isGlobalSessionKeyForSharedScope(params.cfg, key) ? `agent:${scopedAgentId}:global` : key;
return trustedAgentScope || guard?.check(visibilityKey).allowed === true;
}
const candidateEntry = combinedSessionStore[key];
if (!allowAnchorTranscript && (key === anchorSessionKey || isSameStoredTranscript(anchorEntry, candidateEntry))) return false;
const candidateAgentId = resolveSessionAgentIdStrict({
sessionKey: key,
config: params.cfg
});
if (normalizeOptionalLowercaseString(candidateAgentId) !== normalizeOptionalLowercaseString(recallAgentId)) return false;
return isPrivateConversation({
agentId: recallAgentId,
entry: candidateEntry,
key
});
};
const expandRecallAliasKeys = (keys) => {
const expanded = new Set(keys);
for (const key of keys) {
const entry = combinedSessionStore[key];
if (!entry) continue;
for (const [candidateKey, candidateEntry] of Object.entries(combinedSessionStore)) if (isSameStoredTranscript(entry, candidateEntry)) expanded.add(candidateKey);
}
return [...expanded];
};
const areSessionKeysAllowed = (keys, allowAnchorTranscript = false) => {
return conversationRecall ? expandRecallAliasKeys(keys).every((key) => isSessionKeyAllowed(key, allowAnchorTranscript)) : keys.some((key) => isSessionKeyAllowed(key));
};
const next = [];
for (const hit of params.hits) {
if (hit.source !== "sessions") {
if (!conversationRecall || conversationRecall.corpus === "configured") next.push(hit);
continue;
}
if (!trustedAgentScope && (!params.requesterSessionKey || !guard && !conversationRecall)) continue;
const identity = extractTranscriptIdentityFromSessionsMemoryHit(hit.path);
if (!identity) continue;
const archiveReason = readSessionArchiveReasonFromHitPath(hit.path);
if (conversationRecall && archiveReason === "deleted") continue;
const normalizedScopedAgentId = normalizeOptionalLowercaseString(scopedAgentId);
const normalizedOwnerAgentId = normalizeOptionalLowercaseString(identity.ownerAgentId);
if (normalizedScopedAgentId && normalizedOwnerAgentId && normalizedOwnerAgentId !== normalizedScopedAgentId) continue;
const sameAgentLiveOwnerId = !identity.archived && normalizedScopedAgentId && normalizedOwnerAgentId === normalizedScopedAgentId ? normalizedOwnerAgentId : void 0;
const archivedOwnerAgentId = Boolean(identity.archived && identity.ownerAgentId && (!scopedAgentId || normalizeOptionalLowercaseString(identity.ownerAgentId) === normalizeOptionalLowercaseString(scopedAgentId))) ? identity.ownerAgentId ?? scopedAgentId : void 0;
const canonicalArchive = identity.archived ? archivedSessionsByName.get(hit.path.replace(/\\/g, "/").split("/").at(-1) ?? "") : void 0;
const resolvedKeys = canonicalArchive?.sessionKey ? [canonicalArchive.sessionKey] : resolveTranscriptStemToSessionKeys({
store: combinedSessionStore,
stem: canonicalArchive?.sessionId ?? identity.stem,
...archivedOwnerAgentId ? { archivedOwnerAgentId } : {}
});
const keys = filterSessionKeysByScopedAgent({
cfg: params.cfg,
scopedAgentId,
keys: resolvedKeys
});
if (keys.length === 0) {
if (sameAgentLiveOwnerId && (visibility === "agent" || visibility === "all") && !conversationRecall) next.push(hit);
continue;
}
let allowResetAnchor = false;
const anchorSessionId = anchorEntry?.sessionId?.trim();
if (conversationRecall && !identity.archived && recallAgentId && anchorSessionId && identity.stem === anchorSessionId && normalizedOwnerAgentId === normalizeOptionalLowercaseString(recallAgentId)) {
const cutoff = await resolveAnchorResetCutoff();
allowResetAnchor = cutoff?.state === "valid" && hit.endLine < cutoff.cutoffLine;
}
if (!areSessionKeysAllowed(keys, archiveReason === "reset" || allowResetAnchor)) continue;
next.push(hit);
}
return next;
}
//#endregion
export { filterMemorySearchHitsBySessionVisibility as t };