char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
49a2848
main
1import { assertEquals } from "@std/assert" ; 2import type { SystemMessage , Tool } from "@earendil-works/pi-ai" ; 3import type { ConversationId , EntryId , EntryRecord } from "@earendil-works/pi-durable" ; 4import { withDom } from "@berry/util/dom.ts" ; 5 6const record = ( id :number , patch :Partial < SystemMessage >) :EntryRecord => ({ 7id :id as EntryId , 8conversationId :1 as ConversationId , 9kind :"pi.system" , 10model :[{ role :"system" , content :"" , timestamp :id , ...patch }], 11}); 12 13Deno . test ( "system summaries show net tool changes, not replay churn" , async ( t ) => { 14await withDom ( async () => { 15const { transcript} = await import ( "./transcript.tsx" ); 16const tool = ( name :string , description = name ) :Tool => ({ 17 name, 18 description, 19parameters :{ type :"object" , properties :{} }, 20}); 21const codemode = tool ( "codemode" ); 22const read = tool ( "read" ); 23const write = tool ( "write" ); 24const baseline = record ( 1 , { toolsAdded :[ codemode , read , write ] }); 25const cases :{ name :string ; patch :Partial < SystemMessage >; expected :string }[] = [ 26{ 27name :"unchanged remove/re-add pairs disappear but additions remain" , 28patch :{ 29sections :{ berry :"guide" , codemode :"catalog" }, 30toolsAdded :[ tool ( "subagent" ), codemode , read , write ], 31toolsRemoved :[ codemode , read , write ], 32}, 33expected :"prompt sections +berry +codemode tools +subagent" , 34}, 35{ 36name :"a changed declaration is shown once" , 37patch :{ 38toolsAdded :[ tool ( "codemode" , "updated" ), read , write ], 39toolsRemoved :[ codemode , read , write ], 40}, 41expected :"prompt tools +codemode" , 42}, 43{ 44name :"real removals remain visible" , 45patch :{ 46toolsAdded :[ codemode , read ], 47toolsRemoved :[ codemode , read , write ], 48}, 49expected :"prompt tools -write" , 50}, 51{ 52name :"reordering alone is not a tool interface change" , 53patch :{ 54sections :{ berry :"guide" }, 55toolsAdded :[ write , read , codemode ], 56toolsRemoved :[ codemode , read , write ], 57}, 58expected :"prompt sections +berry" , 59}, 60]; 61for ( const { name, patch, expected} of cases ) { 62await t . step ( name , () => { 63const view = transcript (); 64try { 65view . update ([ baseline ], {}); 66view . update ([ baseline , record ( 2 , patch )], {}); 67const summaries = view . element . querySelectorAll ( ".system summary" ); 68assertEquals ( summaries [ 0 ]. textContent , "prompt tools +codemode +read +write" ); 69assertEquals ( summaries [ 1 ]. textContent , expected ); 70} finally { 71view . dispose (); 72} 73}); 74} 75await t . step ( "summaries are recalculated when earlier context changes" , () => { 76const view = transcript (); 77try { 78const updatedTools = [ tool ( "codemode" , "updated" ), read , write ]; 79const update = record ( 2 , { 80toolsAdded :updatedTools , 81toolsRemoved :[ codemode , read , write ], 82}); 83view . update ([ baseline , update ], {}); 84const summary = view . element . querySelectorAll ( ".system summary" )[ 1 ]; 85assertEquals ( summary . textContent , "prompt tools +codemode" ); 86view . update ([ 87record ( 3 , { toolsAdded :updatedTools }), 88update , 89], {}); 90assertEquals ( view . element . querySelectorAll ( ".system summary" )[ 1 ], summary ); 91assertEquals ( summary . textContent , "prompt" ); 92} finally { 93view . dispose (); 94} 95}); 96}); 97}); 98 99Deno . test ( "section summaries compare the net effect of a reorder's patches" , async ( t ) => { 100await withDom ( async () => { 101const { transcript} = await import ( "./transcript.tsx" ); 102const sections = { berry :"guide" , code :"coding" , web :"browsing" }; 103const baseline = record ( 1 , { sections}); 104const removal = record ( 2 , { sections :{ berry :null , code :null , web :null } }); 105const cases :{ name :string ; sections :Record < string , string >; expected :string [] }[] = [ 106{ 107name :"inserting a section does not report unchanged sections" , 108sections :{ berry :"guide" , code :"coding" , code_project :"project" , web :"browsing" }, 109expected :[ "+code_project" ], 110}, 111{ 112name :"changed values and real removals survive replay churn" , 113sections :{ code :"updated coding" , berry :"guide" }, 114expected :[ "code" , "-web" ], 115}, 116{ 117name :"reordering alone does not report content changes" , 118sections :{ web :"browsing" , code :"coding" , berry :"guide" }, 119expected :[], 120}, 121]; 122for ( const { name, sections, expected} of cases ) { 123await t . step ( name , () => { 124const view = transcript (); 125try { 126view . update ([ baseline , removal , record ( 3 , { timestamp :2 , sections})], {}); 127const summaries = view . element . querySelectorAll ( ".system summary" ); 128assertEquals ( summaries . length , 2 ); 129assertEquals ( 130Array . from ( summaries [ 1 ]. querySelectorAll ( ".system-name" ), ( name ) => name . textContent ), 131expected , 132); 133} finally { 134view . dispose (); 135} 136}); 137} 138await t . step ( "split delivery and rewinds update the same summary and its expanded body" , () => { 139const view = transcript (); 140try { 141const addition = record ( 3 , { 142timestamp :2 , 143sections :{ ...sections , code_project :"project" }, 144}); 145view . update ([ baseline , removal ], {}); 146const details = view . element . querySelectorAll < HTMLDetailsElement >( ".system" )[ 1 ]; 147const summary = details . querySelector ( "summary" ) ! ; 148details . open = true ; 149details . dispatchEvent ( new Event ( "toggle" )); 150view . update ([ baseline , removal , addition ], {}); 151assertEquals ( view . element . querySelectorAll ( ".system" ). length , 2 ); 152assertEquals ( details . querySelector ( "summary" ), summary ); 153assertEquals ( summary . textContent , "prompt sections +code_project" ); 154assertEquals ( Array . from ( details . querySelectorAll ( "pre" ), ( pre ) => pre . textContent ), [ 155"guide" , 156"coding" , 157"browsing" , 158"project" , 159]); 160assertEquals ( details . open , true ); 161 162view . update ([ baseline , removal ], {}); 163assertEquals ( summary . textContent , "prompt sections -berry -code -web" ); 164assertEquals ( details . querySelectorAll ( "pre" ). length , 0 ); 165 166view . update ([ 167record ( 4 , { sections :{ ...sections , code_project :"project" } }), 168removal , 169addition , 170], {}); 171assertEquals ( details . querySelector ( "summary" ), summary ); 172assertEquals ( summary . textContent , "prompt" ); 173} finally { 174view . dispose (); 175} 176}); 177await t . step ( "an intervening message prevents unrelated changes from cancelling out" , () => { 178const view = transcript (); 179try { 180const user :EntryRecord = { 181id :3 as EntryId , 182conversationId :1 as ConversationId , 183kind :"pi.user" , 184model :[{ role :"user" , content :"continue" , timestamp :2 }], 185}; 186view . update ([ baseline , removal , user , record ( 4 , { timestamp :2 , sections})], {}); 187assertEquals ( 188Array . from ( view . element . querySelectorAll ( ".system summary" ), ( node ) => node . textContent ), 189[ 190"prompt sections +berry +code +web" , 191"prompt sections -berry -code -web" , 192"prompt sections +berry +code +web" , 193], 194); 195} finally { 196view . dispose (); 197} 198}); 199}); 200});