char-slop/berry

multipurpose self-modifiable agentic harness

git clone https://git.t4t.associates/char-slop/berry

Charlotte Somcut a bunch of code volumecb11725

main
4.2 KiB115 linesraw
1import { assertEquals } from "@std/assert";
2import type { ConversationId, ConversationView, EntryId } from "@earendil-works/pi-durable";
3import { withDom } from "@berry/util/dom.ts";
4import { readWhenVisible } from "./read.ts";
5import { Scope } from "./ui.ts";
6
7Deno.test("read receipts follow visible settled views without acknowledging unseen turns", async (t) => {
8  await withDom(async (window) => {
9    let visibility = "visible";
10    Object.defineProperty(document, "visibilityState", {
11      configurable: true,
12      get: () => visibility,
13    });
14    const scope = new Scope();
15    const sent: number[] = [];
16    let acknowledge = (_entryId: number) => Promise.resolve();
17    const update = readWhenVisible(scope, (entryId) => {
18      sent.push(entryId);
19      return acknowledge(entryId);
20    });
21    const view = (id: number, docs: ConversationView["docs"] = {}): ConversationView => ({
22      conversation: { id: 1 as ConversationId },
23      entries: [
24        { id: (id - 1) as EntryId, conversationId: 1 as ConversationId, kind: "pi.user" },
25        { id: id as EntryId, conversationId: 1 as ConversationId, kind: "pi.assistant" },
26        { id: (id + 1) as EntryId, conversationId: 1 as ConversationId, kind: "berry.event" },
27      ],
28      docs,
29    });
30    const settle = () => window.happyDOM.waitUntilComplete();
31    const visible = (state: string) => {
32      visibility = state;
33      document.dispatchEvent(new window.Event("visibilitychange") as unknown as Event);
34    };
35    try {
36      await t.step("working views and queued follow-ups do not send read receipts", async () => {
37        update(view(10, { "pi.live": { run: { taskId: 1, inputs: [] } } }));
38        update(view(10, { "pi.inbox": { items: [{ id: 1, mode: "followUp", content: "next" }] } }));
39        await settle();
40        assertEquals(sent, []);
41        update(view(10));
42        await settle();
43        assertEquals(sent, [10]);
44        update(view(10));
45        await settle();
46        assertEquals(sent, [10]);
47      });
48      await t.step("hidden turns wait until the tab becomes visible", async () => {
49        visible("hidden");
50        update(view(20));
51        await settle();
52        assertEquals(sent, [10]);
53        visible("visible");
54        await settle();
55        assertEquals(sent, [10, 20]);
56      });
57      await t.step(
58        "a late read receipt sends only its captured message, then the newer displayed one",
59        async () => {
60          const pending = Promise.withResolvers<void>();
61          acknowledge = (entryId) => entryId === 30 ? pending.promise : Promise.resolve();
62          update(view(30));
63          update(view(40));
64          assertEquals(sent, [10, 20, 30]);
65          pending.resolve();
66          await settle();
67          assertEquals(sent, [10, 20, 30, 40]);
68        },
69      );
70      await t.step(
71        "a newer hidden turn is not acknowledged after the older receipt finishes",
72        async () => {
73          const pending = Promise.withResolvers<void>();
74          acknowledge = () => pending.promise;
75          update(view(50));
76          visible("hidden");
77          update(view(60));
78          pending.resolve();
79          await settle();
80          assertEquals(sent, [10, 20, 30, 40, 50]);
81          acknowledge = () => Promise.resolve();
82          visible("visible");
83          await settle();
84          assertEquals(sent, [10, 20, 30, 40, 50, 60]);
85        },
86      );
87      await t.step(
88        "returning to an earlier branch cannot move the read watermark backwards",
89        async () => {
90          update(view(20));
91          await settle();
92          assertEquals(sent, [10, 20, 30, 40, 50, 60]);
93        },
94      );
95      await t.step(
96        "leaving the conversation removes visibility acknowledgements and pending work",
97        async () => {
98          const pending = Promise.withResolvers<void>();
99          acknowledge = () => pending.promise;
100          update(view(70));
101          visible("hidden");
102          update(view(80));
103          scope.dispose();
104          visible("visible");
105          pending.resolve();
106          await settle();
107          update(view(90));
108          assertEquals(sent, [10, 20, 30, 40, 50, 60, 70]);
109        },
110      );
111    } finally {
112      scope.dispose();
113    }
114  });
115});