Files
Moltbot/src/acp/runtime/session-meta.ts
Gustavo Madeira Santana 46f0bfc55b Gateway: harden custom session-store discovery (#44176)
Merged via squash.

Prepared head SHA: 52ebbf5188b47386f2a78ac4715993bc082e911b
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-12 16:44:46 +00:00

171 lines
4.2 KiB
TypeScript

import type { OpenClawConfig } from "../../config/config.js";
import { loadConfig } from "../../config/config.js";
import {
loadSessionStore,
resolveAllAgentSessionStoreTargets,
resolveStorePath,
updateSessionStore,
} from "../../config/sessions.js";
import {
mergeSessionEntry,
type SessionAcpMeta,
type SessionEntry,
} from "../../config/sessions/types.js";
import { parseAgentSessionKey } from "../../routing/session-key.js";
export type AcpSessionStoreEntry = {
cfg: OpenClawConfig;
storePath: string;
sessionKey: string;
storeSessionKey: string;
entry?: SessionEntry;
acp?: SessionAcpMeta;
storeReadFailed?: boolean;
};
function resolveStoreSessionKey(store: Record<string, SessionEntry>, sessionKey: string): string {
const normalized = sessionKey.trim();
if (!normalized) {
return "";
}
if (store[normalized]) {
return normalized;
}
const lower = normalized.toLowerCase();
if (store[lower]) {
return lower;
}
for (const key of Object.keys(store)) {
if (key.toLowerCase() === lower) {
return key;
}
}
return lower;
}
export function resolveSessionStorePathForAcp(params: {
sessionKey: string;
cfg?: OpenClawConfig;
}): { cfg: OpenClawConfig; storePath: string } {
const cfg = params.cfg ?? loadConfig();
const parsed = parseAgentSessionKey(params.sessionKey);
const storePath = resolveStorePath(cfg.session?.store, {
agentId: parsed?.agentId,
});
return { cfg, storePath };
}
export function readAcpSessionEntry(params: {
sessionKey: string;
cfg?: OpenClawConfig;
}): AcpSessionStoreEntry | null {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) {
return null;
}
const { cfg, storePath } = resolveSessionStorePathForAcp({
sessionKey,
cfg: params.cfg,
});
let store: Record<string, SessionEntry>;
let storeReadFailed = false;
try {
store = loadSessionStore(storePath);
} catch {
storeReadFailed = true;
store = {};
}
const storeSessionKey = resolveStoreSessionKey(store, sessionKey);
const entry = store[storeSessionKey];
return {
cfg,
storePath,
sessionKey,
storeSessionKey,
entry,
acp: entry?.acp,
storeReadFailed,
};
}
export async function listAcpSessionEntries(params: {
cfg?: OpenClawConfig;
env?: NodeJS.ProcessEnv;
}): Promise<AcpSessionStoreEntry[]> {
const cfg = params.cfg ?? loadConfig();
const storeTargets = await resolveAllAgentSessionStoreTargets(
cfg,
params.env ? { env: params.env } : undefined,
);
const entries: AcpSessionStoreEntry[] = [];
for (const target of storeTargets) {
const storePath = target.storePath;
let store: Record<string, SessionEntry>;
try {
store = loadSessionStore(storePath);
} catch {
continue;
}
for (const [sessionKey, entry] of Object.entries(store)) {
if (!entry?.acp) {
continue;
}
entries.push({
cfg,
storePath,
sessionKey,
storeSessionKey: sessionKey,
entry,
acp: entry.acp,
});
}
}
return entries;
}
export async function upsertAcpSessionMeta(params: {
sessionKey: string;
cfg?: OpenClawConfig;
mutate: (
current: SessionAcpMeta | undefined,
entry: SessionEntry | undefined,
) => SessionAcpMeta | null | undefined;
}): Promise<SessionEntry | null> {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) {
return null;
}
const { storePath } = resolveSessionStorePathForAcp({
sessionKey,
cfg: params.cfg,
});
return await updateSessionStore(
storePath,
(store) => {
const storeSessionKey = resolveStoreSessionKey(store, sessionKey);
const currentEntry = store[storeSessionKey];
const nextMeta = params.mutate(currentEntry?.acp, currentEntry);
if (nextMeta === undefined) {
return currentEntry ?? null;
}
if (nextMeta === null && !currentEntry) {
return null;
}
const nextEntry = mergeSessionEntry(currentEntry, {
acp: nextMeta ?? undefined,
});
if (nextMeta === null) {
delete nextEntry.acp;
}
store[storeSessionKey] = nextEntry;
return nextEntry;
},
{
activeSessionKey: sessionKey.toLowerCase(),
},
);
}