Files
orca/src/main/native-chat/transcript-zstd-decompression.test.ts
T
BoyceandKelvin Amoaba 42f55bfa37 fix(session-history): index Codex rollouts archived as .jsonl.zst (#40593)
* fix(session-history): index Codex rollouts archived as .jsonl.zst

Codex compresses rollouts idle for 7+ days to rollout-*.jsonl.zst. The
session scanner only discovered .jsonl files, so every Codex chat older
than about a week disappeared from Agent Session History. Discover .zst
rollouts, decode them through a shared zstd stream wrapper above
openTranscriptReadStream (native, WSL UNC, and SSH alike), never resume
a compressed archive from an offset, and drop a .jsonl.zst candidate
when its plain .jsonl twin is listed, matching Codex's own rule.

* fix(session-history): admit only rollout-named zst archives and close raw zstd streams early

Review follow-up on #40593: a filePredicate limits compressed discovery
to rollout-*.jsonl.zst so unrelated .zst files are never decompressed,
and zstdTranscriptStream links the raw and decoded streams with pipeline
so an early close of the decoded stream releases the raw stream's fd.

* fix(session-history): cover archived Codex rollouts by compressed size and collapse .zst twins in every listing

Archived rollouts keep the bounded resumable reader but never resume, and report their compressed stat size as the read cursor, so search coverage checks match. The .zst suffix is dropped from Codex alias keys with the plain file preferred, which also dedupes scoped walks and rollouts archived in only one home.

* fix(session-history): prefer the live rollout over an archive in another Codex home

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-11 06:20:22 +00:00

25 lines
974 B
TypeScript

import { describe, expect, it } from 'vitest'
import { Readable } from 'node:stream'
import { randomBytes } from 'node:crypto'
import { zstdCompressSync } from 'node:zlib'
import { readZstdTranscriptSlice, zstdTranscriptStream } from './transcript-zstd-decompression'
describe('zstdTranscriptStream', () => {
it('closes the raw stream when the decoded stream stops before EOF', async () => {
const compressed = zstdCompressSync(randomBytes(1024 * 1024))
async function* rawChunks(): AsyncGenerator<Buffer> {
for (let offset = 0; offset < compressed.length; offset += 16 * 1024) {
yield compressed.subarray(offset, offset + 16 * 1024)
}
}
const raw = Readable.from(rawChunks())
const decoded = zstdTranscriptStream(raw)
const head = await readZstdTranscriptSlice(decoded, 64 * 1024)
expect(head.length).toBe(64 * 1024)
await new Promise((resolve) => setImmediate(resolve))
expect(raw.destroyed).toBe(true)
})
})