openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
149 lines (148 loc) • 6.71 kB
JavaScript
import { l as resolveSessionStorePathCore } from "./paths-CAzSovN1.mjs";
import { s as getOwnedSessionTranscriptWriterFence } from "./transcript-write-context-DdT_6wqt.mjs";
import { E as resolveSessionEntry, v as loadExactSessionEntry } from "./session-accessor.sqlite-pending-inputs-BpI_NyvM.mjs";
import { v as readTranscriptEventId, y as readTranscriptEventMessage } from "./session-accessor.sqlite-read-CAR7IGtb.mjs";
import { n as readActiveTranscriptEntryAnchor } from "./session-accessor.sqlite-transcript-anchor-Cxy4d8xe.mjs";
import { i as retainAssistantModelContent, r as readAssistantDisplayContent } from "./assistant-display-content-mzartsGP.mjs";
import { f as persistSessionTranscriptTurn, m as sessionMatchesExpectedTranscriptTurn } from "./session-accessor-BM3Wa1Bx.mjs";
import { t as findTranscriptEvent } from "./session-transcript-match-BsYSZyUw.mjs";
import "./sessions-nJdQddS3.mjs";
import { t as appendAssistantMessageToSessionTranscript } from "./transcript-D08D0EMY.mjs";
import { r as getAgentScopedMediaLocalRootsForSources } from "./local-roots-CQtu4chZ.mjs";
import { t as createKeyedFifoLeaseRegistry } from "./keyed-fifo-lease-PQSbpEtY.mjs";
import { n as readClawHubRecommendations } from "./clawhub-recommendations-Da9BiasP.mjs";
import { a as createManagedOutgoingMediaBlocks, c as prepareOutgoingMediaFromReplyPayload, n as attachManagedOutgoingMediaToMessage, u as removeManagedOutgoingMediaBlocks } from "./managed-image-attachments-bqFcPGbP.mjs";
//#region src/gateway/internal-source-reply-persistence.ts
const internalSourceReplyPersistenceLeases = createKeyedFifoLeaseRegistry(Symbol.for("openclaw.internalSourceReplyPersistenceLeases"));
async function completePersistedInternalSourceReply(params) {
if (!params.expectedSessionId || !params.idempotencyKey) return false;
const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId: params.agentId });
const scope = {
agentId: params.agentId,
sessionId: params.expectedSessionId,
sessionKey: params.sessionKey,
storePath
};
scope.sessionKey = resolveSessionEntry(scope).normalizedKey;
const expected = {
expectedSessionId: params.expectedSessionId,
...getOwnedSessionTranscriptWriterFence({ sessionKey: scope.sessionKey })
};
const found = await findTranscriptEvent(scope, {
kind: "idempotency",
key: params.idempotencyKey,
deliveryMirror: true
});
if (!found) return false;
const messageId = readTranscriptEventId(found.event);
const message = readTranscriptEventMessage(found.event);
if (!messageId || !message) throw new Error("Internal source reply transcript identity is unavailable");
const assertCurrentReplay = (entryId) => {
if (!sessionMatchesExpectedTranscriptTurn(loadExactSessionEntry(scope), expected) || !readActiveTranscriptEntryAnchor({
...scope,
entryId
})) throw new Error("Internal source reply no longer owns the active transcript");
};
const replay = await persistSessionTranscriptTurn(scope, {
config: params.cfg,
...expected,
messages: [{
eventId: messageId,
message,
idempotencyLookup: "scan",
shouldAppendInTransaction: () => {
assertCurrentReplay(messageId);
return true;
}
}],
touchSessionEntry: false,
updateMode: "file-only",
publishWhen: "always",
onMessageCommitted: (result, acceptCompletion) => {
assertCurrentReplay(result.messageId);
attachSourceReplyMedia(result, acceptCompletion);
}
});
if (replay.rejectedReason || replay.messages.length === 0) throw new Error("Internal source reply no longer owns the active transcript");
return true;
}
function attachSourceReplyMedia(result, acceptCompletion) {
const message = result.message;
const blocks = readAssistantDisplayContent(message).filter((block) => block.type !== "text" && block.type !== "clawhub");
if (blocks.length > 0) acceptCompletion(async () => {
if (!await attachManagedOutgoingMediaToMessage({
messageId: result.messageId,
blocks
})) throw new Error("Internal source reply media ownership could not be persisted");
});
}
/** Persist the private WebChat source reply before its successful tool result becomes visible. */
async function persistInternalSourceReply(params) {
const leaseKey = params.idempotencyKey ? JSON.stringify([
params.agentId ?? "",
params.sessionKey,
params.expectedSessionId ?? "",
params.idempotencyKey
]) : void 0;
const lease = leaseKey ? internalSourceReplyPersistenceLeases.reserve([leaseKey]) : void 0;
await lease?.wait();
try {
if (await completePersistedInternalSourceReply(params)) return;
const media = prepareOutgoingMediaFromReplyPayload(params.payload);
const mediaBlocks = await createManagedOutgoingMediaBlocks({
sessionKey: params.sessionKey,
agentId: params.agentId,
items: media,
localRoots: getAgentScopedMediaLocalRootsForSources({
cfg: params.cfg,
agentId: params.agentId,
mediaSources: media.map((item) => item.url)
})
});
let committed = false;
try {
const content = [
...readClawHubRecommendations(params.payload.channelData),
...params.payload.text ? [{
type: "text",
text: params.payload.text
}] : [],
...mediaBlocks
];
const writerFence = getOwnedSessionTranscriptWriterFence({ sessionKey: params.sessionKey });
const appended = await appendAssistantMessageToSessionTranscript({
agentId: params.agentId,
sessionKey: params.sessionKey,
...params.expectedSessionId ? { expectedSessionId: params.expectedSessionId } : {},
...writerFence?.expectedLifecycleRevision !== void 0 ? { expectedLifecycleRevision: writerFence.expectedLifecycleRevision } : {},
...writerFence ? { expectedWriterRunId: writerFence.expectedWriterRunId } : {},
content: retainAssistantModelContent(content),
displayContent: content,
mediaUrls: media.map((item) => item.url),
idempotencyKey: params.idempotencyKey,
runId: params.runId,
...params.sourceReplyFinal !== void 0 ? { deliveryMirror: {
kind: "message-tool-source-reply",
final: params.sourceReplyFinal,
...params.toolCallId ? { toolCallId: params.toolCallId } : {},
...params.sourceTurnId ? { sourceTurnId: params.sourceTurnId } : {}
} } : {},
config: params.cfg,
onMessageCommitted: (result, acceptCompletion) => {
committed = result.appended;
attachSourceReplyMedia(result, acceptCompletion);
}
});
if (!appended.ok) throw new Error(`Internal source reply persistence failed: ${appended.reason}`);
} finally {
if (!committed) await removeManagedOutgoingMediaBlocks({
blocks: mediaBlocks,
messageId: null
});
}
} finally {
lease?.release();
}
}
//#endregion
export { persistInternalSourceReply };