UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

119 lines (118 loc) 7.11 kB
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 };