char-slop/berry

multipurpose self-modifiable agentic harness

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

Charlotte Somupdate to pi-durable 1.2.0049fbda

main
4.4 KiB111 linesraw
1import { assertEquals, assertRejects } from "@std/assert";
2import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context";
3import { type Message, Type } from "@earendil-works/pi-ai";
4import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai/providers/faux";
5import { defineExtension, type JsonValue } from "@berry/api";
6import { BerryError } from "./berry.ts";
7import { sectionsSeen, setup } from "./fixture.ts";
8
9const configurable = defineExtension<
10  { cwd: string; verbose: boolean; edits: number },
11  { cwd: string; verbose: boolean }
12>({
13  name: "configurable",
14  initial: () => ({ cwd: "/", verbose: false, edits: 0 }),
15  apply: (state, event) => ({ ...state, ...event, edits: state.edits + 1 }),
16  setup(ext) {
17    ext.configuration({
18      parameters: Type.Object({
19        cwd: Type.String({ default: "/" }),
20        verbose: Type.Boolean({ default: false }),
21      }),
22      values: ({ cwd, verbose }) => ({ cwd, verbose }),
23      events: ({ cwd, verbose }) => {
24        if (!cwd.startsWith("/")) throw new Error(`not absolute: ${cwd}`);
25        return [{ cwd, verbose }];
26      },
27    });
28    return (conv) => conv.prompt(() => `cwd: ${conv.state.cwd}`);
29  },
30});
31
32Deno.test("configuration seeds new conversations and exposes only editable state", async () => {
33  await using harness = await setup();
34  const { berry } = harness;
35  await berry.install(configurable);
36  const tid = await berry.create({ extensions: { configurable: { cwd: "/seed" } } });
37  const feed = (await berry.watchConfiguration(tid, "configurable"))!;
38  assertEquals(feed.value, { cwd: "/seed", verbose: false });
39  await feed.stop();
40  const defaults = await berry.create({ extensions: { configurable: {} } });
41  assertEquals(await berry.state(defaults, "configurable"), {
42    cwd: "/",
43    verbose: false,
44    edits: 1,
45  });
46  await assertRejects(
47    () => berry.create({ extensions: { configurable: { cwd: 5 } } }),
48    BerryError,
49    "cwd",
50  );
51  await assertRejects(
52    () => berry.create({ extensions: { configurable: { cwd: "relative" } } }),
53    BerryError,
54    "not absolute",
55  );
56});
57
58Deno.test("configuration patches preserve other fields, validate before writing, and replay from events", async () => {
59  await using harness = await setup();
60  const { berry } = harness;
61  await berry.install(configurable);
62  const tid = await berry.create({ extensions: { configurable: { cwd: "/seed" } } });
63  assertEquals(await berry.configureExtension(tid, "configurable", { verbose: true }), {
64    cwd: "/seed",
65    verbose: true,
66  });
67  const invalid: Record<string, JsonValue>[] = [{ cwd: 5 }, { cwd: "relative" }, { edits: 999 }];
68  for (const change of invalid) {
69    await assertRejects(() => berry.configureExtension(tid, "configurable", change), BerryError);
70  }
71  await berry.install(configurable);
72  assertEquals(await berry.state(tid, "configurable"), { cwd: "/seed", verbose: true, edits: 2 });
73  await berry.configure(tid, { extensions: [] });
74  await assertRejects(
75    () => berry.configureExtension(tid, "configurable", { cwd: "/disabled" }),
76    BerryError,
77    "no enabled configuration",
78  );
79  await assertRejects(() => berry.configureExtension(tid, "missing", {}), BerryError);
80});
81
82Deno.test("a configuration edit lands at once while busy and reaches the turn's next request", async () => {
83  await using harness = await setup();
84  const { berry, faux, model, gate } = harness;
85  try {
86    await berry.install(configurable);
87    const tid = await berry.create({ extensions: { configurable: {}, gate: {} }, model });
88    let next: readonly Message[] = [];
89    faux.setResponses([
90      fauxAssistantMessage(fauxToolCall("hold", {}), { stopReason: "toolUse" }),
91      (ctx) => (next = ctx.messages, fauxAssistantMessage("done")),
92    ]);
93    const conversation = (await berry.conversation(tid))!;
94    const submission = await conversation.submit({ type: "input", content: "go" }, context);
95    await gate.entered.promise;
96    assertEquals(await berry.configureExtension(tid, "configurable", { cwd: "/mid" }), {
97      cwd: "/mid",
98      verbose: false,
99    });
100    assertEquals(await berry.state(tid, "configurable"), { cwd: "/mid", verbose: false, edits: 2 });
101    gate.release.resolve("released");
102    await submission.wait(context);
103    await conversation.waitForIdle(context);
104    assertEquals(
105      sectionsSeen(next).at(-1)?.configurable,
106      "<configurable>\ncwd: /mid\n</configurable>",
107    );
108  } finally {
109    gate.release.resolve("released");
110  }
111});