openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
119 lines (118 loc) • 7.11 kB
JavaScript
import { l as runSqliteDeferredTransactionSync } from "./node-sqlite-BpQX3W0e.js";
import { i as executeSqliteQueryTakeFirstSync, o as iterateSqliteQuerySync, s as prepareSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { n as iterateSessionContextEntries, r as iterateSessionContextMessages } from "./session-CcAchsL_.js";
import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-CHjf8FxN.js";
import { d as resolveSqliteTranscriptReadScope, i as getSessionKysely, m as toDatabaseOptions } from "./session-accessor.sqlite-scope-2KfMzb44.js";
import { c as scanSessionTranscriptTree, d as selectSessionTranscriptTreePathNodes } from "./transcript-tree-BH69pMSi.js";
import { i as runWithSessionTranscriptReadFence, o as projectModelContextEventSql, r as resolveSqliteSessionTranscriptReadFence, s as projectModelContextNavigationSql, t as SessionTranscriptReadFenceError } from "./session-transcript-read-fence-CeDmfWcC.js";
import { sql } from "kysely";
import { isCompactionReplayCheckpoint } from "@openclaw/ai/transports";
//#region src/config/sessions/session-accessor.sqlite-model-context.ts
const MODEL_CONTEXT_PAYLOAD_BATCH_SIZE = 400;
function readContextVersion(database, sessionId) {
const db = getSessionKysely(database.db);
return executeSqliteQueryTakeFirstSync(database.db, db.selectFrom("transcript_events").select((eb) => [eb.fn.max("seq").as("rawSeq"), eb.selectFrom("transcript_rewrite_watermarks").select("generation").where("session_id", "=", sessionId).as("generation")]).where("session_id", "=", sessionId));
}
/** Unadmitted context must still describe this session when an async read returns. */
function validateSessionTranscriptContextVersion(scope, version) {
const resolved = resolveSqliteTranscriptReadScope(scope);
const result = withOpenClawAgentDatabaseReadOnly((database) => readContextVersion(database, resolved.sessionId), toDatabaseOptions(resolved), { throwOnMissingTable: true });
const current = result.found ? result.value : void 0;
if (current?.generation !== version?.generation || current?.rawSeq !== version?.rawSeq) throw new SessionTranscriptReadFenceError("Session transcript changed during context read");
}
/** Revalidate admission after an asynchronous read before accepting its detached result. */
function validateSessionTranscriptContextAdmission(scope, admission) {
if (!admission) return;
const resolved = resolveSqliteTranscriptReadScope(scope);
const result = runWithSessionTranscriptReadFence(admission, () => withOpenClawAgentDatabaseReadOnly((database) => resolveSqliteSessionTranscriptReadFence({
database,
...resolved
}), toDatabaseOptions(resolved), { throwOnMissingTable: true }));
if (!result.found || !result.value) throw new SessionTranscriptReadFenceError("Current-turn transcript admission is no longer readable");
}
/** Read a transient context without opening the writer lifecycle or copying native evidence. */
function readSessionTranscriptModelContext(scope) {
const result = withTranscriptContextSnapshot(scope, ({ header, entries, readModelEntries, version }) => {
const requests = [];
for (const { entry, context } of iterateSessionContextEntries(entries)) {
const omitCheckpoint = context !== "current" && entry.type === "message" && entry.message.role === "assistant" && isCompactionReplayCheckpoint(entry.message.providerReplay);
requests.push({
entry,
omitCheckpoint
});
}
const payloads = readModelEntries(requests);
return {
events: [...header ? [header] : [], ...entries.map((entry) => payloads.get(entry) ?? entry)],
version
};
});
return result.found ? result.value : { events: [] };
}
/** Consume full-fidelity context lazily inside one read snapshot, never retaining raw history. */
function readSessionTranscriptContextMessages(scope, read) {
const result = withTranscriptContextSnapshot(scope, ({ header, entries, readEntry, version }) => {
const messages = iterateSessionContextMessages(entries, readEntry);
try {
return read(messages, header, version);
} finally {
messages.return(void 0);
}
});
return result.found ? result.value : read([], void 0);
}
function withTranscriptContextSnapshot(scope, read) {
const resolved = resolveSqliteTranscriptReadScope(scope);
return withOpenClawAgentDatabaseReadOnly((database) => runSqliteDeferredTransactionSync(database.db, () => {
const db = getSessionKysely(database.db);
const fence = resolveSqliteSessionTranscriptReadFence({
database,
...resolved
});
const version = readContextVersion(database, resolved.sessionId);
const base = db.selectFrom("transcript_events").where("session_id", "=", resolved.sessionId).$if(fence !== void 0, (query) => query.where("seq", "<", fence.beforeRawSeq));
const header = executeSqliteQueryTakeFirstSync(database.db, base.select("event_json").where(sql`json_extract(event_json, '$.type')`, "=", "session").orderBy("seq", "asc").limit(1));
const tree = scanSessionTranscriptTree((function* () {
for (const row of iterateSqliteQuerySync(database.db, base.select((eb) => ["seq", projectModelContextNavigationSql(eb.ref("event_json")).as("navigation_json")]).orderBy("seq", "asc"))) yield {
...JSON.parse(row.navigation_json),
seq: row.seq
};
})());
const entries = selectSessionTranscriptTreePathNodes(tree, tree.leafId).map(({ entry, parentId }) => {
entry.parentId = parentId;
return entry;
});
const readPayload = prepareSqliteQuerySync(database.db, (parameter) => base.select("event_json").where("seq", "=", parameter((row) => row.seq)));
return read({
header: header ? JSON.parse(header.event_json) : void 0,
entries,
version,
readEntry: (entry) => {
const row = readPayload(entry).rows[0];
return {
...JSON.parse(row.event_json),
parentId: entry.parentId
};
},
readModelEntries: (requests) => {
const payloads = /* @__PURE__ */ new Map();
for (let offset = 0; offset < requests.length; offset += MODEL_CONTEXT_PAYLOAD_BATCH_SIZE) {
const batch = requests.slice(offset, offset + MODEL_CONTEXT_PAYLOAD_BATCH_SIZE);
const bySeq = new Map(batch.map(({ entry }) => [entry.seq, entry]));
const omitted = batch.filter(({ omitCheckpoint }) => omitCheckpoint).map(({ entry }) => entry.seq);
const query = base.select((eb) => ["seq", projectModelContextEventSql(eb.ref("event_json"), omitted.length > 0 ? eb.case().when("seq", "in", omitted).then(1).else(0).end() : eb.val(0)).as("event_json")]).where("seq", "in", [...bySeq.keys()]);
for (const row of iterateSqliteQuerySync(database.db, query)) {
const entry = bySeq.get(row.seq);
payloads.set(entry, {
...JSON.parse(row.event_json),
parentId: entry.parentId
});
}
}
return payloads;
}
});
}, { operationLabel: "session context snapshot read" }), toDatabaseOptions(resolved), { throwOnMissingTable: true });
}
//#endregion
export { validateSessionTranscriptContextVersion as i, readSessionTranscriptModelContext as n, validateSessionTranscriptContextAdmission as r, readSessionTranscriptContextMessages as t };