openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
137 lines (136 loc) • 8.09 kB
JavaScript
import { y as parseDateStringTimestampMs } from "./number-coercion-CLj0HTDM.js";
import { a as getNodeSqliteKysely, i as executeSqliteQueryTakeFirstSync, o as iterateSqliteQuerySync, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { _t as coerceRequiredSqliteNumber } from "./openclaw-state-db-BRTnL-D8.js";
import { g as runOpenClawAgentWriteTransaction } from "./openclaw-agent-db-CWtDoRbC.js";
import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-CHjf8FxN.js";
import { m as toDatabaseOptions, s as resolveSqliteReadScope } from "./session-accessor.sqlite-scope-2KfMzb44.js";
import "./paths-DBZSw_GC.js";
//#region src/trajectory/runtime-store.sqlite.ts
const TRAJECTORY_RUNTIME_RETENTION_MAX_AGE_MS = 12096e5;
const TRAJECTORY_RUNTIME_GLOBAL_MAX_BYTES = 536870912;
const TRAJECTORY_RUNTIME_GLOBAL_SWEEP_INTERVAL_MS = 36e5;
const TRAJECTORY_RUNTIME_DELETE_RUN_BATCH_SIZE = 100;
const lastGlobalSweepAtByDatabase = /* @__PURE__ */ new WeakMap();
/** Appends runtime trajectory events to the per-agent SQLite session store. */
function appendSqliteTrajectoryRuntimeEvents(scope, events) {
if (events.length === 0) return;
const options = toDatabaseOptions(resolveSqliteReadScope(scope));
const maxRuntimeBytes = Math.max(1, Math.floor(scope.maxRuntimeBytes ?? 10485760));
const maxGlobalRuntimeBytes = Math.max(1, Math.floor(scope.maxGlobalRuntimeBytes ?? TRAJECTORY_RUNTIME_GLOBAL_MAX_BYTES));
const sweepAt = Date.now();
let sweptDatabase;
runOpenClawAgentWriteTransaction((database) => {
const db = getTrajectoryKysely(database.db);
let seq = readNextTrajectorySeq(database, scope.sessionId);
for (const event of events) {
const eventJson = JSON.stringify(event);
executeSqliteQuerySync(database.db, db.insertInto("trajectory_runtime_events").values({
session_id: scope.sessionId,
seq,
run_id: event.runId ?? null,
event_json: eventJson,
created_at: readTrajectoryEventTimestamp(event) ?? Date.now()
}));
seq += 1;
}
trimSqliteTrajectoryRuntimeWindow(database, scope.sessionId, maxRuntimeBytes);
const lastSweptAt = lastGlobalSweepAtByDatabase.get(database);
if (lastSweptAt === void 0 || sweepAt < lastSweptAt || sweepAt - lastSweptAt >= TRAJECTORY_RUNTIME_GLOBAL_SWEEP_INTERVAL_MS) {
sweepSqliteTrajectoryRuntimeRetention(database, scope.sessionId, sweepAt, maxGlobalRuntimeBytes);
sweptDatabase = database;
}
}, options);
if (sweptDatabase) lastGlobalSweepAtByDatabase.set(sweptDatabase, sweepAt);
}
/** Loads runtime trajectory events from per-agent SQLite rows in storage order. */
async function loadSqliteTrajectoryRuntimeEvents(scope) {
return loadSqliteTrajectoryRuntimeEventsSync(scope);
}
/** Loads runtime trajectory events synchronously for CLI and export paths. */
function loadSqliteTrajectoryRuntimeEventsSync(scope) {
return loadSqliteTrajectoryRuntimeEventRowsSync(scope).map((row) => row.event);
}
/** Loads runtime trajectory event rows with storage seqs for follow/export cursors. */
function loadSqliteTrajectoryRuntimeEventRowsSync(scope) {
const read = withOpenClawAgentDatabaseReadOnly((database) => {
const db = getTrajectoryKysely(database.db);
const tailEvents = scope.tailEvents !== void 0 && Number.isFinite(scope.tailEvents) ? Math.max(0, Math.floor(scope.tailEvents)) : void 0;
let query = db.selectFrom("trajectory_runtime_events").select(["seq", "event_json"]).where("session_id", "=", scope.sessionId).orderBy("seq", tailEvents === void 0 ? "asc" : "desc");
const afterSeq = scope.afterSeq;
if (afterSeq !== void 0 && Number.isFinite(afterSeq)) query = query.where("seq", ">", Math.floor(afterSeq));
const normalizedMaxEvents = scope.maxEvents !== void 0 && Number.isFinite(scope.maxEvents) ? Math.max(0, Math.floor(scope.maxEvents)) : void 0;
const maxEvents = tailEvents === void 0 ? normalizedMaxEvents : normalizedMaxEvents === void 0 ? tailEvents : Math.min(tailEvents, normalizedMaxEvents);
if (maxEvents !== void 0 && Number.isFinite(maxEvents)) query = query.limit(Math.max(0, Math.floor(maxEvents)));
const rows = executeSqliteQuerySync(database.db, query).rows.map((row) => ({
event: JSON.parse(row.event_json),
seq: row.seq
}));
return tailEvents === void 0 ? rows : rows.toReversed();
}, toDatabaseOptions(resolveSqliteReadScope(scope)));
return read.found ? read.value : [];
}
function sweepSqliteTrajectoryRuntimeRetention(database, currentSessionId, now, maxGlobalRuntimeBytes) {
const runs = readSqliteTrajectoryRuntimeRuns(database);
let retainedBytes = runs.reduce((total, run) => total + run.runtimeBytes, 0);
const cutoff = now - TRAJECTORY_RUNTIME_RETENTION_MAX_AGE_MS;
const deletedRuns = /* @__PURE__ */ new Set();
for (const run of runs) if (run.sessionId !== currentSessionId && run.newestCreatedAt < cutoff) {
deletedRuns.add(run);
retainedBytes -= run.runtimeBytes;
}
for (const run of runs.toSorted(compareTrajectoryRuntimeRunsOldestFirst)) {
if (retainedBytes <= maxGlobalRuntimeBytes) break;
if (run.sessionId === currentSessionId || deletedRuns.has(run)) continue;
deletedRuns.add(run);
retainedBytes -= run.runtimeBytes;
}
deleteSqliteTrajectoryRuntimeRuns(database, [...deletedRuns]);
}
function readSqliteTrajectoryRuntimeRuns(database) {
const db = getTrajectoryKysely(database.db);
return executeSqliteQuerySync(database.db, db.selectFrom("trajectory_runtime_events").select(["session_id", "run_id"]).select((eb) => [eb.fn.max("created_at").as("newest_created_at"), eb.fn.sum(eb(eb.fn("octet_length", ["event_json"]), "+", 1)).as("runtime_bytes")]).groupBy(["session_id", "run_id"])).rows.map((row) => ({
newestCreatedAt: coerceRequiredSqliteNumber(row.newest_created_at),
runId: row.run_id,
runtimeBytes: coerceRequiredSqliteNumber(row.runtime_bytes),
sessionId: row.session_id
}));
}
function deleteSqliteTrajectoryRuntimeRuns(database, runs) {
const db = getTrajectoryKysely(database.db);
for (let index = 0; index < runs.length; index += TRAJECTORY_RUNTIME_DELETE_RUN_BATCH_SIZE) {
const batch = runs.slice(index, index + TRAJECTORY_RUNTIME_DELETE_RUN_BATCH_SIZE);
executeSqliteQuerySync(database.db, db.deleteFrom("trajectory_runtime_events").where((eb) => eb.or(batch.map((run) => eb.and([eb("session_id", "=", run.sessionId), run.runId === null ? eb("run_id", "is", null) : eb("run_id", "=", run.runId)])))));
}
}
function compareTrajectoryRuntimeRunsOldestFirst(left, right) {
return left.newestCreatedAt - right.newestCreatedAt || left.sessionId.localeCompare(right.sessionId) || (left.runId ?? "").localeCompare(right.runId ?? "");
}
function getTrajectoryKysely(database) {
return getNodeSqliteKysely(database);
}
function readNextTrajectorySeq(database, sessionId) {
const db = getTrajectoryKysely(database.db);
const row = executeSqliteQueryTakeFirstSync(database.db, db.selectFrom("trajectory_runtime_events").select((eb) => eb.fn.max("seq").as("max_seq")).where("session_id", "=", sessionId));
if (row?.max_seq === null || row?.max_seq === void 0) return 0;
return coerceRequiredSqliteNumber(row.max_seq) + 1;
}
function trimSqliteTrajectoryRuntimeWindow(database, sessionId, maxRuntimeBytes) {
const db = getTrajectoryKysely(database.db);
const rows = iterateSqliteQuerySync(database.db, db.selectFrom("trajectory_runtime_events").select(["seq", "event_json"]).where("session_id", "=", sessionId).orderBy("seq", "desc"));
let retainedBytes = 0;
let removeThroughSeq;
for (const row of rows) {
retainedBytes += Buffer.byteLength(row.event_json, "utf8") + 1;
if (!(retainedBytes <= maxRuntimeBytes)) {
removeThroughSeq = row.seq;
break;
}
}
if (removeThroughSeq === void 0) return;
executeSqliteQuerySync(database.db, db.deleteFrom("trajectory_runtime_events").where("session_id", "=", sessionId).where("seq", "<=", removeThroughSeq));
}
function readTrajectoryEventTimestamp(event) {
return parseDateStringTimestampMs(event.ts);
}
//#endregion
export { loadSqliteTrajectoryRuntimeEventRowsSync as n, loadSqliteTrajectoryRuntimeEvents as r, appendSqliteTrajectoryRuntimeEvents as t };