openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
153 lines (152 loc) • 6.78 kB
JavaScript
import { r as assertExistingDatabaseIdentity } from "./sqlite-worker-identity-BRNRpRyc.mjs";
import { t as SessionTranscriptWriterClaimReboundError } from "./session-transcript-writer-claim-error-eP-_sDN9.mjs";
import { o as registerOpenClawAgentDatabaseAsyncResource } from "./openclaw-agent-db-resources-Cm-sH7kg.mjs";
import { l as hasSqliteWorkerOutcomeUnknown } from "./sqlite-worker-contract-C51cJHzg.mjs";
import { r as captureOpenClawStateWorkerContext } from "./openclaw-state-worker-context-BvvgY6-t.mjs";
import { n as executeOpenClawStateWorker } from "./openclaw-state-worker-store-BxZXTKna.mjs";
import { r as getRuntimeConfig } from "./io.runtime-B134MOMz.mjs";
import "./io-BGddrCVX.mjs";
import { h as withSessionTranscriptWriteAssertion } from "./transcript-write-context-DwwjCPCE.mjs";
import { v as retainPreparedSessionEntryPredicate } from "./session-accessor.sqlite-entry-cache-publication-state-Ba-XKArI.mjs";
import { y as prepareSessionPlacementRead } from "./placement-row-codec-Cpn-JKmv.mjs";
import { u as withGatewaySessionEntry } from "./session-utils-store-qn-zT-Fk.mjs";
import { a as selectSourceDeliverablePayloads } from "./delivery-result-VTEWmJCm.mjs";
import { n as attachManagedOutgoingMediaToMessage, u as removeManagedOutgoingMediaBlocks } from "./managed-image-attachments-CNmXrf8z.mjs";
import { a as hasAssistantDisplayMediaContent } from "./chat-assistant-content-CZwd1b0s.mjs";
import { a as webchatReplyMediaAuthority, i as prepareWebchatReplyMediaForDisplay } from "./chat-reply-media-Bu6kF3Cz.mjs";
import { a as publishAssistantTranscriptRewrite, n as enrichAssistantTranscriptMediaForRun } from "./chat-transcript-persistence-JKSwSL50.mjs";
//#region src/gateway/agent-turn/agent-run-final-media.ts
/** Materialize one visible agent final before its Gateway run releases custody. */
async function finalizeAgentRunMedia(params, reply, candidates) {
const { options, session } = params;
const visible = new Set(selectSourceDeliverablePayloads(candidates.map((entry) => entry.payload), options));
const plan = candidates.filter((entry) => visible.has(entry.payload));
if (!plan.some(({ parts }) => parts.mediaUrls.length > 0)) return;
const runId = params.getRunId();
const assertCurrent = () => {
params.abortSignal?.throwIfAborted();
if (!params.isCurrent() || params.getRunId() !== runId) throw new SessionTranscriptWriterClaimReboundError();
};
assertCurrent();
let active = true;
let predicate;
let releaseSource;
let releasePlacement;
let work;
try {
const state = captureOpenClawStateWorkerContext();
const placement = await prepareSessionPlacementRead(state.admission.databasePath, reply.sessionId, async () => {
return (await executeOpenClawStateWorker(state, {
type: "workerPlacements.read",
input: { sessionIds: [reply.sessionId] }
}))[0];
});
releasePlacement = placement.release;
assertCurrent();
const initialPlacement = placement.current();
const prepared = await withGatewaySessionEntry(session.sessionKey, { agentId: session.agentId }, (selected) => {
assertCurrent();
const source = selected.capturedReadSource;
if (selected.storePath !== reply.storePath || selected.entry?.sessionId !== reply.sessionId || selected.entry.lifecycleRevision !== reply.lifecycleRevision || !source || typeof source.databaseIdentity !== "string") throw new SessionTranscriptWriterClaimReboundError();
const databaseIdentity = `file:${source.databaseIdentity}`;
const scope = {
...session,
sessionEntry: selected.entry,
requesterContext: params.requesterContext,
accountId: params.accountId
};
const expected = webchatReplyMediaAuthority(scope, initialPlacement);
predicate = retainPreparedSessionEntryPredicate({
databaseIdentity,
sessionKey: selected.legacyKey ?? selected.canonicalKey,
entry: selected.entry,
matches: (_before, after) => webchatReplyMediaAuthority({
...scope,
sessionEntry: after
}, initialPlacement).key === expected.key
});
releaseSource = registerOpenClawAgentDatabaseAsyncResource({
agentId: source.agentId,
path: source.path,
revoke: () => {
active = false;
},
close: async () => {
active = false;
await Promise.allSettled(work ? [work] : []);
}
});
return {
...scope,
readSource: source,
workspace: expected.workspace,
assertCurrent: () => {
assertCurrent();
assertExistingDatabaseIdentity(source.path, databaseIdentity, source.databaseBirthtime);
if (!active || !predicate?.isCurrent() || webchatReplyMediaAuthority({
...scope,
cfg: getRuntimeConfig()
}, placement.current()).key !== expected.key) throw new SessionTranscriptWriterClaimReboundError();
}
};
}, session.cfg);
work = (async () => {
const scope = {
sessionKey: session.sessionKey,
agentId: session.agentId,
sessionId: reply.sessionId,
storePath: reply.storePath
};
const { persistedAssistantContent: content } = await prepareWebchatReplyMediaForDisplay({
scope: prepared,
storePath: scope.storePath,
inputs: plan.map((entry) => ({
kind: "prepared",
plan: entry
})),
abortSignal: params.abortSignal,
includeSensitiveMedia: false,
includeSensitiveDisplay: false
});
if (!content || !hasAssistantDisplayMediaContent(content)) throw new Error("WebChat final media could not be prepared");
let retained = false;
try {
const rewritten = await withSessionTranscriptWriteAssertion(scope, prepared.assertCurrent, () => enrichAssistantTranscriptMediaForRun({
scope,
readSource: prepared.readSource,
runId,
expectedLifecycleRevision: reply.lifecycleRevision ?? null,
content,
mediaUrls: plan.flatMap((entry) => entry.parts.mediaUrls)
}));
if (!rewritten) throw new Error("WebChat final media has no owning assistant transcript message");
retained = true;
if (content.some((block) => block.type === "image" || block.type === "audio" || block.type === "video" || block.type === "attachment") && !await attachManagedOutgoingMediaToMessage({
messageId: rewritten.messageId,
blocks: content
})) throw new Error("WebChat final media ownership could not be persisted");
await publishAssistantTranscriptRewrite({
scope,
readSource: prepared.readSource,
rewritten: [rewritten]
});
} catch (error) {
retained ||= hasSqliteWorkerOutcomeUnknown(error);
throw error;
} finally {
if (!retained) await removeManagedOutgoingMediaBlocks({
blocks: content,
messageId: null
});
}
})();
await work;
} finally {
active = false;
predicate?.release();
releaseSource?.();
releasePlacement?.();
}
}
//#endregion
export { finalizeAgentRunMedia };