char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
a4e772a
main
1import { assertEquals , assertRejects } from "@std/assert" ; 2import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context" ; 3import { FileError , getOrThrow } from "@earendil-works/pi-durable/env" ; 4import { tempDir } from "@berry/util" ; 5import { Bundles , MAX_BUNDLE_BYTES } from "./bundles.ts" ; 6import { hostEnvironment , targetEnvironment , writeSkill } from "./testing.ts" ; 7 8Deno . test ( "host bundles preserve binary assets, empty directories, and resource-only versions on new targets" , async () => { 9await usinghome = await tempDir (); 10await usingtarget = await tempDir (); 11const host = hostEnvironment ( home . path ); 12const main = await writeSkill ( 13host , 14` ${ home . path } /.config/berry/skills` , 15"review" , 16"Read references/checklist.txt." , 17); 18const directory = main . slice ( 0 , - "/SKILL.md" . length ); 19getOrThrow ( 20await host . writeFile ( ` ${ directory } /references/checklist.txt` , "original checklist" , context ), 21); 22getOrThrow ( 23await host . writeFile ( ` ${ directory } /assets/data.bin` , new Uint8Array ([ 0 , 255 , 128 ]), context ), 24); 25getOrThrow ( await host . createDir ( ` ${ directory } /empty` , { recursive :true }, context )); 26await Deno . symlink ( "checklist.txt" , ` ${ directory } /references/alias.txt` ); 27const bundles = new Bundles ( host ); 28const first = await bundles . capture ( directory , context ); 29const env = targetEnvironment ( target . path ); 30const copied = await bundles . materialize ( first . id , env , context ); 31assertEquals ( 32[ ...getOrThrow ( await env . readBinaryFile ( ` ${ copied } /assets/data.bin` , context ))], 33[ 0 , 255 , 128 ], 34); 35assertEquals ( 36getOrThrow ( await env . readTextFile ( ` ${ copied } /references/alias.txt` , context )), 37"original checklist" , 38); 39assertEquals ( getOrThrow ( await env . fileInfo ( ` ${ copied } /empty` , context )). kind , "directory" ); 40 41getOrThrow ( 42await host . writeFile ( ` ${ directory } /references/checklist.txt` , "updated checklist" , context ), 43); 44const second = await bundles . capture ( directory , context ); 45const updated = await bundles . materialize ( second . id , env , context ); 46assertEquals ( first . content , second . content ); 47assertEquals ( 48getOrThrow ( await env . readTextFile ( ` ${ updated } /references/checklist.txt` , context )), 49"updated checklist" , 50); 51getOrThrow ( await host . remove ( directory , { recursive :true }, context )); 52await usingother = await tempDir (); 53const next = targetEnvironment ( other . path ); 54const historical = await new Bundles ( hostEnvironment ( home . path )). materialize ( 55first . id , 56next , 57context , 58); 59assertEquals ( 60getOrThrow ( await next . readTextFile ( ` ${ historical } /references/checklist.txt` , context )), 61"original checklist" , 62); 63}); 64 65Deno . test ( "bundle capture refuses escaping symlinks and cycles rather than copying unrelated host files" , async () => { 66await usinghome = await tempDir (); 67const host = hostEnvironment ( home . path ); 68const main = await writeSkill ( 69host , 70` ${ home . path } /.config/berry/skills` , 71"review" , 72"instructions" , 73); 74const directory = main . slice ( 0 , - "/SKILL.md" . length ); 75await Deno . writeTextFile ( ` ${ home . path } /private.txt` , "not part of the skill" ); 76await Deno . symlink ( ` ${ home . path } /private.txt` , ` ${ directory } /escape.txt` ); 77const bundles = new Bundles ( host ); 78await assertRejects (() => bundles . capture ( directory , context ), Error , "escapes" ); 79await Deno . remove ( ` ${ directory } /escape.txt` ); 80await Deno . symlink ( directory , ` ${ directory } /cycle` ); 81await assertRejects (() => bundles . capture ( directory , context ), Error , "cycle" ); 82}); 83 84Deno . test ( "oversized resource bundles fail before publication" , async () => { 85await usinghome = await tempDir (); 86const host = hostEnvironment ( home . path ); 87const main = await writeSkill ( 88host , 89` ${ home . path } /.config/berry/skills` , 90"review" , 91"instructions" , 92); 93const directory = main . slice ( 0 , - "/SKILL.md" . length ); 94await Deno . writeTextFile ( ` ${ directory } /large.bin` , "" ); 95await Deno . truncate ( ` ${ directory } /large.bin` , MAX_BUNDLE_BYTES + 1 ); 96await assertRejects (() => new Bundles ( host ). capture ( directory , context ), Error , "size limit" ); 97}); 98 99Deno . test ( "an interrupted copy can be retried concurrently without exposing partial bundles" , async () => { 100await usinghome = await tempDir (); 101await usingtarget = await tempDir (); 102const host = hostEnvironment ( home . path ); 103const main = await writeSkill ( 104host , 105` ${ home . path } /.config/berry/skills` , 106"review" , 107"instructions" , 108); 109const directory = main . slice ( 0 , - "/SKILL.md" . length ); 110getOrThrow ( 111await host . writeFile ( ` ${ directory } /references/checklist.txt` , "complete checklist" , context ), 112); 113const bundles = new Bundles ( host ); 114const snapshot = await bundles . capture ( directory , context ); 115const env = targetEnvironment ( target . path ); 116const write = env . writeFile ; 117env . writeFile = ( path , content , context ) => 118path . endsWith ( "/checklist.txt" ) 119 ?Promise . resolve ({ ok :false , error :new FileError ( "unknown" , "Transfer interrupted" ) }) 120 :write ( path , content , context ); 121await assertRejects ( 122() => bundles . materialize ( snapshot . id , env , context ), 123Error , 124"Transfer interrupted" , 125); 126env . writeFile = write ; 127const paths = await Promise . all ([ 128bundles . materialize ( snapshot . id , env , context ), 129bundles . materialize ( snapshot . id , env , context ), 130]); 131assertEquals ( paths [ 0 ], paths [ 1 ]); 132assertEquals ( 133getOrThrow ( await env . readTextFile ( ` ${ paths [ 0 ]} /references/checklist.txt` , context )), 134"complete checklist" , 135); 136assertEquals ( 137getOrThrow ( await env . readTextFile ( ` ${ paths [ 0 ]} /SKILL.md` , context )), 138snapshot . content , 139); 140});