Skip to main content
The SDK owns the provider logins used by its embedded runtime. Each app can manage its own accounts and credentials without reading keys from another tool’s config directory.

Login isolation

SDK 0.7 defaults useMachineLogin to false. The runtime starts with isolated provider logins, so a Claude or Codex login on the host machine is not silently reused. BREAKING from SDK 0.6: to keep using machine logins, pass useMachineLogin: true explicitly:
doctor() reports logins.mode (isolated, machine, or attached) and a per-provider logins.perProvider map (isolated, shared, or unknown). Check this before describing which credentials a thread can use. Isolation covers the providers’ sign-in folders. It does not cover API keys in your app’s own environment: the runtime inherits your process environment, so an ANTHROPIC_API_KEY, OPENAI_API_KEY or CURSOR_API_KEY set there is still visible to it. Pass an env to createAdeChat without them if your users should only ever run on their own logins and keys.

Provider accounts

SignInHandle also provides submitCode(code) and cancel(). done resolves to the refreshed account or rejects when sign-in fails or is cancelled. Always cancel a login the user abandons. The provider account API also has remove, rename, setDefault, signOut, and refresh. AccountInfo contains only the public account summary: id, provider, label, signedIn, isDefault, and optional email, plan, loginBroken, accentColor, createdAt, and usage. Runtime-only fields such as an absolute provider config path are removed before the result reaches the host. list() and refresh() include usage when the runtime has a matching quota row for that account:
Usage can be absent when the runtime has no usage tracker or the latest report has no row for that account yet. readAt is the quota reading time; note explains a stale, signed-out, failed, or not-yet-read state. Only accounts with an instanceId in the usage report receive per-account usage; one-login providers do not have one. client.providers.capabilities(provider) returns the runtime’s choices for permission modes, reasoning effort, fast mode, accounts, sign-in, API keys, presets and MCP (ProviderCapabilities). It throws unsupported if the runtime does not advertise provider capabilities.

Credentials and presets

Credentials are stored by the runtime, encrypted with the OS credential store (Keychain on macOS, DPAPI on Windows; on Linux, a file only this user can read). credentials.list() returns summaries with labels, provider, source, timestamps and a masked tail; the API key value is never returned. A key is filed under its API vendor, not the harness that uses it: a Claude thread runs on an anthropic key, Codex on openai, Kimi on moonshotai, Grok on xai. Other harnesses use their own name (cursor, droid, pi, opencode, qwen, copilot).
credentials.remove(id, provider?) removes a stored key. Custom endpoint presets use client.presets.list(), add(input), update(id, patch), and remove(id). Preset results omit key material. When a key lives in your own vault instead, pass its id to a thread and supply the value with resolveSecret:
The SDK pushes the value to the runtime on every open and resume. The runtime holds it in memory only, under the thread’s API vendor, so it is never saved in the thread record, the runtime’s key store or its account vault, and it is gone when the runtime exits. credentials.list() shows it with source: "host". If resolveSecret returns nothing, the open fails with invalid_option. Two harnesses read keys only from a config file: Droid (settings.json) and OpenCode presets (opencode.json). For those, the runtime writes the key into a private launch folder under the SDK home while the thread runs, exactly as it does for a stored key.

Accounts on a thread

Pass account, credential, or preset when opening a thread to select the provider instance, stored credential, or endpoint preset. These choices are saved with the thread and used again if the runtime loses the session and the SDK recreates it. thread.switchAccount(instanceId) changes the account for a live thread; check its result because the runtime can report busy, signed_out, or failed without switching.

Usage limits

New threads default to onUsageLimit: "wait". Choose another policy when creating the client:
usageLimit fires whenever a thread’s limit state changes: { key, sessionId, provider, status }. status is null when the limit clears; otherwise it carries state (armed, resuming, paused, opted_out or no_reset), resetAt, fireAt (when the runtime will resume on its own), and alternateAccount when another signed-in account still has room. "failover" continues the interrupted turn on that alternateAccount, once per limited turn. The runtime picks the account, because it has the quota reading. With no alternate, the thread waits for the reset. "wait" and "ask" only emit the event. Threads expose usageLimit.resumeNow() and usageLimit.continueOnAlternate() for an explicit choice.

Who can manage accounts

Only the client that started the runtime can manage accounts, credentials and presets. The SDK proves it with a secret it hands the runtime at launch, so the agents running inside your chats, which can reach the same runtime, cannot sign your users’ accounts out or touch their keys. A client created with attach: true, or one that found a runtime an earlier copy of your app left running, cannot manage accounts and gets unsupported. Dispose it, stop that runtime, and create the client again.

Electron renderers

Account, credential, and preset methods are refused for renderers by default. Enable them only for trusted frames by passing allowAccountManagement to registerAdeIpc:
The gate covers accounts.*, credentials.*, and presets.*. Credential values can cross into main only through credentials.add; list and verify responses contain summaries, not secret values.