mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-02 04:45:21 +00:00
Carve the Codex decoder into @codeburn/core/providers/codex as a pure function over supplied rollout records with EXPLICIT, JSON-serializable state (CodexDecodeState): the running cumulative token counters, fork-replay cutoff, mid-turn accumulators, id/turn bookkeeping, and the cross-file dedup memory (seenKeys). Decoding a corpus in one pass equals decoding it in any number of passes when the state threads between them (the resume invariant). Cost leaves the decoder: the rich calls are cost-free and the CLI prices them through the established estimated-cost seam (costBasis: 'estimated'), retiring both Phase-0 residual calculateCost sites. The CLI codex provider is now discovery + file streaming + cache I/O + pricing. codex-results cache bumped to v8 (one deliberate bump): it persists the decoder end-state blob + a byte offset + host-priced calls, so a grown rollout resumes from the stored state and decodes only the appended bytes. Lossless — rollout files are durable, so the one-time re-derive rebuilds identical data. Adds a codex path to the content-smuggling guardrail and the import-smoke subpath set. Byte-parity verified against a frozen 826-file corpus (codex-only and all-providers, rel_tol 1e-9); perf within ±1.5% cold / ±0.5% warm.
129 lines
7.4 KiB
TypeScript
129 lines
7.4 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { decodeCodex } from '../../src/providers/codex/index.js'
|
|
import type { CodexDecodeState } from '../../src/providers/codex/index.js'
|
|
import type { DecodeContext } from '../../src/contracts.js'
|
|
|
|
const context: DecodeContext = { privacyKey: 'k', providerId: 'codex', sourceRef: 'ref' }
|
|
|
|
function sessionMeta(opts: { session_id: string; model?: string; forked_from_id?: string; timestamp?: string; cwd?: string }) {
|
|
return JSON.stringify({
|
|
type: 'session_meta',
|
|
timestamp: opts.timestamp ?? '2026-04-14T10:00:00Z',
|
|
payload: {
|
|
cwd: opts.cwd ?? '/Users/t/p',
|
|
originator: 'codex-cli',
|
|
session_id: opts.session_id,
|
|
model: opts.model ?? 'gpt-5.3-codex',
|
|
...(opts.forked_from_id ? { forked_from_id: opts.forked_from_id } : {}),
|
|
},
|
|
})
|
|
}
|
|
function userMessage(text: string, timestamp: string) {
|
|
return JSON.stringify({ type: 'response_item', timestamp, payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text }] } })
|
|
}
|
|
function assistantMessage(text: string, timestamp: string) {
|
|
return JSON.stringify({ type: 'response_item', timestamp, payload: { type: 'message', role: 'assistant', content: [{ type: 'output_text', text }] } })
|
|
}
|
|
function functionCall(name: string, timestamp: string) {
|
|
return JSON.stringify({ type: 'response_item', timestamp, payload: { type: 'function_call', name } })
|
|
}
|
|
function tokenCount(opts: { timestamp: string; last?: { input?: number; cached?: number; output?: number; reasoning?: number }; total?: { input?: number; cached?: number; output?: number; reasoning?: number; total?: number }; noInfo?: boolean }) {
|
|
const info = opts.noInfo ? undefined : {
|
|
last_token_usage: opts.last ? { input_tokens: opts.last.input ?? 0, cached_input_tokens: opts.last.cached ?? 0, output_tokens: opts.last.output ?? 0, reasoning_output_tokens: opts.last.reasoning ?? 0, total_tokens: (opts.last.input ?? 0) + (opts.last.output ?? 0) } : undefined,
|
|
total_token_usage: opts.total ? { input_tokens: opts.total.input ?? 0, cached_input_tokens: opts.total.cached ?? 0, output_tokens: opts.total.output ?? 0, reasoning_output_tokens: opts.total.reasoning ?? 0, total_tokens: opts.total.total ?? 0 } : undefined,
|
|
}
|
|
return JSON.stringify({ type: 'event_msg', timestamp: opts.timestamp, payload: { type: 'token_count', ...(opts.noInfo ? {} : { info }) } })
|
|
}
|
|
|
|
// A corpus spanning THREE sessions, including a forked session that replays the
|
|
// parent's history and a session whose token_count carries no info (char-estimate
|
|
// branch). Session boundaries (session_meta) exercise the per-session reset while
|
|
// the fork boundary exercises cross-session dedup memory in the threaded state.
|
|
const CORPUS: string[] = [
|
|
// Session A (parent): 1100 tokens of real work across two turns.
|
|
sessionMeta({ session_id: 'sess-parent', timestamp: '2026-04-14T10:00:00Z' }),
|
|
userMessage('fix the parser', '2026-04-14T10:00:01Z'),
|
|
functionCall('exec_command', '2026-04-14T10:00:02Z'),
|
|
tokenCount({ timestamp: '2026-04-14T10:00:03Z', last: { input: 700 }, total: { input: 700, total: 700 } }),
|
|
userMessage('now the tests', '2026-04-14T10:00:04Z'),
|
|
functionCall('read_file', '2026-04-14T10:00:05Z'),
|
|
tokenCount({ timestamp: '2026-04-14T10:00:06Z', last: { input: 400 }, total: { input: 1100, total: 1100 } }),
|
|
// Session B (fork of A): replays A's two events past the 5s cutoff, then +400.
|
|
sessionMeta({ session_id: 'sess-fork', forked_from_id: 'sess-parent', timestamp: '2026-04-14T10:05:00Z' }),
|
|
tokenCount({ timestamp: '2026-04-14T10:05:20Z', last: { input: 700 }, total: { input: 700, total: 700 } }),
|
|
tokenCount({ timestamp: '2026-04-14T10:05:21Z', last: { input: 400 }, total: { input: 1100, total: 1100 } }),
|
|
functionCall('spawn_agent', '2026-04-14T10:05:22Z'),
|
|
tokenCount({ timestamp: '2026-04-14T10:05:23Z', last: { input: 400 }, total: { input: 1500, total: 1500 } }),
|
|
// Session C: char-estimate branch (token_count with no info).
|
|
sessionMeta({ session_id: 'sess-est', model: 'gpt-5.5', timestamp: '2026-04-14T11:00:00Z' }),
|
|
userMessage('hello there estimate branch', '2026-04-14T11:00:01Z'),
|
|
assistantMessage('a fairly long assistant reply that should estimate some output tokens here', '2026-04-14T11:00:02Z'),
|
|
tokenCount({ timestamp: '2026-04-14T11:00:03Z', noInfo: true }),
|
|
]
|
|
|
|
const FORK_BOUNDARY_INDEX = 7 // the fork's session_meta
|
|
const MID_PARENT_INDEX = 4 // between A's two turns
|
|
|
|
function decodeCold(records: string[]) {
|
|
return decodeCodex({ records, context }).calls
|
|
}
|
|
|
|
function decodeTwoPass(records: string[], split: number) {
|
|
const first = decodeCodex({ records: records.slice(0, split), context })
|
|
// Serialize state to JSON and back between passes (the resume invariant).
|
|
const serialized: CodexDecodeState = JSON.parse(JSON.stringify(first.state))
|
|
const second = decodeCodex({ records: records.slice(split), context, state: serialized })
|
|
return [...first.calls, ...second.calls]
|
|
}
|
|
|
|
describe('codex decoder — round-trip resume invariant', () => {
|
|
it('cold single pass produces the expected number of calls', () => {
|
|
const cold = decodeCold(CORPUS)
|
|
// A: 2 real turns; fork: replays dropped, 1 genuine; C: 1 estimate = 4.
|
|
expect(cold).toHaveLength(4)
|
|
// The fork's genuine event survives (input 400 at cumulative 1500).
|
|
const tokensTotal = cold.reduce((n, c) => n + c.inputTokens + c.outputTokens + c.cachedInputTokens + c.reasoningTokens, 0)
|
|
// A: 700 + 400; fork: +400; C: estimate input+output.
|
|
expect(tokensTotal).toBeGreaterThan(1500)
|
|
})
|
|
|
|
it('two-pass split at EVERY index deep-equals the cold pass', () => {
|
|
const cold = decodeCold(CORPUS)
|
|
for (let split = 0; split <= CORPUS.length; split++) {
|
|
const twoPass = decodeTwoPass(CORPUS, split)
|
|
expect(twoPass, `split at index ${split}`).toEqual(cold)
|
|
}
|
|
})
|
|
|
|
it('mid-session split (between two turns of the same session) matches cold', () => {
|
|
expect(decodeTwoPass(CORPUS, MID_PARENT_INDEX)).toEqual(decodeCold(CORPUS))
|
|
})
|
|
|
|
it('split across the forked-session boundary matches cold (dedup memory threads)', () => {
|
|
expect(decodeTwoPass(CORPUS, FORK_BOUNDARY_INDEX)).toEqual(decodeCold(CORPUS))
|
|
})
|
|
|
|
it('serialized state is plain JSON (no Map/Set/undefined-only loss)', () => {
|
|
const { state } = decodeCodex({ records: CORPUS.slice(0, FORK_BOUNDARY_INDEX), context })
|
|
const round = JSON.parse(JSON.stringify(state)) as CodexDecodeState
|
|
// seenKeys survives as an array carrying the parent's dedup fingerprints.
|
|
expect(Array.isArray(round.seenKeys)).toBe(true)
|
|
expect(round.seenKeys.length).toBeGreaterThan(0)
|
|
// prevCumulativeTotal is a JSON-safe null|number.
|
|
expect(round.prevCumulativeTotal === null || typeof round.prevCumulativeTotal === 'number').toBe(true)
|
|
})
|
|
|
|
it('a fork replay is dropped only because seenKeys threaded across the split', () => {
|
|
// Splitting exactly at the fork boundary, WITHOUT threading state, would let
|
|
// the fork replay its parent's events (double count). With threaded state the
|
|
// replay collides and drops — proving the dedup memory is in the state.
|
|
const threaded = decodeTwoPass(CORPUS, FORK_BOUNDARY_INDEX)
|
|
const parentAndForkFresh = [
|
|
...decodeCodex({ records: CORPUS.slice(0, FORK_BOUNDARY_INDEX), context }).calls,
|
|
// second pass with NO state: the fork can't see the parent's keys.
|
|
...decodeCodex({ records: CORPUS.slice(FORK_BOUNDARY_INDEX), context }).calls,
|
|
]
|
|
expect(parentAndForkFresh.length).toBeGreaterThan(threaded.length)
|
|
})
|
|
})
|