mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-29 18:33:01 +00:00
fix(codex): walk billed+nest trees on the no-arg singleton
filterOverlap required an explicit nest path, so export const codex = createCodexProvider() never deduped. When the resolved dir is a launcher nest and ~/.codex is a distinct existing tree, discover both and drop nest sources whose session id is already billed. Unique nest and billed sessions stay. Soften the Buzz doctor note to a presence heuristic.
This commit is contained in:
parent
7042048608
commit
2c87ee3eaa
3 changed files with 77 additions and 27 deletions
|
|
@ -23,8 +23,9 @@ export function sameCodexHome(a: string, b: string): boolean {
|
|||
}
|
||||
|
||||
/** True when `dir` is a Codex home nested under a launcher nest, and a distinct
|
||||
* primary Codex home exists. Gate for overlap-only filtering on a *second*
|
||||
* provider instance — it does not by itself drop sessions. */
|
||||
* primary Codex home exists. Gate for overlap-only filtering — it does not by
|
||||
* itself drop sessions. The production no-arg factory uses this to walk both
|
||||
* trees; an explicit nest factory uses it to drop overlapping nest ids. */
|
||||
export function isNestedLauncherCodexHome(
|
||||
dir: string,
|
||||
opts: { primaryDir: string; launcherRoots: string[] },
|
||||
|
|
@ -125,7 +126,7 @@ export function collectLauncherNotes(home = homedir()): LauncherNote[] {
|
|||
name: 'buzz',
|
||||
path: buzz,
|
||||
billedVia: 'codex',
|
||||
verdict: 'LAUNCHER (billed via Codex)',
|
||||
verdict: 'LAUNCHER (heuristic; billed via Codex)',
|
||||
})
|
||||
}
|
||||
const grokStore = join(home, '.grok')
|
||||
|
|
|
|||
|
|
@ -1330,24 +1330,40 @@ export async function parseCodexFileFull(source: SessionSource, seenKeys: Set<st
|
|||
return { calls, ...(capture.write ? { write: capture.write } : {}) }
|
||||
}
|
||||
|
||||
function rootsFor(home: string): ProbeRoot[] {
|
||||
return [
|
||||
{ path: join(home, 'sessions'), label: 'sessions' },
|
||||
{ path: join(home, 'archived_sessions'), label: 'archived' },
|
||||
]
|
||||
}
|
||||
|
||||
function dropOverlappingNestSources(sources: SessionSource[], billedHome: string): SessionSource[] {
|
||||
const billedIds = listRolloutSessionIds(billedHome)
|
||||
return sources.filter(source => {
|
||||
const id = rolloutFileSessionId(source.path)
|
||||
return !(id && billedIds.has(id))
|
||||
})
|
||||
}
|
||||
|
||||
export function createCodexProvider(
|
||||
codexDir?: string,
|
||||
opts?: { primaryDir?: string; launcherRoots?: string[] },
|
||||
): Provider {
|
||||
const dir = getCodexDir(codexDir)
|
||||
// Production singleton (no args) still honors CODEX_HOME / override as the
|
||||
// one root. An explicit nest path — even without second-factory opts —
|
||||
// overlap-filters against the default billed home using defaultLauncherRoots
|
||||
// so a future createCodexProvider(nest) cannot double-count session ids.
|
||||
const primaryDir = opts?.primaryDir ?? defaultBilledCodexHome()
|
||||
const launcherRoots = opts?.launcherRoots ?? defaultLauncherRoots()
|
||||
// Explicit nest factory whose path is a realpath alias of the billed home:
|
||||
// empty so a second factory cannot double-count the same tree. The no-arg
|
||||
// singleton must not take this branch — CODEX_HOME may be that alias.
|
||||
const duplicateHome =
|
||||
codexDir !== undefined &&
|
||||
sameCodexHome(dir, primaryDir) &&
|
||||
resolve(dir) !== resolve(primaryDir)
|
||||
const filterOverlap =
|
||||
codexDir !== undefined &&
|
||||
isNestedLauncherCodexHome(dir, { primaryDir, launcherRoots })
|
||||
const nestHome = isNestedLauncherCodexHome(dir, { primaryDir, launcherRoots })
|
||||
// Production `codex` singleton is createCodexProvider() with no args. When
|
||||
// the resolved dir is a launcher nest and ~/.codex is a distinct existing
|
||||
// tree, walk BOTH and drop nest sources whose session id is already billed.
|
||||
const scanBoth = nestHome && codexDir === undefined
|
||||
|
||||
return {
|
||||
name: 'codex',
|
||||
|
|
@ -1364,13 +1380,12 @@ export function createCodexProvider(
|
|||
return toolNameMap[rawTool] ?? rawTool
|
||||
},
|
||||
|
||||
// Same `dir` discoverSessionsInDir walks: <codexDir>/sessions (dated
|
||||
// rollout files) and <codexDir>/archived_sessions. Honors CODEX_HOME.
|
||||
// Trees discoverSessions actually walks. Honors CODEX_HOME; when the
|
||||
// production singleton scans nest + billed home, both appear here.
|
||||
async probeRoots(): Promise<ProbeRoot[]> {
|
||||
return [
|
||||
{ path: join(dir, 'sessions'), label: 'sessions' },
|
||||
{ path: join(dir, 'archived_sessions'), label: 'archived' },
|
||||
]
|
||||
if (duplicateHome) return []
|
||||
if (scanBoth) return [...rootsFor(primaryDir), ...rootsFor(dir)]
|
||||
return rootsFor(dir)
|
||||
},
|
||||
|
||||
async discoverSessions(): Promise<SessionSource[]> {
|
||||
|
|
@ -1378,12 +1393,12 @@ export function createCodexProvider(
|
|||
// distinct nest. isNestedLauncherCodexHome is false in that case.
|
||||
if (duplicateHome) return []
|
||||
const sources = await discoverSessionsInDir(dir)
|
||||
if (!filterOverlap) return sources
|
||||
const primaryIds = listRolloutSessionIds(primaryDir)
|
||||
return sources.filter(source => {
|
||||
const id = rolloutFileSessionId(source.path)
|
||||
return !(id && primaryIds.has(id))
|
||||
})
|
||||
if (scanBoth) {
|
||||
const billed = await discoverSessionsInDir(primaryDir)
|
||||
return [...billed, ...dropOverlappingNestSources(sources, primaryDir)]
|
||||
}
|
||||
if (!nestHome) return sources
|
||||
return dropOverlappingNestSources(sources, primaryDir)
|
||||
},
|
||||
|
||||
createSessionParser(source: SessionSource, seenKeys: Set<string>): SessionParser {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import { mkdtemp, mkdir, writeFile, rm, symlink } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { tmpdir } from 'os'
|
||||
|
|
@ -114,6 +114,8 @@ describe('Codex discover overlap-only nest filter', () => {
|
|||
expect(names(await provider.discoverSessions())).toEqual(['rollout-buzz.jsonl'])
|
||||
const roots = await provider.probeRoots!()
|
||||
expect(roots.map(r => r.path)).toEqual([
|
||||
join(primary, 'sessions'),
|
||||
join(primary, 'archived_sessions'),
|
||||
join(nested, 'sessions'),
|
||||
join(nested, 'archived_sessions'),
|
||||
])
|
||||
|
|
@ -125,19 +127,51 @@ describe('Codex discover overlap-only nest filter', () => {
|
|||
}
|
||||
})
|
||||
|
||||
it('default factory with CODEX_HOME=nest does not drop unique nest rollouts when ~/.codex has its own', async () => {
|
||||
it('no-arg factory discovers billed + unique nest and drops overlapping nest ids', async () => {
|
||||
const home = root
|
||||
const primary = join(home, '.codex')
|
||||
const nested = join(home, '.buzz', '.codex')
|
||||
await writeCodexSession(primary, 'rollout-primary.jsonl')
|
||||
await writeCodexSession(nested, 'rollout-buzz.jsonl')
|
||||
await writeCodexSession(primary, 'rollout-billed.jsonl', 'sess-shared')
|
||||
await writeCodexSession(primary, 'rollout-billed-only.jsonl', 'sess-billed')
|
||||
await writeCodexSession(nested, 'rollout-renamed.jsonl', 'sess-shared')
|
||||
await writeCodexSession(nested, 'rollout-buzz-only.jsonl', 'sess-buzz')
|
||||
const prevHome = process.env.HOME
|
||||
const prevCodex = process.env.CODEX_HOME
|
||||
process.env.HOME = home
|
||||
process.env.CODEX_HOME = nested
|
||||
try {
|
||||
const provider = createCodexProvider()
|
||||
expect(names(await provider.discoverSessions())).toEqual(['rollout-buzz.jsonl'])
|
||||
expect(names(await provider.discoverSessions()).sort()).toEqual([
|
||||
'rollout-billed-only.jsonl',
|
||||
'rollout-billed.jsonl',
|
||||
'rollout-buzz-only.jsonl',
|
||||
])
|
||||
} finally {
|
||||
if (prevHome === undefined) delete process.env.HOME
|
||||
else process.env.HOME = prevHome
|
||||
if (prevCodex === undefined) delete process.env.CODEX_HOME
|
||||
else process.env.CODEX_HOME = prevCodex
|
||||
}
|
||||
})
|
||||
|
||||
it('exported codex singleton dedups nest ids against billed home', async () => {
|
||||
const home = root
|
||||
const primary = join(home, '.codex')
|
||||
const nested = join(home, '.buzz', '.codex')
|
||||
await writeCodexSession(primary, 'rollout-billed.jsonl', 'sess-shared')
|
||||
await writeCodexSession(nested, 'rollout-renamed.jsonl', 'sess-shared')
|
||||
await writeCodexSession(nested, 'rollout-buzz-only.jsonl', 'sess-buzz')
|
||||
const prevHome = process.env.HOME
|
||||
const prevCodex = process.env.CODEX_HOME
|
||||
process.env.HOME = home
|
||||
process.env.CODEX_HOME = nested
|
||||
vi.resetModules()
|
||||
try {
|
||||
const { codex } = await import('../src/providers/codex.js')
|
||||
expect(names(await codex.discoverSessions()).sort()).toEqual([
|
||||
'rollout-billed.jsonl',
|
||||
'rollout-buzz-only.jsonl',
|
||||
])
|
||||
} finally {
|
||||
if (prevHome === undefined) delete process.env.HOME
|
||||
else process.env.HOME = prevHome
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue