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 · 03 of 03

Types

Every declaration in the file, verbatim, with the doc comment above it.

Declarations663over 14 pages
Mined from2.1.319the shipped binary

Every named declaration in the file, 663 of them, verbatim and in the order Claude Code wrote them, 50 to a page. This is the page to land on from a signature: a verb that returns ToolCallResult links here, at whichever page it is on, and what you get is the declaration itself rather than a description of it.

The source is 17,469 lines of TypeScript mined from build 2.1.319. Anything longer than 12 lines is folded; click the line count to open it. To find a name on another page, search every symbol.

Names on this page, 1 to 50 of 663

AgentCallRecord type # line 109

Added in 2.1.260

What an Agent call a plugin raised ($.tool.call({ tool: "Agent", ... })) answers as result once the agent settles; text is its final answer.

A plugin's spawn always runs in the background, so the call resolves with this record and not the Agent tool's own (BuiltinToolResults), which is what a model's Agent call carries at tool.call.

  export type AgentCallRecord = {
      /**
       * The spawned agent's id, as `$.agent.list()` names it.
       */
      agentId: string;
      /**
       * The model the agent resolved to, when one is known.
       */
      resolvedModel?: string;
  };

AgentInfo type # line 124

In the first published surface (2.1.259) · changed in 2.1.267, 2.1.289

One agent loop of this session as $.agent.list() returns it: a subagent or a teammate.

60 lines
  export type AgentInfo = {
      /**
       * The agent's one id: `agent.spawn` answers it as `agentId`, its loop's
       * events carry it as `agentId`, a spawn inside it as `parentAgentId`.
       *
       * The Agent tool's record spells it `agentId` too, the classic events
       * (SubagentStart, SubagentStop, TeammateIdle) `agent_id`. A teammate in a
       * terminal pane of its own runs no loop here: its id is its address.
       */
      id: string;
      /**
       * A teammate's address in its team, `<name>@<team>`, which the roster keys
       * it by; absent for any other agent.
       *
       * It joins `agent.spawn`'s `teammateId` and the Agent tool's `teammate_id`,
       * and no event's `agentId`: that is `id`. A respawn under the name keeps it.
       */
      teammateId?: string;
      /**
       * The Agent call's own `description` of the task (a few words), as an
       * `agent.spawn` hook left it.
       */
      description: string;
      /**
       * The agent type it was spawned as, `agent.spawn`'s `subagentType`
       * (`general-purpose`, `Explore`, ...); `teammate` for one spawned as none.
       *
       * A teammate's classic events give its `name` as `agent_type`, not this.
       */
      type: string;
      /**
       * Where its loop stands now (AgentStatus): a teammate that waits for a
       * message is `idle`, not `running`.
       *
       * One in a terminal pane of its own is `running` or `idle` by what it last
       * wrote in its team's roster, at a turn's start and at its end: a pane that
       * is closed or dies leaves that word standing.
       */
      status: AgentStatus;
      /**
       * The id of the agent whose loop spawned it; absent when the main loop
       * did.
       */
      parentId?: string;
      /**
       * The plugin whose `$.agent.spawn` (or Agent `$.tool.call`) started it;
       * absent when the model or the person did.
       *
       * The origin axis: which plugin caused it, whichever loop that hook ran in.
       */
      spawnedBy?: string;
      /**
       * What SendMessage addresses it by (`Agent({ name })`, or the engine's own
       * for a background agent), when it has one; not `description` or `type`.
       *
       * A teammate's is its name in the team, with a suffix when the call's was
       * taken. Two agents may hold one name: `id` tells them apart.
       */
      name?: string;
  };

AgentLoop type # line 193

Added in 2.1.267

Which model loop an event happened in: the loop's agent id inside a subagent's or a teammate's loop, absent on the main loop.

The agent axis, not the origin axis: next.origin names the plugin whose hook frame caused the dispatch (the recursion skip), whichever loop it ran in; agentId names the loop, whoever caused the call.

  export type AgentLoop = {
      /**
       * The id of the loop this call runs in: for a subagent or a teammate, the
       * `id` `$.agent.list()` gives it; absent on the main loop.
       *
       * A workflow's agents and the engine's own forks (compaction, memory) carry
       * ids no list names. Pinned: a different value is refused, one left out is
       * kept. Which loop, not which plugin caused it (that is `next.origin`).
       */
      agentId?: string;
  };

AgentOfferInput type # line 213

In the first published surface (2.1.259) · changed in 2.1.267

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

Listed for the model (the agent listing) or named by it at dispatch (subagent_type): the same event at each. Model-facing only: a plugin's own $.agent.spawn of a type is no offer, and agent.spawn governs it.

21 lines
  export type AgentOfferInput = {
      /**
       * Which type (`Explore`, `Plan`, a plugin's agent); the key a matcher
       * narrows on.
       */
      agent: string;
      /**
       * Its listing line's text as the definition states it (`whenToUse`).
       */
      description: string;
      /**
       * Where the definition came from (`built-in`, `plugin`, a settings
       * source), so a matcher tells a built-in from a user's agent of its name.
       */
      source: string;
      /**
       * Who provides this agent: the plugin and its tier; `{ plugin: "engine",
       * tier: "core" }` for a built-in. Pinned: a rewrite is refused.
       */
      provider: Origin;
  };

AgentOfferResult type # line 239

In the first published surface (2.1.259)

What an agent.offer hook returns: whether the model is offered the agent type, in its listing and at dispatch.

  export type AgentOfferResult = {
      isOffered: boolean;
  };

AgentSpawnArgs type # line 250

In the first published surface (2.1.259) · changed in 2.1.269

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

A call's spawn runs in the background: it resolves once the subagent started, and nothing holds it in the foreground.

  export type AgentSpawnArgs = Pick<AgentSpawnInput, 'prompt'> & Partial<Pick<AgentSpawnInput, 'description' | 'subagentType' | 'model' | 'name' | 'cwd'>>;

AgentSpawnInput type # line 260

In the first published surface (2.1.259) · changed in 2.1.267, 2.1.289, 2.1.292, 2.1.299

The input of agent.spawn: what is decided about an agent about to start, before its model is resolved.

A subagent, a teammate, a workflow agent or a resumed run. A hook rewrites content (prompt, description, subagentType, model, background, cwd) unless resume fixes it, or workflow all but two; every other key is pinned.

149 lines
  export type AgentSpawnInput = {
      /**
       * The Agent tool call this spawn belongs to (for `$.ui.notice`). Pinned:
       * the spawn's identity.
       *
       * For a workflow agent, the Workflow tool call that started its run, shared
       * by all of the run's agents and possibly empty on a resumed or
       * server-launched run.
       */
      tool_use_id: string;
      /**
       * The task the subagent is given: the Agent tool's `prompt` parameter. A
       * rewrite is the prompt the subagent runs with.
       *
       * A workflow agent's is a fixed placeholder on a run that keeps its script
       * out of SDK events (Code Review's, say): a hook has not seen the prompt, so
       * a rewrite of it is not applied, and the agent starts with the script's own.
       */
      prompt: string;
      /**
       * The Agent tool's short `description` of the task (a few words). A rewrite
       * is what the task shows as.
       */
      description: string;
      /**
       * The resolved agent type (`general-purpose`, `Explore`, a plugin's agent,
       * `fork`). A rewrite names another agent this call can dispatch, exactly.
       *
       * That definition is the one spawned; a name matching none refuses the
       * spawn, and a fork dispatches no other. A teammate's may be a role no
       * definition has, as the call spelled it, or `teammate` when it named none.
       */
      subagentType: string;
      /**
       * Who provides this agent: the plugin and its tier; `{ plugin: "engine",
       * tier: "core" }` for a built-in. Pinned with the spawn's identity.
       */
      provider: Origin;
      /**
       * The Agent tool's `model` parameter as given, an alias (`haiku`) or a
       * full id; undefined lets the agent's own model, then the parent's, decide.
       *
       * Ignored for forks, which always inherit. A hook sets this to pick the
       * subagent's model; where the session forces one model on every subagent,
       * that outranks a workflow agent's, a hook's included.
       */
      model?: string;
      /**
       * The parent's effective model, what `inherit` resolves to. Pinned: a fact
       * of the parent (set `model` to change what the subagent runs on).
       */
      parentModel: string;
      /**
       * The id of the loop the spawn happens in (its `tool.call` `agentId`; for a
       * subagent or teammate its `$.agent.list()` id); absent from main.
       *
       * Pinned: a different value is refused, one left out is kept. The agent
       * axis, not the origin: `next.origin` names the plugin whose hook frame
       * caused the spawn, this names the model loop it happened in.
       */
      parentAgentId?: string;
      /**
       * The parent's permission mode (`default`, `acceptEdits`, `plan`, ...), which
       * the subagent inherits. Pinned: a fact of the parent.
       */
      permissionMode?: string;
      /**
       * True when the subagent will run in the background (or remotely). A
       * rewrite is read back as the call's `run_in_background`.
       *
       * The agent's own definition and remote isolation can still force it on,
       * and disabled background tasks force it off. A teammate's is true: a
       * rewrite is left out, said once in the plugin's failure line.
       */
      background: boolean;
      /**
       * True for a fork of the parent: it inherits the parent's context and model,
       * and `model` is ignored. Pinned: the spawn's identity.
       */
      fork: boolean;
      /**
       * Present, and true, when the call starts a teammate: a named agent of the
       * session's team, which goes idle between turns and wakes on a message.
       *
       * Absent for any other agent. Pinned: the spawn's identity.
       */
      isTeammate?: true;
      /**
       * Present when a workflow script's `agent()` starts the agent, absent for
       * any other. Pinned: the spawn's identity.
       *
       * A rewrite of its `prompt` and `model` applies; `description`,
       * `subagentType`, `background` and `cwd` do not apply to it, and one is left
       * out and said once. A deny is the error the script's `agent()` call throws.
       *
       * `next` settles with the first attempt's `agentId`; a stalled agent's
       * retry is not raised again. With `isolation: 'remote'` (internal builds
       * only) no `turn.complete`, loop event or list row carries that id.
       */
      workflow?: {
          /**
           * The run's id, which starts `wf_` (empty only outside a real run).
           *
           * A child `workflow()`'s agents carry their parent run's, and a resume
           * keeps it; a Workflow call that starts a fresh run gets a new id.
           */
          runId: string;
          /**
           * This agent's place among the run's `agent()` calls, from 1. Agents
           * replayed from the run's journal count but are not raised.
           *
           * The journal knows an agent by what the script asked, before any hook.
           * It restarts whenever the run's script starts over (a new run, a resume
           * or a background hand-off of the same `runId`); a call that reopens a
           * settled run keeps counting. A cap across resumes keeps its own count.
           */
          agentIndex: number;
      };
      /**
       * Present when the run resumes a subagent or brings a teammate back from
       * disk (`isTeammate` beside it), absent for a new agent.
       *
       * A hook can only refuse it; a changed content key is ignored, said once.
       * `prompt` is the resuming message (empty where none is appended). A deny
       * starts no run and ends nothing: a denied wake leaves its agent parked.
       */
      resume?: {
          /**
           * The id the run continues under, as `$.agent.list()` lists it; `next`
           * settles with it. An agent started with no event raised has one too.
           */
          agentId: string;
      };
      /**
       * Given by the call (`Agent({ name })`, addressable by SendMessage);
       * undefined when unnamed. Pinned: the address the parent routes by.
       *
       * A teammate whose name the team has already gets a suffix (`scout-2`),
       * which the answer's `teammateId` and `$.agent.list()` show.
       */
      name?: string;
      /**
       * The directory the subagent runs in when the call set one (`cwd`); undefined
       * means the parent's. A rewrite is where the subagent runs.
       *
       * A teammate runs in the session's: a rewrite is left out, as `background`'s.
       */
      cwd?: string;
  };

AgentSpawnResult type # line 421

In the first published surface (2.1.259) · changed in 2.1.269, 2.1.289

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

It and $.agent.spawn(input) resolve once the subagent started; its answer is its own turn.complete, carrying this agentId. Keep the id and set got[agentId] to what waits; a turn.complete hook resolves it:

on("turn.complete", ($, e, next) => (got[e.agentId]?.(e.answer), next(e)))
37 lines
  export type AgentSpawnResult = {
      /**
       * What the subagent runs on: from core the resolved id; from a hook an
       * alias (`haiku`) or an id.
       */
      model: string;
      /**
       * The started subagent's id: the same string its loop's `tool.call`
       * events carry as `agentId` and `$.agent.list()` lists it by.
       *
       * A workflow's remote agent has no local loop, so nothing else carries
       * it. Set by core; a hook that answers without `next` started none.
       */
      agentId?: string;
      /**
       * A started teammate's address in its team, `<name>@<team>`, as
       * `$.agent.list()` gives it; absent for any other agent.
       *
       * Set by core. It joins the list's `teammateId` and the tool's
       * `teammate_id`; every event of the teammate's loop carries `agentId`.
       */
      teammateId?: string;
      deny?: undefined;
  } | {
      /**
       * Refuses the spawn, so nothing starts; the text is the Agent tool's
       * error, or the error a workflow script's `agent()` call throws.
       *
       * Returned after a `next(e)` was answered it fails the hook: its
       * `.catch` is asked (a deny from it fails too), or it is skipped by
       * name and its last `next` stands.
       */
      deny: string;
      model?: undefined;
      agentId?: undefined;
      teammateId?: undefined;
  };

AgentSpec type # line 466

Added in 2.1.274 · changed in 2.1.296

What $.agent.register takes: an agent type this plugin defines, spelled as an agent definition in settings JSON is, plus its name.

Every field an agent file or --agents entry may carry is here and takes effect as it does there; the engine validates it with the same schema.

90 lines
  type AgentSpec = {
      /**
       * The type's short name (letters, digits, `_`, `-`; up to 64); the type is
       * `<plugin>:<name>`, as a plugin's agent file `agents/<name>.md` names one.
       */
      name: string;
      /**
       * When to delegate to this agent: the line the model reads in its listing
       * of agent types, and `agent.offer`'s `description`.
       */
      description: string;
      /**
       * The agent's system prompt, whole: it replaces the session's.
       */
      prompt: string;
      /**
       * What the agent may call, by tool name (`Read`, `Bash`, `mcp__x__y`); a
       * call to any other is refused. Left out: every tool the parent has.
       */
      tools?: readonly string[];
      /**
       * Tools withheld from the agent, by name, out of whatever `tools` allows.
       */
      disallowedTools?: readonly string[];
      /**
       * The agent's model: an alias (`haiku`, `sonnet`, `opus`), a full id, or
       * `inherit` for the parent's. Left out: the session's subagent default.
       */
      model?: string;
      /**
       * The agent's effort: a level (`low`, `medium`, `high`, `xhigh`, `max`)
       * or an integer budget.
       */
      effort?: string | number;
      /**
       * How the agent's permission asks are decided, a mode: `default`,
       * `acceptEdits`, `plan`, `auto`, `dontAsk` or `bypassPermissions`.
       *
       * Left out: the parent's.
       */
      permissionMode?: string;
      /**
       * Servers connected over MCP for the agent's run: a configured server's
       * name, or `{ "<name>": { command, args } }`, one server's config inline.
       */
      mcpServers?: readonly (string | Record<string, unknown>)[];
      /**
       * Settings hooks in force while the agent runs, in settings.json's `hooks`
       * shape (`{ PreToolUse: [{ matcher, hooks: [...] }] }`).
       */
      hooks?: Record<string, unknown>;
      /**
       * The most turns the agent takes before it stops.
       */
      maxTurns?: number;
      /**
       * Token count, 100000 to 1000000, at which the agent compacts its own
       * conversation as a subagent. It only lowers the window the agent would
       * otherwise get.
       */
      autoCompactWindow?: number;
      /**
       * Preloaded into the agent's context before its first turn: skill names.
       */
      skills?: readonly string[];
      /**
       * The agent's first user turn; `{{intent}}` in it takes the spawn's prompt.
       * Left out: the spawn's prompt is the first turn.
       */
      initialPrompt?: string;
      /**
       * A persistent memory the agent keeps between runs, and where: `user`,
       * `project` or `local`.
       */
      memory?: 'user' | 'project' | 'local';
      /**
       * Set so that every spawn of the agent runs in the background.
       */
      background?: true;
      /**
       * Set so that the agent's context carries no CLAUDE.md block (the person's,
       * the project's, the rules): it takes what it needs from its prompt.
       */
      omitClaudeMd?: true;
      /**
       * Where the agent runs apart from the session: `worktree`, a git worktree
       * of its own; `remote`, a cloud session where the build allows one.
       */
      isolation?: 'worktree' | 'remote';
  };

AgentStatus type # line 565

Added in 2.1.289

Where an agent's loop stands: pending (not started), running a turn, waiting (held), idle (between turns, until a message wakes it), or ended.

waiting is on background work it owns, on a plan's approval or, in a background subagent, on an Agent call alone. Ended is completed, failed or killed (stopped); a message may yet resume one, under the same id.

  export type AgentStatus = 'pending' | 'running' | 'waiting' | 'idle' | 'completed' | 'failed' | 'killed';

AgentTeammateRecord type # line 574

Added in 2.1.289 · changed in 2.1.290

What an Agent call that started a teammate answers as result at tool.call: the started teammate's two ids, its name and its model.

The call answers once the teammate started; each of its turns ends in its own turn.complete, carrying agentId (none here from a terminal pane).

49 lines
  export type AgentTeammateRecord = {
      status: 'teammate_spawned';
      /**
       * The teammate's one id, as every Agent record spells it: `agent.spawn`'s
       * `agentId`, its loop's events' `agentId`, its `$.agent.list()` `id`.
       *
       * For one in a terminal pane, which runs no loop here, its address. Absent
       * from a record an earlier version wrote, which a resumed transcript holds.
       */
      agentId?: string;
      /**
       * The same id in this record's older spelling, which the classic events
       * share (`classic.SubagentStop`'s `agent_id`).
       */
      agent_id: string;
      /**
       * Its address in its team, `<name>@<team>`: `$.agent.list()`'s
       * `teammateId`, and no event's `agentId`.
       */
      teammate_id: string;
      /**
       * What SendMessage addresses it by: the call's `name`, any `@` replaced,
       * with a suffix (`scout-2`) when the team already has it.
       */
      name: string;
      /**
       * The team it joined: the session's own.
       */
      team_name?: string;
      /**
       * What it was spawned as, when the call named a type.
       */
      agent_type?: string;
      /**
       * What it was started on, an alias resolved, as every Agent record spells
       * it: `agent.spawn`'s `model`.
       *
       * Absent where `agentId` is: `model` has it then, an alias as spelled.
       */
      resolvedModel?: string;
      /**
       * The older spelling of that, an alias left as the call spelled it.
       */
      model?: string;
      /**
       * The task it was given, as an `agent.spawn` hook left it.
       */
      prompt: string;
  };

AnyEventHook type # line 635

In the first published surface (2.1.259)

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

Until next.is(pattern, e) narrows e, all a hook can do with it is pass it on, time it, log it, or fail it; once narrowed it is an ordinary hook on those events.

$
the engine interface; at engine.create the empty table, so a hook that reads $ tests next.is("engine.create", e) first
  export type AnyEventHook = ($: EngineInterface, e: unknown, next: StarNext) => unknown;

AnyKeyOf type # line 640

In the first published surface (2.1.259)

Every key of every variant, index signatures included.

  type AnyKeyOf<I> = I extends unknown ? keyof I : never;

ApiContentBlock type # line 650

Added in 2.1.280

One content block of an ApiMessage: type names its kind (text, tool_use, tool_result, image, document, thinking, ...).

The other fields are that kind's as the Messages API defines them (see its reference); the engine hands the block over as it holds it, nothing renamed or dropped.

  type ApiContentBlock = {
      /**
       * The block's kind; the rest of the block is that kind's fields.
       */
      type: string;
      [field: string]: unknown;
  };

ApiMessage type # line 666

Added in 2.1.280

One message of the conversation in Messages API form, as $.session.messages({ as: "api" }) returns it.

The messages are the ones the next request is built from, after the engine's normalization (the last compaction's summary in place of what it replaced, reminders as text blocks); at most 4096, opening on a user one.

15 lines
  type ApiMessage = {
      /**
       * Who wrote it.
       */
      role: 'user' | 'assistant';
      /**
       * Its blocks in order, always an array: text, tool_use and tool_result
       * (paired across an assistant message and the next), image and document.
       *
       * Media inline as the engine holds it, thinking as it goes back. In no
       * message: the system prompt, the tool list, and what one send adds for
       * its model (cache marks, reminders promoted to system turns).
       */
      content: ApiContentBlock[];
  };

Args type # line 686

In the first published surface (2.1.259)

The argument of event N: e in its hooks, and what its call takes. For a union of names, the union of their arguments.

  export type Args<N extends EventName = EventName> = EventOf[N];

AskOptions type # line 691

Added in 2.1.268

Options of $.ui.ask.

15 lines
  export type AskOptions = {
      /**
       * 2-4 option labels; fewer than two are padded with Yes/No; free text is the
       * dialog's Other.
       */
      options?: readonly string[];
      /**
       * A short chip beside the question (`Approach`, 12 characters at most).
       */
      header?: string;
      /**
       * Allow several options; the answer comes back comma-joined.
       */
      multiSelect?: true;
  };

Atom type # line 714

Added in 2.1.281

A named value with its initial, as atom(ref, initial) makes it: read, it is never undefined; while nothing is written it reads as the initial.

Plain frozen data, the plugin's own: pass it to read, update, derive and memberOf. The engine knows nothing of it.

  export type Atom<T> = {
      readonly ref: StateAddress;
      readonly initial: T;
      /**
       * The tag the value is kept under, when the atom was given one: a stored
       * value under another tag reads as absent (Shaped).
       */
      readonly shape?: string;
  };

AtomFunction type # line 734

Added in 2.1.281

atom(ref, initial): a named value with its initial, so a read is never undefined; given { shape }, for a key declared Shaped<T>.

Pure: it builds a frozen description and calls nothing. A family's reference is given without its id, which memberOf adds.

const count = atom({ plugin: "counter", key: "count" } as const, 0)
  export type AtomFunction = {
      <P extends keyof PluginState & string, K extends keyof PluginState[P] & string>(ref: StateName<P, K> & Readonly<Pick<StateAddress, 'id'>>, initial: StateValue<P, K>): Atom<StateValue<P, K>>;
      <P extends keyof PluginState & string, K extends keyof PluginState[P] & string>(ref: StateName<P, K> & Readonly<Pick<StateAddress, 'id'>>, initial: ShapedValue<StateValue<P, K>>, options: AtomOptions): Atom<ShapedValue<StateValue<P, K>>>;
  };

AtomOptions type # line 746

Added in 2.1.281

The options of atom: shape, a tag the value is kept under, so a reload of the plugin's code that names another tag finds the value absent.

Bump it when the code's idea of the value changed; the engine keeps whole what the old code wrote, and the tag is how the new code declines it.

  export type AtomOptions = {
      shape: string;
  };

AttributionTextInput type # line 757

In the first published surface (2.1.259)

The input of attribution.text: one text the engine asks the model to write into a commit or a pull request, at the moment it is composed.

Composed for the Bash description, the commit skills, a PR's body, the pre-ship mandate and the commit gate's deny: the same event at each.

  export type AttributionTextInput = {
      /**
       * Which text (`commit`, `pr`, `exemption`, `remedy`); the key a matcher
       * narrows on.
       */
      kind: AttributionTextKind;
      /**
       * As the engine composed it, the settings applied.
       */
      text: string;
  };

AttributionTextKind type # line 773

In the first published surface (2.1.259)

Which git text attribution.text carries: the commit trailer, the PR footer, the mandate's or the commit gate's sentence naming the exemption.

  export type AttributionTextKind = 'commit' | 'pr' | 'exemption' | 'remedy';

AttributionTextResult type # line 779

In the first published surface (2.1.259)

What an attribution.text hook returns: the text the model reads in that place.

  export type AttributionTextResult = {
      text: string;
  };

AudioClip type # line 786

In the first published surface (2.1.259)

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

32 lines
  export type AudioClip = {
      /**
       * A file of the calling plugin's own, relative to the plugin's
       * directory (`fx/open.wav`); no `.`, no leading slash.
       *
       * The engine resolves it and loads the file from disk.
       */
      asset: string;
      url?: undefined;
      base64?: undefined;
      mime?: undefined;
  } | {
      /**
       * The clip's URL; the engine fetches it (never the plugin), or refuses
       * it as `$.http.fetch` would refuse the same URL.
       */
      url: string;
      asset?: undefined;
      base64?: undefined;
      mime?: undefined;
  } | {
      /**
       * The clip's bytes, base64.
       */
      base64: string;
      /**
       * What the bytes are (`audio/mpeg`, `audio/wav`).
       */
      mime: string;
      asset?: undefined;
      url?: undefined;
  };

BackgroundTaskSummary type # line 819

Added in 2.1.265

32 lines
  type BackgroundTaskSummary = {
      id: string;
      /**
       * Friendly task-type label (e.g. 'shell', 'subagent', 'monitor', 'workflow'). Falls back to the raw discriminant for unknown types.
       */
      type: string;
      status: string;
      /**
       * Free-text description. Capped at 1000 chars; clipped values append an in-string "... [+N chars]" marker.
       */
      description: string;
      /**
       * Shell command line. Only present for 'shell' tasks. Capped at 1000 chars with the same "... [+N chars]" marker.
       */
      command?: string;
      /**
       * Subagent type name. Only present for 'subagent' tasks.
       */
      agent_type?: string;
      /**
       * MCP server name. Only present for 'monitor' / 'MCP task' tasks.
       */
      server?: string;
      /**
       * MCP tool name. Only present for 'monitor' / 'MCP task' tasks.
       */
      tool?: string;
      /**
       * Workflow name. Only present for 'workflow' tasks.
       */
      name?: string;
  };

BaseHookInput type # line 852

Added in 2.1.265

27 lines
  type BaseHookInput = {
      session_id: string;
      transcript_path: string;
      cwd: string;
      /**
       * UUID correlating a user prompt with all subsequent events until the next prompt. Same value emitted on OpenTelemetry events as the `prompt.id` attribute, so hook output can be joined to OTel events at prompt grain. Absent until the first user input of the process lifetime.
       */
      prompt_id?: string;
      permission_mode?: string;
      /**
       * Subagent identifier. Present only when the hook fires from within a subagent (e.g., a tool called by an AgentTool worker). Absent for the main thread, even in --agent sessions. Use this field (not agent_type) to distinguish subagent calls from main-thread calls.
       */
      agent_id?: string;
      /**
       * Agent type name (e.g., "general-purpose", "code-reviewer"). Present when the hook fires from within a subagent (alongside agent_id), or on the main thread of a session started with --agent (without agent_id).
       */
      agent_type?: string;
      /**
       * Reasoning effort applied to the current turn. Same shape as StatusLineCommandInput.effort. Present for hooks that fire within a tool-use context (PreToolUse, PostToolUse, Stop, SubagentStop, etc.) on a model that supports the effort parameter; absent for session-lifecycle hooks and models without effort support.
       */
      effort?: {
          /**
           * Active effort level for the current turn (e.g., "low", "medium", "high", "xhigh", "max"), after any silent downgrade for the selected model. Also exposed to hook commands and Bash as the CLAUDE_EFFORT env var.
           */
          level: string;
      };
  };

BoxHoverProps type # line 888

Added in 2.1.267 · changed in 2.1.271, 2.1.274, 2.1.290

The Box props a hover may override, none of which moves the Box's siblings, and scope, which names the hover group the Box joins.

The surface applies a hover as the pointer moves and as what is drawn under a resting pointer changes; a reveal that keeps moving what it rests on settles, never loops.

34 lines
  export type BoxHoverProps = {
      /**
       * Names a hover group of this plugin's: every element it draws with the
       * same `scope`, in any site, lights while any is hovered, reveals included.
       *
       * A Pane row and a mark on a transcript message can share one. Another
       * plugin's elements under the same string are a different group. One to
       * 64 characters, no control characters; no keyed Box needed; no hook runs.
       */
      scope?: string;
      /**
       * Restyles a border the Box has; it adds none.
       */
      borderStyle?: string;
      borderColor?: Color;
      borderDimColor?: boolean;
      backgroundColor?: Color;
      /**
       * `"flex"` alone, on a Box drawn `display: "none"`: with a `scope`, every
       * member of the lit group is revealed, in whichever site it sits.
       *
       * So a pointer on a glyph in the transcript can swap an entry into a fixed
       * row of the band, or reveal an absolutely positioned card.
       */
      display?: 'flex';
      /**
       * Moves a Box drawn `position: "absolute"`, as `left`, `right` and
       * `bottom` do; none of the four moves a Box in the flow.
       */
      top?: number;
      left?: number;
      right?: number;
      bottom?: number;
  };

BoxProps type # line 927

In the first published surface (2.1.259) · changed in 2.1.267, 2.1.274, 2.1.290, 2.1.299

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

96 lines
  export type BoxProps = {
      /**
       * Makes the Box a hover scope: while the pointer is anywhere over it, its
       * own `hover` and that of every element beneath it apply.
       *
       * A nested Box with a `key` of its own scopes what is beneath it, a placed
       * card outside its rows included. A key a sibling Box already took, or one
       * that is no plain string, names no scope: hovers beneath it stay inert.
       */
      key?: string;
      /**
       * Style overrides applied by the surface while the pointer is over the
       * nearest keyed `Box`, this one included, or, given a `scope`, its group.
       *
       * No hook runs and nothing crosses to the plugin. To reveal on hover, draw
       * the Box `display: "none"` with `hover: { display: "flex" }` in a visible
       * keyed Box or under a `scope`; on a `position: "absolute"` Box, no reflow.
       */
      hover?: BoxHoverProps;
      /**
       * `"absolute"` leaves the flow, as in CSS: placed against its parent by
       * the offsets, no room among its siblings, painted over those before it.
       *
       * So showing or moving it (a hover may) moves nothing; the pointer on it is
       * on its parent. Clipped, pointer and paint, by the region its site is in
       * (viewport, pane, band), by the site itself on the main screen or export.
       *
       * @example <Box key="k"><Text>glyph</Text><Box position="absolute" top={-2}
       *   left={2} display="none" hover={{ display: 'flex' }}>the card</Box></Box>
       */
      position?: 'relative' | 'absolute';
      /**
       * Rows from the parent's top edge, in character cells: an integer,
       * negative above the edge.
       *
       * With `bottom` and no `height` the Box spans the two.
       */
      top?: number;
      /**
       * Columns from the parent's left edge, in character cells: an integer,
       * negative left of the edge.
       *
       * With `right` and no `width` the Box spans the two.
       */
      left?: number;
      /**
       * Columns from the parent's right edge, in character cells: an integer,
       * negative right of the edge.
       */
      right?: number;
      /**
       * Rows from the parent's bottom edge, in character cells: an integer,
       * negative below the edge.
       */
      bottom?: number;
      flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
      flexGrow?: number;
      flexShrink?: number;
      flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
      alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
      alignSelf?: 'flex-start' | 'center' | 'flex-end' | 'auto';
      justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
      /**
       * Space between the children, across and down, in cells or a step name.
       * Spacing flows from the parent: set `gap` on the Box rather than a margin
       * on each child. A graphical surface draws `xs` between the Buttons of a
       * row Box with no gap; the terminal draws them as written.
       */
      gap?: Space;
      columnGap?: Space;
      rowGap?: Space;
      width?: number | string;
      height?: number | string;
      minWidth?: number | string;
      minHeight?: number | string;
      margin?: Space;
      marginX?: Space;
      marginY?: Space;
      marginTop?: Space;
      marginBottom?: Space;
      marginLeft?: Space;
      marginRight?: Space;
      padding?: Space;
      paddingX?: Space;
      paddingY?: Space;
      paddingTop?: Space;
      paddingBottom?: Space;
      paddingLeft?: Space;
      paddingRight?: Space;
      borderStyle?: string;
      borderColor?: Color;
      borderDimColor?: boolean;
      backgroundColor?: Color;
      overflow?: 'visible' | 'hidden';
      display?: 'flex' | 'none';
  };

BuiltinToolCallInput type # line 1028

In the first published surface (2.1.259)

One variant per built-in tool; with none in the table (a plugin author's project with no tools entry laid), one loose variant over every name.

  export type BuiltinToolCallInput = [BuiltinToolName] extends [never] ? BuiltinToolCallInputFallback : {
      [N in BuiltinToolName]: ToolInputOf<N, BuiltinToolInputs[N]>;
  }[BuiltinToolName];

BuiltinToolCallInputFallback type # line 1036

In the first published surface (2.1.259)

The built-in branch's answer when no built-in tool is declared: every name, its args unconstrained.

13 lines
  type BuiltinToolCallInputFallback = {
      /**
       * The name of the tool being called (`Bash`); comparing it narrows `e` once
       * the table has entries. Reserved: a rewrite of it is ignored by core.
       */
      tool: string;
      /**
       * The tool_use block's id: the same at every event of the call and in
       * `$.ui.notice`. Reserved: a rewrite of it is ignored by core.
       */
      tool_use_id: string;
      [argument: string]: unknown;
  };

BuiltinToolInputs interface # line 1061

In the first published surface (2.1.259)

The arguments of each built-in tool by name, for declaration merging; empty until a declaration file adds entries, then e.tool === "Bash" narrows e to Bash's arguments.

The engine lays this build's set beside a mod it loads (claude-code-tools/index.d.ts), from each tool's input schema.

interface BuiltinToolInputs { Bash: { command: string; timeout?: number } }
  export interface BuiltinToolInputs {
  }

BuiltinToolName type # line 1067

In the first published surface (2.1.259)

The names of the built-in tools.

  export type BuiltinToolName = keyof BuiltinToolInputs & string;

BuiltinToolResults interface # line 1081

Added in 2.1.260

The structured result of each built-in tool by name, for declaration merging; empty until a declaration file adds entries, then after e.tool === "Bash" the result of next(e) is Bash's record.

The engine lays this build's set beside a mod it loads (claude-code-tools/index.d.ts), from each tool's output schema; a tool without one is unknown.

interface BuiltinToolResults { Bash: { stdout: string; stderr: string } }
  export interface BuiltinToolResults {
  }

ButtonProps type # line 1092

Added in 2.1.260 · changed in 2.1.267, 2.1.271, 2.1.282, 2.1.283, 2.1.284

The props of Button, every surface's pressable: an address, a label or children, the closure a press runs, and the styles a hover overrides.

The terminal draws [ label ] (when plain, 1: label or the label alone), a desktop a native button; a click, a hotkey, the chord for its action, or Enter under the focus raises ui.press, its bottom onPress.

106 lines
  export type ButtonProps = {
      /**
       * The element's address: `e.element` at `ui.press`, what a matcher names.
       * Defaults to the `label`, or the one string child.
       *
       * Needed where the Button holds other children and no `label`: a row's
       * text holds what changes (a count, an age), its address must not.
       *
       * Give every Button in a list its own: a surface finds one again by it
       * after a redraw, and cannot tell apart two that share it.
       */
      key?: string;
      /**
       * The text drawn on the button; or the one string child.
       *
       * Children of strings and `Text` (a chip, a dim part) are drawn in its
       * place, one press wherever on them it lands; any other element is refused.
       * The label then names the control; absent, it is the children's text.
       *
       * @example
       * <Button key={id} onPress={open}>{name} <Text dimColor>2m</Text></Button>
       */
      label?: string;
      /**
       * One digit (`"1"`) or one lowercase letter (`"w"`) that presses it while
       * the plugin's site holds the focus; anything else is refused.
       *
       * Its site holds it after ctrl+x tab, a click or `open({ focus })`: the band
       * or its `Pane`; never the composer, save that a bare digit in an empty one
       * answers a band Button (a survey). Shift+w is `"w"`; two clash, later wins.
       */
      hotkey?: string;
      /**
       * An engine keybinding action (`"app:cycleDiffBase"`) whose chord, as the
       * person bound it, presses this from the prompt; unknown names refused.
       *
       * Chords, or a modified key Global or an active context binds, on the
       * terminal while mounted, no dialog up and no engine handler of the action
       * mounted; a pane's over the band's over another's, then the last drawn.
       */
      action?: string;
      /**
       * Drawn without chrome: the hotkey in the accent color, a colon, the
       * label (`1: Yes`), as a survey's row reads; no `hotkey`, the label alone.
       *
       * A one-glyph label (`'\u{1F50A}'`, a speaker) is then a control by
       * itself: the focus and the pointer still invert it, `dimColor` and `hover`
       * still apply. A desktop draws its native button either way.
       */
      plain?: true;
      /**
       * The label drawn dim at rest, as `Text`'s `dimColor`, and at full strength
       * under the pointer or the focus: a secondary control, a path in a list.
       */
      dimColor?: boolean;
      /**
       * Which button of several is the main action: `"primary"` is drawn as the
       * surface marks the one to press, `"secondary"`, and absent, as before.
       *
       * The terminal draws a primary `[ label ]` in the accent color, a desktop
       * its own primary button; `dimColor`, `hover`, the focus and the pointer
       * apply to both. `plain` wins: a plain Button draws the same either way.
       *
       * @example <Button variant="primary" onPress={save}>Save</Button>
       */
      variant?: 'primary' | 'secondary';
      /**
       * Marks the Button that closes its site: a drawing hint only. `onPress`
       * still does the dismissing and the press is raised as for any Button.
       *
       * The terminal draws it as without the prop; a desktop draws its native
       * close control at the site's trailing edge, the label its accessible name.
       *
       * @example <Button role="dismiss" onPress={close}>Dismiss</Button>
       */
      role?: 'dismiss';
      /**
       * The site's focus ring starts here when the site takes the keyboard,
       * instead of on nothing, as the DOM's `autofocus`: Enter acts on it at once.
       *
       * A pane opened with `focus`, or the person's focus chord or click, is the
       * take. Of several in one site the first drawn wins; it raises `ui.focus`,
       * origin this plugin. A ring the person has moved stays where it was put.
       */
      autoFocus?: true;
      /**
       * Label style overrides (the `Text` set) applied by the surface while the
       * nearest enclosing keyed `Box`, or given a `scope` its group, is hovered.
       *
       * No hook runs and nothing crosses to the plugin; under the pointer itself
       * the button inverts as it always has. Refused outside a keyed Box unless
       * it names a `scope`.
       */
      hover?: TextHoverProps;
      /**
       * What the press runs, in the plugin's own environment: the bottom of the
       * `ui.press` chain. The host holds only a handle, for the drawing's life.
       *
       * Run with the `ui.press` argument as the chain above left it: `e.surface`
       * names the surface the press came from, which a copy passes on.
       *
       * @example
       * onPress: press => $.ui.copy({ text: url, surface: press.surface })
       */
      onPress: (e: UiPressArgument) => void;
  };

CatchHandler type # line 1207

Added in 2.1.267 · changed in 2.1.269

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.

next carries error and called (Caught) and is replay-safe; a return within the grace is the hook's result, undefined the hook absent. On a streaming event the handler is a generator too, continuing the stream.

  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;

Caught type # line 1217

Added in 2.1.267

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

There next is replay-safe: when called, next(e) resolves to what the hook's last call settled to, nothing beneath running again, the argument unread; when not, it runs the hooks beneath once and a later call replays.

  export type Caught = {
      /**
       * Why the hook failed (HookFailure); undefined on an ordinary hook's
       * `next`, so its presence says a handler is running.
       */
      readonly error: HookFailure;
      /**
       * True when the failed hook had called `next()` or `next.to()` at least
       * once, settled or in flight; the handler runs once that call settled.
       */
      readonly called: boolean;
  };

Chunk type # line 1234

Added in 2.1.269

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

  export type Chunk<N extends StreamingEventName = StreamingEventName> = ChunkOf[N];

ChunkOf type # line 1239

Added in 2.1.269 · changed in 2.1.280

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

  export type ChunkOf = {
      /**
       * One piece of the model's response (TurnStepChunk).
       */
      'turn.step': TurnStepChunk;
      /**
       * One piece of the child's output (ProcessSpawnChunk).
       */
      'process.spawn': ProcessSpawnChunk;
  };

ChunkRef type # line 1258

Added in 2.1.269

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.

A hook that passes a chunk on, or rewrites it by spreading it, keeps the handle, and whatever it left unchanged reaches the engine as the engine streamed it; a chunk built afresh has none and is taken at its word.

  type ChunkRef = {
      /**
       * The engine's handle on its own streamed item; opaque, this step's only.
       */
      ref?: number;
  };

ClassicEventName type # line 1273

Added in 2.1.265

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

Each fires wherever the engine runs the classic hook, whether or not any settings hook is configured, and reaches a * or classic.* hook as it reaches a named one.

  export type ClassicEventName = `classic.${ClassicHookEvent}`;

ClassicEventOf type # line 1286

Added in 2.1.265 · changed in 2.1.267

The classic (settings) hook events, one per classic event name: e is the hook's whole stdin input, base fields (transcript_path, cwd) included.

The chain is [managed settings hooks, ...hooks modules, the other settings hooks as core], so a managed block ends it above every module. In shape classic.PreToolUse alone differs: its e is ToolCallEnvelope, no more.

  export type ClassicEventOf = {
      [E in ClassicHookEvent as `classic.${E}`]: E extends 'PreToolUse' ? ToolCallEnvelope : ClassicHookInputs[E];
  };

ClassicHookEvent type # line 1293

Added in 2.1.265

The name of a classic hook event: PreToolUse, Stop, and the rest.

  export type ClassicHookEvent = HookInput['hook_event_name'];

ClassicHookInputs type # line 1299

Added in 2.1.265

What a classic hook receives on stdin for each event, by event name: the Agent SDK's <Event>HookInput.

  export type ClassicHookInputs = {
      [I in HookInput as I['hook_event_name']]: I;
  };

ClassicResult type # line 1311

Added in 2.1.265

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 settings hooks below fold into one of these (last write wins, contexts concatenate); a hooks module returns next(e), a copy with fields changed, or its own. A field of the wrong shape fails the hook, which is skipped.

77 lines
  export type ClassicResult = {
      /**
       * `decision: "block"` with this text as `reason` (a command hook's exit
       * code 2): the event's block, veto or re-prompt.
       */
      block?: string;
      /**
       * `continue: false`: the session stops after this event.
       */
      preventContinuation?: true;
      /**
       * Shown when `preventContinuation` stops the session (`stopReason`).
       */
      stopReason?: string;
      /**
       * `hookSpecificOutput.additionalContext`, one entry per hook: text handed
       * to the model with the event.
       */
      additionalContext?: string[];
      /**
       * `hookSpecificOutput.sessionTitle` (UserPromptSubmit, SessionStart).
       */
      sessionTitle?: string;
      /**
       * `hookSpecificOutput.suppressOriginalPrompt` (UserPromptSubmit,
       * UserPromptExpansion).
       */
      suppressOriginalPrompt?: true;
      /**
       * `hookSpecificOutput.initialUserMessage` (SessionStart).
       */
      initialUserMessage?: string;
      /**
       * `hookSpecificOutput.watchPaths` (SessionStart).
       */
      watchPaths?: string[];
      /**
       * `hookSpecificOutput.reloadSkills` (SessionStart).
       */
      reloadSkills?: true;
      /**
       * `hookSpecificOutput.permissionDecision` (PreModelSwitch).
       */
      permissionDecision?: 'allow' | 'deny' | 'ask';
      /**
       * `hookSpecificOutput.permissionDecisionReason` (PreModelSwitch).
       */
      permissionDecisionReason?: string;
      /**
       * `hookSpecificOutput.decision` (PermissionRequest).
       */
      decision?: PermissionRequestDecision;
      /**
       * `hookSpecificOutput.updatedToolOutput` (PostToolUse): replaces what the
       * model sees of the tool's result.
       */
      updatedToolOutput?: unknown;
      /**
       * `hookSpecificOutput.updatedMCPToolOutput` (PostToolUse, MCP tools only).
       */
      updatedMCPToolOutput?: unknown;
      /**
       * `hookSpecificOutput.retry` (PermissionDenied).
       */
      retry?: true;
      /**
       * `hookSpecificOutput.displayContent` (MessageDisplay).
       */
      displayContent?: string;
      /**
       * `hookSpecificOutput.worktreePath` (WorktreeCreate; a command hook prints
       * it): the worktree the hook created, absolute or relative to its cwd.
       *
       * Left unset, the session creates its git worktree as it would unhooked.
       */
      worktreePath?: string;
  };

ClassicResultFields type # line 1395

Added in 2.1.265

The event-specific fields of ClassicResult each classic event reads (its hookSpecificOutput), by event; an event absent here reads none of them.

block, preventContinuation and stopReason are every event's.

18 lines
  export type ClassicResultFields = {
      UserPromptSubmit: 'additionalContext' | 'sessionTitle' | 'suppressOriginalPrompt';
      UserPromptExpansion: 'additionalContext' | 'suppressOriginalPrompt';
      SessionStart: 'additionalContext' | 'initialUserMessage' | 'sessionTitle' | 'watchPaths' | 'reloadSkills';
      Setup: 'additionalContext';
      PreModelSwitch: 'permissionDecision' | 'permissionDecisionReason';
      PostModelSwitch: 'additionalContext';
      SubagentStart: 'additionalContext';
      PostToolUse: 'additionalContext' | 'updatedToolOutput' | 'updatedMCPToolOutput';
      PostToolUseFailure: 'additionalContext';
      PostToolBatch: 'additionalContext';
      Stop: 'additionalContext';
      SubagentStop: 'additionalContext';
      PermissionDenied: 'retry';
      PermissionRequest: 'decision';
      MessageDisplay: 'displayContent';
      WorktreeCreate: 'worktreePath';
  };

ClassicResultOf type # line 1421

Added in 2.1.265

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

classic.PreToolUse keeps its allow / ask / deny result (PreToolUseResult).

  export type ClassicResultOf = {
      [E in ClassicHookEvent as `classic.${E}`]: E extends 'PreToolUse' ? PreToolUseResult : Pick<ClassicResult, 'block' | 'preventContinuation' | 'stopReason' | (E extends keyof ClassicResultFields ? ClassicResultFields[E] : never)>;
  };

ClassifyOptions type # line 1428

In the first published surface (2.1.259)

Options of $.model.classify.

  export type ClassifyOptions = {
      /**
       * An alias (`haiku`) or a full model id; default the engine's small fast
       * model.
       */
      model?: string;
  };

ClientElements type # line 1444

Added in 2.1.267 · changed in 2.1.271, 2.1.275

The element table a surface module draws with, surface.elements: the terminal's (Elements) less Client (none nests), Raster and Image.

What $.ui.resolve(e) is to a hooks module; a Button, Input or Select keeps its handler here. A tree of them keeps every tree's bounds (20,000 nodes, 32 deep, 100,000 characters serialized) or the instance unmounts.

  export type ClientElements = Omit<Elements['terminal'], 'Client' | 'Raster' | 'Image'>;

ClientKeyEvent type # line 1450

Added in 2.1.267

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

13 lines
  export type ClientKeyEvent = {
      /**
       * A special key's name (`up`, `down`, `left`, `right`, `return`, `tab`,
       * `backspace`, `delete`, `pageup`, `home`, ...) or the character typed.
       */
      key: string;
      /**
       * Modifier keys held with it, each present only when true.
       */
      ctrl?: true;
      shift?: true;
      meta?: true;
  };

ClientModule type # line 1475

Added in 2.1.267

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

Runs on the drawing thread, a second per call at most; h and Fragment are the environment's, so nothing of the module's takes those names. A throw, an overrun or a tree past the bounds (ClientElements) unmounts it.

const Tag: ClientModule<string> = (t, s) => s.elements.Text({ children: t })
  export type ClientModule<P extends JsonValue = JsonValue, S = unknown> = (props: P, surface: ClientSurface<S>) => RenderElement;
Feedback