Follow Discord
Sweep 06 Nov 2026 · 01:32Z Build v2.1.319 537 read Stable v2.1.311 Latest v2.1.318 Next v2.1.319 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.

Releases55 since 2.1.259
Newest2.1.319+9 added · 4 removed · 3 changed · 3 docs only
Machine readablechanges.jsonin pages: counts, and each release's items

v2.1.270

No change to the surface since 2.1.269

v2.1.269

+60 added · 0 removed · 16 changed · 22 docs only since 2.1.268

The engine

  • added $.config noun

    Every row of the settings menu (/config), the panel's own and each enabled plugin's userConfig fields alike: listing and changing them.

  • added $.config.list verb

    Returns the rows the /config menu would draw now, in its order, each with its current value, its kind, its owner and its lock.

  • added $.config.set verb

    Changes one row as if the person did in the menu: the event config.set with origin { kind: 'plugin', name }, then the writer.

  • Returns every surface the session draws on, each once: terminal under the REPL first, then desktop and mobile in the order they attached.

  • added $.tool.check verb

    Asks the engine's permission decision for a tool call now: the event tool.check, resolved to { decision, reason?, rule? }.

Events

  • added config.describe event

    Fires once per /config row, when the menu lists it and for $.config.list; next(e) resolves to { label, description, isHidden }.

  • added config.describe result

    { label, description, isHidden }.

  • added config.list event

    The argument of $.config.list().

  • added config.set event

    Fires when a /config row is about to change, from the menu or a plugin's $.config.set; next(e) resolves to { value } once written.

  • added config.set result

    { value } once written, or { deny }.

  • added plugin.register event

    Fires once per hooks module about to join the chain, at load (the set folded and built, nothing swapped in) and at reload; core allows.

  • added plugin.register result

    { allow: true }, or { refuse }.

  • added session.attach event

    Fires when a remote client joins the session's roster of attached surfaces: it said so (ui_attach), or it first asked to draw.

  • added session.attach result

    { clientId }.

  • added session.detach event

    Fires when a client leaves the roster: it detached, or the session ended with it attached (e.reason). Observe; next(e) echoes { clientId }.

  • added session.detach result

    { clientId }.

  • added session.surfaces event

    The argument of $.session.surfaces().

  • added tool.check event

    Fires when the engine decides whether a tool call may run, after the tool.call and PreToolUse hooks and before the mode settles an ask.

  • added tool.check result

    { decision, reason?, rule? }.

Types

  • changed AgentSpawnArgs type may break

    agent.spawn's input as the call takes it: what the Agent tool's caller says; the engine fills the rest.

    export type AgentSpawnArgs = Pick<AgentSpawnInput, 'prompt'> & Partial<Pick<AgentSpawnInput, 'description' | 'subagentType' | 'model' | 'name' | 'cwd' | 'background'>>;export type AgentSpawnArgs = Pick<AgentSpawnInput, 'prompt'> & Partial<Pick<AgentSpawnInput, 'description' | 'subagentType' | 'model' | 'name' | 'cwd'>>;
  • changed AgentSpawnResult type may break

    What an agent.spawn hook returns and what next(e) resolves to: the started subagent's { model, agentId }, or { deny: reason }, refusing it.

    export type AgentSpawnResult = {    model: string;    text?: string;    isError?: true;    agentId?: string;    deny?: undefined;} | {    deny: string;    model?: undefined;    text?: undefined;    isError?: undefined;    agentId?: undefined;};
  • changed CatchHandler type may break

    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.

    export type CatchHandler<F> = F extends ($: infer D, e: infer E, next: infer N) => infer R ? ($: D, e: E, next: N & Caught) => R | undefined | Promise<Awaited<R> | undefined> : never;export type CatchHandler<F> = F extends ($: infer D, e: infer E, next: infer N) => infer R ? [R] extends [AsyncGenerator<unknown, unknown, unknown>] ? ($: D, e: E, next: N & Caught) => R : ($: D, e: E, next: N & Caught) => R | undefined | Promise<Awaited<R> | undefined> : never;
  • changed CommandRunArgs type may break

    command.run's input as a plugin's $.command.run takes it: args may be left out (/command, bare), and origin is the engine's to set.

    export type CommandRunArgs = Omit<CommandRunInput, 'origin'>;export type CommandRunArgs = Omit<CommandRunInput, 'origin' | 'args'> & {    args?: string;};
  • changed EventCalls type may break

    The engine's own events as calls on $, one signature each: $.<noun>.<event>(input) resolves to its result, or to its stream.

    export type EventCalls = {    tool: {        call: ToolCallOverloads;        check: (input: ToolCheckArgs) => Promise<ToolCheckResult>;        describe: (input: ToolDescribeInput) => Promise<ToolDescribeResult>;    };    command: {        run: (input: CommandRunArgs) => Promise<CommandRunResult>;        describe: (input: CommandDescribeInput) => Promise<CommandDescribeResult>;    };    config: {        set: (input: ConfigSetArgs) => Promise<ConfigSetResult>;        describe: (input: ConfigDescribeInput) => Promise<ConfigDescribeResult>;    };    prompt: {        submit: (input: PromptSubmitArgs) => Promise<PromptSubmitResult>;…        start: (input: SessionStartInput) => Promise<SessionStartResult>;        receive: (input: SessionReceiveInput) => Promise<SessionReceiveResult>;        compact: (input?: SessionCompactArgs) => Promise<SessionCompactResult>;        attach: (input: SessionAttachInput) => Promise<SessionAttachResult>;        detach: (input: SessionDetachInput) => Promise<SessionDetachResult>;    };    turn: {        start: (input: TurnStartInput) => Promise<TurnStartResult>;        step: (input: TurnStepInput) => Promise<TurnStepResult>;        step: (input: TurnStepInput) => HookStream<TurnStepChunk, TurnStepResult>;        complete: (input: TurnCompleteInput) => Promise<TurnCompleteResult>;    };    ui: {
  • changed Events type may break

    The hook signature of each event, ($, e, next), as one mapped type over EventOf; a streaming event's is the generator form (StreamHook).

    export type Events = {    [E in keyof EventOf]: ($: E extends 'engine.create' ? NoEngineInterface : EngineInterface, e: Frozen<Args<E>>, next: Next<E>) => EventResult<E> | Promise<EventResult<E>>;    [E in keyof EventOf]: E extends StreamingEventName ? StreamHook<E> : ($: E extends 'engine.create' ? NoEngineInterface : EngineInterface, e: Frozen<Args<E>>, next: Next<E>) => EventResult<E> | Promise<EventResult<E>>;};
  • changed InvalidatableEventName type may break

    What $.ui.invalidate takes: a render event, or one of the five events whose answers the engine caches for the session.

    export type InvalidatableEventName = RenderEventName | 'prompt.section' | 'prompt.context' | 'tool.describe' | 'command.describe';export type InvalidatableEventName = RenderEventName | 'prompt.section' | 'prompt.context' | 'tool.describe' | 'command.describe' | 'config.describe';
  • 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<P extends Pattern, M> = ($: EngineInterface, e: Frozen<MatchedEvent<P, M>>, next: Next<MatchedNames<P, M>, KeptEvent<P, M>, MatchedResult<P, M>, {export type MatchedHook<P extends Pattern, M> = P extends StreamingEventName ? MatchedStreamHook<P, M> : ($: EngineInterface, e: Frozen<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 OpValueOf type extended

    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.surfaces': readonly RenderSurface[];    'session.authorize': SessionAuthorization;    'session.usage': SessionUsage;    'turn.abort': void;…    'command.register': {        command: string;    };    'config.list': ConfigRow[];    'agent.list': AgentInfo[];    'ui.toast': void;    'ui.status': void;
  • changed PromptSubmitArgs type may break

    prompt.submit's input as a plugin's call takes it: origin, turnId and wait are the engine's to set, context the hooks' to attach.

    export type PromptSubmitArgs = Omit<PromptSubmitInput, 'origin' | 'turnId' | 'wait'>;export type PromptSubmitArgs = Omit<PromptSubmitInput, 'origin' | 'turnId' | 'wait' | 'context'>;
  • changed PromptSubmitInput type extended

    The input of prompt.submit: the prompt as typed, after the input became a user message and before it enters the session.

    export type PromptSubmitInput = {    text: string;    attachments?: readonly PromptAttachment[];    context?: readonly string[];    turnId?: string;    wait: boolean;    origin: PromptOrigin;
  • 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 }).

            title: string;        isFocused: boolean;        bodyColumns: number;        placement: 'dock' | 'inline';        scroll: SiteScroll;    };};
  • changed TraceEntry type extended

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

        readonly outcome: TraceOutcome;    readonly reason?: string;    readonly ms: number;    readonly chunks?: number;    readonly received: E;    readonly returned: O | undefined;};
  • changed TurnCompleteFields type extended

    What every turn.complete carries whatever its reason: the answer, the duration, the interrupt flag, the turn's id, its loop and what it cost.

        durationMs: number;    isAborted: boolean;    turnId: string;    agentId?: string;    usage?: TurnUsage;};
  • changed TurnStepInput type may break

    The input of turn.step: one model request inside a turn, at the moment the engine is about to send it; the bottom of the chain sends it.

    export type TurnStepInput = {    turnId: string;    index: number;    answer: string;    toolUses: readonly TurnStepToolUse[];    stopReason: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use' | 'pause_turn' | 'compaction' | 'refusal' | 'model_context_window_exceeded';    usage?: TurnUsage;    model: string;    effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | number;    messageCount: number;};
  • changed TurnStepResult type may break

    What a turn.step hook returns and what next(e) resolves to: the model's response to the step's request, once its blocks are all in.

    export type TurnStepResult = {    turnId: string;    index: number;    usage?: TurnUsage;    answer: string;    toolUses: readonly TurnStepToolUse[];    stopReason: TurnStopReason;    usage: TurnUsage | null;};
  • added Chunk type

    What a hook on streaming event N yields, and what its next(e) yields to it (ChunkOf by name).

  • added ChunkOf type

    The chunk type of each streaming event, by name: what its stream yields.

  • added ChunkRef type

    What every turn.step chunk may carry: the engine's handle on the item of its own stream the chunk was read off, absent on a chunk a hook made.

  • The input of config.describe: how one /config row presents, at the moment the menu lists it (and for $.config.list).

  • What a config.describe hook returns: the label, help text and hidden flag the menu uses; the key and provider stay as they were.

  • added ConfigKind type

    How a /config row takes its value: boolean toggles, choice picks one of its options, text takes a string, number a number.

  • added ConfigOrigin type

    Where a config.set came from, in prompt.submit's words: the person in the /config menu (composer), the bridge, or a plugin, named.

  • added ConfigRow type

    One /config row as $.config.list() returns it: what the menu would draw now, after every config.describe hook, a hidden row left out.

  • added ConfigSetArgs type

    config.set's input as a plugin's $.config.set(args) takes it: the row's key and the value; the engine fills the rest.

  • added ConfigSetInput type

    The input of config.set: one /config row about to change, from the menu or a plugin's $.config.set, with what it holds now and who owns it.

  • added ConfigSetResult type

    What a config.set hook returns and what next(e) resolves to: { value } once written, or { deny: reason }, the row left as it was.

  • added ConfigValue type

    A /config row's value as a hook and $.config see it: a toggle's boolean, a choice's or a text's string, a number, or a list of strings.

  • added HookOf type

    The hook event E takes: an async generator over its chunks for a streaming event (turn.step), ($, e, next) => result for every other.

  • added HookStream type

    What next(e) returns on a streaming event: the stream of everything beneath, chunk by chunk, whose return value is the result from beneath.

  • The hook on(event, matcher, hook) takes on a streaming event: the generator form, e narrowed by the matcher, next(e) the stream beneath.

  • What a matched streaming hook's next.is(pattern, e) narrows e to: the streaming event's argument narrowed by the matcher.

  • A matched streaming hook's next: the variants the matcher keeps, the result tagged the same way, next.is narrowing as the matcher does.

  • The input of plugin.register: one hooks module the engine is about to load, read off its manifest and its scanned source. Every field is pinned.

  • What a plugin.register hook returns and what next(e) resolves to: { allow: true } from core, or { refuse: reason }.

  • What a hooks module uses, as the host scanned its source before loading it: the same lists claude plugin validate prints and the host's rule reads.

  • The input of session.attach: a surface joined the session's roster of attached clients (a phone opened the session; the desktop app connected).

  • What a session.attach hook returns and what next(e) resolves to: { clientId }, echoed by core; a hook's own value changes nothing.

  • The input of session.detach: a client left the session's roster. Every field is the engine's: a hook observes, and next(e) passes them on.

  • Why a client left the roster: detach, it said so (ui_detach); end, the session ended with it still attached.

  • What a session.detach hook returns and what next(e) resolves to: { clientId }, echoed by core; a hook's own value changes nothing.

  • added StreamHook type

    The hook a streaming event takes: async function* ($, e, next) {}, yielding the event's chunks and returning its result.

  • added StreamHookBody type

    What a hook on a streaming event evaluates to: the async generator an async function* makes, yielding C and returning R or nothing.

  • added StreamNext type

    The rest of the chain as a hook on a streaming event receives it: Next, except that next(e) is the stream beneath (HookStream), not a promise.

  • The events that stream: their hooks are async generators, next(e) is the stream of everything beneath, and the result is what it returns.

  • added ToolCheckArgs type

    tool.check's input as $.tool.check takes it: the tool and its arguments; tool_use_id is the engine's to set, never a query's.

  • The verdict of tool.check: run the tool, put it to the mode's decider (the dialog, the auto-mode classifier, a headless host), or refuse it.

  • added ToolCheckInput type

    The input of tool.check: the tool, its arguments, and the call's id when the engine is deciding a real call.

  • added ToolCheckResult type

    What a tool.check hook returns and what next(e) resolves to: the verdict, why, and the settings rule behind it when one decided.

  • added TurnStepChunk type

    One piece of a turn.step response as it streams through the chain: what a hook's next(e) yields and what the hook yields up in turn.

  • An item of the engine's stream the other chunk kinds do not model (the envelope, a block's start and end, a retry marker), opaque by ref.

  • A piece of a tool call's arguments as they arrive: JSON text, partial, for the tool chunk of the same index.

  • The response is whole: why the model stopped and what the request cost, as the step's result will carry them. The last chunk of one response.

  • A piece of the response's visible text as it arrives, in block index.

  • A piece of the model's thinking as it arrives, in block index: what the person sees of it live.

  • The model begins a tool call in block index: the tool's name and the call's id; its arguments follow as input chunks of the same index.

  • added TurnStopReason type

    Why the model stopped, as a turn.step result and its stop chunk carry it: one of the API's stop reasons, or null when no response arrived.

22 docs only: reworded, same shape
  • Spawns a subagent: the event agent.spawn, the same call the engine makes when the Agent tool starts one; the engine fills the rest.

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

  • Returns the first of $.session.surfaces(), or null where nothing draws.

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

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

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

  • turn.step event

    Fires when the engine is about to send one model request of the main thread's turn; next(e) sends it and resolves to the whole response.

  • turn.step result

    The response: { turnId, index, answer, toolUses, stopReason, usage }.

  • ui.close event

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

  • The component a surface module exports (default, or its one PascalCase export): from props and surface to the tree drawn (no nested Client).

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

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

  • The element constructors each surface draws, by e.surface: what $.ui.resolve(e) returns and a ui.resolve hook passes on; no globals.

  • Why a pane closes, as the engine stamped it at ui.close.

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

  • What a prompt.submit hook returns and what next(e) resolves to: the prompt that entered, { text, context?, origin? }, or { drop: reason }.

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

  • One ui.render input, for a component narrowed to one surface.

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

  • The input of session.start: the session the process starts with, read the way $.session reads it at that moment.

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

  • The argument of ui.message: what a Client instance's surface module posted (surface.post(data)), addressed by where the instance is drawn.

v2.1.268

+24 added · 6 removed · 53 changed · 46 docs only since 2.1.267

The engine

  • removed $.session.turnCount verb breaking

    Returns how many prompts the user has sent this session (user turns in the transcript).

  • changed $.model.fork verb may break

    Runs one tool-less completion over the session's OWN transcript, sharing the main thread's prompt cache; the reply's text and the fork's usage.

    (request: ModelForkRequest) => Promise<ModelForkReply | null>(request: ModelForkRequest) => Promise<ModelForkResult | null>
  • changed $.ui.ask verb may break

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

    (question: string, options?: readonly string[] | AskProps) => Promise<string>(question: string, options?: readonly string[] | AskOptions) => Promise<string>
  • added $.prompt.fill verb

    Writes input.text into the prompt box as the person's draft, cursor at its end, replacing what it held: the event prompt.fill.

  • added $.prompt.suggest verb

    Proposes input.text as the prompt box's dim suggestion, Tab to take: the event prompt.suggest, as the engine's own guess after a turn.

  • Holds the session's Anthropic credential on the host and answers an opaque handle and its kind; the secret never reaches the plugin.

  • added $.session.turns verb

    Returns how many prompts the user has sent this session (user turns in the transcript).

Events

  • removed session.turnCount event breaking

    The argument of $.session.turnCount().

  • changed ui.press event may break

    Fires when a Button a render hook drew is pressed on a surface; e is { plugin, element, component, surface }, element the button's key.

    UiPressInputUiPressArgument
  • added prompt.fill event

    Fires when a text is about to be written into the prompt box as the person's draft (a plugin's $.prompt.fill); next(e) writes it.

  • added prompt.fill result

    { isFilled }.

  • added prompt.suggest event

    Fires when a text is proposed as the prompt box's dim suggestion, Tab to take: the engine's guess after a turn, or a plugin's $.prompt.suggest.

  • added prompt.suggest result

    { isShown }.

  • added session.authorize event

    The argument of $.session.authorize().

  • added session.turns event

    The argument of $.session.turns().

  • added tool.register event

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

Types

  • removed AskProps type breaking

    Options of $.ui.ask.

  • removed ModelForkReply type breaking

    What $.model.fork resolves to when the fork answered: the reply's text and what the fork cost.

  • removed RenderAnswerOptions type breaking

    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.

  • removed UiPressInput type breaking

    The argument of ui.press: a press on a Button a render hook drew. Flat and frozen like every event's.

  • changed CommandDescribeInput type may break

    The input of command.describe: how one slash command presents in the typeahead and /help, at the moment the engine lists it.

    type CommandDescribeInput = {export type CommandDescribeInput = {    command: string;    description: string;    argumentHint?: string;
  • 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' | 'provider'>;export type CommandDescribeResult = Omit<CommandDescribeInput, 'command' | 'immediate' | 'provider'>;
  • changed CommandInfo type may break

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

    type CommandInfo = {export type CommandInfo = {    name: string;    description: string;    source: CommandSource;
  • changed CommandRunArgs type may break

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

    type CommandRunArgs = Omit<CommandRunInput, 'origin'>;export type CommandRunArgs = Omit<CommandRunInput, 'origin'>;
  • changed CommandRunInput type may break

    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.

    type CommandRunInput = {export type CommandRunInput = {    command: string;    args: string;    origin: PromptOrigin;
  • changed CommandRunResult type may break

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

    type CommandRunResult = {export type CommandRunResult = {    text?: string;    ref?: number;};
  • changed CommandSource type may break

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

    type CommandSource = 'builtin' | 'plugin' | 'user' | 'mcp';export type CommandSource = 'builtin' | 'plugin' | 'user' | 'mcp';
  • changed CommandSpec type may break

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

    type CommandSpec = {export type CommandSpec = {    name: string;    description: string;    argumentHint?: string;
  • changed Elements type extended

    The element constructors each surface draws, by e.surface: what $.ui.resolve(e) returns and a ui.resolve hook passes on; no globals.

            Code: ElementConstructor<CodeProps>;        Client: ElementConstructor<ClientProps>;    };    mobile: {        Box: ElementConstructor<BoxProps>;        Text: ElementConstructor<TextProps>;        Button: ElementConstructor<ButtonProps>;        Svg: ElementConstructor<SvgProps>;        Link: ElementConstructor<LinkProps>;        Code: ElementConstructor<CodeProps>;    };};
  • changed EventCalls type extended

    The engine's own events as calls on $, one signature each: $.<noun>.<event>(input) dispatches the event and resolves to its result.

        };    prompt: {        submit: (input: PromptSubmitArgs) => Promise<PromptSubmitResult>;        fill: (input: PromptFillArgs) => Promise<PromptFillResult>;        suggest: (input: PromptSuggestArgs) => Promise<PromptSuggestResult>;        section: (input: PromptSectionInput) => Promise<PromptSectionResult>;        context: (input: PromptContextInput) => Promise<PromptContextResult>;    };
  • changed Events type may break

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

    export type Events = {    [E in keyof EventOf]: ($: E extends 'engine.create' ? NoEngineInterface : EngineInterface, e: Args<E>, next: Next<E>) => EventResult<E> | Promise<EventResult<E>>;    [E in keyof EventOf]: ($: E extends 'engine.create' ? NoEngineInterface : EngineInterface, e: Frozen<Args<E>>, next: Next<E>) => EventResult<E> | Promise<EventResult<E>>;};
  • changed GlobHook type may break

    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.

    export type GlobHook<P extends Pattern, N extends EventName = Selected<P>> = ($: EngineInterface, e: Args<N>, next: GlobNext<P>) => EventResult<N> | Promise<EventResult<N>>;export type GlobHook<P extends Pattern, N extends EventName = Selected<P>> = ($: EngineInterface, e: Frozen<Args<N>>, next: GlobNext<P>) => EventResult<N> | Promise<EventResult<N>>;
  • 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.

        (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 is: <M extends PatternOver<N>>(pattern: M, e: unknown) => e is Frozen<Args<Extract<N, Selected<M>>>>;    readonly event: N;    readonly origin: Origin;    readonly trace: readonly TraceEntry<N, Args<N>, GlobNextResult<N>>[];
  • changed HttpInit type extended

    Options of $.http.fetch.

        method?: string;    headers?: Record<string, string>;    body?: string;    auth?: string;};
  • 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<P extends Pattern, M> = ($: EngineInterface, e: MatchedEvent<P, M>, next: Next<MatchedNames<P, M>, KeptEvent<P, M>, MatchedResult<P, M>, {export type MatchedHook<P extends Pattern, M> = ($: EngineInterface, e: Frozen<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 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.

            (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 is: <M extends PatternOver<N>>(pattern: M, e: unknown) => e is Frozen<S[Extract<N, Selected<M>>]>;    readonly event: N;    readonly origin: Origin;    readonly trace: readonly TraceEntry<N, E, O>[];
  • changed OpValueOf type may break

    What each call on $ answers (the value of its event's result), by event name.

    export type OpValueOf = {    'model.complete': string;    'model.classify': string | undefined;    'model.fork': ModelForkReply | null;    'model.fork': ModelForkResult | null;    'audio.play': void;    'audio.speak': SpeakResult;    'mcp.call': McpToolResult;    'session.cwd': string;    'session.model': string;    'session.turnCount': number;    'session.turns': number;    'session.id': string;    'session.messages': SessionMessage[];    'session.repo': SessionRepo | null;    'session.surface': RenderSurface | null;    'session.authorize': SessionAuthorization;    'session.usage': SessionUsage;    'turn.abort': void;    'tool.list': ToolInfo[];
  • changed Origin type may break

    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.

    type Origin = {export type Origin = {    readonly plugin: string;    readonly tier: Tier;};
  • changed PaneCloseArgs type may break

    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.

    type PaneCloseArgs = Omit<PaneCloseInput, 'origin'>;export type PaneCloseArgs = Omit<PaneCloseInput, 'origin'>;
  • changed PaneCloseInput type may break

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

    type PaneCloseInput = {export type PaneCloseInput = {    id: string;    origin: PaneCloseOrigin;};
  • changed PaneCloseOrigin type may break

    Why a pane closes, as the engine stamped it at ui.close.

    type PaneCloseOrigin = 'plugin' | 'person' | 'unload';export type PaneCloseOrigin = {    kind: 'plugin' | 'person' | 'unload';};
  • changed PaneOpenArgs type may break

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

    type PaneOpenArgs = {export type PaneOpenArgs = {    id: string;    title?: string;    focus?: true;
  • changed ProcessRunInit type may break

    Options of $.process.run.

    type ProcessRunInit = {export type ProcessRunInit = {    cwd?: string;    env?: Record<string, string>;    stdin?: string;
  • changed ProcessRunResult type may break

    What $.process.run resolves with once the child has exited.

    type ProcessRunResult = {export type ProcessRunResult = {    exitCode: number;    stdout: string;    stderr: string;
  • changed RenderChildren type may break

    What an element takes as children, as JSX passes them: a node, a number (drawn as its string), a value the factory drops, or a list that may nest.

    export type RenderChildren = RenderNode | readonly RenderChildren[];export type RenderChildren = RenderNode | number | boolean | null | undefined | readonly RenderChildren[];
  • 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 }).

    export type RenderPropsOf = {    AskUserQuestion: {        toolName: string;        tool: string;        questions: unknown[];        metadataSource?: string;    };…    };    AssistantMessage: {        text: string;        firstOfReply: boolean;        isFirstOfReply: boolean;    };    ToolUse: {        tool_use_id: string;        toolName: string;        tool: string;        input: unknown;        running: boolean;        errored: boolean;        interrupted: boolean;        isRunning: boolean;        isErrored: boolean;        isInterrupted: boolean;        output?: unknown;    };    ToolResult: {        tool_use_id: string;        toolName: string;        tool: string;        output: unknown;        errored: boolean;        isErrored: boolean;    };    ToolGroup: {        calls: ReadonlyArray<ToolGroupCall>;        active: boolean;        expanded: boolean;        isActive: boolean;        isExpanded: boolean;    };    Spinner: {        word: string;…    };    Pane: {        title: string;        focused: boolean;        isFocused: boolean;        bodyColumns: number;        scroll: SiteScroll;    };
  • changed RenderSurface type may break

    Where a render event's component is drawn: terminal is Ink, which draws the hook's whole tree; desktop (Claude Code Desktop) and mobile (the Claude mobile app) are remote surfaces that draw the tree themselves.

    export type RenderSurface = 'terminal' | 'desktop';export type RenderSurface = 'terminal' | 'desktop' | 'mobile';
  • 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' | 'cloud_credential_error';type SDKAssistantMessageError = 'authentication_failed' | 'oauth_org_not_allowed' | 'account_on_hold' | 'verification_required' | 'billing_error' | 'rate_limit' | 'overloaded' | 'invalid_request' | 'model_not_found' | 'server_error' | 'unknown' | 'max_output_tokens' | 'cloud_credential_error';
  • changed SessionContextUsage type may break

    The live context window as the status line reads it: the last API response's input side against the model's window.

    type SessionContextUsage = {export type SessionContextUsage = {    tokens?: number;    window: number;    percent?: number;
  • changed SessionCost type may break

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

    type SessionCost = {export type SessionCost = {    usd: number;};
  • changed SessionRateLimit type may break

    One rate-limit window as the rate-limit notices read it.

    type SessionRateLimit = {export type SessionRateLimit = {    kind: string;    percentUsed: number;    resetsAt?: string;
  • changed SessionReceiveOrigin type may break

    Where an inbound delivery came from, as the bridge classified it from the server's stamps: prompt.submit's e.origin, less what never arrives.

    export type SessionReceiveOrigin = 'bridge' | 'task-notification' | 'scheduled-trigger' | 'peer' | 'peer-send-message' | 'projects-relay' | 'slack-ping' | 'unclassified';export type SessionReceiveOrigin = {    kind: 'bridge' | 'task-notification' | 'scheduled-trigger' | 'peer' | 'peer-send-message' | 'projects-relay' | 'slack-ping' | 'unclassified';};
  • changed SessionStartInput type may break

    The input of session.start: the session the process starts with, read the way $.session reads it at that moment.

    export type SessionStartInput = {    cwd: string;    surface: RenderSurface | null;    interactive: boolean;    isInteractive: boolean;};
  • changed SessionUsage type may break

    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.

    type SessionUsage = {export type SessionUsage = {    context: SessionContextUsage;    rateLimits: SessionRateLimit[];    cost?: SessionCost;
  • changed Settings type may break

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

    type Settings = Readonly<Record<string, unknown>>;export type Settings = Readonly<Record<string, unknown>>;
  • changed SettingsReadArgs type may break

    What $.settings.read(args) takes.

    type SettingsReadArgs = {export type SettingsReadArgs = {    source?: SettingsSource;};
  • changed SettingsSource type may break

    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.

    type SettingsSource = 'user' | 'project' | 'local' | 'flag' | 'policy';export type SettingsSource = 'user' | 'project' | 'local' | 'flag' | 'policy';
  • changed SiteScroll type may break

    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.

    type SiteScroll = {export type SiteScroll = {    offset: number;    bodyRows: number;};
  • 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.

        (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 is: <M extends Pattern>(pattern: M, e: unknown) => e is Frozen<Args<Selected<M>>>;    readonly event: EventName;    readonly origin: Origin;    readonly trace: readonly TraceEntry<EventName, unknown, unknown>[];
  • changed SvgProps type may break

    The props of Svg, the desktop and mobile surfaces' vector leaf: the markup is the element's data, as a string is a Text's, drawn isolated.

        alt: string;    width?: number;    height?: number;    interactive?: boolean;    isInteractive?: boolean;};
  • changed ToolGroupCall type may break

    One tool call of a ToolGroup, as ui.render sees it under calls.

    export type ToolGroupCall = {    tool_use_id?: string;    toolName: string;    tool: string;    input: unknown;    running: boolean;    errored: boolean;    interrupted: boolean;    isRunning: boolean;    isErrored: boolean;    isInterrupted: boolean;    output?: unknown;};
  • changed ToolResultSummary type may break

    One tool_result block of a user message.

    export type ToolResultSummary = {    id: string;    tool_use_id: string;    text: string;    isError: boolean;    result?: unknown;
  • changed ToolUseSummary type may break

    One tool_use block of an assistant message, with its outcome once the transcript holds the call's tool_result (paired by tool_use_id).

    export type ToolUseSummary = {    id: string;    name: string;    tool_use_id: string;    tool: string;    input: Record<string, unknown>;    result?: unknown;    text?: string;
  • changed TraceOutcome type may break

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

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

    What every turn.complete carries whatever its reason: the answer, the duration, the interrupt flag, the turn's id and what the turn cost.

    type TurnCompleteFields = {    answer: string;    durationMs: number;    aborted: boolean;    isAborted: boolean;    turnId: string;    usage?: TurnUsage;};
  • changed TurnUsage type may break

    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.

    type TurnUsage = ModelForkUsage & {export type TurnUsage = ModelForkUsage & {    model: string;};
  • changed UiInputArgument type may break

    The argument of ui.input: a change of, or a submit from, an Input a render hook drew. Flat and frozen like every event's.

    type UiInputArgument = {export type UiInputArgument = {    plugin: string;    element: string;    component: RenderComponent;    requestId: string;    surface: RenderSurface;    kind: 'change' | 'submit';    value: string;
  • changed UiInputResult type may break

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

    type UiInputResult = {export type UiInputResult = {    element: string;    value: string;};
  • changed UiSelectArgument type may break

    The argument of ui.select: a pick from a Select a render hook drew. Flat and frozen like every event's.

    type UiSelectArgument = {export type UiSelectArgument = {    plugin: string;    element: string;    component: RenderComponent;    requestId: string;    surface: RenderSurface;    value: string;};
  • changed UiSelectResult type may break

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

    type UiSelectResult = {export type UiSelectResult = {    element: string;    value: string;};
  • added AskOptions type

    Options of $.ui.ask.

  • added Frozen type

    T with every property read-only to every depth, arrays and tuples kept as declared: how a hook's e is typed.

  • added ModelForkResult type

    What $.model.fork resolves to when the fork answered: the reply's text and what the fork cost.

  • added PromptFillArgs type

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

  • added PromptFillInput type

    The input of prompt.fill (prompt-fill/): a text about to be written into the prompt box as the person's draft, replacing what the box holds.

  • added PromptFillOrigin type

    Who writes the prompt box at prompt.fill, as the engine stamps it where the write starts; a closed set a matcher narrows on.

  • added PromptFillResult type

    What a prompt.fill hook returns and what next(e) resolves to: whether the text went into the prompt box.

  • prompt.suggest's input as a plugin's $.prompt.suggest(args) takes it: origin is the engine's to set (the calling plugin's name).

  • The input of prompt.suggest (prompt-suggest/): a text about to be shown dim in the empty prompt box, for Tab (or the right arrow) to take.

  • Who proposes the text at prompt.suggest, as the engine stamps it where the proposal starts; a closed set a matcher narrows on.

  • What a prompt.suggest hook returns and what next(e) resolves to: whether the text is now the box's dim suggestion.

  • What $.session.authorize() answers: an opaque handle for the session's Anthropic credential and its kind, or null when there is none to hold.

  • added UiPressArgument type

    The argument of ui.press: a press on a Button a render hook drew. Flat and frozen like every event's.

46 docs only: reworded, same shape
  • Spawns a subagent: the event agent.spawn, the same call the engine makes when the Agent tool starts one; the engine fills the rest.

  • Plays one audio clip, starting now; clips are not queued, so two calls play together (a bed under speech).

  • Speaks text with the platform's own synthesizer (say on macOS). Plain text.

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

  • Fetches url through the host (never the plugin's own network) and resolves { status, ok, headers, text } once the body is read.

  • Picks one of labels for text with one completion over $.model.complete and a fixed classifier prompt.

  • Submitting a prompt the model reads as a user turn, and putting a text in the person's prompt box, written or proposed.

  • Submits a prompt: the event prompt.submit, the same call the engine makes for a typed prompt; input.text runs when the session is idle.

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

  • Returns where the session draws: terminal under the REPL, desktop or mobile once a remote surface has asked; null where nothing draws.

  • $.store noun

    This plugin's own key-value store, kept between sessions and hot reloads; values are JSON data.

  • Calls a tool: the event tool.call, the same call the engine makes for the model's tool calls, under a tool_use_id of its own.

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

  • Fires when the engine offers an agent type to the model, in the agent listing and again at dispatch; next(e) resolves to { isOffered: true }.

  • { isOffered }.

  • The input of agent.offer: one agent type, at the moment the engine offers it to the model.

  • The input of agent.spawn: what the Agent tool decided about the subagent it is about to start, before its model is resolved.

  • What $.audio.play plays: a URL the engine fetches, or the bytes.

  • The props of Box: the layout, margin, padding and border props of Ink's Box a tree may set, and the two of hover.

  • The names of the built-in tools.

  • 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 element table a surface module draws with, surface.elements: the terminal's constructors (Elements) less Client (none nests).

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

  • 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 name of an event the engine defines itself (a key of CoreEventOf); EventName adds the declared plugin nouns' events.

  • The children field every element constructor's props carry, appended beside its own props type: one child, or a list that may nest.

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

  • The input of engine.create: the fold that builds $, once per load, core innermost.

  • The props of Link, a hyperlink every surface draws: an OSC 8 span on the terminal (else its text then the URL in dim), an anchor on desktop.

  • What matches one value of type V, by the runtime's kinds: a scalar leaf takes the value or a RegExp; an object, a partial of it.

  • How many object or array levels a matcher narrows e through, counted as a tuple's length: the runtime's own limit, which refuses a deeper matcher.

  • How $.audio.play plays a clip: looped until signal aborts, or once.

  • The nouns a plugin declared on $ by merging into EngineInterface; never one the engine's own events are under (tool, session, ui).

  • Where a prompt.submit submission came from, as the engine knows it at the site it was queued from; a closed set, never a text prefix.

  • The input of prompt.submit: the prompt as typed, after the input became a user message and before the turn starts.

  • What a prompt.submit hook returns and what next(e) resolves to: the prompt that proceeds, { text, context?, origin? }, or { drop: reason }.

  • Everything ui.render can draw: one name per component that has a render site; a matcher narrows on it.

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

  • One ui.render input, for a component narrowed to one surface.

  • The size of what a surface draws into, in character cells of the surface's monospace metric: on the terminal, the screen's columns and rows; on a remote surface, the pane's width and height divided by the advance and line height of its code font. A pixel-sized companion arrives with the first element that lays out in pixels; until then every element on every surface is cell-based, and so is this.

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

  • The argument of $.audio.speak(text, options) as the event carries it.

  • What $.audio.speak resolves with once the utterance has ended.

  • The props of Text: the color and style props of Ink's Text a tree may set. Colors are a theme key or a raw color.

  • The keys tool.call's input carries beside the tool's own arguments, none of which the tool sees (the engine strips them before the tool runs).

  • What a tool.call hook returns and what next(e) and $.tool.call(input) resolve to: the tool's result ({ result, context? }) or { deny }.

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
Feedback