zidane/ ipc
The shared chat host, its unix-socket daemon and the typed client that keeps several windows in sync.
import { … } from 'zidane/ipc'src/ipc/index.ts
Use zidane/ipc when several windows must share one set of live agents. startIpcDaemon serves the chat host over a unix socket, startInProcessHost runs it in your process, and connectIpcClient and connectOrSpawnDaemon return a typed client. The daemon explains how the apps use it. Server protocol lists the methods and signals.
import { connectIpcClient, defaultSocketPath } from 'zidane/ipc'
const client = await connectIpcClient({ socketPath: defaultSocketPath() })
const sessions = await client.request('sessions.list', {})
const stop = client.onSignal(signal => console.log(signal.kind))
console.log(sessions.length)
stop()
client.close()Functions#
connectIpcClientFunction#
Connects to a daemon over its unix socket and completes the ipc.hello handshake.
On a protocol, build or code version mismatch it throws IpcVersionMismatchError. Unless drainOnMismatch is false, it first asks the stale daemon to drain. Rejects when nothing listens on the socket.
connectOrSpawnDaemonFunction#
One-call convenience for windows: make sure a daemon is up (spawning if needed), connect, and — when the running daemon speaks an older protocol — drain it and spawn a fresh one before retrying once.
createDaemonEnvironmentFunction#
The daemon's environment: its own config, per-cwd extension discovery, multi-process.
createFileApprovalsStoreFunction#
File-backed pending-approvals persistence (pending-approvals.json next
to state.json). Small, rewritten atomically enough for its purpose —
the queue holds at most a handful of rows.
createHostEnvironmentFunction#
Disk-backed host environment: the daemon's, or (with shared) a host
sharing the window's process and setup.
createHostImagePortFunction#
The image port of a host's ctx.generate (GeneratePort.image).
createHostTranscriptPagerFunction#
A host's pager. The worker is used only for a store that is a SQLite file
(sqliteStoreFile), with the kill switch off; it spawns lazily, on the
first page, for the first such file (one host, one store).
createMachineHandlersFunction#
Builds the machines.list, machines.forget and machines.listDirs handlers.
machines.forget broadcasts a machine:status signal with the machine offline when it removed one. machines.listDirs is confined to the machine's declared roots and throws for a path outside them.
createMachineLinkHandlersFunction#
Builds the machines.share.*, machines.useServer and machines.links.status handlers.
The links load lazily and only while remote hosts are enabled. On a zidane-server every method throws. Turning remote hosts off stops every link.
createTranscriptHistoryWorkerFunction#
A client of the history worker over dbPath. The worker spawns on the
first page.
createTranscriptReaderFunction#
Whole-session reads that build on the last one while a session runs. Its version moves with every turn saved, so each page asked of it reads the session again: the whole of it, hundreds of ms on a long one. A live read keeps what it parsed and, next time, reads only the rows added since.
Saving appends rows. Anything else (compaction, a rewind, a full save)
deletes rows or writes them again, so a kept read is used only when every
row it holds is still there at the same seq with the same size: one
query over the rows' headers (octet_length reads no row's content). A
history edit keeping every size is the one change it cannot see, and the
host refuses history edits while a session runs: a kept read serves the run
it was read in, and the first read of a new run reads whole. Kept reads are
bounded by stored size (KEPT_READ_MAX_BYTES, KEPT_READS_MAX_BYTES):
a bigger session is read whole on each change. A read that is not live
reads everything and drops what was kept; so does a live one when the
incremental read fails.
createTranscriptReplayCacheFunction#
A replay and document cache.
createWorkerRpcClientFunction#
A worker RPC client. The worker spawns on the first request.
daemonSocketNameFunction#
Socket file name. Dev builds (ZIDANE_DEV=1, set by every dev script
in the repo) get their OWN socket so a checkout's TUI, GUI and zidane daemon pair up among themselves and never attach to — or get refused by
— the installed daemon. Without this, a protocol bump in the working
tree turned the dev GUI's dot red against the stable daemon, and a dev
daemon could drain the one the installed TUI relies on.
decisionsPortFunction#
ctx.generate.decide: the decisions model the settings pick
(resolveDecisionsOption), resolved at each call since settings and
credentials change.
Each decision fires decision:start / decision:end on hooks: the
daemon hands its registry's agent-hook fan-out there, so the extensions'
hooks.on('decision:end') observers see the decisions extensions make
outside any run as they see an agent's. (Inside a hook observer of a run
ctx.generate.decide uses that run's decider instead, journaled like the
agent's own.)
defaultSocketPathFunction#
Path of the daemon socket this process uses.
The ZIDANE_DAEMON_SOCKET environment variable wins. Otherwise it is <stateDir>/daemon/<socket name>, see daemonSocketName.
ensureDaemonRunningFunction#
Makes sure a daemon is listening on the socket, spawning one when nothing is. Concurrent calls for the same socket share one start.
Rejects when the spawn fails or the daemon does not accept connections within timeoutMs.
exchangeBoundariesFunction#
Per turn: does a run start there (a parent-run user turn carrying no tool result, not loop-injected)?
fixPathFunction#
Merge the login shell's PATH into process.env.PATH. No-op on Windows or when no shell answers.
historyWorkerUrlFunction#
The history worker's module: the sibling source file in development, the
sibling history-worker.js entry in the published dist, and the entry
embedded under the binary's root in a compiled executable
(scripts/build-executables.ts, scripts/build-server.ts).
hostRefOfFunction#
The host's identity from a hello (or daemon.info) reply; null from a host that predates it.
isClientScopeFunction#
Whether a string is a ClientScope.
loadAllEventsFunction#
A session's whole transcript, loaded a page at a time from the end so no single reply carries it all (a long session with images can outgrow a transport's message size). A page refused as too large is asked again at half the turns, down to one; a single turn that still doesn't fit fails the load. An older host answers the whole transcript to the first call.
loadEventsPageFunction#
One page of sessions.loadEvents (see its paging). An older host answers
the whole transcript: it comes back as one page with no cursor.
loadSessionDocumentFunction#
A session's whole document, read a page of turns at a time so no reply
carries it all (sessions.getTurns pages, then the sessions.get
turns: false head; a page refused as too large is asked again at half
the turns). Every page and the head name the snapshot they come from: a
version change mid-read (a turn appended, a compaction) starts the read
over, so the result is one version, never pages of two. After
SESSION_DOCUMENT_RESTARTS restarts (a session busier than the
read), the whole document comes in one reply. A host that predates
sessions.getTurns sends it that way too; one that names no version is
read as before. null when the host doesn't know the session.
mcpStatusOfFunction#
ctx.mcp.list() of a folder, for hosts that run extensions outside a daemon (the desktop app's in-process host).
mergePathFunction#
Entries of current the shell doesn't know about (ad-hoc prepends from the
launching terminal) stay in front; then the shell's PATH in its own order.
From the Dock every launchd entry is already in the shell PATH, so the
result is exactly the shell PATH, with the shell's precedence intact.
probeSocketFunction#
True when something accepts connections on socketPath. Distinguishes a
live daemon from a stale socket file left by a crashed one.
readTranscriptFunction#
Every turn (with its seq) and run of a session; null when it does not exist.
readTurnsAtFunction#
The session's turns stored at seqs, in seq order; a missing or corrupt row is skipped.
runAttachFunction#
Runs the zidane attach terminal client: picks or creates a session on the daemon (or a remote server), replays its transcript, and relays input and live signals until the user detaches.
Rejects when the connection to the daemon or server drops. It does not exit the process.
runRemoteOAuthFunction#
Runs the client half of a remote OAuth sign-in against a host.
Starts the flow on the host, opens the URL through the UI, forwards the code from the loopback listener or from paste, and resolves with the host's outcome, which never carries a token. Rejects when aborted or when the UI cannot answer a question the provider asks.
runWorkspaceExecStreamFunction#
Runs a command in a session's workspace on a host and streams its output.
The result has the shape of a local ! run. A host without sessions.workspaceExecStream gets the buffered sessions.workspaceExec instead, and then an abort stops the wait, not the command.
selfDaemonSpawnCommandFunction#
Spawn command that re-enters the CURRENT zidane program as … daemon.
Three launch shapes:
- compiled Bun binary —
Bun.mainis the virtual/$bunfs/…entry the binary self-inserts as argv[1], so the spawn must NOT repeat it (verified: repeating it makes the child parse the junk path as its subcommand and boot the full TUI instead of the daemon). - dev (
bun run cli.ts) — the script path must ride along. - Node hosts (no
Bunglobal) — fall back to the published bin.
serveWorkerRpcFunction#
Serve handler on this worker thread.
shellPathFunction#
Reads the PATH of the user's interactive login shell, blocking until it answers.
Tries the user's shell, then /bin/zsh and /bin/bash. Returns undefined on Windows, or when no shell answers in time.
shellPathAsyncFunction#
shellPath without blocking the event loop: same shells, args,
timeout and parsing, run with runBounded (a timeout ends the shell's
whole tree; under Bun its output survives a shell that exits before Bun
watches it). For hosts (the GUI main process) that can start on a cached
PATH and refresh in the background.
startInProcessHostFunction#
Starts a chat host in this process. Clients from connect behave like socket clients, without a socket or a second process.
startIpcDaemonFunction#
Starts a unix-socket daemon around a chat host. Each peer must open with ipc.hello.
A stale socket file left by a dead daemon is replaced. Throws when a live daemon already listens on socketPath.
transcriptPageFunction#
[from, to) of the page, and the cursor of the one before it, over the
turns' exchangeBoundaries (run starts) and turnCutPoints
(where a run too long to send whole may be split; run starts when absent).
turnCutPointsFunction#
Per turn: can a page start there without leaving a tool call's result behind (not a tool-result turn)?
watchFileChangesFunction#
Call onChange after file changes. Returns the function that stops watching.
Classes#
IpcVersionMismatchErrorClass#
Thrown when the host refuses the handshake because its protocol, build or code version does not match the client's.
WorkerRequestErrorClass#
The worker's handler threw: remoteName and message are the thrown error's.
WorkerUnavailableErrorClass#
No worker served the request: do the work on this thread. degraded: none will until the client is recreated.
Constants#
HISTORY_WORKER_KILL_SWITCHConstant#
Kill switch: 1 pages on the host's thread, with no worker.
IPC_BUILD_IDConstant#
Build fingerprint for the handshake. Running from source it is the newest
mtime under the src/ tree at import time. Bundled, it is the mtime of this
module's own file. Client and daemon load the same files from disk, so a
daemon spawned before a rebuild (or a source edit in dev) reports a
different id than a freshly-started client, and the client drains +
respawns it. This closes the "same protocol version, different code"
gap that a hand-bumped version constant can't: during development the
daemon process is frozen at spawn while the tree keeps moving.
Compiled binaries (/$bunfs/… virtual paths) can't be stat'ed and
report 'unknown'; the check is skipped when EITHER side is unknown,
so released builds rely on IPC_PROTOCOL_VERSION alone — correct,
since their code only changes when the version does.
LOAD_EVENTS_MAX_PAGE_TURNSConstant#
Largest limit a host honors.
LOAD_EVENTS_PAGE_TURNSConstant#
Turns per sessions.loadEvents page when only before is given.
MACHINE_PROTOCOL_VERSIONConstant#
Bumped on an incompatible change; a host refuses any other version.
MachineErrorCodeConstant#
Error codes of the machine protocol (JSON-RPC server-error range, apart from IpcErrorCode).
REDACTEDConstant#
Every secret-bearing value of a catalog entry is replaced by this.
SESSION_TURNS_MAX_PAGEConstant#
Largest limit a host honors for sessions.getTurns.
SESSION_TURNS_PAGEConstant#
Turns per sessions.getTurns page by default.
SIGNAL_SEQ_FIELDConstant#
Session-scoped signals fanned out to every peer carry their number in
their session as this extra field on the ipc.signal params (clients
predating it ignore it; IpcClient strips it and hands it to listeners as
SignalMeta). Per session, from 1, gap-free within one host process.
WHOLE_RUN_PAGESConstant#
A run longer than this many pages is split (by limit, so the client's
halving retry on a reply too large still converges).
Interfaces#
AttachOptionsInterface#
Options for runAttach.
CreateSessionParamsInterface#
Params of sessions.create.
DaemonEnvironmentInterface#
The host environment of a daemon or an embedded host: a ChatHostEnvironment backed by disk, with settings writes and runtime-config control.
DaemonExtensionsHostInterface#
The extension host of one daemon or server process.
EnsureDaemonOptionsInterface#
Options for ensureDaemonRunning.
FsDirEntryInterface#
One folder in an FsDirListing.
FsDirListingInterface#
A folder listing from fs.listDirs.
FsRootsInterface#
Folders a host lets clients browse, from fs.roots.
HelloParamsInterface#
Params of the hello handshake.
HistoryWorkerInitInterface#
What ./worker.ts takes as init.
HostAuthMethodInterface#
One credential source of a host's provider (auth.status); no secret.
HostCatalogErrorInterface#
A config file that failed to parse (or an informational skip).
HostCatalogExtensionInterface#
An extension in the host's catalog.
HostCatalogMcpInterface#
An MCP server in the host's catalog. Secret values are redacted.
HostCatalogPluginInterface#
A plugin in the host's catalog.
HostCatalogSkillInterface#
A skill in the host's catalog.
HostEnvironmentOptionsInterface#
Options for createHostEnvironment.
HostImagePortDepsInterface#
What createHostImagePort reads at call time.
HostModelInfoInterface#
ModelInfo as it crosses the wire.
HostModelProviderInterface#
One provider a host can run, with its models (models.list).
HostOAuthBeginInterface#
Result of auth.oauth.begin.
HostOAuthCompleteInterface#
auth.oauth.complete: the outcome, never a token.
HostProviderAuthInterface#
Sign-in state of one provider on a host, from auth.status. Holds no secret.
HostRefInterface#
Which host a client is talking to, as clients keep it (from hostRefOf).
HostTranscriptPagerInterface#
Where a host's paged sessions.loadEvents are served: the history worker when it can, else the in-thread cache.
InProcessClientOptionsInterface#
Options for InProcessHost.connect.
InProcessHostInterface#
A chat host running in the caller's process, with clients that call it directly.
InProcessHostOptionsInterface#
Options for startInProcessHost.
IpcClientInterface#
A connected host client, shared by every transport. Requests are checked against IpcMethods.
IpcClientOptionsInterface#
Options for connectIpcClient.
IpcDaemonInterface#
A running unix-socket daemon.
IpcDaemonOptionsInterface#
Options for startIpcDaemon.
LoadEventsPagingInterface#
Paging options of sessions.loadEvents.
MachineLinksHandlerOptionsInterface#
Options for createMachineLinkHandlers: the host, settings access and the signal sink.
ModelFallbackRefInterface#
One ordered model-fallback target (primary excluded).
RemoteOAuthStartInterface#
Result of auth.oauth.begin.
RemoteOAuthUiInterface#
Hooks a window supplies to runRemoteOAuth: open the browser, take pasted input, answer the provider's questions, and abort.
ReplayDataInterface#
What a client's own load gives replay.
ReplayReadInterface#
A client's own read of a stored session, for replay.
SequencedSignalInterface#
A session signal with its position in the session, as kept in the host's replay log.
SessionEventsPageInterface#
One page of a session's stream events, as sessions.loadEvents answers it.
SessionRowInterface#
One row of a cross-window session list.
SharedHostSetupInterface#
What a host in the window's own process takes from the window.
TranscriptHistoryWorkerInterface#
A client of the history worker, from createTranscriptHistoryWorker.
TranscriptReadInterface#
A whole session as a replay needs it.
TranscriptReaderInterface#
Whole-session reads, from createTranscriptReader.
TranscriptReplayInterface#
A whole-session replay: pages are transcriptPage slices of it.
TranscriptReplayCacheInterface#
Whole-session replays and documents, kept per session version (see the module header).
WorkerLikeInterface#
A worker from either runtime: Bun / Web Worker (addEventListener) or Node worker_threads.Worker (on).
WorkerPortLikeInterface#
A worker's port to its parent, from either runtime.
WorkerRpcClientInterface#
The calling side of a worker RPC, from createWorkerRpcClient.
WorkerRpcClientOptionsInterface#
How createWorkerRpcClient spawns and supervises its worker.
WorkerRpcContextInterface#
What a handler can do besides answering.
WorkerRpcHandlerInterface#
What serveWorkerRpc runs in the worker.
WorkspaceExecStreamOptionsInterface#
Options for runWorkspaceExecStream.
WorkspaceExecStreamResultInterface#
Outcome of runWorkspaceExecStream, shaped like a local ! run.
Types#
HistoryWorkerRequestType#
The history worker's requests.
HostKindType#
What a host process is: a window's own (in-process), the local shared
daemon (daemon), or a remote zidane-server (server).
HostOAuthStepType#
A question the provider's sign-in asks before (or after) its URL: a choice
between flows, or a free text. The client shows it and answers with
auth.oauth.respond; the host never picks for the user.
QueueDeliveryType#
What queue.add does with a message while a run works: auto follows
Settings.messageDelivery, steer and followUp hand it to the run,
queue keeps it in the queue until the run ends.
RemoteOAuthTargetType#
What to sign in to: a provider by key (account: the stored OAuth
account, by its stable id, whose tokens it replaces), or an MCP server by name.
SessionDocumentHeadType#
What sessions.get answers: the document; with turns: false, the
version of the snapshot it was cut from, the one sessions.getTurns
pages carry.
SessionResumeResultType#
Result of sessions.resume. On success, the signals the client missed after its last position. Otherwise the reason it must reload instead.
Re-exports#
These names are documented on the entry point that owns them.
- ApprovalHub
- ApprovalOriginator
- ApprovalRequestInput
- ChatHost
- ChatHostEnvironment
- CompactionEntry
- createApprovalHub
- createChatHost
- createInteractionHub
- createMemoryApprovalsStore
- EditOutcomeLedger
- extractEditPayloadForGate
- HostSettings
- HostSignal
- InteractionHub
- PendingAnnotation
- PendingApprovalsStore
- PendingInteraction
- PlansSnapshot
- PromptAttachment
- QueuedItem
- readFileSafe
- resolveCwdPath
- resolveStorageDirs
- runCompaction
- RunningWorkItem
- SendOptions
- SerializedPreview
- serializePreview
- SessionPresence
- StorageDirs
- WireApprovalRequest
- BlockRunClient
- BlockRunOnHostOptions
- CLIENT_SCOPES
- ClientScope
- createExtensionsClient
- extensionInstallErrorData
- ExtensionInstallErrorData
- ExtensionsClient
- ExtensionsUiServer
- HelloResult
- IPC_MIN_PROTOCOL_VERSION
- IPC_PROTOCOL_VERSION
- IpcErrorCode
- IpcMethodName
- IpcMethods
- IpcParams
- IpcResult
- METHOD_SCOPES
- methodScope
- runBlockOnHost
- scopeAllows
- SignalMeta