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

Types

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

Declarations570over 12 pages
Mined from2.1.283the shipped binary

Every named declaration in the file, 570 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 13,820 lines of TypeScript mined from build 2.1.283. 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, 201 to 250 of 570

Negation type # line 5713

Added in 2.1.265

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

  type Negation = `!${Exclude<EventName | Glob, '*'>}`;

Next type # line 5728

In the first published surface (2.1.259) · changed in 2.1.260, 2.1.265, 2.1.267, 2.1.268, 2.1.275

The rest of the chain, as one hook receives it: made once per dispatch per hook, frozen; next(e) resolves to the downstream result.

Each call runs the hooks below again; core is the last, and below it next rejects. Called with no argument it rejects, naming the hook. A hook that returns without calling it ends the chain; returning nothing is a failure.

74 lines
  export type Next<N extends EventName = EventName, E = Args<N>, O = NextResult<N>, S extends {
      [K in N]?: unknown;
  } = {
      [K in N]: Args<K>;
  }> = {
      <T extends string>(e: E & ToolNamed<T>): Promise<NextResultFor<N, O, T>>;
      (e: E): Promise<O>;
      /**
       * Continues this dispatch at a tier: `next(e)` with every link between
       * this hook's own tier and that one skipped, by tier and narrowing only.
       *
       * A managed hook's: prepend may name append, builtin or core, append core;
       * it never skips a tier with more authority, so no user hook skips the
       * org's. A literal on the hook's own `next`; skipped links are traced.
       */
      readonly to: {
          <T extends string>(e: E & ToolNamed<T>, tier: TargetTier): Promise<NextResultFor<N, O, T>>;
          (e: E, tier: TargetTier): Promise<O>;
      };
      /**
       * Aborts when the call this dispatch belongs to is abandoned: the user
       * interrupted, a hook above settled first, or this hook ran out of budget.
       *
       * Anything the hook started (timers, requests) should stop on it. It is an
       * AbortSignal of the plugin's own environment, driven by the chain's.
       */
      readonly signal: AbortSignal;
      /**
       * Whether this dispatch's event is selected by `pattern` (a name, a glob,
       * a negation), as a type predicate on `e`: `next.is("tool.call", e)`.
       *
       * Under a matcher the narrowing includes it. `pattern` names events this
       * hook covers (PatternOver); a name outside them is a compile error.
       */
      readonly is: <M extends PatternOver<N>>(pattern: M, e: unknown) => e is Frozen<S[Extract<N, Selected<M>>]>;
      /**
       * The name of this dispatch's event, as a value, for a glob hook to log or
       * switch on.
       */
      readonly event: N;
      /**
       * Who raised this dispatch: the calling plugin's name and the tier it sits
       * in (Origin); the engine reads `{ plugin: "engine", tier: "core" }`.
       *
       * Set by the host alone, from the environment the call came from (its own
       * MessagePort) and that plugin's seat; nothing a plugin writes reaches it.
       * Every hook of one dispatch sees the same origin, and `next.to` keeps it.
       *
       * @example
       * return next.origin.tier === "prepend" ? next.to(e, "append") : next(e)
       */
      readonly origin: Origin;
      /**
       * What settled beneath this hook on its latest `next()` call, the one
       * started last: an entry per link beneath, nearest first, the engine's last.
       *
       * Empty before `next` is called; filled even when `next` rejected; a link
       * still running joins in place later, nothing listed leaves; it ends short
       * of the engine at a link that answered its last call itself. Data, frozen.
       */
      readonly trace: readonly TraceEntry<N, E, O>[];
      /**
       * How much time this hook runs under and how much is left (NextBudget),
       * read fresh on each access; HookBudget says what the clock counts.
       *
       * It stands still while a `next` or `$` call of the hook's is in flight;
       * in a `.catch` handler it is the grace, `ms` being `next.error.budget`;
       * at `session.end` it reads the exit's one short bound, which never stops.
       *
       * @example
       * if (next.budget.remainingMs < 2_000) return next(e) // skip the polish
       */
      readonly budget: NextBudget;
  };

NextBudget type # line 5814

Added in 2.1.275

The budget the code reading next.budget runs under: the whole allowance and what is left of it now, plain data read fresh on each access.

In a hook ms is HookBudget's ms; in a .catch handler its catchMs (what next.error.budget names); on a next nothing meters (the engine's own, a test's root one) ms is 0 and remainingMs Infinity.

if (next.budget.remainingMs < 2_000) return next(e) // skip the slow path
19 lines
  export type NextBudget = {
      /**
       * The whole budget in milliseconds; 0 where nothing meters this `next`.
       *
       * On a dispatch the engine cuts short as a whole (`session.end`: one short
       * wall-clock bound for the entire chain, so an exit stays fast whatever is
       * loaded) it is no more than that cut left when the hook started, 0 past it.
       */
      readonly ms: number;
      /**
       * What is left of it now, in milliseconds, never below 0; Infinity where
       * nothing meters. It stands still while a `next` or `$` call is in flight.
       *
       * Except under a cut of the whole dispatch (`session.end`): the cut's clock
       * never stops, `$` waits included, and this reads no more than it leaves,
       * so a later hook in that chain starts with what the earlier ones left.
       */
      readonly remainingMs: number;
  };

NextResult type # line 5841

In the first published surface (2.1.259)

What next(e) resolves to for event N: the event's result, except at engine.create, where the steps beneath return $ as built so far.

A withheld noun is on that $ as a stub, so the built table is typed whole where what a hook returns (EngineCreateResult) is partial.

  export type NextResult<N extends EventName> = N extends 'engine.create' ? EngineInterfaceBuilt : EventResult<N>;

NextResultFor type # line 5850

Added in 2.1.260

What next(e) resolves to once e.tool is the literal T: on tool.call the result typed for that tool; on every other event, O as declared.

result is Bash's record after e.tool === "Bash"; an un-narrowed e names every tool, and result stays unknown.

  export type NextResultFor<N extends EventName, O, T extends string> = [
  N

NoArgs type # line 5858

In the first published surface (2.1.259)

The argument of a call on $ that takes nothing ($.session.cwd()): an object with no keys.

  type NoArgs = Record<never, never>;

NoArgsEvent type # line 5864

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

The events whose argument is exactly NoArgs (session.cwd, a declared plugin noun's () => ...); their overloads come last (LateOverload).

  type NoArgsEvent = {
      [N in EventName]: Args<N> extends NoArgs ? NoArgs extends Args<N> ? N : never : never;
  }[EventName];

NoEngineInterface type # line 5872

In the first published surface (2.1.259)

What an engine.create hook receives as $: nothing. Every property reads as never, so $.model inside the hook is a compile error.

  export type NoEngineInterface = {
      readonly [noun: string]: never;
  };

NonEmpty type # line 5883

In the first published surface (2.1.259)

Array type V, of element Item, once some element of it is known to match: a tuple of at least one Item, readonly when V is.

So e still passes wherever the declared array is taken; a V that is already a non-empty tuple is kept as it is.

  type NonEmpty<V, Item> = V extends readonly [unknown, ...unknown[]] ? V : V extends Item[] ? [Item, ...Item[]] : readonly [Item, ...Item[]];

NotificationHookInput type # line 5885

Added in 2.1.265

  type NotificationHookInput = BaseHookInput & {
      hook_event_name: 'Notification';
      message: string;
      title?: string;
      notification_type: string;
  };

NounEvent type # line 5896

Added in 2.1.265

The declared plugin nouns' methods as event rows (NounEventRow), one per <noun>.<method> that is a function; a member that is not is no event.

  type NounEvent = {
      [K in PluginNoun]: {
          [M in keyof EngineInterface[K] & string]: EngineInterface[K][M] extends (...args: infer Parameters) => infer Result ? NounEventRow<`${K}.${M}`, Parameters extends readonly [] ? NoArgs : Parameters[0], Awaited<Result>> : never;
      }[keyof EngineInterface[K] & string];
  }[PluginNoun];

NounEventName type # line 5905

Added in 2.1.265

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

  type NounEventName = keyof NounEventOf & string;

NounEventOf type # line 5914

Added in 2.1.265

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

declare module "claude-code" { interface EngineInterface { voice: Voice } }
  export type NounEventOf = {
      [E in NounEvent as E['name']]: E['args'];
  };

NounEventResult type # line 5922

Added in 2.1.265

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

  type NounEventResult<N extends NounEventName> = ValueOrDeny<NounValueOf[N]>;

NounEventRow type # line 5928

Added in 2.1.265

One method of a declared plugin noun as an event row: its event's name, argument (the method's first parameter) and value (its awaited result).

  type NounEventRow<Name extends string, Args, Value> = {
      name: Name;
      args: Args;
      value: Value;
  };

NounValueOf type # line 5938

Added in 2.1.265

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

  type NounValueOf = {
      [E in NounEvent as E['name']]: E['value'];
  };

On type # line 5953

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

Registers hook on the events pattern selects: one by name, every one under a namespace (classic.*), all (*), or all but some (!tool.*).

One function stands on every selected event (next.event says which), under a matcher for the inputs it matches; a plugin's registrations nest in order, first outermost; a repeat throws. Returns the Registration.

  export type On = {
      <P extends Pattern>(pattern: P, hook: NoInfer<HookFor<P>>): Registration<HookFor<P>>;
      <P extends Pattern, const M extends Matcher<Args<MatchedNames<P>>>>(pattern: P, matcher: M, hook: NoInfer<MatchedHook<P, M>>): Registration<MatchedHook<P, M>>;
  };

OnScreen type # line 5971

Added in 2.1.275

The part of a transcript message the surface that drew it has on screen: units first to last of the message's of, counted from its start.

The terminal counts the rows the site laid out, from its first (the blank row the engine draws above a message is its row 0; a hook's tree starts at its own); other surfaces count in their unit, so compare first / of there.

if (e.props.onScreen) shown.set(e.requestId, e.props.onScreen)
  else shown.delete(e.requestId) // the band's legend lists `shown`
19 lines
  type OnScreen = {
      /**
       * The unit inside the viewport, from 0: on the terminal, `7` when the
       * message's top seven rows are scrolled away.
       */
      first: number;
      /**
       * The unit inside the viewport, inclusive; at least `first`.
       */
      last: number;
      /**
       * The message's whole extent in the same unit, as laid out (a hook's tree
       * taller than the engine's counts its own rows); more than `last`.
       *
       * One surface's numbers say nothing of another's: what the phone shows the
       * terminal may not, and each `ui.render` carries its own surface's.
       */
      of: number;
  };

OpenMatcher type # line 5998

In the first published surface (2.1.259)

The keys a variant with a string index signature (an MCP tool's input) takes beyond its own: another variant's key as typed there; others free.

So a misspelt value for a key some variant declares (command: 5) is refused on every variant, not admitted by the open one.

  type OpenMatcher<I, All> = {
      readonly [K in Exclude<MatcherKeys<All>, KnownKeys<I>>]?: MatcherValue<MatcherValueOf<All, K>>;
  } & Readonly<Record<string, unknown>>;

OpEventName type # line 6005

In the first published surface (2.1.259)

The name of a call on $ the host serves, as an event.

  export type OpEventName = keyof OpEventOf;

OpEventOf type # line 6015

In the first published surface (2.1.259)

The calls on $ the host serves, as events: e is the call's argument as it crosses to the host, and every one is hookable by name and by on("*").

A hook above the caller passes it on, rewrites it, refuses it with { deny } or answers with { value }; core is the host's implementation. The calling hook alone is skipped, and next.origin names the caller.

317 lines
  export type OpEventOf = {
      /**
       * The argument of `$.model.complete(request)`.
       */
      'model.complete': ModelCompleteRequest;
      /**
       * The argument of `$.model.classify(text, labels, options)`.
       */
      'model.classify': {
          text: string;
          labels: readonly string[];
          options?: ClassifyOptions;
      };
      /**
       * The argument of `$.model.fork(request)`.
       */
      'model.fork': ModelForkRequest;
      /**
       * The clip and how to play it (`shouldLoop`, `gain`); the signal does not
       * cross.
       */
      'audio.play': {
          clip: AudioClip;
          shouldLoop: boolean;
          gain?: number;
      };
      /**
       * The argument of `$.audio.speak(text, { voice })`.
       */
      'audio.speak': SpeakRequest;
      /**
       * The argument of `$.mcp.call(server, tool, args)`.
       */
      'mcp.call': {
          server: string;
          tool: string;
          args: Record<string, unknown>;
      };
      /**
       * The argument of `$.session.cwd()`.
       */
      'session.cwd': NoArgs;
      /**
       * The argument of `$.session.root()`.
       */
      'session.root': NoArgs;
      /**
       * The argument of `$.session.model()`.
       */
      'session.model': NoArgs;
      /**
       * The argument of `$.session.turns()`.
       */
      'session.turns': NoArgs;
      /**
       * The argument of `$.session.id()`.
       */
      'session.id': NoArgs;
      /**
       * The argument of `$.session.messages(args)`: `{}` for the main
       * conversation, `{ agentId }` for one of its agents, `as` for the form.
       */
      'session.messages': SessionMessagesArgs;
      /**
       * The argument of `$.session.repo()`.
       */
      'session.repo': NoArgs;
      /**
       * The argument of `$.session.surface()`.
       *
       * @deprecated with `$.session.surface()`; hook `session.surfaces`
       */
      'session.surface': NoArgs;
      /**
       * The argument of `$.session.surfaces()`.
       */
      'session.surfaces': NoArgs;
      /**
       * The argument of `$.session.authorize()`.
       */
      'session.authorize': NoArgs;
      /**
       * The argument of `$.session.usage({ breakdown, columns })`.
       */
      'session.usage': SessionUsageArgs;
      /**
       * The argument of `$.session.version()`.
       */
      'session.version': NoArgs;
      /**
       * The argument of `$.turn.abort({ turnId })`.
       */
      'turn.abort': {
          turnId: string;
      };
      /**
       * The argument of `$.prompt.read()`.
       */
      'prompt.read': NoArgs;
      /**
       * The argument of `$.tool.list()`.
       */
      'tool.list': NoArgs;
      /**
       * The argument of `$.tool.register(spec)`.
       */
      'tool.register': Required<ToolSpec>;
      /**
       * The argument of `$.command.list()`.
       */
      'command.list': NoArgs;
      /**
       * The argument of `$.command.register(spec)`.
       */
      'command.register': CommandSpec;
      /**
       * The argument of `$.config.list()`.
       */
      'config.list': NoArgs;
      /**
       * The argument of `$.agent.list()`.
       */
      'agent.list': NoArgs;
      /**
       * The argument of `$.agent.register(spec)`: the agent type as the plugin
       * defined it. A hook above rewrites any of it; the type stays the caller's.
       */
      'agent.register': AgentSpec;
      /**
       * The argument of `$.ui.toast(text, { timeoutMs })`.
       */
      'ui.toast': {
          text: string;
          timeoutMs?: number;
      };
      /**
       * The argument of `$.ui.status(text)`; `text` undefined clears the line.
       */
      'ui.status': {
          text: string | undefined;
      };
      /**
       * The argument of `$.ui.log(text, { to })`; `to` is always present
       * (UiLogSink), and `next({ ...e, to: "debug" })` keeps a line off screen.
       */
      'ui.log': {
          text: string;
          to: UiLogSink;
      };
      /**
       * The argument of `$.ui.notice(tool_use_id, text)`.
       */
      'ui.notice': {
          tool_use_id: string;
          text: string | undefined;
      };
      /**
       * The argument of `$.ui.invalidate(event)`.
       */
      'ui.invalidate': {
          event: InvalidatableEventName;
      };
      /**
       * The argument of `$.ui.open({ id, title, focus })`; a hook above the
       * opener may retitle it or refuse it with `{ deny }`, never rename it.
       */
      'ui.open': PaneOpenArgs;
      /**
       * The argument of `$.ui.close({ id })` with `origin` `plugin`; the engine
       * raises it too, for the person (`person`) and an unload (`unload`).
       */
      'ui.close': PaneCloseInput;
      /**
       * The argument of `$.ui.panes()`.
       */
      'ui.panes': NoArgs;
      /**
       * The argument of `$.ui.copy({ text, surface })`, `surface` filled with
       * the session's first when left out; rewritable, deniable, answerable.
       */
      'ui.copy': UiCopyArgs;
      /**
       * The argument of `$.ui.blit(...)`: a Raster's `cells` or a keyed Image's
       * `source`; a hook above may rewrite either with `next`, or `{ deny }`.
       */
      'ui.blit': UiBlitArgs;
      /**
       * The argument of `$.fs.read(path, { as })`: `as` is `text` unless the
       * caller asked for `bytes`.
       */
      'fs.read': {
          path: string;
          as: FsReadAs;
      };
      /**
       * The argument of `$.fs.write(path, text)`.
       */
      'fs.write': {
          path: string;
          text: string;
      };
      /**
       * The argument of `$.fs.list(path)`.
       */
      'fs.list': {
          path: string;
      };
      /**
       * The argument of `$.fs.exists(path)`.
       */
      'fs.exists': {
          path: string;
      };
      /**
       * The argument of `$.fs.stat(path, { resolve })`: `resolve` is false
       * unless the caller asked where the path lands.
       */
      'fs.stat': {
          path: string;
          resolve: boolean;
      };
      /**
       * The argument of `$.fs.ancestors({ names, of, below })`.
       */
      'fs.ancestors': FsAncestorsRequest;
      /**
       * The argument of `$.store.get(key)`.
       */
      'store.get': {
          key: string;
      };
      /**
       * The argument of `$.store.set(key, value)`.
       */
      'store.set': {
          key: string;
          value: unknown;
      };
      /**
       * The argument of `$.store.delete(key)`.
       */
      'store.delete': {
          key: string;
      };
      /**
       * The argument of `$.store.keys()`.
       */
      'store.keys': NoArgs;
      /**
       * The argument of `$.state.get(ref)`: the reference itself, `plugin`, `key`
       * and a family member's `id`; identity, pinned.
       */
      'state.get': StateGetEvent;
      /**
       * The argument of `$.state.set(ref, value, { ifVersion })`: the reference,
       * the value, the condition, and `previous`, what stood there (the host's).
       *
       * A hook above rewrites `value` with `next({ ...e, value })`; the
       * reference is identity, pinned. Raised by the value's owner alone.
       */
      'state.set': StateSetEvent;
      /**
       * The argument of `$.clock.now()`.
       */
      'clock.now': NoArgs;
      /**
       * The argument of `$.clock.sleep(ms, { signal })`; the signal does not
       * cross, it aborts the dispatch.
       */
      'clock.sleep': ClockWait;
      /**
       * The argument of `$.clock.after(ms, fn)`: the wait before `fn`, which
       * stays in the plugin's environment and runs once the dispatch resolves.
       */
      'clock.after': ClockWait;
      /**
       * The argument of `$.clock.every(ms, fn)`, dispatched once per period:
       * `fn` runs each time a dispatch resolves, and the next period is asked.
       */
      'clock.every': ClockWait;
      /**
       * The argument of `$.http.fetch(url, init)`.
       */
      'http.fetch': {
          url: string;
          init?: HttpInit;
      };
      /**
       * The argument of `$.process.run(argv, init)`.
       */
      'process.run': {
          argv: readonly string[];
          init?: ProcessRunInit;
      };
      /**
       * The argument of `$.process.spawn(request)`: the request itself.
       */
      'process.spawn': ProcessSpawnRequest;
      /**
       * The argument of `$.settings.read({ source })`.
       */
      'settings.read': SettingsReadArgs;
      /**
       * The argument of `$.env.get(name)`; `name` is identity, pinned.
       */
      'env.get': {
          name: string;
      };
      /**
       * The argument of `$.env.set(name, value)`; `name` is identity, pinned,
       * and no `value` unsets.
       */
      'env.set': {
          name: string;
          value?: string;
      };
  };

OpEventResult type # line 6337

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

The result of a call on $ as its event's hooks see it: { value } (the call's answer) or { deny }.

  export type OpEventResult<N extends OpEventName = OpEventName> = ValueOrDeny<OpValueOf[N]>;

OpValueOf type # line 6343

In the first published surface (2.1.259) · changed in 2.1.260, 2.1.265, 2.1.267, 2.1.268, 2.1.269, 2.1.271, 2.1.274, 2.1.275, 2.1.277, 2.1.280, 2.1.281

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

100 lines
  export type OpValueOf = {
      'model.complete': ModelCompleteResult;
      'model.classify': string | undefined;
      'model.fork': ModelForkResult;
      'audio.play': void;
      'audio.speak': SpeakResult;
      'mcp.call': McpToolResult;
      'session.cwd': string;
      'session.root': string;
      'session.model': string;
      'session.turns': number;
      'session.id': string;
      /**
       * The conversation's messages, as rows or in Messages API form; `{ deny }`
       * for an `agentId` the session cannot read.
       */
      'session.messages': SessionMessagesValue;
      'session.repo': SessionRepo | null;
      'session.surface': RenderSurface | null;
      'session.surfaces': readonly RenderSurface[];
      /**
       * The credential handle, or null where the session holds no first-party
       * credential (a third-party provider, a cloud gateway, no login or key).
       */
      'session.authorize': SessionAuthorization;
      'session.usage': SessionUsage;
      /**
       * The engine's version, its release, and its build time when stamped.
       */
      'session.version': SessionVersion;
      'turn.abort': void;
      /**
       * The box as it stands; the empty box where the session draws none.
       */
      'prompt.read': PromptBox;
      'tool.list': ToolInfo[];
      'tool.register': {
          tool: string;
      };
      'command.list': CommandInfo[];
      'command.register': {
          command: string;
      };
      'config.list': ConfigRow[];
      'agent.list': AgentInfo[];
      'agent.register': {
          agent: string;
      };
      'ui.toast': void;
      'ui.status': void;
      'ui.log': void;
      'ui.notice': void;
      'ui.invalidate': void;
      /**
       * Whether a surface draws the pane now, or why it waits undrawn.
       */
      'ui.open': UiOpenResult;
      'ui.close': void;
      /**
       * The calling plugin's open panes, placed then unplaced, in open order.
       */
      'ui.panes': readonly UiPane[];
      'ui.copy': UiCopyResult;
      'ui.blit': UiBlitResult;
      /**
       * The text; `{ base64 }` when asked for bytes.
       */
      'fs.read': string | FsBytes;
      'fs.write': void;
      'fs.list': FsEntry[];
      'fs.exists': boolean;
      'fs.stat': FsStat;
      'fs.ancestors': readonly FsAncestor[];
      'store.get': unknown;
      'store.set': void;
      'store.delete': void;
      'store.keys': string[];
      /**
       * The value and the version it stands at; `undefined` at 0 when never
       * written.
       */
      'state.get': StateRead;
      /**
       * Whether the write landed, and the version the value stands at now.
       */
      'state.set': StateSetResult;
      /**
       * Milliseconds since the epoch.
       */
      'clock.now': number;
      'clock.sleep': void;
      'clock.after': void;
      'clock.every': void;
      'http.fetch': HttpResponse;
      'process.run': ProcessRunResult;
      'process.spawn': ProcessSpawnResult;
      'settings.read': Settings;
      'env.get': string | undefined;
      'env.set': void;
  };

OrderedOverloads type # line 6450

Added in 2.1.265

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

An empty group contributes nothing (Overloads<never> is unknown).

  type OrderedOverloads<Names extends EventName> = Overloads<Exclude<Names, LateOverload>> & Overloads<Extract<Names, 'classic.PreToolUse'>> & Overloads<Extract<Names, 'turn.abort'>> & Overloads<Extract<Names, NoArgsEvent>>;

Origin type # line 6460

Added in 2.1.267 · changed in 2.1.268

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.

The same pair a next.trace entry names its link by, and an event's provider its subject's definer by. Set by the host from where the call came from and where that plugin was seated; nothing a plugin writes.

  export type Origin = {
      /**
       * Whose hook made the `$` call, by name; `"engine"` for a call site,
       * `"client"` for a `Client` surface module's `ui.message` post.
       */
      readonly plugin: string;
      /**
       * Where that plugin sits among the chain's tiers (Tier); `"core"` for the
       * engine, the owning plugin's for a `client` post.
       */
      readonly tier: Tier;
  };

Overloads type # line 6476

In the first published surface (2.1.259)

One call signature per event in Names, intersected into an overload set.

  type Overloads<Names extends EventName> = UnionToIntersection<{
      [N in Names]: (e: Args<N>) => Promise<NextResult<N>>;
  }[Names]>;

PaneCloseArgs type # line 6484

Added in 2.1.265 · changed in 2.1.268

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.

  export type PaneCloseArgs = Omit<PaneCloseInput, 'origin'>;

PaneCloseInput type # line 6490

Added in 2.1.265 · changed in 2.1.268

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

  export type PaneCloseInput = {
      /**
       * What `$.ui.open` named the pane; pinned: `next(e)` passes it on.
       */
      id: string;
      /**
       * Who closes it, set by the engine: a hook that answers without `next`
       * keeps the pane open on `plugin` and `person`, never on `unload`.
       */
      origin: PaneCloseOrigin;
  };

PaneCloseOrigin type # line 6509

Added in 2.1.265 · changed in 2.1.268

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

unload drops a pane nothing draws any more (its plugin unloaded, or its drawing threw): it is gone before the hooks hear of it, and its opener's hooks do not run. next(e) passes the origin on as received; none sets it.

  export type PaneCloseOrigin = {
      /**
       * `plugin`, a plugin's `$.ui.close`; `person`, the person's close mark or
       * close key; `unload`, the engine's own.
       */
      kind: 'plugin' | 'person' | 'unload';
  };

PaneOpenArgs type # line 6527

Added in 2.1.265 · changed in 2.1.268, 2.1.271, 2.1.277

The argument of $.ui.open: which pane, its title, whether it asks the person's keyboard, its dialog manners, its size: rows inline, columns docked.

An open answering the person's input (a command or prompt they entered, a press) is placed at any width; one the plugin makes on its own waits undrawn below 144 terminal columns, 110 once they asked for that id (in this session or an earlier one, until they close the pane by hand), and the call resolves { isPlaced: false, reason } (UiOpenResult) saying so.

67 lines
  export type PaneOpenArgs = {
      /**
       * Names the pane: 1-64 of letters, digits, `_` and `-`. One pane per id:
       * opening an open id delivers the new title, never a second instance.
       *
       * Pinned at the hooks: `next(e)` passes it on; the title and focus rewrite.
       */
      id: string;
      /**
       * The pane's tab while more than one pane is open (with one, no title is
       * drawn), and the `title` its hook sees; the id when omitted.
       *
       * An unpaired surrogate half (a `.slice()` through an emoji) is drawn as
       * U+FFFD; a control character is refused.
       */
      title?: string;
      /**
       * A request, not a grant: the surface focuses (and raises) the pane only
       * while the prompt has the keys over an empty composer.
       *
       * An element of the band or a pane the person holds, text in the
       * composer, a dialog or a survey each refuse it: the pane opens without
       * the keyboard.
       */
      focus?: true;
      /**
       * While the pane holds the keyboard, the key that hands it back (Escape)
       * also closes it as the person's close does: `ui.close`, origin `person`.
       *
       * So does Escape at an idle, empty prompt once the prompt has the keys
       * again (the person typed, or `focus` was refused); over text, a turn, a
       * dialog, a footer selection, a viewed agent or a prompt mode it is theirs.
       *
       * @remarks A hook may refuse that close and keep it open. Left out, Escape
       *   returns the keys and the pane stays. Each open sets it anew, as a title.
       */
      closeOnEscape?: true;
      /**
       * While the pane is on screen the surface holds its transient toasts (the
       * plugin toast stack, the notification line) and shows them once it closes.
       *
       * As it does behind the engine's own side panel; pinned warnings still
       * show. Left out, toasts show as they come. Each open sets it anew.
       */
      holdToasts?: true;
      /**
       * The body rows the pane's content wants while seated inline above the
       * prompt: it opens that tall, up to what the layout spares, not a third.
       *
       * A request, not a grant: a size the person dragged or keyed the block
       * to wins, this session's or a kept one, and the dock ignores it. A
       * positive whole number; left out, a third. Each open sets it anew.
       */
      rows?: number;
      /**
       * The body columns the pane's content wants while docked beside a
       * fullscreen transcript: the dock opens that wide, floor to ceiling.
       *
       * A request, not a grant: a width the person dragged or keyed the dock
       * to wins, this session's or a kept one, and the inline block ignores
       * it. A positive whole number; left out, the share. Each open sets it anew.
       *
       * @example
       * await $.ui.open({ id: "browser", focus: true, rows: 24, columns: 100 })
       */
      columns?: number;
  };

Pattern type # line 6599

Added in 2.1.265

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

  export type Pattern = EventName | Glob | Negation;

PatternOver type # line 6608

Added in 2.1.265

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

A name none of them has is a compile error, as is a glob over a namespace none is under; a negation that selects none of them narrows e to never.

  type PatternOver<N extends EventName> = N | '*' | `${Namespace<N>}.*` | Negation;

PermissionBehavior type # line 6610

Added in 2.1.265

  type PermissionBehavior = 'allow' | 'deny' | 'ask';

PermissionDeniedHookInput type # line 6612

Added in 2.1.265 · changed in 2.1.274

  type PermissionDeniedHookInput = BaseHookInput & {
      hook_event_name: 'PermissionDenied';
      tool_name: string;
      tool_input: unknown;
      tool_use_id: string;
      reason: string;
      mcp_server?: McpServerProvenance;
  };

PermissionMode type # line 6624

Added in 2.1.265

Permission mode for controlling how tool executions are handled. 'default' - Standard behavior, prompts for dangerous operations. 'acceptEdits' - Auto-accept file edit operations. 'bypassPermissions' - Bypass all permission checks (requires allowDangerouslySkipPermissions). 'plan' - Planning mode, no actual tool execution. 'dontAsk' - Don't prompt for permissions, deny if not pre-approved. 'auto' - Use a model classifier to approve/deny permission prompts.

  type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan' | 'dontAsk' | 'auto';

PermissionRequestDecision type # line 6630

Added in 2.1.265

A classic.PermissionRequest answer's decision, as the classic hook's hookSpecificOutput.decision: allow (with a rewrite or rules) or deny.

  export type PermissionRequestDecision = {
      behavior: 'allow';
      updatedInput?: Record<string, unknown>;
      updatedPermissions?: PermissionUpdates;
  } | {
      behavior: 'deny';
      message?: string;
      interrupt?: true;
  };

PermissionRequestHookInput type # line 6640

Added in 2.1.265 · changed in 2.1.274

  type PermissionRequestHookInput = BaseHookInput & {
      hook_event_name: 'PermissionRequest';
      tool_name: string;
      tool_input: unknown;
      permission_suggestions?: PermissionUpdate[];
      mcp_server?: McpServerProvenance;
  };

PermissionRuleValue type # line 6648

Added in 2.1.265

  type PermissionRuleValue = {
      toolName: string;
      ruleContent?: string;
  };

PermissionUpdate type # line 6653

Added in 2.1.265

28 lines
  type PermissionUpdate = {
      type: 'addRules';
      rules: PermissionRuleValue[];
      behavior: PermissionBehavior;
      destination: PermissionUpdateDestination;
  } | {
      type: 'replaceRules';
      rules: PermissionRuleValue[];
      behavior: PermissionBehavior;
      destination: PermissionUpdateDestination;
  } | {
      type: 'removeRules';
      rules: PermissionRuleValue[];
      behavior: PermissionBehavior;
      destination: PermissionUpdateDestination;
  } | {
      type: 'setMode';
      mode: PermissionMode;
      destination: PermissionUpdateDestination;
  } | {
      type: 'addDirectories';
      directories: string[];
      destination: PermissionUpdateDestination;
  } | {
      type: 'removeDirectories';
      directories: string[];
      destination: PermissionUpdateDestination;
  };

PermissionUpdateDestination type # line 6682

Added in 2.1.265

  type PermissionUpdateDestination = 'userSettings' | 'projectSettings' | 'localSettings' | 'session' | 'cliArg';

PermissionUpdates type # line 6688

Added in 2.1.265

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

  type PermissionUpdates = NonNullable<ClassicHookInputs['PermissionRequest']['permission_suggestions']>;

PlayOptions type # line 6695

In the first published surface (2.1.259)

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

A loop needs the signal that ends it; a single play takes one as an option.

27 lines
  export type PlayOptions = {
      /**
       * Repeat the clip until `signal` aborts (the promise then resolves).
       */
      shouldLoop: true;
      /**
       * Linear gain, from 0 to 4; default 1.
       */
      gain?: number;
      /**
       * Stops the clip: playback ends at once and the promise resolves.
       */
      signal: AbortSignal;
  } | {
      /**
       * Play once.
       */
      shouldLoop?: false;
      /**
       * Linear gain, from 0 to 4; default 1.
       */
      gain?: number;
      /**
       * Stops the clip early, as above.
       */
      signal?: AbortSignal;
  };

PluginNoun type # line 6727

Added in 2.1.265

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

  type PluginNoun = Exclude<keyof EngineInterface & string, keyof CoreEngineInterface | Namespace<CoreEventName>>;

PluginOptions type # line 6741

In the first published surface (2.1.259)

A plugin's options as register(on, options) receives them: the values of the fields its manifest's userConfig declares, defaults filled in.

Stored in settings.json pluginConfigs[<plugin>].options (sensitive ones in secure storage), validated against the declared type before the module loads; a required field with no value fails the load, naming the field. A string field that declares options holds one of them: /config draws it as a picker over them, and a stored value outside them counts as unset, so its default applies. A --plugin-dir plugin's key is its plugin.json <name> (or <name>@inline).

  export type PluginOptions = Readonly<Record<string, string | number | boolean | readonly string[]>>;

PluginRegisterInput type # line 6751

Added in 2.1.269

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.

tier and uses are the host's reading of the module, what a gate holds it to; name, root, version and provenance are its own word (its manifest, its place), so a rule keyed on them is one a rename walks past.

28 lines
  export type PluginRegisterInput = {
      /**
       * The plugin's name, as its own plugin.json declares it.
       */
      name: string;
      /**
       * Where its hooks would stand among the chain's tiers (Tier); never
       * `core`, the engine's own.
       */
      tier: Exclude<Tier, 'core'>;
      /**
       * The plugin's directory (the one holding plugin.json), absolute.
       */
      root: string;
      /**
       * Its manifest's `version`, when it states one.
       */
      version?: string;
      /**
       * Where the plugin came from, as the loader keys it: `<name>@<marketplace>`
       * installed, `<name>@inline` by `--plugin-dir`, `<name>@builtin` bundled.
       */
      provenance: string;
      /**
       * What the module hooks and calls, as scanned (PluginRegisterUses).
       */
      uses: PluginRegisterUses;
  };

PluginRegisterResult type # line 6784

Added in 2.1.269

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

14 lines
  export type PluginRegisterResult = {
      /**
       * The module loads: its `engine.create` step runs and its hooks join.
       */
      allow: true;
      refuse?: undefined;
  } | {
      /**
       * The module does not load: no step, no hooks, no tools or commands;
       * the transcript names the plugin that refused and this reason.
       */
      refuse: string;
      allow?: undefined;
  };

PluginRegisterUses type # line 6806

Added in 2.1.269 · changed in 2.1.281

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.

Exact, since a module that spells on, $, $.env or a $.state reference other than literally does not load.

40 lines
  export type PluginRegisterUses = {
      /**
       * The patterns its `on(...)` registrations name, as written (`tool.call`,
       * `*`, `classic.*`, `!tool.describe`), in registration order, each once.
       */
      events: readonly string[];
      /**
       * What it calls on `$`, spelled `noun.method` (`fs.write`, `http.fetch`),
       * sorted, each once.
       */
      calls: readonly string[];
      /**
       * The environment variables its `$.env.get` and `$.env.set` calls name;
       * absent when it calls neither.
       */
      env?: {
          /**
           * The names its `$.env.get` calls spell, sorted, each once.
           */
          reads: readonly string[];
          /**
           * The names its `$.env.set` calls spell, sorted, each once.
           */
          writes: readonly string[];
      };
      /**
       * The named values its `$.state.get` and `$.state.set` calls refer to by
       * literal, each `{ plugin, key }`; absent when it calls neither.
       */
      state?: {
          /**
           * The values its `$.state.get` calls spell, sorted, each once.
           */
          reads: readonly StateName[];
          /**
           * The values its `$.state.set` calls spell, sorted, each once.
           */
          writes: readonly StateName[];
      };
  };

PluginStamp type # line 6854

Added in 2.1.271

Whose element: the plugin whose hook drew it, stamped by the runtime as the tree leaves it; a Box's or Text's group, a Client's or Raster's own.

A Button's press names its plugin the same way, beside its handle. Two plugins under one hover.scope string never share a group.

  type PluginStamp = {
      plugin: string;
  };

PluginState interface # line 6869

Added in 2.1.281

The named values plugins keep in the session ($.state), by plugin name then key, for declaration merging; empty by default.

A plugin declares its own in the contract it ships, inside declare module "claude-code"; a key's type is the value $.state.get answers and $.state.set takes, and a StateFamily key holds one value per id.

interface PluginState { swarm: { workers: Worker[] } }
  export interface PluginState {
  }

PostCompactHookInput type # line 6872

Added in 2.1.265

  type PostCompactHookInput = BaseHookInput & {
      hook_event_name: 'PostCompact';
      trigger: 'manual' | 'auto';
      /**
       * The conversation summary produced by compaction
       */
      compact_summary: string;
  };
Feedback