Follow Discord
Sweep 25 Sep 2026 · 19:33Z Build v2.1.283 504 read Stable v2.1.274 Latest v2.1.283 Next v2.1.283 Feeds RSS JSON llms.txt llms-full.txt Unofficial
Mods API changes

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.

Releases22 since 2.1.259
Newest2.1.283+0 added · 0 removed · 2 changed · 2 docs only
Machine readablechanges.jsonin pages: counts, and each release's items

v2.1.267

+62 added · 11 removed · 35 changed · 11 docs only since 2.1.266

The engine

  • removed $.fs.listDir verb breaking

    Lists a directory: { name, kind, size } per entry, by name.

  • removed $.fs.readFile verb breaking

    Reads a file and returns its text. Rejects when missing.

  • removed $.fs.writeFile verb breaking

    Writes text to a file, creating it and its directories as needed.

  • changed $.ui.resolve verb may break

    The elements of the surface e is 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']]
  • added $.env noun

    The environment of this process, the one every Bash child, MCP server and $.process.run command started after inherits.

  • added $.env.get verb

    Resolves with the variable's value, or undefined when it is unset.

  • added $.env.set verb

    Sets the variable for this process and everything it starts after, or unsets it when value is undefined.

  • added $.fs.list verb

    Lists a directory: { name, kind, size } per entry, by name.

  • added $.fs.read verb

    Reads a file and returns its text. Rejects when missing.

  • added $.fs.write verb

    Writes text to a file, creating it and its directories as needed.

  • Compacts the conversation: the event session.compact with trigger plugin, the same call /compact makes, between turns.

  • added $.session.usage verb

    Returns how full the context window is, the account's rate-limit windows, and the session's cost: the status line's own figures.

  • added $.settings noun

    What the settings files, --settings and managed policy hold, as the engine runs under it; read only.

  • added $.settings.read verb

    Resolves with the settings merged over every source, as the engine reads them, or with one source's settings as loaded ({ source }).

Events

  • changed ui.resolve event may break

    Fires when the plugins load (not per draw), once per surface, component and plugin: e names the surface and component, never the props.

    RenderInputResolveInput
  • added session.compact event

    Fires when the conversation is about to be compacted (/compact, the threshold, a plugin, or ahead of time); next(e) resolves { messages }.

  • added session.compact result

    { messages, tokensBefore?, tokensAfter? }, or { skip }.

  • added session.receive event

    Fires when a delivery reaches the session (a relay's event, a peer's message, a Remote Control prompt), before it is queued; { text }.

  • added session.receive result

    { text }, or { consumed }.

  • added session.usage event

    The argument of $.session.usage().

  • added settings.read event

    The argument of $.settings.read({ source }).

  • added ui.message event

    Fires when a Client THIS plugin drew posts from its surface module (surface.post(data)); only this plugin's hooks see it.

  • added ui.message result

    { props? }: the posting instance's next props, when a hook hands some.

Types

  • removed Box const breaking

    <Box>: layout (an allowlisted subset of Ink's Box props).

  • removed Button const breaking

    <Button key="explain" label="Explain" onPress={() => ...} />: a real button; a press raises ui.press with e.element the key.

  • removed DomProps type breaking

    The props of div, span and b: one style, a CSS declaration string (no url(), expression() or @import; render-site/ styleProblem).

  • removed Input const breaking

    <Input key="reply" onSubmit={text => ...} />: a one-line text field; a change and Enter raise ui.input with e.element the key, e.kind which and e.value the text.

  • removed Link const breaking

    <Link href="https://...">label</Link>: a hyperlink; an OSC-8 span on the terminal, an anchor on the desktop.

  • removed PaneScroll type breaking

    Where 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 Select const breaking

    <Select key="peer" options={[...]} onSelect={value => ...} />: a one-of-several picker; a pick raises ui.select with e.element the key and e.value the option's value.

  • removed Text const breaking

    <Text>: a styled string (an allowlisted subset of Ink's Text props).

  • changed AgentInfo type extended

    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;};
  • changed AgentOfferInput type extended

    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;};
  • changed AgentSpawnInput type extended

    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;
  • changed BoxProps type extended

    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;
  • changed ButtonProps type extended

    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;};
  • changed ClassicEventOf type may break

    The classic (settings) hook events, one per classic event name: e is 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];};
  • changed CommandDescribeInput type extended

    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;};
  • changed CommandDescribeResult type may break

    What a command.describe hook returns: the description, hint and hidden flag the menu uses; the name, immediate and provider stay as they were.

    type CommandDescribeResult = Omit<CommandDescribeInput, 'command' | 'immediate'>;type CommandDescribeResult = Omit<CommandDescribeInput, 'command' | 'immediate' | 'provider'>;
  • changed ElementChildren type may break

    The children field 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;};
  • changed Elements type may break

    The element constructors each surface draws, by e.surface: what $.ui.resolve(e) returns and a ui.resolve hook 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>;    };};
  • changed EventCalls type may break

    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']];    };};
  • changed GlobNext type may break

    next in a hook on a glob or a negation: an overload per selected event, then one over their union for an e not 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>>[];};
  • changed JSX namespace may break

    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  }
  • changed MatcherOne type may break

    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;
  • changed Next type may break

    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>[];};
  • changed On type may break

    Registers hook on the events pattern selects: 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>>;};
  • changed OpValueOf type may break

    What each call on $ answers (the value of 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;};
  • changed Register type may break

    The hooks module's entry: export function register(on, options). on registers hooks; options is the plugin's configuration (PluginOptions).

    export type Register = (on: On, options: PluginOptions) => void | Promise<void>;export type Register = (on: On, options: PluginOptions) => unknown;
  • changed RenderElement type may break

    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;
  • changed RenderPropsOf type may break

    The plain-data props of each renderable component, as ui.render sees them under e.props; a hook rewrites them with next({ ...e, props }).

            hasSurvey: boolean;        isWorking: boolean;        maxRows: number;        scroll: SiteScroll;    };    Pane: {        title: string;        focused: boolean;        bodyColumns: number;        scroll: PaneScroll;        scroll: SiteScroll;    };};
  • changed SDKAssistantMessageError type may break
    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';
  • changed SessionMessage type extended

    One message of the transcript as $.session.messages() returns it.

        text: string;    toolUses: ToolUseSummary[];    toolResults?: ToolResultSummary[];    handle?: string;};
  • changed StarNext type may break

    next in a * hook: the set of events is open at runtime, so e is unknown until next.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>[];};
  • changed TextProps type extended

    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;
  • changed ToolCallArgs type may break

    tool.call's input as the call takes it: tool_use_id and agentId may 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;
  • changed ToolCallInput type may break

    The input of tool.call: the tool, the id of this call, the tool's arguments beside them (e.command for Bash), and agentId in a subagent.

    export type ToolCallInput = BuiltinToolCallInput | McpToolCallInput;export type ToolCallInput = ToolCallEnvelope & AgentLoop;
  • changed ToolDescribeInput type extended

    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;};
  • changed TraceEntry type may break

    One settled run of a link beneath the caller, as next.trace lists 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;
  • changed TraceOutcome type may break

    What the chain decided for one link, as next.trace names it.

    type TraceOutcome = 'expired' | 'kept' | 'passed' | 'rejected' | 'returned' | 'skipped';type TraceOutcome = 'caught' | 'expired' | 'kept' | 'passed' | 'rejected' | 'returned' | 'skipped';
  • changed TurnCompleteFields type extended

    What every turn.complete carries 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;};
  • changed TurnCompleteResult type extended

    What a turn.complete hook returns and what next(e) resolves to: { text }; a text other than the answer's is shown beneath it.

    export type TurnCompleteResult = {    text: string;    usage?: TurnUsage;};
  • changed TurnStepInput type extended

    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;};
  • changed TurnStepResult type extended

    What a turn.step hook returns and what next(e) resolves to, echoed by core with the step's usage; a hook's value does not change the step.

    export type TurnStepResult = {    turnId: string;    index: number;    usage?: TurnUsage;};
  • added AgentLoop type

    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.

  • added BoxHoverProps type

    The Box props a hover may override, none of which moves layout: colors, the style of a border the Box already has, and display to reveal.

  • added CatchHandler type

    The handler on(...).catch(handler) takes for a hook of type F: the hook's ($, e, next), run afresh when it throws, misreturns or overruns.

  • added Caught type

    What next carries into a .catch handler and nowhere else: why the hook failed, and whether it had called next before it did.

  • added ClientElements type

    The element table a surface module draws with, surface.elements: the terminal's constructors (types/ Elements) less Client (none nests).

  • added ClientKeyEvent type

    One key the person pressed while a Client had the focus, as surface.onKey hands it. Escape never arrives: it returns the focus.

  • added ClientModule type

    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 nested Client).

  • One pointer event over a Client's region, as surface.onPointer hands 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.

  • added ClientProps type

    The props of Client: which export of the plugin's surface module draws here, under what key, with what data, in how much room.

  • added ClientSurface type

    What a surface module's function receives as its second argument: its elements, the instance's local state, its region, input, clock and port.

  • added HookFailure type

    Why a hook failed, as its .catch handler reads it on next.error: plain frozen data.

  • added JsonValue type

    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).

  • added Origin type

    Who raised a dispatch, as next.origin holds it: the calling plugin's name and the tier it sits in; the engine reads engine in core.

  • added Registration type

    What on(...) returns for a hook of type F: the registration, which takes one .catch (CatchHandler); without it a failed hook is absent.

  • added RenderChildren type

    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.

  • added ResolveInput type

    The input of ui.resolve: which surface's elements, for which component; a union with one member per surface (ResolveInputOf).

  • added ResolveInputOf type

    One ui.resolve input, for a component on one surface.

  • session.compact's input as a plugin's $.session.compact(args) takes it: trigger (plugin), messages and agentId are the engine's.

  • The input of session.compact: one compaction of the conversation, about to run; messages is the transcript it runs over.

  • What a session.compact hook returns and what next(e) resolves to: the compaction ({ messages, tokensBefore?, tokensAfter? }) or { skip }.

  • A compaction vetoed: on precompute nothing 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 a precompute.

  • added SessionCompacted type

    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.

  • added SessionCost type

    What the session has cost, as /cost and the status line total it.

  • added SessionRateLimit type

    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's e.origin.kind, less what never arrives.

  • What a session.receive hook returns and what next(e) resolves to: the delivery that proceeds, { text }, or { consumed: reason }.

  • added SessionUsage type

    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.

  • added Settings type

    What $.settings.read answers: an object keyed as a settings.json is (permissions, env, hooks, model, enabledPlugins, ...).

  • added SettingsReadArgs type

    What $.settings.read(args) takes.

  • added SettingsSource type

    One source of settings by the name a plugin gives it, lowest precedence first; the engine's own name is the same word with Settings appended.

  • added SiteScroll type

    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.

  • added TIERS const

    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.

  • added TargetTier type

    A tier next.to(e, tier) may name: one a floor can reach past a tier of less authority to, so never prepend or user, which nothing skips to.

  • added TextHoverProps type

    The Text props a hover may override: its colors and styles, not its wrapping. A Button takes the same set for its label.

  • added Tier type

    One of the chain's five tiers (TIERS), outermost first; on every next.trace entry, and what next.to(e, tier) names.

  • added ToolCallEnvelope type

    The envelope the two tool events share: the tool, the id of this call, and the tool's arguments spread beside them (e.command for Bash).

  • added TurnUsage type

    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 a Client instance's surface module posted (surface.post(data)), addressed by where the instance is drawn.

  • added UiMessageResult type

    What a ui.message hook returns and what next(e) resolves to.

11 docs only: reworded, same shape
  • The running session, read as plain data, and compacting it.

  • Sets key to value, which must be JSON data.

  • Declares a tool the model can call from the next prompt on: the name, description and input schema of mcp__<plugin>__<name>.

  • Asks the user question in the engine's own AskUserQuestion dialog and resolves to the label they chose, or the text typed under "Other".

  • An element as $.ui.resolve(e) hands it out: a constructor from props to the frozen plain-data element, children among the props as JSX passes.

  • EventOf type

    The argument of each event, by event name: what a hook receives as e and what the call on $ takes. Plain data, frozen to every depth.

  • Events type

    The hook signature of each event, ($, e, next), as one mapped type over EventOf.

  • FsEntry type

    One entry of $.fs.list.

  • The events whose overload must come after the rest, lest it shadow them.

  • Matcher type

    What on(event, matcher, hook) takes for an argument of type I: the shape of the e the hook wants, a partial of it at any depth.

  • Any 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.265

v2.1.265

+105 added · 1 removed · 22 changed · 16 docs only since 2.1.263

The engine

  • added $.command noun

    The slash commands the person can run in this session, and running one.

  • added $.command.list verb

    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.

  • added $.command.run verb

    Runs a slash command as if the person typed /command args: the event command.run, queued and run once the session is idle.

  • added $.ui.close verb

    Closes one of the open panes; an id that is not open is left alone.

  • added $.ui.open verb

    Opens a pane: a framed region the surface places, whose body this plugin draws by hooking ui.render for { component: "Pane" }.

Events

  • added command.describe event

    Fires once per command, when the engine lists it for the typeahead and /help; next(e) resolves to { description, argumentHint, isHidden }.

  • added command.describe result

    { description, argumentHint, isHidden }.

  • added command.list event

    The argument of $.command.list().

  • added command.register event

    The argument of $.command.register(spec).

  • added command.run event

    Fires when a slash command is about to run (/name args typed, or a plugin's $.command.run); next(e) resolves to { text }, its output.

  • added command.run result

    { text } (the command's output, when it printed one).

  • added ui.close event

    The argument of $.ui.close({ id }) with origin plugin; the engine raises it too, for the header's [x] (person) and an unload (unload).

  • added ui.open event

    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 StarOverloads type breaking

    One call signature per event * fans out to (every event but the settings hooks' PreToolUse), intersected: an overload set from EventOf and ResultOf.

  • changed Elements type extended

    The element constructors each surface draws, by e.surface: what $.ui.resolve(e) resolves to, and what a ui.resolve hook 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>;    };};
  • changed EventCalls type extended

    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>;
  • changed EventOf type may break

    The argument of each event, by event name: what a hook receives as e and what the call on $ takes. Plain data, frozen to every depth.

    export type EventOf = EngineEventOf & OpEventOf;export type EventOf = CoreEventOf & NounEventOf;
  • changed HookFor type may break

    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>;
  • changed InvalidatableEventName type may break

    What $.ui.invalidate takes: 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';
  • changed JSX namespace extended
        b: DomProps & { children?: Children }    Svg: SvgProps    Link: LinkProps & { children?: Children }    Code: CodeProps    Button: {      key?: string      label?: string
  • changed KeptEvent type may break

    What next takes in a matched hook: the variants of e the 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;
  • changed MatchedEvent type may break

    The argument a matched hook receives: e narrowed by M (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;
  • changed MatchedHook type may break

    The hook on(pattern, matcher, hook) takes: ($, e, next) with e narrowed 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>>;
  • changed MatchedNames type may break

    The events a matched registration on P covers: 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>];
  • changed MatchedResult type may break

    What a matched hook returns: the event's result, narrowed by M where 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;
  • changed Next type may break

    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>[];};
  • changed NoArgsEvent type may break

    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];
  • changed On type may break

    Registers hook on the events pattern selects: 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;};
  • changed OpEventResult type may break

    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]>;
  • changed OpValueOf type extended

    What each call on $ answers (the value of 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 RenderAnswerOptions type may break

    How a site instance asks for its ui.render answer: 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;};
  • changed RenderComponent type may break

    Everything ui.render can 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';
  • changed RenderElement type extended

    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;
  • changed RenderPropsOf type extended

    The plain-data props of each renderable component, as ui.render sees them under e.props; a hook rewrites them with next({ ...e, props }).

            isWorking: boolean;        maxRows: number;    };    Pane: {        title: string;        focused: boolean;        bodyColumns: number;        scroll: PaneScroll;    };};
  • changed ResultOf type may break

    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>;};
  • changed StarNext type may break

    next in a * hook: the set of events is open at runtime, so e is unknown until next.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>[];};
  • added BaseHookInput type
  • added ClassicEventName type

    The name of a classic hook event as a function-hooks event: the settings hook's own name under classic (classic.Stop, classic.PreToolUse).

  • added ClassicEventOf type

    The classic (settings) hook events, one per classic event name: e is what the classic hook receives on stdin for that event.

  • added ClassicHookEvent type

    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.

  • added ClassicResult type

    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.

  • added ClassicResultOf type

    What each classic hook event's hook returns and its next(e) resolves to: the event's own subset of ClassicResult.

  • added CodeProps type

    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.describe hook returns: the description, hint and hidden flag the menu uses, the name and immediate left where they were.

  • added CommandInfo type

    One slash command as $.command.list() returns it.

  • added CommandRunArgs type

    command.run's input as a plugin's $.command.run takes it: origin is the engine's to set (the calling plugin's name).

  • added CommandRunInput type

    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.

  • added CommandRunResult type

    What a command.run hook returns and what next(e) and $.command.run resolve to: the command's output text, when it has one.

  • added CommandSource type

    Where a slash command comes from, as $.command.list() tells them apart.

  • added CommandSpec type

    What $.command.register takes: the slash command this plugin serves.

  • added CoreEventName type

    The name of an event the engine defines itself (a key of CoreEventOf): what EVENT_NAMES lists; EventName adds the declared plugin nouns' events.

  • added CoreEventOf type

    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.

  • added ExitReason type
  • added Glob type

    Every event (*), or every event under a namespace (classic.*: each one whose name starts with classic.).

  • added GlobHook type

    The hook on(pattern, hook) takes for a glob or a negation: one function placed on every selected event, e and the result typed as their union.

  • added GlobNext type

    next in a hook on a glob or a negation: an overload per selected event, then one over their union for an e not yet narrowed.

  • added GlobNextResult type

    What next(e) resolves to in a glob hook before e is narrowed: the NextResult of each selected event, as a union.

  • added HookInput type
  • added LateOverload type

    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.

  • added Namespace type

    The prefixes a glob may name: one or more whole leading segments of an event name (tool of tool.call), derived, plugin nouns included.

  • added Negation type

    ! before a name or a glob: every event except the ones it selects. !* would select none, so it is no pattern.

  • added NounEvent type

    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.

  • added NounEventName type

    The name of a declared plugin noun's event (voice.speak).

  • added NounEventOf type

    The events of the plugin nouns declared on EngineInterface, by name: the argument of each <noun>.<method>. Empty until a plugin declares a noun.

  • added NounEventResult type

    The result of a declared plugin noun's event as its hooks see it: { value } (the method's answer) or { deny }.

  • added NounEventRow type

    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).

  • added NounValueOf type

    What each declared plugin noun's method answers (the value of its event's result), by event name.

  • added OrderedOverloads type

    One call signature per event in Names, intersected, the ambiguous ones (LateOverload) after the rest: next for a hook covering several events.

  • added PaneCloseArgs type

    The argument of $.ui.close: the pane to close ({ id }). origin is the engine's to set: a plugin's call reads plugin at the hooks.

  • added PaneCloseInput type

    The input of ui.close: the pane closing and why (PaneCloseOrigin). Closing an id that is not open does nothing.

  • added PaneCloseOrigin type

    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.

  • added PaneOpenArgs type

    The argument of $.ui.open: which pane, its title, and whether the plugin asks the person's keyboard for it.

  • added PaneScroll type

    Where 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.

  • added Pattern type

    What on(pattern, hook) and next.is(pattern, e) take: an event's name, a glob (*, classic.*), or a negation of either (!tool.describe).

  • added PatternOver type

    The patterns next.is takes in a hook covering the events N: their names, *, a glob over one of their namespaces, or a negation.

  • added PermissionMode type

    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.PermissionRequest answer's decision, as the classic hook's hookSpecificOutput.decision: allow (with a rewrite or rules) or deny.

  • added PermissionUpdate type
  • The permission rules a PermissionRequest allow may add: the shape of the request's own permission_suggestions (the SDK's PermissionUpdate list).

  • added PluginNoun type

    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.

  • added Selected type

    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.

  • added SetupHookInput type
  • added StopHookInput type
  • added TraceEntry type

    One settled run of a link beneath the caller, as next.trace lists it: data, not a handle.

  • added TraceOutcome type

    What the chain decided for one link, as next.trace names it.

  • added ValueOrDeny type

    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
  • $.fs noun

    The file system as the engine's own process reaches it, text only (UTF-8); a relative path is under the session's working directory.

  • Reads the named instruction files in every directory above the session's original working directory, the way the engine reads CLAUDE.md.

  • Returns whether the path exists; never rejects.

  • $.fs.listDir verb

    Lists a directory: { name, kind, size } per entry, by name.

  • $.fs.readFile verb

    Reads a file and returns its text. Rejects when missing.

  • $.fs.writeFile verb

    Writes text to a file, creating it and its directories as needed.

  • $.ui noun

    Display: a line under an open dialog, a redraw request, a transcript line, a pane the surface places.

  • Re-runs an event whose results the engine caches: ui.render draws the instances this plugin may draw again; the others drop the cached answers.

  • The hook on("*", hook) takes: it runs on every event, plugin nouns no declaration names included, so e is unknown and next is StarNext.

  • The props of Button, every surface's pressable leaf: an address, a label, and the closure a press runs.

  • The name of an event: a key of EventOf, the engine's own (CoreEventName) and the declared plugin nouns' (NounEventName).

  • The argument of $.fs.ancestors: the file names to look for in each directory, and the file to walk down to.

  • The decision of a classic.PreToolUse result: allow, ask, deny, or none.

  • What a classic.PreToolUse hook returns: one of allow, ask, deny, or none of them, which passes the call on to the normal permission flow.

  • One tool_result block of a user message.

  • One 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.261

v2.1.261

+0 added · 0 removed · 0 changed · 2 docs only since 2.1.260
2 docs only: reworded, same shape
  • Commands on the host, run as the user the session runs as. CLI only.

  • Where a render event's component is drawn: terminal is Ink, which draws the hook's whole tree; desktop is a surface that draws its own DOM.

Feedback