fix(codex): preserve transcript sync across mode switches

This commit is contained in:
weishu
2026-07-28 12:20:54 +08:00
parent f0c7d0b7e7
commit 92958890de
6 changed files with 118 additions and 9 deletions
+21 -3
View File
@@ -46,6 +46,7 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch
let pendingScannerSetup: Promise<void> | null = null;
let transcriptLocator: CodexTranscriptLocator | null = null;
let scannerTranscriptPath: string | null = null;
let scannerReplayedExistingHistory = false;
const pendingPlansByTurnId = new Map<string, ProposedPlanMessage>();
const pendingExecWrappers = new Map<string, PendingExecWrapper>();
const toolHookBridge = new CodexToolHookBridge();
@@ -76,6 +77,21 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch
});
};
const drainAndCleanupScanner = async (
activeScanner: CodexSessionScanner,
replayedExistingHistory: boolean
): Promise<void> => {
try {
// Codex can flush its final transcript records after the last watcher tick.
await activeScanner.flush();
if (replayedExistingHistory) {
session.markTranscriptHistoryReplayConsumed();
}
} finally {
await activeScanner.cleanup();
}
};
const handleSessionFound = (sessionId: string, allowSwitch = false): void => {
if (primarySessionId && primarySessionId !== sessionId && !allowSwitch) {
logger.debug(`[codex-local]: Ignoring non-primary Codex session id ${sessionId}; primary is ${primarySessionId}`);
@@ -174,10 +190,11 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch
scannerTranscriptPath = transcriptPath;
return;
}
const replayExistingHistory = session.shouldReplayTranscriptHistory();
const createdScanner = await createCodexSessionScanner({
transcriptPath,
// 中文注释:导入模式下允许 scanner 首次回放 transcript 全量内容,补齐 Codex 客户端里已有但 Hapi 还未看到的消息。
replayExistingHistory: session.replayTranscriptHistoryOnStart,
replayExistingHistory,
onSessionId: (sessionId) => {
if (!isPrimarySessionId(sessionId)) {
logger.debug(`[codex-local]: Ignoring transcript session id ${sessionId}; primary is ${primarySessionId}`);
@@ -237,11 +254,12 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch
}
});
if (shuttingDown) {
await createdScanner.cleanup();
await drainAndCleanupScanner(createdScanner, replayExistingHistory);
return;
}
scanner = createdScanner;
scannerTranscriptPath = transcriptPath;
scannerReplayedExistingHistory = replayExistingHistory;
};
const handleTranscriptPath = (transcriptPath: string): Promise<void> => {
@@ -376,7 +394,7 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch
}
const activeScanner = scanner as CodexSessionScanner | null;
if (activeScanner) {
await activeScanner.cleanup();
await drainAndCleanupScanner(activeScanner, scannerReplayedExistingHistory);
}
flushAllPendingExecWrappers();
for (const message of toolHookBridge.finish()) {