char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
70e18bf
main
1import { assert , assertEquals , assertObjectMatch , assertStringIncludes } from "@std/assert" ; 2import { Type } from "@earendil-works/pi-ai" ; 3import { getCurrentTools } from "@earendil-works/pi-ai/utils/transcript" ; 4import { fauxAssistantMessage , fauxToolCall } from "@earendil-works/pi-ai/providers/faux" ; 5import { defineExtension } from "@berry/api" ; 6import { lastToolResult , script , sectionsSeen , setup , toolsOffered , turn } from "./fixture.ts" ; 7 8Deno . test ( "conversation tools follow configuration in both catalogs and validate the current schema" , async () => { 9await usingh = await setup (); 10let calls = 0 ; 11await h . berry . install ( defineExtension < boolean , { enabled :boolean }>({ 12name :"dynamic" , 13initial :() => false , 14apply :( _ , event ) => event . enabled , 15setup ( ext ) { 16ext . configuration ({ 17parameters :Type . Object ({ enabled :Type . Boolean ({ default :false }) }), 18values :( enabled ) => ({ enabled}), 19events :({ enabled}) => [{ enabled}], 20}); 21return ( conv ) => { 22conv . tool ({ 23name :"answer" , 24description :conv . state ?"configured answer" :"initial answer" , 25parameters :Type . Object ({ value :conv . state ?Type . Number () :Type . String () }), 26returns :Type . String (), 27expose :"both" , 28execute :({ value}) => { 29calls ++ ; 30return String ( value ); 31}, 32text :( value ) => `answer: ${ value } ` , 33}); 34if ( conv . state ) { 35conv . tool ({ 36name :"private" , 37description :"direct only" , 38parameters :Type . Object ({}), 39expose :"model" , 40execute :() => "private answer" , 41}); 42} 43}; 44}, 45})); 46const tid = await h . berry . create ({ extensions :{ dynamic :{} }, model :h . model }); 47const other = await h . berry . create ({ extensions :{ dynamic :{} }, model :h . model }); 48const direct = ( value :string | number ) => 49fauxAssistantMessage ( fauxToolCall ( "answer" , { value}), { stopReason :"toolUse" }); 50const initial = await turn ( h , tid , [ direct ( "hello" )]); 51assert ( toolsOffered ( initial [ 0 ]). includes ( "answer" )); 52assertStringIncludes ( lastToolResult ( initial [ 1 ]). text , "answer: hello" ); 53 54await h . berry . configureExtension ( tid , "dynamic" , { enabled :true }); 55const configured = await turn ( h , tid , [ 56direct ( "stale" ), 57direct ( 42 ), 58script ( "return await tools.answer({ value: 43 })" ), 59script ( "return await tools.private({})" ), 60]); 61assert ( lastToolResult ( configured [ 1 ]). isError ); 62assertStringIncludes ( lastToolResult ( configured [ 2 ]). text , "answer: 42" ); 63assertStringIncludes ( lastToolResult ( configured [ 3 ]). text , "43" ); 64assert ( lastToolResult ( configured [ 4 ]). isError ); 65assert ( toolsOffered ( configured [ 0 ]). includes ( "private" )); 66const catalog = 67sectionsSeen ( configured [ 0 ]). findLast (( section ) => section . codemode !== undefined )?. codemode ?? 68"" ; 69assertStringIncludes ( catalog , "answer: string" ); 70assert ( ! catalog . includes ( "value:" )); 71assert ( ! catalog . includes ( "private" )); 72const offered = getCurrentTools ( configured [ 0 ]); 73const answer = offered . find (( tool ) => tool . name === "answer" ) ! ; 74assertEquals ( answer . description , "configured answer" ); 75assertObjectMatch ( answer . parameters , { properties :{ value :{ type :"number" } } }); 76assertEquals ( calls , 3 ); 77 78const unchanged = await turn ( h , other , [ direct ( "still a string" )]); 79assert ( ! toolsOffered ( unchanged [ 0 ]). includes ( "private" )); 80assertStringIncludes ( lastToolResult ( unchanged [ 1 ]). text , "still a string" ); 81await h . berry . configureExtension ( tid , "dynamic" , { enabled :false }); 82const removed = await turn ( h , tid , [ 83fauxAssistantMessage ( fauxToolCall ( "private" , {}), { stopReason :"toolUse" }), 84]); 85assert ( ! toolsOffered ( removed [ 0 ]). includes ( "private" )); 86assert ( lastToolResult ( removed [ 1 ]). isError ); 87}); 88 89Deno . test ( "conversation registrations replace static tools and emitted state changes the next request" , async () => { 90await usingh = await setup (); 91await h . berry . install ( defineExtension < boolean , boolean >({ 92name :"replacement" , 93initial :() => false , 94apply :( _ , event ) => event , 95setup ( ext ) { 96ext . tool ({ 97name :"replace" , 98description :"static" , 99parameters :Type . Object ({}), 100expose :"both" , 101execute :() => "must not execute" , 102}); 103return ( conv ) => 104conv . tool ({ 105name :"replace" , 106description :"dynamic" , 107parameters :Type . Object ({}), 108expose :conv . state ?"codemode" :"both" , 109async execute ( _ , ctx ) { 110await ctx . emit ( true ); 111return "replacement" ; 112}, 113}); 114}, 115})); 116const tid = await h . berry . create ({ extensions :{ replacement :{} }, model :h . model }); 117const direct = fauxAssistantMessage ( fauxToolCall ( "replace" , {}), { stopReason :"toolUse" }); 118const seen = await turn ( h , tid , [ direct , direct , script ( "return await tools.replace({})" )]); 119assertStringIncludes ( lastToolResult ( seen [ 1 ]). text , "replacement" ); 120assert ( ! toolsOffered ( seen [ 1 ]). includes ( "replace" )); 121assert ( lastToolResult ( seen [ 2 ]). isError ); 122assertStringIncludes ( lastToolResult ( seen [ 3 ]). text , "replacement" ); 123});