




















@@ -4,7 +4,11 @@ import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/st
44import type { Insertable, Selectable } from "kysely";
55import { getRuntimeConfig } from "../../config/config.js";
66import { resolveStorePath } from "../../config/sessions/paths.js";
7-import { loadSessionStore } from "../../config/sessions/store-load.js";
7+import {
8+listSessionEntries,
9+patchSessionEntryWithKey,
10+type SessionEntrySummary,
11+} from "../../config/sessions/session-accessor.js";
812import {
913mergeSessionEntry,
1014type AcpSessionRuntimeOptions,
@@ -43,30 +47,24 @@ type AcpSessionsTable = OpenClawStateKyselyDatabase["acp_sessions"];
4347type AcpSessionMetaDatabase = Pick<OpenClawStateKyselyDatabase, "acp_sessions">;
4448type AcpSessionRow = Selectable<AcpSessionsTable>;
454946-let sessionStoreRuntimePromise:
47-| Promise<typeof import("../../config/sessions/store.runtime.js")>
48-| undefined;
49-50-function loadSessionStoreRuntime() {
51-sessionStoreRuntimePromise ??= import("../../config/sessions/store.runtime.js");
52-return sessionStoreRuntimePromise;
53-}
54-55-function resolveStoreSessionKey(store: Record<string, SessionEntry>, sessionKey: string): string {
50+function resolveStoreSessionKey(
51+entries: readonly SessionEntrySummary[],
52+sessionKey: string,
53+): string {
5654const normalized = sessionKey.trim();
5755if (!normalized) {
5856return "";
5957}
60-if (store[normalized]) {
58+if (entries.some((entry) => entry.sessionKey === normalized)) {
6159return normalized;
6260}
6361const lower = normalizeLowercaseStringOrEmpty(normalized);
64-if (store[lower]) {
62+if (entries.some((entry) => entry.sessionKey === lower)) {
6563return lower;
6664}
67-for (const key of Object.keys(store)) {
68-if (normalizeLowercaseStringOrEmpty(key) === lower) {
69-return key;
65+for (const entry of entries) {
66+if (normalizeLowercaseStringOrEmpty(entry.sessionKey) === lower) {
67+return entry.sessionKey;
7068}
7169}
7270return lower;
@@ -371,12 +369,13 @@ function readSessionEntryFromStore(params: {
371369env: params.env,
372370});
373371try {
374-const store = loadSessionStore(
372+const entries = listSessionEntries({
375373 storePath,
376-params.clone === false ? { clone: false } : undefined,
377-);
378-const storeSessionKey = resolveStoreSessionKey(store, params.sessionKey);
379-return { cfg, storePath, storeSessionKey, entry: store[storeSessionKey] };
374+ ...(params.clone === false ? { clone: false } : {}),
375+});
376+const storeSessionKey = resolveStoreSessionKey(entries, params.sessionKey);
377+const entry = entries.find((candidate) => candidate.sessionKey === storeSessionKey)?.entry;
378+return { cfg, storePath, storeSessionKey, entry };
380379} catch {
381380return {
382381 cfg,
@@ -437,14 +436,19 @@ export async function listAcpSessionEntries(params: {
437436 cfg,
438437env: params.env,
439438});
440-let store: Record<string, SessionEntry>;
439+let sessionEntries: SessionEntrySummary[];
441440try {
442-store = loadSessionStore(storePath, params.clone === false ? { clone: false } : undefined);
441+sessionEntries = listSessionEntries({
442+ storePath,
443+ ...(params.clone === false ? { clone: false } : {}),
444+});
443445} catch {
444446continue;
445447}
446-const storeSessionKey = resolveStoreSessionKey(store, sessionKey);
447-const entry = store[storeSessionKey];
448+const storeSessionKey = resolveStoreSessionKey(sessionEntries, sessionKey);
449+const entry = sessionEntries.find(
450+(candidate) => candidate.sessionKey === storeSessionKey,
451+)?.entry;
448452if (!entry || !acpSessionRowMatchesEntry(row, entry)) {
449453continue;
450454}
@@ -518,63 +522,86 @@ export async function upsertAcpSessionMeta(params: {
518522current,
519523current ? mergeAcpForReturn(preparedEntry, current) : entry,
520524);
521-if (nextMeta === null) {
522-executeSqliteQuerySync(
523-database.db,
524-getAcpSessionKysely(database.db)
525-.deleteFrom("acp_sessions")
526-.where("session_key", "=", storageSessionKey),
527-);
528-return;
529-}
530-if (nextMeta !== undefined) {
531-upsertAcpSessionMetaRow(
532-database.db,
533-bindAcpSessionMeta({
534-sessionKey: storageSessionKey,
535-sessionId: preparedEntry.sessionId,
536-meta: nextMeta,
537- updatedAt,
538-}),
539-);
540-}
541525},
542526{ env: params.env, path: params.databasePath },
543527);
544-if (nextMeta === undefined) {
528+const metaToPersist = nextMeta;
529+if (metaToPersist === undefined) {
545530return current ? mergeAcpForReturn(entry, current) : (entry ?? null);
546531}
547-if (nextMeta === null) {
548-if (!entry) {
549-return null;
550-}
551-const { updateSessionStore } = await loadSessionStoreRuntime();
552-return await updateSessionStore(
553-storeEntry.storePath,
554-(store) => {
555-const storeSessionKey = resolveStoreSessionKey(store, storageSessionKey);
556-const next = { ...(store[storeSessionKey] ?? entry) };
557-delete next.acp;
558-store[storeSessionKey] = next;
559-return next;
532+if (metaToPersist === null) {
533+const patched = entry
534+ ? await patchSessionEntryWithKey(
535+{ storePath: storeEntry.storePath, sessionKey: storageSessionKey },
536+(currentEntry) => {
537+const next = { ...currentEntry };
538+delete next.acp;
539+return next;
540+},
541+{
542+ ...sessionStoreUpdateOptions({ ...params, sessionKey: storageSessionKey }),
543+replaceEntry: true,
544+},
545+)
546+ : null;
547+runOpenClawStateWriteTransaction(
548+(database) => {
549+const sessionKeysToDelete = new Set([storageSessionKey]);
550+if (patched?.sessionKey) {
551+sessionKeysToDelete.add(patched.sessionKey);
552+}
553+for (const key of sessionKeysToDelete) {
554+executeSqliteQuerySync(
555+database.db,
556+getAcpSessionKysely(database.db)
557+.deleteFrom("acp_sessions")
558+.where("session_key", "=", key),
559+);
560+}
560561},
561-sessionStoreUpdateOptions({ ...params, sessionKey: storageSessionKey }),
562+{ env: params.env, path: params.databasePath },
562563);
564+return patched?.entry ?? null;
563565}
564-const { updateSessionStore } = await loadSessionStoreRuntime();
565-const persisted = await updateSessionStore(
566-storeEntry.storePath,
567-(store) => {
568-const storeSessionKey = resolveStoreSessionKey(store, storageSessionKey);
569-const next = mergeSessionEntry(store[storeSessionKey], {
570-sessionId: preparedEntry?.sessionId,
566+const persisted = await patchSessionEntryWithKey(
567+{ storePath: storeEntry.storePath, sessionKey: storageSessionKey },
568+(currentEntry) => {
569+const next = mergeSessionEntry(currentEntry, {
571570 updatedAt,
572571});
573572delete next.acp;
574-store[storeSessionKey] = next;
575573return next;
576574},
577-sessionStoreUpdateOptions({ ...params, sessionKey: storageSessionKey }),
575+{
576+ ...sessionStoreUpdateOptions({ ...params, sessionKey: storageSessionKey }),
577+fallbackEntry: preparedEntry,
578+replaceEntry: true,
579+},
580+);
581+if (!persisted) {
582+return null;
583+}
584+runOpenClawStateWriteTransaction(
585+(database) => {
586+upsertAcpSessionMetaRow(
587+database.db,
588+bindAcpSessionMeta({
589+sessionKey: persisted.sessionKey,
590+sessionId: persisted.entry.sessionId,
591+meta: metaToPersist,
592+ updatedAt,
593+}),
594+);
595+if (persisted.sessionKey !== storageSessionKey) {
596+executeSqliteQuerySync(
597+database.db,
598+getAcpSessionKysely(database.db)
599+.deleteFrom("acp_sessions")
600+.where("session_key", "=", storageSessionKey),
601+);
602+}
603+},
604+{ env: params.env, path: params.databasePath },
578605);
579-return mergeAcpForReturn(persisted, nextMeta);
606+return mergeAcpForReturn(persisted.entry, metaToPersist);
580607}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。