char-slop/berry

multipurpose self-modifiable agentic harness

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

Charlotte Somadd skills extension for loading skillsa4e772a

main
8.5 KiB178 linesraw
1import { assert, assertEquals, assertRejects, assertStringIncludes } from "@std/assert";
2import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context";
3import { getOrThrow } from "@earendil-works/pi-durable/env";
4import { rootedEnvironment, testExtension } from "@berry/api/testing.ts";
5import { tempDir } from "@berry/util";
6import { skillsExtension } from "./mod.ts";
7import { activeDirectory, hostEnvironment, targetEnvironment, writeSkill } from "./testing.ts";
8
9Deno.test("project skills require explicit loading or conversation-local trust", async () => {
10  await using root = await tempDir();
11  const env = rootedEnvironment(root.path, { cwd: "/repo/src" });
12  getOrThrow(await env.createDir(env.cwd, { recursive: true }, context));
13  getOrThrow(await env.writeFile("/repo/.jj/repo", "", context));
14  await writeSkill(env, "/repo/.agents/skills", "release", "Use the release checklist.");
15  const ext = testExtension(skillsExtension(hostEnvironment(root.path)), { env });
16  assert(!(await ext.prompt()).includes("release"));
17  assertStringIncludes(await ext.command("skills") as string, "explicit loading only");
18  await assertRejects(() => ext.call("load_skill", { name: "release" }), Error, "not available");
19
20  await ext.command("skill", "release");
21  assertStringIncludes(await ext.prompt(), "Use the release checklist.");
22  await ext.command("unskill", "release");
23  assert(!(await ext.prompt()).includes("Use the release checklist."));
24  await ext.command("skills", "trust");
25  assertStringIncludes(await ext.prompt(), '"name":"release"');
26  await ext.call("load_skill", { name: "release" });
27  await ext.command("skills", "untrust");
28  assertStringIncludes(await ext.prompt(), "Use the release checklist.");
29  await assertRejects(() => ext.call("load_skill", { name: "release" }), Error, "not available");
30  assertEquals(ext.messages, []);
31});
32
33Deno.test("activation preserves an exact snapshot, reload replaces it, and resources stay on demand", async () => {
34  await using root = await tempDir();
35  const env = rootedEnvironment(root.path);
36  const body =
37    "# Review\nHuman prose:  keep this spacing.\nRead references/checklist.md.\nRun scripts/check.sh when needed.\n";
38  const path = await writeSkill(env, "/home/.agents/skills", "review", body);
39  getOrThrow(
40    await env.writeFile(
41      "/home/.agents/skills/review/references/checklist.md",
42      "resource-only text",
43      context,
44    ),
45  );
46  const ext = testExtension(skillsExtension(hostEnvironment(root.path)), { env });
47  const acknowledgment = await ext.call("load_skill", { name: "review" }) as string;
48  assert(!acknowledgment.includes(body));
49  const prompt = await ext.prompt();
50  assertStringIncludes(prompt, body);
51  assertStringIncludes(prompt, "/home/.agents/skills/review");
52  assert(!prompt.includes("resource-only text"));
53
54  await writeSkill(env, "/home/.agents/skills", "review", "Updated instructions.");
55  assertStringIncludes(await ext.prompt(), body);
56  await ext.call("load_skill", { name: "review" });
57  const reloaded = await ext.prompt();
58  assert(!reloaded.includes(body));
59  assertEquals(reloaded.split("Updated instructions.").length - 1, 1);
60  getOrThrow(await env.remove(path, {}, context));
61  assertStringIncludes(await ext.prompt(), "Updated instructions.");
62  await ext.command("unskill", "review");
63  assertEquals(await ext.prompt(), "");
64});
65
66Deno.test("trust and active resource paths do not transfer to a different repository or machine", async () => {
67  await using root = await tempDir();
68  const env = rootedEnvironment(root.path, { cwd: "/first" });
69  getOrThrow(await env.writeFile("/first/.git", "", context));
70  getOrThrow(await env.writeFile("/second/.git", "", context));
71  await writeSkill(env, "/first/.agents/skills", "first", "First repository workflow.");
72  await writeSkill(env, "/second/.agents/skills", "second", "Second repository workflow.");
73  const ext = testExtension(skillsExtension(hostEnvironment(root.path)), { env });
74  await ext.command("skills", "trust");
75  await ext.call("load_skill", { name: "first" });
76  env.cwd = "/second";
77  assertStringIncludes(await ext.prompt(), "First repository workflow.");
78  assert(!(await ext.prompt()).includes('"name":"second"'));
79  await assertRejects(() => ext.call("load_skill", { name: "second" }), Error, "not available");
80
81  const other = Object.assign(rootedEnvironment(root.path, { cwd: "/first" }), { name: "other" });
82  // replay the same history against another machine, as an environment rebind does
83  const first = ext.state.active[0];
84  assert(first.scope !== "host");
85  const events = [
86    { type: "trust" as const, environment: env.name, root: "/first", trusted: true },
87    { type: "load" as const, skill: first },
88  ];
89  const rebound = testExtension(skillsExtension(hostEnvironment(root.path)), {
90    env: other,
91    events,
92  });
93  assertEquals(await rebound.prompt(), "");
94  await assertRejects(() => rebound.call("load_skill", { name: "first" }), Error, "not available");
95  assertStringIncludes(await rebound.command("skills") as string, "paused");
96});
97
98Deno.test("parallel loads enforce the aggregate budget without evicting existing skills", async () => {
99  await using root = await tempDir();
100  const env = rootedEnvironment(root.path);
101  for (const name of ["first", "second", "third"]) {
102    await writeSkill(env, "/home/.agents/skills", name, `${name}\n${"x".repeat(50 * 1024)}`);
103  }
104  const ext = testExtension(skillsExtension(hostEnvironment(root.path)), { env });
105  const results = await Promise.allSettled(
106    ["first", "second", "third"].map((name) => ext.call("load_skill", { name })),
107  );
108  assertEquals(results.map(({ status }) => status), ["fulfilled", "fulfilled", "rejected"]);
109  if (results[2].status === "rejected") assertStringIncludes(results[2].reason.message, "Unload");
110  assertEquals(ext.state.active.map(({ name }) => name), ["first", "second"]);
111  await ext.call("load_skill", { name: "second" });
112  await ext.command("unskill", "first");
113  await ext.call("load_skill", { name: "third" });
114  assertEquals(ext.state.active.map(({ name }) => name), ["second", "third"]);
115});
116
117Deno.test("host activations follow new environments with their pinned resource version until reloaded", async () => {
118  await using home = await tempDir();
119  await using target = await tempDir();
120  await using other = await tempDir();
121  const host = hostEnvironment(home.path);
122  const main = await writeSkill(
123    host,
124    `${home.path}/.config/berry/skills`,
125    "release",
126    "Read the release checklist.",
127  );
128  const source = main.slice(0, -"/SKILL.md".length);
129  getOrThrow(
130    await host.writeFile(`${source}/references/checklist.txt`, "original checklist", context),
131  );
132  const env = targetEnvironment(target.path);
133  const ext = testExtension(skillsExtension(host), { env });
134  assert(!(await ext.prompt()).includes("original checklist"));
135  await ext.call("load_skill", { name: "release" });
136  const snapshot = ext.state.active[0];
137  assert(snapshot.scope === "host");
138  const directory = await activeDirectory(env, await ext.prompt());
139  assertEquals(
140    getOrThrow(await env.readTextFile(`${directory}/references/checklist.txt`, context)),
141    "original checklist",
142  );
143
144  getOrThrow(
145    await host.writeFile(`${source}/references/checklist.txt`, "updated checklist", context),
146  );
147  const next = Object.assign(targetEnvironment(other.path), { name: "other" });
148  const replayed = testExtension(skillsExtension(hostEnvironment(home.path)), {
149    env: next,
150    events: [{ type: "loadHost", skill: snapshot }],
151  });
152  const prompt = await replayed.prompt();
153  assertStringIncludes(prompt, "Read the release checklist.");
154  const historical = await activeDirectory(next, prompt);
155  assertEquals(
156    getOrThrow(await next.readTextFile(`${historical}/references/checklist.txt`, context)),
157    "original checklist",
158  );
159  await replayed.call("load_skill", { name: "release" });
160  const updated = await activeDirectory(next, await replayed.prompt());
161  assertEquals(
162    getOrThrow(await next.readTextFile(`${updated}/references/checklist.txt`, context)),
163    "updated checklist",
164  );
165});
166
167Deno.test("a failed activation does not poison another queued load", async () => {
168  await using root = await tempDir();
169  const env = rootedEnvironment(root.path);
170  await writeSkill(env, "/home/.agents/skills", "valid", "Valid workflow.");
171  const ext = testExtension(skillsExtension(hostEnvironment(root.path)), { env });
172  const results = await Promise.allSettled([
173    ext.call("load_skill", { name: "missing" }),
174    ext.call("load_skill", { name: "valid" }),
175  ]);
176  assertEquals(results.map(({ status }) => status), ["rejected", "fulfilled"]);
177  assertStringIncludes(await ext.prompt(), "Valid workflow.");
178});