char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
049fbda
main
1import { assertEquals , assertRejects } from "@std/assert" ; 2import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context" ; 3import { type Message , Type } from "@earendil-works/pi-ai" ; 4import { fauxAssistantMessage , fauxToolCall } from "@earendil-works/pi-ai/providers/faux" ; 5import { defineExtension , type JsonValue } from "@berry/api" ; 6import { BerryError } from "./berry.ts" ; 7import { sectionsSeen , setup } from "./fixture.ts" ; 8 9const configurable = defineExtension < 10{ cwd :string ; verbose :boolean ; edits :number }, 11{ cwd :string ; verbose :boolean } 12>({ 13name :"configurable" , 14initial :() => ({ cwd :"/" , verbose :false , edits :0 }), 15apply :( state , event ) => ({ ...state , ...event , edits :state . edits + 1 }), 16setup ( ext ) { 17ext . configuration ({ 18parameters :Type . Object ({ 19cwd :Type . String ({ default :"/" }), 20verbose :Type . Boolean ({ default :false }), 21}), 22values :({ cwd, verbose}) => ({ cwd, verbose}), 23events :({ cwd, verbose}) => { 24if ( ! cwd . startsWith ( "/" )) throw new Error ( `not absolute: ${ cwd } ` ); 25return [{ cwd, verbose}]; 26}, 27}); 28return ( conv ) => conv . prompt (() => `cwd: ${ conv . state . cwd } ` ); 29}, 30}); 31 32Deno . test ( "configuration seeds new conversations and exposes only editable state" , async () => { 33await usingharness = await setup (); 34const { berry} = harness ; 35await berry . install ( configurable ); 36const tid = await berry . create ({ extensions :{ configurable :{ cwd :"/seed" } } }); 37const feed = ( await berry . watchConfiguration ( tid , "configurable" )) ! ; 38assertEquals ( feed . value , { cwd :"/seed" , verbose :false }); 39await feed . stop (); 40const defaults = await berry . create ({ extensions :{ configurable :{} } }); 41assertEquals ( await berry . state ( defaults , "configurable" ), { 42cwd :"/" , 43verbose :false , 44edits :1 , 45}); 46await assertRejects ( 47() => berry . create ({ extensions :{ configurable :{ cwd :5 } } }), 48BerryError , 49"cwd" , 50); 51await assertRejects ( 52() => berry . create ({ extensions :{ configurable :{ cwd :"relative" } } }), 53BerryError , 54"not absolute" , 55); 56}); 57 58Deno . test ( "configuration patches preserve other fields, validate before writing, and replay from events" , async () => { 59await usingharness = await setup (); 60const { berry} = harness ; 61await berry . install ( configurable ); 62const tid = await berry . create ({ extensions :{ configurable :{ cwd :"/seed" } } }); 63assertEquals ( await berry . configureExtension ( tid , "configurable" , { verbose :true }), { 64cwd :"/seed" , 65verbose :true , 66}); 67const invalid :Record < string , JsonValue >[] = [{ cwd :5 }, { cwd :"relative" }, { edits :999 }]; 68for ( const change of invalid ) { 69await assertRejects (() => berry . configureExtension ( tid , "configurable" , change ), BerryError ); 70} 71await berry . install ( configurable ); 72assertEquals ( await berry . state ( tid , "configurable" ), { cwd :"/seed" , verbose :true , edits :2 }); 73await berry . configure ( tid , { extensions :[] }); 74await assertRejects ( 75() => berry . configureExtension ( tid , "configurable" , { cwd :"/disabled" }), 76BerryError , 77"no enabled configuration" , 78); 79await assertRejects (() => berry . configureExtension ( tid , "missing" , {}), BerryError ); 80}); 81 82Deno . test ( "a configuration edit lands at once while busy and reaches the turn's next request" , async () => { 83await usingharness = await setup (); 84const { berry, faux, model, gate} = harness ; 85try { 86await berry . install ( configurable ); 87const tid = await berry . create ({ extensions :{ configurable :{}, gate :{} }, model}); 88let next :readonly Message [] = []; 89faux . setResponses ([ 90fauxAssistantMessage ( fauxToolCall ( "hold" , {}), { stopReason :"toolUse" }), 91( ctx ) => ( next = ctx . messages , fauxAssistantMessage ( "done" )), 92]); 93const conversation = ( await berry . conversation ( tid )) ! ; 94const submission = await conversation . submit ({ type :"input" , content :"go" }, context ); 95await gate . entered . promise ; 96assertEquals ( await berry . configureExtension ( tid , "configurable" , { cwd :"/mid" }), { 97cwd :"/mid" , 98verbose :false , 99}); 100assertEquals ( await berry . state ( tid , "configurable" ), { cwd :"/mid" , verbose :false , edits :2 }); 101gate . release . resolve ( "released" ); 102await submission . wait ( context ); 103await conversation . waitForIdle ( context ); 104assertEquals ( 105sectionsSeen ( next ). at ( - 1 )?. configurable , 106"<configurable>\ncwd: /mid\n</configurable>" , 107); 108} finally { 109gate . release . resolve ( "released" ); 110} 111});