openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
410 lines (409 loc) • 16.8 kB
JavaScript
import "./src-vebZIeLe.js";
import { n as safeParseJsonRecord } from "./json-coercion-AulM0PZ6.js";
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { n as getRuntimeConfig } from "./io.runtime-B9iJRs3w.js";
import { r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { r as withExistingOpenClawStateDatabaseReadOnly } from "./openclaw-state-db-readonly-BRgmrGHt.js";
import { s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js";
import "./config-Cs0XXL3x.js";
import "./session-accessor-YsytfDtG.js";
import { r as mergeSessionEntry } from "./types-BHV0IaPK.js";
import { r as patchSessionEntryWithKey } from "./session-accessor.entry-CQ2WPpfn.js";
import { a as buildAcpDatabaseSessionKey, c as parseAcpDatabaseSessionKeyCandidates, d as selectAcpSessionRowForStoreEntry, i as acpSessionRowMatchesEntry, l as resolveReadableAcpSessionRow, n as resolveSessionStorePathForAcp, o as getAcpSessionKysely, r as resolveStoreEntryForSessionKey, s as legacyAcpDatabaseSessionKeys, t as readSessionEntryFromStore, u as selectAcpSessionRow } from "./session-meta-store-BkOt1zC9.js";
//#region src/acp/runtime/session-meta-legacy-cleanup.ts
async function clearLegacyEmbeddedAcpMetadata(params) {
const sessionKeys = new Set(Array.from(params.sessionKeys, (sessionKey) => sessionKey?.trim()).filter((sessionKey) => Boolean(sessionKey)));
for (const sessionKey of sessionKeys) await patchSessionEntryWithKey({
storePath: params.storePath,
agentId: params.agentId,
sessionKey
}, (entry) => {
if (!entry.acp) return null;
const next = { ...entry };
delete next.acp;
return next;
}, {
replaceEntry: true,
skipMaintenance: true
});
}
//#endregion
//#region src/acp/runtime/session-meta.ts
function rowToAcpSessionMeta(row) {
const identity = safeParseJsonRecord(row.identity_json ?? "");
const runtimeOptions = safeParseJsonRecord(row.runtime_options_json ?? "");
return {
backend: row.backend,
agent: row.agent,
runtimeSessionName: row.runtime_session_name,
...identity ? { identity } : {},
mode: row.mode === "oneshot" ? "oneshot" : "persistent",
...runtimeOptions ? { runtimeOptions } : {},
...row.cwd != null ? { cwd: row.cwd } : {},
state: row.state === "running" || row.state === "error" ? row.state : "idle",
lastActivityAt: row.last_activity_at,
...row.last_error != null ? { lastError: row.last_error } : {}
};
}
function bindAcpSessionMeta(params) {
return {
session_key: params.sessionKey,
session_id: params.lifecycleRevision ?? params.sessionId ?? null,
backend: params.meta.backend,
agent: params.meta.agent,
runtime_session_name: params.meta.runtimeSessionName,
identity_json: params.meta.identity ? JSON.stringify(params.meta.identity) : null,
mode: params.meta.mode,
runtime_options_json: params.meta.runtimeOptions ? JSON.stringify(params.meta.runtimeOptions) : null,
cwd: params.meta.cwd ?? null,
state: params.meta.state,
last_activity_at: params.meta.lastActivityAt,
last_error: params.meta.lastError ?? null,
updated_at: params.updatedAt
};
}
function readAcpSessionMeta(params) {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) return;
const storeEntry = readSessionEntryFromStore({
sessionKey,
agentId: params.agentId,
cfg: params.cfg,
env: params.env,
clone: false
});
if (!storeEntry.storePath) return;
const row = withExistingOpenClawStateDatabaseReadOnly(({ db }) => resolveReadableAcpSessionRow({
row: selectAcpSessionRowForStoreEntry(db, storeEntry.storeSessionKey, storeEntry.agentId, storeEntry.cfg, storeEntry.entry),
entry: storeEntry.entry
}), {
env: params.env,
path: params.databasePath
});
if (!row) return;
return rowToAcpSessionMeta(row);
}
function readAcpSessionMetaForEntry(params) {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) return;
const row = withExistingOpenClawStateDatabaseReadOnly(({ db }) => resolveReadableAcpSessionRow({
row: selectAcpSessionRowForStoreEntry(db, sessionKey, params.agentId, params.cfg, params.entry),
entry: params.entry
}), {
env: params.env,
path: params.databasePath
});
if (!row) return;
return rowToAcpSessionMeta(row);
}
function readAcpSessionMetaBatch(params) {
const result = /* @__PURE__ */ new Map();
const entriesByKey = /* @__PURE__ */ new Map();
for (const item of params.entries) {
const rawSessionKey = item.sessionKey.trim();
const sessionKey = buildAcpDatabaseSessionKey(rawSessionKey, item.agentId);
if (!sessionKey) continue;
if (item.entry?.acp) {
result.set(item.entry, item.entry.acp);
continue;
}
const legacyKeys = legacyAcpDatabaseSessionKeys(rawSessionKey, item.agentId, params.cfg);
const entries = entriesByKey.get(sessionKey) ?? [];
entries.push({
entry: item.entry,
rawSessionKey,
legacyKeys
});
entriesByKey.set(sessionKey, entries);
}
if (entriesByKey.size === 0) return result;
withExistingOpenClawStateDatabaseReadOnly(({ db: database }) => {
const db = getAcpSessionKysely(database);
const requestedKeySet = /* @__PURE__ */ new Set();
for (const [sessionKey, entries] of entriesByKey) {
requestedKeySet.add(sessionKey);
for (const item of entries) for (const legacyKey of item.legacyKeys) requestedKeySet.add(legacyKey);
}
const requestedKeys = [...requestedKeySet];
const keyChunks = [];
for (let index = 0; index < requestedKeys.length; index += 500) keyChunks.push(requestedKeys.slice(index, index + 500));
const rows = keyChunks.flatMap((chunk) => executeSqliteQuerySync(database, db.selectFrom("acp_sessions").selectAll().where("session_key", "in", chunk)).rows);
const rowsByKey = new Map(rows.map((row) => [row.session_key, row]));
for (const [sessionKey, entries] of entriesByKey) for (const item of entries) {
const row = [sessionKey, ...item.legacyKeys].map((key) => rowsByKey.get(key)).map((candidateRow) => resolveReadableAcpSessionRow({
row: candidateRow,
entry: item.entry
})).find((candidateRow) => candidateRow !== void 0);
result.set(item.entry, row ? rowToAcpSessionMeta(row) : void 0);
}
}, {
env: params.env,
path: params.databasePath
});
return result;
}
function selectAcpSessionRows(options = {}) {
return withExistingOpenClawStateDatabaseReadOnly(({ db }) => executeSqliteQuerySync(db, getAcpSessionKysely(db).selectFrom("acp_sessions").selectAll().orderBy("last_activity_at", "desc").orderBy("session_key", "asc")).rows, options) ?? [];
}
function writeAcpSessionMetaForMigration(params) {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) return;
const row = bindAcpSessionMeta({
sessionKey,
sessionId: params.sessionId,
lifecycleRevision: params.lifecycleRevision,
meta: params.meta,
updatedAt: params.now?.() ?? Date.now()
});
runOpenClawStateWriteTransaction((database) => {
upsertAcpSessionMetaRow(database.db, row);
}, {
env: params.env,
path: params.databasePath
});
}
function repairAcpSessionMetaKeyForMigration(params) {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) return false;
let repaired = false;
runOpenClawStateWriteTransaction((database) => {
const currentRow = selectAcpSessionRow(database.db, sessionKey);
if (currentRow && acpSessionRowMatchesEntry(currentRow, params.entry)) return;
const normalizedSessionKey = normalizeLowercaseStringOrEmpty(sessionKey);
const candidateKeys = /* @__PURE__ */ new Set();
candidateKeys.add(normalizedSessionKey);
for (const candidate of params.candidateSessionKeys ?? []) {
const trimmed = typeof candidate === "string" ? candidate.trim() : "";
if (trimmed && trimmed !== sessionKey && normalizeLowercaseStringOrEmpty(trimmed) === normalizedSessionKey) candidateKeys.add(trimmed);
}
let row;
for (const candidateKey of candidateKeys) {
const candidateRow = selectAcpSessionRow(database.db, candidateKey);
if (candidateRow && acpSessionRowMatchesEntry(candidateRow, params.entry)) {
row = candidateRow;
break;
}
}
row ??= executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).selectFrom("acp_sessions").selectAll().where((eb) => eb.fn("lower", ["session_key"]), "=", normalizedSessionKey).orderBy("last_activity_at", "desc").orderBy("session_key", "asc")).rows.find((candidate) => candidate.session_key !== sessionKey && acpSessionRowMatchesEntry(candidate, params.entry));
if (!row) return;
upsertAcpSessionMetaRow(database.db, {
...row,
session_key: sessionKey,
updated_at: params.now?.() ?? Date.now()
});
executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", row.session_key));
repaired = true;
}, {
env: params.env,
path: params.databasePath
});
return repaired;
}
function upsertAcpSessionMetaRow(db, row) {
executeSqliteQuerySync(db, getAcpSessionKysely(db).insertInto("acp_sessions").values(row).onConflict((conflict) => conflict.column("session_key").doUpdateSet({
session_id: (eb) => eb.ref("excluded.session_id"),
backend: (eb) => eb.ref("excluded.backend"),
agent: (eb) => eb.ref("excluded.agent"),
runtime_session_name: (eb) => eb.ref("excluded.runtime_session_name"),
identity_json: (eb) => eb.ref("excluded.identity_json"),
mode: (eb) => eb.ref("excluded.mode"),
runtime_options_json: (eb) => eb.ref("excluded.runtime_options_json"),
cwd: (eb) => eb.ref("excluded.cwd"),
state: (eb) => eb.ref("excluded.state"),
last_activity_at: (eb) => eb.ref("excluded.last_activity_at"),
last_error: (eb) => eb.ref("excluded.last_error"),
updated_at: (eb) => eb.ref("excluded.updated_at")
})));
}
function readAcpSessionEntry(params) {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) return null;
const storeEntry = readSessionEntryFromStore(params);
if (!storeEntry.storePath) return null;
const row = withExistingOpenClawStateDatabaseReadOnly(({ db }) => resolveReadableAcpSessionRow({
row: selectAcpSessionRowForStoreEntry(db, storeEntry.storeSessionKey, storeEntry.agentId, storeEntry.cfg, storeEntry.entry),
entry: storeEntry.entry
}), {
env: params.env,
path: params.databasePath
});
const acp = row ? rowToAcpSessionMeta(row) : void 0;
return {
cfg: storeEntry.cfg,
agentId: storeEntry.agentId,
storePath: storeEntry.storePath,
sessionKey,
storeSessionKey: storeEntry.storeSessionKey,
entry: storeEntry.entry,
acp,
storeReadFailed: storeEntry.storeReadFailed
};
}
async function listAcpSessionEntries(params) {
const cfg = params.cfg ?? getRuntimeConfig();
const rows = selectAcpSessionRows({
env: params.env,
path: params.databasePath
});
const entries = [];
for (const row of rows) for (const databaseIdentity of parseAcpDatabaseSessionKeyCandidates(row.session_key)) {
const sessionKey = databaseIdentity.storeSessionKey;
const { agentId, storePath } = resolveSessionStorePathForAcp({
sessionKey,
agentId: databaseIdentity.agentId,
cfg,
env: params.env
});
if (!storePath) continue;
let storeSessionKey;
let entry;
try {
({storeSessionKey, entry} = resolveStoreEntryForSessionKey({
...agentId ? { agentId } : {},
storePath,
sessionKey,
...params.clone === false ? { clone: false } : {}
}));
} catch {
continue;
}
const readableRow = resolveReadableAcpSessionRow({
row,
entry
});
if (!entry || !readableRow) continue;
entries.push({
cfg,
agentId,
storePath,
sessionKey,
storeSessionKey,
entry,
acp: rowToAcpSessionMeta(readableRow)
});
break;
}
return entries;
}
function mergeAcpForReturn(entry, acp) {
return mergeSessionEntry(entry, { acp });
}
function sessionStoreUpdateOptions(params) {
return {
activeSessionKey: normalizeLowercaseStringOrEmpty(params.sessionKey),
...params.skipMaintenance === true ? { skipMaintenance: true } : {},
...params.takeCacheOwnership === true ? { takeCacheOwnership: true } : {}
};
}
async function upsertAcpSessionMeta(params) {
const sessionKey = params.sessionKey.trim();
if (!sessionKey) return null;
const storeEntry = readSessionEntryFromStore({
sessionKey,
agentId: params.agentId,
cfg: params.cfg,
env: params.env,
clone: false
});
if (!storeEntry.storePath) return null;
const { entry } = storeEntry;
const storageSessionKey = storeEntry.storeSessionKey;
const databaseSessionKey = buildAcpDatabaseSessionKey(storageSessionKey, storeEntry.agentId);
let current;
let currentRowKey;
let nextMeta;
let preparedEntry;
const updatedAt = params.now?.() ?? Date.now();
runOpenClawStateWriteTransaction((database) => {
const currentRow = selectAcpSessionRowForStoreEntry(database.db, storageSessionKey, storeEntry.agentId, storeEntry.cfg, entry);
currentRowKey = currentRow?.session_key;
current = currentRow ? rowToAcpSessionMeta(currentRow) : void 0;
preparedEntry = mergeSessionEntry(entry, { updatedAt });
nextMeta = params.mutate(current, current ? mergeAcpForReturn(preparedEntry, current) : entry);
}, {
env: params.env,
path: params.databasePath
});
const metaToPersist = nextMeta;
if (metaToPersist === void 0) return current ? mergeAcpForReturn(entry, current) : entry ?? null;
if (metaToPersist === null) {
const patched = entry ? await patchSessionEntryWithKey({
...storeEntry.agentId ? { agentId: storeEntry.agentId } : {},
storePath: storeEntry.storePath,
sessionKey: storageSessionKey
}, (currentEntry) => {
const next = { ...currentEntry };
delete next.acp;
return next;
}, {
...sessionStoreUpdateOptions({
...params,
sessionKey: storageSessionKey
}),
replaceEntry: true,
assertCommitAllowed: params.assertCommitAllowed
}) : null;
runOpenClawStateWriteTransaction((database) => {
params.assertCommitAllowed?.();
const sessionKeysToDelete = /* @__PURE__ */ new Set([databaseSessionKey]);
if (currentRowKey) sessionKeysToDelete.add(currentRowKey);
if (patched?.sessionKey) sessionKeysToDelete.add(buildAcpDatabaseSessionKey(patched.sessionKey, storeEntry.agentId));
for (const key of sessionKeysToDelete) executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", key));
}, {
env: params.env,
path: params.databasePath
});
await clearLegacyEmbeddedAcpMetadata({
agentId: storeEntry.agentId,
storePath: storeEntry.storePath,
sessionKeys: [storageSessionKey, patched?.sessionKey]
});
return patched?.entry ?? null;
}
const persisted = await patchSessionEntryWithKey({
...storeEntry.agentId ? { agentId: storeEntry.agentId } : {},
storePath: storeEntry.storePath,
sessionKey: storageSessionKey
}, (currentEntry) => {
const next = mergeSessionEntry(currentEntry, { updatedAt });
delete next.acp;
return next;
}, {
...sessionStoreUpdateOptions({
...params,
sessionKey: storageSessionKey
}),
fallbackEntry: preparedEntry,
replaceEntry: true,
assertCommitAllowed: params.assertCommitAllowed
});
if (!persisted) return null;
await clearLegacyEmbeddedAcpMetadata({
agentId: storeEntry.agentId,
storePath: storeEntry.storePath,
sessionKeys: [storageSessionKey, persisted.sessionKey]
});
runOpenClawStateWriteTransaction((database) => {
params.assertCommitAllowed?.();
const persistedDatabaseSessionKey = buildAcpDatabaseSessionKey(persisted.sessionKey, storeEntry.agentId);
upsertAcpSessionMetaRow(database.db, bindAcpSessionMeta({
sessionKey: persistedDatabaseSessionKey,
sessionId: persisted.entry.sessionId,
lifecycleRevision: persisted.entry.lifecycleRevision,
meta: metaToPersist,
updatedAt: persisted.entry.updatedAt
}));
if (persistedDatabaseSessionKey !== databaseSessionKey) executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", databaseSessionKey));
if (currentRowKey && currentRowKey !== persistedDatabaseSessionKey) executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", currentRowKey));
if (persistedDatabaseSessionKey !== persisted.sessionKey) {
const legacyRow = selectAcpSessionRow(database.db, persisted.sessionKey);
if (legacyRow && acpSessionRowMatchesEntry(legacyRow, persisted.entry)) executeSqliteQuerySync(database.db, getAcpSessionKysely(database.db).deleteFrom("acp_sessions").where("session_key", "=", persisted.sessionKey));
}
}, {
env: params.env,
path: params.databasePath
});
return mergeAcpForReturn(persisted.entry, metaToPersist);
}
//#endregion
export { readAcpSessionMetaForEntry as a, upsertAcpSessionMeta as c, readAcpSessionMetaBatch as i, writeAcpSessionMetaForMigration as l, readAcpSessionEntry as n, repairAcpSessionMetaKeyForMigration as o, readAcpSessionMeta as r, rowToAcpSessionMeta as s, listAcpSessionEntries as t };