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.267
+62 added · 11 removed · 35 changed · 11 docs only since 2.1.266The engine
- removed
$.fs.listDirverb breakingLists a directory:
{ name, kind, size }per entry, by name. - removed
$.fs.readFileverb breakingReads a file and returns its text. Rejects when missing.
- removed
$.fs.writeFileverb breakingWrites
textto a file, creating it and its directories as needed. -
The elements of the surface
eis drawn on (Elements[e.surface]): a frozen table of constructors, the JSX tags a render hook draws with.<E extends RenderInput>(e: E) => Promise<Elements[E['surface']]><E extends ResolveInput>(e: E) => Elements[E['surface']] -
The environment of this process, the one every Bash child, MCP server and
$.process.runcommand started after inherits. -
Resolves with the variable's value, or
undefinedwhen it is unset. -
Sets the variable for this process and everything it starts after, or unsets it when
valueisundefined. -
Lists a directory:
{ name, kind, size }per entry, by name. -
Reads a file and returns its text. Rejects when missing.
-
Writes
textto a file, creating it and its directories as needed. -
Compacts the conversation: the event
session.compactwithtriggerplugin, the same call/compactmakes, between turns. -
Returns how full the context window is, the account's rate-limit windows, and the session's cost: the status line's own figures.
-
What the settings files,
--settingsand managed policy hold, as the engine runs under it; read only. -
Resolves with the settings merged over every source, as the engine reads them, or with one source's settings as loaded (
{ source }).
Events
-
Fires when the plugins load (not per draw), once per surface, component and plugin:
enames the surface and component, never the props.RenderInputResolveInput -
Fires when the conversation is about to be compacted (
/compact, the threshold, a plugin, or ahead of time);next(e)resolves{ messages }. -
{ messages, tokensBefore?, tokensAfter? }, or{ skip }. -
Fires when a delivery reaches the session (a relay's event, a peer's message, a Remote Control prompt), before it is queued;
{ text }. -
{ text }, or{ consumed }. -
The argument of
$.session.usage(). -
The argument of
$.settings.read({ source }). -
Fires when a
ClientTHIS plugin drew posts from its surface module (surface.post(data)); only this plugin's hooks see it. -
{ props? }: the posting instance's next props, when a hook hands some.
Types
- removed
Boxconst breaking<Box>: layout (an allowlisted subset of Ink's Box props). - removed
Buttonconst breaking<Button key="explain" label="Explain" onPress={() => ...} />: a real button; a press raisesui.presswithe.elementthe key. - removed
DomPropstype breakingThe props of
div,spanandb: onestyle, a CSS declaration string (no url(), expression() or @import; render-site/ styleProblem). - removed
Inputconst breaking<Input key="reply" onSubmit={text => ...} />: a one-line text field; a change and Enter raiseui.inputwithe.elementthe key,e.kindwhich ande.valuethe text. - removed
Linkconst breaking<Link href="https://...">label</Link>: a hyperlink; an OSC-8 span on the terminal, an anchor on the desktop. - removed
PaneScrolltype breakingWhere a pane's body window sits over the tree a hook drew in it: the engine's to move (the person scrolls while focused), the plugin's to read.
- removed
Selectconst breaking<Select key="peer" options={[...]} onSelect={value => ...} />: a one-of-several picker; a pick raisesui.selectwithe.elementthe key ande.valuethe option's value. - removed
Textconst breaking<Text>: a styled string (an allowlisted subset of Ink's Text props). -
One agent loop of this session as
$.agent.list()returns it: a subagent or an in-process teammate.description: string; type: string; status: string; parentId?: string; spawnedBy?: string; name?: string;}; -
The input of
agent.offer: one agent type, at the moment the engine offers it to the model.agent: string; description: string; source: string; provider: Origin;}; -
The input of
agent.spawn(agent-spawn/): what the Agent tool decided about the subagent it is about to start, before its model is resolved.prompt: string; description: string; subagentType: string; provider: Origin; model?: string; parentModel: string; parentAgentId?: string; permissionMode?: string; background: boolean; fork: boolean; -
The props of
Box: the layout, margin, padding and border props of Ink's Box a tree may set (render-site/ RENDER_PROPS), and the two of hover.export type BoxProps = { key?: string; hover?: BoxHoverProps; flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'; flexGrow?: number; flexShrink?: number; -
The props of
Button, every surface's pressable leaf: an address, a label, the closure a press runs, and the label styles a hover overrides.label?: string; hotkey?: string; plain?: true; hover?: TextHoverProps; onPress: () => void;}; -
The classic (settings) hook events, one per classic event name:
eis what the classic hook receives on stdin for that event.export type ClassicEventOf = { [E in ClassicHookEvent as `classic.${E}`]: E extends 'PreToolUse' ? ToolCallInput : ClassicHookInputs[E]; [E in ClassicHookEvent as `classic.${E}`]: E extends 'PreToolUse' ? ToolCallEnvelope : ClassicHookInputs[E];}; -
The input of
command.describe: how one slash command presents in the typeahead and/help, at the moment the engine lists it.argumentHint?: string; isHidden: boolean; immediate: boolean; provider: Origin;}; -
What a
command.describehook returns: the description, hint and hidden flag the menu uses; the name,immediateandproviderstay as they were.type CommandDescribeResult = Omit<CommandDescribeInput, 'command' | 'immediate'>;type CommandDescribeResult = Omit<CommandDescribeInput, 'command' | 'immediate' | 'provider'>; -
The
childrenfield every element constructor's props carry, appended beside its own props type: one child, or a list that may nest.export type ElementChildren = { children?: RenderNode | readonly RenderNode[]; children?: RenderChildren;}; -
The element constructors each surface draws, by
e.surface: what$.ui.resolve(e)returns and aui.resolvehook passes on; no globals.terminal: { Box: ElementConstructor<BoxProps>; Text: ElementConstructor<TextProps>; div: ElementConstructor<DomProps>; span: ElementConstructor<DomProps>; b: ElementConstructor<DomProps>; Button: ElementConstructor<ButtonProps>; Input: ElementConstructor<InputProps>; Select: ElementConstructor<SelectProps>; Link: ElementConstructor<LinkProps>; Code: ElementConstructor<CodeProps>; Client: ElementConstructor<ClientProps>; }; desktop: { div: ElementConstructor<DomProps>; span: ElementConstructor<DomProps>; b: ElementConstructor<DomProps>; Box: ElementConstructor<BoxProps>; Text: ElementConstructor<TextProps>; Button: ElementConstructor<ButtonProps>;… Svg: ElementConstructor<SvgProps>; Link: ElementConstructor<LinkProps>; Code: ElementConstructor<CodeProps>; Client: ElementConstructor<ClientProps>; };}; -
The events as calls on
$, one signature each:$.<noun>.<event>(input)takes the event's input and resolves to its result, from either side.}; session: { start: (input: SessionStartInput) => Promise<SessionStartResult>; receive: (input: SessionReceiveInput) => Promise<SessionReceiveResult>; compact: (input?: SessionCompactArgs) => Promise<SessionCompactResult>; }; turn: { start: (input: TurnStartInput) => Promise<TurnStartResult>;… }; ui: { render: <C extends RenderComponent>(input: RenderInput<C>) => Promise<RenderElement>; resolve: <E extends RenderInput>(e: E) => Promise<Elements[E['surface']]>; resolve: <E extends ResolveInput>(e: E) => Elements[E['surface']]; };}; -
nextin a hook on a glob or a negation: an overload per selected event, then one over their union for anenot yet narrowed.export type GlobNext<P extends Pattern, N extends EventName = Selected<P>> = OrderedOverloads<N> & { (e: Args<N>): Promise<GlobNextResult<N>>; readonly to: (e: Args<N>, tier: TargetTier) => Promise<GlobNextResult<N>>; readonly signal: AbortSignal; readonly is: <M extends PatternOver<N>>(pattern: M, e: unknown) => e is Args<Extract<N, Selected<M>>>; readonly event: N; readonly origin: string; readonly origin: Origin; readonly trace: readonly TraceEntry<N, Args<N>, GlobNextResult<N>>[];}; -
JSX over the element table: every tag is a constructor from
$.ui.resolve(e)(const { Box, Text } = $.ui.resolve(e)), typed by its props; there are no intrinsic (string) tags.namespace JSX { type Element = RenderElement type Children = RenderNode | readonly RenderNode[] type ElementType = | keyof IntrinsicElements | ((props: never) => RenderNode | null | undefined) interface IntrinsicElements { Box: BoxProps & { children?: Children } box: BoxProps & { children?: Children } Text: TextProps & { children?: Children } text: TextProps & { children?: Children } div: DomProps & { children?: Children } span: DomProps & { children?: Children } b: DomProps & { children?: Children } Svg: SvgProps Link: LinkProps & { children?: Children } Code: CodeProps Button: { key?: string label?: string hotkey?: string plain?: true onPress: () => void children?: string } Input: InputProps Select: SelectProps } type Children = RenderChildren type ElementType = (props: never) => RenderNode | null | undefined interface IntrinsicElements {} interface ElementChildrenAttribute { children: unknown } -
What matches one value of type
V, by the runtime's kinds (matchesWith): a scalar leaf takes the value or a RegExp; an object, a partial of it.type MatcherOne<V> = unknown extends V ? MatcherData : V extends readonly (infer Item)[] ? MatcherOne<Item> : V extends string ? V | RegExp : V extends number | boolean | null ? V : V extends object ? Matcher<V> : V extends undefined ? never : unknown;type MatcherOne<V> = unknown extends V ? MatcherData : V extends readonly (infer Item)[] ? MatcherOne<Item> : V extends string | number | boolean | null ? V | RegExp : V extends object ? Matcher<V> : V extends undefined ? never : unknown; -
The rest of the chain, as one hook receives it: made once per dispatch per hook, frozen;
next(e)resolves to the downstream result.}> = { <T extends string>(e: E & ToolNamed<T>): Promise<NextResultFor<N, O, T>>; (e: E): Promise<O>; readonly to: { <T extends string>(e: E & ToolNamed<T>, tier: TargetTier): Promise<NextResultFor<N, O, T>>; (e: E, tier: TargetTier): Promise<O>; }; readonly signal: AbortSignal; readonly is: <M extends PatternOver<N>>(pattern: M, e: unknown) => e is S[Extract<N, Selected<M>>]; readonly event: N; readonly origin: string; readonly origin: Origin; readonly trace: readonly TraceEntry<N, E, O>[];}; -
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>>): void; <P extends Pattern, const M extends Matcher<Args<MatchedNames<P>>>>(pattern: P, matcher: M, hook: NoInfer<MatchedHook<P, M>>): void; <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>>;}; -
What each call on
$answers (thevalueof its event's result), by event name.'session.messages': SessionMessage[]; 'session.repo': SessionRepo | null; 'session.surface': RenderSurface | null; 'session.usage': SessionUsage; 'turn.abort': void; 'tool.list': ToolInfo[]; 'tool.register': {… 'ui.invalidate': void; 'ui.open': void; 'ui.close': void; 'fs.readFile': string; 'fs.writeFile': void; 'fs.listDir': FsEntry[]; 'fs.read': string; 'fs.write': void; 'fs.list': FsEntry[]; 'fs.exists': boolean; 'fs.stat': FsStat; 'fs.ancestors': readonly FsAncestor[];… 'store.keys': string[]; 'http.fetch': HttpResponse; 'process.run': ProcessRunResult; 'settings.read': Settings; 'env.get': string | undefined; 'env.set': void;}; -
The hooks module's entry:
export function register(on, options).onregisters hooks;optionsis the plugin's configuration (PluginOptions).export type Register = (on: On, options: PluginOptions) => void | Promise<void>;export type Register = (on: On, options: PluginOptions) => unknown; -
What a render hook returns, and what
next(e)resolves to: a plain-data tree of elements, strings allowed as children of Text and Box.export type RenderElement = { type: 'Box' | 'Text' | 'div' | 'span' | 'b'; type: 'Box'; props?: Record<string, string | number | boolean>; hover?: BoxHoverProps; children?: RenderNode[];} | { type: 'Text'; props?: Record<string, string | number | boolean>; hover?: TextHoverProps; children?: RenderNode[];} | { type: 'Button';… plugin: string; handle: number; }; hover?: TextHoverProps;} | { type: 'Input'; props: {… props: CodeProps; children?: undefined;} | { type: 'Client'; props: ClientProps; client: { plugin: string; }; children?: undefined;} | { type: 'Svg'; props: SvgProps; children?: undefined; -
The plain-data props of each renderable component, as
ui.rendersees them undere.props; a hook rewrites them withnext({ ...e, props }).hasSurvey: boolean; isWorking: boolean; maxRows: number; scroll: SiteScroll; }; Pane: { title: string; focused: boolean; bodyColumns: number; scroll: PaneScroll; scroll: SiteScroll; };}; -
type SDKAssistantMessageError = 'authentication_failed' | 'oauth_org_not_allowed' | 'account_on_hold' | 'billing_error' | 'rate_limit' | 'overloaded' | 'invalid_request' | 'model_not_found' | 'server_error' | 'unknown' | 'max_output_tokens';type SDKAssistantMessageError = 'authentication_failed' | 'oauth_org_not_allowed' | 'account_on_hold' | 'billing_error' | 'rate_limit' | 'overloaded' | 'invalid_request' | 'model_not_found' | 'server_error' | 'unknown' | 'max_output_tokens' | 'cloud_credential_error'; -
One message of the transcript as
$.session.messages()returns it.text: string; toolUses: ToolUseSummary[]; toolResults?: ToolResultSummary[]; handle?: string;}; -
nextin a*hook: the set of events is open at runtime, soeisunknownuntilnext.is(pattern, e)narrows it to events it knows.export type StarNext = OrderedOverloads<EventName> & { (e: unknown): Promise<unknown>; readonly to: (e: unknown, tier: TargetTier) => Promise<unknown>; readonly signal: AbortSignal; readonly is: <M extends Pattern>(pattern: M, e: unknown) => e is Args<Selected<M>>; readonly event: EventName; readonly origin: string; readonly origin: Origin; readonly trace: readonly TraceEntry<EventName, unknown, unknown>[];}; -
The props of
Text: the color and style props of Ink's Text a tree may set (render-site/ RENDER_PROPS). Colors are a theme key or a raw color.export type TextProps = { hover?: TextHoverProps; color?: string; backgroundColor?: string; dimColor?: boolean; -
tool.call's input as the call takes it:tool_use_idandagentIdmay ride along (a hook passing its event's input on) and are dropped.export type ToolCallArgs = ToolCallInput extends infer I ? I extends ToolCallInput ? Omit<I, 'tool_use_id'> & ToolCallReserved<I['tool']> : never : never;export type ToolCallArgs = ToolCallEnvelope extends infer I ? I extends ToolCallEnvelope ? Omit<I, 'tool_use_id'> & ToolCallReserved<I['tool']> : never : never; -
The input of
tool.call: the tool, the id of this call, the tool's arguments beside them (e.commandfor Bash), andagentIdin a subagent.export type ToolCallInput = BuiltinToolCallInput | McpToolCallInput;export type ToolCallInput = ToolCallEnvelope & AgentLoop; -
The input of
tool.describe: one tool's description, at the moment the engine first renders the tool's schema for the model.export type ToolDescribeInput = { tool: string; description: string; provider: Origin;}; -
One settled run of a link beneath the caller, as
next.tracelists it: data, not a handle.type TraceEntry<N extends EventName = EventName, E = Args<N>, O = NextResult<N>> = {export type TraceEntry<N extends EventName = EventName, E = Args<N>, O = NextResult<N>> = { readonly index: number; readonly plugin: string; readonly tier: Tier; readonly event: N; readonly outcome: TraceOutcome; readonly reason?: string; readonly ms: number; readonly received: E; readonly returned: O | undefined; -
What the chain decided for one link, as
next.tracenames it.type TraceOutcome = 'expired' | 'kept' | 'passed' | 'rejected' | 'returned' | 'skipped';type TraceOutcome = 'caught' | 'expired' | 'kept' | 'passed' | 'rejected' | 'returned' | 'skipped'; -
What every
turn.completecarries whatever its reason: the answer, the duration, the interrupt flag, the turn's id and what the turn cost.durationMs: number; aborted: boolean; turnId: string; usage?: TurnUsage;}; -
What a
turn.completehook returns and whatnext(e)resolves to:{ text }; a text other than the answer's is shown beneath it.export type TurnCompleteResult = { text: string; usage?: TurnUsage;}; -
The input of
turn.step: one model response inside a turn, once its blocks are all in: at its first tool result, or at the turn's end.answer: string; toolUses: readonly TurnStepToolUse[]; stopReason: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use' | 'pause_turn' | 'compaction' | 'refusal' | 'model_context_window_exceeded'; usage?: TurnUsage;}; -
What a
turn.stephook returns and whatnext(e)resolves to, echoed by core with the step'susage; a hook's value does not change the step.export type TurnStepResult = { turnId: string; index: number; usage?: TurnUsage;}; -
Which model loop an event happened in: the loop's agent id inside a subagent's or a teammate's loop, absent on the main loop.
-
The
Boxprops ahovermay override, none of which moves layout: colors, the style of a border the Box already has, anddisplayto reveal. -
The handler
on(...).catch(handler)takes for a hook of typeF: the hook's($, e, next), run afresh when it throws, misreturns or overruns. -
What
nextcarries into a.catchhandler and nowhere else: why the hook failed, and whether it had callednextbefore it did. -
The element table a surface module draws with,
surface.elements: the terminal's constructors (types/ Elements) lessClient(none nests). -
One key the person pressed while a
Clienthad the focus, assurface.onKeyhands it. Escape never arrives: it returns the focus. -
One export of a plugin's surface module: from the
Client's props and the instance's surface to the tree drawn in its region (no nestedClient). -
One pointer event over a
Client's region, assurface.onPointerhands it: cell coordinates relative to the region's top-left corner. -
What the pointer did over a
Client's region: a button went down, the pointer moved, the button came up, or the pointer crossed the region's edge. -
The props of
Client: which export of the plugin's surface module draws here, under what key, with what data, in how much room. -
What a surface module's function receives as its second argument: its elements, the instance's local state, its region, input, clock and port.
-
Why a hook failed, as its
.catchhandler reads it onnext.error: plain frozen data. -
Plain data: what JSON holds, and what crosses between a plugin's hooks module and its surface module whole (a
Client's props, a post's data). -
Who raised a dispatch, as
next.originholds it: the calling plugin's name and the tier it sits in; the engine readsengineincore. -
What
on(...)returns for a hook of typeF: the registration, which takes one.catch(CatchHandler); without it a failed hook is absent. -
What an element takes as
children: a node, or a list of children that may nest, as JSX passes{items.map(row)}beside a sibling. -
The input of
ui.resolve: which surface's elements, for which component; a union with one member per surface (ResolveInputOf). -
One
ui.resolveinput, for a component on one surface. -
session.compact's input as a plugin's$.session.compact(args)takes it:trigger(plugin),messagesandagentIdare the engine's. -
The input of
session.compact: one compaction of the conversation, about to run;messagesis the transcript it runs over. -
What a
session.compacthook returns and whatnext(e)resolves to: the compaction ({ messages, tokensBefore?, tokensAfter? }) or{ skip }. -
A compaction vetoed: on
precomputenothing is computed or kept; on any other trigger the conversation stays as it is and one line says why. -
Who compacts: the person's
/compact(manual), the engine at its threshold or on a prompt too long (auto), a plugin, or aprecompute. -
A compaction that stands: the conversation as it reads afterwards, and the token counts the engine recorded when it was the one compacting.
-
The live context window as the status line reads it: the last API response's input side against the model's window.
-
What the session has cost, as
/costand the status line total it. -
One rate-limit window as the rate-limit notices read it.
-
An external-event wake the delivery's text parsed as (a GitHub relay event, a signal's notice): the envelope's attributes and its JSON body.
-
The input of
session.receive: one inbound delivery (a relay's event, a peer's message, a Remote Control prompt), sanitized, before it is queued. -
Where an inbound delivery came from, as the bridge classified it from the server's stamps;
prompt.submit'se.origin.kind, less what never arrives. -
What a
session.receivehook returns and whatnext(e)resolves to: the delivery that proceeds,{ text }, or{ consumed: reason }. -
What
$.session.usage()answers: the context window's fill, the account's rate-limit windows and the session's cost, as the status line has them. -
What
$.settings.readanswers: an object keyed as a settings.json is (permissions,env,hooks,model,enabledPlugins, ...). -
What
$.settings.read(args)takes. -
One source of settings by the name a plugin gives it, lowest precedence first; the engine's own name is the same word with
Settingsappended. -
Where 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.
-
The chain's five tiers, outermost first: earlier is outer is more authority, and same-event hooks nest in this order and no other way.
-
A tier
next.to(e, tier)may name: one a floor can reach past a tier of less authority to, so neverprependoruser, which nothing skips to. -
The
Textprops ahovermay override: its colors and styles, not its wrapping. AButtontakes the same set for its label. -
One of the chain's five tiers (TIERS), outermost first; on every
next.traceentry, and whatnext.to(e, tier)names. -
The envelope the two tool events share: the tool, the id of this call, and the tool's arguments spread beside them (
e.commandfor Bash). -
What a model turn, or one response inside it, cost as the API reported it: the four token counts (
$.model.fork's usage shape) and the model's id. -
The argument of
ui.message: what aClientinstance's surface module posted (surface.post(data)), addressed by where the instance is drawn. -
What a
ui.messagehook returns and whatnext(e)resolves to.
11 docs only: reworded, same shape
$.sessionnounThe running session, read as plain data, and compacting it.
$.store.setverbSets
keytovalue, which must be JSON data.$.tool.registerverbDeclares a tool the model can call from the next prompt on: the name, description and input schema of
mcp__<plugin>__<name>.$.ui.askverbAsks the user
questionin the engine's own AskUserQuestion dialog and resolves to the label they chose, or the text typed under "Other".ElementConstructortypeAn element as
$.ui.resolve(e)hands it out: a constructor from props to the frozen plain-data element,childrenamong the props as JSX passes.EventOftypeThe argument of each event, by event name: what a hook receives as
eand what the call on$takes. Plain data, frozen to every depth.EventstypeThe hook signature of each event,
($, e, next), as one mapped type over EventOf.FsEntrytypeOne entry of
$.fs.list.LateOverloadtypeThe events whose overload must come after the rest, lest it shadow them.
MatchertypeWhat
on(event, matcher, hook)takes for an argument of typeI: the shape of theethe hook wants, a partial of it at any depth.MatcherDatatypeAny matcher at all, for a field typed
unknown(a tool's input, a result's output): the kinds the engine accepts, unchecked there.
v2.1.266
No change to the surface since 2.1.265v2.1.265
+105 added · 1 removed · 22 changed · 16 docs only since 2.1.263The engine
-
The slash commands the person can run in this session, and running one.
-
Returns the slash commands the person can run now, built-in, plugin and MCP alike, in the order the typeahead lists them.
-
Declares the slash command
/<name>for this session, listed in the typeahead from the next keystroke on. -
Runs a slash command as if the person typed
/command args: the eventcommand.run, queued and run once the session is idle. -
Closes one of the open panes; an id that is not open is left alone.
-
Opens a pane: a framed region the surface places, whose body this plugin draws by hooking
ui.renderfor{ component: "Pane" }.
Events
-
Fires once per command, when the engine lists it for the typeahead and
/help;next(e)resolves to{ description, argumentHint, isHidden }. -
{ description, argumentHint, isHidden }. -
The argument of
$.command.list(). -
The argument of
$.command.register(spec). -
Fires when a slash command is about to run (
/name argstyped, or a plugin's$.command.run);next(e)resolves to{ text }, its output. -
{ text }(the command's output, when it printed one). -
The argument of
$.ui.close({ id })withoriginplugin; the engine raises it too, for the header's[x](person) and an unload (unload). -
The argument of
$.ui.open({ id, title, focus }); a hook above the opener may retitle it or refuse it with{ deny }, never rename it.
Types
- removed
StarOverloadstype breakingOne call signature per event
*fans out to (every event but the settings hooks'PreToolUse), intersected: an overload set from EventOf and ResultOf. -
The element constructors each surface draws, by
e.surface: what$.ui.resolve(e)resolves to, and what aui.resolvehook passes on.Input: ElementConstructor<InputProps>; Select: ElementConstructor<SelectProps>; Link: ElementConstructor<LinkProps>; Code: ElementConstructor<CodeProps>; }; desktop: { div: ElementConstructor<DomProps>;… Select: ElementConstructor<SelectProps>; Svg: ElementConstructor<SvgProps>; Link: ElementConstructor<LinkProps>; Code: ElementConstructor<CodeProps>; };}; -
The events as calls on
$, one signature each:$.<noun>.<event>(input)takes the event's input and resolves to its result, from either side.tool: { call: ToolCallOverloads; describe: (input: ToolDescribeInput) => Promise<ToolDescribeResult>; }; command: { run: (input: CommandRunArgs) => Promise<CommandRunResult>; describe: (input: CommandDescribeInput) => Promise<CommandDescribeResult>; }; prompt: { submit: (input: PromptSubmitArgs) => Promise<PromptSubmitResult>; -
The argument of each event, by event name: what a hook receives as
eand what the call on$takes. Plain data, frozen to every depth.export type EventOf = EngineEventOf & OpEventOf;export type EventOf = CoreEventOf & NounEventOf; -
The hook type per pattern: an event's own (Events),
*'s (AnyEventHook), or a glob's over the events it selects (GlobHook), as one conditional type.export type HookFor<E extends EventName | '*'> = E extends '*' ? AnyEventHook : Events[E & EventName];export type HookFor<P extends Pattern> = P extends '*' ? AnyEventHook : P extends EventName ? Events[P] : GlobHook<P>; -
What
$.ui.invalidatetakes: a render event, or one of the four events whose answers the engine caches for the session.export type InvalidatableEventName = RenderEventName | 'prompt.section' | 'prompt.context' | 'tool.describe';export type InvalidatableEventName = RenderEventName | 'prompt.section' | 'prompt.context' | 'tool.describe' | 'command.describe'; -
b: DomProps & { children?: Children } Svg: SvgProps Link: LinkProps & { children?: Children } Code: CodeProps Button: { key?: string label?: string -
What
nexttakes in a matched hook: the variants ofethe matcher can match (KeptMembers), as declared, so a rewrite of a pinned field passes.type KeptEvent<E extends EventName | '*', M> = MatchedNames<E, M> extends infer N extends EventName ? N extends unknown ? KeptMembers<Args<N>, M> : never : never;type KeptEvent<P extends Pattern, M> = MatchedNames<P, M> extends infer N extends EventName ? N extends unknown ? KeptMembers<Args<N>, M> : never : never; -
The argument a matched hook receives:
enarrowed byM(Narrowed), per event the registration covers.export type MatchedEvent<E extends EventName | '*', M> = MatchedNames<E, M> extends infer N extends EventName ? N extends unknown ? Narrowed<Args<N>, M> : never : never;export type MatchedEvent<P extends Pattern, M> = MatchedNames<P, M> extends infer N extends EventName ? N extends unknown ? Narrowed<Args<N>, M> : never : never; -
The hook
on(pattern, matcher, hook)takes:($, e, next)withenarrowed by the matcher (MatchedEvent), and a tagged result the same way.export type MatchedHook<E extends EventName | '*', M> = ($: EngineInterface, e: MatchedEvent<E, M>, next: Next<MatchedNames<E, M>, KeptEvent<E, M>, MatchedResult<E, M>, { [K in MatchedNames<E, M>]: Narrowed<Args<K>, M>;}>) => MatchedResult<E, M> | Promise<MatchedResult<E, M>>;export type MatchedHook<P extends Pattern, M> = ($: EngineInterface, e: MatchedEvent<P, M>, next: Next<MatchedNames<P, M>, KeptEvent<P, M>, MatchedResult<P, M>, { [K in MatchedNames<P, M>]: Narrowed<Args<K>, M>;}>) => MatchedResult<P, M> | Promise<MatchedResult<P, M>>; -
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<E, M = never> = E extends '*' ? { [N in EventName]: [M] extends [never] ? N : keyof M extends AnyKeyOf<Args<N>> ? N : never;}[EventName] : E;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>]; -
What a matched hook returns: the event's result, narrowed by
Mwhere the result is a union tagged by the matcher's tag keys.export type MatchedResult<E extends EventName | '*', M> = MatchedNames<E, M> extends infer N extends EventName ? N extends unknown ? Select<EventResult<N>, Selection<Args<N>, M>> : never : never;export type MatchedResult<P extends Pattern, M> = MatchedNames<P, M> extends infer N extends EventName ? N extends unknown ? Select<EventResult<N>, Selection<Args<N>, M>> : never : never; -
The rest of the chain, as one hook receives it: made once per dispatch per hook, frozen;
next(e)resolves to the downstream result.<T extends string>(e: E & ToolNamed<T>): Promise<NextResultFor<N, O, T>>; (e: E): Promise<O>; readonly signal: AbortSignal; readonly is: <M extends N>(name: M, e: unknown) => e is S[M]; readonly is: <M extends PatternOver<N>>(pattern: M, e: unknown) => e is S[Extract<N, Selected<M>>]; readonly event: N; readonly origin: string; readonly trace: readonly TraceEntry<N, E, O>[];}; -
The events whose argument is exactly NoArgs (
session.cwd, a declared plugin noun's() => ...); their overloads come last (LateOverload).type NoArgsEvent = { [N in OpEventName]: OpEventOf[N] extends NoArgs ? NoArgs extends OpEventOf[N] ? N : never : never;}[OpEventName]; [N in EventName]: Args<N> extends NoArgs ? NoArgs extends Args<N> ? N : never : never;}[EventName]; -
Registers
hookon the eventspatternselects: one by name, every one under a namespace (classic.*), all (*), or all but some (!tool.*).export type On = { <E extends EventName | '*'>(event: E, hook: HookFor<E>): void; <E extends EventName | '*', const M extends Matcher<Args<MatchedNames<E>>>>(event: E, matcher: M, hook: MatchedHook<E, M>): void; <P extends Pattern>(pattern: P, hook: NoInfer<HookFor<P>>): void; <P extends Pattern, const M extends Matcher<Args<MatchedNames<P>>>>(pattern: P, matcher: M, hook: NoInfer<MatchedHook<P, M>>): void;}; -
The result of a call on
$as its event's hooks see it:{ value }(the call's answer) or{ deny }.export type OpEventResult<N extends OpEventName = OpEventName> = { value: OpValueOf[N]; deny?: undefined;} | { deny: string; value?: undefined;};export type OpEventResult<N extends OpEventName = OpEventName> = ValueOrDeny<OpValueOf[N]>; -
What each call on
$answers (thevalueof its event's result), by event name.'tool.register': { tool: string; }; 'command.list': CommandInfo[]; 'command.register': { command: string; }; 'agent.list': AgentInfo[]; 'ui.toast': void; 'ui.status': void; 'ui.log': void; 'ui.notice': void; 'ui.invalidate': void; 'ui.open': void; 'ui.close': void; 'fs.readFile': string; 'fs.writeFile': void; 'fs.listDir': FsEntry[]; - changed
RenderAnswerOptionstype may breakHow a site instance asks for its
ui.renderanswer: the version it is on, whether a static frame is drawing, and whose submit it serves.export type RenderAnswerOptions = { version: number; version: string; staticFrame: boolean; submittedBy?: string;}; -
Everything
ui.rendercan draw: one name per component that has a render site (render-site/); a matcher narrows on it.export type RenderComponent = 'AskUserQuestion' | 'UserMessage' | 'AssistantMessage' | 'ToolUse' | 'ToolResult' | 'ToolGroup' | 'Spinner' | 'TurnDuration' | 'InfoNotice' | 'SessionMode' | 'PromptHint' | 'AbovePrompt';export type RenderComponent = 'AskUserQuestion' | 'UserMessage' | 'AssistantMessage' | 'ToolUse' | 'ToolResult' | 'ToolGroup' | 'Spinner' | 'TurnDuration' | 'InfoNotice' | 'SessionMode' | 'PromptHint' | 'AbovePrompt' | 'Pane'; -
What a render hook returns, and what
next(e)resolves to: a plain-data tree of elements, strings allowed as children of Text and Box.props: LinkProps; children?: RenderNode[];} | { type: 'Code'; props: CodeProps; children?: undefined;} | { type: 'Svg'; props: SvgProps; children?: undefined; -
The plain-data props of each renderable component, as
ui.rendersees them undere.props; a hook rewrites them withnext({ ...e, props }).isWorking: boolean; maxRows: number; }; Pane: { title: string; focused: boolean; bodyColumns: number; scroll: PaneScroll; };}; -
What each event's hook returns, and what its
next(e)resolves to, by event name.export type ResultOf = EngineResultOf & {export type ResultOf = EngineResultOf & ClassicResultOf & { [N in OpEventName]: OpEventResult<N>;} & { [N in NounEventName]: NounEventResult<N>;}; -
nextin a*hook: the set of events is open at runtime, soeisunknownuntilnext.is(pattern, e)narrows it to events it knows.export type StarNext = StarOverloads & {export type StarNext = OrderedOverloads<EventName> & { (e: unknown): Promise<unknown>; readonly signal: AbortSignal; readonly is: <M extends EventName>(name: M, e: unknown) => e is Args<M>; readonly is: <M extends Pattern>(pattern: M, e: unknown) => e is Args<Selected<M>>; readonly event: EventName; readonly origin: string; readonly trace: readonly TraceEntry<EventName, unknown, unknown>[];}; -
The name of a classic hook event as a function-hooks event: the settings hook's own name under
classic(classic.Stop,classic.PreToolUse). -
The classic (settings) hook events, one per classic event name:
eis what the classic hook receives on stdin for that event. -
The name of a classic hook event:
PreToolUse,Stop, and the rest. -
What a classic hook receives on stdin for each event, by event name: the Agent SDK's
<Event>HookInput. -
Everything a classic hook event's answer can carry, named as the classic hook's JSON output names it; each event reads its subset (ClassicResultOf).
-
The event-specific fields of ClassicResult each classic event reads (its
hookSpecificOutput), by event; an event absent here reads none of them. -
What each classic hook event's hook returns and its
next(e)resolves to: the event's own subset of ClassicResult. -
The 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. -
The input of
command.describe: how one slash command presents in the typeahead and/help, at the moment the engine lists it. -
What a
command.describehook returns: the description, hint and hidden flag the menu uses, the name andimmediateleft where they were. -
One slash command as
$.command.list()returns it. -
command.run's input as a plugin's$.command.runtakes it:originis the engine's to set (the calling plugin's name). -
The input of
command.run: one slash command about to run, the way the person typed it (/name args), and where the run came from. -
What a
command.runhook returns and whatnext(e)and$.command.runresolve to: the command's output text, when it has one. -
Where a slash command comes from, as
$.command.list()tells them apart. -
What
$.command.registertakes: the slash command this plugin serves. -
The name of an event the engine defines itself (a key of CoreEventOf): what EVENT_NAMES lists; EventName adds the declared plugin nouns' events.
-
The argument of each event the engine defines itself: its call sites' (EngineEventOf), the classic hooks' (ClassicEventOf), the calls on
$. -
Hook input for the Elicitation event. Fired when an MCP server requests user input. Hooks can auto-respond (accept/decline) instead of showing the dialog.
-
Hook input for the ElicitationResult event. Fired after the user responds to an MCP elicitation. Hooks can observe or override the response before it is sent to the server.
-
Every event (
*), or every event under a namespace (classic.*: each one whose name starts withclassic.). -
The hook
on(pattern, hook)takes for a glob or a negation: one function placed on every selected event,eand the result typed as their union. -
nextin a hook on a glob or a negation: an overload per selected event, then one over their union for anenot yet narrowed. -
What
next(e)resolves to in a glob hook beforeeis narrowed: the NextResult of each selected event, as a union. -
The events whose overload must come after the rest, lest it shadow them.
-
Hook input for the MessageDisplay event. Fired with each batch of newly completed lines while an assistant message streams. Display-only: the stored message and what the model sees are untouched.
-
The prefixes a glob may name: one or more whole leading segments of an event name (
tooloftool.call), derived, plugin nouns included. -
!before a name or a glob: every event except the ones it selects.!*would select none, so it is no pattern. -
The declared plugin nouns' methods as event rows (NounEventRow), one per
<noun>.<method>that is a function; a member that is not is no event. -
The name of a declared plugin noun's event (
voice.speak). -
The events of the plugin nouns declared on EngineInterface, by name: the argument of each
<noun>.<method>. Empty until a plugin declares a noun. -
The result of a declared plugin noun's event as its hooks see it:
{ value }(the method's answer) or{ deny }. -
One method of a declared plugin noun as an event row: its event's name, argument (the method's first parameter) and value (its awaited result).
-
What each declared plugin noun's method answers (the
valueof its event's result), by event name. -
One call signature per event in
Names, intersected, the ambiguous ones (LateOverload) after the rest:nextfor a hook covering several events. -
The argument of
$.ui.close: the pane to close ({ id }).originis the engine's to set: a plugin's call readspluginat the hooks. -
The input of
ui.close: the pane closing and why (PaneCloseOrigin). Closing an id that is not open does nothing. -
Why a pane closes, as the engine stamped it at
ui.close:plugin, a plugin's$.ui.close;person, the header's[x];unload, the engine's. -
The argument of
$.ui.open: which pane, its title, and whether the plugin asks the person's keyboard for it. - added
PaneScrolltypeWhere a pane's body window sits over the tree a hook drew in it: the engine's to move (the person scrolls while focused), the plugin's to read.
-
What
on(pattern, hook)andnext.is(pattern, e)take: an event's name, a glob (*,classic.*), or a negation of either (!tool.describe). -
The patterns
next.istakes in a hook covering the eventsN: their names,*, a glob over one of their namespaces, or a negation. -
Permission mode for controlling how tool executions are handled. 'default' - Standard behavior, prompts for dangerous operations. 'acceptEdits' - Auto-accept file edit operations. 'bypassPermissions' - Bypass all permission checks (requires allowDangerouslySkipPermissions). 'plan' - Planning mode, no actual tool execution. 'dontAsk' - Don't prompt for permissions, deny if not pre-approved. 'auto' - Use a model classifier to approve/deny permission prompts.
-
A
classic.PermissionRequestanswer'sdecision, as the classic hook'shookSpecificOutput.decision: allow (with a rewrite or rules) or deny. -
The permission rules a PermissionRequest allow may add: the shape of the request's own
permission_suggestions(the SDK's PermissionUpdate list). -
The nouns a plugin declared on
$by merging into EngineInterface; never one the engine's own events are under (tool,flag: isPluginEventName). -
Hook input for the PostToolBatch event. Fired once after every tool call in a batch has resolved, before the next model request. PostToolUse fires per-tool and may run concurrently for parallel tool calls; PostToolBatch fires exactly once with the full batch.
-
The events a pattern selects, as a union of names: the one named, every one under a glob's namespace, or every one a negation does not exclude.
-
One settled run of a link beneath the caller, as
next.tracelists it: data, not a handle. -
What the chain decided for one link, as
next.tracenames it. -
The result of a call on
$as the hooks on its event see it:{ value }, the call's answer, or{ deny }, the reason the caller's promise rejects.
16 docs only: reworded, same shape
$.fsnounThe file system as the engine's own process reaches it, text only (UTF-8); a relative path is under the session's working directory.
$.fs.ancestorsverbReads the named instruction files in every directory above the session's original working directory, the way the engine reads CLAUDE.md.
$.fs.existsverbReturns whether the path exists; never rejects.
$.fs.listDirverbLists a directory:
{ name, kind, size }per entry, by name.$.fs.readFileverbReads a file and returns its text. Rejects when missing.
$.fs.writeFileverbWrites
textto a file, creating it and its directories as needed.$.uinounDisplay: a line under an open dialog, a redraw request, a transcript line, a pane the surface places.
$.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.AnyEventHooktypeThe hook
on("*", hook)takes: it runs on every event, plugin nouns no declaration names included, soeisunknownandnextis StarNext.ButtonPropstypeThe props of
Button, every surface's pressable leaf: an address, a label, and the closure a press runs.EventNametypeThe name of an event: a key of EventOf, the engine's own (CoreEventName) and the declared plugin nouns' (NounEventName).
FsAncestorsRequesttypeThe argument of
$.fs.ancestors: the file names to look for in each directory, and the file to walk down to.PreToolUseDecisiontypeThe decision of a
classic.PreToolUseresult:allow,ask,deny, or none.PreToolUseResulttypeWhat a
classic.PreToolUsehook returns: one ofallow,ask,deny, or none of them, which passes the call on to the normal permission flow.ToolResultSummarytypeOne tool_result block of a user message.
ToolUseSummarytypeOne tool_use block of an assistant message, with its outcome once the transcript holds the call's tool_result (paired by
id).
v2.1.263
No change to the surface since 2.1.261v2.1.261
+0 added · 0 removed · 0 changed · 2 docs only since 2.1.2602 docs only: reworded, same shape
$.processnounCommands on the host, run as the user the session runs as. CLI only.
RenderSurfacetypeWhere a render event's component is drawn:
terminalis Ink, which draws the hook's whole tree;desktopis a surface that draws its own DOM.