openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
300 lines (299 loc) • 11.7 kB
JavaScript
import { n as assertExistingDatabaseIdentity } from "../../sqlite-worker-identity-KSR14F6C.mjs";
import { c as resolveOpenClawStateSqlitePath } from "../../openclaw-state-db.paths-DfifnSGp.mjs";
import { l as readSqliteSchemaCookie } from "../../sqlite-schema-contract-BTxZz0OV.mjs";
import { i as closeOpenClawStateDatabase, t as borrowOpenClawStateDatabaseForAsyncRead } from "../../openclaw-state-db-cache-BKCnj4of.mjs";
import { n as assertExistingOpenClawStateRuntimeSchema } from "../../openclaw-state-db-existing-schema-C4QR2GGf.mjs";
import "../../openclaw-state-db-D1jWDzK3.mjs";
import { c as claimOpenClawAgentDatabaseLease, g as releaseOpenClawAgentDatabaseLease } from "../../openclaw-agent-db-lease-D8zYIRzn.mjs";
import { n as openOpenClawAgentDatabaseReadOnly, t as hasOpenClawAgentReadOnlySchema } from "../../openclaw-agent-db-readonly-open-hQ4ohjUT.mjs";
import { n as serveWorkerTasks } from "../../worker-task-server-BC_7U18u.mjs";
import { c as listSessionsNeedingTranscriptIndexReconcile, v as prepareMemorySessionTranscriptProjection, y as prepareSessionTranscriptProjection } from "../../session-transcript-index-FbEuN6S5.mjs";
import { t as runSqliteReconciliationLifecyclePhase } from "../../session-accessor.sqlite-worker-coordination-CnWXpMvZ.mjs";
import { MessagePort } from "node:worker_threads";
//#region src/config/sessions/session-transcript-reconcile.worker.ts
/** Worker entrypoint for transcript parsing and active-branch resolution only. */
const ACTIVE_ROWS_PER_CHUNK = 512;
const FTS_ROWS_PER_CHUNK = 128;
const FTS_TEXT_BYTES_PER_CHUNK = 262144;
function parseWorkerInput(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) return;
const input = value;
if (input.mode === "memory" && Array.isArray(input.sessionIds) && input.sessionIds.every((sessionId) => typeof sessionId === "string")) return {
mode: "memory",
sessionIds: input.sessionIds
};
if (typeof input.stateDir !== "string" || typeof input.externallySupervised !== "boolean") return;
const owner = {
stateDir: input.stateDir,
externallySupervised: input.externallySupervised
};
if (input.mode === "release" && typeof input.leaseId === "string" && typeof input.path === "string") return {
...owner,
mode: "release",
leaseId: input.leaseId,
path: input.path
};
if (typeof input.agentId !== "string" || typeof input.path !== "string") return;
if (input.preferredSessionId !== void 0 && typeof input.preferredSessionId !== "string") return;
const plan = {
...owner,
agentId: input.agentId,
path: input.path,
...typeof input.preferredSessionId === "string" ? { preferredSessionId: input.preferredSessionId } : {}
};
if (input.mode === "disk" && typeof input.leaseId === "string") return {
...plan,
mode: "disk",
leaseId: input.leaseId
};
}
function orderSessionIds(sessionIds, preferredSessionId) {
if (!preferredSessionId || !sessionIds.includes(preferredSessionId)) return sessionIds;
return [preferredSessionId, ...sessionIds.filter((sessionId) => sessionId !== preferredSessionId)];
}
function resolveLeaseEnvironment(owner) {
return {
OPENCLAW_STATE_DIR: owner.stateDir,
...owner.externallySupervised ? { OPENCLAW_SUPERVISOR_MODE: "external" } : {}
};
}
function releaseLease(owner, port, readOnlyClosed = false) {
let failure;
try {
releaseOpenClawAgentDatabaseLease(owner.leaseId, {
env: resolveLeaseEnvironment(owner),
initializationAgentPaths: [owner.path]
}, readOnlyClosed ? "read-only" : void 0);
} catch (error) {
failure = error instanceof Error ? error : new Error(String(error));
} finally {
closeOpenClawStateDatabase();
}
if (failure) port.postMessage({
type: "lease-release-failed",
error: failure.message
});
else port.postMessage({ type: "lease-released" });
port.close();
}
function waitForContinue(port) {
return new Promise((resolve, reject) => {
port.once("message", (message) => {
if (message?.type !== "continue" || typeof message.accepted !== "boolean") {
reject(/* @__PURE__ */ new Error("session transcript reconcile worker received an invalid command"));
return;
}
resolve(message.accepted);
});
});
}
async function postAndWait(port, message, transferList = []) {
port.postMessage(message, transferList);
return await waitForContinue(port);
}
function encodeFtsChunk(rows) {
const encoder = new TextEncoder();
const encoded = rows.map((row) => ({
bytes: encoder.encode(row.text),
row
}));
const textBytes = new Uint8Array(encoded.reduce((total, entry) => total + entry.bytes.length, 0));
let textByteOffset = 0;
return {
rows: encoded.map(({ bytes, row }) => {
textBytes.set(bytes, textByteOffset);
const result = {
messageId: row.messageId,
role: row.role,
textByteLength: bytes.length,
textByteOffset,
timestamp: row.timestamp
};
textByteOffset += bytes.length;
return result;
}),
textBytes
};
}
function takeFtsChunkEnd(rows, start) {
let bytes = 0;
let end = start;
while (end < rows.length && end - start < FTS_ROWS_PER_CHUNK) {
const rowBytes = Buffer.byteLength(rows[end]?.text ?? "", "utf8");
if (end > start && bytes + rowBytes > FTS_TEXT_BYTES_PER_CHUNK) break;
bytes += rowBytes;
end += 1;
}
return end;
}
async function streamPreparedProjection(plan, port) {
const { activeRows, ftsRows, ...metadata } = plan;
if (!await postAndWait(port, {
type: "plan-start",
plan: metadata
})) return;
for (let offset = 0; offset < activeRows.length; offset += ACTIVE_ROWS_PER_CHUNK) if (!await postAndWait(port, {
type: "active-chunk",
rows: activeRows.slice(offset, offset + ACTIVE_ROWS_PER_CHUNK),
sessionId: plan.sessionId
})) return;
for (let offset = 0; offset < ftsRows.length;) {
const end = takeFtsChunkEnd(ftsRows, offset);
const chunk = encodeFtsChunk(ftsRows.slice(offset, end));
if (!await postAndWait(port, {
type: "fts-chunk",
chunk,
sessionId: plan.sessionId
}, [chunk.textBytes.buffer])) return;
offset = end;
}
await postAndWait(port, {
type: "plan-finish",
sessionId: plan.sessionId
});
}
async function prepareMemoryProjection(sessionId, port) {
const rows = /* @__PURE__ */ new Map();
const decoder = new TextDecoder();
let fragments = [];
while (true) {
const pending = new Promise((resolve) => {
port.once("message", resolve);
});
port.postMessage({
type: "source-read",
sessionId
});
const frame = await pending;
if (frame.type === "source-unavailable") return;
if (frame.type === "source-end") {
const plan = prepareMemorySessionTranscriptProjection(sessionId, frame.snapshot.transcriptUpdatedAt, rows, frame.snapshot.generation);
rows.clear();
return plan;
}
fragments.push(decoder.decode(frame.bytes, { stream: !frame.final }));
if (frame.final) {
rows.set(frame.seq, {
seq: frame.seq,
created_at: frame.createdAt,
event_json: fragments.join("")
});
fragments = [];
}
}
}
async function run(input, port, coordination, sourceIdentity) {
let unsettled = false;
const phase = (name, operation) => coordination ? runSqliteReconciliationLifecyclePhase(coordination, name, operation, () => {
unsettled = true;
}) : Promise.resolve(operation());
const assertSource = () => {
if (input.mode === "disk") {
if (!sourceIdentity) throw new Error("Transcript worker lost its captured source identity");
assertExistingDatabaseIdentity(input.path, sourceIdentity);
}
};
if (input.mode === "release") {
await phase("close", () => releaseLease(input, port));
return;
}
const reconcileInput = input;
let closeDatabase;
let sharedBorrow;
let assertAdmittedSchemas;
let terminalMessage;
try {
const database = await phase("open", () => {
if (reconcileInput.mode === "memory") return;
const options = {
agentId: reconcileInput.agentId,
path: reconcileInput.path,
env: resolveLeaseEnvironment(reconcileInput)
};
assertSource();
claimOpenClawAgentDatabaseLease(options, reconcileInput.leaseId);
const opened = openOpenClawAgentDatabaseReadOnly(options);
if (!opened.found) throw new Error(`Cannot prepare transcript indexes: ${opened.reason}`);
closeDatabase = opened.database.close;
assertSource();
if (coordination?.reconciliation) {
sharedBorrow = borrowOpenClawStateDatabaseForAsyncRead(coordination.databasePath);
if (!sharedBorrow) throw new Error("Transcript worker lost its admitted shared-state handle");
const shared = sharedBorrow.database;
const sharedSchema = readSqliteSchemaCookie(shared.db);
const agentSchema = readSqliteSchemaCookie(opened.database.db);
if (typeof sharedSchema !== "number" || typeof agentSchema !== "number") throw new Error("Transcript worker cannot retain its admitted schema identities");
assertAdmittedSchemas = () => {
sharedBorrow?.assertCurrent();
if (!shared.db.isOpen || readSqliteSchemaCookie(shared.db) !== sharedSchema || readSqliteSchemaCookie(opened.database.db) !== agentSchema) throw new Error("Transcript worker schema changed between lifecycle phases");
assertExistingOpenClawStateRuntimeSchema(shared.db, shared.path);
if (!hasOpenClawAgentReadOnlySchema(opened.database)) throw new Error("Transcript worker lost its admitted agent schema");
};
}
return opened.database;
});
const sessionIds = reconcileInput.mode === "memory" ? reconcileInput.sessionIds : orderSessionIds(listSessionsNeedingTranscriptIndexReconcile(database.db), reconcileInput.preferredSessionId);
for (const sessionId of sessionIds) {
assertSource();
const plan = reconcileInput.mode === "memory" ? await prepareMemoryProjection(sessionId, port) : prepareSessionTranscriptProjection(database.db, sessionId);
if (plan) {
await streamPreparedProjection(plan, port);
assertSource();
}
}
terminalMessage = { type: "done" };
} catch (error) {
if (unsettled) throw error;
terminalMessage = {
type: "failed",
error: error instanceof Error ? error.message : String(error)
};
}
try {
if (!coordination?.reconciliation) closeDatabase?.();
port.postMessage(terminalMessage);
if (reconcileInput.mode === "disk") {
await new Promise((resolve, reject) => {
port.once("message", (message) => {
if (message?.type !== "release") {
reject(/* @__PURE__ */ new Error("session transcript reconcile worker expected lease release"));
return;
}
resolve();
});
});
await phase("close", () => {
assertSource();
assertAdmittedSchemas?.();
if (coordination?.reconciliation) {
closeDatabase?.();
sharedBorrow?.release();
sharedBorrow = void 0;
}
releaseLease(reconcileInput, port, closeDatabase !== void 0);
});
}
} finally {
if (reconcileInput.mode === "memory") port.close();
}
}
serveWorkerTasks(async (value) => {
if (!value || typeof value !== "object" || !("input" in value) || !("port" in value)) throw new Error("session transcript reconcile worker requires a task");
const input = parseWorkerInput(value.input);
if (!input || !(value.port instanceof MessagePort)) throw new Error("session transcript reconcile worker requires valid task data");
const port = value.port;
try {
if (input.mode === "memory") await run(input, port);
else {
const { coordination, sourceIdentity } = value;
if (!coordination?.reconciliation || coordination.actorId !== `transcript:${input.mode}:${input.leaseId}` || coordination.databasePath !== resolveOpenClawStateSqlitePath(resolveLeaseEnvironment(input))) throw new Error("Transcript worker shared-state owner changed");
await run(input, port, coordination, sourceIdentity);
}
} catch {
process.exit(1);
} finally {
value.port.close();
}
});
//#endregion
export {};