openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
260 lines (259 loc) • 11.6 kB
JavaScript
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { r as resolveStateDir } from "./state-dir-CYkBUd_G.mjs";
import { c as resolveSessionStorePathCore } from "./paths-Bonddxfk.mjs";
import { i as isIncognitoOpenClawAgentSqlitePath, l as resolveOpenClawAgentSqlitePath, r as createOpenClawAgentDatabasePathMatcher } from "./openclaw-agent-db.paths-BbmZlEP5.mjs";
import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-DAqV3tqr.mjs";
import { c as SqliteWorkerError } from "./sqlite-worker-contract-C51cJHzg.mjs";
import { r as createSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs";
import { n as captureOpenClawStateReadWorkerContext } from "./openclaw-state-worker-context-BvvgY6-t.mjs";
import { i as getOpenClawAgentDatabaseIfOpen } from "./openclaw-agent-db-DWI9Bgur.mjs";
import { t as runOpenClawAgentWorkerWrite } from "./openclaw-agent-write-admission-CXJiE5VB.mjs";
import { i as toDatabaseOptions } from "./session-accessor.sqlite-scope-helpers-CfDTAEsL.mjs";
import { t as captureSessionTranscriptStorageEnvironment } from "./transcript-target-binding-DpcuSIFy.mjs";
import { n as captureOpenClawAgentDatabaseExecution } from "./openclaw-agent-execution-C2B2fucX.mjs";
import { o as resolveSqliteReadScope } from "./session-accessor.sqlite-scope-BkgwxatT.mjs";
import { n as selectConversationRowsFromDatabase } from "./session-accessor.sqlite-conversation-read-WXGxcUdb.mjs";
import { r as withConversationPublication } from "./session-accessor.sqlite-conversation-publication-D5ZVrcUt.mjs";
import { t as captureSessionStoreReadCandidates } from "./session-store-target-inventory-C9ERDnSH.mjs";
import { t as withOpenClawAgentDatabaseWrite } from "./openclaw-agent-db-write-B3Uithdx.mjs";
import { u as withSessionStoreReaderInWorker } from "./session-entry-read-runtime-BdnQjoL6.mjs";
import path from "node:path";
//#region src/config/sessions/conversation-registry.ts
function resolveConversationRegistryScope(params) {
return pinConversationDatabaseScope({
agentId: params.agentId,
storePath: resolveSessionStorePathCore(params.config.session?.store, { agentId: params.agentId })
}).scope;
}
async function prepareConversationRegistryScope(params) {
const input = {
agentId: params.agentId,
storePath: resolveSessionStorePathCore(params.config.session?.store, { agentId: params.agentId })
};
if (isIncognitoOpenClawAgentSqlitePath(input.storePath, input)) return pinConversationDatabaseScope(input).scope;
return withConversationRead(input, async ({ database, logicalAgentId }) => ({
agentId: logicalAgentId,
databaseAgentId: database.agentId,
storePath: database.path,
env: database.env
}));
}
function withConversationRead(input, read) {
const env = captureSessionTranscriptStorageEnvironment(input.env ?? process.env);
const storePath = path.resolve(input.storePath ?? resolveSessionStorePathCore(void 0, {
agentId: input.agentId,
env
}));
const context = captureOpenClawStateReadWorkerContext({ env });
const source = createOpenClawAgentDatabasePathMatcher();
for (const candidate of captureSessionStoreReadCandidates(storePath)) source(candidate.path, candidate.path);
return withSessionStoreReaderInWorker({
agentId: input.agentId,
storePath,
env
}, async (owner) => {
if (input.databaseAgentId && owner.database.agentId !== input.databaseAgentId) throw new Error("Conversation database owner changed. Retry the request.");
const result = await read(owner);
owner.assertCurrent();
return result;
}, {
dataOnly: true,
logical: { assertCurrent() {
context.maintenanceScope?.assertAdmission();
context.admission.assertCurrent();
if (!source.isCurrent()) throw new Error("Session store changed while reading conversations. Retry the request.");
} }
});
}
function selectConversationRowsInWorker(scope, query) {
const capturedQuery = structuredClone(query);
if (scope.storePath && isIncognitoOpenClawAgentSqlitePath(scope.storePath, scope)) return Promise.resolve(selectConversationRows(scope, capturedQuery));
return withConversationRead(scope, ({ reader, database }) => reader.readConversations({
query: capturedQuery,
env: database.env
}));
}
function pinConversationDatabaseScope(input) {
const env = { ...input.env ?? process.env };
env.OPENCLAW_STATE_DIR = resolveStateDir(env);
const options = input.databaseAgentId && input.storePath ? {
agentId: input.databaseAgentId,
path: input.storePath,
env
} : toDatabaseOptions(resolveSqliteReadScope({
...input,
env
}));
const storePath = resolveOpenClawAgentSqlitePath(options);
return {
options: {
...options,
path: storePath
},
scope: {
...input,
databaseAgentId: options.agentId,
storePath,
env
}
};
}
/** Keep the logical agent and physical store fixed while its synchronous write waits. */
function runConversationDatabaseWrite(input, operation) {
const { options, scope } = pinConversationDatabaseScope(input);
return withOpenClawAgentDatabaseWrite(options, () => operation(scope));
}
function selectConversationRows(scope, options = {}) {
const resolved = resolveSqliteReadScope({
agentId: scope.agentId,
...scope.env ? { env: scope.env } : {},
...scope.storePath ? { storePath: scope.storePath } : {}
});
const databaseOptions = toDatabaseOptions(resolved);
const readRows = (database) => selectConversationRowsFromDatabase(database, options);
const held = getOpenClawAgentDatabaseIfOpen(databaseOptions);
if (held?.db.isTransaction) return readRows(held);
const read = withOpenClawAgentDatabaseReadOnly(readRows, databaseOptions);
return read.found ? read.value : [];
}
/** Catalogs routable addresses in the existing agent writer without creating sessions. */
async function registerConversationAddresses(scope, identities, discoveredAt = Date.now(), selectEligible = (values) => values.map(() => true), query) {
if (identities.length === 0) return;
const { options } = pinConversationDatabaseScope(scope);
const input = {
identities: structuredClone(identities),
discoveredAt,
query: query && structuredClone(query)
};
const selectCurrent = () => {
const selected = selectEligible(input.identities);
if (selected.length !== input.identities.length) throw new Error("Conversation route owner returned an incomplete eligibility selection");
return selected;
};
const assertCurrent = () => {
if (!selectCurrent().every(Boolean)) throw new Error("Conversation route ownership changed during discovery");
};
const source = {
assertCurrent() {},
createAdmission(binding) {
return withConversationPublication(() => ({
nativeLocations: binding.nativeLocations,
admission: createSqliteWorkerOperationAdmission((request, grant) => {
binding.authorize(request);
if (request.stage === "transaction" || request.stage === "commit") assertCurrent();
if (!grant()) throw new Error("Conversation registration authority expired");
}, binding.attachment)
}), () => binding.assertCurrent(), () => execution.captureGenerationClaim(), () => [...new Set(input.identities.map((identity) => JSON.stringify(["catalogue", identity.conversationRef])))]);
}
};
const execution = captureOpenClawAgentDatabaseExecution(options);
try {
return await runOpenClawAgentWorkerWrite(options, async () => {
const eligible = selectCurrent();
input.identities = input.identities.filter((_, index) => eligible[index]);
if (input.identities.length === 0) return;
if (!execution.capturePreparedGenerationClaim()) await execution.prepare(source);
return await execution.runExisting(source, (worker) => worker.execute({
type: "conversation.register",
input
}));
});
} finally {
await execution.release();
}
}
/** Initiate under the writer grant; settle network work after releasing its transaction and FIFO. */
async function withConversationAuthority(scope, query, select) {
const { options } = pinConversationDatabaseScope(scope);
const input = structuredClone(query);
const execution = captureOpenClawAgentDatabaseExecution(options);
let consumed;
try {
try {
await runOpenClawAgentWorkerWrite(options, async () => {
const settled = await execution.runExisting({
assertCurrent() {},
createAdmission(binding) {
return () => ({
nativeLocations: binding.nativeLocations,
admission: createSqliteWorkerOperationAdmission((request, grant) => {
binding.authorize(request);
let consume;
if (request.stage === "commit") {
const publication = isRecord(request.facts) && request.facts.publication;
if (consumed || !isRecord(publication) || publication.kind !== "conversation-authority" || !isRecord(publication.facts)) throw new Error("Conversation authority omitted its transaction facts");
const facts = publication.facts;
const initiate = select(facts);
consume = () => {
consumed = new Promise((resolve) => {
resolve(initiate());
}).then((value) => ({
ok: true,
value
}), (error) => ({
ok: false,
error
}));
};
}
if (!grant(consume)) throw new Error("Conversation read authority expired");
}, binding.attachment)
});
}
}, (worker) => worker.execute({
type: "conversation.authority",
input
}));
if (!consumed && settled !== void 0) throw new Error("Conversation authority omitted its transaction grant");
});
} finally {
await execution.release();
}
} catch (cause) {
if (!consumed) throw cause;
await consumed;
const error = new SqliteWorkerError("Conversation authority settlement failed after dispatch initiation", "outcome-unknown");
error.cause = cause;
throw error;
}
if (!consumed) return await select({
operation: void 0,
conversation: void 0
})();
const outcome = await consumed;
if (!outcome.ok) throw outcome.error;
return outcome.value;
}
/** Lists stable external addresses for one agent, newest activity first. */
function listConversations(scope, options = {}) {
return selectConversationRowsInWorker(scope, options);
}
async function readConversation(scope, conversationRef) {
return (await selectConversationRowsInWorker(scope, {
conversationRef,
limit: 1
}))[0];
}
/** Reads only an authoritative association on an address's current session window. */
function resolveCurrentConversationSession(scope, conversationRef, currentSession) {
const [conversation] = selectConversationRows(scope, {
conversationRef,
currentBindingOnly: true,
currentSession,
limit: 1
});
return conversation?.sessionKey && conversation.sessionId ? {
sessionKey: conversation.sessionKey,
sessionId: conversation.sessionId
} : void 0;
}
/** Reads only the primary address bound to this exact current session window. */
async function resolveCurrentSessionPrimaryConversation(scope) {
const [conversation] = await selectConversationRowsInWorker(scope, { primarySession: {
sessionId: scope.sessionId,
sessionKey: scope.sessionKey
} });
return conversation?.sessionId === scope.sessionId && conversation.sessionKey === scope.sessionKey ? conversation : void 0;
}
//#endregion
export { registerConversationAddresses as a, resolveCurrentSessionPrimaryConversation as c, readConversation as i, runConversationDatabaseWrite as l, pinConversationDatabaseScope as n, resolveConversationRegistryScope as o, prepareConversationRegistryScope as r, resolveCurrentConversationSession as s, listConversations as t, withConversationAuthority as u };