import { isRecord } from "@openclaw/normalization-core/record-coerce"; import type { TranscriptMessageAppendOptions, TranscriptMessageAppendResult, } from "../config/sessions/session-accessor.js"; import type { AgentMessage } from "./agent-core.js"; import { withProjectedSessionTranscriptWriteLock, type InternalSessionTranscriptWriteLockContext, type InternalSessionTranscriptWriteLockParams, } from "./session-transcript-lock-runtime.js"; import { publishSessionTranscriptUpdateByIdentity } from "./session-transcript-runtime.js"; export type CodexSessionTranscriptMirrorWriteLockContext = InternalSessionTranscriptWriteLockContext & { appendMessageWithMessageSequence: ( options: Omit, "config">, ) => Promise<{ messageSeq?: number; result: TranscriptMessageAppendResult | undefined; }>; readMessageFacts: (params: { idempotencyKeys: readonly string[] }) => Promise<{ existingIdempotencyKeys: Set; messagesByIdempotencyKey: Map; }>; }; /** Runs the bundled Codex mirror under the transcript writer lock. */ export async function withCodexSessionTranscriptMirrorWriteLock( params: InternalSessionTranscriptWriteLockParams, run: (context: CodexSessionTranscriptMirrorWriteLockContext) => Promise | T, ): Promise { return await withProjectedSessionTranscriptWriteLock( params, run, (context, locked) => ({ ...context, appendMessageWithMessageSequence: (options) => locked.appendMessageWithMessageSequence({ ...options, ...(params.config !== undefined ? { config: params.config } : {}), }), readMessageFacts: async (factParams) => { const facts = await locked.readMessageFacts(factParams); const messagesByIdempotencyKey = new Map(); for (const [idempotencyKey, message] of facts.messagesByIdempotencyKey) { if (isAgentMessageRecord(message)) { messagesByIdempotencyKey.set(idempotencyKey, message); } } return { ...facts, messagesByIdempotencyKey }; }, }), publishSessionTranscriptUpdateByIdentity, ); } function isAgentMessageRecord(value: unknown): value is AgentMessage & Record { return isRecord(value) && typeof value.role === "string" && value.role.trim().length > 0; }