| 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(), |
| }, |
| ); |
| } |
|
|