fix(codex): resume compressed rollout representations

This commit is contained in:
Brennan Benson
2026-07-20 22:41:09 -07:00
parent 0654c955de
commit 9327a7b643
5 changed files with 169 additions and 31 deletions
@@ -9,7 +9,7 @@ import {
writeFileSync
} from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { basename, join } from 'node:path'
import { prepareLegacySharedCodexSessionResume } from './codex-legacy-session-resume'
describe('prepareLegacySharedCodexSessionResume', () => {
@@ -47,6 +47,16 @@ describe('prepareLegacySharedCodexSessionResume', () => {
expect(statSync(targetPath).ino).toBe(statSync(rolloutPath).ino)
})
it('materializes a compressed legacy rollout without changing its representation', async () => {
const compressedPath = `${rolloutPath}.zst`
rmSync(rolloutPath)
rolloutPath = compressedPath
writeFileSync(rolloutPath, 'compressed-rollout', 'utf-8')
await expect(prepare()).resolves.toEqual({ useRealCodexHome: true })
expect(readFileSync(targetRolloutPath(), 'utf-8')).toBe('compressed-rollout')
})
it('coalesces concurrent resumes of the same legacy rollout', async () => {
const [first, second] = await Promise.all([prepare(), prepare()])
@@ -129,14 +139,7 @@ describe('prepareLegacySharedCodexSessionResume', () => {
}
function targetRolloutPath(): string {
return join(
systemHome,
'sessions',
'2026',
'07',
'20',
'rollout-2026-07-20T12-00-00-session.jsonl'
)
return join(systemHome, 'sessions', '2026', '07', '20', basename(rolloutPath))
}
})
@@ -162,7 +162,7 @@ function isLegacyRolloutRelativePath(relativePath: string): boolean {
/^\d{4}$/.test(parts[0] ?? '') &&
/^\d{2}$/.test(parts[1] ?? '') &&
/^\d{2}$/.test(parts[2] ?? '') &&
/^rollout-.+\.jsonl$/.test(parts[3] ?? '')
/^rollout-.+\.jsonl(?:\.zst)?$/.test(parts[3] ?? '')
)
}
+29 -1
View File
@@ -58,6 +58,34 @@ export async function* listCodexSessionJsonlFilesIncrementally(
rootPath: string,
options: CodexSessionBridgeIncrementalOptions,
onDirectoryError?: (directoryPath: string, error: unknown) => void | Promise<void>
): AsyncGenerator<string> {
yield* listCodexSessionFilesIncrementally(
rootPath,
options,
(fileName) => fileName.endsWith('.jsonl'),
onDirectoryError
)
}
/** Yields both physical representations that current Codex can resume. */
export async function* listCodexSessionRolloutFilesIncrementally(
rootPath: string,
options: CodexSessionBridgeIncrementalOptions,
onDirectoryError?: (directoryPath: string, error: unknown) => void | Promise<void>
): AsyncGenerator<string> {
yield* listCodexSessionFilesIncrementally(
rootPath,
options,
(fileName) => fileName.endsWith('.jsonl') || fileName.endsWith('.jsonl.zst'),
onDirectoryError
)
}
async function* listCodexSessionFilesIncrementally(
rootPath: string,
options: CodexSessionBridgeIncrementalOptions,
isSessionFile: (fileName: string) => boolean,
onDirectoryError?: (directoryPath: string, error: unknown) => void | Promise<void>
): AsyncGenerator<string> {
const batchSize = Math.max(1, options.batchSize ?? INCREMENTAL_BRIDGE_BATCH_SIZE)
const yieldMs = Math.max(0, options.yieldMs ?? INCREMENTAL_BRIDGE_YIELD_MS)
@@ -75,7 +103,7 @@ export async function* listCodexSessionJsonlFilesIncrementally(
const childPath = join(currentDirectory, entry.name)
if (entry.isDirectory()) {
pendingDirectories.push(childPath)
} else if (entry.isFile() && entry.name.endsWith('.jsonl')) {
} else if (entry.isFile() && isSessionFile(entry.name)) {
yield childPath
}
entriesSinceYield += 1
@@ -95,11 +95,69 @@ describe('resolveTrustedCodexSessionResumeHome', () => {
).toBe(homePath)
})
it('follows Codex when a persisted plain rollout was compressed in place', async () => {
const homePath = mkdtempSync(join(tmpdir(), 'orca-codex-resume-home-'))
tempRoots.push(homePath)
const plainPath = join(
homePath,
'sessions',
'2026',
'07',
'20',
'rollout-2026-07-20T12-00-00-session.jsonl'
)
const compressedPath = `${plainPath}.zst`
mkdirSync(join(plainPath, '..'), { recursive: true })
writeFileSync(compressedPath, 'compressed-rollout')
await expect(
findTrustedCodexSessionResume({
sessionId: 'session-a',
transcriptPath: plainPath,
trustedCodexHomes: [homePath]
})
).resolves.toEqual({ homePath, transcriptPath: compressedPath })
writeFileSync(plainPath, 'active-rollout')
await expect(
findTrustedCodexSessionResume({
sessionId: 'session-a',
transcriptPath: compressedPath,
trustedCodexHomes: [homePath]
})
).resolves.toEqual({ homePath, transcriptPath: plainPath })
})
it('finds compressed rollouts for legacy records without transcript provenance', async () => {
const homePath = mkdtempSync(join(tmpdir(), 'orca-codex-resume-home-'))
tempRoots.push(homePath)
const sessionId = '019f81b9-19a9-7651-a8d1-352d9420bd11'
const compressedPath = join(
homePath,
'sessions',
'2026',
'07',
'20',
`rollout-2026-07-20T12-00-00-${sessionId}.jsonl.zst`
)
mkdirSync(join(compressedPath, '..'), { recursive: true })
writeFileSync(compressedPath, 'compressed-rollout')
await expect(
findTrustedCodexSessionResume({
sessionId,
transcriptPath: undefined,
trustedCodexHomes: [homePath]
})
).resolves.toEqual({ homePath, transcriptPath: compressedPath })
})
it('finds older saved sessions by id when transcript provenance is absent', async () => {
const sessionId = '019f81b9-19a9-7651-a8d1-352d9420bd11'
const rolloutPath = `/managed/account/home/sessions/2026/07/20/rollout-2026-07-20T15-50-19-${sessionId}.jsonl`
const listSessionFiles = async function* (sessionsRoot: string): AsyncIterable<string> {
if (sessionsRoot === '/managed/account/home/sessions') {
yield `/managed/account/home/sessions/misplaced-${sessionId}.jsonl`
yield rolloutPath
}
}
+69 -20
View File
@@ -5,9 +5,15 @@ import {
normalizeRuntimePathForComparison,
relativePathInsideRoot
} from '../../shared/cross-platform-path'
import { listCodexSessionJsonlFilesIncrementally } from './codex-session-file-listing'
import { listCodexSessionRolloutFilesIncrementally } from './codex-session-file-listing'
const ROLLOUT_RELATIVE_PATH = /^\d{4}\/\d{2}\/\d{2}\/rollout-[^/]+\.jsonl$/
// Why: only Codex's dated rollout layout may establish account-home provenance; nested/misplaced JSONL must not select credentials.
const ROLLOUT_RELATIVE_PATH = /^\d{4}\/\d{2}\/\d{2}\/rollout-[^/]+\.jsonl(?:\.zst)?$/
function isCodexRolloutInsideSessionsRoot(sessionsRoot: string, filePath: string): boolean {
const relativePath = relativePathInsideRoot(sessionsRoot, filePath)
return Boolean(relativePath && ROLLOUT_RELATIVE_PATH.test(relativePath.replace(/\\/g, '/')))
}
function isRegularFile(filePath: string): boolean {
try {
@@ -17,23 +23,52 @@ function isRegularFile(filePath: string): boolean {
}
}
function resolveExistingRolloutPath(
transcriptPath: string,
fileIsRegular: (filePath: string) => boolean
): string | null {
const plainPath = transcriptPath.endsWith('.jsonl.zst')
? transcriptPath.slice(0, -'.zst'.length)
: transcriptPath.endsWith('.jsonl')
? transcriptPath
: null
if (!plainPath) {
return fileIsRegular(transcriptPath) ? transcriptPath : null
}
if (fileIsRegular(plainPath)) {
return plainPath
}
const compressedPath = `${plainPath}.zst`
return fileIsRegular(compressedPath) ? compressedPath : null
}
function resolveTrustedCodexSessionResume(args: {
transcriptPath: string | undefined
trustedCodexHomes: readonly string[]
fileIsRegular?: (filePath: string) => boolean
}): { homePath: string; transcriptPath: string } | null {
const persistedPath = args.transcriptPath?.trim()
const transcriptPath = persistedPath
? resolveExistingRolloutPath(persistedPath, args.fileIsRegular ?? isRegularFile)
: null
if (!transcriptPath) {
return null
}
for (const homePath of args.trustedCodexHomes) {
if (isCodexRolloutInsideSessionsRoot(join(homePath, 'sessions'), transcriptPath)) {
return { homePath, transcriptPath }
}
}
return null
}
export function resolveTrustedCodexSessionResumeHome(args: {
transcriptPath: string | undefined
trustedCodexHomes: readonly string[]
fileIsRegular?: (filePath: string) => boolean
}): string | null {
const transcriptPath = args.transcriptPath?.trim()
if (!transcriptPath || !(args.fileIsRegular ?? isRegularFile)(transcriptPath)) {
return null
}
for (const homePath of args.trustedCodexHomes) {
const relativePath = relativePathInsideRoot(join(homePath, 'sessions'), transcriptPath)
if (relativePath && ROLLOUT_RELATIVE_PATH.test(relativePath.replace(/\\/g, '/'))) {
return homePath
}
}
return null
return resolveTrustedCodexSessionResume(args)?.homePath ?? null
}
export async function findTrustedCodexSessionResume(args: {
@@ -43,9 +78,9 @@ export async function findTrustedCodexSessionResume(args: {
fileIsRegular?: (filePath: string) => boolean
listSessionFiles?: (sessionsRoot: string) => AsyncIterable<string>
}): Promise<{ homePath: string; transcriptPath: string } | null> {
const directHome = resolveTrustedCodexSessionResumeHome(args)
if (directHome && args.transcriptPath) {
return { homePath: directHome, transcriptPath: args.transcriptPath.trim() }
const directSession = resolveTrustedCodexSessionResume(args)
if (directSession) {
return directSession
}
if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(args.sessionId)) {
return null
@@ -54,7 +89,7 @@ export async function findTrustedCodexSessionResume(args: {
const listSessionFiles =
args.listSessionFiles ??
((sessionsRoot: string) =>
listCodexSessionJsonlFilesIncrementally(sessionsRoot, { batchSize: 64, yieldMs: 0 }))
listCodexSessionRolloutFilesIncrementally(sessionsRoot, { batchSize: 64, yieldMs: 0 }))
const expectedSuffix = `-${args.sessionId}.jsonl`.toLowerCase()
const seenHomes = new Set<string>()
for (const homePath of args.trustedCodexHomes) {
@@ -68,8 +103,22 @@ export async function findTrustedCodexSessionResume(args: {
continue
}
for await (const filePath of listSessionFiles(sessionsRoot)) {
if (getRuntimePathBasename(filePath).toLowerCase().endsWith(expectedSuffix)) {
return { homePath, transcriptPath: filePath }
const plainFilePath = filePath.endsWith('.jsonl.zst')
? filePath.slice(0, -'.zst'.length)
: filePath
// Why: the directory entry already proves the compressed file exists; only probe its preferred plain sibling.
const preferredFilePath =
plainFilePath !== filePath && (args.fileIsRegular ?? isRegularFile)(plainFilePath)
? plainFilePath
: filePath
const plainFileName = getRuntimePathBasename(preferredFilePath)
.toLowerCase()
.replace(/\.zst$/, '')
if (
isCodexRolloutInsideSessionsRoot(sessionsRoot, preferredFilePath) &&
plainFileName.endsWith(expectedSuffix)
) {
return { homePath, transcriptPath: preferredFilePath }
}
}
}