Skip to content
Source
Sign inSign up

usageTranscriptReader.test.ts

579 lines23.2 KB

1// @effect-diagnostics nodeBuiltinImport:off - resume coverage writes, appends
2// to, and truncates real transcript files byte-exactly, mirroring the reader's
3// own deliberate node:fs usage.
4import * as NodeFSP from "node:fs/promises";
5import * as NodeOS from "node:os";
6import * as NodePath from "node:path";
7import * as NodeZlib from "node:zlib";
8​
9import { afterEach, assert, beforeEach, describe, it } from "@effect/vitest";
10import { totalTokens, type TranscriptUsageFormat } from "@t3tools/provider-core/server/usage";
11import * as Schema from "effect/Schema";
12​
13import { TEST_FORMATS } from "./usageTestFormats.ts";
14import {
15 listTranscriptFiles,
16 readTranscriptRecords,
17 transcriptLineGate,
18} from "./usageTranscriptReader.ts";
19​
20let dir: string;
21​
22beforeEach(async () => {
23 dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "usage-reader-test-"));
24});
25​
26afterEach(async () => {
27 await NodeFSP.rm(dir, { recursive: true, force: true });
28});
29​
30function claudeLine(id: number, outputTokens: number): string {
31 return `${JSON.stringify({
32 type: "assistant",
33 timestamp: "2026-08-01T10:00:00Z",
34 requestId: `req_${id}`,
35 sessionId: "session-1",
36 message: {
37 id: `msg_${id}`,
38 model: "claude-fable-5",
39 usage: { input_tokens: 10, output_tokens: outputTokens },
40 },
41 })}\n`;
43​
44function codexMetaLine(): string {
45 return `${JSON.stringify({
46 type: "session_meta",
47 timestamp: "2026-08-01T10:00:00Z",
48 payload: { type: "session_meta", id: "codex-session-1" },
49 })}\n`;
51​
52function codexModelLine(model: string): string {
53 return `${JSON.stringify({
54 type: "turn_context",
55 timestamp: "2026-08-01T10:00:01Z",
56 payload: { type: "turn_context", model },
57 })}\n`;
59​
60function codexTierLine(serviceTier: string): string {
61 return `${JSON.stringify({
62 type: "event_msg",
63 timestamp: "2026-08-01T10:00:01Z",
64 payload: { type: "thread_settings_applied", thread_settings: { service_tier: serviceTier } },
65 })}\n`;
67​
68function codexUsageLine(outputTokens: number, secondsOffset: number): string {
69 return `${JSON.stringify({
70 type: "event_msg",
71 timestamp: `2026-08-01T10:00:${String(secondsOffset).padStart(2, "0")}Z`,
72 payload: {
73 type: "token_count",
74 info: { last_token_usage: { input_tokens: 100, output_tokens: outputTokens } },
75 },
76 })}\n`;
78​
79describe("compressed transcripts", () => {
80 it("discovers nested plain and zstd JSONL files within the requested window", async () => {
81 const nested = NodePath.join(dir, "nested");
82 await NodeFSP.mkdir(nested);
83 const plain = NodePath.join(dir, "active.jsonl");
84 const compressed = NodePath.join(nested, "archived.jsonl.zst");
85 const old = NodePath.join(nested, "old.jsonl.zst");
86 await NodeFSP.writeFile(plain, claudeLine(1, 5));
87 await NodeFSP.writeFile(compressed, NodeZlib.zstdCompressSync(claudeLine(2, 7)));
88 await NodeFSP.writeFile(old, NodeZlib.zstdCompressSync(claudeLine(3, 11)));
89 await NodeFSP.writeFile(NodePath.join(nested, "unrelated.zst"), "ignored");
90 await NodeFSP.writeFile(NodePath.join(nested, "partial.jsonl.zst.tmp"), "ignored");
91 await NodeFSP.utimes(old, 1, 1);
92​
93 const listed = await listTranscriptFiles(dir, Date.parse("2026-01-01T00:00:00Z"));
94 assert.deepStrictEqual(
95 listed.files.map((file) => file.path).sort(),
96 [plain, compressed].sort(),
97 );
98 assert.strictEqual(listed.failedPaths, 0);
99 assert.isAbove(listed.files.find((file) => file.path === compressed)?.size ?? 0, 0);
100 });
101​
102 it("lists files in directory order when more directories wait than it reads at once", async () => {
103 for (let index = 0; index < 40; index += 1) {
104 const nested = NodePath.join(dir, `project-${index}`, "sessions");
105 await NodeFSP.mkdir(nested, { recursive: true });
106 await NodeFSP.writeFile(NodePath.join(nested, "a.jsonl"), "");
107 await NodeFSP.writeFile(NodePath.join(nested, "..", "b.jsonl"), "");
108 }
109 // Usage that two transcripts share counts in the first one listed, so the order is part of the result.
110 const inOrder = async (folder: string): Promise<string[]> => {
111 const paths: string[] = [];
112 for (const entry of await NodeFSP.readdir(folder, { withFileTypes: true })) {
113 const child = NodePath.join(folder, entry.name);
114 if (entry.isDirectory()) paths.push(...(await inOrder(child)));
115 else paths.push(child);
116 }
117 return paths;
118 };
119 const listed = await listTranscriptFiles(dir, 0);
120 assert.deepStrictEqual(
121 listed.files.map((file) => file.path),
122 await inOrder(dir),
123 );
124 });
125​
126 it.each([
127 { parser: "native", streamingThresholdBytes: undefined },
128 { parser: "projected", streamingThresholdBytes: 1024 },
129 ])(
130 "matches plain Codex records and unfinished tails through long lines ($parser)",
131 async ({ streamingThresholdBytes }) => {
132 const plain = NodePath.join(dir, "rollout.jsonl");
133 const compressed = plain + ".zst";
134 const large = codexUsageLine(9, 5).replace(
135 '"payload":',
136 '"padding":' + JSON.stringify("日本語 é".repeat(128 * 1024)) + ',"payload":',
137 );
138 const content =
139 codexMetaLine() +
140 codexModelLine("gpt-6-astra") +
141 codexTierLine("ultrafast") +
142 large +
143 codexUsageLine(13, 6).replace('"output_tokens":13', '"output_tokens":"invalid"') +
144 codexUsageLine(21, 8).trimEnd();
145 await NodeFSP.writeFile(plain, content);
146 await NodeFSP.writeFile(compressed, NodeZlib.zstdCompressSync(content));
147​
148 const options =
149 streamingThresholdBytes === undefined ? undefined : { streamingThresholdBytes };
150 const expected = await readTranscriptRecords(plain, TEST_FORMATS.codex, undefined, options);
151 const actual = await readTranscriptRecords(
152 compressed,
153 TEST_FORMATS.codex,
154 undefined,
155 options,
156 );
157 assert.isNotNull(expected);
158 assert.isNotNull(actual);
159 assert.strictEqual(expected.records.length, 1);
160 assert.strictEqual(expected.tailRecords.length, 1);
161 assert.deepStrictEqual(actual.records, expected.records);
162 assert.deepStrictEqual(actual.tailRecords, expected.tailRecords);
163 assert.strictEqual(expected.malformedRecords, 1);
164 assert.strictEqual(actual.malformedRecords, expected.malformedRecords);
165 assert.strictEqual(actual.records[0]?.speed, "ultrafast");
166 assert.isFalse(actual.resumed);
167 assert.strictEqual(actual.position.resumeOffset, 0);
168 },
169 );
170​
171 it("ignores a prior resume position and rereads compressed rollouts from their start", async () => {
172 const plain = NodePath.join(dir, "rollout.jsonl");
173 const content = codexMetaLine() + codexModelLine("gpt-6-astra") + codexUsageLine(9, 5);
174 await NodeFSP.writeFile(plain, content);
175 const first = await readTranscriptRecords(plain, TEST_FORMATS.codex);
176 assert.isNotNull(first);
177​
178 const compressed = plain + ".zst";
179 await NodeFSP.writeFile(compressed, NodeZlib.zstdCompressSync(content + codexUsageLine(21, 8)));
180 const actual = await readTranscriptRecords(compressed, TEST_FORMATS.codex, first.position);
181 assert.isNotNull(actual);
182 assert.isFalse(actual.resumed);
183 assert.deepStrictEqual(
184 actual.records.map((record) => record.totals.outputTokens),
185 [9, 21],
186 );
187 const repeated = await readTranscriptRecords(compressed, TEST_FORMATS.codex, actual.position);
188 assert.isNotNull(repeated);
189 assert.isFalse(repeated.resumed);
190 assert.deepStrictEqual(repeated.records, actual.records);
191 });
192​
193 it("returns null for zstd decoder errors rather than caching an empty parse", async () => {
194 const path = NodePath.join(dir, "corrupt.jsonl.zst");
195 await NodeFSP.writeFile(path, "not a zstd frame");
196 assert.isNull(await readTranscriptRecords(path, TEST_FORMATS.claude));
197 });
198});
199​
200describe("readTranscriptRecords resume", () => {
201 it.each([
202 { mode: "buffered", streamingThresholdBytes: Infinity },
203 { mode: "streaming", streamingThresholdBytes: 0 },
204 ])("counts malformed tails once across $mode reads and resume", async (options) => {
205 const format: TranscriptUsageFormat<{ errors: number }> = {
206 ...TEST_FORMATS.claude,
207 selectFields: { ...TEST_FORMATS.claude.selectFields, usage: true },
208 state: {
209 initial: () => ({ errors: 0 }),
210 schema: Schema.Struct({ errors: Schema.Number }),
211 },
212 parseLine(line, state) {
213 return this.parseProjected(JSON.parse(line), state);
214 },
215 parseProjected(value, state) {
216 if (
217 typeof value === "object" &&
218 value !== null &&
219 "usage" in value &&
220 value.usage === "malformed"
221 ) {
222 state.errors += 1;
223 return [];
224 }
225 return TEST_FORMATS.claude.parseProjected(value, undefined);
226 },
227 readMalformedRecords: (state: { errors: number }) => state.errors,
228 };
229 const path = NodePath.join(dir, "diagnostics.jsonl");
230 const malformed = JSON.stringify({ usage: "malformed" });
231 // Completed lines enter the projected parser only when they cross a stream chunk.
232 const committed = `${JSON.stringify({ usage: "malformed", padding: "x".repeat(256 * 1024) })}\n${claudeLine(1, 5)}`;
233 await NodeFSP.writeFile(path, committed + malformed);
234​
235 const first = await readTranscriptRecords(path, format, undefined, options);
236 assert.isNotNull(first);
237 assert.deepStrictEqual(
238 first.records.map((record) => record.totals.outputTokens),
239 [5],
240 );
241 assert.deepStrictEqual(first.tailRecords, []);
242 assert.strictEqual(first.position.resumeOffset, Buffer.byteLength(committed));
243 assert.deepStrictEqual(first.position.state, { errors: 1 });
244 assert.strictEqual(first.malformedRecords, 2);
245​
246 const savedPosition = JSON.parse(JSON.stringify(first.position));
247 await NodeFSP.appendFile(path, `\n${claudeLine(2, 7)}${malformed}\n`);
248 const resumed = await readTranscriptRecords(path, format, savedPosition, options);
249 assert.isNotNull(resumed);
250 assert.isTrue(resumed.resumed);
251 assert.deepStrictEqual(
252 resumed.records.map((record) => record.totals.outputTokens),
253 [7],
254 );
255 assert.deepStrictEqual(resumed.position.state, { errors: 3 });
256 assert.strictEqual(resumed.malformedRecords, 3);
257​
258 const full = await readTranscriptRecords(path, format, undefined, options);
259 assert.isNotNull(full);
260 assert.deepStrictEqual([...first.records, ...resumed.records], full.records);
261 assert.deepStrictEqual(resumed.tailRecords, full.tailRecords);
262 assert.deepStrictEqual(resumed.position, full.position);
263 assert.strictEqual(resumed.malformedRecords, full.malformedRecords);
264 });
265​
266 it("parses only appended lines when resuming a grown file", async () => {
267 const path = NodePath.join(dir, "claude.jsonl");
268 await NodeFSP.writeFile(path, claudeLine(1, 5) + claudeLine(2, 7));
269 const first = await readTranscriptRecords(path, TEST_FORMATS.claude);
270 assert.isNotNull(first);
271 assert.strictEqual(first.records.length, 2);
272 assert.isFalse(first.resumed);
273 assert.strictEqual(first.malformedRecords, 0);
274​
275 await NodeFSP.appendFile(path, claudeLine(3, 11));
276 const second = await readTranscriptRecords(path, TEST_FORMATS.claude, first.position);
277 assert.isNotNull(second);
278 assert.isTrue(second.resumed);
279 assert.strictEqual(second.records.length, 1);
280 assert.strictEqual(second.records[0]?.totals.outputTokens, 11);
281​
282 const full = await readTranscriptRecords(path, TEST_FORMATS.claude);
283 assert.isNotNull(full);
284 assert.deepStrictEqual([...first.records, ...second.records], [...full.records]);
285 });
286​
287 it("carries the Codex reducer state across the resume boundary", async () => {
288 const path = NodePath.join(dir, "rollout.jsonl");
289 await NodeFSP.writeFile(
290 path,
291 codexMetaLine() + codexModelLine("gpt-5.2-codex") + codexTierLine("ultrafast"),
292 );
293 const first = await readTranscriptRecords(path, TEST_FORMATS.codex);
294 assert.isNotNull(first);
295 assert.strictEqual(first.records.length, 0);
296​
297 await NodeFSP.appendFile(path, codexUsageLine(9, 5));
298 const second = await readTranscriptRecords(path, TEST_FORMATS.codex, first.position);
299 assert.isNotNull(second);
300 assert.isTrue(second.resumed);
301 assert.strictEqual(second.records.length, 1);
302 assert.strictEqual(second.records[0]?.model, "gpt-5.2-codex");
303 assert.strictEqual(second.records[0]?.speed, "ultrafast");
304 assert.strictEqual(second.records[0]?.sessionId, "codex-session-1");
305 });
306​
307 it("suppresses a Codex duplicate usage event that straddles the boundary", async () => {
308 const path = NodePath.join(dir, "rollout.jsonl");
309 await NodeFSP.writeFile(
310 path,
311 codexMetaLine() + codexModelLine("gpt-5.2-codex") + codexUsageLine(9, 5),
312 );
313 const first = await readTranscriptRecords(path, TEST_FORMATS.codex);
314 assert.isNotNull(first);
315 assert.strictEqual(first.records.length, 1);
316​
317 // Codex duplicates token_count at stream boundaries, including after resume points.
318 await NodeFSP.appendFile(path, codexUsageLine(9, 5) + codexUsageLine(21, 8));
319 const second = await readTranscriptRecords(path, TEST_FORMATS.codex, first.position);
320 assert.isNotNull(second);
321 assert.isTrue(second.resumed);
322 assert.deepStrictEqual(
323 second.records.map((record) => record.totals.outputTokens),
324 [21],
325 );
326 });
327​
328 it("defers an unterminated trailing line to tailRecords, then consumes it once terminated", async () => {
329 const path = NodePath.join(dir, "claude.jsonl");
330 const unterminated = claudeLine(2, 7).trimEnd();
331 await NodeFSP.writeFile(path, claudeLine(1, 5) + unterminated);
332 const first = await readTranscriptRecords(path, TEST_FORMATS.claude);
333 assert.isNotNull(first);
334 assert.strictEqual(first.records.length, 1);
335 assert.strictEqual(first.tailRecords.length, 1);
336 assert.strictEqual(first.tailRecords[0]?.totals.outputTokens, 7);
337​
338 await NodeFSP.appendFile(path, `\n${claudeLine(3, 11)}`);
339 const second = await readTranscriptRecords(path, TEST_FORMATS.claude, first.position);
340 assert.isNotNull(second);
341 assert.isTrue(second.resumed);
342 assert.deepStrictEqual(
343 second.records.map((record) => record.totals.outputTokens),
344 [7, 11],
345 );
346 assert.strictEqual(second.tailRecords.length, 0);
347 });
348​
349 it.each([
350 { mode: "buffered", streamingThresholdBytes: Infinity },
351 { mode: "streaming", streamingThresholdBytes: 0 },
352 ])(
353 "resumes an initial Codex carried-total tail without rebilling it ($mode)",
354 async (options) => {
355 const path = NodePath.join(dir, "continuation.jsonl");
356 const usage = (input: number, output: number) => ({
357 input_tokens: input,
358 output_tokens: output,
359 });
360 const event = (last: unknown, cumulative: unknown) =>
361 JSON.stringify({
362 type: "event_msg",
363 timestamp: "2026-08-01T10:00:05Z",
364 payload: {
365 type: "token_count",
366 info: { last_token_usage: last, total_token_usage: cumulative },
367 },
368 });
369 const firstLine = event(usage(100, 20), usage(500, 60));
370 await NodeFSP.writeFile(
371 path,
372 codexMetaLine() + codexModelLine("gpt-6-astra") + codexTierLine("ultrafast") + firstLine,
373 );
374 const first = await readTranscriptRecords(path, TEST_FORMATS.codex, undefined, options);
375 assert.isNotNull(first);
376 assert.strictEqual(first.records.length, 0);
377 assert.deepStrictEqual(
378 first.tailRecords.map((r) => [r.totals.outputTokens, r.speed]),
379 [[20, "ultrafast"]],
380 );
381 assert.strictEqual(first.malformedRecords, 0);
382​
383 await NodeFSP.appendFile(path, `\n${firstLine}\n${event(usage(10, 2), usage(510, 62))}\n`);
384 const resumed = await readTranscriptRecords(
385 path,
386 TEST_FORMATS.codex,
387 JSON.parse(JSON.stringify(first.position)),
388 options,
389 );
390 const full = await readTranscriptRecords(path, TEST_FORMATS.codex, undefined, options);
391 assert.isNotNull(resumed);
392 assert.isNotNull(full);
393 assert.isTrue(resumed.resumed);
394 assert.deepStrictEqual(resumed.records, full.records);
395 assert.deepStrictEqual(
396 resumed.records.map((r) => [r.totals.outputTokens, r.speed]),
397 [
398 [20, "ultrafast"],
399 [2, "ultrafast"],
400 ],
401 );
402 assert.deepStrictEqual(resumed.tailRecords, []);
403 assert.strictEqual(resumed.malformedRecords, 0);
404 assert.deepStrictEqual(resumed.position, full.position);
405 },
406 );
407​
408 it.each([
409 { mode: "buffered", streamingThresholdBytes: Infinity },
410 { mode: "streaming", streamingThresholdBytes: 0 },
411 ])(
412 "bills a Codex request whose turn was interrupted before its token_count ($mode)",
413 async (options) => {
414 const path = NodePath.join(dir, "rollout.jsonl");
415 const usage = (input: number, output: number) => ({
416 input_tokens: input,
417 cached_input_tokens: 0,
418 cache_write_input_tokens: 0,
419 output_tokens: output,
420 reasoning_output_tokens: 0,
421 total_tokens: input + output,
422 });
423 const context = (turn: number) => ({
424 type: "turn_context",
425 timestamp: `2026-11-02T10:00:0${turn * 4 - 4}.000Z`,
426 payload: { turn_id: `turn-${turn}`, model: "gpt-6-sol" },
427 });
428 const request = (turn: number, id: number, timestamp: string, last: unknown) => ({
429 type: "token_usage_record",
430 timestamp,
431 payload: { turn_id: `turn-${turn}`, response_id: `resp_${id}`, usage: last },
432 });
433 const tokenCount = (timestamp: string, last: unknown, total: unknown) => ({
434 type: "event_msg",
435 timestamp,
436 payload: {
437 type: "token_count",
438 info: { last_token_usage: last, total_token_usage: total },
439 },
440 });
441 const lines = [
442 {
443 type: "session_meta",
444 timestamp: "2026-11-02T10:00:00.000Z",
445 payload: { id: "aborted-turn-repro", model_provider: "openai" },
446 },
447 context(1),
448 request(1, 1, "2026-11-02T10:00:01.000Z", usage(100, 20)),
449 tokenCount("2026-11-02T10:00:01.001Z", usage(100, 20), usage(100, 20)),
450 // Interrupted after this request finished: no token_count reports it.
451 request(1, 2, "2026-11-02T10:00:02.000Z", usage(150, 10)),
452 {
453 type: "event_msg",
454 timestamp: "2026-11-02T10:00:03.000Z",
455 payload: { type: "turn_aborted", turn_id: "turn-1", reason: "interrupted" },
456 },
457 context(2),
458 request(2, 3, "2026-11-02T10:00:05.000Z", usage(200, 30)),
459 tokenCount("2026-11-02T10:00:05.001Z", usage(200, 30), usage(450, 60)),
460 ];
461 // Lines longer than a read chunk take the projected path when streaming.
462 const padding = "x".repeat(256 * 1024);
463 await NodeFSP.writeFile(
464 path,
465 lines.map((line) => `${JSON.stringify({ padding, ...line })}\n`).join(""),
466 );
467​
468 const result = await readTranscriptRecords(path, TEST_FORMATS.codex, undefined, options);
469 assert.isNotNull(result);
470 const requests = [...result.records, ...result.tailRecords]
471 .map((record) => totalTokens(record.totals))
472 .toSorted((a, b) => a - b);
473 assert.deepStrictEqual(
474 {
475 billedTokens: requests.reduce((sum, tokens) => sum + tokens, 0),
476 requests,
477 malformedRecords: result.malformedRecords,
478 },
479 { billedTokens: 510, requests: [120, 160, 230], malformedRecords: 0 },
480 );
481 },
482 );
483​
484 it("re-parses from the start when the guard bytes no longer match", async () => {
485 const path = NodePath.join(dir, "claude.jsonl");
486 await NodeFSP.writeFile(path, claudeLine(1, 5));
487 const first = await readTranscriptRecords(path, TEST_FORMATS.claude);
488 assert.isNotNull(first);
489​
490 await NodeFSP.writeFile(path, claudeLine(4, 13) + claudeLine(5, 17));
491 const second = await readTranscriptRecords(path, TEST_FORMATS.claude, first.position);
492 assert.isNotNull(second);
493 assert.isFalse(second.resumed);
494 assert.deepStrictEqual(
495 second.records.map((record) => record.totals.outputTokens),
496 [13, 17],
497 );
498 });
499​
500 it("re-parses from the start when the file shrank below the resume point", async () => {
501 const path = NodePath.join(dir, "claude.jsonl");
502 await NodeFSP.writeFile(path, claudeLine(1, 5) + claudeLine(2, 7));
503 const first = await readTranscriptRecords(path, TEST_FORMATS.claude);
504 assert.isNotNull(first);
505​
506 await NodeFSP.writeFile(path, claudeLine(3, 11));
507 const second = await readTranscriptRecords(path, TEST_FORMATS.claude, first.position);
508 assert.isNotNull(second);
509 assert.isFalse(second.resumed);
510 assert.deepStrictEqual(
511 second.records.map((record) => record.totals.outputTokens),
512 [11],
513 );
514 });
515​
516 it("parses a line larger than one stream chunk", async () => {
517 const path = NodePath.join(dir, "claude.jsonl");
518 const bigLine = `${JSON.stringify({
519 type: "assistant",
520 timestamp: "2026-08-01T10:00:00Z",
521 requestId: "req_big",
522 sessionId: "session-1",
523 padding: "x".repeat(512 * 1024),
524 message: {
525 id: "msg_big",
526 model: "claude-fable-5",
527 usage: { input_tokens: 10, output_tokens: 42 },
528 },
529 })}\n`;
530 await NodeFSP.writeFile(path, bigLine + claudeLine(2, 7));
531​
532 const parsed = await readTranscriptRecords(path, TEST_FORMATS.claude);
533 assert.isNotNull(parsed);
534 assert.deepStrictEqual(
535 parsed.records.map((record) => record.totals.outputTokens),
536 [42, 7],
537 );
538 });
539​
540 it("keeps a CRLF Codex reducer line whose marker straddles a stream chunk", async () => {
541 const path = NodePath.join(dir, "rollout.jsonl");
542 const crlf = (line: string) => line.replace("\n", "\r\n");
543 const modelLine = crlf(codexModelLine("gpt-5.2-codex"));
544 const head = crlf(codexMetaLine());
545 const fillerBytes =
546 256 * 1024 - Buffer.byteLength(head) - modelLine.indexOf("turn_context") - 4;
547 const fillerEnvelope = crlf(`${JSON.stringify({ type: "response_item", output: "" })}\n`);
548 const padding = "é".repeat((fillerBytes - Buffer.byteLength(fillerEnvelope)) / 2);
549 const filler = crlf(`${JSON.stringify({ type: "response_item", output: padding })}\n`);
550 const content = head + filler + modelLine + crlf(codexUsageLine(40, 2));
551 await NodeFSP.writeFile(path, content);
552​
553 const result = await readTranscriptRecords(path, TEST_FORMATS.codex);
554 assert.isNotNull(result);
555 assert.strictEqual(result.records.length, 1);
556 assert.strictEqual(result.records[0]?.model, "gpt-5.2-codex");
557 assert.strictEqual(result.records[0]?.sessionId, "codex-session-1");
558 assert.strictEqual(result.position.resumeOffset, Buffer.byteLength(content));
559 });
560});
561​
562describe("transcriptLineGate", () => {
563 const gates = {
564 one: transcriptLineGate(['"usage"']),
565 several: transcriptLineGate(['"token_count"', '"session_meta"', '"a.b(c)"']),
566 };
567 const markers = { one: ['"usage"'], several: ['"token_count"', '"session_meta"', '"a.b(c)"'] };
568​
569 it("matches markers beside multi-byte and malformed UTF-8", () => {
570 const invalid = Buffer.from([0xff, 0xc3]);
571 for (const kind of ["one", "several"] as const) {
572 for (const marker of markers[kind]) {
573 assert.isTrue(gates[kind](Buffer.from(`{"text":"日本語 é",${marker}:1}`)));
574 assert.isTrue(gates[kind](Buffer.concat([invalid, Buffer.from(marker), invalid])));
575 assert.isFalse(gates[kind](Buffer.from(`{"text":"日本語 é ${marker.slice(1, -1)}"}`)));
576 }
577 }
578 });
579});