char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
e9d146d
main
1import { assert , assertEquals , assertStringIncludes } from "@std/assert" ; 2import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context" ; 3import { fauxAssistantMessage , fauxToolCall } from "@earendil-works/pi-ai/providers/faux" ; 4import { readFile , writeFile } from "node:fs/promises" ; 5import { join } from "node:path" ; 6import diff from "../extensions/diff/mod.ts" ; 7import type { OpenFile , Saved } from "../extensions/diff/edit-types.ts" ; 8import { onPath , tempDir } from "@berry/util" ; 9import { setup } from "./fixture.ts" ; 10 11const IDENTITY = { 12GIT_AUTHOR_NAME :"Test" , 13GIT_AUTHOR_EMAIL :"test@example.com" , 14GIT_COMMITTER_NAME :"Test" , 15GIT_COMMITTER_EMAIL :"test@example.com" , 16GIT_CONFIG_GLOBAL :"/dev/null" , 17}; 18 19async function run ( cwd :string , ...args :string []) { 20const result = await new Deno . Command ( "git" , { 21 args, 22 cwd, 23env :IDENTITY , 24stdout :"piped" , 25stderr :"piped" , 26}). output (); 27if ( ! result . success ) throw new Error ( new TextDecoder (). decode ( result . stderr )); 28} 29 30Deno . test ({ 31name :"a diff save lands while the conversation is busy, and its report follows at the boundary" , 32ignore :! onPath ( "git" ), 33fn :async () => { 34await usingharness = await setup (); 35await usingrootDir = await tempDir ({ prefix :"diff-busy-test-" }); 36const root = rootDir . path ; 37await run ( root , "init" , "--quiet" ); 38await writeFile ( join ( root , "a.txt" ), "base\nshared\nend\n" ); 39await run ( root , "add" , "-A" ); 40await run ( root , "commit" , "--quiet" , "-m" , "first" ); 41await writeFile ( join ( root , "a.txt" ), "agent\nshared\nend\n" ); 42 43const { berry, faux, gate, model} = harness ; 44await berry . install ( diff ); 45const tid = await berry . create ({ 46extensions :{ environment :{ cwd :root }, gate :{}, diff :{} }, 47 model, 48}); 49const conversation = ( await berry . conversation ( tid )) ! ; 50const opened = await berry . request ( tid , "diff" , "open" , { 51 root, 52path :"a.txt" , 53}) as unknown as OpenFile ; 54 55faux . setResponses ([ 56fauxAssistantMessage ( fauxToolCall ( "hold" , {}), { stopReason :"toolUse" }), 57fauxAssistantMessage ( "done" ), 58]); 59const running = await conversation . submit ({ type :"input" , content :"go" }, context ); 60await gate . entered . promise ; 61let finishing :Promise < unknown > | undefined ; 62try { 63// the agent's turn is held open; the save must still write the file and answer 64const saved = await berry . request ( tid , "diff" , "save" , { 65session :"review" , 66id :"edit" , 67working :opened . working , 68path :"a.txt" , 69version :opened . version , 70content :"agent\nhuman\nend\n" , 71}) as unknown as Saved ; 72assertEquals ( await readFile ( join ( root , "a.txt" ), "utf8" ), "agent\nhuman\nend\n" ); 73// reporting it may wait for the boundary, and is not awaited here 74finishing = berry . command ( 75tid , 76"diff-finish" , 77JSON . stringify ({ session :"review" , saves :[ saved ] }), 78); 79assertEquals ( 80( await conversation . context ( context )). messages . some (( message ) => 81typeof message . content === "string" && message . content . includes ( "human edits" ) 82), 83false , 84); 85} finally { 86gate . release . resolve ( "released" ); 87} 88await finishing ; 89await running . wait ( context ); 90await conversation . waitForIdle ( context ); 91const report = ( await conversation . context ( context )). messages . findLast (( message ) => 92typeof message . content === "string" && message . content . includes ( "Historical human edits" ) 93); 94assert ( report ); 95assertStringIncludes ( report . content as string , "+human" ); 96}, 97});