openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
388 lines (387 loc) • 16.5 kB
JavaScript
import { c as isRecord } from "../../record-coerce-TIsquoVR.mjs";
import { u as readDatabasePathIdentitySync } from "../../sqlite-worker-identity-KSR14F6C.mjs";
import { t as assertTransactionUsable } from "../../sqlite-transaction-DsZ3X6nD.mjs";
import { t as encodeOpenClawStateWorkerError } from "../../openclaw-state-worker-error-Cmtk9G4_.mjs";
import { i as requestSqliteWorkerOperationAdmission } from "../../sqlite-worker-operation-admission-cNGSH5v0.mjs";
import { i as getSqliteWorkerStateContext } from "../../sqlite-worker-state-context-BeVaUlhr.mjs";
import { c as resolveOpenClawAgentSqlitePath } from "../../openclaw-agent-db.paths-DVbbFaej.mjs";
import { u as runOpenClawAgentWriteTransaction } from "../../openclaw-agent-db-DBug3LUA.mjs";
import { C as parseOpaqueLeafEntry, S as isReadableSessionMessage, a as prepareTranscriptPayloadForReuse, b as findSessionTranscriptHeader, x as isIndexedSessionEntry } from "../../transcript-payload-Yc_w8Yx5.mjs";
import { t as SessionTranscriptWriterClaimReboundError } from "../../transcript-write-context-m7NE2aEm.mjs";
import { i as toDatabaseOptions } from "../../session-accessor.sqlite-scope-helpers-DOK3pvYX.mjs";
import { d as resolveSqliteTranscriptScope } from "../../session-accessor.sqlite-scope-CdpbxR_m.mjs";
import { n as assertCanonicalSessionKeyWrite } from "../../session-canonical-key-D1TSJMmX.mjs";
import { a as runWithSessionTranscriptReadFence } from "../../session-transcript-read-fence-DfxRHzep.mjs";
import { v as runWithSessionPendingInputWorkerCustody } from "../../session-accessor.sqlite-pending-inputs-DtDOOibA.mjs";
import { T as validatePreparedAssistantAppendSync, a as inspectTranscriptEventsSync, d as loadTranscriptReadSnapshotSync } from "../../session-accessor.sqlite-read-DhXli1yO.mjs";
import { t as ensureSessionEntryInTransaction } from "../../session-accessor.sqlite-initial-entry-CwgkYlsN.mjs";
import { t as readSessionTranscriptBoundedActiveContextCore } from "../../session-accessor.sqlite-active-context-d1JosJrK.mjs";
import { a as appendTranscriptMessageSnapshotSync, c as replaceSessionWithBranchedTranscriptInTransaction, h as replaceTranscriptSuffixEventsSync, n as appendTranscriptEventSnapshotSync } from "../../session-accessor.sqlite-transcript-write-tYk0mJFG.mjs";
import { i as persistCompactionBoundaryWithSessionEntryInWorker, n as readTranscriptMutationAtSync } from "../../session-accessor.sqlite-metadata-read-BFw5XeYL.mjs";
import { t as prepareTranscriptRewriteSync } from "../../session-accessor.sqlite-branch-rewrite-DDotZftj.mjs";
import { serialize } from "node:v8";
//#region src/agents/sessions/session-manager-maintenance.worker.ts
function executeSessionMaintenance(command, scope, context) {
let projectionNeedsReconcile = false;
const projection = {
scheduleProjectionReconcile: false,
onProjectionReconcileNeeded: () => {
projectionNeedsReconcile = true;
}
};
if (command.type === "session.transcript.branch") return runOpenClawAgentWriteTransaction((database) => {
if (database.db !== context.database) throw new Error("Session branch lost its borrowed canonical connection");
context.admit("transaction");
const result = replaceSessionWithBranchedTranscriptInTransaction(database, scope, command.input.branch, command.input.expectedLifecycleRevision, void 0, projection);
context.admit("commit");
return {
...result,
projectionNeedsReconcile
};
}, toDatabaseOptions(resolveSqliteTranscriptScope(scope)), { operationLabel: command.type });
if (command.type === "session.transcript.replaceSuffix") {
const [expected, next, prefix, mutationAt, startsAtPrefix, retained] = command.input.args;
let version;
return {
replaced: replaceTranscriptSuffixEventsSync(scope, expected, next, prefix, mutationAt, (committed) => {
version = committed;
}, startsAtPrefix, retained, (stage) => context.admit(stage), projection),
version,
projectionNeedsReconcile
};
}
let version;
prepareTranscriptRewriteSync(scope, command.input.appendParentId, () => {}, command.input.version, (stage) => context.admit(stage), {
messagesAlreadyRedacted: true,
...projection
})(command.input.entries, new Map(command.input.sources), (committed) => {
version = committed;
});
if (!version) throw new Error("Session rewrite did not return its committed version");
return {
version,
entries: command.input.entries,
projectionNeedsReconcile
};
}
//#endregion
//#region src/agents/sessions/session-manager-metadata.worker.ts
function runWithMetadataMessageAdmission(context, controls, run) {
let transactionFacts;
let pendingAuthorityChecked = false;
const requestMessageCheck = (check) => {
requestSqliteWorkerOperationAdmission({
stage: "prepare",
facts: {
kind: "session-message",
domainFacts: transactionFacts,
check
}
});
if (check === "pending") pendingAuthorityChecked = true;
};
const admit = (stage) => context.admit(stage, (request, dispatch) => {
transactionFacts = request.facts;
dispatch({
...request,
facts: {
kind: "session-message",
domainFacts: request.facts,
...stage === "commit" && pendingAuthorityChecked ? { check: "pending" } : {}
}
});
});
const write = () => run(admit, () => requestMessageCheck("fresh"));
const pending = controls?.pendingInput;
if (!pending) return { value: write() };
const result = runWithSessionPendingInputWorkerCustody(pending.facts, pending.relocation, () => requestMessageCheck("pending"), write);
return {
value: result.value,
pendingInputReceipt: result.receipt
};
}
function decodeMetadataAppendEvent(input) {
const event = typeof input.event === "string" ? JSON.parse(input.event) : {
...input.event,
message: JSON.parse(input.message?.messageJson ?? "null")
};
if (isIndexedSessionEntry(event)) {
if (event.type === "message" !== (typeof input.event !== "string")) throw new Error("Session message append requires prepared storage bytes");
return event;
}
const header = findSessionTranscriptHeader([event]);
if (header) return header;
const leaf = parseOpaqueLeafEntry(event);
if (leaf && isRecord(event) && typeof event.timestamp === "string") return {
...leaf,
type: "leaf",
timestamp: event.timestamp
};
throw new Error("Invalid serialized session transcript entry");
}
function copyTranscriptRefusal(value) {
if (value === void 0) return;
if (!isRecord(value) || typeof value.agentIdHash !== "string" || typeof value.expectedSessionIdHash !== "string" || typeof value.sessionKeyHash !== "string") throw new Error("Session metadata refusal has an invalid identity");
const identity = {
agentIdHash: value.agentIdHash,
expectedSessionIdHash: value.expectedSessionIdHash,
sessionKeyHash: value.sessionKeyHash
};
if (value.code === "session-entry-missing") return {
...identity,
code: value.code
};
if (value.code === "session-rebound" && typeof value.actualSessionIdHash === "string") return {
...identity,
code: value.code,
actualSessionIdHash: value.actualSessionIdHash
};
throw new Error("Session metadata refusal has an invalid kind");
}
function readCommittedMetadataView(scope, limits, admission) {
return runWithSessionTranscriptReadFence(admission, () => {
if (limits) return {
kind: "bounded",
snapshot: readSessionTranscriptBoundedActiveContextCore(scope, {
...limits,
...admission !== void 0 ? { ignoreReadFence: true } : {}
})
};
if (admission !== void 0) {
const inspected = inspectTranscriptEventsSync(scope);
return {
kind: "full",
snapshot: {
events: inspected.events,
version: {
generation: inspected.snapshot.generation,
rawSeq: inspected.snapshot.lastSeq,
updatedAt: inspected.snapshot.transcriptUpdatedAt
}
}
};
}
return {
kind: "full",
snapshot: loadTranscriptReadSnapshotSync(scope)
};
});
}
/** Borrow the canonical actor's connection; this domain never opens or closes a database. */
function bindSqliteWorkerBackend(_input, context) {
let closed = false;
const assertOpen = () => {
if (closed || !context.database.isOpen) throw new Error("Session metadata domain is closed");
assertTransactionUsable(context.database);
};
const execute = (command) => {
assertOpen();
const scope = {
...command.input.scope,
env: getSqliteWorkerStateContext().environment
};
const resolved = resolveSqliteTranscriptScope(scope);
const options = toDatabaseOptions(resolved);
if (readDatabasePathIdentitySync(resolveOpenClawAgentSqlitePath(options)).canonicalPath !== context.databasePath) throw new Error("Session metadata target changed its database owner");
scope.storePath = context.databasePath;
resolved.path = context.databasePath;
options.path = context.databasePath;
if (command.type === "session.metadata.mutation") return {
ok: true,
value: readTranscriptMutationAtSync(scope)
};
assertCanonicalSessionKeyWrite(resolved.sessionKey, resolved.agentId);
if (command.type === "session.transcript.compactionBoundary") return {
ok: true,
value: persistCompactionBoundaryWithSessionEntryInWorker(scope, {
...command.input,
prepared: {
...command.input.prepared,
scope: {
...command.input.prepared.scope,
env: scope.env
}
}
}, context)
};
if (command.type === "session.transcript.branch") return {
ok: true,
value: executeSessionMaintenance(command, scope, context)
};
if (command.type === "session.transcript.replaceSuffix") return {
ok: true,
value: executeSessionMaintenance(command, scope, context)
};
if (command.type === "session.transcript.rewrite") {
const result = runWithMetadataMessageAdmission(context, command.input, (admit) => executeSessionMaintenance(command, scope, {
...context,
admit
}));
return {
ok: true,
value: {
...result.value,
pendingInputReceipt: result.pendingInputReceipt
}
};
}
if (command.type === "session.transcript.appendMessage") {
const message = JSON.parse(command.input.messageJson);
if (!isReadableSessionMessage(message)) throw new Error("Invalid serialized session transcript message");
const prepared = {
messageJson: command.input.messageJson,
persistedMessage: message
};
const result = runWithMetadataMessageAdmission(context, command.input, (admit) => runOpenClawAgentWriteTransaction((database) => {
if (database.db !== context.database) throw new Error("Session message lost its borrowed canonical connection");
admit("transaction");
let projectionNeedsReconcile = false;
const snapshot = appendTranscriptMessageSnapshotSync(scope, {
message,
cwd: command.input.cwd
}, prepared, {
messageAlreadyRedacted: true,
scheduleProjectionReconcile: false,
onProjectionReconcileNeeded: () => {
projectionNeedsReconcile = true;
}
});
admit("commit");
return {
ok: true,
value: {
snapshot,
projectionNeedsReconcile
}
};
}, options, { operationLabel: command.type }));
if (result.value.ok) {
result.value.value.pendingInputReceipt = result.pendingInputReceipt;
const { snapshot } = result.value.value;
if (snapshot.ok && snapshot.value.result?.message === message) snapshot.value.result.message = void 0;
}
return result.value;
}
const event = command.type === "session.metadata.append" ? decodeMetadataAppendEvent(command.input) : void 0;
let prepared;
if (command.type === "session.metadata.append" && event?.type === "message") {
const { message } = command.input;
if (!message) throw new Error("Session message append requires prepared storage bytes");
const { message: _message, ...envelope } = event;
const eventJson = `${JSON.stringify(envelope).slice(0, -1)},"message":${message.messageJson}}`;
prepared = {
messageJson: message.messageJson,
persistedMessage: event.message,
physicalPayload: prepareTranscriptPayloadForReuse(context.database, eventJson, event)
};
if (message.validateTurn) {
const mutationAt = validatePreparedAssistantAppendSync(scope, event.parentId, command.input.view?.admission?.entryId);
if (mutationAt === void 0) {
const error = /* @__PURE__ */ new Error(`SQLite transcript changed while preparing rewrite for ${scope.sessionId}`);
error.name = "SqliteTranscriptMutationConflictError";
throw error;
}
command.input.options.expectedMutationAt = mutationAt;
}
}
const result = runWithMetadataMessageAdmission(context, command.type === "session.metadata.append" ? command.input.message : void 0, (admit, beforeFreshMessageCommit) => runOpenClawAgentWriteTransaction((database) => {
if (database.db !== context.database) throw new Error("Session metadata lost its borrowed canonical connection");
admit("transaction");
if (command.type === "session.metadata.initialize") {
const initialized = ensureSessionEntryInTransaction(database, resolved, scope, command.input.entry, command.input.initialWriterRunId);
admit("commit");
return {
ok: true,
value: initialized
};
}
let projectionNeedsReconcile = false;
const projection = {
scheduleProjectionReconcile: false,
onProjectionReconcileNeeded: () => {
projectionNeedsReconcile = true;
}
};
if (!event) throw new Error("Session metadata append requires a serialized event");
const { message } = command.input;
const snapshot = event.type === "message" && message ? appendTranscriptMessageSnapshotSync(scope, {
...command.input.options,
message: event.message,
eventId: event.id,
parentId: event.parentId,
now: Date.parse(event.timestamp),
cwd: message.cwd,
idempotencyLookup: message.idempotencyLookup,
...message.freshMessageCheck ? { beforeFreshMessageCommit } : {}
}, prepared, projection) : appendTranscriptEventSnapshotSync(scope, event, command.input.options, {
...projection,
eventJson: typeof command.input.event === "string" ? command.input.event : void 0
});
admit("commit");
return {
ok: true,
value: {
snapshot,
projectionNeedsReconcile
}
};
}, options, {
operationLabel: command.type,
diagnosticContext: {
sessionId: scope.sessionId,
eventType: event?.type,
messageRole: event?.type === "message" ? event.message.role : void 0
}
}));
const outcome = result.value;
if (outcome.ok && "snapshot" in outcome.value && outcome.value.snapshot.ok) {
const receipt = outcome.value.snapshot.value.result;
if (receipt && "message" in receipt && receipt.message === prepared?.persistedMessage) receipt.message = void 0;
}
if (result.pendingInputReceipt && outcome.ok && "snapshot" in outcome.value) outcome.value.pendingInputReceipt = result.pendingInputReceipt;
if (command.type === "session.metadata.append" && event && event.type !== "session" && command.input.view && outcome.ok && "snapshot" in outcome.value && outcome.value.snapshot.ok) {
const { view } = command.input;
const committed = outcome.value.snapshot.value;
if (!committed.result) return outcome;
const adoptedMessage = "messageId" in committed.result && committed.result.messageId !== event.id;
if (!committed.result.appended && !adoptedMessage) return outcome;
const version = view.loadedVersion;
const effectiveParentId = "effectiveParentId" in committed.result ? committed.result.effectiveParentId : void 0;
if (adoptedMessage || version && (committed.before.generation !== version.generation || committed.before.rawSeq !== version.rawSeq) || effectiveParentId !== void 0 && effectiveParentId !== event.parentId) try {
outcome.value.reload = {
ok: true,
value: readCommittedMetadataView(scope, view.limits, view.admission)
};
serialize(outcome);
} catch (error) {
outcome.value.reload = {
ok: false,
error: encodeOpenClawStateWorkerError(error, { includeOrdinary: true })
};
}
}
return outcome;
};
return {
execute(command) {
try {
return execute(command);
} catch (error) {
if (error instanceof SessionTranscriptWriterClaimReboundError) return {
ok: false,
refusal: copyTranscriptRefusal(error.cause)
};
throw error;
}
},
assertSettled() {
assertOpen();
if (context.database.isTransaction) throw new Error("Session metadata command left a transaction open");
},
close() {
closed = true;
}
};
}
//#endregion
export { bindSqliteWorkerBackend };