char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
f4dfe65
main
1import { assert , assertEquals , assertMatch , assertStringIncludes } from "@std/assert" ; 2import { Type } from "@earendil-works/pi-ai" ; 3import { renderCatalog } from "./codemode.ts" ; 4import { lastToolResult , script , sectionsSeen , setup , turn } from "./fixture.ts" ; 5 6Deno . test ( "scripts call tools in their own extension's context and see its emits" , async () => { 7await usingharness = await setup (); 8const tid = await harness . berry . create ({ extensions :{ dir :{} }, model :harness . model }); 9const seen = await turn ( harness , tid , [ 10script ( `return [await tools.pwd({}), await tools.cd({ path: "/x" }), await tools.pwd({})]` ), 11]); 12assertEquals ( lastToolResult ( seen [ 1 ]). text , `Script completed\n["/","now in /x","/x"]` ); 13assertEquals ( await harness . berry . state ( tid , "dir" ), { cwd :"/x" }); 14}); 15 16Deno . test ( "the catalog declares only the selected extensions' codemode tools" , async () => { 17await usingharness = await setup (); 18const tid = await harness . berry . create ({ extensions :{ dir :{} }, model :harness . model }); 19const [ seen ] = await turn ( harness , tid , []); 20const catalog = sectionsSeen ( seen ). at ( - 1 )?. codemode ?? "" ; 21assertStringIncludes ( catalog , "print the working directory" ); 22assertStringIncludes ( catalog , "cd: string" ); 23assert ( 24! catalog . includes ( "path:" ), 25"direct tools' arguments are supplied by their native schemas" , 26); 27assertStringIncludes ( catalog , "text: string" ); 28assert ( ! catalog . includes ( "change directory" ), "direct tools are described only once" ); 29assert ( ! catalog . includes ( "shout" ), "model-only tools are not declared to scripts" ); 30assert ( ! catalog . includes ( "hold" ), "unselected extensions are not declared" ); 31}); 32 33Deno . test ( "the catalog retains tools and return types across extensions" , () => { 34const catalog = renderCatalog ([ 35{ 36name :"run" , 37extension :"shell" , 38description :"execute a command" , 39parameters :Type . Object ({ command :Type . String () }), 40returns :Type . Object ({ 41exitCode :Type . Integer (), 42output :Type . String (), 43fullOutputPath :Type . Optional ( Type . String ()), 44}), 45direct :true , 46}, 47{ 48name :"find" , 49extension :"search" , 50description :"search indexed documents" , 51parameters :Type . Object ({ query :Type . String () }), 52returns :Type . Array ( Type . String ()), 53}, 54]) ! ; 55assertStringIncludes ( catalog , "run:" ); 56assertStringIncludes ( catalog , "exitCode: number" ); 57assertStringIncludes ( catalog , "output: string" ); 58assertStringIncludes ( catalog , "fullOutputPath?: string" ); 59assertStringIncludes ( catalog , "find(args:" ); 60assertStringIncludes ( catalog , "search indexed documents" ); 61assertStringIncludes ( catalog , "query: string" ); 62assertStringIncludes ( catalog , "Promise<Array<string>>" ); 63}); 64 65Deno . test ( "tools of unselected extensions are unreachable" , async () => { 66await usingharness = await setup (); 67const tid = await harness . berry . create ({ extensions :{ gate :{} }, model :harness . model }); 68const seen = await turn ( harness , tid , [ script ( `return await tools.pwd({})` )]); 69const result = lastToolResult ( seen [ 1 ]); 70assert ( result . isError ); 71assertMatch ( result . text , / ^Script failed / ); 72}); 73 74Deno . test ( "nested arguments are validated like the model's, before the tool runs" , async () => { 75await usingharness = await setup (); 76const tid = await harness . berry . create ({ extensions :{ dir :{} }, model :harness . model }); 77const seen = await turn ( harness , tid , [ 78script ( `try { await tools.cd({ nowhere: 1 }) } catch (error) { return error.message }` ), 79]); 80assertStringIncludes ( lastToolResult ( seen [ 1 ]). text , "path" ); 81assertEquals ( await harness . berry . state ( tid , "dir" ), { cwd :"/" }); 82}); 83 84Deno . test ( "nested calls are recorded for display, never for the model" , async () => { 85await usingharness = await setup (); 86const tid = await harness . berry . create ({ extensions :{ dir :{} }, model :harness . model }); 87const seen = await turn ( harness , tid , [ 88script ( `await tools.pwd({}); try { await tools.cd({}) } catch {} ; return "ok"` ), 89]); 90const { text, details} = lastToolResult ( seen [ 1 ]); 91assertEquals ( text , "Script completed\nok" ); 92const calls = ( details as { calls :Record < string , unknown >[] }). calls ; 93assertEquals ( 94calls . map (({ name, extension, status, value}) => ({ name, extension, status, value})), 95[ 96{ name :"pwd" , extension :"dir" , status :"ok" , value :"/" }, 97{ name :"cd" , extension :"dir" , status :"error" , value :undefined }, 98], 99); 100assert ( typeof calls [ 1 ]. error === "string" ); 101}); 102 103Deno . test ( "nested running output streams into the call's record" , async () => { 104await usingharness = await setup (); 105const tid = await harness . berry . create ({ extensions :{ dir :{} }, model :harness . model }); 106const seen = await turn ( harness , tid , [ script ( `return await tools.say({ text: "one two" })` )]); 107const { text, details} = lastToolResult ( seen [ 1 ]); 108assertEquals ( text , "Script completed\n7" ); 109assertEquals (( details as { calls :{ output ?:string }[] }). calls [ 0 ]. output , "one\ntwo\n" ); 110}); 111 112Deno . test ( "the store keeps successful scripts' writes across calls" , async () => { 113await usingharness = await setup (); 114const tid = await harness . berry . create ({ extensions :{}, model :harness . model }); 115const seen = await turn ( harness , tid , [ 116script ( `store("n", 1)` ), 117script ( `store("n", load("n") + 1); throw new Error("no")` ), 118script ( `return load("n")` ), 119]); 120assert ( lastToolResult ( seen [ 2 ]). isError ); 121assertEquals ( lastToolResult ( seen [ 3 ]). text , "Script completed\n1" ); 122assertEquals ( await harness . berry . state ( tid , "berry" ), { n :1 }); 123}); 124 125Deno . test ( "failures report the calls already made, which stand" , async () => { 126await usingharness = await setup (); 127const tid = await harness . berry . create ({ extensions :{ dir :{} }, model :harness . model }); 128const seen = await turn ( harness , tid , [ 129script ( `await tools.cd({ path: "/y" }); throw new Error("boom")` ), 130]); 131const { text, isError} = lastToolResult ( seen [ 1 ]); 132assert ( isError ); 133assertStringIncludes ( text , "Error: boom" ); 134assertStringIncludes ( text , "cd (ok)" ); 135assertEquals ( await harness . berry . state ( tid , "dir" ), { cwd :"/y" }); 136}); 137 138Deno . test ( "output over budget keeps its start and end and spills the whole of it" , async () => { 139await usingharness = await setup (); 140const tid = await harness . berry . create ({ extensions :{}, model :harness . model }); 141const seen = await turn ( harness , tid , [ 142script ( `// @options: {"max_output_tokens": 10}\ntext("a".repeat(100) + "b".repeat(100))` ), 143]); 144const { text} = lastToolResult ( seen [ 1 ]); 145assertMatch ( text , / ^Script completed\na{20}…\d+ tokens truncated…b{20} / ); 146const path = text . match ( / \[Full output: (.+)\] / ) ! [ 1 ]; 147assertEquals ( await Deno . readTextFile ( path ), "a" . repeat ( 100 ) + "b" . repeat ( 100 )); 148await Deno . remove ( path ); 149});