char-slop/berry

multipurpose self-modifiable agentic harness

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

Charlotte Sommove computer-use out of the repof5275c6

main
2.8 KiB76 linesraw
1import { assertEquals } from "@std/assert";
2import { withDom } from "@berry/util/dom.ts";
3import type { JsonValue } from "@berry/api";
4import type { ToolCall } from "@berry/api/web.ts";
5import { builtRenderers } from "./fixture.ts";
6
7const call = (patch: Partial<ToolCall> = {}): ToolCall => ({
8  name: "test",
9  args: null,
10  result: null,
11  details: null,
12  error: null,
13  output: null,
14  status: "ok",
15  startedAt: null,
16  durationMs: null,
17  ...patch,
18});
19
20/** What a card may be handed: arguments the model is still writing, and results of any shape. */
21const ODD: JsonValue[] = [
22  null,
23  42,
24  "partial",
25  "not json",
26  [],
27  [null, false],
28  {},
29  { path: 42, offset: "x", edits: "x", command: 5, url: false, format: 42 },
30  { actions: "bad", screenshots: "bad", completed: "bad", output: 42, exitCode: "bad" },
31  { actions: [null, false, { action: "pointer", x: 12 }] },
32  [{ title: "bad", url: false, snippet: "" }],
33];
34
35Deno.test("every built tool renderer survives partial arguments and malformed results", () =>
36  withDom(async () => {
37    for (const name of ["browser", "code", "web"]) {
38      for (const [tool, renderer] of Object.entries((await builtRenderers(name)).tools ?? {})) {
39        const body = renderer.body?.();
40        for (const value of ODD) {
41          renderer.summary?.(value);
42          renderer.timeout?.(value);
43          for (const status of ["streaming", "ok", "error"] as const) {
44            body?.update(call({ name: tool, status, args: value, result: value, details: value }));
45          }
46          body?.update(call({ name: tool, output: "streamed", error: "failed" }));
47        }
48        body?.dispose?.();
49      }
50    }
51  }));
52
53Deno.test("search results read the same as a value or as the text a direct call returns", () =>
54  withDom(async () => {
55    const view = (await builtRenderers("web")).tools!.web_search.body!();
56    const found = [{ title: "A result", url: "https://example.com/", snippet: "A snippet" }];
57    for (const result of [found, JSON.stringify(found)]) {
58      view.update(call({ result }));
59      assertEquals(view.element.querySelector("a")?.getAttribute("href"), "https://example.com/");
60    }
61  }));
62
63Deno.test("naive file renderers keep literal text that resembles line anchors", () =>
64  withDom(async () => {
65    const renderers = (await builtRenderers("code")).tools!;
66    const read = renderers.read.body!();
67    read.update(call({ result: "AbCd│literal", details: { editMode: "naive" } }));
68    assertEquals(read.element.textContent, "AbCd│literal");
69
70    const diff = renderers.edit.body!();
71    const result = "+AbCd│literal";
72    diff.update(call({ result, details: { editMode: "naive" } }));
73    assertEquals(diff.element.textContent, "+AbCd│literal");
74    diff.update(call({ result }));
75    assertEquals(diff.element.textContent, "+literal");
76  }));