char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
f07649d
main
1import { BACKGROUND_CONTEXT as context } from "@earendil-works/chord/context" ; 2import { 3type ConversationId , 4defineDoc , 5GenerationTask , 6type Harness , 7InboxDoc , 8LiveDoc , 9} from "@earendil-works/pi-durable" ; 10 11/** The last time the conversation handed control back to the user. */ 12export const SettledDoc = defineDoc <{ updated ?:number }>({ 13kind :"berry.settled" , 14version :1 , 15scope :"conversation" , 16history :"rewindable" , 17fork :"asOf" , 18initial :() => ({}), 19checkpointWhen :() => true , 20}); 21 22const pending = new WeakMap < Harness , Promise < void >>(); 23 24/** Record when a run yields control instead of handing it to another generation. */ 25export function recordSettled ( harness :Harness ) :() => Promise < void > { 26const queued = new Map < ConversationId , number >(); 27const unsubscribe = harness . subscribeCommits (( publication ) => { 28for ( const change of publication . changes ) { 29if ( 30change . type === "document" && change . record . kind === InboxDoc . definition . kind && 31change . conversationId !== undefined && Array . isArray ( change . value ?. items ) 32) queued . set ( change . conversationId , change . value . items . length ); 33} 34for ( const change of publication . changes ) { 35if ( 36change . type !== "task" || change . value . kind !== GenerationTask . definition . name || 37change . value . state . status !== "terminal" 38) continue ; 39const task = change . value ; 40const live = publication . changes . find (( change ) => 41change . type === "document" && change . record . kind === LiveDoc . definition . kind && 42change . conversationId === task . conversationId 43); 44if ( live ?. type !== "document" || live . value === null || live . value . run !== undefined ) { 45continue ; 46} 47const count = queued . get ( task . conversationId ); 48const next = ( pending . get ( harness ) ?? Promise . resolve ()). then ( async () => { 49const remaining = count ?? 50( await harness . snapshot ( InboxDoc , task . conversationId , context ))?. items . length ?? 0 ; 51if ( remaining > 0 ) return ; 52await harness . commit ( async ( tx ) => { 53( await tx . doc ( SettledDoc , task . conversationId )). updated = task . endedAt ?? Date . now (); 54}, context ); 55}). catch (( error ) => console . error ( "[berry] recording settled turn:" , error )); 56pending . set ( harness , next ); 57} 58}); 59return async () => { 60unsubscribe (); 61await settledThrough ( harness ); 62}; 63} 64 65/** Wait for the timestamps queued by committed run endings. */ 66export async function settledThrough ( harness :Harness ) :Promise < void > { 67let next ; 68do { 69next = pending . get ( harness ); 70await next ; 71} while ( next !== pending . get ( harness )); 72}