mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 01:31:08 +00:00
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
import { SessionManager } from "@mariozechner/pi-coding-agent";
|
|
import {
|
|
acquireSessionWriteLock,
|
|
emitSessionTranscriptUpdate,
|
|
} from "openclaw/plugin-sdk/agent-harness";
|
|
|
|
export async function mirrorCodexAppServerTranscript(params: {
|
|
sessionFile: string;
|
|
sessionKey?: string;
|
|
messages: AgentMessage[];
|
|
idempotencyScope?: string;
|
|
}): Promise<void> {
|
|
const messages = params.messages.filter(
|
|
(message) => message.role === "user" || message.role === "assistant",
|
|
);
|
|
if (messages.length === 0) {
|
|
return;
|
|
}
|
|
|
|
await fs.mkdir(path.dirname(params.sessionFile), { recursive: true });
|
|
const lock = await acquireSessionWriteLock({
|
|
sessionFile: params.sessionFile,
|
|
timeoutMs: 10_000,
|
|
});
|
|
try {
|
|
const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
|
|
const sessionManager = SessionManager.open(params.sessionFile);
|
|
for (const [index, message] of messages.entries()) {
|
|
const idempotencyKey = params.idempotencyScope
|
|
? `${params.idempotencyScope}:${message.role}:${index}`
|
|
: undefined;
|
|
if (idempotencyKey && existingIdempotencyKeys.has(idempotencyKey)) {
|
|
continue;
|
|
}
|
|
const transcriptMessage = {
|
|
...message,
|
|
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
} as Parameters<SessionManager["appendMessage"]>[0];
|
|
sessionManager.appendMessage(transcriptMessage);
|
|
if (idempotencyKey) {
|
|
existingIdempotencyKeys.add(idempotencyKey);
|
|
}
|
|
}
|
|
} finally {
|
|
await lock.release();
|
|
}
|
|
|
|
if (params.sessionKey) {
|
|
emitSessionTranscriptUpdate({ sessionFile: params.sessionFile, sessionKey: params.sessionKey });
|
|
} else {
|
|
emitSessionTranscriptUpdate(params.sessionFile);
|
|
}
|
|
}
|
|
|
|
async function readTranscriptIdempotencyKeys(sessionFile: string): Promise<Set<string>> {
|
|
const keys = new Set<string>();
|
|
let raw: string;
|
|
try {
|
|
raw = await fs.readFile(sessionFile, "utf8");
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
throw error;
|
|
}
|
|
return keys;
|
|
}
|
|
for (const line of raw.split(/\r?\n/)) {
|
|
if (!line.trim()) {
|
|
continue;
|
|
}
|
|
try {
|
|
const parsed = JSON.parse(line) as { message?: { idempotencyKey?: unknown } };
|
|
if (typeof parsed.message?.idempotencyKey === "string") {
|
|
keys.add(parsed.message.idempotencyKey);
|
|
}
|
|
} catch {
|
|
continue;
|
|
}
|
|
}
|
|
return keys;
|
|
}
|