Skip to content

Commit 258b83c

Browse files
authored
refactor: migrate plugin transcript mirrors (#89518)
1 parent d095d98 commit 258b83c

11 files changed

Lines changed: 377 additions & 273 deletions

extensions/codex/src/app-server/attempt-context.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,16 @@ type CodexWorkspaceBootstrapContext = CodexBootstrapContext & {
7878
};
7979

8080
/** Reads mirrored Codex session history for harness hooks. */
81-
export async function readMirroredSessionHistoryMessages(
82-
sessionFile: string,
83-
): Promise<AgentMessage[] | undefined> {
84-
const messages = await readCodexMirroredSessionHistoryMessages(sessionFile);
81+
export async function readMirroredSessionHistoryMessages(params: {
82+
agentId?: string;
83+
sessionFile: string;
84+
sessionId: string;
85+
sessionKey?: string;
86+
}): Promise<AgentMessage[] | undefined> {
87+
const messages = await readCodexMirroredSessionHistoryMessages(params);
8588
if (!messages) {
8689
embeddedAgentLog.warn("failed to read mirrored session history for codex harness hooks", {
87-
sessionFile,
90+
sessionFile: params.sessionFile,
8891
});
8992
}
9093
return messages;

extensions/codex/src/app-server/event-projector.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,14 @@ export class CodexAppServerEventProjector {
18271827
}
18281828

18291829
private async readMirroredSessionMessages(): Promise<AgentMessage[]> {
1830-
return (await readCodexMirroredSessionHistoryMessages(this.params.sessionFile)) ?? [];
1830+
return (
1831+
(await readCodexMirroredSessionHistoryMessages({
1832+
agentId: this.params.agentId,
1833+
sessionFile: this.params.sessionFile,
1834+
sessionId: this.params.sessionId,
1835+
sessionKey: this.params.sessionKey,
1836+
})) ?? []
1837+
);
18311838
}
18321839

18331840
private createAssistantMessage(text: string): AssistantMessage {

extensions/codex/src/app-server/run-attempt.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,16 @@ export async function runCodexAppServerAttempt(
849849
},
850850
});
851851
const hadSessionFile = await pathExists(activeSessionFile);
852-
let historyMessages = (await readMirroredSessionHistoryMessages(activeSessionFile)) ?? [];
852+
const activeTranscriptTarget = {
853+
agentId: sessionAgentId,
854+
sessionFile: activeSessionFile,
855+
sessionId: activeSessionId,
856+
sessionKey: contextSessionKey,
857+
};
858+
let historyMessages =
859+
!activeContextEngine && initialStartupBindingHadInactiveThreadBootstrap
860+
? []
861+
: ((await readMirroredSessionHistoryMessages(activeTranscriptTarget)) ?? []);
853862
const hookContextWindowFields = {
854863
...(params.contextWindowInfo?.tokens
855864
? { contextTokenBudget: params.contextWindowInfo.tokens }
@@ -907,7 +916,7 @@ export async function runCodexAppServerAttempt(
907916
warn: (message) => embeddedAgentLog.warn(message),
908917
});
909918
historyMessages =
910-
(await readMirroredSessionHistoryMessages(activeSessionFile)) ?? historyMessages;
919+
(await readMirroredSessionHistoryMessages(activeTranscriptTarget)) ?? historyMessages;
911920
}
912921
const memoryToolNames = getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs);
913922
const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
@@ -3039,7 +3048,7 @@ export async function runCodexAppServerAttempt(
30393048
const activeContextEnginePluginIdLocal =
30403049
resolveContextEngineOwnerPluginId(activeContextEngine);
30413050
const finalMessages =
3042-
(await readMirroredSessionHistoryMessages(activeSessionFile)) ??
3051+
(await readMirroredSessionHistoryMessages(activeTranscriptTarget)) ??
30433052
historyMessages.concat(result.messagesSnapshot);
30443053
await finalizeHarnessContextEngineTurn({
30453054
contextEngine: activeContextEngine,

extensions/codex/src/app-server/session-history.test.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ function messageEntry(params: {
5151
};
5252
}
5353

54+
function mirroredTarget(sessionFile: string) {
55+
return {
56+
sessionFile,
57+
sessionId: "codex-session",
58+
sessionKey: "codex-session",
59+
};
60+
}
61+
5462
describe("readCodexMirroredSessionHistoryMessages", () => {
5563
it("replays only the branch selected by a leaf control", async () => {
5664
const sessionFile = await writeSession([
@@ -75,7 +83,9 @@ describe("readCodexMirroredSessionHistoryMessages", () => {
7583
},
7684
]);
7785

78-
await expect(readCodexMirroredSessionHistoryMessages(sessionFile)).resolves.toMatchObject([
86+
await expect(
87+
readCodexMirroredSessionHistoryMessages(mirroredTarget(sessionFile)),
88+
).resolves.toMatchObject([
7989
{ role: "user", content: "root prompt" },
8090
{ role: "assistant", content: "active answer" },
8191
]);
@@ -93,7 +103,9 @@ describe("readCodexMirroredSessionHistoryMessages", () => {
93103
},
94104
]);
95105

96-
await expect(readCodexMirroredSessionHistoryMessages(sessionFile)).resolves.toEqual([]);
106+
await expect(
107+
readCodexMirroredSessionHistoryMessages(mirroredTarget(sessionFile)),
108+
).resolves.toEqual([]);
97109
});
98110

99111
it("keeps visible history when continuation rows use a disjoint append cursor", async () => {
@@ -125,7 +137,9 @@ describe("readCodexMirroredSessionHistoryMessages", () => {
125137
}),
126138
]);
127139

128-
await expect(readCodexMirroredSessionHistoryMessages(sessionFile)).resolves.toMatchObject([
140+
await expect(
141+
readCodexMirroredSessionHistoryMessages(mirroredTarget(sessionFile)),
142+
).resolves.toMatchObject([
129143
{ role: "user", content: "visible prompt" },
130144
{ role: "assistant", content: "continued answer" },
131145
]);
@@ -154,7 +168,9 @@ describe("readCodexMirroredSessionHistoryMessages", () => {
154168
}),
155169
]);
156170

157-
await expect(readCodexMirroredSessionHistoryMessages(sessionFile)).resolves.toMatchObject([
171+
await expect(
172+
readCodexMirroredSessionHistoryMessages(mirroredTarget(sessionFile)),
173+
).resolves.toMatchObject([
158174
{ role: "user", content: "visible prompt" },
159175
{ role: "assistant", content: "continued answer" },
160176
]);

extensions/codex/src/app-server/session-history.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,59 @@ import {
1010
migrateSessionEntries,
1111
parseSessionEntries,
1212
} from "openclaw/plugin-sdk/agent-sessions";
13+
import {
14+
resolveSessionTranscriptTarget,
15+
type SessionTranscriptTargetParams,
16+
} from "openclaw/plugin-sdk/session-transcript-runtime";
1317
import { sanitizeCodexHistoryImagePayloads } from "./image-payload-sanitizer.js";
1418

15-
function isMissingFileError(error: unknown): boolean {
16-
return Boolean(
17-
error &&
18-
typeof error === "object" &&
19-
"code" in error &&
20-
(error as { code?: unknown }).code === "ENOENT",
21-
);
22-
}
19+
export type CodexMirroredSessionHistoryTarget = {
20+
agentId?: string;
21+
sessionFile: string;
22+
sessionId: string;
23+
sessionKey?: string;
24+
};
2325

2426
/** Returns sanitized session-context messages for a Codex mirrored session file. */
2527
export async function readCodexMirroredSessionHistoryMessages(
26-
sessionFile: string,
28+
target: CodexMirroredSessionHistoryTarget,
2729
): Promise<AgentMessage[] | undefined> {
2830
try {
29-
const raw = await fs.readFile(sessionFile, "utf-8");
31+
await resolveSessionTranscriptTarget(resolveCodexHistoryTranscriptTarget(target));
32+
const raw = await fs.readFile(target.sessionFile, "utf-8");
3033
const entries = parseSessionEntries(raw);
34+
if (entries.length === 0) {
35+
return [];
36+
}
3137
const firstEntry = entries[0] as { type?: unknown; id?: unknown } | undefined;
3238
if (firstEntry?.type !== "session" || typeof firstEntry.id !== "string") {
3339
return undefined;
3440
}
35-
migrateSessionEntries(entries);
36-
const sessionEntries = entries.filter(
37-
(entry): entry is SessionEntry => entry.type !== "session",
38-
);
41+
migrateSessionEntries(entries as SessionEntry[]);
42+
const sessionEntries = entries.filter((entry): entry is SessionEntry => {
43+
return (
44+
entry !== null &&
45+
typeof entry === "object" &&
46+
!Array.isArray(entry) &&
47+
(entry as { type?: unknown }).type !== "session"
48+
);
49+
});
3950
return sanitizeCodexHistoryImagePayloads(
4051
buildSessionContext(sessionEntries).messages,
4152
"codex mirrored history",
4253
);
43-
} catch (error) {
44-
if (isMissingFileError(error)) {
45-
return [];
46-
}
54+
} catch {
4755
return undefined;
4856
}
4957
}
58+
59+
function resolveCodexHistoryTranscriptTarget(
60+
target: CodexMirroredSessionHistoryTarget,
61+
): SessionTranscriptTargetParams {
62+
return {
63+
...(target.agentId ? { agentId: target.agentId } : {}),
64+
sessionFile: target.sessionFile,
65+
sessionId: target.sessionId,
66+
sessionKey: target.sessionKey ?? "",
67+
};
68+
}

0 commit comments

Comments
 (0)