char-slop/berry

multipurpose self-modifiable agentic harness

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

Charlotte Somperf: abort subprocesses instantly on restartba8ed3c

main
6.6 KiB164 linesraw
1import { assert, assertEquals, assertMatch, assertRejects } from "@std/assert";
2import { join } from "node:path";
3import { tempDir } from "@berry/util";
4import { denoCheck, type ExtensionEntry } from "./loader.ts";
5import { extensionWeb } from "./web.ts";
6import { extension, fixture, open, put } from "./loader-fixture.ts";
7
8Deno.test("an aborted check rejects with its reason instead of reporting a failure", async () => {
9  const stopping = new AbortController();
10  stopping.abort(new Error("berry is stopping"));
11  await assertRejects(
12    () => denoCheck(Deno.cwd(), ["server/main.ts"], stopping.signal),
13    Error,
14    "berry is stopping",
15  );
16});
17
18Deno.test("startup traces include slow and failed imports and survive later reloads", () =>
19  fixture({
20    "slow/mod.ts": `await new Promise((resolve) => setTimeout(resolve, 20));\n${extension("slow")}`,
21    "slow/version.ts": `export const VERSION = "one";`,
22    "slow/web.tsx": `export default {};`,
23    "broken/mod.ts": `await new Promise((resolve) => setTimeout(resolve, 20));
24      throw new Error("broken import");`,
25  }, async (root) => {
26    const { loader, close } = await open(root);
27    try {
28      await loader.loadAll();
29      const startup = loader.entries().find((entry) => entry.name === "slow")!.startup;
30      assert(startup);
31      assert(startup.phases.find((phase) => phase.name === "import")!.durationMs >= 10);
32      assertEquals(startup.phases.find((phase) => phase.name === "frontend")!.source, "build");
33      assert(startup.phases.some((phase) => phase.name === "install"));
34      assert(startup.totalMs >= startup.phases.reduce((sum, phase) => sum + phase.durationMs, 0));
35      const failed = loader.entries().find((entry) => entry.name === "broken")!.startup;
36      assert(failed);
37      assertEquals(failed.status, "failed");
38      assert(failed.phases.find((phase) => phase.name === "import")!.durationMs >= 10);
39      assert(!failed.phases.some((phase) => phase.name === "install"));
40      await loader.reload("slow");
41      assertEquals(loader.entries().find((entry) => entry.name === "slow")!.startup, startup);
42      await put(join(root, "extensions/slow"), { "version.ts": `export const VERSION = ;` });
43      await loader.reload("slow");
44      const current = loader.entries().find((entry) => entry.name === "slow")!;
45      assertEquals(current.status, "failed");
46      assertEquals(current.startup, startup);
47    } finally {
48      await close();
49    }
50  }));
51
52Deno.test("startup traces distinguish restored frontends from failed builds", () =>
53  fixture({
54    "cached/mod.ts": extension("cached", `const VERSION = "one";`),
55    "cached/web.tsx": "export default {};",
56    "badweb/mod.ts": extension("badweb", `const VERSION = "one";`),
57    "badweb/web.tsx": "export default (;",
58  }, async (root) => {
59    await using cache = await tempDir();
60    await extensionWeb({ cacheDirectory: cache.path }).build(
61      "cached",
62      join(root, "extensions/cached/web.tsx"),
63    );
64    const { loader, close } = await open(
65      root,
66      undefined,
67      extensionWeb({ cacheDirectory: cache.path }),
68    );
69    try {
70      await loader.loadAll();
71      const restored = loader.entries().find((entry) => entry.name === "cached")!.startup!;
72      assertEquals(restored.status, "loaded");
73      assertEquals(restored.phases.find((phase) => phase.name === "frontend")!.source, "cache");
74      const failed = loader.entries().find((entry) => entry.name === "badweb")!.startup!;
75      assertEquals(failed.status, "failed");
76      assertEquals(failed.phases.find((phase) => phase.name === "frontend")!.source, "build");
77    } finally {
78      await close();
79    }
80  }));
81
82Deno.test("subagent is a selectable builtin and disk reloads cannot replace it", () =>
83  fixture({
84    "subagent/mod.ts": extension("subagent"),
85    "subagent/version.ts": `export const VERSION = "shadow";`,
86  }, async (root) => {
87    const { loader, close } = await open(root);
88    const feed = loader.feed();
89    try {
90      await loader.loadAll();
91      const installed = (feed.value as ExtensionEntry[]).find((ext) => ext.name === "subagent");
92      assert(installed);
93      assert(installed.builtin && installed.running);
94      const result = await loader.reload("subagent");
95      assertEquals(result.status, "failed");
96      assertMatch(result.error!, /restart berry/);
97      assertEquals((feed.value as ExtensionEntry[]).filter((ext) => ext.name === "subagent"), [
98        installed,
99      ]);
100    } finally {
101      await feed.stop();
102      await close();
103    }
104  }));
105
106Deno.test("the ui lists extensions by configuration order, and those without one after them by name", () =>
107  fixture({
108    "middle/mod.ts": extension("middle", `const VERSION = "middle";`, 20),
109    "beta/mod.ts": extension("beta", `const VERSION = "beta";`),
110    "alpha/mod.ts": extension("alpha", `const VERSION = "alpha";`),
111  }, async (root) => {
112    const { loader, close } = await open(root);
113    const feed = loader.feed();
114    try {
115      await loader.loadAll();
116      assertEquals(
117        (feed.value as ExtensionEntry[]).map((entry) => entry.name),
118        ["environment", "middle", "subagent", "alpha", "beta"],
119      );
120    } finally {
121      await feed.stop();
122      await close();
123    }
124  }));
125
126Deno.test("the ui sees which extensions a new conversation starts with", () =>
127  fixture({
128    "lean/mod.ts": `import { defineExtension } from "@berry/api";
129      export default defineExtension({
130        name: "lean",
131        defaultEnabled: true,
132        initial: () => null,
133        setup() {},
134      });`,
135    "shy/mod.ts": extension("shy"),
136  }, async (root) => {
137    const { loader, close } = await open(root);
138    try {
139      await loader.loadAll();
140      const byName = Object.fromEntries(loader.entries().map((entry) => [entry.name, entry]));
141      assertEquals(byName.lean.defaultEnabled, true);
142      assertEquals(byName.shy.defaultEnabled, false);
143    } finally {
144      await close();
145    }
146  }));
147
148Deno.test("each extension loads on its own, and one that fails stops none of the others", () =>
149  fixture({
150    "good/mod.ts": extension("good"),
151    "good/version.ts": `export const VERSION = "one";`,
152    "misnamed/mod.ts": extension("other", `const VERSION = "x";`),
153    "empty/notes.md": "no mod.ts here",
154  }, async (root) => {
155    const { loader, close, describe } = await open(root);
156    await loader.loadAll();
157    const byName = Object.fromEntries(loader.entries().map((e) => [e.name, e]));
158    assertEquals(byName.good.status, "loaded");
159    assertEquals(describe("good"), "one");
160    assertEquals(byName.misnamed.status, "failed");
161    assertMatch(byName.misnamed.error!, /names itself other/);
162    assertMatch(byName.empty.error!, /has no mod\.ts/);
163    await close();
164  }));