char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
f07649d
main
1import type { Context , JsonValue } from "@earendil-works/chord" ; 2import { 3defineDoc , 4defineEntry , 5defineTask , 6type Harness , 7type TaskRuntime , 8} from "@earendil-works/pi-durable" ; 9import type { ShellExecution , ShellState } from "@berry/api/shell.ts" ; 10 11export const ShellDoc = defineDoc < ShellState >({ 12kind :"berry.shell" , 13version :1 , 14scope :"conversation" , 15history :"latest" , 16fork :"initial" , 17initial :() => ({}), 18checkpointWhen :() => true , 19}); 20export const ShellEntry = defineEntry < ShellExecution >( "berry.shell" ); 21 22type Input = { command :string ; excludeFromContext :boolean }; 23type State = { phase :"run" }; 24type Runtime = TaskRuntime < Input , State , null , object >; 25 26/** Passive shell execution: its result is a write, never an input that starts a model run. */ 27export function shellTask ( 28execute :( 29command :string , 30runtime :Runtime , 31context :Context , 32progress :( details :JsonValue ) => void , 33) => Promise <{ value :JsonValue | undefined ; text :string }>, 34harness :() => Harness , 35) { 36async function finish ( runtime :Runtime , context :Context , run :ShellExecution , text :string ) { 37const conversation = ( await harness (). conversation ( runtime . conversationId , context )) ! ; 38await conversation . submit ({ 39type :"write" , 40requestId :`shell: ${ runtime . taskId } ` , 41entry :{ 42kind :ShellEntry . kind , 43data :run , 44 ...( run . excludeFromContext ?{} :{ 45model :[{ 46role :"user" as const , 47content :`Ran \` ${ run . command } \`\n\n ${ text } ` , 48timestamp :run . startedAt , 49}], 50}), 51}, 52}, context ); 53await runtime . commit ( async ( tx ) => { 54delete ( await tx . doc ( ShellDoc , runtime . conversationId )). run ; 55return { 56status :"terminal" , 57outcome :run . status === "cancelled" 58 ?{ status :"aborted" } 59 :{ status :"completed" , result :null }, 60}; 61}, context ); 62} 63 64return defineTask < Input , State , null >({ 65name :"berry.shell" , 66version :1 , 67initial :() => ({ phase :"run" }), 68phases :{ 69async run ( _ , runtime , context ) { 70const run = structuredClone ( 71( await runtime . snapshot ( ShellDoc , runtime . conversationId , context ))?. run , 72); 73if ( ! run ) throw new Error ( "Shell execution has no live state" ); 74// shell side effects cannot safely be replayed after a restart 75const invocation = crypto . randomUUID (); 76const started = await runtime . memo ( "started" , invocation , context ); 77let pending = Promise . resolve (); 78let text = "" ; 79try { 80if ( started !== invocation ) { 81throw new Error ( "Command interrupted by a restart; not rerun." ); 82} 83const result = await execute ( run . command , runtime , context , ( details ) => { 84run . details = details ; 85pending = pending . then (() => 86runtime . commit ( async ( tx ) => { 87const live = ( await tx . doc ( ShellDoc , runtime . conversationId )). run ; 88if ( live ) live . details = details ; 89}, context ) 90); 91void pending . catch (() => {}); 92}); 93run . result = result . value ?? null ; 94run . status = "ok" ; 95text = result . text ; 96} catch ( error ) { 97if ( runtime . signal . aborted ) throw error ; 98run . status = "error" ; 99run . error = error instanceof Error ?error . message :String ( error ); 100text = ` ${ output ( run )} \n\n ${ run . error } ` ; 101} 102await pending ; 103run . durationMs = runtime . now () - run . startedAt ; 104await finish ( runtime , context , run , text ); 105}, 106}, 107async abort ( _ , runtime , context ) { 108const run = structuredClone ( 109( await runtime . snapshot ( ShellDoc , runtime . conversationId , context ))?. run , 110); 111if ( ! run ) { 112await runtime . commit ( 113() => ({ status :"terminal" , outcome :{ status :"aborted" } }), 114context , 115); 116return ; 117} 118run . status = "cancelled" ; 119run . error = "Command cancelled." ; 120run . durationMs = runtime . now () - run . startedAt ; 121await finish ( runtime , context , run , ` ${ output ( run )} \n\n(command cancelled)` ); 122}, 123}); 124} 125 126function output ( run :ShellExecution ) :string { 127const details = run . details as { rawOutput ?:string } | null ; 128return details ?. rawOutput ?? "(no output)" ; 129}