mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 16:02:42 +00:00
fix(codex): keep automatic resume homes launch-authoritative
This commit is contained in:
@@ -1,16 +1,27 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
findTrustedCodexSessionResume,
|
||||
resolveTrustedCodexSessionResumeHome
|
||||
} from './codex-session-resume-home'
|
||||
|
||||
const tempRoots: string[] = []
|
||||
|
||||
afterEach(() => {
|
||||
for (const root of tempRoots.splice(0)) {
|
||||
rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
describe('resolveTrustedCodexSessionResumeHome', () => {
|
||||
it('returns the trusted home containing a persisted rollout', () => {
|
||||
expect(
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: '/Users/example/.codex/sessions/2026/07/20/rollout-session.jsonl',
|
||||
trustedCodexHomes: ['/managed/account/home', '/Users/example/.codex'],
|
||||
fileExists: () => true
|
||||
fileIsRegular: () => true
|
||||
})
|
||||
).toBe('/Users/example/.codex')
|
||||
})
|
||||
@@ -20,25 +31,33 @@ describe('resolveTrustedCodexSessionResumeHome', () => {
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: 'C:\\Users\\Example\\.codex\\sessions\\2026\\07\\20\\rollout-a.jsonl',
|
||||
trustedCodexHomes: ['c:\\users\\example\\.codex'],
|
||||
fileExists: () => true
|
||||
fileIsRegular: () => true
|
||||
})
|
||||
).toBe('c:\\users\\example\\.codex')
|
||||
})
|
||||
|
||||
it('rejects paths outside trusted homes or outside the rollout layout', () => {
|
||||
const fileExists = (): boolean => true
|
||||
const fileIsRegular = (): boolean => true
|
||||
expect(
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: '/tmp/sessions/2026/07/20/rollout-a.jsonl',
|
||||
trustedCodexHomes: ['/Users/example/.codex'],
|
||||
fileExists
|
||||
fileIsRegular
|
||||
})
|
||||
).toBeNull()
|
||||
expect(
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: '/Users/example/.codex/sessions/index.jsonl',
|
||||
trustedCodexHomes: ['/Users/example/.codex'],
|
||||
fileExists
|
||||
fileIsRegular
|
||||
})
|
||||
).toBeNull()
|
||||
expect(
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath:
|
||||
'/Users/example/.codex/sessions/2026/07/20/rollout-a/../../../../outside.jsonl',
|
||||
trustedCodexHomes: ['/Users/example/.codex'],
|
||||
fileIsRegular
|
||||
})
|
||||
).toBeNull()
|
||||
})
|
||||
@@ -48,11 +67,34 @@ describe('resolveTrustedCodexSessionResumeHome', () => {
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: '/Users/example/.codex/sessions/2026/07/20/rollout-a.jsonl',
|
||||
trustedCodexHomes: ['/Users/example/.codex'],
|
||||
fileExists: () => false
|
||||
fileIsRegular: () => false
|
||||
})
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('requires the transcript provenance to name a regular rollout file', () => {
|
||||
const homePath = mkdtempSync(join(tmpdir(), 'orca-codex-resume-home-'))
|
||||
tempRoots.push(homePath)
|
||||
const rolloutDirectory = join(homePath, 'sessions', '2026', '07', '20', 'rollout-a.jsonl')
|
||||
mkdirSync(rolloutDirectory, { recursive: true })
|
||||
|
||||
expect(
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: rolloutDirectory,
|
||||
trustedCodexHomes: [homePath]
|
||||
})
|
||||
).toBeNull()
|
||||
|
||||
const rolloutFile = join(homePath, 'sessions', '2026', '07', '20', 'rollout-b.jsonl')
|
||||
writeFileSync(rolloutFile, '{}\n')
|
||||
expect(
|
||||
resolveTrustedCodexSessionResumeHome({
|
||||
transcriptPath: rolloutFile,
|
||||
trustedCodexHomes: [homePath]
|
||||
})
|
||||
).toBe(homePath)
|
||||
})
|
||||
|
||||
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`
|
||||
@@ -72,6 +114,25 @@ describe('resolveTrustedCodexSessionResumeHome', () => {
|
||||
).resolves.toEqual({ homePath: '/managed/account/home', transcriptPath: rolloutPath })
|
||||
})
|
||||
|
||||
it('does not scan session trees when exact transcript provenance is valid', async () => {
|
||||
const transcriptPath =
|
||||
'/managed/account/home/sessions/2026/07/20/rollout-2026-07-20-session.jsonl'
|
||||
const listSessionFiles = vi.fn((): AsyncIterable<string> => {
|
||||
throw new Error('must not scan')
|
||||
})
|
||||
|
||||
await expect(
|
||||
findTrustedCodexSessionResume({
|
||||
sessionId: 'session-a',
|
||||
transcriptPath,
|
||||
trustedCodexHomes: ['/managed/account/home'],
|
||||
fileIsRegular: () => true,
|
||||
listSessionFiles
|
||||
})
|
||||
).resolves.toEqual({ homePath: '/managed/account/home', transcriptPath })
|
||||
expect(listSessionFiles).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not scan homes for an untrusted legacy session id shape', async () => {
|
||||
const listSessionFiles = (): AsyncIterable<string> => {
|
||||
throw new Error('must not scan')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { existsSync, lstatSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
getRuntimePathBasename,
|
||||
@@ -7,15 +7,23 @@ import {
|
||||
} from '../../shared/cross-platform-path'
|
||||
import { listCodexSessionJsonlFilesIncrementally } from './codex-session-file-listing'
|
||||
|
||||
const ROLLOUT_RELATIVE_PATH = /^\d{4}\/\d{2}\/\d{2}\/rollout-.+\.jsonl$/
|
||||
const ROLLOUT_RELATIVE_PATH = /^\d{4}\/\d{2}\/\d{2}\/rollout-[^/]+\.jsonl$/
|
||||
|
||||
function isRegularFile(filePath: string): boolean {
|
||||
try {
|
||||
return lstatSync(filePath).isFile()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveTrustedCodexSessionResumeHome(args: {
|
||||
transcriptPath: string | undefined
|
||||
trustedCodexHomes: readonly string[]
|
||||
fileExists?: (filePath: string) => boolean
|
||||
fileIsRegular?: (filePath: string) => boolean
|
||||
}): string | null {
|
||||
const transcriptPath = args.transcriptPath?.trim()
|
||||
if (!transcriptPath || !(args.fileExists ?? existsSync)(transcriptPath)) {
|
||||
if (!transcriptPath || !(args.fileIsRegular ?? isRegularFile)(transcriptPath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -32,7 +40,7 @@ export async function findTrustedCodexSessionResume(args: {
|
||||
sessionId: string
|
||||
transcriptPath: string | undefined
|
||||
trustedCodexHomes: readonly string[]
|
||||
fileExists?: (filePath: string) => boolean
|
||||
fileIsRegular?: (filePath: string) => boolean
|
||||
listSessionFiles?: (sessionsRoot: string) => AsyncIterable<string>
|
||||
}): Promise<{ homePath: string; transcriptPath: string } | null> {
|
||||
const directHome = resolveTrustedCodexSessionResumeHome(args)
|
||||
|
||||
+4
-6
@@ -797,10 +797,10 @@ async function prepareCodexSessionResumeForLaunch(args: {
|
||||
if (args.target.runtime === 'wsl' || !codexRuntimeHome || !store) {
|
||||
return null
|
||||
}
|
||||
const configuredSourceHome = resolveHostCodexSessionSourceHome(store.getSettings())
|
||||
const systemHomePath = getSystemCodexHomePath()
|
||||
// Why: codexSessionSourceHome is import-only; treating it as CODEX_HOME would mutate history sources and bypass account auth.
|
||||
const trustedHomes = [
|
||||
configuredSourceHome ?? systemHomePath,
|
||||
systemHomePath,
|
||||
...codexRuntimeHome.getHostCodexHomePathsForSessionDiscovery()
|
||||
]
|
||||
const sessionSource = await findTrustedCodexSessionResume({
|
||||
@@ -821,12 +821,10 @@ async function prepareCodexSessionResumeForLaunch(args: {
|
||||
},
|
||||
{
|
||||
isHostSystemDefaultRealHome: () => codexRuntimeHome!.isHostSystemDefaultRealHome(),
|
||||
systemCodexHomePath: configuredSourceHome
|
||||
systemCodexHomePath: systemHomePath
|
||||
}
|
||||
)
|
||||
const resumeHome = migrated.useRealCodexHome
|
||||
? (configuredSourceHome ?? systemHomePath)
|
||||
: sessionSource.homePath
|
||||
const resumeHome = migrated.useRealCodexHome ? systemHomePath : sessionSource.homePath
|
||||
|
||||
if (args.workspacePath) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user