Skip to content
Source
Sign inSign up

workflowAgentTranscript.test.ts

65 lines2.8 KB

1// @effect-diagnostics nodeBuiltinImport:off
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4import * as NodeServices from "@effect/platform-node/NodeServices";
5import { it } from "@effect/vitest";
6import * as Effect from "effect/Effect";
7import * as FileSystem from "effect/FileSystem";
8import { assert } from "vite-plus/test";
9​
10import {
11 initialWorkflowAgentTranscriptCursor,
12 readWorkflowAgentTranscript,
13} from "./workflowAgentTranscript.ts";
14​
15const line = (value: unknown) => `${JSON.stringify(value)}\n`;
16const assistant = (uuid: string, text: string) =>
17 line({
18 type: "assistant",
19 uuid,
20 message: { role: "assistant", id: uuid, content: [{ type: "text", text }] },
21 });
22const uuids = (lines: ReadonlyArray<unknown>) =>
23 lines.map((entry) => (entry as { readonly uuid: string }).uuid);
24​
25it.layer(NodeServices.layer)("readWorkflowAgentTranscript", (it) => {
26 it.effect("reads complete lines from its cursor and starts over on a new file", () =>
27 Effect.gen(function* () {
28 const fileSystem = yield* FileSystem.FileSystem;
29 const configDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "wf-transcript-" });
30 const transcriptDir = NodePath.join(configDir, "projects", "test");
31 yield* fileSystem.makeDirectory(transcriptDir, { recursive: true });
32 const file = NodePath.join(transcriptDir, "agent-a1.jsonl");
33 const read = (cursor = initialWorkflowAgentTranscriptCursor) =>
34 readWorkflowAgentTranscript({ transcriptDir, agentId: "a1", cursor, configDir });
35​
36 const bookkeeping =
37 line({ type: "attachment", uuid: "att", attachment: { type: "skill_listing" } }) +
38 line({ type: "user", uuid: "meta", isMeta: true, message: { content: "context" } });
39 const second = assistant("b", "second");
40 NodeFS.writeFileSync(file, bookkeeping + assistant("a", "first") + second.slice(0, 20));
41 const first = yield* read();
42 assert.deepEqual(uuids(first.lines), ["a"]);
43 assert.equal(
44 first.cursor.offset,
45 Buffer.byteLength(bookkeeping + assistant("a", "first")),
46 "a half-written line is left for the next read",
47 );
48​
49 NodeFS.appendFileSync(file, second.slice(20));
50 const next = yield* read(first.cursor);
51 assert.deepEqual(uuids(next.lines), ["b"]);
52 assert.equal(next.cursor.offset, NodeFS.statSync(file).size);
53​
54 NodeFS.rmSync(file);
55 NodeFS.writeFileSync(file, assistant("c", "replaced"));
56 const replaced = yield* read(next.cursor);
57 assert.deepEqual(uuids(replaced.lines), ["c"], "a new inode reads from 0");
58​
59 NodeFS.writeFileSync(file, assistant("d", "x"));
60 assert.equal(NodeFS.statSync(file).ino, replaced.cursor.identity?.ino);
61 const shrunk = yield* read(replaced.cursor);
62 assert.deepEqual(uuids(shrunk.lines), ["d"], "a shrunk file reads from 0");
63 }).pipe(Effect.scoped),
64 );
65});