UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

274 lines (273 loc) • 14.4 kB
import { r as resolveStateDir } from "./state-dir-CYkBUd_G.mjs"; import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs"; import "./session-key-DY3MOX4N.mjs"; import { l as resolveOpenClawAgentSqlitePath, o as isSameOpenClawAgentDatabasePath } from "./openclaw-agent-db.paths-BbmZlEP5.mjs"; import { o as isGatewayExternallySupervised } from "./gateway-supervision-D9M_dluZ.mjs"; import { r as createSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs"; import { r as captureOpenClawStateWorkerContext } from "./openclaw-state-worker-context-BvvgY6-t.mjs"; import { n as runOpenClawAgentWriteAdmission, t as runOpenClawAgentWorkerWrite } from "./openclaw-agent-write-admission-CXJiE5VB.mjs"; import { i as toDatabaseOptions } from "./session-accessor.sqlite-scope-helpers-CfDTAEsL.mjs"; import { n as captureOpenClawAgentDatabaseExecution } from "./openclaw-agent-execution-C2B2fucX.mjs"; import { o as resolveSqliteReadScope } from "./session-accessor.sqlite-scope-BkgwxatT.mjs"; import { h as targetDiscoveryLane } from "./session-transcript-worker-resources-cnBmyJ1M.mjs"; import { c as withSessionHistoryWorkerDatabase } from "./session-transcript-worker-runtime-BdmdicNg.mjs"; import { r as projectPendingFinalDeliverySettlement } from "./session-entry-patch-operation-C0KSoGVF.mjs"; import { c as patchSessionEntryCore, t as applySessionEntryOperation } from "./session-accessor.sqlite-entry-DLa7_9ui.mjs"; import "./session-accessor-C3ljJ974.mjs"; import { n as pinConversationDatabaseScope } from "./conversation-registry-DcdOMU8T.mjs"; import { r as getOwedHarnessCompletionTask } from "./agent-harness-completion-recovery-C0Xm0Pt1.mjs"; import { i as resolveMessageReceiptPrimaryId } from "./receipt-DD2wc-6s.mjs"; import { n as ConversationDeliveryMissingError, t as ConversationDeliveryInputError } from "./conversation-delivery-store.types-CMzZxTv2.mjs"; import { n as resolveDeliveryQueueStateEnv } from "./delivery-queue-state-context-CpJOlAo7.mjs"; import "./delivery-queue-sqlite-DCCj1ADm.mjs"; //#region src/config/sessions/conversation-delivery-store.ts async function deliveryResult(operation) { try { return await operation(); } catch (error) { if (error instanceof Error) { if (error.name === "ConversationDeliveryInputError") throw new ConversationDeliveryInputError(error.message); if (error.name === "ConversationDeliveryMissingError") throw new ConversationDeliveryMissingError(error.message); } throw error; } } function readConversationDelivery(scope, lookup) { const { options, scope: preparedScope } = pinConversationDatabaseScope(scope); const captured = structuredClone(lookup); return deliveryResult(() => runOpenClawAgentWriteAdmission(options, () => withSessionHistoryWorkerDatabase(options, (reader) => reader.readConversationDelivery({ lookup: captured, env: preparedScope.env }), targetDiscoveryLane))); } async function getConversationDeliveryOperation(scope, operationId, expectedInput) { return readConversationDelivery(scope, { operationId, expectedInput }); } async function findConversationTurnDeliveryByReplyTarget(scope, params) { return readConversationDelivery(scope, params); } function writeConversationDelivery(scope, type, input, assertCurrent = () => {}) { const { options } = pinConversationDatabaseScope(scope); const execution = captureOpenClawAgentDatabaseExecution(options); const captured = structuredClone(input); return deliveryResult(async () => { try { const result = await runOpenClawAgentWorkerWrite(options, () => execution.runExisting({ assertCurrent, createAdmission(binding) { return () => ({ nativeLocations: binding.nativeLocations, admission: createSqliteWorkerOperationAdmission((request, grant) => { binding.authorize(request); assertCurrent(); if (!grant()) throw new Error("Conversation delivery authority expired"); }, binding.attachment) }); } }, (worker) => worker.execute({ type, input: captured }))); if (!result) throw new ConversationDeliveryMissingError("Conversation delivery database is missing"); return result; } finally { await execution.release(); } }); } async function beginConversationDeliveryOperation(scope, params, assertCurrent) { return writeConversationDelivery(scope, "conversation.delivery.begin", params, assertCurrent); } async function markConversationDeliveryQueued(scope, operationId, queueId) { return writeConversationDelivery(scope, "conversation.delivery.transition", { operationId, status: "queued", queueId, allowedFrom: ["created"] }); } async function markConversationDeliverySent(scope, operationId, platformMessageId) { return writeConversationDelivery(scope, "conversation.delivery.transition", { operationId, status: "sent", ...platformMessageId ? { platformMessageId } : {}, allowedFrom: ["created", "queued"] }); } async function markConversationDeliverySuppressed(scope, operationId) { return writeConversationDelivery(scope, "conversation.delivery.transition", { operationId, status: "suppressed", allowedFrom: ["created", "queued"] }); } async function markConversationDeliveryRejected(scope, operationId, rejectionError) { const normalizedError = rejectionError.trim(); if (!normalizedError) throw new Error("Conversation delivery rejection error is required"); return writeConversationDelivery(scope, "conversation.delivery.transition", { operationId, status: "rejected", rejectionError: normalizedError, allowedFrom: ["created", "queued"] }); } async function markConversationDeliveryUnknown(scope, operationId) { return writeConversationDelivery(scope, "conversation.delivery.transition", { operationId, status: "unknown", allowedFrom: ["created", "queued"] }); } async function markConversationDeliveryReplied(scope, params, assertCurrent) { return writeConversationDelivery(scope, "conversation.delivery.transition", { operationId: params.operationId, status: "replied", reply: params.reply, allowedFrom: ["queued", "sent"], session: params.session }, assertCurrent); } //#endregion //#region src/infra/outbound/delivery-completion.ts function captureConversationDeliveryTarget(scope) { return { workerContext: captureOpenClawStateWorkerContext({ env: scope.env }), agentId: scope.agentId, databaseAgentId: scope.databaseAgentId, storePath: scope.storePath, stateDir: resolveStateDir(scope.env), ...isGatewayExternallySupervised(scope.env) ? { supervisorMode: "external" } : {} }; } function resolveConversationDeliveryScope(completion, stateDir, stateContext, target) { const scope = { agentId: completion.agentId, ...completion.storePath ? { storePath: completion.storePath } : {}, env: resolveDeliveryQueueStateEnv(stateDir, target ?? stateContext) }; if (!target) return scope; const options = toDatabaseOptions(resolveSqliteReadScope(scope)); if (normalizeAgentId(scope.agentId) !== normalizeAgentId(target.agentId) || options.agentId !== target.databaseAgentId || !isSameOpenClawAgentDatabasePath(resolveOpenClawAgentSqlitePath(options), target.storePath)) throw new Error("Conversation delivery target does not match durable custody"); return { ...scope, storePath: target.storePath, databaseAgentId: target.databaseAgentId }; } async function conversationResult(completion, update, stateDir, stateContext, target) { let record; try { record = await update(resolveConversationDeliveryScope(completion, stateDir, stateContext, target)); } catch (error) { if (error instanceof ConversationDeliveryMissingError) return { state: "stale" }; throw error; } const delivered = record.status === "sent" || record.status === "replied"; return { state: delivered ? "delivered" : record.status === "suppressed" || record.status === "rejected" || record.status === "unknown" ? record.status : "queued", ...delivered && (record.platformMessageId || record.preparedMessageId) ? { platformMessageId: record.platformMessageId ?? record.preparedMessageId } : {}, ...record.status === "rejected" && record.rejectionError ? { rejectionError: record.rejectionError } : {} }; } async function settlePendingFinalDelivery(completion, state, expectedStates, options = {}) { let settled = "stale"; let wakeRecovery = false; const scope = { agentId: completion.agentId, sessionKey: completion.sessionKey, storePath: completion.storePath, env: resolveDeliveryQueueStateEnv(options.stateDir, options.stateContext) }; const settlement = { sessionId: completion.sessionId, intentId: completion.intentId, deliveryId: completion.deliveryId, state, expectedStates: expectedStates?.slice() }; const patchOptions = { skipMaintenance: true, takeCacheOwnership: true, preserveActivity: options.preserveActivity, workerGuard: {} }; const authority = completion.sessionWriterDeliveryAuthority; const claim = authority?.harnessCompletion; if (!claim) { if (completion.agentId !== void 0 && authority?.agentId !== void 0 && normalizeAgentId(authority.agentId) !== normalizeAgentId(completion.agentId)) return { state: "stale" }; let committed = false; const entry = await applySessionEntryOperation(scope, { kind: "pending-final-settle", settlement }, { ...patchOptions, onCommitted(current) { const delivery = current.pendingFinalDelivery?.deliveries?.find(({ id }) => id === settlement.deliveryId); if (!delivery) throw new Error("Pending final settlement omitted its committed delivery"); committed = true; settled = delivery.state; wakeRecovery = settled !== "queued" && current.abortedLastRun === true; } }); if (!committed && entry) settled = projectPendingFinalDeliverySettlement(entry, settlement).state; } else await patchSessionEntryCore(scope, (entry) => { if (entry.sessionId !== completion.sessionId || entry.pendingFinalDelivery?.intentId !== completion.intentId || !entry.pendingFinalDelivery.deliveries?.some(({ id }) => id === completion.deliveryId)) return null; if (completion.agentId !== void 0 && (authority?.agentId !== void 0 && normalizeAgentId(authority.agentId) !== normalizeAgentId(completion.agentId) || normalizeAgentId(claim.requesterAgentId) !== normalizeAgentId(completion.agentId))) return null; if (!authority || authority.sessionKey !== completion.sessionKey || authority.storePath !== void 0 && authority.storePath !== completion.storePath || claim.requesterSessionKey !== completion.sessionKey || claim.sessionId !== completion.sessionId || authority.expectedSessionId !== completion.sessionId || authority.agentId !== void 0 && authority.agentId !== claim.requesterAgentId || authority.expectedLifecycleRevision !== void 0 && authority.expectedLifecycleRevision !== entry.lifecycleRevision || authority.expectedWriterRunId !== void 0 && authority.expectedWriterRunId !== entry.activeWriterRunId || !getOwedHarnessCompletionTask(claim, entry)) return null; const result = options.identifiedResult; const projected = projectPendingFinalDeliverySettlement(entry, settlement, { claim, result: result ? { channel: result.channel, target: result.target, platformMessageId: readPlatformMessageId(result) } : void 0 }); settled = projected.state; wakeRecovery = projected.wakeRecovery; return projected.patch; }, patchOptions); if (wakeRecovery) { const { scheduleMainSessionRecoveryPendingTarget } = await import("./main-session-recovery-owner-release-Rrvejf22.mjs"); scheduleMainSessionRecoveryPendingTarget({ ...completion.agentId !== void 0 ? { agentId: completion.agentId } : {}, sessionId: completion.sessionId, sessionKey: completion.sessionKey, ...options.stateDir !== void 0 ? { stateDir: options.stateDir } : {}, storePath: completion.storePath }); } return { state: settled }; } function readPlatformMessageId(result) { return (result.receipt ? resolveMessageReceiptPrimaryId(result.receipt) : void 0) ?? (result.messageId.trim() || void 0); } /** Records queue ownership before either the live sender or recovery crosses platform I/O. */ async function markDurableDeliveryQueued(completion, queueId, expectedPendingFinalState, stateDir, stateContext, target) { return completion.kind === "pending-final" ? await settlePendingFinalDelivery(completion, "queued", expectedPendingFinalState ? ["prepared", "queued"] : void 0, { stateDir, stateContext }) : conversationResult(completion, (scope) => markConversationDeliveryQueued(scope, completion.operationId, queueId), stateDir, stateContext, target); } /** Finalizes owner state from identified platform evidence before queue acknowledgement. */ async function completeDurableDelivery(completion, result, stateDir, stateContext, target) { return settleDurableDelivery(completion, { result }, stateDir, stateContext, target); } /** Settles the completion owner from the final evidence held by its lifecycle owner. */ async function settleDurableDelivery(completion, evidence, stateDir, stateContext, target) { const state = "result" in evidence ? "delivered" : "platformSendStarted" in evidence && evidence.platformSendStarted ? "unknown" : "suppressed"; return completion.kind === "pending-final" ? await settlePendingFinalDelivery(completion, state, void 0, { stateDir, stateContext, ..."result" in evidence ? { identifiedResult: evidence.result } : {} }) : conversationResult(completion, (scope) => { if ("result" in evidence) return markConversationDeliverySent(scope, completion.operationId, readPlatformMessageId(evidence.result)); if ("rejectionError" in evidence) return markConversationDeliveryRejected(scope, completion.operationId, evidence.rejectionError); return evidence.platformSendStarted ? markConversationDeliveryUnknown(scope, completion.operationId) : markConversationDeliverySuppressed(scope, completion.operationId); }, stateDir, stateContext, target); } //#endregion export { settleDurableDelivery as a, findConversationTurnDeliveryByReplyTarget as c, markConversationDeliverySent as d, markConversationDeliverySuppressed as f, resolveConversationDeliveryScope as i, getConversationDeliveryOperation as l, completeDurableDelivery as n, settlePendingFinalDelivery as o, markDurableDeliveryQueued as r, beginConversationDeliveryOperation as s, captureConversationDeliveryTarget as t, markConversationDeliveryReplied as u };