What changed, release by release.
Every release's mods API against the one before it: which nouns, verbs, events and types it added, which it removed, and which changed shape, with the lines that moved. A removal is marked breaking; a change that took something out may break a mod; one that only added to a shape is marked extended.
v2.1.291
No change to the surface since 2.1.290v2.1.290
+8 added · 0 removed · 11 changed · 29 docs only since 2.1.289Events
-
Fires once per path a prompt names after an
@, a directory aside, before the engine reads it;next(e)reads it and resolves{ type }. -
{ type, context? }(null when nothing was attached) or{ deny }.
Types
-
What an Agent call that started a teammate answers as
resultattool.call: the started teammate's two ids, its name and its model.export type AgentTeammateRecord = { status: 'teammate_spawned'; agentId?: string; agent_id: string; teammate_id: string; agent_id: string; name: string; team_name?: string; agent_type?: string; resolvedModel?: string; model?: string; prompt: string;}; -
The
Boxprops ahovermay override, none of which moves the Box's siblings, andscope, which names the hover group the Box joins.export type BoxHoverProps = { scope?: string; borderStyle?: string; borderColor?: string; borderColor?: Color; borderDimColor?: boolean; backgroundColor?: string; backgroundColor?: Color; display?: 'flex'; top?: number; left?: number; -
The props of
Box: the layout, position, margin, padding and border props of Ink's Box a tree may set, and the two of hover.paddingLeft?: number; paddingRight?: number; borderStyle?: string; borderColor?: string; borderColor?: Color; borderDimColor?: boolean; backgroundColor?: string; backgroundColor?: Color; overflow?: 'visible' | 'hidden'; display?: 'flex' | 'none';}; -
The engine's own events as calls on
$, one signature each:$.<noun>.<event>(input)resolves to its result, or to its stream.section: (input: PromptSectionInput) => Promise<PromptSectionResult>; context: (input: PromptContextInput) => Promise<PromptContextResult>; attachment: (input: PromptAttachmentInput) => Promise<PromptAttachmentResult>; mention: (input: PromptMentionInput) => Promise<PromptMentionResult>; compose: (input?: PromptComposeArgs) => Promise<PromptComposeResult>; }; skill: { -
Twith every property read-only to every depth, arrays and tuples kept as declared: how a hook'seis typed.export type Frozen<T> = T extends (...args: never[]) => unknown ? T : T extends readonly unknown[] ? {export type Frozen<T> = T extends string | ((...args: never[]) => unknown) ? T : T extends readonly unknown[] ? { [K in keyof T]: Frozen<T[K]>;} : T extends object ? { readonly [K in keyof T]: Frozen<T[K]>; -
The plain-data props of each renderable component, as
ui.rendersees them undere.props; a hook rewrites them withnext({ ...e, props }).AssistantMessage: { text: string; isFirstOfReply: boolean; isSummary?: true; onScreen?: OnScreen | null; }; ToolUse: { -
The
Textprops ahovermay override (its colors and styles, not its wrapping) andscope, the hover group it joins; aButton's label too.export type TextHoverProps = { scope?: string; color?: string; backgroundColor?: string; color?: Color; backgroundColor?: Color; dimColor?: boolean; bold?: boolean; italic?: boolean; -
The props of
Text: the color and style props of Ink's Text a tree may set. Colors are a theme key or a raw color.export type TextProps = { hover?: TextHoverProps; color?: string; backgroundColor?: string; color?: Color; backgroundColor?: Color; dimColor?: boolean; bold?: boolean; italic?: boolean; -
The input of
tool.check: the tool, its arguments, the organization's ceiling if it set one, and on a real call its id and the loop'sagentId.tool: string; input: unknown; tool_use_id?: string; agentId?: string; ceiling?: ToolCheckDecision;}; -
What a
tool.checkhook returns and whatnext(e)resolves to: the verdict, why, and the rule, classic hook or ceiling behind it, if any.reason?: string; rule?: string; hook?: string; ceiling?: ToolCheckDecision;}; -
What a
turn.stephook returns and whatnext(e)resolves to: the model's response to the step's request, once its blocks are all in.index: number; answer: string; toolUses: readonly TurnStepToolUse[]; serverToolUses?: readonly TurnStepServerToolUse[]; stopReason: TurnStopReason; usage: TurnUsage | null;}; -
What a prop that paints may hold: a theme key, which follows the person's theme, or any other string, a raw color (a name such as
"red", or hex). -
How the engine attached a mentioned file, in the names
prompt.attachmentgives itstype. -
The input of
prompt.mention: one file a prompt names after an@, as the engine resolved it and before it is read for the model. -
What a
prompt.mentionhook returns and whatnext(e)resolves to: how the file was attached ({ type, context? }), or{ deny: reason }. -
The theme keys named for what a color means: text, status, the prompt box and its modes, and diffs, as the custom theme reference lists them.
-
One tool call of a step that the API ran itself, inside the request (the advisor is one): the engine ran nothing for it.
29 docs only: reworded, same shape
$.tool.checkverbAsks the engine's permission decision for a tool call now: the event
tool.check, resolved to{ decision, reason?, rule?, ceiling? }.$.ui.logverbAppends one line to the transcript, drawn like a system notice (dim; not sent to the model), or with
{ to: "debug" }to the debug log alone.$.ui.statusverbPins
textas this plugin's status line under the prompt, beside the engine's own pinned notices, until the next call replaces it.$.ui.toastverbShows
textfor a few seconds under the plugin's name: a small box on the stack of plugin toasts over the transcript's top right corner.engine.createeventRuns while
$is being built, once per load or reload of this plugin and before any other hook of it;next(e)resolves to$built so far.process.spawneventThe argument of
$.process.spawn(request): the request itself.prompt.submiteventFires when a prompt is submitted, before the turn starts.
next(e)runs the hooks beneath and the UserPromptSubmit settings hooks, then it enters.tool.calleventFires when the engine is about to run a tool. Each
next(e)runs the hooks beneath, then core (the permission prompt, the tool itself).tool.checkeventFires when the engine decides whether a tool call may run, after the
tool.calland PreToolUse hooks and before the mode settles an ask.tool.checkresult{ decision, reason?, rule?, hook?, ceiling? }.turn.stepresultThe response:
{ turnId, index, answer, toolUses, stopReason, usage }, andserverToolUseswhen the API ran tools of its own in the request.AgentInfotypeOne agent loop of this session as
$.agent.list()returns it: a subagent or a teammate.ClientPropstypeThe props of
Client: which of the plugin's surface modules draws here, under what key, with what data, in how much room.CodePropstypeThe props of
Code, source text every surface draws with the engine's own highlighter: coloured tokens, a line gutter on request, or a unified diff.FsAncestorsRequesttypeThe argument of
$.fs.ancestors: the file names to look for in each directory, the file to walk down to, and the directory to walk beneath.HookFailuretypeWhy a hook failed, as its
.catchhandler reads it onnext.error: plain frozen data.MarkdownLeafPropstypeWhat a
Markdowncarries across the boundary: its address, text, dimness and which links it answers;onLinkPressstays behind, apressinstead.MarkdownPropstypeThe props of
Markdown, a block of markdown every surface draws as it draws an assistant reply's text: its own renderer, links, tables, fences.-
Who authored the text an injected attachment carries, as the engine knows it from the attachment itself; a closed set, pinned on the event.
PromptSubmitResulttypeWhat a
prompt.submithook returns and whatnext(e)resolves to: the prompt that entered,{ text, context?, origin? }, or{ drop: reason }.RegistrationtypeWhat
on(...)returns for a hook of typeF: the registration, which takes one.catch(CatchHandler); without it a failed hook is absent.RenderElementtypeWhat a render hook returns, and what
next(e)resolves to: a plain-data tree of elements (a Box or Text is a StyledElement), strings as children.SiteScrolltypeWhere a site's window sits over the tree a hook drew in it (a pane's body, the band): the engine's to move (the person scrolls), the plugin's to read.
ToolCallResulttypeWhat a
tool.callhook returns and whatnext(e)and$.tool.call(input)resolve to: the tool's result ({ result, context? }) or{ deny }.ToolCheckArgstypetool.check's input as$.tool.checktakes it: the tool and its arguments; the call's id,agentIdand the ceiling are the engine's to set.TurnCompleteFieldstypeWhat every
turn.completecarries whatever its reason: the answer, the duration, the interrupt flag, the turn's id, its loop and what it cost.TurnStepEngineChunktypeAn item of the engine's stream the other chunk kinds do not model (the envelope, a block's start and end, a retry marker), opaque by
ref.UiMessageArgumenttypeThe argument of
ui.message: what aClientinstance's surface module posted (surface.post(data)), addressed by where the instance is drawn.UiScrollPointertypeThe cell the pointer was over when the person's wheel raised
ui.scroll, in the site's body: the box itsui.renderhook draws into, as painted.
v2.1.289
+7 added · 0 removed · 5 changed · 10 docs only since 2.1.288Events
-
Fires when a
ClientTHIS plugin drew failed on a surface: its module did not load, its drawing failed, or its code failed after it had drawn. -
{}: the fault was heard.
Types
-
One agent loop of this session as
$.agent.list()returns it: a subagent or a teammate.export type AgentInfo = { id: string; teammateId?: string; description: string; type: string; status: string; status: AgentStatus; parentId?: string; spawnedBy?: string; name?: string; -
The input of
agent.spawn: what the Agent tool decided about the agent it is about to start, a teammate included, before its model is resolved.permissionMode?: string; background: boolean; fork: boolean; isTeammate?: true; name?: string; cwd?: string;}; -
What an
agent.spawnhook returns and whatnext(e)resolves to: the started subagent's{ model, agentId }, or{ deny: reason }, refusing it.export type AgentSpawnResult = { model: string; agentId?: string; teammateId?: string; deny?: undefined;} | { deny: string; model?: undefined; agentId?: undefined; teammateId?: undefined;}; -
What a
tool.checkhook returns and whatnext(e)resolves to: the verdict, why, and the settings rule or classic hook behind it, if any.decision: ToolCheckDecision; reason?: string; rule?: string; hook?: string;}; -
The structured result of the tool named
Name: its BuiltinToolResults entry for a built-in tool, elseunknown.export type ToolResultOf<Name extends string> = string extends Name ? unknown : Name extends keyof BuiltinToolResults & string ? BuiltinToolResults[Name] | (Name extends 'Agent' ? AgentCallRecord : never) : unknown;export type ToolResultOf<Name extends string> = string extends Name ? unknown : Name extends keyof BuiltinToolResults & string ? BuiltinToolResults[Name] | (Name extends 'Agent' ? AgentCallRecord | AgentTeammateRecord : never) : unknown; -
Where an agent's loop stands:
pending(not started),runninga turn,waiting(held),idle(between turns, until a message wakes it), or ended. -
What an Agent call that started a teammate answers as
resultattool.call: the started teammate's address, its name and its model. -
The input of
ui.fault: aClientthis plugin drew failed on a surface, addressed by where the instance is drawn. -
When a
Clientfailed, asui.faultnames it; a closed set a matcher narrows on. -
What a
ui.faulthook returns and whatnext(e)resolves to:{}, the fault was heard.
10 docs only: reworded, same shape
$.agent.listverbReturns the session's agents so far, subagents and teammates: the ones the model spawned and the ones plugins did alike.
$.ui.selectionverbReturns what the person last selected with the mouse: the text as a copy would take it, and the transcript row it lies in.
tool.checkresult{ decision, reason?, rule?, hook? }.ui.rendereventFires when the engine is about to draw a component: once per input value (props, viewport width), plugin load or
$.ui.invalidate("ui.render").LinkPropstypeThe props of
Link, a hyperlink every surface draws: an OSC 8 span on the terminal (else its text then the URL in dim), an anchor on desktop.PaneOpenArgstypeThe argument of
$.ui.open: which pane, its title, whether it asks the person's keyboard, its dialog manners, its size: rows inline, columns docked.RenderElementtypeWhat a render hook returns, and what
next(e)resolves to: a plain-data tree of elements (a Box or Text is a StyledElement), strings as children.RenderPropsOftypeThe plain-data props of each renderable component, as
ui.rendersees them undere.props; a hook rewrites them withnext({ ...e, props }).SessionSendAddresstypeWhom
$.session.sendaddresses: a recipient as the SendMessage tool names one (a string), or a session or an agent of this session by id.UiScrollPointertypeThe cell the pointer was over when the person's wheel raised
ui.scroll, in the site's body: the box itsui.renderhook draws into, as painted.
v2.1.288
+3 added · 0 removed · 1 changed · 7 docs only since 2.1.287The engine
-
Returns what the person last selected with the mouse: the text as a copy would take it, and the transcript row it lies in.
Events
-
The argument of
$.ui.selection().
Types
-
What each call on
$answers (thevalueof its event's result), by event name.'ui.open': UiOpenResult; 'ui.close': void; 'ui.panes': readonly UiPane[]; 'ui.selection': UiSelection | undefined; 'ui.copy': UiCopyResult; 'ui.blit': UiBlitResult; 'fs.read': string | FsBytes; -
What the person has selected on screen, as
$.ui.selection()answers it: the text, and the transcript row it lies in when it lies in one.
7 docs only: reworded, same shape
$.http.fetchverbFetches
urlthrough the host (never the plugin's own network) and resolves{ status, ok, headers, text }once the body is read.$.ui.invalidateverbRe-runs an event whose results the engine caches:
ui.renderdraws the instances this plugin may draw again; the others drop the cached answers.AudioCliptypeWhat
$.audio.playplays: a URL the engine fetches, or the bytes.CodePropstypeThe props of
Code, source text every surface draws with the engine's own highlighter: coloured tokens, a line gutter on request, or a unified diff.HttpInittypeOptions of
$.http.fetch.-
Who compacts: the person's
/compact(manual), the engine at its threshold or on a prompt too long (auto), a plugin, or aprecompute. ToolCheckInputtypeThe input of
tool.check: the tool, its arguments, and the call's id when the engine is deciding a real call.
v2.1.287
+10 added · 0 removed · 10 changed · 11 docs only since 2.1.286Types
-
Whether tag key
Kselects members ofI: it does when each member gives it ONE literal (component: "ToolUse"on the ToolUse variant).type IsDiscriminant<I, K> = I extends unknown ? K extends KnownKeys<I> ? IsSingleLiteral<I[K]> : true : never;type IsDiscriminant<I, K> = I extends unknown ? K extends KnownKeys<I> ? IsSingleLiteral<I[K & keyof I]> : true : never; -
The declared keys of
T, the string and number index signatures left out.type KnownKeys<T> = keyof { [K in keyof T as string extends K ? never : number extends K ? never : K]: 0;};type KnownKeys<T> = keyof (string extends keyof T ? IndexFree<T> : number extends keyof T ? IndexFree<T> : T); -
The events a matched registration on
Pcovers: the event named, or for a glob every selected event whose input has each key the matcher names.type MatchedNames<P, M = never> = P extends EventName ? P : {type MatchedNames<P, M = never> = (P extends EventName ? P : { [N in Selected<P & string>]: [M] extends [never] ? N : keyof M extends AnyKeyOf<Args<N>> ? N : never;}[Selected<P & string>];}[Selected<P & string>]) extends infer Names extends EventName ? Names : never; -
What
on(event, matcher, hook)takes for an argument of typeI: the shape of theethe hook wants, a partial of it at any depth.export type Matcher<I, All = I> = I extends unknown ? { readonly [K in KnownKeys<I>]?: MatcherValue<I[K], MatcherValueOf<All, K>>; readonly [K in KnownKeys<I>]?: MatcherValue<I[K & keyof I], MatcherValueOf<All, K>>;} & (string extends keyof I ? OpenMatcher<I, All> : unknown) : never; -
The type of key
Kacross the variants ofIthat declare it: one read of their table by key (MatcherValues), for a key some variant declares.type MatcherValueOf<I, K> = I extends unknown ? K extends KnownKeys<I> ? I[K] : never : never;type MatcherValueOf<I, K> = [I] extends [unknown] ? symbol extends K ? MatcherValueAcross<I, K> : MatcherValues<I>[K & PropertyKey] : never; -
Registers
hookon the eventspatternselects: one by name, every one under a namespace (classic.*), all (*), or all but some (!tool.*).export type On = { <P extends Pattern>(pattern: P, hook: NoInfer<HookFor<P>>): Registration<HookFor<P>>; <P extends Pattern, const M extends Matcher<Args<MatchedNames<P>>>>(pattern: P, matcher: M, hook: NoInfer<MatchedHook<P, M>>): Registration<MatchedHook<P, M>>; <P extends Pattern, const M extends MatcherFor<P>>(pattern: P, matcher: M, hook: NoInfer<MatchedHook<P, M>>): Registration<MatchedHook<P, M>>;}; -
The keys a variant with a string index signature (an MCP tool's input) takes beyond its own: another variant's key as typed there; others free.
type OpenMatcher<I, All> = { readonly [K in Exclude<MatcherKeys<All>, KnownKeys<I>>]?: MatcherValue<MatcherValueOf<All, K>>; readonly [K in Exclude<MatcherKeys<All>, keyof IndexFree<I>>]?: MatcherValue<MatcherValueOf<All, K>>;} & Readonly<Record<string, unknown>>; -
The input of
prompt.attachment: one message the engine injects into the conversation for the model on its own, as a request is about to carry it.export type PromptAttachmentInput = { type: string; text: string; origin: PromptAttachmentOrigin; agentId?: string;}; [K in keyof PromptAttachmentDetailOf]: DeclaredAttachmentInput<K>;}[keyof PromptAttachmentDetailOf] | UndeclaredAttachmentInput; -
The literal each tag key of
Iis held to by matcherM: one-of arrays flattened, RegExps widened tounknown.type Selection<I, M> = { [K in keyof M & TagKeys<I>]: Literal<M[K] extends readonly (infer One)[] ? One : M[K]>; [K in keyof M & TagKeys<I, keyof M>]: Literal<M[K] extends readonly (infer One)[] ? One : M[K]>;}; -
The keys among
Amonga matcher may select variants ofIby: valued by literals in every variant, one literal per variant (IsDiscriminant).type TagKeys<I> = { [K in MatcherKeys<I>]: IsLiteralValued<MatcherValueOf<I, K>> extends true ? IsDiscriminant<I, K> extends true ? K : never : never;}[MatcherKeys<I>];type TagKeys<I, Among> = Among extends MatcherKeys<I> ? IsLiteralValued<MatcherValueOf<I, Among>> extends true ? IsDiscriminant<I, Among> extends true ? Among : never : never : never; -
One
prompt.attachmentinput of a type PromptAttachmentDetailOf names: the attachment's text, and underdetailthe facts it was rendered from. -
An object with the declared keys of
Tand no other: its string and number index signatures are left out. What KnownKeys reads the keys of. -
The matcher type per pattern: a Matcher of the
eof the event named, or of each event a glob or a negation selects. -
The type of key
Kacross the variants ofIthat declare it, read variant by variant: what MatcherValueOf reads for a key as wide assymbol. -
The type of each declared key of
Iacross the variants that declare it, as one table by key: made once perI, then read at a key. -
What the engine knew as it made a plan-mode reminder: which of its two wordings the row carries, where the plan is kept, whether one is there.
-
What the engine knew as it made the note that plan mode has ended: where the plan is kept, whether one is there.
-
What the engine knew as it made the note that plan mode is entered again: where the plan of the earlier planning is kept.
-
The facts
prompt.attachmentpins undere.detail, by attachment type: what the engine computed for the row, as it rendered the row's text. -
One
prompt.attachmentinput of every other type, the ones PromptAttachmentDetailOf does not name: the attachment's text alone.
11 docs only: reworded, same shape
$.audio.playverbPlays one audio clip, starting now; clips are not queued, so two calls play together (a bed under speech).
prompt.composeeventFires when the engine renders a system prompt;
next(e)resolves to{ sections }, each{ id, text, scope }, in the order they are sent.prompt.editeventFires when the person edits the main prompt box: a key the editor took as an edit, or a paste;
next(e)resolves the box the editor shows.prompt.suggesteventFires when a text is proposed as the prompt box's dim suggestion, Tab to take: the engine's guess after a turn, or a plugin's
$.prompt.suggest.BuiltinToolCallInputtypeOne variant per built-in tool; with none in the table (a plugin author's project with no tools entry laid), one loose variant over every name.
BuiltinToolInputsinterfaceThe arguments of each built-in tool by name, for declaration merging; empty until a declaration file adds entries, then
e.tool === "Bash"narrowseto Bash's arguments.BuiltinToolResultsinterfaceThe structured result of each built-in tool by name, for declaration merging; empty until a declaration file adds entries, then after
e.tool === "Bash"theresultofnext(e)is Bash's record.-
The
eatool.call(orclassic.PreToolUse) hook receives for an MCP tool while no MCP tool is declared: everymcp__*name, loose arguments. McpToolInputsinterfaceThe inputs of the MCP tools this project knows, keyed by full tool name, for declaration merging; empty by default, then every MCP tool is loose.
PromptComposeScopetypeWhich side of the prompt cache's boundary a section of the system prompt sits on:
sharedbefore it,sessionafter it.PromptComposeSectiontypeOne section of the system prompt as
prompt.composeanswers it: a stable id, the text the model reads, and the side of the cache boundary it is on.