mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-24 07:54:18 +00:00
The second half of #940 (@ozymandiashh): upstream `main` measures Codex
throughput — a task's wall time minus its recorded tool wait, divided across
the task's calls by generated tokens — and none of it exists on this branch.
Added on one side only, so a `main` merge would land `src/codex-throughput.ts`
at a path npm workspaces does not build.
Rehomed against main as it ships today, with one deliberate divergence from
#940, which the maintainer decided: main's resume design wins.
- Timing state is captured ONLY at a `task_started` boundary, where every
per-task accumulator is provably empty, and a task's calls are buffered
until its window is known. No recorded call is ever mutated after it has
been handed to the host. #940's alternative — threading the open task
window through the serialized state and back-patching earlier-pass calls
via applyCodexTimingPatches — is dropped in full.
- The branch's Phase-4 token-decode resume is untouched: it stays any-offset
and round-trip proven. Marrying the two needed one adaptation, since core
decodes records and never sees bytes: the decoder now reports its last
task_started as a `checkpoint` (record index + call count + state), and the
CLI turns that index into a byte offset and replays only the calls before
it, letting the still-open task re-derive. A pass that crosses no boundary
keeps the previous one; a cold decode of a file with no task_started at all
falls back to end-of-file with `taskOpen: false`, so a task_complete whose
window this pass never saw attributes nothing rather than spreading a whole
task's active time over part of its tokens.
Restores the three fad84662 review fixes that #940 reverted: the discovery
fast path already short-circuits on cachedProject before isValidCodexSession
(unchanged here, verified); payload-level `duration_ms` outranks any nested one
(`timingDuration ?? timingNumber('duration_ms')`), so a duration buried in an
oversized mcp_tool_call_end's invocation.arguments can no longer inflate tool
wait; and MIN_WIDE stays 90 with the Tok/s column behind a showTps gate rather
than jumping to 130 and costing 90-129 column terminals their two-column
dashboard. Also ports the fork-suppressed-task_started regression test and the
depth-1 payloadString helper (main 1d36f444/497f6556), which the branch lacked.
Scope discipline: main's codex pricing work (billableOutputTokens, #1078) is
NOT dragged along — that is #1083 — and neither are its unported parser
changes (custom-tool transport, exact token counts and MCP names on oversized
lines), so cost, calls and tokens are untouched. Verified on a 1326-session
real corpus: codex totals byte-identical to the base branch, with 1302 of 1328
model slices now carrying timing (36.5 Tok/s on GPT-5.5).
CODEX_CACHE_VERSION takes 12, clear of main's ladder (11 as of #1078) so a
cache written by either line can never be read as current by the other, and
the codex parse version bumps in lockstep so session-cache.json cannot keep
serving timing-less turns without invoking the parser.
46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
import { mkdtemp, rm } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { spawnSync } from 'node:child_process'
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
|
|
const homes: string[] = []
|
|
|
|
afterEach(async () => {
|
|
while (homes.length) await rm(homes.pop()!, { recursive: true, force: true })
|
|
})
|
|
|
|
function runCli(args: string[], home: string) {
|
|
return spawnSync(process.execPath, ['--import', 'tsx', 'src/cli.ts', ...args], {
|
|
cwd: process.cwd(),
|
|
env: { ...process.env, HOME: home, CODEX_HOME: join(home, '.codex'), TZ: 'UTC' },
|
|
encoding: 'utf-8',
|
|
timeout: 30_000,
|
|
})
|
|
}
|
|
|
|
describe('codex-tps CLI validation', () => {
|
|
it('rejects sub-second watch intervals', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'codeburn-tps-cli-'))
|
|
homes.push(home)
|
|
const result = runCli(['codex-tps', '--watch', '0.1'], home)
|
|
expect(result.status).toBe(1)
|
|
expect(result.stderr).toContain('watch must be 0 or at least 1 second')
|
|
})
|
|
|
|
it('rejects JSON watch output instead of concatenating invalid JSON documents', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'codeburn-tps-cli-'))
|
|
homes.push(home)
|
|
const result = runCli(['codex-tps', '--json', '--watch', '1'], home)
|
|
expect(result.status).toBe(2)
|
|
expect(result.stderr).toContain('--json cannot be combined with --watch')
|
|
})
|
|
|
|
it('returns a nonzero status for a missing explicit rollout', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'codeburn-tps-cli-'))
|
|
homes.push(home)
|
|
const result = runCli(['codex-tps', join(home, 'missing.jsonl')], home)
|
|
expect(result.status).toBe(1)
|
|
expect(result.stderr).toContain('session file not found')
|
|
})
|
|
})
|