> ## Documentation Index
> Fetch the complete documentation index at: https://ade-app.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Reference

> createAdeChat options, AdeChatClient, AdeThread, doctor(), errors, and the MCP capability report.

Published packages: [`@ade-dev/sdk`](https://www.npmjs.com/package/@ade-dev/sdk), [`@ade-dev/chat-ui`](https://www.npmjs.com/package/@ade-dev/chat-ui). Full narrative docs start at [ADE SDK](https://ade-app.dev/docs/sdk/overview.md).

> [!NOTE]
>
> This page walks the objects you use every day. For the complete export list with exact signatures, see the generated [API reference](https://ade-app.dev/docs/sdk/api.md) and [Types](https://ade-app.dev/docs/sdk/types.md).

## `createAdeChat(options)`

```ts theme={null}
function createAdeChat(options: CreateAdeChatOptions): Promise<AdeChatClient>
```

| Option | Type | |
| - | - | - |
| `home` | `string` | Required. Isolated state root. Created if missing. |
| `binaryPath` | `string` | Pin an `ade` binary. Skips every later resolution step. |
| `runtimeNodeModules` | `string` | `ADE_RUNTIME_NODE_MODULES`. Required with `binaryPath` when the native modules are not beside the binary. |
| `runtimeRoot` | `string` | `ADE_RUNTIME_ROOT`. Defaults to the parent of `runtimeNodeModules`. |
| `allowDownload` | `boolean` | Default `true`. `false` throws `runtime_unavailable` instead of reaching the network. |
| `channel` | `string` | Downloader channel: `latest` (default) or a release tag. |
| `logger` | `(line: string) => void` | Sidecar / client log sink. |
| `instructions` | `string \| { mode, text }` | Default instructions for every thread. A per-thread value replaces it. |
| `mcpHeaders` | `(key, serverName) => Record<string, string> \| undefined \| Promise<…>` | Supplies MCP header values at every create, resume, recreate and restart, and before each turn of a thread with remote servers (a changed value is pushed before the turn). May be async. Header values are never stored. See [MCP](https://ade-app.dev/docs/sdk/mcp.md#credentials-that-change). |
| `autoRestart` | `boolean \| { maxAttempts?, backoffMs? }` | Default off. Respawns the runtime after an unexpected exit and re-opens every live thread. `true` = 5 attempts, 1 s backoff that doubles. |
| `requireCompatibleRuntime` | `boolean` | Default off. `true` throws `runtime_incompatible` for a runtime outside `SUPPORTED_RUNTIME_RANGE`. |
| `source` | `"packaged"` | Set by `resolvePackagedRuntime()`. Makes `doctor().runtime.source` report `packaged`. |

`resolvePackagedRuntime(resourcesPath, { dir?, platform?, arch? })` returns `{ binaryPath, runtimeRoot, runtimeNodeModules, source: "packaged" }` or `null`. Spread it into the options. See [Bundling](https://ade-app.dev/docs/sdk/bundling.md#inside-a-packaged-app).

`SUPPORTED_RUNTIME_RANGE` is the runtime range this SDK build supports. See [Version compatibility](https://ade-app.dev/docs/sdk/runtime.md#version-compatibility) for each SDK version's range.

Runtime resolution runs in five steps: `binaryPath`, then an installed `@ade-dev/runtime-<target>` package, then the home cache, then `ade` on `PATH`, then a download. See [Bundling](https://ade-app.dev/docs/sdk/bundling.md).

## `AdeChatClient`

| Member | |
| - | - |
| `threads.open(key, opts?)` | Open-or-resume. Create requires `provider` + `model`. Resume reads the stored record. |
| `threads.list()` | Every chat in this home as [`ThreadSummary`](#threadsummary), archived ones included. Not sorted. |
| `threads.get(key)` | One chat's [`ThreadSummary`](#threadsummary), or `null` when the runtime has no session for the key. SDK 0.4. |
| `threads.delete(key)` | Interrupt a running turn, delete the runtime session and its transcript, and remove the key. |
| `threads.archive(key)` / `threads.unarchive(key)` | Keep the key and the transcript; `list()` reports `archived`. A running turn is interrupted first. |
| `on("exit" \| "transport" \| "restart" \| "threadLifecycle", cb)` | Runtime and thread lifecycle events. See [Runtime](https://ade-app.dev/docs/sdk/runtime.md#when-the-runtime-exits) and [Client events](#client-events). Returns unsubscribe. |
| `providers.status()` | Map of provider id → [`ProviderStatus`](#providerstatus). Served from the runtime's probe cache. |
| `providers.refresh()` | The same map with the probe cache bypassed. Spawns `--version` per provider. |
| `providers.onChange(cb)` | Full status list on each change. Never bypasses the cache. Returns unsubscribe. |
| `providers.capabilities(provider)` | Provider-specific permission, model, account and sign-in support. Throws `unsupported` when not advertised. |
| `accounts.list/add/remove/rename/setDefault/signOut/refresh` | Manage provider accounts. See [Accounts](https://ade-app.dev/docs/sdk/accounts.md). |
| `accounts.signIn(id, options?)` | Start a login and receive a [`SignInHandle`](#signinhandle). |
| `credentials.list/add/remove/verify` | Manage API keys; returned records never contain key values. |
| `presets.list/add/update/remove` | Manage custom endpoint presets. Returned records omit key material. |
| `models.list()` | Catalog. `providerId` must match a status entry to be selectable in chat-ui. |
| `models.onChange(cb)` | The whole catalog, each time a model's id, `isAvailable`, `connected` or `isDefault` changes. Not called on subscribe. Returns unsubscribe. SDK 0.4. |
| `models.defaultModel({ available?, providers?, prefer? }?)` | The model a new thread should use, or `null`. See [`pickDefaultModel`](#pickdefaultmodel). SDK 0.4; `prefer` is SDK 0.5. |
| `run(prompt, { provider, model, schema? })` | Run a temporary thread and delete it after completion. A schema response is parsed and validated, then retried once if invalid. See [One-shot runs](https://ade-app.dev/docs/sdk/testing.md#one-shot-runs). |
| `doctor()` | Structured health check. |
| `exportThread(key)` | Transcript as JSONL. |
| `dispose()` | Stop the runtime. Idempotent. Ends in-flight turns without `error`/`done`. |

## `threads.open` options

| Option | |
| - | - |
| `provider` | Any provider id present in the runtime model catalog; known provider ids are suggestions, not a closed set. |
| `model` | Catalog id (aliases resolved by the runtime). |
| `budget` | `{ maxTokens?, maxCostUsd? }`. Cumulative per-thread limit; stored with the key and enforced only when the runtime/provider reports the corresponding usage. |
| `permissionMode` | Provider-native permission mode, checked against provider capabilities when available. |
| `reasoningEffort` / `fastMode` | Provider-native reasoning tier and fast-mode flag, checked against provider capabilities when available. |
| `account` / `credential` / `preset` | Provider account instance, stored credential, or endpoint preset id. Create-only and persisted for recreation. |
| `permissions` | `"always-allow"` \| `"default"` \| `ThreadPermissionPolicy`. See [Permissions](https://ade-app.dev/docs/sdk/permissions.md). |
| `instructions` | `string \| { mode: "append" \| "replace", text }`. A bare string means append. |
| `cwd` | Absolute path the provider runs in. Created at mode `0755`. Defaults to `<home>/personal-chats/workspaces`. |
| `attachmentRoots` | `string[]`. Extra directories an absolute attachment path may point into. Same rules as `cwd`; at most 32. Runtime 1.2.82 or later. One refused entry fails the whole open, so test a candidate with `checkAttachmentRoot` or prepare the list with `filterAttachmentRoots` (SDK 0.5). See [Attachments](https://ade-app.dev/docs/sdk/threads.md#where-a-path-may-point). SDK 0.4. |
| `settingSources` | `"none"` (default) \| `"project"` \| `"user"` \| `"all"`. |
| `title` | Display title stored on the thread record. |
| `mcpServers` | Caller-owned servers for this thread. See [MCP](https://ade-app.dev/docs/sdk/mcp.md). |
| `loadUserMcpServers` | Opt the user's MCP back in. Explicit `false` ≠ omitting the field. |
| `refresh` | `{ mcpServers?, attachmentRoots? }`. Replaces the thread's MCP servers or attachment roots on a resume, so a new token, port or folder takes effect. The one option a resume does not ignore. A map or list identical to the one already in use is not sent again, so a host that repeats its own values costs no update call. |

Host configuration and provider launch choices persist on the thread record, so a resume that has to recreate a lost session rebuilds the same thread.

They apply on **create only**, with one exception: `refresh`. A resume reads `cwd`, `attachmentRoots`, `instructions`, `settingSources`, `permissions`, `permissionMode`, `reasoningEffort`, `fastMode`, `account`, `credential`, `preset`, `mcpServers` and `loadUserMcpServers` off the stored record and ignores what this call passed, logging one line per differing option. Open a different key to run under different configuration.

A **recreate** (the key is stored, but the runtime lost its session) uses the stored record first. This call's options fill only fields the record does not have. The SDK logs one line for each stored value it used.

```ts theme={null}
type ThreadPermissionPolicy = {
  allowedTools?: string[];          // exact names, or a trailing `*`
  deniedTools?: string[];           // wins over allowedTools
  autoApproveMcpServers?: string[]; // same as `mcp:<server>:*`
  sandboxRoot?: string;             // absolute; containment, not a working dir
  fallback: "ask" | "deny";         // required
};
```

What a policy is worth depends on the provider and, on Claude, on `fallback`. A deny fallback removes the unnamed mutating built-ins and scopes MCP to the servers the policy names. An ask fallback relies on the Agent SDK's permission prompt. Read `thread.permissionCapability` and see [Permissions](https://ade-app.dev/docs/sdk/permissions.md).

## `AdeThread`

| Member | |
| - | - |
| `id` / `key` | Runtime session id and caller key. |
| `mcpCapability` | Honesty report. `null` when MCP was not requested, **or** when an older runtime omitted the report — treat the latter as unverified if you asked. |
| `send(text, opts?)` | Dispatch a turn. Resolves when accepted, not when finished. |
| `title` | The thread's title, or `null`. |
| `model` | `ThreadModelSelection` — `{ modelId, provider, model, displayName }`, or `null` before the runtime reported one. |
| `steer(text, { attachments }?)` | Mid-turn follow-up. Does not start a turn. Text or at least one attachment. |
| `interrupt(options?)` | Stop the in-flight turn; `queue: "keep"` preserves queued messages, `"drop"` clears them. |
| `respond(requestId, response)` | Send `{ answers?: Record<string, string \| string[]>; decision?: "accept" \| "accept_for_session" \| "decline" \| "cancel"; responseText?: string \| null }` to `respondToInput`. Answers use question ids; plan approval requires explicit `"accept"` or `"decline"`. |
| `queue` | `list`, `cancel`, `edit`, `move`, `dispatch`, `cancelDispatched`, `restore` queued messages. |
| `stopTask(taskId)` / `slashCommands()` | Stop a provider task or list provider slash commands. |
| `recover(options?)` / `resolveUnprocessed(args)` | Recover an interrupted turn or resolve queued input the runtime could not process. |
| `usageLimit.resumeNow()` / `.continueOnAlternate()` | Resume after a usage limit or switch to another signed-in account. |
| `switchAccount(instanceId)` | Change this thread's provider account; inspect the result for busy or signed-out failures. |
| `setModel(id, opts?)` | Switch model/provider. Refused mid-turn unless `{ force: true }`. Returns the selection plus `capabilities` (the four reports for the new provider), and emits `capabilities_changed` on the `status` channel. |
| `update({ title?, reasoningEffort?, fastMode? }, { force? }?)` | Rename, or change effort or fast mode. A rename always works; the other two are refused mid-turn with `turn_in_flight` unless `{ force: true }`. Returns `{ title, model }`. |
| `retry()` / `editLast(text, { displayText?, attachments? }?)` | Run the last user message again, or a new one in its place. The old turn leaves the history. Claude and Codex, runtime 1.2.82. Returns `{ retractedFromSequence, historyGeneration, conversationRollback }`. See [Retry and edit](https://ade-app.dev/docs/sdk/threads.md#retry-and-edit). SDK 0.4. |
| `updateMcpServers(servers)` | Replace the thread's MCP servers. Takes effect on the next turn; mid-turn the runtime queues them for the turn's end. Returns the `mcpCapability`. Needs runtime 1.2.81 or later. |
| `refreshMcpHeaders()` | Re-resolve the thread's MCP header values through `mcpHeaders` and push them if they changed. `send`, `retry` and `editLast` already do this before each turn. |
| `history({ limit }?)` | Envelopes in transcript order. |
| `usage()` | Cumulative token totals, optional context window and provider-reported cost. `complete: false` means the SDK could not seed prior totals from transcript history. |
| `historyPage({ beforeSequence?, limit? }?)` | One page: `{ events, hasMore, nextBeforeSequence }`. Omit `beforeSequence` for the newest page. |
| `approve(itemId, decision, responseText?)` | Answer an approval. `"accept"` \| `"accept_always"` \| `"reject"`. Throws `approval_not_found` when it is already settled, and `invalid_option` for a question-shaped request. |
| `pendingApprovals()` | Outstanding requests, so a reload can restore cards. |
| `instructionsCapability` | `{ requested, level, mode, mechanism, detail }` or `null`. |
| `settingSourcesCapability` | `{ requested, level, value, mechanism, detail }` or `null`. |
| `permissionCapability` | `{ level, mechanism, residual }` or `null`. Refreshed after `setModel`. |
| `on("event" \| "status" \| "usage", cb)` | Subscribe. Returns unsubscribe. When the runtime exits, each live thread receives `{ type: "status", turnStatus: "failed", message, synthetic: true }` on `status`. |

## Login and usage options

| Option | Type | |
| - | - | - |
| `useMachineLogin` | `boolean` | Default `false` in SDK 0.7: provider logins are isolated per app. Set `true` to reuse the host machine's logins; **BREAKING default change from 0.6.** |
| `resolveSecret` | `(provider, credentialId) => string \| undefined \| Promise<…>` | Resolve a host-vault secret for a live thread session. The SDK never persists the value. |
| `onUsageLimit` | `"failover" \| "wait" \| "ask"` | Default `"wait"`. `"failover"` tries another signed-in account; `"ask"` emits `usageLimit` for the host to handle. |

`ThreadOpenOptions` also accepts `permissionMode`, `fastMode`, `account`,
`credential`, and `preset`. These launch settings persist and are reused when a
lost runtime session is recreated. See [Accounts](https://ade-app.dev/docs/sdk/accounts.md).

The three capability fields are `null` when the thread requested nothing, **and** when an older runtime reported nothing. `level` is `"applied"` | `"best-effort"` | `"ignored"` for the two host-config reports, and `"enforced"` | `"best-effort"` | `"unsupported"` for the permission one.

## Client events

| Event | Payload | |
| - | - | - |
| `exit` | `{ code, signal, error }` | The runtime this client spawned exited without `dispose()`. |
| `transport` | `{ state: "closed" \| "reconnected", error }` | The socket closed, or `autoRestart` brought it back. |
| `restart` | `{ attempt, ok, error, final }` | One `autoRestart` attempt finished. `final` (SDK 0.4) is `true` when no attempt follows: it succeeded, or it was the last one `maxAttempts` allows. |
| `threadLifecycle` | `{ key, change: "deleted" \| "archived" \| "unarchived" }` | A `threads.delete`, `archive` or `unarchive` through this client succeeded, from main or from a renderer over the bridge. SDK 0.4. |
| `usageLimit` | [`UsageLimitEvent`](#usagelimitevent) | A provider account reached a usage limit. SDK 0.7. |

## `SignInHandle`

| Member | |
| - | - |
| `on("update", listener)` | Receive the current and later [`SignInStatus`](#signinstatus) values. Returns unsubscribe. |
| `submitCode(code)` | Submit a code when the provider login requests one. |
| `cancel()` | Cancel the login attempt. |
| `done` | Resolves to the refreshed [`AccountInfo`](#accountinfo), or rejects on failure/cancellation. |

## `AccountInfo`

`id`, `provider`, `label`, `signedIn` and `isDefault`, with optional `email`,
`plan` and provider usage data. Account responses are summaries and omit
secret-valued fields.

## `SignInStatus`

| Field | |
| - | - |
| `state` | `running` \| `verifying` \| `succeeded` \| `failed` \| `cancelled` \| `expired` |
| `loginId`, `instanceId`, `provider` | The sign-in and provider account being authenticated. |
| `url`, `deviceCode`, `awaitingCode` | Optional browser/device login details and whether a code is requested. |
| `email`, `message` | The signed-in email, and the provider's message when a sign-in fails. |
| `startedAt`, `endedAt` | ISO timestamps; `endedAt` is present after completion. |

## `ProviderCapabilities`

Provider-specific supported permission modes, reasoning efforts, fast mode,
multiple accounts, sign-in method, API keys, presets and MCP support. Obtain it
with `client.providers.capabilities(provider)`; the runtime must advertise the
provider capability surface.

## `UsageLimitEvent`

```ts theme={null}
type UsageLimitEvent = {
  key: string;
  sessionId: string;
  provider: string;
  turnId?: string;
  message?: string;
};
```

`doctor().logins` reports the login mode (`isolated`, `machine`, or `attached`)
and whether each provider is `isolated`, `shared`, or `unknown`.

## `pickDefaultModel`

```ts theme={null}
function pickDefaultModel(
  models: readonly ModelCatalogEntry[],
  opts?: {
    available?: boolean;
    providers?: readonly string[];
    prefer?: (model: ModelCatalogEntry) => number;
  },
): ModelCatalogEntry | null
```

`available` (default `true`) keeps only models with `isAvailable` and `connected`. `providers` keeps only those providers and tries them in order. Within a provider, or within all of them when `providers` is absent, the catalog's `isDefault` model wins, then the first model the catalog lists. `client.models.defaultModel(opts)` is the same rule on a fresh catalog.

`prefer` (SDK 0.5) is your own ordering inside one provider's qualifying models. Return a number greater than zero to prefer a model: the highest score wins. Return 0 or less for "no opinion", and that model is ranked the usual way. A model nobody scores falls back to `isDefault`, then to catalog order, so `prefer` never removes a model from the pool. Use it to state a cost or family preference instead of keeping your own copy of the rule:

```ts theme={null}
// Start new chats on the newest Sonnet, not on the provider's most
// expensive default.
const start = await ade.models.defaultModel({
  prefer: (model) => (/sonnet/i.test(model.id) ? 1 : 0),
});
```

`isSupportedProvider(value)` narrows a string such as `ModelCatalogEntry.provider` to `AdeProvider` without a cast. `ModelCatalogEntry.provider` is deliberately typed open, because a downloaded catalog can name a provider newer than this SDK.

## `checkAttachmentRoot` and `filterAttachmentRoots`

Both are SDK 0.5. They answer the question "will the runtime accept this root?" before you send it, because one refused entry fails the whole `threads.open`.

```ts theme={null}
function checkAttachmentRoot(
  value: unknown,
  sdkHome: string,
): { ok: true; root: string } | { ok: false; reason: string };

function filterAttachmentRoots(
  value: unknown,
  sdkHome: string,
): { roots: string[]; dropped: Array<{ root: string; reason: string }> };
```

`checkAttachmentRoot` never throws. `ok: true` carries the canonical spelling the runtime will accept — the same one `threads.open` stores — so pass that, not your original string. `sdkHome` is the same `home` you give `createAdeChat`.

`filterAttachmentRoots` keeps every valid entry in list order, canonicalized and de-duplicated, capped at 32, and reports each refused or over-the-limit entry with its reason. Its `roots` is safe to pass straight to `threads.open`, so one bad entry no longer locks every chat:

```ts theme={null}
const { roots, dropped } = filterAttachmentRoots([downloads, musicDir, "/"], home);
for (const entry of dropped) log.warn(`attachment root ignored: ${entry.root} — ${entry.reason}`);
await ade.threads.open("main", { ...createOptions, attachmentRoots: roots });
```

A non-array argument still throws `invalid_option`: that is a programming error, not a root you meant to filter.

## `ThreadSummary`

| Field | |
| - | - |
| `key` | The caller key, or `null` for a chat created outside the SDK. |
| `sessionId`, `provider`, `model`, `title`, `status` | As the runtime reports them. |
| `startedAt`, `lastActivityAt`, `updatedAt` | ISO strings. `updatedAt` equals `lastActivityAt`. |
| `archived` | `true` after `threads.archive`. |
| `modelSelection` | `ThreadModelSelection` with `displayName`, or `null`. |

## Session summary

`AgentChatSessionSummary` is the runtime's record of a tracked session. Two fields report the Codex app-server collaboration mode when a live runtime has one:

| Field | |
| - | - |
| `codexEffectiveCollaborationMode` | `"default"` \| `"plan"`. Absent when the runtime did not report a mode. |
| `codexEffectiveCollaborationModeWasCleared` | `true` when a live Codex runtime confirms there is no accepted turn mode. |

`AgentChatCodexCollaborationMode` is that `"default"` | `"plan"` union.

## `ProviderStatus`

```ts theme={null}
type ProviderStatus = {
  provider: string;
  displayName: string;
  installed: boolean;
  binaryPath: string | null;
  version: string | null;
  authenticated: boolean;
  authMethod?: string | null;
  installCommand: string | null;
  loginCommand: string | null;
  docsUrl: string | null;
  available: boolean;
  requiresConfiguration: boolean;
  modelCount: number;
  stale: boolean;
  source: "probed" | "derived";
  checkedAt: string;
  detail?: string | null;
};
```

`source` is the field to read first. On a `"derived"` record — an older runtime with no `providers.status` RPC — `installed` is `modelCount > 0`, and `binaryPath`, `version` and the three remediation fields are `null`. `stale` is true when either the probe or the model catalog behind the verdict was served from the cache rather than produced by this call. It is not a claim that the record is past the runtime's TTL: a record served from inside the TTL is stale by this definition too.

## `doctor()`

```ts theme={null}
type DoctorReport = {
  ok: boolean;
  sdkVersion: string;
  binary: {
    path: string;
    version: string | null;
    source: "option" | "path" | "download" | "cache";
    checksumVerified: boolean;
  };
  runtime: {
    source:
      | "packaged"
      | "explicit"
      | "bundled-package"
      | "cached-download"
      | "path"
      | "downloaded"
      | "attached";
    binaryPath: string;
    version: string | null;
    runtimeRoot: string | null;
    nodeModulesPath: string | null;
    signature: { signed: boolean; authority?: string; accepted?: boolean | null } | null;
    downloadedThisSession: boolean;
    checksumVerified: boolean;
    compatibility: {
      supported: boolean;
      range: string;          // SUPPORTED_RUNTIME_RANGE
      version: string | null; // what the runtime reported
      note: string | null;    // why a version passed without a check
    };
  };
  socket: {
    path: string;
    connected: boolean;
    runtimeVersion: string | null;
    pid: number | null;
  };
  events: {
    mode: "push" | "drain" | "unavailable";
    epoch: string | null;
    gapsRecovered: number;
  };
  providers: Record<string, ProviderStatus>;
  logins: {
    mode: "isolated" | "machine" | "attached";
    perProvider: Record<string, "isolated" | "shared" | "unknown">;
  };
  threads: { tracked: number; live: number };
  recentErrors: Array<{ at: string; scope: string; message: string }>;
};
```

`sdkVersion` is injected at package build time from `package.json`. Compare it to `socket.runtimeVersion` when versions disagree.

`binary` is the 0.1.x view and keeps its four `source` values. A `packaged` or `attached` runtime reports `binary.source: "option"`. `runtime` is the 0.2 view: which of the five resolution steps produced the binary, and what the OS thinks of its signature. `runtime.signature` is `null` for "not known", never for "not signed" — see [Runtime](https://ade-app.dev/docs/sdk/runtime.md#doctorruntime). `accepted` is Gatekeeper's verdict on the outermost `.app` that contains the runtime, and `null` when it could not be judged. `runtime.compatibility` compares the runtime version with `SUPPORTED_RUNTIME_RANGE`; see [Runtime](https://ade-app.dev/docs/sdk/runtime.md#version-compatibility).

## Errors

`AdeError` with `code`:

`runtime_incompatible` · `binary_not_found` · `download_failed` · `checksum_mismatch` · `spawn_failed` · `connect_failed` · `handshake_failed` · `rpc_error` · `rpc_timeout` · `transport_closed` · `protocol_error` · `thread_not_found` · `invalid_option` · `invalid_output` · `runtime_unavailable` · `approval_not_found` · `unauthorized` · `disposed` · `turn_in_flight` · `unsupported` · `sign_in_failed`

`turn_in_flight` (SDK 0.4) is every refusal that exists only because a turn is running: `setModel` or a reasoning `update` without `{ force: true }`, and a runtime action that must wait for the turn to end. Nothing changed; wait for the turn to finish, or call `interrupt()`, then call again. Before 0.4 these used `invalid_option`.

`unsupported` (SDK 0.4): the provider or the runtime cannot do what you asked. `retry()` and `editLast()` throw it for a provider other than Claude or Codex, and on a runtime without the `rerunLastTurn` action.

A relative or refused `cwd` throws `invalid_option`. So does a bad `attachmentRoots` entry, a policy with no `fallback`, a `sandboxRoot` that is not absolute, and an `approve()` naming a question-shaped request — the SDK checks those before any RPC, and the engine's own `invalid_argument:` refusals are mapped onto the same code. Prepare an `attachmentRoots` list with [`filterAttachmentRoots`](#attachment-root-helpers) to keep one bad entry from failing the open.

`runtime_incompatible` comes from `createAdeChat({ requireCompatibleRuntime: true })` when the runtime version is outside `SUPPORTED_RUNTIME_RANGE`.

`invalid_output` is thrown by `client.run()` after the structured response fails JSON parsing or schema validation twice. `InvalidOutputError.rawText` contains the last model response. `client.run()` rejects with `rpc_timeout` when a turn outlasts `timeoutMs` (default 10 minutes), and with `unsupported` when the turn asks for an approval or an answer, which a one-shot run cannot give.

`sign_in_failed` rejects a sign-in handle's `done` when the sign-in failed, was cancelled, or expired; the handle's last status names which.

`approve()` throws `approval_not_found` when the item is already settled. `unauthorized` comes from an Electron bridge whose `authorize` hook refused the call.

## MCP capability

See [MCP servers](https://ade-app.dev/docs/sdk/mcp.md). Shape:

```ts theme={null}
type McpCapabilityReport = {
  strictRequested: boolean;
  level: "enforced" | "best-effort" | "unsupported";
  residual: string | null;
  mechanism: string;
  delivered: boolean;
};
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.