char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
481a657
main
1// What an extension's `web.tsx` exports: how the ui shows its tools and its events. 2 3import type { Channel , JsonValue } from "./mod.ts" ; 4 5export type { Channel , ChannelMessage } from "./mod.ts" ; 6import type { ImageContent } from "@earendil-works/pi-ai" ; 7 8/** Highlighted source, as the rest of the ui shows it, or its lines alone. */ 9export { code , type CodeOptions , highlight } from "../web/code.tsx" ; 10 11export type CallStatus = "streaming" | "pending" | "running" | "ok" | "error" | "cancelled" ; 12 13/** One tool call as the ui knows it, whether the model made it or a script did. Absent parts are `null`. */ 14export type ToolCall = { 15readonly name :string ; 16/** Partial while the model is still writing it. */ 17readonly args :JsonValue | null ; 18readonly status :CallStatus ; 19/** Running output so far. */ 20readonly output :string | null ; 21readonly details :JsonValue | null ; 22/** What the tool returned: the value for a script's call, the result text for the model's. */ 23readonly result :JsonValue | null ; 24/** Image output, separate from the result text or value. */ 25readonly images ?:readonly ImageContent []; 26readonly error :string | null ; 27/** When it started running, in wall-clock milliseconds, as far as the ui knows. */ 28readonly startedAt :number | null ; 29/** How long it ran, once it has ended and that is known. */ 30readonly durationMs :number | null ; 31}; 32 33/** A card's body: `update` draws each new state of the call, the first before the body is shown. */ 34export type ToolView = { 35readonly element :Element ; 36update ( call :ToolCall ) :void ; 37/** Once the card is gone. */ 38dispose ?() :void ; 39}; 40 41export type ToolRenderer = { 42/** How the card first shows its body: whole, clipped at a height (the default), or not at all. */ 43readonly initial ?:"open" | "preview" | "closed" ; 44/** A line for the card's header, such as the path an edit touches. */ 45readonly summary ?:( args :JsonValue | null ) => string | null ; 46/** The timeout in seconds, shown alongside the call's elapsed time. */ 47readonly timeout ?:( args :JsonValue | null ) => number | null ; 48/** What a call that succeeded came to, when that is a failure all the same, such as `exit 3`; shown in place of `ok`. */ 49readonly outcome ?:( call :ToolCall ) => string | null ; 50/** The card's expanded body. */ 51readonly body ?:() => ToolView ; 52}; 53 54/** A header widget's current conversation, its extension's handlers, and user-command access. */ 55export type HeaderContext = { 56readonly tid :string ; 57readonly model :{ readonly provider :string ; readonly modelId :string } | null ; 58/** Ask one of this extension's handlers, in this conversation. */ 59request ( name :string , args ?:JsonValue , options ?:{ signal ?:AbortSignal }) :Promise < JsonValue >; 60/** Open a channel to this extension, in this conversation. */ 61connect ( name :string , args ?:JsonValue ) :Channel ; 62/** Run a command as the user would. */ 63command ( name :string , args ?:string ) :Promise < JsonValue | null >; 64}; 65 66/** A header widget, updated with the selected model and disposed with its conversation view. */ 67export type HeaderView = { 68readonly element :Element ; 69update ( context :HeaderContext ) :void ; 70dispose ?() :void ; 71}; 72 73export type FieldSchema = { 74type ?:string ; 75title ?:string ; 76description ?:string ; 77default ?:JsonValue ; 78enum ?:JsonValue []; 79anyOf ?:{ const ?:JsonValue }[]; 80items ?:FieldSchema ; 81}; 82export type ConfigurationSchema = { properties :Record < string , FieldSchema > }; 83 84/** The configuration being edited, either in a conversation or before one is created. */ 85export type ConfigurationContext = { 86readonly tid :string | null ; 87readonly extension :string ; 88readonly model :HeaderContext [ "model" ]; 89readonly schema :ConfigurationSchema ; 90/** Null until the conversation's configuration has loaded. */ 91readonly values :Readonly < Record < string , JsonValue >> | null ; 92readonly saveStatus :"idle" | "saving" | "error" ; 93readonly error :string | null ; 94save ( patch :Record < string , JsonValue >) :Promise < void >; 95/** The same generated form on every call, with its saving and validation behavior intact. */ 96renderDefault () :HTMLElement ; 97/** These capabilities are absent before conversation creation. */ 98readonly request ?:HeaderContext [ "request" ]; 99readonly command ?:HeaderContext [ "command" ]; 100}; 101 102/** Configuration controls, updated with their context and disposed when replaced or unmounted. */ 103export type ConfigurationView = { 104readonly element :HTMLElement ; 105update ( context :ConfigurationContext ) :void ; 106dispose ?() :void ; 107}; 108 109/** What a view is shown with: its conversation, its own place in the address, and user-command access. */ 110export type ViewContext = HeaderContext & { 111/** The view's place, after `#/<tid>/<extension>/` in the address: empty when it opens. */ 112readonly route :string ; 113/** The address of another place in the view, for links to it. */ 114href ( route :string ) :string ; 115/** Move to another place in the view, as a link would, so the browser's back button returns. */ 116go ( route :string ) :void ; 117/** The conversation's latest entry: a new one, such as a tool's result, may change what the view shows. */ 118readonly latest :number | null ; 119}; 120 121export type ViewDestination = { tid :string | null ; extension :string | null ; route :string }; 122 123/** A view that takes the conversation's main pane while it is open, the transcript moving aside. */ 124export type View = { 125readonly element :Element ; 126/** Each new context, the first before the view is shown. */ 127update ( context :ViewContext ) :void ; 128/** Resolve drafts and report changes before navigation; false keeps the view open. */ 129beforeLeave ?( destination :ViewDestination ) :boolean | Promise < boolean >; 130/** Report held context before an idle message sends, so the agent sees it first. */ 131flush ?() :Promise < void >; 132dispose ?() :void ; 133}; 134 135export type Renderers < E extends JsonValue = JsonValue > = { 136/** Mounted in the top right while this extension is selected or always on. */ 137readonly header ?:() => HeaderView ; 138/** Replaces the generated configuration controls in both new and existing conversations. */ 139readonly configuration ?:() => ConfigurationView ; 140/** 141* Opened from the conversation's bar, which links it by `label`, at `#/<tid>/<extension>`, while this extension is 142* selected or always on. 143*/ 144readonly view ?:{ readonly label :string ; readonly create :() => View }; 145readonly tools ?:Readonly < Record < string , ToolRenderer >>; 146/** One of the extension's events in the transcript: `null` hides it, `undefined` falls back to the default. */ 147readonly event ?:( event :E ) => Element | null | undefined ; 148}; 149 150export function defineRenderers < E extends JsonValue = JsonValue > ( 151renderers :Renderers < E >, 152) :Renderers < E > { 153return renderers ; 154}