---
title: API
description: Every export of every entry point, from the TypeScript declarations that ship with the package.
---

<!-- Generated by scripts/docs.mjs from types/*.d.ts. Edit those, not this file. -->

# API

Version 0.9.0. This page is generated from the TypeScript declarations in `types/`, which a test keeps in step with `src/`. For what the pieces are for, start with [Commands and providers](../concepts/commands.md).

## `@d34dman/sutram`

What app authors use: the registry, formats, the policy, commandLine() and agentPrompt(), and the types every entry point shares. No DOM.

```ts
export type SchemaType =
  | "object"
  | "array"
  | "string"
  | "number"
  | "integer"
  | "boolean"
  | "null";

/** The subset of JSON Schema that command input and output use. */
export interface Schema {
  type?: SchemaType | [SchemaType, "null"] | ["null", SchemaType];
  title?: string;
  description?: string;
  properties?: Record<string, Schema>;
  required?: string[];
  items?: Schema;
  enum?: unknown[];
  examples?: unknown[];
  default?: unknown;
  format?: string;
  minimum?: number;
  maximum?: number;
  exclusiveMinimum?: number;
  exclusiveMaximum?: number;
  multipleOf?: number;
  minLength?: number;
  maxLength?: number;
  pattern?: string;
  const?: unknown;
  minItems?: number;
  maxItems?: number;
  uniqueItems?: boolean;
  $ref?: string;
  $defs?: Record<string, Schema>;
  [key: string]: unknown;
}

export declare const EXIT: Readonly<{
  OK: 0;
  FAILED: 1;
  USAGE: 2;
  NOT_FOUND: 127;
  CANCELLED: 130;
}>;
/** 0 done, 1 failed, 2 usage error, 127 not found, 130 cancelled, or a CommandError's own code. */
export type ExitCode = 0 | 1 | 2 | 127 | 130 | (number & {});

/** Thrown from run(), sets the result's code. 0 counts as 1: a thrown error is never success. */
export declare class CommandError extends Error {
  constructor(message: string, code?: ExitCode);
  code: ExitCode;
}

/** A contract with every client. register() refuses keys not in ANNOTATIONS. */
export interface Annotations {
  readOnly?: boolean;
  consequential?: boolean;
  /** Leaves the page. Implies reinitializes. */
  navigates?: boolean;
  /** May start Sutram again: run calls context.reinitializes() when it does. */
  reinitializes?: boolean;
  untrustedContent?: boolean;
}

/** The annotation keys register() accepts. */
export declare const ANNOTATIONS: readonly ["readOnly", "consequential", "navigates", "reinitializes", "untrustedContent"];

/** Who ran a command: "console", "agent", "code", "status", or a client's own name. */
export type Caller = "console" | "agent" | "code" | "status" | (string & {});

export interface RunContext {
  caller: Caller;
  signal?: AbortSignal;
  /**
   * Says Sutram starts again now, as the command is about to leave the page or
   * swap the page's commands. Throws unless the command declares reinitializes
   * or navigates.
   */
  reinitializes(): void;
}

export interface CommandOutput<D = unknown> {
  text?: string;
  data?: D;
}

/** A completion a command offers for one of its arguments. */
export type Suggestion = string | {
  value: string;
  label?: string;
  description?: string;
};

export interface CommandDefinition<I extends Record<string, any> = Record<string, any>, D = unknown> {
  /** Lowercase words separated by single spaces: "cart add". */
  name: string;
  /** Who owns it; help groups by this. */
  provider: string;
  /** A short name for people, such as "Save a form": tool title in WebMCP, and the question before a consequential run. */
  title?: string;
  description: string;
  /** JSON Schema for the input. Name parameters with one lowercase word, or words joined by _ (per_page). Leave it out for a command with no arguments. */
  input?: Schema;
  output?: Schema;
  /** Input keys that can be given without --, in order. The last takes the rest of the line. */
  positional?: string[];
  annotations: Annotations;
  /** The question asked before the command runs, when the page's policy asks. Gets the checked input, as run() does. It is asked again just before the run, and a different answer means the page changed meanwhile, so nothing runs: keep it free of things that change on their own, such as the time. */
  confirm?: (input: I) => string;
  /** Values for an argument, for completion. May be async. */
  suggest?: (
    key: string,
    partial: string,
    context: { input: Partial<I>; signal?: AbortSignal },
  ) => Suggestion[] | Promise<Suggestion[]>;
  view?: { columns?: string[] };
  examples?: string[];
  seeAlso?: string[];
  /** Returns { text?, data? }, a string (the text), or nothing. */
  run: (
    input: I,
    context: RunContext,
  ) => CommandOutput<D> | string | void | Promise<CommandOutput<D> | string | void>;
}

/**
 * A run's result. Each side names the other's fields as never, so `r.text`
 * and `r.message` type-check on any Result, and `"message" in r` narrows.
 */
export type Result<D = unknown> =
  | { code: 0; text?: string; data?: D; message?: never }
  | { code: Exclude<ExitCode, 0>; message: string; text?: never; data?: never };

export interface BatchHooks {
  begin?: () => unknown;
  commit?: () => unknown;
  rollback?: () => unknown;
}

export interface BatchEnd {
  /** Providers whose changes were undone. */
  rolledBack: string[];
  /** Providers whose changes stay because they have no batch hooks. */
  kept: string[];
}

/**
 * `expect`: the definition the caller checked or asked about. When the name maps to another by the time it runs, nothing runs (130).
 * `asked`: the question the user said yes to, from the command's confirm() (null when it has none). When it would ask something else by the time it runs, because the page changed meanwhile, nothing runs (130).
 */
export interface RunOptions {
  caller?: Caller;
  signal?: AbortSignal;
  expect?: CommandDefinition;
  asked?: string | null;
}

/** A started batch. Always finish it with commit() or rollback(): the next batch waits until you do. */
export interface Batch {
  run(
    name: string,
    input?: Record<string, unknown>,
    options?: { expect?: CommandDefinition; asked?: string | null },
  ): Promise<Result>;
  commit(): Promise<BatchEnd>;
  rollback(): Promise<BatchEnd>;
}

export interface BatchResult {
  code: ExitCode;
  message?: string;
  results: Result[];
  completed: number;
  total: number;
  rolledBack?: string[];
  kept?: string[];
  /** For an agent: the batch started Sutram again (see REINITIALIZED_NOTE). */
  note?: string;
}

export type ChangeEvent = {
  type: "register" | "unregister";
  name: string;
  def: CommandDefinition;
};
export type RunEvent = {
  name: string;
  input: Record<string, unknown>;
  caller: Caller;
};
export type ResultEvent = RunEvent & { result: Result };

export interface Registry {
  readonly formats: Formats;
  readonly policy: Policy;
  register<I extends Record<string, any>, D>(def: CommandDefinition<I, D>): () => void;
  get(name: string): CommandDefinition | undefined;
  has(name: string): boolean;
  list(): CommandDefinition[];
  onChange(fn: (event: ChangeEvent) => void): () => void;
  onRun(fn: (event: RunEvent) => void): () => void;
  onResult(fn: (event: ResultEvent) => void): () => void;
  /** Resolves once the page's commands are registered; a new promise after every reinitialize(). */
  readonly ready: Promise<void>;
  /** How many times Sutram started again on this page. */
  readonly generation: number;
  /**
   * Sutram starts again: clears events, refuses runs, and makes ready wait for initialized().
   * navigates: true says the page is being left; if it is not left after all, the browser
   * entry point calls initialized() again. A command's reinitializes() passes its navigates annotation.
   */
  reinitialize(options?: { navigates?: boolean }): void;
  /** The page's commands are registered: resolves ready and allows runs again. */
  initialized(): void;
  /** Called on every reinitialize(). navigates is true when the page is being left. */
  onReinitialize(fn: (event: {
    generation: number;
    navigates: boolean;
  }) => void): () => void;
  /** A console or agent run of a provider a batch is changing waits until the batch is done. */
  run(
    name: string,
    input?: Record<string, unknown>,
    options?: RunOptions,
  ): Promise<Result>;
  defineBatch(provider: string, hooks: BatchHooks): () => void;
  startBatch(options?: { caller?: Caller; signal?: AbortSignal }): Batch;
  /** One batch runs at a time; the next waits. A step with `def` runs only while its name still maps to that definition. */
  batch(
    steps: Array<{
      name: string;
      input?: Record<string, unknown>;
      def?: CommandDefinition;
      asked?: string | null;
    }>,
    options?: { caller?: Caller; signal?: AbortSignal },
  ): Promise<BatchResult>;
}

export declare function createRegistry(options?: {
  checkResults?: boolean;
  formats?: Formats;
  policy?: Policy;
}): Registry;
export declare function getRegistry(): Registry;
```

### parse

```ts
export interface Token { value: string; quoted: boolean }
export interface Parsed {
  def: CommandDefinition;
  input: Record<string, string | boolean>;
  help: boolean;
  json: boolean;
  wide: boolean;
  fields: string[] | null;
}
export type SpanKind =
  | "space"
  | "command"
  | "client"
  | "unknown"
  | "option"
  | "bad-option"
  | "value"
  | "string"
  | "arg";
export interface Completion { value: string; label: string; description: string }
```

### policy

```ts
export type Level = "none" | "consequential" | "changes";
export type Who = "person" | "agent";
/** `signal` aborts when the call is withdrawn: the question can go, and its answer no longer counts. */
export interface AskRequest {
  who: Who;
  caller: Caller;
  steps: Array<{ def: CommandDefinition; input: Record<string, unknown> }>;
  signal?: AbortSignal;
}
/** The user's answer: true for yes. A yes covers the one call it was asked about. */
export type Answer = boolean;

export interface Policy {
  get(): { person: Level; agent: Level };
  set(levels: Partial<{ person: Level; agent: Level }>): void;
  needs(def: CommandDefinition, who: Who | null | undefined): boolean;
  setAsker(fn: (request: AskRequest) => Answer | Promise<Answer>): () => void;
  hasAsker(): boolean;
  ask(request: AskRequest): Promise<boolean>;
}

export declare const LEVELS: readonly Level[];
export declare function createPolicy(levels?: Partial<{
  person: Level;
  agent: Level;
}>): Policy;
```

### formats

```ts
export interface FormatDefinition {
  type?: string;
  description?: string;
  validate?: (value: unknown) => boolean;
  display?: (value: any, options: { locale?: string }) => string;
  align?: "left" | "right";
}

export interface Formats {
  define(name: string, def: FormatDefinition): () => void;
  get(name: string): FormatDefinition | undefined;
  has(name: string): boolean;
  list(): Array<{ name: string } & FormatDefinition>;
  valid(name: string, value: unknown): boolean;
}

export declare const STANDARD_FORMATS: readonly string[];
export declare function createFormats(options?: { standard?: boolean }): Formats;
export declare function getFormats(): Formats;
```

### format

```ts
export interface Block {
  kind: string;
  text: string;
  head?: string;
  code?: ExitCode;
  commands?: CommandDefinition[];
}
export interface FormatOptions {
  locale?: string;
  formats?: Formats;
  json?: boolean;
  wide?: boolean;
  fields?: string[] | null;
}

/** Input as the command line a person would type. The line parses back to the same input. */
export declare function commandLine(
  def: CommandDefinition,
  input?: Record<string, unknown>,
): string;

/**
 * The default prompt that hands a task to an agent that drives a browser.
 * It sends the agent to the page's WebMCP tools, Sutram.exec() or the
 * console, in that order. `commands` is the page's help text; without it,
 * the agent starts with help.
 */
export declare function agentPrompt(
  task?: string,
  page?: { title?: string; url?: string; commands?: string },
): string;

/** What a site's prompt function gets: the page, and the default prompt to wrap or replace. */
export interface AgentPromptPage {
  title?: string;
  url?: string;
  commands: string;
  prompt: string;
}

/**
 * Ships a site's own agent prompt: the console's prompt button and
 * Sutram.agentPrompt() use it. Returns a function that brings back the
 * default. A function that throws or returns no text falls back to it.
 */
export declare function defineAgentPrompt(
  make: (task: string, page: AgentPromptPage) => string,
  options?: { registry?: Registry },
): () => void;
```

## `@d34dman/sutram/client`

For building a client of your own (a terminal, a console, a test harness): parse and complete command lines, check input, run status, format results. Covered by semver. No DOM.

```ts
// Declarations for @d34dman/sutram/client: what a client of the registry
// needs. Covered by semver.


export declare const RESERVED_WORDS: readonly ["help", "man", "status", "clear"];

export declare const CLIENT_OPTIONS: readonly ["help", "json", "wide"];

/** With { strict: true }, strings are not converted to the declared types (for values from code, such as event payloads). */
export declare function checkInput(
  schema: Schema,
  input?: Record<string, unknown> | null,
  formats?: Formats,
  options?: { strict?: boolean },
): { errors: string[]; value: Record<string, unknown> };

export declare function checkOutput(
  schema: Schema,
  data: unknown,
  root?: Schema,
  formats?: Formats,
): string | null;

export declare function isStatusCommand(def: CommandDefinition): boolean;

/** Runs every <provider> status command, or only those of `providers`. `run` runs each (registry.run by default); a batch passes its own run. */
export declare function runStatus(
  registry: Registry,
  options?: { caller?: Caller; signal?: AbortSignal; providers?: string[]; run?: (
    name: string,
    input: Record<string, unknown>,
    options?: { caller?: Caller; signal?: AbortSignal },
  ) => Promise<Result> },
): Promise<Array<{ provider: string } & Result>>;

export declare function tokenize(line: string): Token[];

export declare function parse(registry: Registry, line: string): Parsed;

export declare function suggest(registry: Registry, words: string[]): string | null;

export declare function highlight(
  registry: Registry,
  line: string,
): Array<{ text: string; kind: SpanKind }>;

export declare function complete(
  registry: Registry,
  line: string,
  options?: { signal?: AbortSignal; limit?: number },
): Promise<{ start: number; end: number; items: Completion[] }>;

export declare function splitLines(text: string): string[];

/** What the text leaves open at its end: a quote, `<<TAG` for a heredoc, or null. */
export declare function openQuote(text: string): '"' | "'" | `<<${string}` | null;

export declare function whoIs(caller: Caller): Who | null;

export declare function ownQuestion(
  def: CommandDefinition,
  input: Record<string, unknown>,
): string | null;

export declare function formatResult(
  result: Result,
  def?: Partial<CommandDefinition>,
  options?: FormatOptions,
): Block[];

export declare function blocksToText(blocks: Block[]): string;

export declare function formatHelp(registry: Registry, prefix?: string): Block[];

export declare function formatUsage(def: CommandDefinition): Block[];

export declare function formatMan(
  def: CommandDefinition | undefined,
  name?: string,
  formats?: Formats,
): Block[];

export declare function checkFields(
  def: CommandDefinition,
  fields: string[],
): string | null;

export declare function selectFields(
  data: unknown,
  fields: string[],
  schema?: Schema,
): { data?: unknown; error?: string };
```

## `@d34dman/sutram/console`

The console, <sutram-console>.

```ts
/** The console's own texts. Functions take the values they mention. */
export interface Messages {
  title: string;
  count: (n: number) => string;
  label: string;
  placeholder: string;
  inputLabel: string;
  copy: string;
  copyTitle: string;
  copied: string;
  copyFailed: string;
  prompt: string;
  promptTitle: string;
  close: string;
  closeLabel: string;
  yes: string;
  no: string;
  answered: (answer: Answer) => string;
  /** The question before a command without its own; gets its title when it has one. */
  run: (name: string, title?: string) => string;
  cancelled: string;
  /** Shown after a question whose call was cancelled before anyone answered. */
  withdrawn: string;
  answerHint: string;
  manWhich: string;
  manMissing: (name: string) => string;
  noStatus: string;
  pasted: (n: number) => string;
  batchConsequential: (n: number, names: string[]) => string;
  batchChanges: (n: number) => string;
  batchCancelled: string;
  batchMistake: (line: number, n: number) => string;
  batchNotLast: (name: string) => string;
  batchStopped: (line: number, n: number) => string;
  batchUndone: (line: number, n: number) => string;
  batchKept: (providers: string[]) => string;
  /** Shown before the commands of an agent's request the person declined in the approval dialog. */
  agentDeclined: string;
  /** The label of the visually hidden log that mirrors the output in the page. */
  logLabel: string;
}

export declare const MESSAGES: Readonly<Messages>;

/** The key left of 1, by position, outside text fields; Ctrl with it anywhere. */
export declare const DEFAULT_HOTKEY: "backquote ctrl+backquote";

export interface HotkeyBinding {
  binding: string;
  key: string;
  ctrl: boolean;
  alt: boolean;
  shift: boolean;
  meta: boolean;
}

/**
 * Reads space-separated bindings: modifiers (ctrl, alt, shift, meta, mod) and a
 * key. Letters and digits are the typed character; other keys are named like
 * KeyboardEvent.code in lowercase (backquote, slash, f2). "none" is no binding.
 */
export declare function parseHotkey(spec?: string): HotkeyBinding[];
/** The binding a key event matches, or null. Bindings without ctrl, alt or meta never fire in a text field. */
export declare function matchHotkey(
  e: Pick<KeyboardEvent, "key" | "code" | "ctrlKey" | "altKey" | "shiftKey" | "metaKey">,
  bindings: HotkeyBinding[],
  options?: { inField?: boolean },
): HotkeyBinding | null;

export declare class SutramConsole extends HTMLElement {
  registry: Registry;
  /** Assign some of MESSAGES' keys to translate them. */
  get messages(): Messages;
  set messages(value: Partial<Messages>);
  readonly inline: boolean;
  /** The bindings of the hotkey attribute, or of DEFAULT_HOTKEY. */
  readonly hotkeys: HotkeyBinding[];
  /** The key held to peek, from the peek attribute; null when the console does not peek. */
  readonly peekKey: "Alt" | "Control" | "Shift" | "Meta" | null;
  /** True while the console is faded by peek (the peeking attribute). */
  readonly peeking: boolean;
  /** Lines run, newest last: this page's, after the history store's. */
  history: string[];
  /** Where history is kept between pages. Null (the default): only on this page. */
  historyStore: HistoryStore | null;
  open(): void;
  close(): void;
  toggle(): void;
  /** Replaces what is in the input field, for example to suggest a command. */
  setInput(value: string): void;
  /** Runs one line as if it was typed. */
  submit(line: string): Promise<ExitCode>;
  /** Runs several lines as one batch, as a paste does. */
  runBatch(text: string): Promise<ExitCode>;
  print(text: string, kind?: string): HTMLElement;
  /** Waits its turn when another question is open; `lead` prints what it is about when it shows. */
  ask(
    message: string,
    options?: { lead?: (() => void) | null; signal?: AbortSignal },
  ): Promise<Answer>;
  /** The output as plain text, as the page's visually hidden log has it. */
  transcript(): string;
}

/**
 * Keeps console history between pages. Sutram stores nothing itself; the app
 * decides where the lines go and for how long.
 */
export interface HistoryStore {
  /** The lines kept so far, oldest first. */
  load(): string[] | Promise<string[]>;
  /** Called with all lines, oldest first, after every line run. */
  save(lines: string[]): void | Promise<void>;
}

export declare function defineConsole(tag?: string): void;

export interface MountAttributes {
  greeting?: string;
  locale?: string;
  /** Space-separated bindings such as "mod+k ctrl+backquote", or "none". Default: DEFAULT_HOTKEY. */
  hotkey?: string;
  /** Hold this key alone to fade the console and see the page: "alt", "ctrl", "shift" or "meta"; true means "alt". Off by default. */
  peek?: boolean | "alt" | "ctrl" | "shift" | "meta";
  messages?: Partial<Messages>;
  /** Keeps history between pages. Without it, history lasts as long as the page. */
  historyStore?: HistoryStore;
  [attribute: string]: unknown;
}

/** Without a container: the page's drop-down console. With one: a new inline console inside it. */
export declare function mountConsole(
  attributes?: MountAttributes,
  container?: Element | null,
): SutramConsole;
```

## `@d34dman/sutram/approval`

The approval dialog, <sutram-approval>: where the person approves what an agent wants to run.

```ts
/** The approval dialog's own texts. Functions take the values they mention. */
export interface ApprovalMessages {
  label: string;
  yes: string;
  no: string;
  /** The question before a command without its own; gets its title when it has one. */
  run: (name: string, title?: string) => string;
  agentAsks: string;
  agentAsksBatch: (n: number) => string;
  agentBatch: (n: number) => string;
  agentBatchConsequential: (n: number, names: string[]) => string;
  /** A batch step's own question, under the batch's; before is true when earlier steps may change what it says. */
  stepAsks: (name: string, question: string, before: boolean) => string;
}

export declare const APPROVAL_MESSAGES: Readonly<ApprovalMessages>;

/** What the dialog says about a request: the lead, one shortened line per command, and the question. */
export declare function approvalText(
  steps: AskRequest["steps"],
  messages?: Partial<ApprovalMessages>,
): { lead: string; lines: string[]; question: string };

/**
 * <sutram-approval>: a modal dialog where the person approves what an agent
 * wants to run. No has the focus; Escape answers no. Fires sutram-ask and
 * sutram-answer (bubbling, composed) with the steps and the question or answer.
 */
export declare class SutramApproval extends HTMLElement {
  get messages(): ApprovalMessages;
  set messages(value: Partial<ApprovalMessages>);
  /** Asks; questions asked while one is open wait their turn. An aborted call's question is withdrawn: no. */
  ask(request: AskRequest): Promise<Answer>;
  /** Answers the open question. */
  answer(yes: boolean): void;
}

export declare function defineApproval(tag?: string): void;

/**
 * Makes the approval dialog the policy's asker unless the page has one; the
 * dialog is added to the page when it first asks. Returns a function that
 * removes it as the asker. The console and dist/sutram.js call it.
 */
export declare function useApprovalDialog(registry?: Registry): () => void;
```

## `@d34dman/sutram/webmcp`

Commands as WebMCP tools, tools() and callTool().

```ts
export interface Tool {
  name: string;
  /** A short name for people, such as "Save a form". */
  title?: string;
  description: string;
  inputSchema: Schema;
  annotations: {
    readOnlyHint?: boolean;
    consequentialHint?: boolean;
    untrustedContentHint?: boolean;
  };
}

export interface ModelContextLike {
  registerTool(
    tool: Tool & { execute: (
      input: any,
      options?: { signal?: AbortSignal },
    ) => Promise<unknown> },
    options: { signal: AbortSignal },
  ): unknown;
}

export declare const STATUS_TOOL: Tool;
export declare const BATCH_TOOL: Tool;
/** Added to an agent's result when the run started Sutram again: wait for ready, then look again. */
export declare const REINITIALIZED_NOTE: string;
export declare function findModelContext(): ModelContextLike | null;
export declare function toolName(def: CommandDefinition): string;
export declare function toTool(def: CommandDefinition, formats?: Formats): Tool;
export declare function listTools(registry?: Registry): Tool[];
/**
 * Converts strings to the declared types, like console text; WebMCP calls do not. Follows the page's policy
 * for an agent: a call that needs approval is asked about through the page's asker, and does not run
 * when the page has none.
 */
export declare function callTool(
  name: string,
  input?: Record<string, unknown>,
  options?: { registry?: Registry; signal?: AbortSignal },
): Promise<Result | BatchResult | QuietBatchResult | {
  code: 0;
  data: { providers: Array<{ provider: string } & Result> };
}>;
/** sutram_batch with quiet: true: no results, only the one that failed. */
export type QuietBatchResult = Omit<BatchResult, "results"> & { failed?: Result };
/**
 * Runs command lines as pasted into the console: several lines as one batch,
 * checked first and approved once. Follows the page's policy, as callTool does. Resolves to the
 * console's text, each line after "› ".
 */
export declare function execLines(
  text: string,
  options?: { registry?: Registry; signal?: AbortSignal },
): Promise<{ code: number; text: string; completed: number; total: number }>;
export declare function connectWebMCP(
  registry?: Registry,
  modelContext?: ModelContextLike | null,
): () => void;
```

## `@d34dman/sutram/events`

Declared events, and the events list and events recent commands.

```ts
export interface SutramEvent<P = Record<string, unknown>> {
  id: number;
  name: string;
  payload: P;
  source: string;
  heardBy: string[];
}

export interface Events {
  define(name: string, def: { description?: string; payload?: Schema }): void;
  emit<P extends Record<string, unknown>>(
    name: string,
    payload?: P,
    options?: { source?: string },
  ): SutramEvent<P>;
  on(
    name: string,
    fn: (event: SutramEvent) => void,
    options?: { as?: string; hidden?: boolean },
  ): () => void;
  /**
   * The first event named `name` ("*" for any) with an id above `since`: one
   * still kept, or else the next one emitted. `since` defaults to the newest
   * id. Resolves to null after `timeout` ms (default 10000) or on abort.
   */
  next(
    name?: string,
    options?: { since?: number; timeout?: number; signal?: AbortSignal },
  ): Promise<Omit<SutramEvent, "heardBy"> | null>;
  list(): Array<{ name: string; description?: string }>;
  lastId(): number;
  clear(upTo?: number): void;
}

export declare function createEvents(
  registry: Registry,
  options?: { bufferSize?: number },
): Events;
export declare function getEvents(registry?: Registry): Events;
```

## `@d34dman/sutram/browser`

globalThis.Sutram: the registry and its helpers in one object, with /client as Sutram.client. Also dist/sutram.js.

```ts
export type SutramGlobal = {
  EXIT: typeof core.EXIT;
  CommandError: typeof core.CommandError;
  registry: core.Registry;
  events: Events;
  /** Resolves once the page's scripts have run and their commands are registered. Wait for it before the first call on every page, and after a run that started Sutram again. */
  readonly ready: Promise<void>;
  /** For a single-page app: its new commands are registered after Sutram started again. */
  initialized(): void;
  formats: core.Formats;
  defineFormat(name: string, def: core.FormatDefinition): () => void;
  register: core.Registry["register"];
  tools(): Tool[];
  call(
    name: string,
    input?: Record<string, unknown>,
    options?: { signal?: AbortSignal },
  ): ReturnType<typeof callTool>;
  /** Command lines as pasted into the console: several as one batch. Answers with the console's text. */
  exec(
    text: string,
    options?: { signal?: AbortSignal },
  ): ReturnType<typeof execLines>;
  /** The agent prompt for this page, with its commands: the site's own (defineAgentPrompt) or the default. page defaults to document.title and location.href. */
  agentPrompt(task?: string, page?: { title?: string; url?: string }): string;
  /** core.defineAgentPrompt for this page's registry. */
  defineAgentPrompt(make: (task: string, page: core.AgentPromptPage) => string): () => void;
  mountConsole: typeof mountConsole;
  connectWebMCP(): () => void;
  /** @d34dman/sutram/client, for pages that build a client of their own. */
  client: typeof client;
  /** Kits loaded as their own files: dist/nav.js adds nav, dist/page.js page, and so on. */
  kits: {
    nav?: typeof nav;
    page?: typeof page;
    user?: typeof user;
    messages?: typeof messages;
    lists?: typeof lists;
    forms?: typeof forms;
  };
};

declare global {
  // eslint-disable-next-line no-var
  var Sutram: SutramGlobal;
}

declare const Sutram: SutramGlobal;
export default Sutram;
```

## `@d34dman/sutram/internals`

Helpers Sutram uses itself. No semver promise: any release may change them.

```ts
// Declarations for @d34dman/sutram/internals: helpers Sutram uses itself.
// No semver promise.


export declare function deref(schema: Schema, root: Schema): Schema | undefined;

export declare function distance(a: string, b: string): number;

export declare function formatValue(
  value: unknown,
  prop?: Schema,
  options?: FormatOptions,
): string;

export declare function formatTable(
  rows: Record<string, unknown>[],
  itemSchema: Schema,
  root?: Schema,
  options?: FormatOptions & { columns?: string[]; maxWidth?: number },
): { head: string; rows: string[] };

export declare function synopsis(def: CommandDefinition): string;

export declare function longSynopsis(def: CommandDefinition): string | null;

export declare function annotationLabel(annotations?: Annotations):
  | "read-only"
  | "consequential"
  | "navigates"
  | null;

export declare function describeShape(
  schema: Schema,
  root?: Schema,
  depth?: number,
): string;

export declare function sentence(s: string): string;

export declare function formatsUsed(
  def: CommandDefinition,
  formats?: Formats,
): Array<{ name: string } & FormatDefinition>;
```
