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