





















@@ -243,6 +243,8 @@ type AppendSessionTranscriptMessageParams<TMessage = unknown> = {
243243useRawWhenLinear?: boolean;
244244/** Opt into transcript idempotency lookup; default append stays O(1) for fresh keyed messages. */
245245idempotencyLookup?: "scan" | "caller-checked";
246+/** Runs under the transcript write lock after idempotency replay checks and before append. */
247+prepareMessageAfterIdempotencyCheck?: (message: TMessage) => TMessage | undefined;
246248config?: OpenClawConfig;
247249};
248250@@ -261,9 +263,17 @@ function isTranscriptAgentMessage(value: unknown): value is AgentMessage {
261263);
262264}
263265266+export async function appendSessionTranscriptMessage<TMessage>(
267+params: AppendSessionTranscriptMessageParams<TMessage> & {
268+prepareMessageAfterIdempotencyCheck: (message: TMessage) => TMessage | undefined;
269+},
270+): Promise<AppendSessionTranscriptMessageResult<TMessage> | undefined>;
271+export async function appendSessionTranscriptMessage<TMessage>(
272+params: AppendSessionTranscriptMessageParams<TMessage>,
273+): Promise<AppendSessionTranscriptMessageResult<TMessage>>;
264274export async function appendSessionTranscriptMessage<TMessage>(
265275params: AppendSessionTranscriptMessageParams<TMessage>,
266-): Promise<AppendSessionTranscriptMessageResult<TMessage>> {
276+): Promise<AppendSessionTranscriptMessageResult<TMessage> | undefined> {
267277const activeLockRunner = resolveOwnedSessionTranscriptWriteLockRunner({
268278sessionFile: params.transcriptPath,
269279});
@@ -300,7 +310,7 @@ async function withSessionTranscriptWriteLock<T>(
300310301311async function appendSessionTranscriptMessageLocked<TMessage>(
302312params: AppendSessionTranscriptMessageParams<TMessage>,
303-): Promise<AppendSessionTranscriptMessageResult<TMessage>> {
313+): Promise<AppendSessionTranscriptMessageResult<TMessage> | undefined> {
304314const now = params.now ?? Date.now();
305315await ensureTranscriptHeader(params.transcriptPath, {
306316 ...(params.sessionId ? { sessionId: params.sessionId } : {}),
@@ -315,6 +325,13 @@ async function appendSessionTranscriptMessageLocked<TMessage>(
315325return { ...existing, message: existing.message as TMessage, appended: false };
316326}
317327328+const message = params.prepareMessageAfterIdempotencyCheck
329+ ? params.prepareMessageAfterIdempotencyCheck(params.message)
330+ : params.message;
331+if (message === undefined) {
332+return undefined;
333+}
334+318335const messageId = randomUUID();
319336const stat = await fs.stat(params.transcriptPath).catch(() => null);
320337let leafInfo: TranscriptLeafInfo = await readTranscriptLeafInfo(params.transcriptPath).catch(
@@ -336,9 +353,9 @@ async function appendSessionTranscriptMessageLocked<TMessage>(
336353};
337354}
338355const finalMessage = (
339-isTranscriptAgentMessage(params.message)
340- ? redactTranscriptMessage(params.message, params.config)
341- : redactSecrets(params.message)
356+isTranscriptAgentMessage(message)
357+ ? redactTranscriptMessage(message, params.config)
358+ : redactSecrets(message)
342359) as TMessage;
343360const entry = {
344361type: "message",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。