1import { assert, describe, it } from "@effect/vitest";
2import * as Effect from "effect/Effect";
3
4import {
5 decodeProviderReplayNdjson,
6 materializeReplayTranscriptWorkspace,
7} from "./replayTranscript.ts";
8
9const header = JSON.stringify({
10 type: "transcript_start",
11 provider: "codex",
12 protocol: "codex.app-server",
13 version: "0.156.1",
14 scenario: "simple",
15});
16
17describe("decodeProviderReplayNdjson", () => {
18 it.effect("reads the header and entries, skipping blank lines", () =>
19 Effect.gen(function* () {
20 const transcript = yield* decodeProviderReplayNdjson(
21 [
22 header,
23 "",
24 JSON.stringify({ type: "expect_outbound", frame: { method: "initialize" } }),
25 JSON.stringify({ type: "emit_inbound", frame: { result: {} } }),
26 ].join("\n"),
27 );
28
29 assert.strictEqual(transcript.provider, "codex");
30 assert.strictEqual(transcript.scenario, "simple");
31 assert.deepStrictEqual(
32 transcript.entries.map((entry) => entry.type),
33 ["expect_outbound", "emit_inbound"],
34 );
35 }),
36 );
37
38 it.effect("rejects a second transcript_start record", () =>
39 Effect.gen(function* () {
40 const error = yield* Effect.flip(decodeProviderReplayNdjson([header, header].join("\n")));
41
42 assert.strictEqual(error._tag, "ProviderReplayNdjsonLineParseError");
43 assert.strictEqual(
44 error._tag === "ProviderReplayNdjsonLineParseError" && error.lineNumber,
45 2,
46 );
47 }),
48 );
49
50 it.effect("requires a header unless fallback metadata is given", () =>
51 Effect.gen(function* () {
52 const entry = JSON.stringify({ type: "emit_inbound", frame: {} });
53
54 const error = yield* Effect.flip(decodeProviderReplayNdjson(entry));
55 assert.strictEqual(error._tag, "ProviderReplayNdjsonMissingHeaderError");
56
57 const transcript = yield* decodeProviderReplayNdjson(entry, {
58 provider: "pi",
59 protocol: "pi.rpc-jsonl",
60 version: "1",
61 scenario: "fallback",
62 });
63 assert.strictEqual(transcript.entries.length, 1);
64 }),
65 );
66});
67
68describe("materializeReplayTranscriptWorkspace", () => {
69 it("replaces workspace placeholders in outbound frames only", () => {
70 const transcript = materializeReplayTranscriptWorkspace(
71 {
72 provider: "codex",
73 protocol: "codex.app-server",
74 version: "0.156.1",
75 scenario: "simple",
76 entries: [
77 {
78 type: "expect_outbound",
79 frame: { params: { cwd: "<workspace>", argv: ["<workspace>"] } },
80 },
81 { type: "emit_inbound", frame: { cwd: "<workspace>" } },
82 ],
83 },
84 "/tmp/fixture",
85 );
86
87 assert.deepStrictEqual(transcript.entries[0], {
88 type: "expect_outbound",
89 frame: { params: { cwd: "/tmp/fixture", argv: ["/tmp/fixture"] } },
90 });
91 assert.deepStrictEqual(transcript.entries[1], {
92 type: "emit_inbound",
93 frame: { cwd: "<workspace>" },
94 });
95 });
96});