Skip to content

zidane/ipc/browser

zidane/ipc/browser — a zidane-server client that runs wherever a WebSocket does: browsers, workers, Node, Bun. No node:*, no process: the kit's server adapter (zidane-ui/adapters) and any web app build on it. Node and Bun clients that keep saved hosts, trust a CA or share a machine use zidane/ipc/remote, which adds those on top.

import { … } from 'zidane/ipc/browser'src/ipc/browser.ts

Functions#

createExtensionsClientFunction#

Wraps a connected client.

TypeScript
function createExtensionsClient(client: ExtensionsTransport): ExtensionsClient
Parameters
NameTypeDescription
clientExtensionsTransport

A connected client.

ExtensionsClient

The typed extension API client.

src/ipc/extensions-client.ts:177

createServerClientFunction#

Creates a client for a zidane-server and starts connecting. See the module docs for what it handles; ServerClient for what it offers.

TypeScript
function createServerClient(options: ServerClientOptions): ServerClient
Parameters
NameTypeDescription
optionsServerClientOptions

The server's url, the token, and optional transport and retry settings.

ServerClient

The client, connecting.

Example
import { createServerClient } from 'zidane/ipc/browser'

const server = createServerClient({ url: 'wss://zidane.example.com', token })
const hello = await server.ready
if (server.can('sessions.list'))
  console.log(await server.call('sessions.list', {}), hello.scope)

src/ipc/browser.ts:262

extensionInstallErrorDataFunction#

The ExtensionInstallErrorData of a call's error data, or undefined when it carries none.

TypeScript
function extensionInstallErrorData(data: unknown): ExtensionInstallErrorData | undefined
Parameters
NameTypeDescription
dataunknown

A JSON-RPC error's data, untrusted.

src/ipc/protocol.ts:436

formatJoinStringFunction#

<url>#<CODE>.

TypeScript
function formatJoinString(url: string, code: string): string
Parameters
NameTypeDescription
urlstring

The main server's client URL (wss://…).

codestring

The join code (XXXX-XXXX).

string

src/ipc/machine/join.ts:22

methodScopeFunction#

The scope a method needs, or undefined for a name outside the protocol.

TypeScript
function methodScope(method: string): 'chat' | 'admin' | undefined
Parameters
NameTypeDescription
methodstring

A method name from the wire.

'chat' | 'admin' | undefined

src/ipc/scopes.ts:380

pairBrowserFunction#

Trades a pairing code for a device token.

TypeScript
function pairBrowser(
  baseUrl: string,
  code: string,
  name?: string,
  options?: PairBrowserOptions,
): Promise<PairedBrowser>
Parameters
NameTypeDefaultDescription
baseUrlstring

The server's origin (https://box.example, or the ws(s):// URL the client uses). '': the page's own origin, for the web app the server serves.

codestring

The code as the user typed it (7KQM-3XFD; the server ignores case and separators).

name?string

The browser's label, used when the operator gave none: the token is named browser: <name>.

options?PairBrowserOptions{}

A fetch to use instead of the global one.

Promise<PairedBrowser>

The token, its id, scope and the server's name.

Example
import { createServerClient, pairBrowser, ServerPairError } from 'zidane/ipc/browser'

try {
  const { token } = await pairBrowser('https://box.example', '7KQM-3XFD', 'Firefox on the laptop')
  const client = createServerClient({ url: 'wss://box.example', token })
}
catch (error) {
  if (error instanceof ServerPairError && error.reason === 'expired')
    console.log('Run zidane-server pair again')
}

src/ipc/browser-pair.ts:93

parseJoinStringFunction#

{ url, code } from a join string (spaces around it, any case in the code, the dash optional); null unless the url is ws:// or wss:// with a host and the code is 8 symbols of the alphabet. The code comes back as XXXX-XXXX, the url without a trailing slash.

TypeScript
function parseJoinString(s: string): { code: string; url: string } | null
Parameters
NameTypeDescription
sstring

The join string as pasted.

{ code: string; url: string } | null

src/ipc/machine/join.ts:34

runBlockOnHostFunction#

Runs a reply's shell block on a host and resolves with the saved run. Rejects with the host's reason when the block could not run.

TypeScript
function runBlockOnHost(
  client: BlockRunClient,
  sessionId: string,
  request: BlockRunRequest,
  options?: BlockRunOnHostOptions,
): Promise<BlockRun>
Parameters
NameTypeDefaultDescription
clientBlockRunClient

The host's client.

sessionIdstring

The session the reply is in.

requestBlockRunRequest

The block.

options?BlockRunOnHostOptions{}

Output listener and stop signal.

Promise<BlockRun>

src/ipc/block-run.ts:37

scopeAllowsFunction#

Whether a connection granted granted may call a method that needs required.

TypeScript
function scopeAllows(granted: 'chat' | 'admin', required: 'chat' | 'admin'): boolean
Parameters
NameTypeDescription
granted'chat' | 'admin'

The connection's scope.

required'chat' | 'admin'

The method's scope (METHOD_SCOPES).

boolean

src/ipc/scopes.ts:48

Classes#

ServerCallErrorClass#

A call the server answered with an error: its JSON-RPC code and data.

TypeScript
class ServerCallError extends Error
TypeScript
new ServerCallError(message: string, code: number, data?: unknown)
Parameters
NameTypeDescription
messagestring

The server's message.

codenumber

The JSON-RPC error code.

data?unknown

The error's data.

ServerCallError properties
NameTypeDescription
codereadonlynumber

The JSON-RPC error code (IpcErrorCode names zidane's own).

data?readonlyunknown

The error's data, when it had some (a Forbidden refusal's required scope).

src/ipc/browser.ts:82

ServerConnectionErrorClass#

A call that couldn't be answered: the connection dropped, or the client was refused or closed.

TypeScript
class ServerConnectionError extends Error
TypeScript
new ServerConnectionError(message: string, state: ServerClientState)
Parameters
NameTypeDescription
messagestring

What happened.

stateServerClientState

The client's state when the call failed.

ServerConnectionError properties
NameTypeDescription
statereadonlyServerClientState

The client's state when the call failed: refused names why, for good.

src/ipc/browser.ts:101

ServerPairErrorClass#

A refused or failed pairing, with its ServerPairFailure.

TypeScript
class ServerPairError extends Error
TypeScript
new ServerPairError(reason: ServerPairFailure, status?: number)
Parameters
NameTypeDescription
reasonServerPairFailure

Why it failed.

status?number

The HTTP status, when the server answered.

ServerPairError properties
NameTypeDescription
reasonreadonlyServerPairFailure

Why it failed.

status?readonlynumber

The HTTP status, when the server answered.

src/ipc/browser-pair.ts:22

Constants#

CLIENT_SCOPESConstant#

A client token's reach, from the narrowest.

TypeScript
const CLIENT_SCOPES: readonly ['chat', 'admin']

src/ipc/scopes.ts:29

IPC_MIN_PROTOCOL_VERSIONConstant#

The oldest protocol version this build still speaks, as a client and as a zidane-server. A server accepts a client whose range (minProtocolVersion…protocolVersion) overlaps its own, and the connection speaks the lower of the two versions, so a web app deployed against one release keeps working after the server moves one version on. Raise it only when the code that speaks a version is removed. A local daemon keeps the exact-version fence: it is respawned on upgrade.

TypeScript
const IPC_MIN_PROTOCOL_VERSION: number

src/ipc/protocol.ts:383

IPC_PROTOCOL_VERSIONConstant#

Changes since v5. Entries marked additive, or "no version bump", did not raise the version. The current version is IPC_PROTOCOL_VERSION.

  • event signals carry an optional echo flag marking host-generated SEND/STEER echoes of the user's own message.
  • chat.pushQueued result gained the 'queued' delivered variant.
  • sessions.setModel now honors fallbacks: it persists the canonical metadata.modelChain and CLEARS a previously persisted chain when omitted/empty. It also accepts effort / modelOptions and resets both when omitted (a pick is atomic — no stale per-model knobs).
  • additive: extension.emit notification + extension:signal fan-out (the permission-gated per-extension cross-window bus, ctx.bus; removed in zidane 10). Older daemons ignore the unknown notification; older clients drop the unknown signal kind — no version bump needed.
  • additive: extension.emit / extension:signal carry an optional nonce (the emitter's per-process random id) so receivers dedupe on (extensionId, nonce, seq) instead of the collision-prone (extensionId, seq). Older peers omit it and fall back to the seq-only key — no version bump needed.
  • SessionRow.modelFallbacks is now populated from the session's persisted metadata.modelChain.
  • Default socket path moved from <stateDir>/daemon.sock to the dedicated <stateDir>/daemon/daemon.sock (dir chmod'd 0700) so the daemon can harden its socket dir without touching shared state files.
  • ADDITIVE (no version bump — older daemons answer "method not found", clients degrade to local compute): suggest.resolve RPC resolves inline ghost-text autocomplete daemon-side, and the suggest:changed signal fans the result out to every OTHER peer on the session so mirroring windows paint the same ghost without recomputing.
  • ADDITIVE (no version bump — optional field, older daemons simply omit it and clients fall back to the single suggestion): suggest.resolve results and suggest:changed signals carry the resolver's full ordered candidates list so daemon mode keeps ↑/↓ candidate rotation instead of degrading to one suggestion.
  • ADDITIVE (no version bump — optional() swallows "method not found" on an older daemon, which then simply keeps its stale registries as before): runtime.reload RPC drops the daemon's cached extension registries and retires live agents after a window reloaded its extensions.
  • ADDITIVE (no version bump): terminal.registerViewer / terminal.unregisterViewer. The desktop app registers as a client that can show terminals; agents only get terminal tools while one is attached. An older daemon answers "method not found" (and keeps giving every agent the tools, as before); an older desktop app on a newer daemon never registers, so its agents run without terminal tools.
  • ADDITIVE (no version bump): project / draft terminals (docs/PROJECT-SCOPE-AUDIT.md): terminal.projectState, terminal.openInProject, terminal.adopt, terminal.releaseDraft, the terminal:project-changed signal, and HelloResult.projectTerminals. An older daemon answers "method not found" and omits the flag; clients then offer terminals only in sessions.
  • ADDITIVE (no version bump): queue.attachments returns a queued entry's attachments with their bytes, and queue.update accepts attachments to replace them. An older daemon answers "method not found" / ignores the field: the entry's attachments stay as queued.
  • ADDITIVE (no version bump): chat.steer / chat.followUp resolve to whether the message reached a live run. Older daemons resolve null, which clients treat as delivered (the pre-existing behavior).
  • ADDITIVE (no version bump): sessions.fork, sessions.deleteTurn, sessions.editTurnText, sessions.setFolder, sessions.appendTurns, sessions.rewind. History and folder edits run in the host instead of being written to the store by the client (then sessions.touch + chat.rebuildAgent). Older clients keep the old path, which still works on a shared disk. A newer client only meets an older daemon while it retires (the release fence drains it otherwise), and then gets "method not found".
  • ADDITIVE (no version bump): sessions.listMeta, sessions.getMeta, sessions.get, sessions.setExtensionState serve a client the session data it used to read and write in its own store, so a window on another machine sees the host's sessions. sessions.create accepts effort, modelOptions and pinnedSkills; todos.set accepts persistIdle. An older host answers "method not found" / ignores the fields: a window falls back to the store it shares with a local daemon, and refuses a remote host.
  • ADDITIVE (no version bump): daemon.info reports activeWork (a run, compaction, / command or background task in flight). An older host omits it; zidane server wait-idle then says it can't tell.
  • ADDITIVE (no version bump): sessions.loadEvents paging accepts beforeTurnId (the page ends right before that turn, winning over before; an unknown turn is invalid params) and the page echoes it. An older host ignores the param and omits the echo, which is how a client tells.
  • ADDITIVE (no version bump): sessions.cacheImpact and sessions.cacheReport. An older host answers "method not found": pickers show no cache estimate and session details no Cache section.
  • ADDITIVE (no version bump): sessions.moveEnvironment. An older host answers "method not found", shown as the move's error. Its removeOldWorktree param and oldWorktree result came later: an older host ignores the param and answers with no result.
  • ADDITIVE (no version bump): a folder's git checkout and files (git.*, files.*, the caller-only git:clone:progress signal), sessions.export and hud.get. Clients find them in the hello's methods; an older host lacks them and those controls stay hidden. hud.get and the hud:state / hud:notify signals are gone since zidane 10: extensions show status through Slot items (extensions.ui.list, extensions:ui); a client that still asks for hud.get gets "method not found".
  • ADDITIVE (no version bump): sessions.titleStatus, artifacts.refresh, usage.summary, git.watch / git.unwatch (the watcher-only git:refs signal), files.mediaTicket (media at GET /zidane/media), project-scope extensions.create, and auth.status's customFields / configuredFields. The methods are in the hello's methods; an older host omits the fields.
  • ADDITIVE (no version bump): daemon.ownedProcesses. An older host answers "method not found"; the desktop app's performance monitor then counts none of its processes as the app's own.
  • ADDITIVE (no version bump): daemon.sessionProcesses. An older host answers "method not found"; the performance monitor then shows none of its processes under a session.
  • ADDITIVE (no version bump): sessions.setPolicy (and sessions.create's policy), integrations.register / update / list, mcp.instances and mcp.restart. An older host answers "method not found" and ignores the field.
  • ADDITIVE (no version bump): interactions.rehydrateSessions, tokens.revokeSelf, fs.mkdir, fs.searchDirs, the hello's clone and createFolders (git.clone and git.cloneCancel take a chat token), whenIdle on sessions.appendTurns, origin, literal and clientMessageId on the send methods, the delivery:changed and integrations:changed signals, and takenOver on run:end. An older host answers "method not found" or ignores the fields; an older client drops the unknown signals and reads a taken-over run as failed.
  • 14: the orchestration.* verbs act as the CONNECTION (its token, or the local window) instead of taking a mainId; createWorker takes tools / subscribe and an optional prompt; new orchestration.wait, orchestration.events, orchestration.pending, orchestration.answer (chat) and orchestration.approve (approve); answeredBy on interaction:resolved / approval:resolved and on interaction responses. A v13 peer still speaks: orchestration.main is unchanged and a stray mainId is ignored.
  • 15: plans replace todos. plans.get / plans.edit / plans.control and the plans:snapshot signal replace todos.get / todos.set and todos:snapshot. Not additive: the minimum version is 15 too, so an older peer is refused at hello instead of silently showing no plans.
  • 16: one way to send a message while a run works. chat.steer and chat.followUp are removed: queue.add with delivery takes their place, with attachments and skills. delivery gains auto (the default, which follows Settings.messageDelivery) and queue. Settings.queueMessages becomes Settings.messageDelivery. Not additive: the minimum version is 16 too.
TypeScript
const IPC_PROTOCOL_VERSION = 16

src/ipc/protocol.ts:371

IpcErrorCodeConstant#

JSON-RPC error codes specific to the zidane daemon. Lives here (not in daemon/server.ts) so clients can import the codes without pulling the server's module graph.

TypeScript
const IpcErrorCode: { readonly Forbidden: -32015; readonly HandlerFailed: -32014; readonly NotHandshaken: -32011; readonly RateLimited: -32012; readonly ResponseTooLarge: -32013; readonly VersionMismatch: -32010 }

src/ipc/protocol.ts:390

METHOD_SCOPESConstant#

The lowest client scope that may call each method.

TypeScript
const METHOD_SCOPES: { readonly [M in IpcMethodName]: ClientScope }

src/ipc/scopes.ts:53

SERVER_AUTH_CHECK_PATHConstant#

The path of a zidane-server's auth check, relative to its WebSocket URL's origin (same as SERVER_AUTH_PATH).

TypeScript
const SERVER_AUTH_CHECK_PATH = SERVER_AUTH_PATH

src/ipc/browser.ts:36

SERVER_AUTH_PATHConstant#

Where a browser client asks why it can't connect, relative to the server's origin: a token's scope, or the refusal an upgrade would get.

TypeScript
const SERVER_AUTH_PATH = '/zidane/auth'

src/ipc/browser.ts:34

SERVER_EXTENSIONS_UI_PATHConstant#

Where the ui bundles are listed (GET ?cwd=) and served (GET /<hash>.js).

TypeScript
const SERVER_EXTENSIONS_UI_PATH = '/zidane/extensions/ui'

src/ipc/extension-paths.ts:9

SERVER_EXTENSIONS_WEBVIEW_PATHConstant#

Where webview URLs are minted (POST ?cwd=&id=) and their files served (/<ticket>/…).

TypeScript
const SERVER_EXTENSIONS_WEBVIEW_PATH = '/zidane/extensions/webview'

src/ipc/extension-paths.ts:12

SERVER_MEDIA_PATHConstant#

Where a zidane-server serves workspace media (GET, files.mediaTicket), relative to its origin.

TypeScript
const SERVER_MEDIA_PATH = '/zidane/media'

src/ipc/browser.ts:38

SERVER_OAUTH_CALLBACK_PATHConstant#

Where a zidane-server takes a browser client's MCP sign-in back (auth.oauth.begin), relative to its origin.

TypeScript
const SERVER_OAUTH_CALLBACK_PATH = '/zidane/oauth/callback'

src/ipc/browser.ts:40

SERVER_PAIR_PATHConstant#

Where a browser trades a pairing code for a device token (POST, JSON), relative to the server's origin.

TypeScript
const SERVER_PAIR_PATH = '/zidane/pair'

src/ipc/browser-pair.ts:10

Interfaces#

BlockRunClientInterface#

What runBlockOnHost needs of a client: a call (a server client's call, an IPC client's request) and its signals.

TypeScript
interface BlockRunClient
BlockRunClient properties
NameTypeDescription
call<M extends 'sessions.runBlock' | 'sessions.runBlockCancel'>(method: M, params: IpcParams<M>) => Promise<IpcResult<M>>

Calls a method on the host.

onSignal(listener: (signal: HostSignal) => void) => () => void

Hears the host's signals; returns the unsubscribe.

src/ipc/block-run.ts:13

BlockRunOnHostOptionsInterface#

Options for runBlockOnHost.

TypeScript
interface BlockRunOnHostOptions
BlockRunOnHostOptions properties
NameTypeDescription
onOutput?(stream: 'stdout' | 'stderr', text: string) => void

Output as it arrives.

signal?AbortSignal

Stops the run on the host.

src/ipc/block-run.ts:21

ExtensionInstallErrorDataInterface#

The data of a HandlerFailed error from an extension install (catalog.install, extensions.changeOrigin) that found extensions in the source but not the one it needed: the installer's error code (name-mismatch, multiple-candidates, no-extension) and the names it found, for a client to list. Additive: older hosts send the names in the message only.

TypeScript
interface ExtensionInstallErrorData
ExtensionInstallErrorData properties
NameTypeDescription
candidatesstring[]

The extension names the source holds.

installErrorstring

The installer's error code: name-mismatch, multiple-candidates or no-extension.

src/ipc/protocol.ts:423

ExtensionsClientInterface#

The typed extension API client.

TypeScript
interface ExtensionsClient
ExtensionsClient properties
NameTypeDescription
announce(info: UiClientAnnounce) => Promise<void>

Tells the host what this window shows (send on focus and session change).

catalogProvenance(cwd: string, kind: CatalogKind) => Promise<CatalogItemProvenance[]>

Where each item of a catalog (kind) comes from, for a folder.

commands{ keymap: (cwd: string, sessionId?: string) => Promise<WireKeymap>; list: (cwd: string, sessionId?: string) => Promise<WireExtensionCommands>; onChange: (listener: (cwd: string, keymap?: { mode?: string }) => void) => () => void; popMode: (handle: string) => Promise<boolean>; pushMode: (cwd: string, mode: string, opts?: { priority?: number; sessionId?: string }) => Promise<string>; run: (cwd: string, id: string, opts?: { args?: string; sessionId?: string }) => Promise<boolean> }

The commands and keymap modes extensions register.

entries{ list: (cwd: string) => Promise<WireExtensionEntry[]>; onChange: (listener: (cwd: string) => void) => () => void }

The extensions of a folder (status, tui entry, ui bundle), and when to list them again.

integrations{ connectKey: (cwd: string, integrationId: string, key: string, methodId?: string) => Promise<WireIntegration>; disconnect: (cwd: string, integrationId: string, account?: string) => Promise<void>; flowInput: (flowId: string, inputId: string, value: string | undefined) => Promise<boolean>; list: (cwd: string) => Promise<WireIntegration[]>; onChange: (listener: (cwd: string) => void) => () => void; setDefault: (cwd: string, integrationId: string, account: string) => Promise<void>; start: (cwd: string, integrationId: string, methodId: string, onStep: (step: IntegrationFlowStep, flowId: string) => void) => Promise<WireIntegration> }

The integrations extensions declare, and their connections.

links{ open: (cwd: string, link: Omit<LinkOpenParams, 'cwd'>) => Promise<{ handled: boolean }> }

The extensions' link handlers.

onRpcEvent(listener: (event: RpcEventEnvelope) => void) => () => void

Hears each event an extension's RPC contract emits.

rpc<T = unknown>(contract: string, method: string, input?: unknown, opts?: { cwd?: string; signal?: AbortSignal }) => Promise<NoInfer<T>>

Calls an RPC method. Rejects with RpcCallError when the call fails (rpc.forbidden: the method's scope is above this connection's token).

serve(server: ExtensionsUiServer) => () => void

Answers the dialogs and UI requests the host routes to this window.

settings{ get: (cwd: string, sessionId?: string) => Promise<WireExtensionSettings>; onChange: (listener: (cwd: string) => void) => () => void; set: (params: SettingsSetParams) => Promise<void> }

The extensions' settings, with where each value comes from.

sources{ browse: (source: string) => Promise<ExtensionSourceListing>; changeOrigin: (params: { name: string; scope: 'user' | 'project'; source: string }) => Promise<{ message: string; name: string; origin: ExtensionInstallOrigin; progress: string[] }> }

Sources of extensions: what one holds, and moving an installed extension to another.

storage{ load: (cwd: string, loc: WireStorageLocation) => Promise<StorageSnapshot & { projectRoot?: string }>; onChange: (listener: (change: { area: StorageArea; extensionId: string; key: string; kind: 'extensions:storage'; projectRoot?: string; scope: StorageScope; sessionId?: string; value?: Json; version?: number }) => void) => () => void; set: (cwd: string, loc: WireStorageLocation, area: StorageArea, key: string, value: Json | undefined) => Promise<WireStorageSetResult> }

The extensions' synced storage.

ui{ invoke: (cwd: string, request: UiInvokeRequest) => Promise<unknown>; list: (cwd: string) => Promise<WireUiList>; onChange: (listener: (cwd: string, change: UiChange | { type: 'reset' }) => void) => () => void; onPanelChanged: (listener: (change: { cwd: string; extensionId: string; panelId: string; sessionId?: string }) => void) => () => void }

The UI items of extensions and the callbacks they register.

src/ipc/extensions-client.ts:43

ExtensionsTransportInterface#

What the client needs of a connection: an IpcClient (zidane/ipc) or a browser ServerClient (zidane/ipc/browser).

TypeScript
interface ExtensionsTransport
ExtensionsTransport properties
NameTypeDescription
onSignal(listener: (signal: HostSignal) => void) => () => void

Hears every signal the host sends.

request<M extends keyof IpcMethods>(method: M, params: IpcParams<M>) => Promise<IpcResult<M>>

Sends an IPC request and resolves with its result.

src/ipc/extensions-client.ts:152

ExtensionsUiServerInterface#

What a window does with what the host routes to it.

TypeScript
interface ExtensionsUiServer
ExtensionsUiServer properties
NameTypeDescription
client(request: UiClientRequest) => unknown

Runs a UI request (panel, navigate, composer); resolves with its result (boolean, or the open panel ids). notify resolves nothing.

dialog(request: DialogRequest, ctx: { dialogId: string; extensionId: string; sessionId?: string; signal: AbortSignal; validate: (key: string, value: unknown) => Promise<string | null> }) => Promise<unknown>

Shows a dialog; resolves with the raw answer (undefined: cancelled). signal aborts when the host gives up on it. A form dialog calls validate(key, value) as the user edits a field and before answering, and shows the message (null: valid) under the field. Answer only once every field validates: the host checks the answer again, and an invalid one makes the extension's form reject.

src/ipc/extensions-client.ts:28

HelloResultInterface#

Result of the hello handshake and of daemon.info.

TypeScript
interface HelloResult
HelloResult properties
NameTypeDescription
activeWork?boolean

daemon.info only: a run, compaction, / command or background task is in flight on this host (open terminals do not count). zidane server wait-idle waits for false before a restart. Additive.

adminCommands?{ pair: string; token: string }

How to get an admin token on this zidane-server: the commands to run on its machine, with its own binary, user and data dir (<device> is the name to fill in). token prints a token to paste into an app; pair prints a code for a browser. Absent: not a zidane-server. Additive.

appVersionstring

zidane version of the host.

buildId?string

Build fingerprint of the host code. 'unknown' in compiled binaries.

clone?boolean

Clients may clone repositories into its workspace roots (git.clone). A zidane-server's allowClone; a local host always does. Absent: an older host, where git.clone needs an admin token. Additive.

codeVersion?string

Version of the zidane code the host runs.

createFolders?boolean

Clients may create folders in its workspace roots (fs.mkdir). A zidane-server's allowCreateFolders; a local host always does. Absent: an older host, without fs.mkdir. Additive.

homeDir?string

The host process's home folder (os.homedir()). Clients show it as ~ in the paths they display; nothing else reads it. Absent: an older host. Additive.

hostId?string

Host identity (additive). hostId is a UUID persisted once in the host's data dir, so it survives restarts; the in-process host and the local daemon share the data dir, and so the id: they are the same computer.

hostKind?HostKind

What kind of host this is.

hostName?string

Display name of the host.

machineServe?boolean

This server lends itself as a machine to the hosts of its clients (machine.attach, allowAsMachine on). Absent: it doesn't. Additive.

methods?keyof IpcMethods[]

The methods this connection may call on this host: the ones it handles, within scope. Clients show what is offered instead of failing on what isn't. Additive.

minProtocolVersion?number

The oldest version the host still speaks (IPC_MIN_PROTOCOL_VERSION). Additive.

negotiatedProtocolVersion?number

The version this connection speaks: the lower of the client's and the host's. Additive: absent, the connection speaks protocolVersion.

pidnumber

Process id of the host.

projectTerminals?true

This host runs terminals without a session (terminal.openInProject and the other project / draft terminal methods). Absent: sessions only. Additive.

protocolVersionnumber

Protocol version the host speaks.

remoteInstalls?boolean

This server takes installs and removals from its clients (catalog.install / remove / update, mcp.upsert / remove; allowRemoteInstalls on). Absent: it doesn't (or predates them). Additive.

retiring?boolean

The daemon is older than a client that attached while it had active work: it accepted the handshake instead of draining (which would abort those runs) and exits once idle. Clients attach normally; their reconnect path lands on a fresh daemon after the exit.

scope?'chat' | 'admin'

The client scope this connection's token grants (./scopes): calls above it are refused. Absent: every method (a local window). Additive.

signalEpoch?string

Names this host process's signal numbering (SIGNAL_SEQ_FIELD): a position from another epoch cannot be resumed. Additive.

startedAtnumber

Start time of the host process, epoch ms.

sync?boolean

This server takes settings sync from a client's computer (sync.*). Absent: it doesn't. Additive.

syncCategories?SyncCategory[]

The sync categories it takes. Absent with sync: the ones before environments. Additive.

src/ipc/protocol.ts:2669

IpcMethodsInterface#

Typed method map for the daemon transport. Keys are JSON-RPC method names; each entry pins params/result so both the daemon dispatch table and the client proxy are checked against the same contract.

Scope: the chat core every client needs. Host-UI-specific surfaces (folder pickers, themes, keybindings) intentionally stay client-local.

TypeScript
interface IpcMethods extends HitlIpcMethods
IpcMethods properties
NameTypeDescription
agents.list{ params: { cwd: string }; result: HostAgentProfile[] }

The agent profiles cwd offers ('': no folder), as the agent picker lists them: a profile a setting turns off (Codemode) is left out. Additive.

approvals.denyAllfrom HitlIpcMethods{ params: { sessionId?: string }; result: void }

Denies every pending approval, or only those of sessionId when given.

approvals.listPendingfrom HitlIpcMethods{ params: Record<string, never>; result: WireApprovalRequest[] }

Lists the approval requests waiting for an answer.

approvals.resolvefrom HitlIpcMethods{ params: { decision: ApprovalDecision; requestId: string }; result: void }

Answers a pending approval request with a decision.

artifacts.badges{ params: { sessionIds: string[] }; result: Record<string, SessionBadgeEntry[]> }

The badges under sessions' titles (commits, pull requests, extension kinds with a badge), for up to ARTIFACT_BADGES_MAX sessions at once, keyed by session id. Each session's badges come from the extensions of the folder its newest artifact landed in. They change with the session's artifact:recorded / artifact:changed and with catalog:changed (extensions): read them again then. Additive.

artifacts.delete{ params: { artifactId: string; sessionId: string }; result: boolean }

Remove one artifact. false when the store has no artifact support. Fans out artifact:changed.

artifacts.list{ params: { kind?: string; limit?: number; projectRoot?: string | null; since?: number; uri?: string }; result: SessionArtifact[] }

Artifacts across sessions, filtered by project root (null = untagged sessions, omitted = every project) and/or kind/uri (reverse lookup).

artifacts.refresh{ params: { artifactId: string; force?: boolean; sessionId: string }; result: SessionArtifact | null }

Re-resolves one stored artifact (is the pull request merged?): its kind's resolve() runs on the host, with the extensions of the folder the artifact landed in. A change is stored and fans out artifact:changed. force asks past the kind's own freshness window. null when nothing changed (no resolver, nothing new, an unknown artifact). Additive.

artifacts.upsert{ params: { artifact: SessionArtifact }; result: SessionArtifact | null }

Upsert an artifact — the lazy resolve() write-back (refreshed label / meta). Same merge semantics as SessionStore.upsertArtifact; null when the session is unknown or the store lacks support.

auth.configure{ params: { customFields?: Record<string, string>; provider: string; value?: string }; result: HostProviderAuth }

Stores a provider's API key and its custom fields (a local server's URL, a cloud's region; onboarding.providers lists them), merged into the stored credential: a field left out keeps its value, an empty one is removed (a blank secret keeps the stored one), a required one may not be emptied, no value keeps the stored key. Additive.

auth.oauth.begin{ params: { account?: string; cwd?: string; kind?: string; mcpServer?: string; provider?: string; redirectUri?: string }; result: HostOAuthBegin }

OAuth sign-in with the browser on the CLIENT and the tokens on the host: the host starts a provider's login (provider) or an MCP server's (mcpServer, found in cwd's catalog, as catalog.mcps), keeping the PKCE verifier and state, and answers with the authorization URL. mode: 'loopback': the client listens at redirectUri (a provider's fixed port; for an MCP server the loopback URI the client passed) and forwards the code; 'browser': the client opens url (pasting a code if the page shows one); 'device': shows userCode. The flow belongs to this connection, is single-use and expires (expiresAt). Additive.

A client that can't listen (a browser) signs in to an MCP server through a zidane-server's own redirect: redirectUri is the server's /zidane/oauth/callback (SERVER_OAUTH_CALLBACK_PATH) at the origin the client reaches it by. The server takes the code there and the flow is 'browser' (the client opens url, then completes without a code). A server whose pre-registered client pins its redirect stays 'loopback': its page fails to load and the user pastes that page's address (complete with input).

account (a provider's only): the stable id of a stored OAuth account (AuthFailure.accountId) this sign-in re-authenticates. Its tokens replace that account's in place (same slot, id and name), instead of becoming a new active account. An id the host does not store is refused. Additive.

auth.oauth.cancel{ params: { flowId: string }; result: { cancelled: boolean } }

Abandon a flow of this connection.

auth.oauth.complete{ params: { code?: string; flowId: string; input?: string; state?: string }; result: HostOAuthComplete }

Deliver the code (code + state from the loopback, or input: what the user pasted) once, then wait for the host's token exchange; without either, just wait (device and polling flows). A wrong state or another connection's flow is refused. Tokens stay on the host: the result says what changed, never a secret.

auth.oauth.respond{ params: { flowId: string; stepId: string; value?: string }; result: HostOAuthBegin }

Answer the step a flow is waiting on (HostOAuthBegin.step before its URL, HostOAuthComplete.step after): a select's option id (none: cancel), a prompt's text. Answers with where the flow is now: its URL, or the next step. Then complete as before. Additive.

auth.remove{ params: { account?: string; provider: string }; result: { provider: HostProviderAuth; removed: boolean } }

Remove one stored credential (account: its key from auth.status) or, without one, every stored credential of the provider; then as auth.setApiKey. A key the host only has from its own environment is left alone. Additive.

auth.rename{ params: { account: string; label: string; provider: string }; result: HostProviderAuth }

Rename a stored OAuth account (account: its key from auth.status). Presentation only: nothing is rebuilt. Additive.

auth.setApiKey{ params: { key: string; provider: string }; result: HostProviderAuth }

Store an API key for one of the host's providers as its active credential, apply it to the host's environment, and rebuild agents (idle now, busy ones at run end). Returns the provider's new status. OAuth sign-in goes through auth.oauth.*. Additive.

auth.setPreferred{ params: { provider: string; target: 'oauth' | 'apikey' | { key: string } }; result: HostProviderAuth }

Make one of a provider's stored credentials the active one (target: an account key from auth.status, or the first stored one of a source), then as auth.setApiKey. A credential no longer stored is refused. Additive.

auth.status{ params: Record<string, never>; result: { providers: HostProviderAuth[] } }

The host's provider credentials, as the host's agents use them (its data dir's credentials.json and environment): per provider, whether something is configured, its stored API key / OAuth accounts (labels, which is active) and an env var it reads. Never a secret. Additive.

badges.actions{ params: { cwd: string; target: BadgeTarget }; result: BadgeMenuItem[] }

The extensions' items (ctx.registerBadgeActions) of a badge's menu in cwd; the built-in ones are the client's. Additive.

badges.run{ params: { cwd: string; id: string; source: string; target: BadgeTarget }; result: BadgeActionEffect }

Runs an extension's badge item; the effect (copy, open, a notice) is the client's to apply. Additive.

browser.registerHost{ params: { hostId: string }; result: { ok: boolean } }

Dev browser: a window offers to host the pages the agent's browser_* tools drive (the GUI's main process). Released when the connection drops. It then answers each browser:request signal addressed to it.

browser.respond{ params: { requestId: string; response: BrowserResponse }; result: { ok: boolean } }

Answers a browser:request signal with the host's result.

browser.unregisterHost{ params: { hostId: string }; result: { ok: boolean } }

Withdraws a window as dev browser host.

catalog.extensions{ params: { cwd?: string; kind?: string }; result: { extensions: HostCatalogExtension[] } }

Extensions installed for cwd.

catalog.install{ params: { all?: boolean; extension?: string; kind: HostInstallKind; overwrite?: boolean; scope?: 'user'; skills?: string[]; spec: string }; result: { installed: string[]; message: string; progress: string[] } }

Install a skill or an extension on the host, in its user's scope only (scope may only be 'user'): a GitHub (owner/repo[#ref][:dir], a GitHub URL, a skills.sh URL) or npm spec; paths are refused. skills / all pick from a multi-skill source (the multiple-candidates error lists them), extension from a multi-extension one (an extension's multiple-candidates or no-extension error carries the names found as ExtensionInstallErrorData). Runs the host's package managers and fetches code there: a zidane-server's allowRemoteInstalls gates it, every attempt is audited. Bounded in time; the progress lines come back with the result. Every window gets catalog:changed. Additive (remoteInstalls on ipc.hello).

catalog.mcps{ params: { cwd?: string; kind?: string }; result: { errors: HostCatalogError[]; servers: HostCatalogMcp[] } }

MCP servers: env and headers values, URL credentials and query values are redacted.

catalog.plugins{ params: { cwd?: string; kind?: string }; result: { plugins: HostCatalogPlugin[] } }

Plugins installed for cwd, with what each contributes.

catalog.remove{ params: { kind: HostInstallKind; name: string }; result: { message: string; removed: string } }

Remove a user-scope skill or extension of the host by name (its directory). As catalog.install.

catalog.skills{ params: { cwd?: string; kind?: string }; result: { skills: HostCatalogSkill[] } }

What the host discovered for the settings tabs, each entry with whether the host's settings enable it (an unset allowlist enables everything). cwd adds the project scope of that folder (inside the workspace roots on a server); without it, the user scope (the host user's home). The enabled lists are written with settings.set. Installing, removing and MCP sign-in stay on the host's machine. Additive.

catalog.update{ params: { kind: HostInstallKind; name: string }; result: { message: string; updated: string; upToDate: boolean } }

Update a user-scope extension installed from a source. As catalog.install.

chat.cancelWork{ params: { id: string; reason?: string; sessionId: string }; result: boolean }

Cancels one tracked work item by id, or every visible item with "all". Resolves true when something was cancelled. reason is passed to a cancelled tool call.

chat.deliveryState{ params: { clientMessageId?: string; requestId?: string; sessionId: string }; result: { runId?: string; settled: boolean; state: 'unknown' | DeliveryState } }

What became of a send, by its requestId, its clientMessageId, or both: the send request record, then the last announced delivery:changed state, then the queue and the transcript (origin.messageId). Resolves a send answered "already accepted" (SendRequestPendingError). state: the last known DeliveryState, or unknown when the host holds no trace. runId: the run that took it, when known. settled: nothing more will happen to it: a final state (answered, failed, interrupted, dropped), or, with unknown, it is in neither the queue nor the transcript and the send request record says no send of it runs (it never landed: send it again with a new requestId); without a requestId an unknown is never settled. Additive.

chat.interrupt{ params: { sessionId: string }; result: void }

Aborts the session's live run. Does nothing when no run is live.

chat.isRunning{ params: { sessionId: string }; result: boolean }

Resolves true while the session has a live run.

chat.listWork{ params: { children?: boolean; sessionId: string }; result: RunningWorkItem[] }

Lists the tool calls and background tasks the session's live run tracks and can cancel. Empty when no run is live. children: true adds the subagents' running calls (childId set), for their starts: a transcript read from disk times them too. Older hosts ignore it.

chat.pushQueued{ params: { activatedSkills?: string[]; clientMessageId?: string; keepDraft?: boolean; literal?: boolean; message: string; metadata?: Record<string, JsonValue>; origin?: TextOrigin; sessionId: string } & SendRequestId; result: { consumed?: 'steer' | 'followUp' | 'handled'; delivered: 'command' | 'send' | 'steer' | 'queued' } }

Delivers a queued message. Steers a live run, otherwise starts a run or runs it as a command. delivered says which path it took. keepDraft keeps the shared composer draft instead of clearing it on the other clients. The message goes through the prompt interceptors first, as chat.send; consumed says one took it (delivered is then send for handled, steer otherwise). Additive.

chat.rebuildAgent{ params: { sessionId: string }; result: void }

Retire the session's live agent so the next run rebuilds against fresh config/credentials (MCP login, extension reload). Run-end deferred when a run is in flight; HITL/queue state survives.

chat.send{ params: Omit<SendOptions, 'wake'> & SendRequestId; result: { consumed?: 'steer' | 'followUp' | 'handled'; runId: string } }

wake is host-internal (see SendOptions.wake) — not accepted over the wire. requestId: see SendRequestId. The prompt goes through the extensions' prompt transforms and prompt interceptors of the session's folder first, once per submission, on the host (clients never run them). consumed says one took it instead of a run (runId is then ''): handled (an extension consumed it), steer / followUp (delivered to the live run). Additive.

claudeImport.detect{ params: { cwd?: string }; result: ClaudeImportDetection }

What ~/.claude on the host holds: whether there is a setup, and its transcripts for cwd and in every project. Additive.

claudeImport.preview{ params: { cwd?: string }; result: OnboardingImportPreview }

What each piece of the Claude Code import would copy and skip. Additive.

claudeImport.run{ params: { cwd?: string } & ClaudeImportSelection; result: ClaudeImportSummary }

Imports a Claude Code setup into the host's own places: conversations into its session store, MCP servers, skills, commands and subagents into the host user's files. Idempotent (what is there is skipped, never overwritten). It copies commands the host will run: a zidane-server's allowRemoteInstalls gates it, every attempt is audited. Every window gets catalog:changed. Additive.

commands.completeArgs{ params: { argsPrefix: string; cwd: string; kind?: string; name: string }; result: CommandArgSuggestionSummary[] }

Argument suggestions for /name <prefix> — best-effort, empty on failure.

commands.list{ params: { cwd: string; kind?: string }; result: CommandSummary[] }

Extension-contributed / commands discovered for cwd.

completions.providers{ params: { cwd: string }; result: ExtensionCompletionProviderSummary[] }

The extension completion providers (ctx.registerCompletionProvider) of cwd a composer can render: a one-character trigger each, @ / # left to the built-in completers, first wins on id and trigger. Additive.

completions.submit{ params: { cursor: number; cwd: string; text: string }; result: void }

Runs the providers' submit hooks (resolveReferences, onSubmit) on a prompt about to be sent; resolves after them, or after 5 s. Additive.

completions.suggest{ params: { cursor: number; cwd: string; providerId: string; query: string; text: string }; result: ExtensionCompletionItemSummary[] }

One provider's items for query (the text after its trigger), at most 8. A newer call for the same provider on the connection aborts this one; [] past 5 s or when the provider throws. Additive.

daemon.info{ params: Record<string, never>; result: HelloResult }

Daemon identity and version: protocolVersion, appVersion, pid, startedAt and the host identity fields of the hello result.

daemon.ownedProcesses{ params: Record<string, never>; result: OwnedProcess[] }

The processes the daemon started for its own work (MCP servers, bounded tool runs such as git and linters), which lead their own process group like a session's command does. The desktop app's performance monitor counts them as the app's. Additive: an older daemon answers "method not found", read as none.

daemon.sessionProcesses{ params: Record<string, never>; result: SessionProcess[] }

The processes the daemon started for a session (its shell tool's commands and background tasks, its terminals, its own MCP servers), by session id. The desktop app's performance monitor shows what each session uses. Additive: an older daemon answers "method not found", read as none.

daemon.shutdown{ params: { whenIdle?: boolean }; result: void | { deferred?: boolean } }

Graceful drain: deny pending approvals, abort live runs, close the socket. Used by clients that detect a protocol-version mismatch and need the stale daemon gone before respawning.

whenIdle (version-skew retirement): a daemon with active work does NOT abort it. It answers { deferred: true } and exits once idle. Explicit stops (zidane daemon stop) omit it and drain immediately. Daemons predating the flag ignore it and drain immediately.

decisions.test{ params: { cwd?: string; kind?: string; model?: string }; result: HostDecisionTestResult }

Send one tiny decision with this host's credentials, to check a decision model works. model is "providerKey/modelId", tested exactly; omitted = the host's Settings.decisionModel (or the first available). Never fails: a broken model comes back as ok: false with a reason. Additive.

drafts.get{ params: { sessionId: string }; result: { attachments?: PromptAttachment[]; search?: { active: boolean; query: string }; shell?: boolean; text: string } }

Current shared draft for a session — backfill on window attach.

drafts.set{ params: { sessionId: string; shell?: boolean; text: string }; result: void }

Replace the session's shared draft. Fans out draft:changed to every OTHER peer. Ephemeral daemon state — drafts don't survive a daemon restart and are cleared when any window submits the prompt.

shell carries the prompt's shell mode OUT OF BAND — never encoded into the text, so a literal leading ! in a normal draft can't flip other windows into shell-execution semantics.

drafts.setAttachments{ params: { attachments: PromptAttachment[]; sessionId: string }; result: void }

Replace the shared draft's composing attachments (notification).

environments.list{ params: { cwd?: string; kind?: string }; result: EnvironmentDescriptor[] }

Every environment kind the host can create, described for a new session in cwd (availability, default, branch suggestion, options — secrets only as "configured"). What pickers and the Environments settings render. kind: a machine kind (local@laptop) says cwd is that machine's folder, checked against its roots instead of this host's (docs/MACHINES.md § Folders); the same on commands.*, catalog.* and models.list. Additive.

environments.update{ params: EnvironmentSettingsChange; result: void }

Apply one Environments-settings edit (enable, default, options, secrets), validated against the kind's schema. Settings changes fan out as settings:changed; secrets are stored host-side and never returned.

environments.verify{ params: { kind: string }; result: EnvironmentVerifyResult }

"Test connection" for a kind with its current options.

extensions.actions{ params: { cwd: string }; result: ExtensionActionSummary[] }

The action rows (registerConfigItem type 'action') the enabled extensions declare for cwd. Additive.

extensions.actionStatuses{ params: { cwd: string }; result: Record<string, ExtensionConfigActionStatus> }

The actions' passive status() probes, keyed extension:key; a probe that throws or takes over 3 s is left out. Additive.

extensions.browse{ params: { source: string }; result: ExtensionSourceListing }

The extensions of a source (owner/repo[#ref][:dir], a GitHub URL, an npm package, or a folder on the host), read from their manifests by the host with its GitHub login; nothing runs. Never fails for an unreadable source: the listing carries error (the default source Tahul/zidane then lists the native catalog, builtin: true). Each row's install is what catalog.install takes (spec, extension). Additive.

extensions.catalogs.provenance{ params: { cwd: string; kind: CatalogKind }; result: CatalogItemProvenance[] }

Which catalog items of cwd extensions added, changed or removed, with the extension that did it (Settings shows it). Additive.

extensions.changeOrigin{ params: { name: string; scope: 'user' | 'project'; source: string }; result: { message: string; name: string; origin: ExtensionInstallOrigin; progress: string[] } }

Install an installed extension again from another source (GitHub or npm): the source must hold an extension of the same name (else the name-mismatch install error, its data an ExtensionInstallErrorData with the names found); the folder is replaced only once the new copy validated, and .install.json records the new source, which catalog.update then follows. A zidane-server takes scope: 'user' only, gated and audited as catalog.install. Additive.

extensions.command.run{ params: CommandRunParams; result: boolean }

Runs a command by its published id (an extension command or a built-in). false when nothing ran. Additive.

extensions.commands{ params: { cwd: string; sessionId?: string }; result: WireExtensionCommands }

The extension commands of cwd with their effective bindings, scopes and modes, and the keymap mode on top for sessionId. Additive.

extensions.configSchema{ params: { cwd: string }; result: WireExtensionConfigSchema }

The options the enabled extensions declare for cwd (registerConfigItem), without the action items or the callbacks; the values live on the settings' extensionConfig. Additive.

extensions.create{ params: CreateExtensionInput; result: CreateExtensionResult }

Scaffolds a new extension and reloads the registries: in the host user's extensions folder (scope: 'user', the default), or in a project (scope: 'project': cwd's checkout, which must lie in the workspace roots; it loads once the project is trusted). Sessions stay in the roots: one may open in a project extension's folder, never in a user extension's. It writes code the host will run: a zidane-server's allowRemoteInstalls gates it, every attempt is audited. Every window gets catalog:changed. Additive.

extensions.entries{ params: { cwd: string }; result: WireExtensionEntry[] }

The extensions of cwd: status, folder, tui entry and compiled ui bundle. What a window draws extensions from in daemon mode, without running their setup. extensions:entries says when to list again. Additive.

extensions.integrations.connectKey{ params: { cwd: string; integrationId: string; key: string; methodId?: string }; result: WireIntegration }

Connects with an API key: verified by the extension, then kept in the credentials file. Additive.

extensions.integrations.disconnect{ params: { account?: string; cwd: string; integrationId: string }; result: void }

Removes the connection of account, or every connection of the integration when it is absent. Additive.

extensions.integrations.flowInput{ params: { flowId: string; inputId: string; value?: string }; result: boolean }

Answers a flow step that asked for input (inputId). value absent cancels the flow. Additive.

extensions.integrations.list{ params: { cwd: string }; result: WireIntegration[] }

The extension integrations of cwd (ctx.integrations.register) and their connections. Additive.

extensions.integrations.setDefault{ params: { account: string; cwd: string; integrationId: string }; result: void }

Makes the connection of account the integration's default. Additive.

extensions.integrations.start{ params: { cwd: string; flowId?: string; integrationId: string; methodId: string }; result: WireIntegration }

Runs an OAuth or command method. Its steps (open this URL, enter this code, paste the redirect) come to this connection as extensions:integration-flow with the returned flowId; the call resolves when the flow ends. Additive.

extensions.keymap.list{ params: { cwd: string; sessionId?: string }; result: WireKeymap }

Every command of cwd with its effective bindings, scope and mode (ctx.keymap.list), and the active mode stack for sessionId (ctx.keymap.active). Additive.

extensions.keymap.pop{ params: { handle: string }; result: boolean }

Pops a mode this connection pushed. Additive.

extensions.keymap.push{ params: { cwd: string; mode: string; priority?: number; sessionId?: string }; result: string }

Activates a keymap mode on the host for cwd (as ctx.keymap.mode.push) until extensions.keymap.pop or this connection closes. Returns its handle. Additive.

extensions.links.open{ params: LinkOpenParams; result: { handled: boolean } }

Offers a link the user opened to cwd's extensions' link handlers (LinkHandler), higher priority first; a handler that returns false, throws or times out passes it on. handled: false: open it the app's own way. Apps call it for every link they open, except with Alt held. Additive.

extensions.runAction{ params: { cwd: string; extension: string; key: string }; result: ExtensionActionRunResult }

Runs an action on the host. The config patch it returns comes back sanitized, not written: the client saves it (settings.set). One run of an action at a time. A token needs the action's scope (the action row's scope; default, and for config-declared actions, admin). Additive.

extensions.settings{ params: { cwd: string; sessionId?: string }; result: WireExtensionSettings }

The extensions' settings of cwd with values, locks and the layer each value comes from. sessionId adds that session's layer. Additive.

extensions.settings.set{ params: SettingsSetParams; result: void }

Writes one extension setting at a scope: user in the settings file, project in the project's extension-settings.json, session in memory; a secret goes to the credentials file, never a settings file. Refused for a locked, unknown or action key, a scope the setting doesn't allow, or a value that fails its checks. Additive.

extensions.storage.load{ params: { cwd: string } & WireStorageLocation; result: StorageSnapshot & { projectRoot?: string } }

Everything stored at a location of cwd's extension storage, and the project root its project/session files live under. Additive.

extensions.storage.set{ params: { area: StorageArea; cwd: string; key: string; value?: Json } & WireStorageLocation; result: WireStorageSetResult }

Writes (or, value absent, deletes) one storage entry. The extensions holding it see the change at once; every other connection gets extensions:storage. A stores write must be a { v, value } record of the store's version whose value passes its schema (checked against every runtime of the folder holding the store); otherwise it is refused and nothing changes. The result says which, with the entry as the host holds it now, for the client to reconcile. Additive (the result was void).

extensions.ui.invoke{ params: { cwd: string } & UiInvokeRequest; result: unknown }

Runs one callback of a UI item by its id (a panel's load / onAction, a reference resolve, a badge menu or action, a settings page action, status or check). Callbacks run on the host under their extension's guard. Additive.

extensions.ui.list{ params: { cwd: string }; result: WireUiList }

Every UI item of cwd's extensions, per token, in wire form, and each active extension's compiled ui bundle (bundles, by extension id). extensions:ui pushes each change after it. Additive.

files.diagnostics{ params: { cwd: string; disabled?: string[]; paths: string[]; timeoutMs?: number }; result: DiagnosticsResult }

Runs the project's own linters and typecheckers on paths (relative to cwd), cached until the files move. disabled: tool ids switched off; timeoutMs: per tool.

files.diagnosticsInvalidate{ params: { cwd: string }; result: void }

Drops the cached diagnostics of cwd: the next run checks again.

files.diagnosticsTools{ params: { cwd: string }; result: DiagnosticsToolInfo[] }

The diagnostics tools and whether each runs for cwd (configured, missing its binary, not used).

files.list{ params: { cwd: string }; result: FileEntry[] }

The files under cwd and the folders holding them, as @ completes them: git's list (ignore rules kept), else a walk; capped.

files.location{ params: { cwd: string }; result: FolderLocation }

Where cwd sits: its git checkout (the folder itself outside git) and its project (the main checkout for a worktree).

files.mediaTicket{ params: { ticket: string }; result: void }

Lets this connection's media play from GET /zidane/media (SERVER_MEDIA_PATH): ?ticket&cwd&path serves a file of a folder in the workspace roots, &side=old|new one side of its git change, with byte ranges, for an image, a video or audio only. The client picks the ticket (at least 32 random URL-safe characters) so an address is known before the call lands; it is good while this connection is open and carries its token's scope. One per connection: a new one replaces it. Only a zidane-server serves media. Additive.

files.resolvePaths{ params: { cwd: string; texts: string[] }; result: ResolvedPath[] }

What names in prose point at (files, folders, else branches), in order: what a transcript's path chips open.

files.search{ params: { caseSensitive?: boolean; cwd: string; query: string; searchId?: string }; result: ContentSearchResult }

Lines containing query (fixed string, at most 500 characters) under cwd: ripgrep, else git grep, else a walk. A later search with the same searchId on the connection cancels this one.

fs.listDirs{ params: { path?: string }; result: FsDirListing }

The folders in path (default: the first root), for a folder picker on the host's machine. Only inside the roots (real paths: .. and symlinks out are refused), no dotfiles. Additive.

fs.mkdir{ params: { name: string; parent: string }; result: FsDirEntry }

Creates folder name in parent (inside the roots, real paths: .. and symlinks out are refused). name is one path segment and does not start with a dot. Refused when the host turns it off (a zidane-server's allowCreateFolders, the hello's createFolders). Additive.

fs.roots{ params: Record<string, never>; result: FsRoots }

Where this host lets a session's folder be: a server's workspace roots (confined: sessions.create / setFolder refuse anything outside), or the user's home on a local host (unconfined). Additive.

fs.searchDirs{ params: { path?: string; query: string }; result: FsDirSearch }

The folders whose name holds query (case-insensitive), in path and below it (default: every root). Bounded: four levels down, 200 results, no dot folders, no node_modules, symlinks not followed. truncated: the result or read bound cut the search short. Additive.

git.branches{ params: { cwd: string }; result: GitBranchRow[] }

The local branches, most recently committed first, each against the default branch.

git.branchPullRequest{ params: { branch: string; cwd: string; fresh?: boolean }; result: GitPr | null }

branch's open (or draft) pull request, or null. fresh: past the cache.

git.checkout{ params: { cwd: string; sessionId?: string; target: GitSwitchTarget }; result: GitCheckoutResult }

Switches the checkout to a branch or a pull request ({ pr }, through gh). Never forced; refused while an agent runs in the checkout (sessionId: the asking session, counted too). A branch another worktree holds is git's refusal plus that worktree and its sessions (sessionId left out).

git.clone{ params: { input: string; parent: string }; result: RepoCloneResult }

Clones input (a git or forge URL, or owner/repo) into a new folder of parent (inside the roots). A folder already holding that repository is answered as existed. One clone per connection; git's progress comes as git:clone:progress signals to the caller only. A dropped connection cancels it. Refused when the host turns cloning off (a zidane-server's allowClone, the hello's clone).

git.cloneCancel{ params: Record<string, never>; result: void }

Cancels this connection's clone: the half-cloned folder is removed and git.clone answers cancelled.

git.commit{ params: { cwd: string; sha: string }; result: GitCommitInfo | null }

One commit's facts, for a sha chip; null when the repository has no such commit.

git.createBranch{ params: { cwd: string; name: string; sessionId?: string }; result: GitCheckoutResult }

Starts branch name from the current checkout and switches to it; refused while an agent runs in it.

git.diff{ params: { context?: number | null; cwd: string; path?: string }; result: string }

The working tree against HEAD as a unified patch (untracked files as new files), the whole checkout or one path, with context lines (absent: 3; null: whole files).

git.fileStatus{ params: { cwd: string }; result: GitStatusEntry[] }

Each changed path's status against HEAD, untracked files included.

git.pullRequests{ params: { cwd: string; fresh?: boolean; search?: string }; result: GitPrListResult }

Open pull requests (gh): waiting on you, yours, then recent others; search searches every open one. null without gh (or signed out); rateLimited while GitHub refuses and nothing is cached.

git.status{ params: { cwd: string }; result: GitInfo | null }

Where the checkout stands (branch, upstream, ahead / behind, uncommitted changes); null outside a checkout.

git.sync{ params: { cwd: string; sessionId?: string }; result: GitSyncResult }

Pulls (merging) then pushes the current branch, or publishes it without an upstream; refused while an agent runs in it.

git.unwatch{ params: { cwd: string }; result: void }

Releases one git.watch of cwd by this connection.

git.watch{ params: { cwd: string }; result: boolean }

Follows cwd's checkout: once its refs move (a commit, a fetch, a push, a branch switch, from this host or any other process), this connection gets git:refs for cwd. false: not a repository, nothing watched. Calls count: each git.watch takes one git.unwatch; a dropped connection releases its own. Additive.

history.searchAll{ params: { excerptLimit?: number; query: string; sessionIds: string[] }; result: Record<string, SessionHistoryMatch> }

Cross-session content search, daemon-side. One RPC replaces a window's per-session index/build/search round-trips — indexes are built once in the daemon and shared by every window.

history.searchSession{ params: { limit?: number; query: string; sessionId: string }; result: SearchTurnMatch[] }

Transcript search over ONE session, daemon-side. The daemon owns the authoritative turns (live wrapper when an agent is active — fresh mid-run, which a window's load-time snapshot is not) and an LRU index cache shared by every window, so repeat queries pay match cost only. Results come back in TURN ORDER (search is a transcript filter).

host.capabilities{ params: { refresh?: boolean }; result: HostCapabilities }

What this host can do: OS, tools on PATH, sandbox, environment kinds, what it allows. Cached a minute; refresh probes again.

host.installTool{ params: { name: string }; result: { capabilities: HostCapabilities; ok: boolean; output: string } }

Install a missing tool on this host, as the host's user, when it can without root (toolInstall says run). Then probes again: capabilities is the new answer.

integrations.list{ params: Record<string, never>; result: IntegrationInfo[] }

The integrations registered on this host, in registration order. Additive.

integrations.register{ params: { detail?: string; kind: string; name: string; state: 'connected' | 'failed' | 'stopped' | 'starting' | 'degraded' }; result: void }

Report an integration this client runs (a channel). Registering a name again from the same connection replaces it; a name another live connection registered is refused. It goes away when this connection closes. Every window gets integrations:changed. Additive.

integrations.update{ params: { detail?: string; name: string; state: 'connected' | 'failed' | 'stopped' | 'starting' | 'degraded' }; result: void }

Change the state of an integration this connection registered; detail replaces the previous one (absent clears it). Additive.

interactions.cancelfrom HitlIpcMethods{ params: { reason?: string; requestId: string }; result: { ok: boolean } }

Cancels a pending interaction. reason is passed back to the agent.

interactions.listfrom HitlIpcMethods{ params: { sessionId?: string }; result: PendingInteraction[] }

Lists pending interactions (questions and prompts the agent asked the user), optionally for one session.

interactions.rehydratefrom HitlIpcMethods{ params: { sessionId: string }; result: PendingInteraction[] }

Re-sends the session's pending interactions, for a client that connected after they were raised.

interactions.rehydrateSessionsfrom HitlIpcMethods{ params: { sessionIds: string[] }; result: PendingInteraction[] }

interactions.rehydrate for each of sessionIds not running on this host, loading only those whose run waits to be resumed or whose last turns hold an unanswered question or plan (a channel start, over every conversation it had).

interactions.respondfrom HitlIpcMethods{ params: { requestId: string; response: InteractionResponse }; result: { ok: boolean } }

Answers a pending interaction.

logs.list{ params: Record<string, never>; result: { records: LogRecord[] } }

A zidane-server's recent log records, oldest first: its own lines and its extensions' (setup, runtime warnings), from a bounded in-memory buffer. Other hosts refuse it. Additive.

logs.unwatch{ params: Record<string, never>; result: void }

Release one logs.watch of this connection.

logs.watch{ params: Record<string, never>; result: void }

Follow the log: this connection gets logs:record for each new record. Calls count; a dropped connection releases its own. Additive.

machine.attach{ params: { hostId: string }; result: MachineRegisterParams }

Reverse direction (docs/MACHINES.md § Reverse direction): a client asks this server to lend itself as a machine over this very connection (allowAsMachine on, else refused). hostId names the asking host, so a reconnect finds its tasks and terminals again. Then machine.call carries each host→machine request (MachineMethods), answered by the server's confined executor; machine notifications come back as machine:notification signals on this connection only.

machine.call{ params: { method: string; params?: JsonValue }; result: JsonValue }

Runs one machine-protocol method on this server's executor and resolves with its JSON result. Refused when the server does not lend itself as a machine.

machine.detach{ params: Record<string, never>; result: { detached: boolean } }

Ends this connection's machine attachment. detached is false when it had none.

machines.forget{ params: { machineId: string }; result: { forgotten: boolean } }

Forgets a machine. It disconnects and its kinds stop resolving.

machines.links.status{ params: Record<string, never>; result: MachineLinksStatus }

State of this host's machine links: the shares it offers and the servers it uses.

machines.list{ params: Record<string, never>; result: MachineInfo[] }

The machines this host knows (docs/MACHINES.md), online or not, and forgetting one (it disconnects; its local@ / worktree@ kinds stop resolving). Additive.

machines.listDirs{ params: { machineId: string; path?: string }; result: { dirs: string[]; path: string; roots: string[] } }

Folders of a machine, for a client's folder picker: the host proxies a listing to the machine (fs.list, confined to its roots there): the folders in path (absent: its first root), and every root. Additive.

machines.share.start{ params: { hostId: string; name: string; roots: string[]; token?: string }; result: MachineLinksStatus }

A local host's machine links (docs/MACHINES.md § Client UI), behind remote hosts: share this computer with a saved server (the machine agent runs in this host's process, with the machine token token or the one already stored for that server), stop sharing, use a saved server as a machine (reverse direction), and read their state. Refused while remote hosts are off. Additive.

machines.share.stop{ params: { hostId: string }; result: MachineLinksStatus }

Stops sharing this computer with a server.

machines.useServer{ params: { enabled: boolean; hostId: string }; result: MachineLinksStatus }

Turns on or off using a server as this computer's linked server.

mcp.instances{ params: { cwd?: string }; result: McpServerInstance[] }

The host's shared MCP server processes (src/mcp/server-pool.ts), cwd's only when given: name, folder, shared or per session, how many sessions hold it, its pid, whether it is retired. Remote servers and scope: 'context' ones are never listed (they are per session). Additive.

mcp.remove{ params: { name: string }; result: { removed: boolean } }

Remove an MCP server from the host user's mcps.json; removed: false when it has none. As catalog.install.

mcp.restart{ params: { cwd?: string; name: string }; result: { restarted: number } }

End name's process now (in cwd only, when given) for every session holding it. Calls in flight fail with "<name> restarted" and are not retried; the next call starts a fresh process. Answers how many processes were restarted (0: none was running). Additive.

mcp.signOut{ params: { name: string }; result: void }

Forget an MCP server's sign-in on the host (its tokens). Every window gets catalog:changed. Additive.

mcp.test{ params: { cwd?: string; kind?: string; name: string }; result: McpServerStatus }

Connect to one of cwd's MCP servers (as catalog.mcps), list its tools into the host's tools cache and record how it went: the server's new status. Every window gets catalog:changed. Additive.

mcp.upsert{ params: { clientSecret?: string; config: McpServerConfig; cwd?: string; scope?: 'user' | 'project' }; result: { created: boolean; name: string } }

Add or replace an MCP server in the host user's mcps.json, or with scope: 'project' in cwd's project (a folder inside the workspace roots on a server). Its env / headers values are stored there and never come back (catalog.mcps redacts them, the reply names the server only). clientSecret, the secret of its oauth client, goes to the host's MCP credential store, never to mcps.json; an entry without an oauth client drops a stored one. As catalog.install.

models.list{ params: { cwd?: string; kind?: string }; result: { providers: HostModelProvider[] } }

The host's credentialed providers (what its agents can run) and their models, for a model picker of a session on that host. cwd adds the providers its extensions contribute there. Additive.

models.test{ params: { cwd?: string; kind?: string; model: string }; result: HostDecisionTestResult }

Send one tiny prompt with this host's credentials, to check a chat model answers. model is "providerKey[@account]/modelId", tested exactly. Never fails: a broken model comes back as ok: false with a reason. Additive.

onboarding.complete{ params: { records: OnboardingState; settings: OnboardingSettingsChange }; result: HostSettings }

The setup wizard's write: settings.set sets (settings.reset back to their defaults, unless also set), as settings.set applies them, and records (the run finished, its steps) merged into the host's stored ones. Returns the settings in effect. Additive.

onboarding.detect{ params: { cwd?: string }; result: OnboardingDetection }

What the setup wizard finds on the host's machine for cwd (default: the host user's home): the providers and their credentials, other agents to import from, local model servers and the install signals. Additive.

onboarding.providers{ params: { cwd?: string }; result: HostOnboardingProvider[] }

The providers as the wizard's Connect step lists them, with their custom fields and the model each runs when none is picked. Additive.

orchestration.answer{ params: { requestId: string; response: InteractionResponse }; result: boolean }

Answers a question or a plan, recording the connection as answeredBy. false when it was answered first.

orchestration.approve{ params: { decision: ApprovalDecision; requestId: string }; result: boolean }

Decides a tool approval (an approve token), recording the connection as answeredBy. false when it was decided first.

orchestration.cancel{ params: { sessionId: string }; result: void }

Cancels a session's run.

orchestration.createWorker{ params: CreateSessionParams; result: string }

Creates a session (optionally holding orchestration tools and a subscription) and resolves with its id.

orchestration.events{ params: EventQuery; result: SessionEvent[] }

Searches the recent session events (runs ended, sessions blocked), newest first.

orchestration.list{ params: { limit?: number }; result: WorkerRow[] }

Lists sessions with their live status, newest first, up to limit. The list can be partial.

orchestration.main{ params: Record<string, never>; result: string | null }

The main session id, creating it (directory-less) when the setting is on and none exists.

orchestration.pending{ params: { sessionId?: string }; result: PendingRequestRow[] }

The prompts waiting for an answer (questions, plans, tool approvals), of one session or all.

orchestration.read{ params: { full?: boolean; sessionId: string }; result: WorkerSummary }

Summary of one session (with its link); rejects when unknown. lastMessage is cut at 600 characters; full: true returns it whole.

orchestration.send{ params: { prompt: string; sessionId: string }; result: boolean }

Queues a prompt as the session's next turn; false when declined (the person stopped the session, or their queued prompts go first).

orchestration.steer{ params: { attachments?: PromptAttachment[]; message: string; sessionId: string }; result: void }

Sends a steering message, with optional attachments, to a session's live run.

orchestration.transcript{ params: { full?: boolean; limit?: number; sessionId: string }; result: string }

The last limit turns of a session as plain text, each cut at 1200 characters; full: true returns them whole.

orchestration.wait{ params: { sessionId: string; timeoutSec?: number }; result: WaitResult }

Resolves once the session is idle or blocked, or after timeoutSec (default 30, at most 50).

palette.list{ params: { ctx: PaletteContext; cwd: string }; result: PaletteItem[] }

The command palette items cwd's extensions contribute (each item's source is its provider), and running one. A provider that fails lists one disabled row naming why; a failed run answers ok: false. Additive.

palette.run{ params: { ctx: PaletteContext; cwd: string; itemId: string; source: string }; result: { ok: true; result: PaletteRunResult } | { error: string; ok: false } }

Run itemId on its provider (source); a nested list, a prompt or a draft comes back as result. Additive.

palette.unwatch{ params: { cwd: string }; result: void }

Release one palette.watch of this connection.

palette.watch{ params: { cwd: string }; result: void }

As panels.watch, for every provider of cwd: palette:changed. Additive.

panels.action{ params: { actionId: string; ctx?: PanelLoadContextInput; cwd: string; panelId: string; payload?: unknown }; result: { ok: true } | { error: string; ok: false } }

Run one of a panel's actions (a row's or the footer's actionId, its payload). Additive.

panels.list{ params: { cwd: string }; result: PanelSummary[] }

The side panels cwd's extensions register. A panel's load and onAction run on the host, bound to cwd and the session ctx names, with a hard timeout: a failing panel answers an empty content naming why (load) or ok: false (action), never an error. Additive.

panels.load{ params: { ctx?: PanelLoadContextInput; cwd: string; panelId: string }; result: PanelContent }

One panel's content for ctx (the session it shows beside, what opened it). Additive.

panels.unwatch{ params: { cwd: string; panelId: string }; result: void }

Release one panels.watch of this connection.

panels.watch{ params: { cwd: string; panelId: string }; result: void }

Keep a panel's live updates on: this connection gets panel:changed when its content changes. Calls count; a dropped connection releases its own. Additive.

perf.history{ params: Record<string, never>; result: { samples: PerfSample[] } }

The samples the host holds (the last 5 minutes of watching), oldest first. Additive.

perf.threads{ params: { pid: number }; result: { threads: PerfOsThread[] } }

The OS threads of one of the processes the host lists (its own tree), busiest first. Refuses any other pid. Additive.

perf.unwatch{ params: Record<string, never>; result: void }

Release one perf.watch of this connection. Additive.

perf.watch{ params: Record<string, never>; result: void }

Follow the host's own performance (Settings › Performance on a server): this connection gets perf:sample every 2 s. The host samples only while a connection watches. Calls count; a dropped connection releases its own. Additive.

plans.control{ params: { action: 'stop' | 'drop'; planId: string; sessionId: string }; result: PlansSnapshot }

Stop or drop a plan. Stopping or dropping the plan a live run applies interrupts that run. Resume is a message (buildPlanResumeMessage), not a call.

plans.edit{ params: { op: PlanEditOp; planId: string; sessionId: string }; result: PlansSnapshot }

A user edit to a plan's steps: rename, add or cancel (a step is never deleted). Through the live agent's session, else the store; refused while another process runs the session. The host fans plans:snapshot out to every window; the result is that snapshot. A refused edit is an error carrying the gate's reason.

plans.get{ params: { sessionId: string }; result: PlansSnapshot | null }

The session's plans (from its live agent, else the store), or null for an unknown session.

prefs.get{ params: Record<string, never>; result: HostComposerPrefs | null }

The picks remembered across apps (state.json): effort and options per model, the next chat's model chain. null when none is. Additive.

prefs.set{ params: { patch: HostComposerPrefs }; result: void }

Merges picks into state.json (each top-level key replaces). Additive.

presence.list{ params: Record<string, never>; result: SessionPresence[] }

Current presence snapshot — backfill for a freshly opened session list.

presence.watch{ params: { sessionId: string }; result: void }

Tag the calling peer as watching a session (one per connection; the TUI opens one connection per activated session). Sent as a notification right after the handshake. Detach is implicit — the daemon recomputes presence when the socket drops.

queue.add{ params: { activatedSkills?: string[]; asCommand?: string; attachments?: PromptAttachment[]; clientMessageId?: string; delivery?: QueueDelivery; keepDraft?: boolean; literal?: boolean; metadata?: Record<string, JsonValue>; origin?: TextOrigin; sessionId: string; text: string } & SendRequestId; result: { consumed?: 'steer' | 'followUp' | 'handled'; delivered: 'steer' | 'sent' | 'queued' } }

Sends a message: right away when the session is idle; while a run works, into the run or into the queue (delivery). delivered says which: sent, steer (the run took it, as a steer or a follow-up) or queued. The message goes through the prompt interceptors first, as chat.send; consumed says one took it (delivered is then sent for handled, steer otherwise).

queue.attachments{ params: { id: string; sessionId: string }; result: PromptAttachment[] | null }

A queued entry's attachments with their bytes; null when already sent / dropped.

queue.list{ params: { sessionId: string }; result: QueuedItem[] }

Type-ahead queue, host-owned so every window sees and can manipulate the same list. queue.add queues when a run is in flight and sends immediately otherwise; the host auto-drains the queue FIFO at run end.

queue.pushNow{ params: { id: string; sessionId: string }; result: { delivered: 'send' | 'steer' | 'queued' | 'missing' } }

Steer a queued entry, attachments included, into the live run now (falls back to send when idle).

queue.remove{ params: { id: string; sessionId: string }; result: boolean }

Removes a queued entry by id. Resolves true when it was removed.

queue.update{ params: { attachments?: PromptAttachment[]; id: string; sessionId: string; text: string }; result: boolean }

Edit a queued entry in place; order is kept. attachments replaces the entry's list when given. false when already sent / dropped.

references.providers{ params: { cwd: string; peek?: boolean }; result: ReferenceProviderWire[] | null }

The extension text reference providers (ctx.registerReferences) of cwd: the patterns a client matches prose with. peek: only from a registry already built for cwd (null when none is), so a list of titles across projects never builds one per project. Additive.

references.resolve{ params: { cwd: string; items: { key: string; providerId: string }[] }; result: (ExtensionReferenceView | null)[] }

The views of the keys a client found, in order: null for one that names nothing. At most 50 keys per call. Additive.

rpc.call{ params: RpcCallParams & { cwd?: string }; result: RpcResponseEnvelope }

Calls an extension's RPC method as this connection. Never throws for a failed call: a declared error, invalid input, a missing provider or a method whose scope this connection's token does not reach (rpc.forbidden; a local connection is not limited) comes back as ok: false with its type. directory (or cwd) picks the runtime; without it, the newest runtime that has the contract. callId lets rpc.cancel abort it. Also served as POST /rpc/<contract>/<method> on a zidane-server, with the same token. Additive.

rpc.cancel{ params: { callId: string }; result: boolean }

Aborts this connection's rpc.call of callId; it answers rpc.aborted. false when none is in flight. Additive.

runtime.reload{ params: Record<string, never>; result: void }

Drop the daemon's cached runtime configs (extension registries, per cwd) and retire every live agent so the next command or run rebuilds against what is on disk now. Sent by a window after it reloaded its own extensions (install, update, remove, /reload): extension / commands execute daemon-side, so without this the daemon keeps running the code it loaded at boot. Busy sessions rebuild at run end.

safelist.listfrom HitlIpcMethods{ params: Record<string, never>; result: { projects: SafelistProject[] } }

The host's "Always allow" entries, per project (its projects.json): what approving with accept-safelist added. Additive.

safelist.removefrom HitlIpcMethods{ params: { entry: string; project: string }; result: { removed: boolean } }

Revoke one "Always allow" entry of a project; removed: false when it was not there. Additive.

schedules.create{ params: ScheduleInput; result: Schedule }

Create a schedule. The fields are a ScheduleInput.

schedules.delete{ params: { id: string }; result: boolean }

Remove a schedule and its fires. false when it was gone.

schedules.extensionRemoved{ params: { extensionId: string }; result: void }

An extension was removed: turn off the schedules whose action calls it (kept, marked, for the person to delete). A no-op while the feature is off.

schedules.fires{ params: { id: string; limit?: number }; result: ScheduleFire[] }

A schedule's fires, newest first (limit default 20, at most 50).

schedules.list{ params: ScheduleListFilter; result: Schedule[] }

The stored schedules, oldest first, each with its last fire. sessionId: only those whose prompt goes to it.

schedules.runNow{ params: { id: string }; result: ScheduleFire }

Run a schedule's action now; its next fire does not move.

schedules.status{ params: Record<string, never>; result: { enabled: boolean; supported: boolean } }

Whether the feature is on (enabled) and whether this host's store keeps schedules (supported).

schedules.update{ params: { id: string; patch: SchedulePatch }; result: Schedule }

Change the fields of patch. A new trigger, or turning it on, counts its next fire from now.

search.set{ params: { active: boolean; query: string; sessionId: string }; result: void }

Replace the shared transcript-search state (notification, per keystroke).

secrets.listfrom HitlIpcMethods{ params: { sessionId: string }; result: SecretEntry[] }

The secrets of the session's project (Settings.secrets): names and expiry times, never values.

secrets.revokefrom HitlIpcMethods{ params: { name: string; sessionId: string }; result: { removed: boolean } }

Remove a secret of the session's project.

secrets.savefrom HitlIpcMethods{ params: { name: string; sessionId: string; ttl: SecretTtl; value: string }; result: SecretEntry }

Add or replace a secret of the session's project. Refused over a plain-text connection from another machine.

server.update.check{ params: Record<string, never>; result: ServerUpdateStatus }

Check for a newer zidane-server release now. With auto-update on, a newer one installs; off, it is downloaded and verified, and the state turns ready.

server.update.configure{ params: { enabled: boolean }; result: ServerUpdateStatus }

Turn the zidane-server's self-update on or off.

server.update.install{ params: Record<string, never>; result: ServerUpdateStatus }

Install the release that is ready or waiting now, without waiting for background work to finish.

server.update.status{ params: Record<string, never>; result: ServerUpdateStatus }

A zidane-server's self-update: its state, the setting, the newest release seen.

sessions.appendNote{ params: { blocks: CommandOutputBlock[]; sessionId: string }; result: boolean }

ctx.appendNote outside a run: a display-only note, rendered live and persisted (ChatHost.appendNote). False for an unknown session or no blocks.

sessions.appendTurns{ params: { sessionId: string; turns: SessionTurn[]; whenIdle?: boolean }; result: void }

Turns written outside a run, e.g. ! shell output (ChatHost.appendTurns). whenIdle: refused while a run is live or an interrupted run waits (a channel's context messages).

sessions.artifacts{ params: { kind?: string; limit?: number; sessionId: string }; result: SessionArtifact[] }

Artifacts one session recorded (PRs, branches, commits, files, extension kinds — see session/artifacts.ts), newest first. Empty on stores without artifact support. Additive; pre-artifact daemons reject the method and clients treat that as empty.

sessions.blockRuns{ params: { sessionId: string }; result: { runs: Record<string, BlockRun> } }

The session's saved block runs, by key (blockRunKey).

sessions.cacheImpact{ params: { change: CacheImpactChange; sessionId: string }; result: CacheImpact | null }

What a switch would cost the session's prompt cache, as the host applies it (ChatHost.cacheImpact): what pickers show next to each option. null without a live agent or for an empty change. Additive: an older host answers "method not found", and clients show no estimate.

sessions.cacheReport{ params: { limit?: number; sessionId: string }; result: CacheReport | null }

The session's last requests and what the cache did for each (ChatHost.cacheReport): what /cache prints, for the GUI's session details. null for an unknown session. Additive: an older host answers "method not found".

sessions.compact{ params: { sessionId: string }; result: CompactionReport | null }

Compact now (ChatHost.compactSessionNow): what was done, null when nothing was. Older daemons resolve null.

sessions.contextBreakdown{ params: { sessionId: string }; result: ContextBreakdown | null }

The session's context usage broken down by category, or null when unknown.

sessions.contextSegments{ params: { sessionId: string }; result: ContextSegmentsReport | null }

Every chunk of the session's system prompt with provenance — the Instructions tab. Built from the live agent's assembly, so it reflects MCP instructions, extension fragments and the resolved SYSTEM.md.

sessions.create{ params: CreateSessionParams; result: SessionRow }

Creates a session and resolves with its row.

sessions.createSystemOverride{ params: { sessionId: string }; result: { created: boolean; path: string } }

Seed a user-scope SYSTEM.md from the session's doctrine; throws when an override could not apply.

sessions.delete{ params: { sessionId: string }; result: { environment?: EnvironmentTeardownResult } }

Resolves with what happened to the session's environment (a provider may keep it).

sessions.deleteTurn{ params: { sessionId: string; turnId: string }; result: void }

History edits (ChatHost.deleteTurn / editTurnText): refused while the session runs.

sessions.editTurnText{ params: { sessionId: string; text: string; turnId: string }; result: void }

Replaces the text of one turn. Refused while the session runs.

sessions.environmentActions{ params: { sessionId: string }; result: EnvironmentActionSummary[] }

The per-session commands the session's environment offers right now (push, open PR, …).

sessions.export{ params: { format: SessionExportFormat; sessionId: string }; result: SessionExportFile }

The session's transcript rendered as a file to hand to the user: Markdown (title, metadata, every turn) or the session's JSON. Nothing is written on the host. Additive (no version bump).

sessions.fork{ params: { sessionId: string; title?: string; turnId: string }; result: { id: string } }

Fork at turnId into a new session (ChatHost.forkSession); announced as a sessions:changed create.

sessions.generateTitle{ params: { model?: string; providerKey?: string; sessionId: string }; result: { title: string } }

Title the session ON the host: its providers and credentials (a server session's transcript never goes to the window's provider), the same generator (generateSessionTitle), written as sessions.setTitle does. providerKey / model: the window's pick, used when the host has that provider; otherwise the session's own provider and model. The Main session is refused (it keeps its name). Additive: an older host answers "method not found".

sessions.get{ params: { sessionId: string; turns?: boolean }; result: SessionDocumentHead | null }

The whole session document as the host holds it: the live copy when an agent is live (turns persisted mid-run, todos kept in memory), else the stored one, a stale running marker demoted. null when unknown.

sessions.getHeader{ params: { sessionId: string; usage?: boolean }; result: SessionHeader | null }

One session's header: its list row plus metadata, profile and status, without turns or runs (the live copy when an agent holds it). What a window re-reads on sessions:changed. Additive. usage: true adds the footer's numbers, computed host-side from the turns and runs (contextTokens, costUsd, and while the host runs the session runStartedAt / runOutputTokens): a client needs no document for its context gauge, cost pill or run readout. Older hosts ignore it.

sessions.getMeta{ params: { sessionId: string }; result: SessionMeta | null }

One SessionMeta by id (rows a filtered list can't return, e.g. the main); null when unknown.

sessions.getToolResult{ params: { callId: string; sessionId: string; turnId?: string }; result: string | null }

The full text of one tool result (callId; turnId, when known, the turn that holds it): what a clipped tool row shows on expand, without the session crossing the wire. null when no such result. Additive.

sessions.getTurn{ params: { sessionId: string; turnId: string }; result: SessionTurn | null }

One turn of a session by id, or null when unknown.

sessions.getTurns{ params: { from?: number; limit?: number; sessionId: string }; result: { total: number; turns: SessionTurn[]; version?: string } | null }

A slice of a session's turns, [from, from + limit) (default from 0, SESSION_TURNS_PAGE turns, at most SESSION_TURNS_MAX_PAGE), from the same copy sessions.get reads, and how many turns it has: a client reads a long session's document a page at a time (loadSessionDocument) instead of in one reply. With sessions.get turns: false (the document without its turns), what an export needs. Pages and that head come from one snapshot of the document per content version (read once, not per page), and name it (version): a client that sees it change mid-load starts over rather than mix two versions (loadSessionDocument). null when unknown. Additive: an older host answers "method not found", ignores turns: false, and names no version.

sessions.list{ params: { archive?: 'active' | 'all' | 'archived'; projectRoot?: string | null }; result: SessionRow[] }

Lists session rows. projectRoot filters by project, archive picks active, archived or all sessions.

sessions.listMeta{ params: { projectRoot?: string | null }; result: SessionMeta[] }

The session list as a window renders it (SessionMeta: title, turn / message / run counts, project, cwd, environment, artifact count, the orchestration main flag), newest first. projectRoot: omitted = every project, null = untagged sessions.

sessions.loadEvents{ params: { sessionId: string }; result: StreamEvent[] }

The session's transcript as canonical events. Without before / limit: every turn, as a plain array (the shape every client predating paging reads). With either: one page, { events, cursor? }, the last limit turns (default LOAD_EVENTS_PAGE_TURNS, at most LOAD_EVENTS_MAX_PAGE_TURNS) before turn position before (default: the end), its start moved back to an exchange boundary when one is near so a tool call is never cut from its result. cursor: the before of the previous page, absent at the first turn. Typed here as the unpaged call every client makes; a paged call goes through loadEventsPage. Paging is additive: an older host ignores the fields and answers the whole array.

sessions.moveEnvironment{ params: { cwd?: string; environment: EnvironmentRequest; removeOldWorktree?: boolean; sessionId: string }; result: { oldWorktree?: EnvironmentTeardownResult } }

Move an idle session to another environment (local, worktree, local@M, worktree@M, any direction), in cwd on the target; provisioning continues in the background.

sessions.removeBlockRun{ params: { key: string; sessionId: string }; result: void }

Forgets a block's saved run.

sessions.resume{ params: { afterSeq: number; epoch: string; sessionId: string }; result: SessionResumeResult }

Reattach without a reload: the session's signals numbered after afterSeq (see SIGNAL_SEQ_FIELD), in order, as long as this host process (epoch, from HelloResult.signalEpoch) still holds them all. Otherwise refused, and the client reloads with sessions.loadEvents. Additive: an older host answers "method not found", same fallback.

sessions.retryEnvironment{ params: { sessionId: string }; result: void }

Provision a failed (or restart-stranded) environment again.

sessions.rewind{ params: { inclusive: boolean; sessionId: string; turnId: string }; result: void }

Cut the history at turnId in place (ChatHost.rewindSession); inclusive keeps the turn. Refused while the session runs.

sessions.runBlock{ params: { code: string; language: string; sessionId: string; turnId: string }; result: { runId: string } }

Runs a shell block of a reply (Run here) in the session's folder, with no input. The reply names the run; its output follows as block-run:data signals and ends with one block-run:end carrying the saved run, to this connection only. Nothing enters the conversation; the run becomes the block's last (sessions.blockRuns), announced to every window by block-runs:changed. A dropped connection stops what it started.

sessions.runBlockCancel{ params: { runId: string }; result: { cancelled: boolean } }

Stops a Run here this connection started (cancelled: false when it is not running).

sessions.runEnvironmentAction{ params: { actionId: string; sessionId: string }; result: EnvironmentActionResult }

Run one of them in the session's environment (resumed when suspended).

sessions.setAgentId{ params: { agentId: string; sessionId: string }; result: void }

Switches the agent the session runs as.

sessions.setArchived{ params: { archived: boolean; sessionId: string }; result: void }

Archives or restores a session.

sessions.setEffort{ params: { effort: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'adaptive'; sessionId: string }; result: void }

Sets the thinking effort of the session.

sessions.setExtensionState{ params: { extension: string; key: string; sessionId: string; value?: JsonValue }; result: void }

One key of an extension's per-session state (metadata.extensions, the bag ctx.session and renderer useSessionState share), written by the host into its copy of the session. value omitted deletes the key.

sessions.setFavorite{ params: { favorite: boolean; sessionId: string }; result: void }

Keep the session at the top of lists (metadata.favorite). Not activity: no recency bump. Additive.

sessions.setFolder{ params: { cwd: string; sessionId: string }; result: void }

Bind to a folder, or back to a plain chat with cwd: '' (ChatHost.setSessionFolder).

sessions.setLaunchOverrides{ params: { sessionId: string } & SessionLaunchOverrides; result: void }

zidane run --system / --mcp for a host-backed window: launch overrides for the session's agent (ChatHost.setLaunchOverrides), owned by the calling client and dropped when it disconnects. A call with neither field clears them.

sessions.setModel{ params: { account?: string; effort?: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'adaptive'; fallbacks?: ModelFallbackRef[]; modelId: string; modelOptions?: Record<string, boolean>; providerKey: string; sessionId: string }; result: void }

The one model-swap contract (ChatHost.setSessionModel). A pick is a whole: fallbacks (ordered, primary excluded) REPLACES the persisted metadata.modelChain (omitted/empty clears it); effort and modelOptions are reset to the supplied values (omitted clears them — per-model knobs never leak onto the new model). The live agent is retired so the next run rebuilds on the new pick (run-end deferred while a run is in flight).

sessions.setModelOptions{ params: { options: Record<string, boolean>; sessionId: string }; result: void }

Replaces the model-specific boolean options of the session.

sessions.setPolicy{ params: { policy: SessionPolicy; sessionId: string }; result: void }

Replaces the session's rules (metadata.policy, SessionPolicy); {} clears them. Loosening the current rules (a looser approvalMode, 'ask' > 'decide' > 'off', removing a rule, raising a ceiling) needs an approve token. Additive.

sessions.setSpace{ params: { sessionId: string; spaceId: string }; result: void }

Move the session to a space (metadata.spaceId); the Default space id clears it. See chat/spaces.

sessions.setTitle{ params: { sessionId: string; title: string }; result: void }

Renames a session.

sessions.sideSession{ params: { sessionId: string }; result: SideSessionLookup }

A session's /side session (sessions.create with sideOf; hidden from sessions.list), the newest when there are several, with the session's own row (null: unknown). Additive.

sessions.systemMd{ params: { sessionId: string }; result: { file: SystemMdFile | null; shadowed: readonly SystemMdFile[] } | null }

Which SYSTEM.md override composes this session's prompt, for windows attaching to an already-warm agent.

The system-md signal fires once per agent BUILD and the daemon caches agents, so the normal multi-window case (daemon already running, open a second terminal or the GUI) would otherwise never learn that the built-in safety and git doctrine has been replaced. null = no live agent yet, in which case the next build broadcasts it.

sessions.titleStatus{ params: { sessionId: string }; result: string[] }

How the session's title came about, as the session details dialog words it (describeTitleStatus): where it came from (auto, typed by you, untitled, …), then the last automatic titling attempt when there was one (generated 2h ago, check failed 5m ago (…)). Additive.

sessions.touch{ params: { sessionId: string }; result: void }

A window appended transcript content OUT-OF-BAND (local / command output, ! shell turns — written straight to the shared store, not through a host run). The daemon fans out sessions:changed so every window (issuer included) re-converges on the canonical replay NOW instead of at the next run end. Sent as a notification.

sessions.workspaceCopy{ params: { from: string; sessionId: string; toDir: string }; result: EntryResult }

Copies from into folder toDir ('': the root) under a free name.

sessions.workspaceCreate{ params: { content?: string; freeName?: boolean; kind: 'file' | 'dir'; path: string; sessionId: string }; result: EntryResult }

Creates a file (content: base64 bytes, default empty) or a folder; freeName takes the next free name instead of refusing a taken one.

sessions.workspaceEmptyDirs{ params: { sessionId: string }; result: string[] }

The workspace's empty folders, relative to its root (a file listing leaves them out), capped.

sessions.workspaceExec{ params: { command: string; cwd?: string; env?: Record<string, string>; sessionId: string; timeoutSeconds?: number }; result: ExecResult }

The session's workspace (ChatHost.workspace*): its execution context, rooted inside the environment when the files live there, else at the session's cwd. Paths and exec cwd resolve against the root as the execution context resolves them; containment is the context's own. cwd / env on exec and the list / size methods are additive: an older daemon ignores the former and answers "method not found" to the latter (active clients are build-fenced, so this is a transient case).

sessions.workspaceExecCancel{ params: { execId: string }; result: { cancelled: boolean } }

Kill a streamed command's process group (this connection's only; cancelled: false otherwise or when it already ended).

sessions.workspaceExecStream{ params: { command: string; cwd?: string; sessionId: string }; result: { execId: string } }

The same command, streamed (! on a remote host): the reply names the exec, its output follows as workspace:exec:data signals and ends with one workspace:exec:exit, all to this connection only (signals may precede the reply). Bounded per connection (concurrent commands, chunk size, total output: past it the process is stopped and the exit says truncated). A dropped connection kills what it started. Additive: an older host answers "method not found" and clients use workspaceExec.

sessions.workspaceFileSize{ params: { path: string; sessionId: string }; result: number | null }

Size in bytes of a file in the session's workspace, or null when it does not exist.

sessions.workspaceListFiles{ params: { options?: WorkspaceListFilesOptions; path: string; sessionId: string }; result: ListFilesEntry[] }

Lists the entries under path in the session's workspace.

sessions.workspaceMove{ params: { from: string; sessionId: string; to: string }; result: EntryResult }

Moves (renames) from to to.

sessions.workspaceReadFile{ params: { path: string; sessionId: string }; result: string }

Reads a text file from the session's workspace.

sessions.workspaceRemove{ params: { path: string; sessionId: string }; result: EntryResult }

Removes path (a folder whole), for good.

sessions.workspaceWriteFile{ params: { content: string; path: string; sessionId: string }; result: void }

Writes a text file in the session's workspace.

settings.get{ params: Record<string, never>; result: HostSettings }

Reads the host settings.

settings.set{ params: { patch: Partial<HostSettings> }; result: HostSettings }

Applies a partial settings patch and resolves with the full settings after it.

sidebar.getSpace{ params: Record<string, never>; result: string | null }

The space a client's sidebar showed last, null when none was saved. Additive.

sidebar.getViews{ params: Record<string, never>; result: Record<string, unknown> }

The sidebar views a client saved on this host, by space id (state.json's sidebarViews): each client checks what it reads. Additive.

sidebar.setSpace{ params: { spaceId: string }; result: void }

Saves the space the sidebar shows. Additive.

sidebar.setView{ params: { spaceId: string; view: Record<string, unknown> }; result: void }

Saves one space's sidebar view (at most 4 KiB of JSON), stored as given: the kit's record is the filter axes plus session, the conversation last open in that space. Additive.

skills.browse{ params: { source: string }; result: SkillSourceListing }

The skills of a source (owner/repo[#ref][:dir], a GitHub or skills.sh URL), fetched by the host with its GitHub login; catalog.install with skills installs picked ones. Additive.

suggest.resolve{ params: { caret: number; seq: number; sessionId: string; text: string }; result: { candidates?: string[]; seq: number; suggestion: string | null } }

Resolve inline ghost-text autocomplete daemon-side (the typing window's request IS the single computation): canonical resolver against the session cwd's extension registry. Latest-wins per session — a seq older than the newest seen returns { suggestion: null } without computing, and a newer request aborts an in-flight compute. The result additionally fans out as suggest:changed to every OTHER peer.

sync.claim{ params: { categories?: SyncCategory[]; deviceId: string; deviceName: string; keys?: string[]; takeover?: boolean }; result: SyncClaimResult }

Makes the caller the settings source device. When another device is the source, the result says which, unless takeover is set. categories and keys narrow what it syncs.

sync.push{ params: { category: SyncCategory; deviceId: string; payload: SyncPayload }; result: SyncPushResult }

Applies one category's snapshot from the source device.

sync.release{ params: { deviceId: string }; result: { released: boolean } }

Stops the source device's sync.

sync.reset{ params: { field: string }; result: { reset: boolean } }

Drops the local override of one field so sync applies to it again.

sync.status{ params: Record<string, never>; result: SyncStatus }

Settings sync, a client's computer → this server (docs/SERVER.md § Settings sync). A zidane-server only; other hosts refuse. One source device per server: sync.claim makes the caller it (or says who is, unless takeover), sync.push applies one category's snapshot through the host's own setters, skipping fields edited here since (overrides), sync.reset drops an override, sync.release stops the source. Every call is audited with its token. Additive.

terminal.ack{ params: { clientId: string; offset: number; terminalId: string }; result: void }

Acknowledges terminal output up to offset, so the host keeps it flowing.

terminal.adopt{ params: { draftId: string; sessionId: string }; result: TerminalAdoptResult }

The draft became sessionId: its terminals move there in one pass (over the cap: back to the project).

terminal.attach{ params: { clientId: string; maxLines?: number; terminalId: string }; result: TerminalAttachResult }

maxLines caps the history snapshot to the last lines a view can show (0 or absent: no cap).

terminal.close{ params: { terminalId: string }; result: void }

Closes a terminal.

terminal.detach{ params: { clientId: string; terminalId: string }; result: void }

Detaches a view from a terminal.

terminal.open{ params: TerminalOpenInput; result: TerminalInfo }

Opens a terminal in a session's environment.

terminal.openInProject{ params: TerminalProjectOpenInput; result: TerminalInfo }

Opens a terminal bound to a project rather than a session.

terminal.projectState{ params: TerminalProjectStateInput; result: TerminalProjectState }

Terminals without a session: a shell on the host's machine in a project's folder, or in a new-chat draft's (draftId). Agents never see them. terminal:project-changed carries a scope's full list.

terminal.registerViewer{ params: { viewerId: string }; result: { ok: boolean } }

This connection can show terminals (the desktop app). While at least one viewer is attached, agents get the terminal tools; the registration is dropped with the connection.

terminal.releaseDraft{ params: { draftId: string }; result: void }

The draft was discarded (or became a server session): its terminals go back to the project.

terminal.resize{ params: { claim?: boolean; clientId: string; cols: number; rows: number; terminalId: string }; result: void }

Resizes the terminal to a view's size. claim makes this view own the size.

terminal.restart{ params: { terminalId: string }; result: TerminalInfo }

Restarts a terminal's process and resolves with its info.

terminal.setHidden{ params: { hidden: boolean; terminalId: string }; result: void }

Hides a terminal from the agent, or shows it again.

terminal.state{ params: { sessionId: string }; result: TerminalSessionState }

Terminals, run by the host in the session's environment. clientId names one view (a window's drawer or panel); views attached through a connection are detached when it drops. Output arrives as terminal:output signals; ack what was written to keep it flowing.

terminal.unregisterViewer{ params: { viewerId: string }; result: { ok: boolean } }

Withdraws a viewer registered with terminal.registerViewer.

terminal.write{ params: { clientId?: string; data: string; response?: boolean; terminalId: string }; result: void }

clientId + response: bytes the view's emulator generated (DSR/DA replies), dropped unless that view owns the size.

tokens.create{ params: { name: string; scope: ServerTokenScope }; result: ServerTokenInfo & { token: string } }

Create a token with a scope (./scopes; machine lends a computer). The secret is in the result only: show it once. Bound to the calling token (createdBy), so revoking that one revokes this one too. Audited. Additive.

tokens.createMachineToken{ params: { name: string }; result: { id: string; name: string; token: string } }

Mint a machine-scoped token on this server (a zidane-server only), so a window can share its computer in one step. An admin token may (./scopes). Audited.

tokens.list{ params: Record<string, never>; result: ServerTokenInfo[] }

This server's tokens, revoked ones included, never their secrets (a zidane-server only). Additive.

tokens.machineInvite{ params: { name?: string; ttlMs?: number }; result: { code: string; expiresAt: number } }

Issue a one-time machine join code (a zidane-server only): a machine posts it to POST /zidane/pair and gets a machine token named name. A public server caps ttlMs at 10 minutes. Audited as pair.create with scope machine. Additive.

tokens.pair{ params: { name?: string; scope: 'chat' | 'admin'; ttlMs?: number }; result: { code: string; expiresAt: number } }

Issue a one-time pairing code (XXXX-XXXX, a zidane-server only): a browser posts it to POST /zidane/pair and gets a token of scope, named browser: <name> and bound to the calling token (createdBy). ttlMs: 30000 to 3600000, default 600000. Single use; five wrong secrets burn it. Audited. Additive.

tokens.revoke{ params: { id: string }; result: { revoked: ServerTokenInfo[] } }

Revoke a live token by id, with the live tokens it minted; their connections drop and the runs and terminals they started stop. Audited. Additive.

tokens.revokeSelf{ params: Record<string, never>; result: { revoked: ServerTokenInfo[] } }

Revoke the token this connection authenticated with, and the live tokens it minted (a browser's sign out). Any client scope may: it only gives up its own access. Audited. Additive.

ui.client.announce{ params: UiClientAnnounce; result: void }

What this connection shows and whether it shows dialogs: dialogs (ui:dialog) and UI requests (ui:client) go to the client viewing the session, else the last active one. Only announced clients get them. Send again on focus or session change. Additive.

ui.client.reply{ params: { requestId: string; result: unknown }; result: boolean }

Answers a ui:client request sent to this connection. false when it is no longer pending. Additive.

ui.dialog.answer{ params: { answer?: unknown; dialogId: string }; result: boolean }

Answers a ui:dialog shown on this connection (answer absent: cancelled). false when it is no longer pending. Additive.

ui.dialog.validate{ params: { dialogId: string; key: string; value: unknown }; result: string | null }

Checks one value of a form dialog shown on this connection: the field's error message, or null when it is valid (or the dialog is no longer pending here). Clients call it as the user edits a field and before they answer, and show the message under the field. The host checks the answer again: an answer with an invalid value makes the extension's ctx.ui.form reject, so a client must not answer until every field validates. Additive.

usage.github{ params: { probe?: boolean }; result: ProviderUsageSnapshot[] }

GitHub's API limits as the host's GitHub client last saw them, per GitHub host; probe asks GitHub (/rate_limit, which costs no call). Additive.

usage.providers{ params: { refresh?: boolean }; result: readonly ProviderUsageSnapshot[] }

The plan and subscription windows of each provider the host holds a credential for (every account), probed with the host's credentials and cached a few minutes; refresh probes past the cache. A provider that can't be read reports status: 'unavailable'. Additive.

usage.summary{ params: { range: UsageRange }; result: UsageSummary }

Token spend, models, streaks and activity over range, from the host's session store, as the desktop app's welcome screen shows its own (summarizeUsage). Days are the host's local days; userName is null (the host's user is not the reader). Refused by a store that keeps no usage history (not a SQLite file). Additive.

src/ipc/protocol.ts:506

PairBrowserOptionsInterface#

Options for pairBrowser.

TypeScript
interface PairBrowserOptions
PairBrowserOptions properties
NameTypeDescription
fetch?(url: string, init: RequestInit) => Promise<Response>

The fetch to use. Default: globalThis.fetch.

src/ipc/browser-pair.ts:54

PairedBrowserInterface#

A paired browser's device token.

TypeScript
interface PairedBrowser
PairedBrowser properties
NameTypeDescription
id?string

The token's id, the one tokens.list and tokens.revoke use. Absent from servers older than it.

scope'chat' | 'admin'

The scope the code was issued with.

serverName?string

The server's name, when it has one (--name).

tokenstring

The token, for createServerClient({ token }). Keep it; it is shown once.

src/ipc/browser-pair.ts:42

ServerClientInterface#

A connection to a zidane-server: typed calls, signals and its state.

TypeScript
interface ServerClient
ServerClient properties
NameTypeDescription
call<M extends keyof IpcMethods>(method: M, params: IpcParams<M>) => Promise<IpcResult<M>>

Calls a method. Waits while (re)connecting; fails when the connection drops mid-call.

can(method: keyof IpcMethods) => boolean

Whether this connection may call method: the host offers it and the token reaches it. true against a host that predates the list. false while not open.

close() => void

Closes the connection and stops reconnecting.

hello() => HelloResult | null

The current connection's hello, null while not open.

notify<M extends keyof IpcMethods>(method: M, params: IpcParams<M>) => void

Sends a notification (no answer). Dropped while not open.

onOpen(listener: (hello: HelloResult, reconnected: boolean) => void) => () => void

Called after every handshake, reconnected after the first: re-read or resume what the page shows. Returns its unsubscribe.

onSignal(listener: (signal: HostSignal, meta?: SignalMeta) => void) => () => void

Called with every signal the host fans out, and its position in its session; returns its unsubscribe.

onState(listener: (state: ServerClientState) => void) => () => void

Called on every state change; returns its unsubscribe.

readyPromise<HelloResult>

Resolves with the first hello; rejects when the client is refused or closed before it.

state() => ServerClientState

The connection's state now.

src/ipc/browser.ts:116

ServerClientOptionsInterface#

Options for createServerClient.

TypeScript
interface ServerClientOptions
ServerClientOptions properties
NameTypeDescription
appVersion?string

Reported in the server's logs and audit (default 'web').

connectTimeoutMs?number

How long an attempt may take to open and finish its handshake before it is dropped and retried, in ms (default 10 000).

fetch?typeof fetch

fetch, for the auth check after a failed connect. Default: the global one.

heartbeatMs?number

Ping interval in ms; a ping unanswered by the next one drops the connection (default 20 000; 0: off).

onState?(state: ServerClientState) => void

Called on every state change.

reconnect?false | { initialMs?: number; maxMs?: number }

First reconnect delay and its ceiling, in ms (default 500 doubling to 15 000). false: never reconnect.

token?string

A client token (zidane server token create --scope chat). Absent: a server without auth.

urlstring

ws:// or wss:// URL of the server.

WebSocket?(url: string | URL, protocols?: string | string[]) => WebSocket

WebSocket constructor. Default: the global one.

src/ipc/browser.ts:60

SignalMetaInterface#

What a signal listener gets beside the signal: its number in its session, when it has one.

TypeScript
interface SignalMeta
SignalMeta properties
NameTypeDescription
seqnumber

Position of the signal in its session. Starts at 1 and has no gaps within one host process.

src/ipc/protocol.ts:461

Types#

ClientScopeType#

A client token's reach: CLIENT_SCOPES.

TypeScript
type ClientScope = typeof CLIENT_SCOPES[number]

src/ipc/scopes.ts:31

IpcMethodNameType#

Name of any method in IpcMethods.

TypeScript
type IpcMethodName = keyof IpcMethods

src/ipc/protocol.ts:1916

IpcParamsType#

Params type of the method M in IpcMethods.

TypeScript
type IpcParams<M extends IpcMethodName> = IpcMethods[M]['params']
IpcParams type parameters
NameTypeDescription
Mextends IpcMethodName

src/ipc/protocol.ts:1918

IpcResultType#

Result type of the method M in IpcMethods.

TypeScript
type IpcResult<M extends IpcMethodName> = IpcMethods[M]['result']
IpcResult type parameters
NameTypeDescription
Mextends IpcMethodName

src/ipc/protocol.ts:1920

ServerClientStateType#

Where a ServerClient's connection is.

TypeScript
type ServerClientState =
  | { attempt: number; status: 'connecting' }
  | { hello: HelloResult; status: 'open' }
  | { attempt: number; error: string; inMs: number; status: 'retrying' }
  | { message: string; reason: ServerRefusal; status: 'refused' }
  | { status: 'closed' }

src/ipc/browser.ts:50

ServerPairFailureType#

Why a pairing failed. The server's answers: invalid (no such code, or not a code's shape), expired, used (spent, or burned by wrong attempts), rate-limited, forbidden (an origin, proxy or transport its upgrade policy refuses). And the client's: unavailable (the server has no pairing route: too old, or running without auth) and network (no answer it understands).

TypeScript
type ServerPairFailure =
  | 'invalid'
  | 'expired'
  | 'used'
  | 'rate-limited'
  | 'forbidden'
  | 'unavailable'
  | 'network'

src/ipc/browser-pair.ts:19

ServerRefusalType#

Why a server refused this client for good: token (unknown or revoked), the server's admission refusals (origin not allowed, insecure-transport, untrusted-proxy, a scope it may not connect with), or a protocol the two don't share.

TypeScript
type ServerRefusal =
  | 'token'
  | 'origin'
  | 'insecure-transport'
  | 'untrusted-proxy'
  | 'scope'
  | 'protocol'

src/ipc/browser.ts:47

Re-exports#

These names are documented on the entry point that owns them.