char-slop/berry

multipurpose self-modifiable agentic harness

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

Charlotte Somcodemode: flatten the generated tool catalogf4dfe65

main
6.6 KiB149 linesraw
1import { assert, assertEquals, assertMatch, assertStringIncludes } from "@std/assert";
2import { Type } from "@earendil-works/pi-ai";
3import { renderCatalog } from "./codemode.ts";
4import { lastToolResult, script, sectionsSeen, setup, turn } from "./fixture.ts";
5
6Deno.test("scripts call tools in their own extension's context and see its emits", async () => {
7  await using harness = await setup();
8  const tid = await harness.berry.create({ extensions: { dir: {} }, model: harness.model });
9  const seen = await turn(harness, tid, [
10    script(`return [await tools.pwd({}), await tools.cd({ path: "/x" }), await tools.pwd({})]`),
11  ]);
12  assertEquals(lastToolResult(seen[1]).text, `Script completed\n["/","now in /x","/x"]`);
13  assertEquals(await harness.berry.state(tid, "dir"), { cwd: "/x" });
14});
15
16Deno.test("the catalog declares only the selected extensions' codemode tools", async () => {
17  await using harness = await setup();
18  const tid = await harness.berry.create({ extensions: { dir: {} }, model: harness.model });
19  const [seen] = await turn(harness, tid, []);
20  const catalog = sectionsSeen(seen).at(-1)?.codemode ?? "";
21  assertStringIncludes(catalog, "print the working directory");
22  assertStringIncludes(catalog, "cd: string");
23  assert(
24    !catalog.includes("path:"),
25    "direct tools' arguments are supplied by their native schemas",
26  );
27  assertStringIncludes(catalog, "text: string");
28  assert(!catalog.includes("change directory"), "direct tools are described only once");
29  assert(!catalog.includes("shout"), "model-only tools are not declared to scripts");
30  assert(!catalog.includes("hold"), "unselected extensions are not declared");
31});
32
33Deno.test("the catalog retains tools and return types across extensions", () => {
34  const catalog = renderCatalog([
35    {
36      name: "run",
37      extension: "shell",
38      description: "execute a command",
39      parameters: Type.Object({ command: Type.String() }),
40      returns: Type.Object({
41        exitCode: Type.Integer(),
42        output: Type.String(),
43        fullOutputPath: Type.Optional(Type.String()),
44      }),
45      direct: true,
46    },
47    {
48      name: "find",
49      extension: "search",
50      description: "search indexed documents",
51      parameters: Type.Object({ query: Type.String() }),
52      returns: Type.Array(Type.String()),
53    },
54  ])!;
55  assertStringIncludes(catalog, "run:");
56  assertStringIncludes(catalog, "exitCode: number");
57  assertStringIncludes(catalog, "output: string");
58  assertStringIncludes(catalog, "fullOutputPath?: string");
59  assertStringIncludes(catalog, "find(args:");
60  assertStringIncludes(catalog, "search indexed documents");
61  assertStringIncludes(catalog, "query: string");
62  assertStringIncludes(catalog, "Promise<Array<string>>");
63});
64
65Deno.test("tools of unselected extensions are unreachable", async () => {
66  await using harness = await setup();
67  const tid = await harness.berry.create({ extensions: { gate: {} }, model: harness.model });
68  const seen = await turn(harness, tid, [script(`return await tools.pwd({})`)]);
69  const result = lastToolResult(seen[1]);
70  assert(result.isError);
71  assertMatch(result.text, /^Script failed/);
72});
73
74Deno.test("nested arguments are validated like the model's, before the tool runs", async () => {
75  await using harness = await setup();
76  const tid = await harness.berry.create({ extensions: { dir: {} }, model: harness.model });
77  const seen = await turn(harness, tid, [
78    script(`try { await tools.cd({ nowhere: 1 }) } catch (error) { return error.message }`),
79  ]);
80  assertStringIncludes(lastToolResult(seen[1]).text, "path");
81  assertEquals(await harness.berry.state(tid, "dir"), { cwd: "/" });
82});
83
84Deno.test("nested calls are recorded for display, never for the model", async () => {
85  await using harness = await setup();
86  const tid = await harness.berry.create({ extensions: { dir: {} }, model: harness.model });
87  const seen = await turn(harness, tid, [
88    script(`await tools.pwd({}); try { await tools.cd({}) } catch {} ; return "ok"`),
89  ]);
90  const { text, details } = lastToolResult(seen[1]);
91  assertEquals(text, "Script completed\nok");
92  const calls = (details as { calls: Record<string, unknown>[] }).calls;
93  assertEquals(
94    calls.map(({ name, extension, status, value }) => ({ name, extension, status, value })),
95    [
96      { name: "pwd", extension: "dir", status: "ok", value: "/" },
97      { name: "cd", extension: "dir", status: "error", value: undefined },
98    ],
99  );
100  assert(typeof calls[1].error === "string");
101});
102
103Deno.test("nested running output streams into the call's record", async () => {
104  await using harness = await setup();
105  const tid = await harness.berry.create({ extensions: { dir: {} }, model: harness.model });
106  const seen = await turn(harness, tid, [script(`return await tools.say({ text: "one two" })`)]);
107  const { text, details } = lastToolResult(seen[1]);
108  assertEquals(text, "Script completed\n7");
109  assertEquals((details as { calls: { output?: string }[] }).calls[0].output, "one\ntwo\n");
110});
111
112Deno.test("the store keeps successful scripts' writes across calls", async () => {
113  await using harness = await setup();
114  const tid = await harness.berry.create({ extensions: {}, model: harness.model });
115  const seen = await turn(harness, tid, [
116    script(`store("n", 1)`),
117    script(`store("n", load("n") + 1); throw new Error("no")`),
118    script(`return load("n")`),
119  ]);
120  assert(lastToolResult(seen[2]).isError);
121  assertEquals(lastToolResult(seen[3]).text, "Script completed\n1");
122  assertEquals(await harness.berry.state(tid, "berry"), { n: 1 });
123});
124
125Deno.test("failures report the calls already made, which stand", async () => {
126  await using harness = await setup();
127  const tid = await harness.berry.create({ extensions: { dir: {} }, model: harness.model });
128  const seen = await turn(harness, tid, [
129    script(`await tools.cd({ path: "/y" }); throw new Error("boom")`),
130  ]);
131  const { text, isError } = lastToolResult(seen[1]);
132  assert(isError);
133  assertStringIncludes(text, "Error: boom");
134  assertStringIncludes(text, "cd (ok)");
135  assertEquals(await harness.berry.state(tid, "dir"), { cwd: "/y" });
136});
137
138Deno.test("output over budget keeps its start and end and spills the whole of it", async () => {
139  await using harness = await setup();
140  const tid = await harness.berry.create({ extensions: {}, model: harness.model });
141  const seen = await turn(harness, tid, [
142    script(`// @options: {"max_output_tokens": 10}\ntext("a".repeat(100) + "b".repeat(100))`),
143  ]);
144  const { text } = lastToolResult(seen[1]);
145  assertMatch(text, /^Script completed\na{20}…\d+ tokens truncated…b{20}/);
146  const path = text.match(/\[Full output: (.+)\]/)![1];
147  assertEquals(await Deno.readTextFile(path), "a".repeat(100) + "b".repeat(100));
148  await Deno.remove(path);
149});