zidane 7.2.2 → 7.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ import { p as resolveStorageDirs } from "./plugins-gbI7SBA6.js";
6
6
  import { a as successResponse, i as errorResponse, n as JsonRpcRemoteError, r as createJsonRpcConnection } from "./json-rpc-rSWMUh9b.js";
7
7
  import { Vt as isMainSession } from "./turn-operations-DrbJlpq9.js";
8
8
  import { c as searchSessionHistoryIndexAsync, n as buildSessionSearchIndexAsync, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
9
- import { Bn as resolveConfig, Di as deriveSessionTitle, Ft as toTodosSnapshot, Jt as createFileMcpCredentialStore, Kt as sessionModelFallbacks, Oi as eventsFromTurns, Pt as createChatHost, fn as DEFAULT_SETTINGS, gn as SETTINGS_TOGGLES, yn as enforceSettingsInvariants } from "./approvals-CnE_8JVZ.js";
9
+ import { Bn as resolveConfig, Di as deriveSessionTitle, Ft as toTodosSnapshot, Jt as createFileMcpCredentialStore, Kt as sessionModelFallbacks, Oi as eventsFromTurns, Pt as createChatHost, fn as DEFAULT_SETTINGS, gn as SETTINGS_TOGGLES, yn as enforceSettingsInvariants } from "./approvals-DKaJZYlm.js";
10
10
  import { h as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath } from "./auth-BG2rVKSt.js";
11
11
  import { basename, delimiter, dirname, join } from "node:path";
12
12
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
@@ -28,7 +28,7 @@ import process$1 from "node:process";
28
28
  * retires it, so an upgrade can never leave windows silently executing on a
29
29
  * process started from the previous release.
30
30
  */
31
- const ZIDANE_VERSION = "7.2.2";
31
+ const ZIDANE_VERSION = "7.2.4";
32
32
  /**
33
33
  * Compare two dotted versions (`7.0.19`, `7.1.0-beta.1`): negative when `a`
34
34
  * is older, positive when newer, 0 when equal. Pre-release tags are ignored
@@ -263,6 +263,11 @@ async function startDaemon(options) {
263
263
  * `suggest.resolve` results and `suggest:changed` signals carry the
264
264
  * resolver's full ordered `candidates` list so daemon mode keeps
265
265
  * ↑/↓ candidate rotation instead of degrading to one suggestion.
266
+ * - ADDITIVE (no version bump — `optional()` swallows "method not
267
+ * found" on an older daemon, which then simply keeps its stale
268
+ * registries as before): `runtime.reload` RPC drops the daemon's
269
+ * cached extension registries and retires live agents after a window
270
+ * reloaded its extensions.
266
271
  */
267
272
  const IPC_PROTOCOL_VERSION = 9;
268
273
  /**
@@ -1053,6 +1058,10 @@ async function startIpcDaemon(options) {
1053
1058
  },
1054
1059
  "chat.isRunning": (p) => host.isSessionRunning(p.sessionId),
1055
1060
  "chat.rebuildAgent": (p) => host.disposeAgentForRebuild(p.sessionId),
1061
+ "runtime.reload": () => {
1062
+ env.invalidateRuntime();
1063
+ host.rebuildAllAgents();
1064
+ },
1056
1065
  "chat.listWork": (p) => host.listRunningWork(p.sessionId),
1057
1066
  "chat.cancelWork": (p) => host.cancelRunningWork(p.sessionId, p.id, p.reason),
1058
1067
  "sessions.list": async (p) => {
@@ -1537,4 +1546,4 @@ function fixPath() {
1537
1546
  //#endregion
1538
1547
  export { selfDaemonSpawnCommand as _, createDaemonEnvironment as a, connectIpcClient as c, IpcErrorCode as d, IPC_BUILD_ID as f, probeSocket as g, ensureDaemonRunning as h, startIpcDaemon as i, connectOrSpawnDaemon as l, defaultSocketPath as m, mergePath as n, createFileApprovalsStore as o, daemonSocketName as p, shellPath as r, IpcVersionMismatchError as s, fixPath as t, IPC_PROTOCOL_VERSION as u };
1539
1548
 
1540
- //# sourceMappingURL=shell-path-C-fcAmH-.js.map
1549
+ //# sourceMappingURL=shell-path-h5czzllR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-path-h5czzllR.js","names":["(pkg as { version?: string }).version","process"],"sources":["../package.json","../src/version.ts","../src/ipc/daemon/lifecycle.ts","../src/ipc/protocol.ts","../src/ipc/client.ts","../src/ipc/daemon/environment.ts","../src/ipc/daemon/server.ts","../src/ipc/daemon/shell-path.ts"],"sourcesContent":["","/**\n * The version of the zidane code this process is running — `package.json`\n * inlined at build time (Bun `--compile`, tsdown for `dist/`, and Vite for\n * the GUI main all fold JSON imports into the bundle, so this never reads\n * the filesystem at runtime and cannot report a NEIGHBOUR package's version\n * the way a `require('../package.json')` from a bundled chunk can).\n *\n * Used by the daemon handshake: a client running newer code than the daemon\n * retires it, so an upgrade can never leave windows silently executing on a\n * process started from the previous release.\n */\nimport pkg from '../package.json' with { type: 'json' }\n\nexport const ZIDANE_VERSION: string = (pkg as { version?: string }).version ?? '0.0.0'\n\n/**\n * Compare two dotted versions (`7.0.19`, `7.1.0-beta.1`): negative when `a`\n * is older, positive when newer, 0 when equal. Pre-release tags are ignored\n * — same release train, same code as far as the daemon fence is concerned.\n * Unparseable input compares equal (never fences on garbage).\n */\nexport function compareVersions(a: string, b: string): number {\n const pa = parse(a)\n const pb = parse(b)\n if (!pa || !pb)\n return 0\n for (let i = 0; i < 3; i++) {\n const d = (pa[i] ?? 0) - (pb[i] ?? 0)\n if (d !== 0)\n return d\n }\n return 0\n}\n\nfunction parse(v: string): number[] | null {\n const m = /^v?(\\d+)\\.(\\d+)\\.(\\d+)/.exec(v.trim())\n return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null\n}\n","/**\n * Daemon lifecycle: socket-path resolution, liveness probing, stale-socket\n * cleanup, and spawn-on-demand.\n *\n * The socket lives in a dedicated daemon subdir of the resolved state dir\n * (`~/.zidane/daemon/daemon.sock` by default, honoring the same\n * `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX` env knobs every other surface\n * uses), overridable via `ZIDANE_DAEMON_SOCKET` for parallel installs and\n * tests.\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync, readdirSync, statSync } from 'node:fs'\nimport { connect } from 'node:net'\nimport { basename, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { resolveStorageDirs } from '../../chat/xdg'\n\n/**\n * Build fingerprint for the handshake — the mtime of this module's file\n * at import time. Client and daemon load the same file from disk, so a\n * daemon spawned before a rebuild (or a source edit in dev) reports a\n * different id than a freshly-started client, and the client drains +\n * respawns it. This closes the \"same protocol version, different code\"\n * gap that a hand-bumped version constant can't: during development the\n * daemon process is frozen at spawn while the tree keeps moving.\n *\n * Compiled binaries (`/$bunfs/…` virtual paths) can't be stat'ed and\n * report `'unknown'`; the check is skipped when EITHER side is unknown,\n * so released builds rely on `IPC_PROTOCOL_VERSION` alone — correct,\n * since their code only changes when the version does.\n */\nexport const IPC_BUILD_ID: string = (() => {\n try {\n const selfPath = fileURLToPath(import.meta.url)\n /*\n * Dev (running from source): fingerprint the WHOLE ipc surface, not\n * just this file — a change to server.ts / engine.ts / runtime code\n * with no edit to lifecycle.ts must still retire an old daemon. Walk\n * `src/ipc/**` and take the newest mtime. When the tree isn't present\n * (bundled dist, compiled binary) the walk throws and we fall back to\n * this module's own file mtime; in a bundle every ipc source collapses\n * into one chunk, so that single mtime already moves on any ipc change.\n */\n /*\n * Root validation is load-bearing: from a built bundle this module\n * lives flat in `dist/`, so the two-levels-up guess would be the\n * PACKAGE root — walking it would synchronously stat the whole repo\n * (node_modules, .git) at import time and fingerprint unrelated\n * churn. Only walk when the candidate directory demonstrably IS the\n * ipc source tree; a bundle falls back to this chunk's own mtime,\n * which already moves on every rebuild.\n */\n const ipcRoot = dirname(dirname(selfPath))\n if (!existsSync(join(ipcRoot, 'protocol.ts')))\n return String(Math.floor(statSync(selfPath).mtimeMs))\n\n /*\n * Dev scope: walk the whole `src/` tree, not just `src/ipc` — daemon\n * behavior also lives in agent.ts, loop.ts, and chat/** (store,\n * replay, discovery), and an edit there must retire a running dev\n * daemon just as surely as an ipc edit (the spawn-marker incident\n * lived in chat/store.ts). A few hundred stats — milliseconds.\n */\n const srcRoot = dirname(ipcRoot)\n let newest = 0\n const walk = (dir: string): void => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory())\n walk(full)\n else\n newest = Math.max(newest, statSync(full).mtimeMs)\n }\n }\n try {\n walk(srcRoot)\n }\n catch {\n newest = statSync(selfPath).mtimeMs\n }\n return String(Math.floor(newest))\n }\n catch {\n return 'unknown'\n }\n})()\n\n/**\n * Socket file name. Dev builds (`ZIDANE_DEV=1`, set by every `dev` script\n * in the repo) get their OWN socket so a checkout's TUI, GUI and `zidane\n * daemon` pair up among themselves and never attach to — or get refused by\n * — the installed daemon. Without this, a protocol bump in the working\n * tree turned the dev GUI's dot red against the stable daemon, and a dev\n * daemon could drain the one the installed TUI relies on.\n */\nexport function daemonSocketName(env: NodeJS.ProcessEnv = process.env): 'daemon.sock' | 'dev.sock' {\n return env.ZIDANE_DEV && env.ZIDANE_DEV !== '0' ? 'dev.sock' : 'daemon.sock'\n}\n\nexport function defaultSocketPath(): string {\n const explicit = process.env.ZIDANE_DAEMON_SOCKET\n if (explicit)\n return explicit\n /*\n * The socket is transient runtime state, so it lands under the resolved\n * state dir — XDG-aware on XDG installs, `<storageDir>/<prefix>` in\n * legacy mode. Uses the same resolver every other surface reads through,\n * honoring `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX`.\n *\n * DEDICATED `daemon/` subdir (protocol v6): the state dir is shared with\n * `state.json` / logs, so the daemon can't chmod it 0700 without\n * affecting unrelated files. The server hardens this subdir instead;\n * client and server must agree on this path.\n */\n const dirs = resolveStorageDirs({\n ...(process.env.ZIDANE_PREFIX !== undefined ? { prefix: process.env.ZIDANE_PREFIX } : {}),\n })\n return join(dirs.stateDir, 'daemon', daemonSocketName())\n}\n\n/**\n * Pre-v6 socket location for the given (v6) socket path, or null when the\n * path isn't the default `<stateDir>/daemon/daemon.sock` layout (explicit\n * `ZIDANE_DAEMON_SOCKET` users never had a migration to run). A daemon\n * still serving the old path would be ORPHANED by the move — new clients\n * never probe it, and a service-style `--idle-exit 0` daemon would hold\n * agents and run-guard markers against the same sessions.db forever —\n * so daemon startup probes this path and drains any survivor.\n */\nexport function legacySocketPath(socketPath: string): string | null {\n const dir = dirname(socketPath)\n if (basename(socketPath) !== 'daemon.sock' || basename(dir) !== 'daemon')\n return null\n return join(dirname(dir), 'daemon.sock')\n}\n\n/**\n * Spawn command that re-enters the CURRENT zidane program as `… daemon`.\n *\n * Three launch shapes:\n * - compiled Bun binary — `Bun.main` is the virtual `/$bunfs/…` entry the\n * binary self-inserts as argv[1], so the spawn must NOT repeat it\n * (verified: repeating it makes the child parse the junk path as its\n * subcommand and boot the full TUI instead of the daemon).\n * - dev (`bun run cli.ts`) — the script path must ride along.\n * - Node hosts (no `Bun` global) — fall back to the published bin.\n */\nexport function selfDaemonSpawnCommand(): string[] {\n const bunMain = (globalThis as { Bun?: { main?: string } }).Bun?.main\n // Compiled binary: the virtual entry parses its own `daemon` subcommand.\n if (bunMain?.startsWith('/$bunfs'))\n return [process.execPath, 'daemon']\n // Dev-from-source: only re-execute `Bun.main` when it's actually a\n // zidane CLI entry (`cli.ts`/`cli.js`). For any other embedder (library\n // consumer, script) re-running their entry with a `daemon` argv would\n // launch an arbitrary program — fall back to the published bin instead.\n if (bunMain && /(?:^|[/\\\\])cli\\.[cm]?[jt]s$/.test(bunMain))\n return [process.execPath, bunMain, 'daemon']\n return ['zidane-daemon']\n}\n\n/**\n * True when something accepts connections on `socketPath`. Distinguishes a\n * live daemon from a stale socket file left by a crashed one.\n */\nexport function probeSocket(socketPath: string, timeoutMs = 1000): Promise<boolean> {\n return new Promise((resolve) => {\n /*\n * Skip the connect entirely unless the path is an actual socket file.\n * A stale REGULAR file (crashed daemon) or a missing path both mean\n * \"nothing listening\" — and connecting to them can surface a noisy\n * async ENOENT/ENOTSOCK that some runtimes treat as uncaught.\n */\n try {\n if (!existsSync(socketPath) || !statSync(socketPath).isSocket()) {\n resolve(false)\n return\n }\n }\n catch {\n resolve(false)\n return\n }\n\n let socket: ReturnType<typeof connect>\n try {\n socket = connect(socketPath)\n }\n catch {\n resolve(false)\n return\n }\n const done = (alive: boolean): void => {\n socket.destroy()\n resolve(alive)\n }\n socket.once('connect', () => done(true))\n socket.once('error', () => done(false))\n socket.setTimeout(timeoutMs, () => done(false))\n })\n}\n\nexport interface EnsureDaemonOptions {\n socketPath?: string\n /**\n * Command used to spawn the daemon when nothing is listening. Defaults to\n * `['zidane-daemon']` (the published bin). The child is detached and\n * unref'd — it outlives the spawning client by design.\n */\n spawnCommand?: string[]\n /** Total time to wait for the daemon to accept connections. */\n timeoutMs?: number\n}\n\n/**\n * Make sure a daemon is listening, spawning one if needed. Resolves with\n * the socket path once a connection succeeds; rejects when the daemon\n * failed to come up within `timeoutMs`.\n */\nconst pendingStarts = new Map<string, Promise<string>>()\n\nexport function ensureDaemonRunning(options: EnsureDaemonOptions = {}): Promise<string> {\n const socketPath = options.socketPath ?? defaultSocketPath()\n const pending = pendingStarts.get(socketPath)\n if (pending)\n return pending\n const start = startDaemon({ ...options, socketPath }).finally(() => pendingStarts.delete(socketPath))\n pendingStarts.set(socketPath, start)\n return start\n}\n\nasync function startDaemon(options: EnsureDaemonOptions & { socketPath: string }): Promise<string> {\n const socketPath = options.socketPath\n if (await probeSocket(socketPath))\n return socketPath\n\n const [cmd, ...args] = options.spawnCommand ?? ['zidane-daemon']\n const child = spawn(cmd!, args, {\n detached: true,\n stdio: 'ignore',\n env: { ...process.env, ZIDANE_DAEMON_SOCKET: socketPath },\n })\n /*\n * A missing binary (e.g. `zidane-daemon` not on PATH) emits an async\n * `error` event; without a listener Node rethrows it as an uncaught\n * exception and crashes the caller. Capture it and surface a clean\n * rejection through the poll loop below.\n */\n let spawnError: Error | undefined\n child.once('error', (err: Error) => {\n spawnError = err\n })\n child.unref()\n\n const deadline = Date.now() + (options.timeoutMs ?? 8000)\n for (;;) {\n if (spawnError !== undefined)\n throw new Error(`Failed to spawn zidane daemon (${cmd}): ${spawnError.message}`)\n if (await probeSocket(socketPath))\n return socketPath\n if (Date.now() > deadline)\n throw new Error(`zidane daemon did not come up on ${socketPath} within ${options.timeoutMs ?? 8000}ms.`)\n await new Promise(r => setTimeout(r, 150))\n }\n}\n","import type { ContextSegmentsReport } from '../chat/host/engine'\nimport type {\n CreateWorkerParams as OrchestrationCreateWorkerParams,\n WorkerRow as OrchestrationWorkerRow,\n WorkerSummary as OrchestrationWorkerSummary,\n} from '../chat/host/orchestration'\nimport type {\n CommandArgSuggestionSummary,\n CommandSummary,\n HostSettings,\n PendingInteraction,\n PromptAttachment,\n QueuedItem,\n RunningWorkItem,\n SendOptions,\n SessionPresence,\n TodosSnapshot,\n WireApprovalRequest,\n} from '../chat/host/signals'\n/**\n * zidane/ipc wire protocol — the transport-agnostic contract between a chat\n * host (the process that owns live `Agent` instances) and its clients (GUI\n * renderer, TUI window, `zidane attach`, …).\n *\n * The host-domain shapes (the `HostSignal` push union and everything it\n * carries) live in `src/chat/host/signals.ts` — the host must not depend on\n * `src/ipc/**`. This module re-exports them unchanged and adds the\n * transport-only contract: the typed JSON-RPC method map, session rows, and\n * the daemon handshake.\n *\n * Everything in this file is JSON-serializable: it must survive Electron's\n * structured clone AND a JSON-RPC socket hop unchanged. Canonical agent\n * types (`StreamEvent`, `InteractionRequest`, …) are re-used where they are\n * already wire-safe; host-only shapes (live `resolve` callbacks, `Map`s)\n * never appear here.\n *\n * Versioning: bump `IPC_PROTOCOL_VERSION` on any breaking change to the\n * signal union or the method map. The daemon handshake refuses mismatched\n * peers so a stale daemon never talks to an upgraded client (see\n * `daemon/lifecycle.ts`).\n */\nimport type {\n ApprovalDecision,\n ContextBreakdown,\n InteractionResponse,\n ProviderKey,\n SearchTurnResult,\n SessionHistoryMatch,\n StreamEvent,\n SystemMdFile,\n TodoItem,\n} from '../chat/index'\nimport type { SessionArtifact } from '../session/artifacts'\nimport type { JsonValue, SessionTurn, ThinkingLevel } from '../types'\n\nexport type {\n ApprovalOriginator,\n CommandArgSuggestionSummary,\n CommandSummary,\n HostSettings,\n HostSignal,\n PendingInteraction,\n PromptAttachment,\n QueuedItem,\n RunningWorkItem,\n SendOptions,\n SerializedPreview,\n SessionPresence,\n TodosSnapshot,\n WireApprovalRequest,\n} from '../chat/host/signals'\n\n/**\n * v6 (changes since v5):\n * - `event` signals carry an optional `echo` flag marking host-generated\n * SEND/STEER echoes of the user's own message.\n * - `chat.pushQueued` result gained the `'queued'` delivered variant.\n * - `sessions.setModel` now honors `fallbacks`: it persists the canonical\n * `metadata.modelChain` and CLEARS a previously persisted chain when\n * omitted/empty. It also accepts `effort` / `modelOptions` and resets\n * both when omitted (a pick is atomic — no stale per-model knobs).\n * - additive: `extension.emit` notification + `extension:signal` fan-out\n * (the permission-gated per-extension cross-window bus, `ctx.bus`).\n * Older daemons ignore the unknown notification; older clients drop the\n * unknown signal kind — no version bump needed.\n * - additive: `extension.emit` / `extension:signal` carry an optional\n * `nonce` (the emitter's per-process random id) so receivers dedupe on\n * `(extensionId, nonce, seq)` instead of the collision-prone\n * `(extensionId, seq)`. Older peers omit it and fall back to the\n * seq-only key — no version bump needed.\n * - `SessionRow.modelFallbacks` is now populated from the session's\n * persisted `metadata.modelChain`.\n * - Default socket path moved from `<stateDir>/daemon.sock` to the\n * dedicated `<stateDir>/daemon/daemon.sock` (dir chmod'd 0700) so the\n * daemon can harden its socket dir without touching shared state files.\n * - ADDITIVE (no version bump — older daemons answer \"method not\n * found\", clients degrade to local compute): `suggest.resolve` RPC\n * resolves inline ghost-text autocomplete daemon-side, and the\n * `suggest:changed` signal fans the result out to every OTHER peer\n * on the session so mirroring windows paint the same ghost without\n * recomputing.\n * - ADDITIVE (no version bump — optional field, older daemons simply\n * omit it and clients fall back to the single `suggestion`):\n * `suggest.resolve` results and `suggest:changed` signals carry the\n * resolver's full ordered `candidates` list so daemon mode keeps\n * ↑/↓ candidate rotation instead of degrading to one suggestion.\n * - ADDITIVE (no version bump — `optional()` swallows \"method not\n * found\" on an older daemon, which then simply keeps its stale\n * registries as before): `runtime.reload` RPC drops the daemon's\n * cached extension registries and retires live agents after a window\n * reloaded its extensions.\n */\nexport const IPC_PROTOCOL_VERSION = 9\n\n/**\n * JSON-RPC error codes specific to the zidane daemon. Lives here (not in\n * `daemon/server.ts`) so clients can import the codes without pulling the\n * server's module graph.\n */\nexport const IpcErrorCode = {\n VersionMismatch: -32010,\n NotHandshaken: -32011,\n} as const\n\n/* -------------------------------------------------------------------------\n * Request/response methods (client → host)\n * ---------------------------------------------------------------------- */\n\n/**\n * Typed method map for the daemon transport. Keys are JSON-RPC method\n * names; each entry pins params/result so both the daemon dispatch table\n * and the client proxy are checked against the same contract.\n *\n * Scope: the chat core every client needs. Host-UI-specific surfaces\n * (folder pickers, themes, keybindings) intentionally stay client-local.\n */\nexport interface IpcMethods {\n /** `wake` is host-internal (see `SendOptions.wake`) — not accepted over the wire. */\n 'chat.send': { params: Omit<SendOptions, 'wake'>, result: { runId: string } }\n 'chat.interrupt': { params: { sessionId: string }, result: void }\n 'chat.steer': { params: { sessionId: string, message: string }, result: void }\n 'chat.followUp': { params: { sessionId: string, message: string }, result: void }\n 'chat.pushQueued': {\n params: { sessionId: string, message: string, activatedSkills?: string[] }\n result: { delivered: 'steer' | 'send' | 'command' | 'queued' }\n }\n 'chat.isRunning': { params: { sessionId: string }, result: boolean }\n /**\n * Retire the session's live agent so the next run rebuilds against\n * fresh config/credentials (MCP login, extension reload). Run-end\n * deferred when a run is in flight; HITL/queue state survives.\n */\n 'chat.rebuildAgent': { params: { sessionId: string }, result: void }\n /**\n * Drop the daemon's cached runtime configs (extension registries, per\n * cwd) and retire every live agent so the next command or run rebuilds\n * against what is on disk now. Sent by a window after it reloaded its\n * own extensions (install, update, remove, `/reload`): extension `/`\n * commands execute daemon-side, so without this the daemon keeps\n * running the code it loaded at boot. Busy sessions rebuild at run end.\n */\n 'runtime.reload': { params: Record<string, never>, result: void }\n 'chat.listWork': { params: { sessionId: string }, result: RunningWorkItem[] }\n 'chat.cancelWork': { params: { sessionId: string, id: string | 'all', reason?: string }, result: boolean }\n\n 'sessions.list': { params: { projectRoot?: string | null, archive?: 'active' | 'archived' | 'all' }, result: SessionRow[] }\n 'sessions.create': { params: CreateSessionParams, result: SessionRow }\n 'sessions.delete': { params: { sessionId: string }, result: void }\n 'sessions.setTitle': { params: { sessionId: string, title: string }, result: void }\n 'sessions.setArchived': { params: { sessionId: string, archived: boolean }, result: void }\n 'sessions.setAgentId': { params: { sessionId: string, agentId: string }, result: void }\n 'sessions.setEffort': { params: { sessionId: string, effort: ThinkingLevel }, result: void }\n /**\n * The one model-swap contract (`ChatHost.setSessionModel`). A pick is a\n * whole: `fallbacks` (ordered, primary excluded) REPLACES the persisted\n * `metadata.modelChain` (omitted/empty clears it); `effort` and\n * `modelOptions` are reset to the supplied values (omitted clears them —\n * per-model knobs never leak onto the new model). The live agent is\n * retired so the next run rebuilds on the new pick (run-end deferred\n * while a run is in flight).\n */\n 'sessions.setModel': {\n params: {\n sessionId: string\n providerKey: ProviderKey\n modelId: string\n fallbacks?: ModelFallbackRef[]\n effort?: ThinkingLevel\n modelOptions?: Record<string, boolean>\n }\n result: void\n }\n 'sessions.setModelOptions': { params: { sessionId: string, options: Record<string, boolean> }, result: void }\n 'sessions.loadEvents': { params: { sessionId: string }, result: StreamEvent[] }\n 'sessions.getTurn': { params: { sessionId: string, turnId: string }, result: SessionTurn | null }\n 'sessions.contextBreakdown': { params: { sessionId: string }, result: ContextBreakdown | null }\n /**\n * Artifacts one session recorded (PRs, branches, commits, files,\n * extension kinds — see `session/artifacts.ts`), newest first. Empty on\n * stores without artifact support. Additive; pre-artifact daemons\n * reject the method and clients treat that as empty.\n */\n 'sessions.artifacts': { params: { sessionId: string, kind?: string, limit?: number }, result: SessionArtifact[] }\n /**\n * Artifacts across sessions, filtered by project root (`null` = untagged\n * sessions, omitted = every project) and/or kind/uri (reverse lookup).\n */\n 'artifacts.list': { params: { projectRoot?: string | null, kind?: string, uri?: string, since?: number, limit?: number }, result: SessionArtifact[] }\n /** Remove one artifact. `false` when the store has no artifact support. Fans out `artifact:changed`. */\n 'artifacts.delete': { params: { sessionId: string, artifactId: string }, result: boolean }\n /**\n * Upsert an artifact — the lazy `resolve()` write-back (refreshed\n * `label` / `meta`). Same merge semantics as `SessionStore.upsertArtifact`;\n * `null` when the session is unknown or the store lacks support.\n */\n 'artifacts.upsert': { params: { artifact: SessionArtifact }, result: SessionArtifact | null }\n /**\n * Transcript search over ONE session, daemon-side. The daemon owns the\n * authoritative turns (live wrapper when an agent is active — fresh\n * mid-run, which a window's load-time snapshot is not) and an LRU index\n * cache shared by every window, so repeat queries pay match cost only.\n * Results come back in TURN ORDER (search is a transcript filter).\n */\n 'history.searchSession': {\n params: { sessionId: string, query: string, limit?: number }\n result: SearchTurnResult[]\n }\n /**\n * Cross-session content search, daemon-side. One RPC replaces a\n * window's per-session index/build/search round-trips — indexes are\n * built once in the daemon and shared by every window.\n */\n 'history.searchAll': {\n params: { query: string, sessionIds: string[], excerptLimit?: number }\n result: Record<string, SessionHistoryMatch>\n }\n /**\n * A window appended transcript content OUT-OF-BAND (local `/` command\n * output, `!` shell turns — written straight to the shared store, not\n * through a host run). The daemon fans out `sessions:changed` so every\n * window (issuer included) re-converges on the canonical replay NOW\n * instead of at the next run end. Sent as a notification.\n */\n 'sessions.touch': { params: { sessionId: string }, result: void }\n 'sessions.compact': { params: { sessionId: string }, result: void }\n\n 'todos.get': { params: { sessionId: string }, result: TodosSnapshot | null }\n /**\n * Apply a user todo edit (add / edit / remove / cycle / clear) to the\n * session the MODEL reads.\n *\n * Required because todos live in `session.metadata` and `todowrite` reads\n * them off the daemon agent's in-memory `Session`. A window writing its own\n * `Session` instance persisted an edit that the daemon never observed, so the\n * model's next `todowrite` silently overwrote it.\n *\n * Sends the whole list rather than a delta: the mutators in\n * `chat/todo-edits.ts` are already whole-list transforms, and a delta would\n * need conflict rules against a list the model rewrites concurrently.\n *\n * `null` = no live agent (or no run owns a todo slot), and the caller should\n * apply its own store-level write — race-free, since nothing is appending.\n */\n 'todos.set': { params: { sessionId: string, items: readonly TodoItem[] }, result: TodosSnapshot | null }\n\n /*\n * Orchestration — a pinned \"main\" session that drives other sessions\n * (\"workers\"). Feature-gated by `Settings.orchestration`; every method below\n * resolves to `null` / rejects when it is off — the main SURVIVES the toggle\n * as a persisted session, so the gate has to live on the verbs, not just on\n * discovery.\n *\n * `mainId` is load-bearing, not decorative: the host refuses any verb whose\n * `mainId` is not itself a main session. Targets are NOT limited to the\n * workers that main created — the main observes and drives the whole store —\n * but it may never target itself or another main (the depth cap).\n */\n /** The main session id, creating it (directory-less) when the setting is on and none exists. */\n 'orchestration.main': { params: Record<string, never>, result: string | null }\n 'orchestration.createWorker': { params: { mainId: string } & OrchestrationCreateWorkerParams, result: string }\n 'orchestration.list': { params: { mainId: string, limit?: number }, result: OrchestrationWorkerRow[] }\n 'orchestration.read': { params: { mainId: string, sessionId: string }, result: OrchestrationWorkerSummary | null }\n /** `wake` (default true): whether that run finishing wakes the main. */\n 'orchestration.send': { params: { mainId: string, sessionId: string, prompt: string, wake?: boolean }, result: void }\n 'orchestration.steer': { params: { mainId: string, sessionId: string, message: string }, result: void }\n 'orchestration.cancel': { params: { mainId: string, sessionId: string }, result: void }\n 'orchestration.transcript': { params: { mainId: string, sessionId: string, limit?: number }, result: string }\n\n /** Extension-contributed `/` commands discovered for `cwd`. */\n 'commands.list': { params: { cwd: string }, result: CommandSummary[] }\n /** Argument suggestions for `/name <prefix>` — best-effort, empty on failure. */\n 'commands.completeArgs': { params: { cwd: string, name: string, argsPrefix: string }, result: CommandArgSuggestionSummary[] }\n\n 'interactions.list': { params: { sessionId?: string }, result: PendingInteraction[] }\n 'interactions.respond': { params: { requestId: string, response: InteractionResponse }, result: { ok: boolean } }\n 'interactions.cancel': { params: { requestId: string, reason?: string }, result: { ok: boolean } }\n 'interactions.rehydrate': { params: { sessionId: string }, result: PendingInteraction[] }\n\n 'approvals.resolve': { params: { requestId: string, decision: ApprovalDecision }, result: void }\n 'approvals.denyAll': { params: { sessionId?: string }, result: void }\n 'approvals.listPending': { params: Record<string, never>, result: WireApprovalRequest[] }\n\n /**\n * Type-ahead queue, host-owned so every window sees and can manipulate\n * the same list. `queue.add` queues when a run is in flight and sends\n * immediately otherwise; the host auto-drains the queue FIFO at run end.\n */\n 'queue.list': { params: { sessionId: string }, result: QueuedItem[] }\n\n /**\n * Which `SYSTEM.md` override composes this session's prompt, for windows\n * attaching to an already-warm agent.\n *\n * The `system-md` signal fires once per agent BUILD and the daemon caches\n * agents, so the normal multi-window case (daemon already running, open a\n * second terminal or the GUI) would otherwise never learn that the built-in\n * safety and git doctrine has been replaced. `null` = no live agent yet, in\n * which case the next build broadcasts it.\n */\n 'sessions.systemMd': {\n params: { sessionId: string }\n result: { file: SystemMdFile | null, shadowed: readonly SystemMdFile[] } | null\n }\n /**\n * Every chunk of the session's system prompt with provenance — the\n * Instructions tab. Built from the live agent's assembly, so it reflects\n * MCP instructions, extension fragments and the resolved `SYSTEM.md`.\n */\n 'sessions.contextSegments': { params: { sessionId: string }, result: ContextSegmentsReport | null }\n /** Seed a user-scope `SYSTEM.md` from the session's doctrine; throws when an override could not apply. */\n 'sessions.createSystemOverride': { params: { sessionId: string }, result: { path: string, created: boolean } }\n 'queue.add': {\n /** `asCommand`: see `SendOptions.asCommand` — the host refuses when it cannot resolve it. */\n params: { sessionId: string, text: string, activatedSkills?: string[], attachments?: PromptAttachment[], asCommand?: string }\n result: { delivered: 'queued' | 'sent' }\n }\n 'queue.remove': { params: { sessionId: string, id: string }, result: boolean }\n /** Edit a queued entry in place; order is kept. `false` when already sent / dropped. */\n 'queue.update': { params: { sessionId: string, id: string, text: string }, result: boolean }\n /** Steer a queued entry into the live run now (falls back to send when idle). */\n 'queue.pushNow': { params: { sessionId: string, id: string }, result: { delivered: 'steer' | 'send' | 'missing' } }\n\n /** Current shared draft for a session — backfill on window attach. */\n 'drafts.get': { params: { sessionId: string }, result: { text: string, shell?: boolean, attachments?: PromptAttachment[], search?: { active: boolean, query: string } } }\n /**\n * Replace the session's shared draft. Fans out `draft:changed` to every\n * OTHER peer. Ephemeral daemon state — drafts don't survive a daemon\n * restart and are cleared when any window submits the prompt.\n */\n /**\n * `shell` carries the prompt's shell mode OUT OF BAND — never encoded\n * into the text, so a literal leading `!` in a normal draft can't flip\n * other windows into shell-execution semantics.\n */\n 'drafts.set': { params: { sessionId: string, text: string, shell?: boolean }, result: void }\n /** Replace the shared draft's composing attachments (notification). */\n 'drafts.setAttachments': { params: { sessionId: string, attachments: PromptAttachment[] }, result: void }\n /** Replace the shared transcript-search state (notification, per keystroke). */\n 'search.set': { params: { sessionId: string, active: boolean, query: string }, result: void }\n /**\n * Extension bus emit from a wire client whose extensions run CLIENT-side\n * (TUI daemon mode — the extension has no host to broadcast through).\n * Sent as a notification. The daemon validates the extension id against\n * the session cwd's registry (boot registry when no `sessionId`) and the\n * `'ipc'` permission, re-validates the 64 KiB payload cap, delivers to\n * its own subscribers, and fans out `extension:signal` to every OTHER\n * peer (origin excluded — the emitter already delivered locally).\n */\n 'extension.emit': { params: { extensionId: string, sessionId?: string, payload: JsonValue, seq: number, nonce?: string }, result: void }\n /**\n * Resolve inline ghost-text autocomplete daemon-side (the typing\n * window's request IS the single computation): canonical resolver\n * against the session cwd's extension registry. Latest-wins per\n * session — a `seq` older than the newest seen returns\n * `{ suggestion: null }` without computing, and a newer request\n * aborts an in-flight compute. The result additionally fans out as\n * `suggest:changed` to every OTHER peer.\n */\n 'suggest.resolve': { params: { sessionId: string, text: string, caret: number, seq: number }, result: { suggestion: string | null, seq: number, candidates?: string[] } }\n\n 'settings.get': { params: Record<string, never>, result: HostSettings }\n 'settings.set': { params: { patch: Partial<HostSettings> }, result: HostSettings }\n\n /** Current presence snapshot — backfill for a freshly opened session list. */\n 'presence.list': { params: Record<string, never>, result: SessionPresence[] }\n /**\n * Tag the calling peer as watching a session (one per connection; the\n * TUI opens one connection per activated session). Sent as a\n * notification right after the handshake. Detach is implicit — the\n * daemon recomputes presence when the socket drops.\n */\n 'presence.watch': { params: { sessionId: string }, result: void }\n\n 'daemon.info': { params: Record<string, never>, result: HelloResult }\n /**\n * Graceful drain: deny pending approvals, abort live runs, close the\n * socket. Used by clients that detect a protocol-version mismatch and\n * need the stale daemon gone before respawning.\n */\n 'daemon.shutdown': { params: Record<string, never>, result: void }\n}\n\nexport type IpcMethodName = keyof IpcMethods\nexport type IpcParams<M extends IpcMethodName> = IpcMethods[M]['params']\nexport type IpcResult<M extends IpcMethodName> = IpcMethods[M]['result']\n\n/** One row in a cross-window session list. */\n/** One ordered model-fallback target (primary excluded). */\nexport interface ModelFallbackRef {\n providerKey: ProviderKey\n modelId: string\n}\n\nexport interface SessionRow {\n id: string\n title: string\n projectRoot: string | null\n cwd: string\n agentId?: string\n status: 'idle' | 'running' | 'completed' | 'error'\n createdAt: number\n updatedAt: number\n archived: boolean\n /** Orchestration main session — see `SessionMeta.isMain`. Additive. */\n isMain?: boolean\n providerKey?: ProviderKey\n modelId?: string\n effort?: ThinkingLevel\n modelOptions?: Record<string, boolean>\n /** Validated (head-matched) fallback chain tail, for pickers/badges. */\n modelFallbacks?: ModelFallbackRef[]\n}\n\nexport interface CreateSessionParams {\n /** Tool-execution directory — `projectRoot` is derived from it host-side. */\n cwd: string\n title?: string\n agentId?: string\n providerKey?: ProviderKey\n modelId?: string\n /** Seed an ordered fallback chain alongside the model pick. */\n fallbacks?: ModelFallbackRef[]\n /** Mark as an extension-authoring session (auto-activates the docs skill). */\n extensionEdit?: boolean\n}\n\n/* -------------------------------------------------------------------------\n * Daemon handshake\n * ---------------------------------------------------------------------- */\n\nexport interface HelloParams {\n protocolVersion: number\n /** Client's zidane version — surfaced in daemon diagnostics. */\n appVersion: string\n pid: number\n /**\n * Build fingerprint (module mtime; `'unknown'` in compiled binaries).\n * When BOTH sides report a real id and they differ, the daemon rejects\n * the handshake like a protocol-version mismatch — the client drains it\n * and respawns. Closes same-version code drift during development.\n */\n buildId?: string\n /**\n * Version of the zidane code the client runs (`ZIDANE_VERSION`, inlined at\n * build). Unlike `buildId` this survives compiled binaries, so it closes\n * the gap released builds had: same protocol version, older code. A\n * client STRICTLY NEWER than the daemon is rejected like a build mismatch\n * and the stale daemon is drained; an older client is accepted and\n * degrades via `optional()`.\n */\n codeVersion?: string\n}\n\nexport interface HelloResult {\n protocolVersion: number\n appVersion: string\n pid: number\n startedAt: number\n buildId?: string\n codeVersion?: string\n}\n","/**\n * Typed daemon client — the only thing a zidane window (TUI, attach CLI,\n * future GUI-proxy mode) needs to talk to a shared chat host.\n *\n * `request` is checked against the `IpcMethods` contract, `onSignal`\n * delivers every `HostSignal` fan-out, and `connectIpcClient` handles the\n * handshake including the protocol-version dance: a mismatched daemon is\n * asked to drain (`daemon.shutdown`) so the caller can respawn a fresh one.\n */\nimport type { Socket } from 'node:net'\nimport type { EnsureDaemonOptions } from './daemon/lifecycle'\nimport type { HelloResult, HostSignal, IpcMethodName, IpcParams, IpcResult } from './protocol'\nimport { connect } from 'node:net'\nimport { createJsonRpcConnection, errorResponse, JsonRpcRemoteError } from '../acp/json-rpc'\nimport { compareVersions, ZIDANE_VERSION } from '../version'\nimport { ensureDaemonRunning, IPC_BUILD_ID, probeSocket } from './daemon/lifecycle'\nimport { IPC_PROTOCOL_VERSION, IpcErrorCode } from './protocol'\n\nfunction daemonRelease(hello: HelloResult): string | undefined {\n for (const value of [hello.codeVersion, hello.appVersion]) {\n if (typeof value !== 'string')\n continue\n const match = /^(?:zidane-gui\\/)?(v?\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?)$/.exec(value.trim())\n if (match)\n return match[1]\n }\n return undefined\n}\n\nexport class IpcVersionMismatchError extends Error {\n constructor(message: string, readonly daemon?: HelloResult) {\n super(message)\n this.name = 'IpcVersionMismatchError'\n }\n}\n\nexport interface IpcClientOptions {\n socketPath: string\n /** Client zidane version, surfaced in daemon diagnostics. */\n appVersion?: string\n /** Called when the daemon connection drops (socket close/error). */\n onClose?: () => void\n /**\n * On a version/build mismatch, ask the stale daemon to drain before\n * throwing (default). PASSIVE clients (the presence watcher) must set\n * this false: they retry on a timer, and an old-build window left open\n * across a protocol bump would otherwise drain the NEW daemon in a\n * loop, aborting every other window's runs.\n */\n drainOnMismatch?: boolean\n}\n\nexport interface IpcClient {\n hello: HelloResult\n request: <M extends IpcMethodName>(method: M, params: IpcParams<M>) => Promise<IpcResult<M>>\n /**\n * Fire-and-forget notification — same method table as `request`, but no\n * id, no response, no pending-promise bookkeeping. The hot path for\n * high-frequency writes (per-keystroke draft sync). Daemons that don't\n * know the method ignore it silently.\n */\n notify: <M extends IpcMethodName>(method: M, params: IpcParams<M>) => void\n onSignal: (cb: (signal: HostSignal) => void) => () => void\n close: () => void\n}\n\nexport async function connectIpcClient(options: IpcClientOptions): Promise<IpcClient> {\n const socket: Socket = await new Promise((resolvePromise, reject) => {\n const s = connect(options.socketPath)\n s.once('connect', () => {\n s.off('error', reject)\n resolvePromise(s)\n })\n s.once('error', reject)\n })\n\n const listeners = new Set<(signal: HostSignal) => void>()\n let closed = false\n\n const connection = createJsonRpcConnection({\n input: socket,\n output: socket,\n // The daemon never sends requests to clients — only notifications.\n onRequest: message => errorResponse(message.id, -32601, 'Clients do not accept requests.'),\n onNotification: (message) => {\n if (message.method !== 'ipc.signal')\n return\n const signal = (message as { params?: HostSignal }).params\n if (!signal)\n return\n for (const cb of listeners)\n cb(signal)\n },\n })\n connection.start()\n\n const close = (): void => {\n if (closed)\n return\n closed = true\n connection.close()\n socket.destroy()\n }\n\n socket.on('close', () => {\n if (closed)\n return\n closed = true\n connection.close()\n options.onClose?.()\n })\n socket.on('error', () => {\n // 'close' follows; swallow so an unhandled 'error' doesn't crash us.\n })\n\n /*\n * Bound the handshake: a wedged daemon (accepting connections but never\n * reading — e.g. SIGSTOP'd) would otherwise hang this promise forever.\n * On timeout the socket is destroyed and connectIpcClient rejects.\n * `Promise.race` keeps the losing request promise handled, so the\n * connection-closed rejection that follows `close()` can't surface as an\n * unhandled rejection.\n */\n const HELLO_TIMEOUT_MS = 5000\n const REQUEST_TIMEOUT_MS = 30_000\n const LONG_REQUEST_TIMEOUT_MS = 10 * 60 * 1000\n /** RPCs that legitimately run long — full compaction, index-backed cross-session search. */\n const LONG_REQUEST_METHODS = new Set<string>(['sessions.compact', 'history.searchAll', 'history.searchSession'])\n let helloTimer: ReturnType<typeof setTimeout> | undefined\n let hello: HelloResult\n try {\n hello = (await Promise.race([\n connection.sendRequest('ipc.hello', {\n protocolVersion: IPC_PROTOCOL_VERSION,\n appVersion: options.appVersion ?? 'dev',\n pid: process.pid,\n // Build fingerprint: a daemon started from an OLDER build of this\n // same protocol version rejects us, gets drained, and respawns —\n // dev daemons can no longer run stale code silently.\n //\n // PASSIVE clients (drainOnMismatch: false — the GUI guest, the TUI\n // presence watcher) opt OUT of the build fence by sending the\n // 'unknown' sentinel: they cannot act on a build mismatch (no drain,\n // no respawn), so fencing them would just lock them out of the\n // fabric until someone else retires the daemon. They stay fenced by\n // protocol version and degrade via `optional()` like any client of\n // an older daemon.\n buildId: options.drainOnMismatch === false ? 'unknown' : IPC_BUILD_ID,\n // Always sent, passive clients included: a stale-RELEASE daemon is\n // safe for anyone to retire (see the drain rule below).\n codeVersion: ZIDANE_VERSION,\n }),\n new Promise<never>((_, reject) => {\n helloTimer = setTimeout(\n () => reject(new Error(`Daemon handshake timed out after ${HELLO_TIMEOUT_MS}ms on ${options.socketPath}.`)),\n HELLO_TIMEOUT_MS,\n )\n }),\n ])) as HelloResult\n }\n catch (err) {\n if (err instanceof JsonRpcRemoteError && err.code === IpcErrorCode.VersionMismatch) {\n /*\n * Ask the stale daemon to drain so the caller can respawn a matching\n * one. AWAIT the reply (bounded) before tearing the socket down —\n * `socket.destroy()` discards unflushed writes, so a fire-and-forget\n * `daemon.shutdown` could be dropped, leaving the stale daemon alive\n * and defeating `connectOrSpawnDaemon`'s retry.\n */\n const daemon = (err.data as { daemon?: HelloResult } | undefined)?.daemon\n /*\n * Passive clients (`drainOnMismatch: false`) never retire a daemon on\n * a protocol / build mismatch — it may be NEWER than them. A daemon\n * that reports an OLDER release than this client is the one case\n * where draining is always safe: nothing newer can be running on it,\n * and whoever attaches next spawns a current one. Without this the GUI\n * (which cannot spawn daemons) would sit in-process forever behind a\n * stale daemon nobody restarts.\n */\n const daemonIsOlderRelease\n = typeof daemon?.codeVersion === 'string' && compareVersions(daemon.codeVersion, ZIDANE_VERSION) < 0\n const release = daemon && daemonRelease(daemon)\n const daemonIsNewerRelease = release !== undefined && compareVersions(release, ZIDANE_VERSION) > 0\n if (!daemonIsNewerRelease && (options.drainOnMismatch !== false || daemonIsOlderRelease)) {\n await Promise.race([\n connection.sendRequest('daemon.shutdown', {}).catch(() => undefined),\n new Promise(resolve => setTimeout(resolve, 1500)),\n ])\n }\n close()\n throw new IpcVersionMismatchError(err.message, daemon)\n }\n close()\n throw err\n }\n finally {\n clearTimeout(helloTimer)\n }\n\n const release = daemonRelease(hello)\n if (release !== undefined && compareVersions(release, ZIDANE_VERSION) < 0) {\n // Pre-fence daemons accept hello without checking codeVersion. Disconnect\n // only: shutting down a successfully attached daemon can abort active runs.\n close()\n throw new IpcVersionMismatchError(\n `Daemon release ${release} is older than client ${ZIDANE_VERSION}. Restart the daemon when active runs have finished.`,\n hello,\n )\n }\n\n return {\n hello,\n notify: (method, params) => {\n try {\n connection.sendNotification(method, params)\n }\n catch {\n // fire-and-forget by contract — a dying socket surfaces via onClose\n }\n },\n request: async (method, params) => {\n /*\n * Per-request deadline: socket-close rejection covers a DEAD\n * daemon, but a wedged one (alive, not reading — the same class\n * the server-side backpressure fix handles for peers) would hang\n * callers forever. On timeout the socket is destroyed (NOT\n * `close()` — the `onClose` path must fire so reconnect logic\n * takes over) and every pending request rejects via the close\n * sweep. Known long-running RPCs get a generous budget; tearing\n * the connection down mid-`sessions.compact` would abort real\n * work, so those bounds are deliberately loose.\n */\n const timeoutMs = LONG_REQUEST_METHODS.has(method) ? LONG_REQUEST_TIMEOUT_MS : REQUEST_TIMEOUT_MS\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return (await Promise.race([\n connection.sendRequest(method, params),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Daemon request ${method} timed out after ${timeoutMs}ms — tearing the connection down.`))\n socket.destroy()\n }, timeoutMs)\n timer.unref?.()\n }),\n ])) as IpcResult<typeof method>\n }\n finally {\n clearTimeout(timer)\n }\n },\n onSignal: (cb) => {\n listeners.add(cb)\n return () => listeners.delete(cb)\n },\n close,\n }\n}\n\n/**\n * One-call convenience for windows: make sure a daemon is up (spawning if\n * needed), connect, and — when the running daemon speaks an older protocol\n * — drain it and spawn a fresh one before retrying once.\n */\nexport async function connectOrSpawnDaemon(\n options: Omit<IpcClientOptions, 'socketPath'> & EnsureDaemonOptions = {},\n): Promise<IpcClient> {\n const socketPath = await ensureDaemonRunning(options)\n try {\n return await connectIpcClient({ ...options, socketPath })\n }\n catch (err) {\n if (!(err instanceof IpcVersionMismatchError))\n throw err\n /*\n * The stale daemon was told to drain by `connectIpcClient`, but\n * `daemon.shutdown` replies BEFORE the socket actually closes (the\n * drain runs on `setImmediate`). Poll until the socket stops\n * accepting connections (bounded) before respawning, so\n * `ensureDaemonRunning` doesn't re-probe the still-listening stale\n * daemon and hit a second mismatch.\n */\n const drainDeadline = Date.now() + 2000\n while (await probeSocket(socketPath)) {\n if (Date.now() > drainDeadline)\n break\n await new Promise(r => setTimeout(r, 100))\n }\n const retryPath = await ensureDaemonRunning(options)\n return connectIpcClient({ ...options, socketPath: retryPath })\n }\n}\n","import type { ChatHostEnvironment, PendingApprovalsStore } from '../../chat/host/environment'\n/**\n * Disk-backed `ChatHostEnvironment` for the zidane daemon.\n *\n * Mirrors the TUI's storage layout so the daemon is a drop-in peer of the\n * terminal process: sessions in `sessions.db` (canonical sqlite store),\n * settings in `state.json`'s `settings` slot, credentials/safelist under the\n * shared user dir, extensions discovered per-cwd with the same allowlist +\n * trust contract every other surface uses.\n */\nimport type { ResolvedConfig } from '../../chat/index'\nimport type { ExtensionBus } from '../../extensions/index'\nimport type { SessionStore } from '../../session/index'\nimport type { HostSettings, HostSignal, WireApprovalRequest } from '../protocol'\nimport { existsSync, mkdirSync, readFileSync, unlinkSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { writeFileAtomic } from '../../atomic-write'\nimport { applyApiKeyEnv, BUILTIN_PROVIDERS, createFileMcpCredentialStore, credentialsPath, DEFAULT_SETTINGS, enforceSettingsInvariants, resolveConfig, SETTINGS_TOGGLES } from '../../chat/index'\nimport { createExtensionBus, createExtensionHostRuntimeBridge, createExtensionHud, discoverAndBuildExtensionRegistry } from '../../extensions/index'\n\nexport interface DaemonEnvironment extends ChatHostEnvironment {\n writeSettings: (patch: Partial<HostSettings>) => HostSettings\n /** Drop the per-cwd runtime-config memo (settings changed). */\n invalidateRuntime: () => void\n /** Base config resolved at daemon boot (paths, store, state). */\n config: ResolvedConfig\n /**\n * Shared extension bus for daemon-side registries (`ctx.bus`). The\n * server delivers validated `extension.emit` notifications into it;\n * daemon-side emits leave through its `onEmit` → `broadcast`. Optional:\n * test environments without extensions omit it.\n */\n extensionBus?: ExtensionBus\n}\n\n/**\n * File-backed pending-approvals persistence (`pending-approvals.json` next\n * to `state.json`). Small, rewritten atomically enough for its purpose —\n * the queue holds at most a handful of rows.\n */\nexport function createFileApprovalsStore(path: string): PendingApprovalsStore {\n function read(): WireApprovalRequest[] {\n if (!existsSync(path))\n return []\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8'))\n return Array.isArray(parsed) ? (parsed as WireApprovalRequest[]) : []\n }\n catch {\n return []\n }\n }\n\n function write(rows: WireApprovalRequest[]): void {\n if (rows.length === 0) {\n try {\n unlinkSync(path)\n }\n catch {\n // absent already — fine\n }\n return\n }\n mkdirSync(dirname(path), { recursive: true })\n // Atomic: this file exists to survive a crash — a torn write would\n // corrupt the very queue we're persisting to recover.\n writeFileAtomic(path, JSON.stringify(rows, null, 2))\n }\n\n return {\n insert(request) {\n const rows = read().filter(r => r.id !== request.id)\n rows.push(request)\n write(rows)\n },\n delete(id) {\n write(read().filter(r => r.id !== id))\n },\n clear() {\n write([])\n },\n list() {\n return read()\n },\n }\n}\n\nexport async function createDaemonEnvironment(options: {\n broadcast: (signal: HostSignal) => void\n /** Base cwd for boot-time config resolution. Default: `process.cwd()`. */\n cwd?: string\n debugStream?: boolean\n}): Promise<DaemonEnvironment> {\n /*\n * `bun:sqlite` is loaded LAZILY so `zidane/ipc` stays importable under\n * plain Node (the Electron GUI main process imports this barrel for\n * `createChatHost`; the sqlite chunk must never be evaluated there).\n * Same invariant the tsdown config maintains for `zidane/session/sqlite`.\n */\n const { createTuiStore } = await import('../../session/sqlite')\n\n /*\n * Boot-time config: paths + store + state. Session-facing runtime config\n * is re-resolved per cwd below (project extensions differ per folder).\n */\n const config = resolveConfig({\n ...(options.cwd !== undefined ? { cwd: options.cwd } : {}),\n store: resolved => createTuiStore(resolved.db),\n })\n\n const store: SessionStore = config.store\n\n /*\n * Project-DB mode resolves storage to a PER-REPO `.zidane/sessions.db`\n * from the boot cwd. A single daemon serving windows opened in different\n * projects would then read/write every session against ONE project's DB.\n * The daemon is a single-store process, so warn loudly rather than\n * silently mixing projects; a project-scoped daemon (own socket) is the\n * supported way to use project-DB mode with the daemon.\n */\n if (config.paths.projectDir) {\n process.stderr.write(\n `[zidane-daemon] warning: project-DB mode is active — this daemon is bound to ${config.paths.db}. `\n + `Windows opened in other projects will share this project's sessions. `\n + `Run a per-project daemon (ZIDANE_DAEMON_SOCKET) to isolate them.\\n`,\n )\n }\n\n /*\n * `config.initialSettings` is `resolveConfig`'s migrated snapshot of the\n * on-disk settings (it ran `migrateLegacySettings`, which is private to\n * `config.ts`). Layering it under the live raw read means a user with\n * legacy-shaped settings gets the SAME effective settings in the daemon\n * as in the TUI, while mid-session writes (live `stateStore`) still win.\n */\n const readSettings = (): HostSettings => ({\n ...DEFAULT_SETTINGS,\n ...config.initialSettings,\n ...(config.stateStore.load().settings ?? {}),\n })\n\n /**\n * Fields the invariant forced (only ever `false`), so the stored file records\n * them too — otherwise a later read of `state.settings` would resurrect the\n * value the invariant just rejected.\n */\n const pickInvariantFields = (merged: HostSettings, patch: Partial<HostSettings>): Partial<HostSettings> => {\n const out: Partial<HostSettings> = {}\n for (const row of SETTINGS_TOGGLES) {\n if (row.requires && merged[row.key] === false && patch[row.key] !== false)\n out[row.key] = false\n }\n return out\n }\n\n const writeSettings = (patch: Partial<HostSettings>): HostSettings => {\n /*\n * Read-modify-write of the whole `state.json`. An in-process TUI\n * pointed at the same file also rewrites it (last-session, UI state),\n * and neither writer locks — a concurrent write can drop the other's\n * fields. Acceptable while the TUI runs its own in-process host; must\n * become field-scoped merge or file locking before the TUI migrates\n * onto the daemon (tracked in docs/IPC.md).\n */\n const state = config.stateStore.load()\n // Same invariants every writer applies (`orchestration` needs `useDaemon`,\n // …) — enforced on the MERGED view, since a patch can flip a requirement\n // off while the dependent stays true in the stored fields.\n const merged = enforceSettingsInvariants({ ...DEFAULT_SETTINGS, ...config.initialSettings, ...(state.settings ?? {}), ...patch })\n const next = { ...(state.settings ?? {}), ...patch, ...pickInvariantFields(merged, patch) }\n config.stateStore.save({ ...state, settings: next })\n return merged\n }\n\n /*\n * Per-(cwd + allowlist + trust) runtime-config memo — same invalidation\n * axes the GUI's extension registry cache uses. Dropped wholesale on\n * settings writes that touch extension keys (see the server's\n * `settings.set` handler calling `invalidate`).\n */\n /*\n * Daemon-side extension HUD: extension hooks run HERE in daemon mode, so\n * their `ctx.tui` writes need a live bus (headless hosts otherwise no-op\n * them). Every change is forwarded to the windows as `hud:state` /\n * `hud:notify` signals; the TUI overlays them under its local HUD.\n */\n const hud = createExtensionHud()\n hud.subscribe(() => {\n const state = hud.getState()\n options.broadcast({\n kind: 'hud:state',\n statuses: Object.fromEntries(\n Object.entries(state.statuses).map(([name, s]) => [name, { text: s.text, loader: s.loader }]),\n ),\n banners: Object.fromEntries(\n Object.entries(state.banners).map(([name, lines]) => [name, [...lines]]),\n ),\n })\n })\n hud.onNotify((n) => {\n options.broadcast({ kind: 'hud:notify', extension: n.extension, text: n.text, tone: n.tone })\n })\n\n /*\n * Daemon-side extension bus: one bus shared by every per-cwd registry\n * (channels are private per extension id, so sharing is safe — and it\n * lets the server deliver a validated `extension.emit` no matter which\n * cwd's registry claimed the id). Emits from daemon-side extensions\n * already delivered loopback inside the bus; the hook fans the envelope\n * out to every window as `extension:signal`.\n */\n const extensionBus = createExtensionBus()\n extensionBus.onEmit((envelope) => {\n options.broadcast({ kind: 'extension:signal', ...envelope })\n })\n\n /*\n * LRU-bounded: a service-style daemon (`--idle-exit 0`) serving many\n * project folders would otherwise accumulate a full ResolvedConfig +\n * extension registry per (cwd × allowlist) key forever — this was the\n * last unbounded collection in the daemon. Map iteration order is\n * insertion order; a cache hit re-inserts to mark recency.\n */\n const RUNTIME_CACHE_MAX = 16\n const runtimeCache = new Map<string, Promise<ResolvedConfig>>()\n\n // One bridge for every registry this daemon builds; the engine scopes the\n // invoking session onto it per command (see `ChatHostEnvironment.hostRuntime`).\n const hostRuntime = createExtensionHostRuntimeBridge()\n\n /**\n * Best-effort release of an evicted entry's extension registry\n * (teardown hooks, HUD writers — `registry.dispose()` is idempotent).\n * Fire-and-forget: eviction must never block or throw on a broken\n * extension teardown, and a rejected resolve has nothing to release.\n */\n const disposeRuntime = (promise: Promise<ResolvedConfig>): void => {\n void promise\n .then(config => config.extensions.dispose())\n .catch(() => undefined)\n }\n\n const runtimeConfig = (cwd: string): Promise<ResolvedConfig> => {\n const settings = readSettings()\n const key = [\n cwd,\n JSON.stringify(settings.enabledExtensions ?? null),\n String(settings.loadProjectExtensions === true),\n ].join('\\u0000')\n\n const existing = runtimeCache.get(key)\n if (existing) {\n // Refresh recency.\n runtimeCache.delete(key)\n runtimeCache.set(key, existing)\n return existing\n }\n\n const promise = (async () => {\n const extensionRegistry = await discoverAndBuildExtensionRegistry({\n cwd,\n hud,\n bus: extensionBus,\n hostRuntime,\n prefix: config.prefix,\n ...(settings.enabledExtensions !== undefined ? { enabledAllowlist: [...settings.enabledExtensions] } : {}),\n trustProjectExtensions: settings.loadProjectExtensions === true,\n readSettings,\n })\n return resolveConfig({\n cwd,\n extensionRegistry,\n store: () => store,\n })\n })()\n\n runtimeCache.set(key, promise)\n while (runtimeCache.size > RUNTIME_CACHE_MAX) {\n const oldest = runtimeCache.keys().next().value\n if (oldest === undefined)\n break\n const evicted = runtimeCache.get(oldest)\n runtimeCache.delete(oldest)\n if (evicted)\n disposeRuntime(evicted)\n }\n promise.catch(() => {\n if (runtimeCache.get(key) === promise)\n runtimeCache.delete(key)\n })\n return promise\n }\n\n const userDataDir = (): string => config.paths.userDir\n\n const applyAuthEnv = (): void => {\n const dir = userDataDir()\n process.env.ZIDANE_CREDENTIALS_PATH = credentialsPath(dir)\n applyApiKeyEnv(dir, BUILTIN_PROVIDERS)\n }\n\n return {\n // The daemon serves explicitly-invoked clients (`zidane attach`)\n // even when `useDaemon` is off — a multi-writer surface by\n // construction, so the run guard + heartbeat must not be\n // setting-gated here (they are in the GUI's single-writer embedding).\n multiProcess: true,\n broadcast: options.broadcast,\n store,\n readSettings,\n writeSettings,\n runtimeConfig,\n hostRuntime,\n userDataDir,\n applyAuthEnv,\n mcpCredentialStore: () => createFileMcpCredentialStore(userDataDir()),\n approvalsPersistence: createFileApprovalsStore(join(dirname(config.paths.state), 'pending-approvals.json')),\n ...(options.debugStream !== undefined ? { debugStream: options.debugStream } : {}),\n invalidateRuntime: () => {\n for (const entry of runtimeCache.values())\n disposeRuntime(entry)\n runtimeCache.clear()\n },\n config,\n extensionBus,\n }\n}\n","/**\n * Unix-socket JSON-RPC server around a `ChatHost`.\n *\n * One `net.Server`, one JSON-RPC connection per peer (NDJSON framing — a\n * `net.Socket` is both the Readable and the Writable). Every `HostSignal`\n * fans out to every handshaken peer as an `ipc.signal` notification, which\n * is what keeps multiple terminal/GUI windows converged on the same state.\n *\n * Handshake: a peer's first request MUST be `ipc.hello` carrying its\n * protocol version. Mismatched peers get a structured error (code\n * `VERSION_MISMATCH`) and can ask the daemon to drain via `daemon.shutdown`\n * before respawning a fresh one.\n */\nimport type { Server, Socket } from 'node:net'\nimport type { JsonRpcConnection } from '../../acp/json-rpc'\nimport type { JsonRpcRequest, JsonRpcResponse } from '../../acp/types'\nimport type { ChatHost } from '../../chat/host/engine'\nimport type { SessionHistoryMatch, SessionSearchIndex } from '../../chat/index'\nimport type { SessionData } from '../../session/index'\nimport type { SessionTurn } from '../../types'\nimport type { CreateSessionParams, HelloParams, HelloResult, HostSignal, IpcMethodName, IpcParams, IpcResult, PromptAttachment, SendOptions, SessionPresence, SessionRow, TodosSnapshot } from '../protocol'\nimport type { DaemonEnvironment } from './environment'\nimport { chmodSync, existsSync, mkdirSync, unlinkSync } from 'node:fs'\nimport { createServer } from 'node:net'\nimport { basename, dirname } from 'node:path'\nimport { createJsonRpcConnection, errorResponse, successResponse } from '../../acp/json-rpc'\nimport { createChatHost, STALE_RUN_MARKER_MS } from '../../chat/host/engine'\nimport { toTodosSnapshot } from '../../chat/host/signals'\nimport { buildSessionSearchIndexAsync, createPathAutocompleteProvider, deriveSessionTitle, eventsFromTurns, resolveInlineSuggestion, searchSessionHistoryIndexAsync, searchSessionIndexAsync, sessionModelFallbacks } from '../../chat/index'\nimport { isMainSession } from '../../chat/orchestration'\nimport { EXTENSION_BUS_MAX_PAYLOAD_BYTES, serializedPayloadBytes } from '../../extensions/index'\nimport { normalizeArtifactInput } from '../../session/artifacts'\nimport { compareVersions, ZIDANE_VERSION } from '../../version'\nimport { connectIpcClient } from '../client'\nimport { IPC_PROTOCOL_VERSION, IpcErrorCode } from '../protocol'\nimport { createDaemonEnvironment } from './environment'\nimport { IPC_BUILD_ID, legacySocketPath, probeSocket } from './lifecycle'\n\n// Canonical home is the protocol module (clients import the codes without\n// pulling the server's module graph); re-exported here for compatibility.\nexport { IpcErrorCode } from '../protocol'\n\n/**\n * Per-peer write backpressure bound. A wedged peer (e.g. a SIGSTOP'd\n * terminal) stops reading its socket, so fan-out writes pile up in\n * `socket.writableLength` without bound and can OOM the daemon. A peer\n * buffering more than this is destroyed — the protocol supports re-attach\n * + backfill, so killing a wedged peer is safe and self-healing.\n */\nconst PEER_WRITE_HIGH_WATERMARK = 8 * 1024 * 1024\n\nexport interface IpcDaemonOptions {\n socketPath: string\n /**\n * Environment factory — the production daemon uses the disk-backed\n * `createDaemonEnvironment`; tests inject an in-memory one. Receives the\n * broadcast sink wired to peer fan-out.\n */\n createEnvironment?: (broadcast: (signal: HostSignal) => void) => DaemonEnvironment | Promise<DaemonEnvironment>\n appVersion?: string\n /**\n * Called when the daemon has had zero peers for `idleShutdownMs` and no\n * session is running. The CLI exits the process; embedded servers ignore.\n */\n idleShutdownMs?: number\n onIdle?: () => void\n onLog?: (line: string) => void\n}\n\nexport interface IpcDaemon {\n socketPath: string\n host: ChatHost\n peerCount: () => number\n close: () => Promise<void>\n}\n\ninterface Peer {\n socket: Socket\n connection: JsonRpcConnection\n ready: boolean\n /** Session this peer's window is attached to (`presence.watch`). */\n watching: string | null\n}\n\nexport async function startIpcDaemon(options: IpcDaemonOptions): Promise<IpcDaemon> {\n const { socketPath } = options\n const log = options.onLog ?? (() => undefined)\n const startedAt = Date.now()\n const appVersion = options.appVersion ?? 'dev'\n\n const peers = new Set<Peer>()\n\n /** Sessions with a run in flight, tracked from the run signals themselves. */\n const runningSessions = new Set<string>()\n\n const broadcast = (signal: HostSignal): void => {\n broadcastExcept(null, signal)\n /*\n * Presence piggybacks on the signals that change it — run state and\n * HITL queues — so the snapshot stays fresh without instrumenting the\n * host. `presence:changed` itself is emitted via `broadcastExcept`\n * directly, so this cannot recurse.\n */\n switch (signal.kind) {\n case 'run:start':\n // Signal-tracked rather than read off `agent.isRunning`: the host\n // broadcasts `run:start` BEFORE invoking `agent.run()`, so the\n // flag lags the signal by a tick.\n runningSessions.add(signal.sessionId)\n broadcastPresence()\n break\n case 'run:end':\n runningSessions.delete(signal.sessionId)\n broadcastPresence()\n break\n case 'approval:request':\n case 'approval:resolved':\n case 'interaction:request':\n case 'interaction:resolved':\n broadcastPresence()\n break\n default:\n break\n }\n }\n\n /**\n * Fan out to every handshaken peer except `origin`. Draft signals use\n * this so a window's own keystrokes never bounce back and fight its\n * cursor — echo-safety by construction, no client-side dedupe.\n */\n function broadcastExcept(origin: Peer | null, signal: HostSignal): void {\n for (const peer of peers) {\n if (!peer.ready || peer === origin)\n continue\n if (peer.socket.writableLength > PEER_WRITE_HIGH_WATERMARK) {\n console.warn(`[ipc-daemon] peer buffered ${peer.socket.writableLength} bytes (> ${PEER_WRITE_HIGH_WATERMARK}) — destroying wedged connection`)\n peer.socket.destroy()\n continue\n }\n try {\n peer.connection.sendNotification('ipc.signal', signal)\n }\n catch {\n // A dying socket must never take the host down with it.\n }\n }\n }\n\n /**\n * Shared per-session prompt drafts. Ephemeral by design — in-memory\n * only, cleared when any window submits, gone on daemon restart.\n */\n const drafts = new Map<string, { text?: string, shell?: boolean, attachments?: PromptAttachment[], search?: { active: boolean, query: string } }>()\n\n /**\n * Bound a peer-supplied attachment list: shape-guarded entries only,\n * 24 MiB of base64 per list. Shared by `queue.add` and\n * `drafts.setAttachments`.\n */\n function boundAttachments(list: unknown[]): PromptAttachment[] {\n let budget = 24 * 1024 * 1024\n const out: PromptAttachment[] = []\n for (const entry of list) {\n const a = entry as Partial<PromptAttachment>\n if (typeof a?.mediaType !== 'string' || typeof a?.data !== 'string')\n continue\n budget -= a.data.length\n if (budget < 0)\n break\n // Names render in every window's UI — cap them so a hostile peer\n // can't pin megabytes of \"name\" per attachment.\n out.push({ mediaType: a.mediaType, data: a.data, ...(typeof a.name === 'string' ? { name: a.name.slice(0, 256) } : {}) })\n }\n return out\n }\n\n /** Total base64 bytes currently pinned by ALL draft attachments. */\n function draftAttachmentBytes(): number {\n let total = 0\n for (const entry of drafts.values()) {\n for (const attachment of entry.attachments ?? [])\n total += attachment.data.length\n }\n return total\n }\n\n /** Consume the draft after a submit — every window except the sender clears. */\n function clearDraft(sessionId: string, origin: Peer | null): void {\n const entry = drafts.get(sessionId)\n if (!entry)\n return\n drafts.delete(sessionId)\n if (entry.text)\n broadcastExcept(origin, { kind: 'draft:changed', sessionId, text: '' })\n if (entry.attachments?.length)\n broadcastExcept(origin, { kind: 'draft:attachments', sessionId, attachments: [] })\n }\n\n /**\n * Shared inline-autocomplete state — per-session latest-wins\n * coalescing for `suggest.resolve`. `seq` is the newest sequence seen\n * (stale requests return without computing); `controller` aborts an\n * in-flight compute when a newer request supersedes it (the GUI's\n * controller-map pattern, daemon-side). Bounded like `drafts`: keys\n * only evict on session delete, so cap the map at 256 sessions.\n */\n const suggests = new Map<string, { seq: number, controller: AbortController | null }>()\n\n /** Drop empty draft entries so the map only holds live composing state. */\n function compactDraft(sessionId: string, entry: { text?: string, shell?: boolean, attachments?: PromptAttachment[], search?: { active: boolean, query: string } }): void {\n if (!entry.text && !entry.shell && !entry.attachments?.length && !entry.search?.active)\n drafts.delete(sessionId)\n else\n drafts.set(sessionId, entry)\n }\n\n const env = await (options.createEnvironment\n ? options.createEnvironment(broadcast)\n : createDaemonEnvironment({ broadcast }))\n const host = createChatHost(env)\n\n /**\n * Live presence: which sessions have attached windows, and their\n * run / awaiting-input state. Union of watched, live-agent, and\n * pending-HITL sessions — small by construction.\n */\n function computePresence(): SessionPresence[] {\n const windowCounts = new Map<string, number>()\n for (const peer of peers) {\n if (peer.ready && peer.watching)\n windowCounts.set(peer.watching, (windowCounts.get(peer.watching) ?? 0) + 1)\n }\n\n const attention = new Set<string>()\n for (const request of host.approvals.listPending())\n attention.add(request.sessionId)\n for (const pending of host.interactions.list())\n attention.add(pending.sessionId)\n\n // Union of signal-tracked runs (fresh) and the agent flag (covers a\n // window attaching to a daemon whose run started before it connected).\n const live = new Map(host.listLiveSessions().map(entry => [entry.sessionId, entry.running]))\n\n const ids = new Set<string>([...windowCounts.keys(), ...attention, ...live.keys(), ...runningSessions])\n return [...ids].map(sessionId => ({\n sessionId,\n windows: windowCounts.get(sessionId) ?? 0,\n running: runningSessions.has(sessionId) || live.get(sessionId) === true,\n attention: attention.has(sessionId),\n }))\n }\n\n function broadcastPresence(): void {\n broadcastExcept(null, { kind: 'presence:changed', sessions: computePresence() })\n }\n\n /*\n * Daemon-side history search — one index cache shared by EVERY window,\n * so repeat queries (and other windows' queries) pay match cost only.\n * Off the UI thread by construction (this process has no renderer), so\n * no Worker indirection is needed. Live sessions index the host's\n * in-memory turns (fresh mid-run); idle sessions index store turns.\n * LRU-bounded — full-text indexes can reach 512k chars per turn.\n */\n const SEARCH_INDEX_CACHE_MAX = 64\n /** Latest `history.searchAll` generation per peer — see the handler. */\n const searchGenerations = new WeakMap<Peer, number>()\n const searchIndexCache = new Map<string, { key: string, index: SessionSearchIndex }>()\n\n async function sessionSearchIndex(sessionId: string): Promise<SessionSearchIndex | null> {\n const liveTurns = host.getLiveTurns(sessionId)\n let turns: readonly SessionTurn[]\n let key: string\n if (liveTurns) {\n /*\n * SNAPSHOT before the async index build — the agent may append\n * mid-build and the builder yields between turns. The cache key\n * folds in the last turn id so a same-count content change (e.g.\n * compaction replacing turns) can't serve a stale index.\n */\n turns = [...liveTurns]\n key = `live:${turns.length}:${turns.at(-1)?.id ?? ''}`\n }\n else {\n const data = await env.store.load(sessionId)\n if (!data)\n return null\n turns = data.turns\n key = `store:${data.updatedAt}:${data.turns.length}`\n }\n\n const cached = searchIndexCache.get(sessionId)\n if (cached?.key === key) {\n // Refresh LRU recency (Map preserves insertion order).\n searchIndexCache.delete(sessionId)\n searchIndexCache.set(sessionId, cached)\n return cached.index\n }\n\n const index = await buildSessionSearchIndexAsync(turns)\n searchIndexCache.set(sessionId, { key, index })\n while (searchIndexCache.size > SEARCH_INDEX_CACHE_MAX) {\n const oldest = searchIndexCache.keys().next().value\n if (oldest === undefined)\n break\n searchIndexCache.delete(oldest)\n }\n return index\n }\n host.approvals.rehydrate()\n\n const store = env.store\n\n /* ----------------------------------------------------------------------\n * Session helpers\n * -------------------------------------------------------------------- */\n\n /** Wire-supplied limit → sane positive integer capped at 1000, else `undefined` (no limit). */\n function boundedLimit(raw: unknown): number | undefined {\n return Number.isInteger(raw) && (raw as number) > 0 ? Math.min(raw as number, 1000) : undefined\n }\n\n function toRow(data: SessionData): SessionRow {\n const meta = data.metadata ?? {}\n // Canonical derivation — same helper as the TUI list (see the\n // listMeta fast path above).\n const title = deriveSessionTitle(data.turns, meta)\n\n return {\n id: data.id,\n title,\n ...(isMainSession(meta) ? { isMain: true } : {}),\n projectRoot: data.projectRoot ?? null,\n cwd: typeof meta.cwd === 'string' ? meta.cwd : (data.projectRoot ?? ''),\n ...(typeof data.agentId === 'string' ? { agentId: data.agentId } : {}),\n status: host.isSessionRunning(data.id)\n ? 'running'\n : data.status === 'running' && Date.now() - data.updatedAt >= STALE_RUN_MARKER_MS\n ? 'idle'\n : data.status,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n archived: meta.archived === true,\n ...(typeof meta.providerKey === 'string' ? { providerKey: meta.providerKey } : {}),\n ...(typeof meta.modelId === 'string' ? { modelId: meta.modelId } : {}),\n ...(typeof meta.effort === 'string' ? { effort: meta.effort as SessionRow['effort'] } : {}),\n ...(meta.modelOptions && typeof meta.modelOptions === 'object'\n ? { modelOptions: meta.modelOptions as Record<string, boolean> }\n : {}),\n ...(() => {\n const fallbacks = sessionModelFallbacks(meta)\n return fallbacks ? { modelFallbacks: fallbacks } : {}\n })(),\n }\n }\n\n async function loadOrThrow(sessionId: string): Promise<SessionData> {\n const data = await store.load(sessionId)\n if (!data)\n throw new Error(`Session \"${sessionId}\" not found.`)\n return data\n }\n\n /**\n * Apply a metadata-only patch. Prefers the host's live-session path (safe\n * mid-run — routes through the in-memory `Session` whose turns are the\n * source of truth); only falls back to a store-level full-document\n * `save()` when the session is idle, where no run is appending turns so\n * `load → mutate → save` can't drop concurrently-persisted turns.\n *\n * `bumpUpdatedAt: false` skips the recency bump so a rename doesn't yank\n * the row to the top of recency-sorted lists (mirrors the GUI).\n */\n async function patchSessionMeta(\n sessionId: string,\n patch: Record<string, unknown>,\n opts: { bumpUpdatedAt?: boolean } = {},\n ): Promise<void> {\n /*\n * Live path: route through the in-memory `Session` so the full-document\n * save() re-persists the CURRENT turns. `session.save()` bumps\n * `updatedAt` unconditionally, so the rare \"rename during a live run\"\n * case bumps recency — accepted, since avoiding turn loss matters more\n * than recency-order purity on that narrow window.\n */\n await host.patchSessionMeta(sessionId, patch, opts)\n broadcast({ kind: 'sessions:changed', op: 'update', sessionId })\n }\n\n async function patchSession(\n sessionId: string,\n mutate: (data: SessionData) => void,\n ): Promise<void> {\n const data = await loadOrThrow(sessionId)\n mutate(data)\n data.updatedAt = Date.now()\n await store.save(data)\n broadcast({ kind: 'sessions:changed', op: 'update', sessionId })\n }\n\n async function createSessionRow(params: CreateSessionParams): Promise<SessionRow> {\n // Creation lives in the host now (shared with the embedded GUI host and\n // orchestration); this adds only the wire row + the change broadcast.\n const id = await host.createSession(params)\n const data = await loadOrThrow(id)\n broadcast({ kind: 'sessions:changed', op: 'create', sessionId: id })\n return toRow(data)\n }\n\n /* ----------------------------------------------------------------------\n * Method dispatch\n * -------------------------------------------------------------------- */\n\n /**\n * Exhaustive, fully typed dispatch table: every `IpcMethodName` MUST\n * have a handler (compile error otherwise), and each handler's params\n * and result are pinned to the `IpcMethods` contract — the same types\n * the client proxy is checked against, so drift between the two sides\n * is a type error, not a runtime surprise. Params still arrive as JSON;\n * the trust boundary is the same as before, but the SHAPE contract is\n * now enforced on both ends.\n */\n type MethodHandlers = {\n [M in IpcMethodName]: (params: IpcParams<M>, peer: Peer) => IpcResult<M> | Promise<IpcResult<M>>\n }\n\n const methods: MethodHandlers = {\n 'chat.send': async (p, peer) => {\n // `wake` is host-internal: a wire peer must not run a session with\n // the `user-prompt` broadcast suppressed (params are unchecked JSON\n // — the Omit<> in the method type is not a runtime guarantee).\n const { wake: _stripped, ...params } = p as SendOptions\n const result = await host.sendPrompt(params)\n // The submit consumed the shared draft — clear it everywhere else\n // (the sender's input is already empty; excluding it means a user\n // already typing their NEXT message is never clobbered).\n clearDraft(p.sessionId, peer)\n return result\n },\n 'chat.interrupt': p => host.interrupt(p.sessionId),\n 'chat.steer': p => host.steer(p.sessionId, p.message),\n 'chat.followUp': p => host.followUp(p.sessionId, p.message),\n 'chat.pushQueued': async (p, peer) => {\n const result = await host.pushQueued(p.sessionId, p.message, p.activatedSkills)\n clearDraft(p.sessionId, peer)\n return result\n },\n\n 'commands.list': p => host.listCommands(p.cwd),\n 'commands.completeArgs': p => host.completeCommandArgs(p.cwd, p.name, p.argsPrefix),\n\n 'queue.list': p => host.queueList(p.sessionId),\n 'sessions.systemMd': p => host.sessionSystemMd(p.sessionId),\n 'queue.add': async (p, peer) => {\n // Same cheap shape guards as `drafts.set` — a malformed peer payload\n // must not propagate a non-string into every window's queue box.\n const text = typeof p.text === 'string' ? p.text.slice(0, 64 * 1024) : ''\n const activatedSkills = Array.isArray(p.activatedSkills)\n ? p.activatedSkills.filter((name): name is string => typeof name === 'string')\n : undefined\n const attachments = Array.isArray(p.attachments)\n ? boundAttachments(p.attachments)\n : undefined\n const asCommand = typeof p.asCommand === 'string' ? p.asCommand.slice(0, 128) : undefined\n const result = await host.queueAdd(p.sessionId, text, activatedSkills, attachments, asCommand)\n // Queuing/sending consumed the shared draft, same as a direct send.\n clearDraft(p.sessionId, peer)\n return result\n },\n\n 'sessions.contextSegments': p => host.getContextSegments(p.sessionId),\n 'sessions.createSystemOverride': p => host.createSystemOverride(p.sessionId),\n 'queue.remove': p => host.queueRemove(p.sessionId, p.id),\n 'queue.update': p => host.queueUpdate(p.sessionId, p.id, typeof p.text === 'string' ? p.text.slice(0, 64 * 1024) : ''),\n 'queue.pushNow': p => host.queuePushNow(p.sessionId, p.id),\n\n 'drafts.get': (p) => {\n const entry = drafts.get(p.sessionId)\n return {\n text: entry?.text ?? '',\n ...(entry?.shell ? { shell: true } : {}),\n ...(entry?.attachments?.length ? { attachments: entry.attachments } : {}),\n ...(entry?.search?.active ? { search: entry.search } : {}),\n }\n },\n 'drafts.set': (p, peer) => {\n // Bound the ephemeral draft store: a buggy/looping client must not\n // grow daemon memory without limit (drafts only evict on submit or\n // session delete). 64 KiB is far above any real prompt.\n const text = typeof p.text === 'string' ? p.text.slice(0, 64 * 1024) : ''\n // When the cap actually truncated, INCLUDE the origin in the fan-out\n // so its local buffer converges to the stored truth — otherwise the\n // origin silently diverges from what every other window (and a\n // remote submit) sees.\n const truncated = typeof p.text === 'string' && p.text.length > text.length\n // Also bound the KEY count: drafts only evict on submit / session\n // delete, so a client writing under many bogus session ids could\n // otherwise grow the map unbounded. 256 concurrent drafts is far\n // beyond any real multi-window session set.\n if (!drafts.has(p.sessionId) && drafts.size >= 256)\n return\n const entry = drafts.get(p.sessionId) ?? {}\n entry.text = text.length > 0 ? text : undefined\n // Shell mode rides out of band — never sniffed from the text.\n entry.shell = p.shell === true ? true : undefined\n compactDraft(p.sessionId, entry)\n broadcastExcept(truncated ? null : peer, { kind: 'draft:changed', sessionId: p.sessionId, text, ...(p.shell === true ? { shell: true } : {}) })\n },\n 'search.set': (p, peer) => {\n // Shared transcript-search state — same lifecycle + caps as drafts.\n const active = p.active === true\n const query = typeof p.query === 'string' ? p.query.slice(0, 4 * 1024) : ''\n if (!drafts.has(p.sessionId) && drafts.size >= 256)\n return\n const entry = drafts.get(p.sessionId) ?? {}\n entry.search = active || query.length > 0 ? { active, query } : undefined\n compactDraft(p.sessionId, entry)\n broadcastExcept(peer, { kind: 'search:changed', sessionId: p.sessionId, active, query })\n },\n 'extension.emit': async (p, peer) => {\n /*\n * Extension bus emit from a client whose extensions run client-side\n * (TUI daemon mode). Trust boundary: params are unchecked JSON —\n * same cheap shape guards as `drafts.set`, the payload cap\n * re-enforced daemon-side, and the extension id validated against\n * the session cwd's registry (spoof-resistance: a hand-rolled peer\n * can't ride an id that isn't a loaded, `'ipc'`-permitted extension).\n */\n const extensionId = typeof p.extensionId === 'string' ? p.extensionId : ''\n if (extensionId.length === 0 || extensionId.length > 256)\n return\n if (typeof p.seq !== 'number' || !Number.isFinite(p.seq))\n return\n const sessionId = typeof p.sessionId === 'string' ? p.sessionId : undefined\n const bytes = serializedPayloadBytes(p.payload)\n if (bytes === null || bytes > EXTENSION_BUS_MAX_PAYLOAD_BYTES)\n return\n // Registry for the session's cwd (same derivation as `suggest.resolve`);\n // sessionless emits validate against the boot-config registry.\n let registry = env.config.extensions\n if (sessionId !== undefined) {\n try {\n const data = await loadOrThrow(sessionId)\n const meta = data.metadata ?? {}\n const cwd = typeof meta.cwd === 'string' ? meta.cwd : (data.projectRoot ?? '')\n registry = (await env.runtimeConfig(cwd)).extensions\n }\n catch {\n return // Unknown session — nothing to validate against, drop.\n }\n }\n const known = registry.loaded.some(ext =>\n !ext.error && ext.name === extensionId && ext.definition?.permissions?.includes('ipc') === true)\n if (!known)\n return\n const nonce = typeof p.nonce === 'string' ? p.nonce : undefined\n const envelope = { extensionId, ...(sessionId !== undefined ? { sessionId } : {}), payload: p.payload, seq: p.seq, ...(nonce !== undefined ? { nonce } : {}) }\n // Daemon-side subscribers first (loopback for extensions living in\n // per-cwd daemon registries), then every OTHER window — the origin\n // already delivered to its local subscribers at emit.\n env.extensionBus?.deliver(envelope)\n broadcastExcept(peer, { kind: 'extension:signal', ...envelope })\n },\n 'suggest.resolve': async (p, peer) => {\n const sessionId = String(p.sessionId)\n const seq = typeof p.seq === 'number' ? p.seq : 0\n // Same cheap shape guards + 64 KiB text cap as `drafts.set`.\n const text = typeof p.text === 'string' ? p.text.slice(0, 64 * 1024) : ''\n const caret = typeof p.caret === 'number'\n ? Math.max(0, Math.min(Math.floor(p.caret), text.length))\n : text.length\n\n const prev = suggests.get(sessionId)\n // Latest-wins: a stale seq answers immediately without computing —\n // the requester's own NEWER request (already in flight or about to\n // land) carries the authoritative result.\n if (prev && seq < prev.seq)\n return { suggestion: null, seq }\n if (!prev && suggests.size >= 256)\n return { suggestion: null, seq }\n // Supersede: abort the in-flight compute for this session, if any.\n prev?.controller?.abort()\n\n const controller = new AbortController()\n const entry = { seq, controller: controller as AbortController | null }\n suggests.set(sessionId, entry)\n\n try {\n // Session cwd from metadata (same derivation as `toRow`) — the\n // per-cwd runtime config is the SAME registry the GUI main uses\n // locally, resolved through the existing daemon environment.\n const data = await loadOrThrow(sessionId)\n const meta = data.metadata ?? {}\n const cwd = typeof meta.cwd === 'string' ? meta.cwd : (data.projectRoot ?? '')\n const config = await env.runtimeConfig(cwd)\n // Canonical provider assembly (built-in path provider first, then\n // extension providers) and canonical deadlines —\n // `resolveInlineSuggestion` races each provider against its own\n // budget exactly as the GUI main / TUI do.\n const providers = [\n createPathAutocompleteProvider({ cwd: () => cwd }),\n ...config.extensions.inlineAutocompleteProviders,\n ]\n const result = await resolveInlineSuggestion(providers, { text, cursor: caret, cwd, signal: controller.signal })\n // Superseded mid-compute — the newer request owns the fan-out.\n if (controller.signal.aborted || suggests.get(sessionId) !== entry)\n return { suggestion: null, seq }\n // Full ordered candidate list rides along (additive field) so\n // clients keep ↑/↓ rotation; `suggestion` stays candidates[0] for\n // older clients that only read the single field.\n const candidates = result ? [...result.suffixes] : []\n const suggestion = candidates[0] ?? null\n // Fan out to every OTHER window on the session — the requester\n // already has the result from this RPC reply (drafts'\n // broadcastExcept pattern).\n broadcastExcept(peer, { kind: 'suggest:changed', sessionId, seq, basis: text, caret, suggestion, ...(candidates.length > 0 ? { candidates } : {}) })\n return { suggestion, seq, ...(candidates.length > 0 ? { candidates } : {}) }\n }\n finally {\n // Keep the seq high-water mark for stale checks; drop only the\n // controller so a finished compute can't be \"aborted\" later.\n if (suggests.get(sessionId) === entry)\n entry.controller = null\n }\n },\n 'drafts.setAttachments': (p, peer) => {\n /*\n * Composing attachments (payloads included, so a remote window can\n * submit the shared draft attachments-and-all). Bounded three ways:\n * shape-guarded entries, 24 MiB of base64 per session, and a 96 MiB\n * GLOBAL budget across every draft — the per-session cap times the\n * 256-key cap would otherwise let a looping client pin gigabytes.\n */\n const bounded = Array.isArray(p.attachments) ? boundAttachments(p.attachments) : []\n if (!drafts.has(p.sessionId) && drafts.size >= 256)\n return\n const current = drafts.get(p.sessionId)?.attachments ?? []\n const currentBytes = current.reduce((sum, a) => sum + a.data.length, 0)\n const incomingBytes = bounded.reduce((sum, a) => sum + a.data.length, 0)\n if (draftAttachmentBytes() - currentBytes + incomingBytes > 96 * 1024 * 1024)\n return\n const entry = drafts.get(p.sessionId) ?? {}\n entry.attachments = bounded.length > 0 ? bounded : undefined\n compactDraft(p.sessionId, entry)\n broadcastExcept(peer, { kind: 'draft:attachments', sessionId: p.sessionId, attachments: bounded })\n },\n 'chat.isRunning': p => host.isSessionRunning(p.sessionId),\n 'chat.rebuildAgent': p => host.disposeAgentForRebuild(p.sessionId),\n 'runtime.reload': () => {\n env.invalidateRuntime()\n host.rebuildAllAgents()\n },\n 'chat.listWork': p => host.listRunningWork(p.sessionId),\n 'chat.cancelWork': p => host.cancelRunningWork(p.sessionId, p.id, p.reason),\n\n 'sessions.list': async (p) => {\n const filter\n = p?.projectRoot !== undefined ? { projectRoot: p.projectRoot as string | null } : undefined\n\n const applyArchive = (rows: SessionRow[]): SessionRow[] => {\n const filtered = rows.filter((row) => {\n if (p?.archive === 'active')\n return !row.archived\n if (p?.archive === 'archived')\n return row.archived\n return true\n })\n filtered.sort((a, b) => b.updatedAt - a.updatedAt)\n return filtered\n }\n\n /*\n * Fast path: `listMeta` returns header rows without materializing\n * turn arrays — O(sessions), not O(total turns). Falls back to the\n * load-per-id path for stores that don't implement it.\n */\n if (store.listMeta) {\n const metas = await store.listMeta(filter)\n return applyArchive(metas.map((meta) => {\n const md = meta.metadata\n // Canonical derivation — the same helper the TUI list uses, so\n // daemon-fed rows can't render a different title for the same\n // session (the hand-rolled fallback had its own truncation).\n const title = deriveSessionTitle(meta.firstUserTurn ? [meta.firstUserTurn] : [], md)\n return {\n id: meta.id,\n title,\n projectRoot: meta.projectRoot ?? null,\n cwd: typeof md.cwd === 'string' ? md.cwd : (meta.projectRoot ?? ''),\n ...(meta.agentId ? { agentId: meta.agentId } : {}),\n // A crashed process's leftover 'running' marker must not render\n // forever — apply the same freshness bound the run guard uses.\n status: host.isSessionRunning(meta.id)\n ? 'running' as const\n : meta.status === 'running' && Date.now() - meta.updatedAt >= STALE_RUN_MARKER_MS\n ? 'idle' as const\n : (meta.status ?? 'idle'),\n createdAt: meta.createdAt ?? 0,\n updatedAt: meta.updatedAt,\n archived: md.archived === true,\n ...(typeof md.providerKey === 'string' ? { providerKey: md.providerKey } : {}),\n ...(typeof md.modelId === 'string' ? { modelId: md.modelId } : {}),\n ...(typeof md.effort === 'string' ? { effort: md.effort as SessionRow['effort'] } : {}),\n ...(md.modelOptions && typeof md.modelOptions === 'object'\n ? { modelOptions: md.modelOptions as Record<string, boolean> }\n : {}),\n ...(() => {\n const fallbacks = sessionModelFallbacks(md)\n return fallbacks ? { modelFallbacks: fallbacks } : {}\n })(),\n }\n }))\n }\n\n const ids = await store.list(filter)\n const rows: SessionRow[] = []\n for (const id of ids) {\n const data = await store.load(id)\n if (!data)\n continue\n rows.push(toRow(data))\n }\n return applyArchive(rows)\n },\n 'sessions.create': p => createSessionRow(p),\n 'sessions.delete': async (p) => {\n drafts.delete(p.sessionId)\n suggests.get(p.sessionId)?.controller?.abort()\n suggests.delete(p.sessionId)\n host.disposeSession(p.sessionId)\n await store.delete(p.sessionId)\n broadcast({ kind: 'sessions:changed', op: 'delete', sessionId: p.sessionId })\n },\n // Rename skips the recency bump so it doesn't yank the row to the top\n // of recency-sorted lists (mirrors the GUI).\n 'sessions.setTitle': p => patchSessionMeta(p.sessionId, { title: p.title }, { bumpUpdatedAt: false }),\n 'sessions.setArchived': p => patchSessionMeta(p.sessionId, { archived: p.archived }),\n 'sessions.setAgentId': async (p) => {\n /*\n * `agentId` is a top-level field (not metadata) and rebuilds the\n * agent. Patch the LIVE document when the host holds one — a bare\n * store patch loads a stale copy whose full save would clobber\n * mid-run turn appends, and the untouched live doc would revert the\n * label on its next save. Idle sessions take the store patch (no\n * concurrent writer). Then retire the agent (deferred to run end\n * while running) so a swap doesn't abort a shared run mid-stream.\n */\n if (await host.setLiveSessionAgentId(p.sessionId, p.agentId)) {\n broadcast({ kind: 'sessions:changed', op: 'update', sessionId: p.sessionId })\n }\n else {\n await patchSession(p.sessionId, (d) => {\n d.agentId = p.agentId\n })\n }\n host.disposeAgentForRebuild(p.sessionId)\n },\n 'sessions.setEffort': p => patchSessionMeta(p.sessionId, { effort: p.effort }),\n 'sessions.setModel': async (p) => {\n // The one model-swap contract (`ChatHost.setSessionModel`): patch\n // race-free, replace the chain, reset effort/options, retire the agent\n // (run-end deferred mid-run). Documented in protocol.ts.\n await host.setSessionModel(p.sessionId, {\n providerKey: p.providerKey,\n modelId: p.modelId,\n ...(p.fallbacks ? { fallbacks: p.fallbacks } : {}),\n ...(p.effort ? { effort: p.effort } : {}),\n ...(p.modelOptions ? { modelOptions: p.modelOptions } : {}),\n })\n broadcast({ kind: 'sessions:changed', op: 'update', sessionId: p.sessionId })\n },\n 'sessions.setModelOptions': p => patchSessionMeta(p.sessionId, { modelOptions: p.options }),\n 'sessions.loadEvents': async (p) => {\n const data = await loadOrThrow(p.sessionId)\n // Pass `runs` so replay reconstructs subagent depth / ancestry /\n // spawn rendering — matching the GUI + TUI local replay paths.\n return eventsFromTurns(data.turns, data.runs)\n },\n 'sessions.getTurn': async (p) => {\n const data = await store.load(p.sessionId)\n return data?.turns.find(t => t.id === p.turnId) ?? null\n },\n 'sessions.contextBreakdown': p => host.getContextBreakdown(p.sessionId),\n 'sessions.artifacts': async (p) => {\n if (!store.listArtifacts)\n return []\n return store.listArtifacts({\n sessionId: p.sessionId,\n ...(typeof p.kind === 'string' ? { kind: p.kind } : {}),\n ...(boundedLimit(p.limit) !== undefined ? { limit: boundedLimit(p.limit) } : {}),\n })\n },\n 'artifacts.list': async (p) => {\n if (!store.listArtifacts)\n return []\n return store.listArtifacts({\n // Tri-state: `null` selects untagged sessions, omitted ignores the axis.\n ...(p.projectRoot === null || typeof p.projectRoot === 'string' ? { projectRoot: p.projectRoot } : {}),\n ...(typeof p.kind === 'string' ? { kind: p.kind } : {}),\n ...(typeof p.uri === 'string' ? { uri: p.uri } : {}),\n ...(typeof p.since === 'number' && Number.isFinite(p.since) ? { since: p.since } : {}),\n ...(boundedLimit(p.limit) !== undefined ? { limit: boundedLimit(p.limit) } : {}),\n })\n },\n 'artifacts.delete': async (p) => {\n if (!store.deleteArtifact)\n return false\n await store.deleteArtifact(p.sessionId, p.artifactId)\n broadcast({ kind: 'artifact:changed', sessionId: p.sessionId, op: 'delete', artifactId: p.artifactId })\n return true\n },\n 'artifacts.upsert': async (p) => {\n if (!store.upsertArtifact)\n return null\n // Wire input: re-normalize the presentation fields (bounds, kind\n // shape) before trusting them; identity fields pass through since\n // the store merges onto the existing row anyway.\n const normalized = normalizeArtifactInput(p.artifact)\n if (!normalized || typeof p.artifact?.sessionId !== 'string' || typeof p.artifact?.id !== 'string')\n return null\n const now = Date.now()\n const stored = await store.upsertArtifact({\n ...normalized,\n id: p.artifact.id,\n sessionId: p.artifact.sessionId,\n ...(typeof p.artifact.runId === 'string' ? { runId: p.artifact.runId } : {}),\n ...(typeof p.artifact.turnId === 'string' ? { turnId: p.artifact.turnId } : {}),\n createdAt: typeof p.artifact.createdAt === 'number' ? p.artifact.createdAt : now,\n updatedAt: now,\n })\n if (stored)\n broadcast({ kind: 'artifact:changed', sessionId: stored.sessionId, op: 'upsert', artifactId: stored.id, artifact: stored })\n return stored\n },\n 'history.searchSession': async (p) => {\n const index = await sessionSearchIndex(p.sessionId)\n if (!index)\n return []\n // Wire-supplied limit: unchecked JSON — coerce to a sane positive\n // integer bounded by the corpus (same policy as queue/draft bounds).\n const limit = Number.isInteger(p.limit) && (p.limit as number) > 0\n ? Math.min(p.limit as number, index.turns.length)\n : index.turns.length\n const matches = await searchSessionIndexAsync(index, p.query, { limit })\n // Turn order, not relevance — search is a transcript filter, and\n // sorting here spares clients from re-fetching the turn list.\n const order = new Map(index.turns.map((entry, i) => [entry.turn.id, i]))\n matches.sort((a, b) => (order.get(a.turn.id) ?? 0) - (order.get(b.turn.id) ?? 0))\n return matches\n },\n 'history.searchAll': async (p, peer) => {\n /*\n * Per-peer supersession: search fires per debounced keystroke and\n * the client discards stale responses, but without this the daemon\n * would still run every superseded full-corpus pass to completion —\n * stacked CPU that delays signal fan-out for every window. A newer\n * query from the same peer makes older loops bail at the next\n * session boundary (partial result; the client ignores it anyway).\n */\n const generation = (searchGenerations.get(peer) ?? 0) + 1\n searchGenerations.set(peer, generation)\n\n const results: Record<string, SessionHistoryMatch> = {}\n // Bound the per-request corpus: `store.load` materializes full turn\n // arrays, and supersession only cancels SAME-peer re-queries — one\n // oversized request must not run the shared daemon to completion.\n const ids = Array.isArray(p.sessionIds) ? p.sessionIds.slice(0, 500) : []\n for (const sessionId of ids) {\n if (searchGenerations.get(peer) !== generation)\n break\n try {\n const index = await sessionSearchIndex(sessionId)\n if (!index)\n continue\n const match = await searchSessionHistoryIndexAsync(index, p.query, p.excerptLimit ?? 3)\n if (match)\n results[sessionId] = match\n }\n catch {\n // One bad session must not sink the whole cross-session search.\n }\n }\n return results\n },\n // Out-of-band transcript append (local command / bash output) — tell\n // every window to re-converge on the store's canonical replay.\n 'sessions.touch': (p) => {\n broadcast({ kind: 'sessions:changed', op: 'update', sessionId: p.sessionId })\n },\n 'sessions.compact': p => host.compactSessionNow(p.sessionId),\n\n 'todos.get': (p): TodosSnapshot | null => {\n const state = host.getTodosSnapshot(p.sessionId)\n return state ? toTodosSnapshot(state) : null\n },\n\n /*\n * The host fans the resulting `todos:snapshot` out to every window,\n * including this caller — the returned snapshot is a confirmation, so the\n * sender can tell \"applied\" from \"no live agent\" (null) rather than\n * assuming success.\n */\n 'todos.set': async (p): Promise<TodosSnapshot | null> => {\n const state = await host.setTodos(p.sessionId, p.items)\n return state ? toTodosSnapshot(state) : null\n },\n\n 'orchestration.main': () => host.orchestration.ensureMain(),\n 'orchestration.createWorker': ({ mainId, ...params }) => host.orchestration.createWorker(mainId, params),\n 'orchestration.list': p => host.orchestration.list(p.mainId, p.limit),\n 'orchestration.read': async p =>\n (await host.orchestration.canOrchestrate(p.mainId, p.sessionId)) ? host.orchestration.read(p.sessionId) : null,\n 'orchestration.send': p => host.orchestration.send(p.mainId, p.sessionId, p.prompt, p.wake),\n 'orchestration.steer': p => host.orchestration.steer(p.mainId, p.sessionId, p.message),\n 'orchestration.cancel': p => host.orchestration.cancel(p.mainId, p.sessionId),\n 'orchestration.transcript': p => host.orchestration.transcript(p.mainId, p.sessionId, p.limit),\n\n 'interactions.list': p => host.interactions.list(p?.sessionId),\n 'interactions.respond': p => host.interactions.respond(p.requestId, p.response),\n 'interactions.cancel': p => host.interactions.cancel(p.requestId, p.reason),\n 'interactions.rehydrate': p => host.interactions.rehydrate(p.sessionId),\n\n 'approvals.resolve': p => host.approvals.resolve(p.requestId, p.decision),\n 'approvals.denyAll': p => host.approvals.denyAll(p?.sessionId),\n 'approvals.listPending': () => host.approvals.listPending(),\n\n 'settings.get': () => env.readSettings(),\n 'settings.set': (p) => {\n const before = env.readSettings()\n const next = env.writeSettings(p.patch)\n\n /*\n * Only runtime-shaping keys tear down live agents — same policy as\n * the GUI's `settings:set` (gui/src/main/chat/ipc.ts). Toggling a\n * display/behavior key (e.g. `safeMode`) must NOT abort every live\n * run in every window; those keys are read at gate/invocation time.\n */\n const changed = (key: keyof typeof next): boolean =>\n JSON.stringify(before[key]) !== JSON.stringify(next[key])\n\n if (changed('enabledExtensions') || changed('loadProjectExtensions')) {\n env.invalidateRuntime()\n host.shutdown()\n }\n /*\n * `enabledPlugins` belongs in THIS branch, not the one above: the host\n * applies the plugin gate at agent-BUILD time (`filterRegistryPlugins` +\n * `buildPluginContributions` in `buildActiveRun`), so tearing down live\n * agents is enough and the runtime config / extension registry stay\n * valid. Without it a live agent keeps a disabled plugin's skills and\n * keeps its stdio MCP servers configured until something unrelated\n * happens to rebuild — i.e. the user revokes permission for third-party\n * code to spawn local processes and nothing happens.\n */\n else if (changed('enabledMcps') || changed('disabledMcpTools') || changed('enabledSkills') || changed('enabledPlugins') || changed('importForeignConfigs') || changed('importRulerConfigs')) {\n host.shutdown()\n }\n\n // Flipping to skip answers the modals already on screen — otherwise an\n // interaction raised under `ask` waits forever for an answer the new mode\n // says nobody will give. Resolved host-side so every window converges.\n if (changed('interactionMode') && next.interactionMode === 'skip')\n host.interactions.resolveAutomatically()\n\n // Converge every window's settings UI on the new state live.\n broadcast({ kind: 'settings:changed', settings: next })\n\n return next\n },\n\n 'presence.list': () => computePresence(),\n 'presence.watch': (p, peer) => {\n peer.watching = p.sessionId\n broadcastPresence()\n // Warm the per-cwd runtime config off the keystroke path: the first\n // suggest.resolve on a cold cwd would otherwise pay full extension\n // discovery inside the typist's wait (the 150ms provider deadline\n // starts only AFTER the registry exists). Fire-and-forget; the\n // 16-entry LRU keeps recently watched cwds hot.\n void store.load(p.sessionId).then((data) => {\n if (!data)\n return\n const meta = data.metadata ?? {}\n const cwd = typeof meta.cwd === 'string' ? meta.cwd : (data.projectRoot ?? '')\n return env.runtimeConfig(cwd)\n }).catch(() => undefined)\n },\n\n 'daemon.info': (): HelloResult => ({\n protocolVersion: IPC_PROTOCOL_VERSION,\n appVersion,\n pid: process.pid,\n startedAt,\n }),\n 'daemon.shutdown': async () => {\n // Reply first (return), then drain on the next tick.\n setImmediate(() => {\n void close().then(() => options.onIdle?.())\n })\n },\n }\n\n /* ----------------------------------------------------------------------\n * Socket plumbing\n * -------------------------------------------------------------------- */\n\n let idleTimer: ReturnType<typeof setTimeout> | undefined\n\n function armIdleTimer(): void {\n if (!options.idleShutdownMs || !options.onIdle)\n return\n clearTimeout(idleTimer)\n idleTimer = setTimeout(() => {\n if (peers.size > 0)\n return // Peers reconnected — the next last-disconnect re-arms.\n /*\n * Never abort a live run just because every window detached — `/exit`\n * is explicitly \"the run keeps going\". Re-arm and check again later.\n */\n if (host.hasActiveWork()) {\n armIdleTimer()\n return\n }\n options.onIdle?.()\n }, options.idleShutdownMs)\n }\n\n function handleConnection(socket: Socket): void {\n const peer: Peer = { socket, connection: undefined as unknown as JsonRpcConnection, ready: false, watching: null }\n\n const connection = createJsonRpcConnection({\n input: socket,\n output: socket,\n onRequest: async (message: JsonRpcRequest): Promise<JsonRpcResponse> => {\n if (message.method === 'ipc.hello') {\n const params = (message.params ?? {}) as Partial<HelloParams>\n /*\n * Build-fingerprint drain is DIRECTIONAL: only a strictly NEWER\n * client retires this (older) daemon. An older client accepts a\n * newer daemon — its unknown methods degrade via `optional()`.\n * Without the direction check, two concurrently-running clients\n * from different builds would each reject the other's daemon and\n * drain-respawn in a loop, aborting live runs each cycle.\n */\n const clientBuild = Number(params.buildId)\n const daemonBuild = Number(IPC_BUILD_ID)\n const staleBuild\n = params.buildId !== undefined\n && params.buildId !== 'unknown'\n && IPC_BUILD_ID !== 'unknown'\n && Number.isFinite(clientBuild)\n && Number.isFinite(daemonBuild)\n && clientBuild > daemonBuild\n /*\n * Release fence: the same protocol version can span several\n * releases, and a daemon outlives the upgrade that replaced its\n * binary — a client running strictly NEWER code must not execute on\n * the old process. Same direction rule as the build fence: only a\n * newer client retires the daemon; older clients attach and degrade.\n */\n const staleRelease\n = typeof params.codeVersion === 'string'\n && compareVersions(params.codeVersion, ZIDANE_VERSION) > 0\n if (params.protocolVersion !== IPC_PROTOCOL_VERSION || staleBuild || staleRelease) {\n return errorResponse(\n message.id,\n IpcErrorCode.VersionMismatch,\n staleRelease\n ? `Stale daemon: it runs zidane ${ZIDANE_VERSION} but the client runs ${params.codeVersion}. Draining for respawn.`\n : staleBuild\n ? `Build mismatch: the daemon was started from an older build (daemon ${IPC_BUILD_ID}, client ${params.buildId}). Draining for respawn.`\n : `Protocol version mismatch: daemon speaks v${IPC_PROTOCOL_VERSION}, client sent v${params.protocolVersion ?? '?'}.`,\n { daemon: { protocolVersion: IPC_PROTOCOL_VERSION, appVersion, pid: process.pid, startedAt, buildId: IPC_BUILD_ID, codeVersion: ZIDANE_VERSION } },\n )\n }\n peer.ready = true\n const result: HelloResult = {\n protocolVersion: IPC_PROTOCOL_VERSION,\n appVersion,\n pid: process.pid,\n startedAt,\n buildId: IPC_BUILD_ID,\n codeVersion: ZIDANE_VERSION,\n }\n return successResponse(message.id, result)\n }\n\n /*\n * `daemon.shutdown` / `daemon.info` are allowed pre-handshake: a\n * client that just got a VERSION_MISMATCH on `ipc.hello` (so it\n * never became `ready`) still needs to drain this stale daemon\n * before respawning a matching one.\n */\n if (!peer.ready && message.method !== 'daemon.shutdown' && message.method !== 'daemon.info')\n return errorResponse(message.id, IpcErrorCode.NotHandshaken, 'Handshake required: send ipc.hello first.')\n\n // Widened view for the string-keyed lookup — the table itself is\n // typed exhaustively above; unknown wire methods fall out here.\n const handler = (methods as Partial<Record<string, (params: unknown, peer: Peer) => unknown>>)[message.method]\n if (!handler)\n return errorResponse(message.id, -32601, `Unknown method \"${message.method}\".`)\n\n try {\n const result = await handler(message.params, peer)\n return successResponse(message.id, result ?? null)\n }\n catch (err) {\n return errorResponse(message.id, -32000, err instanceof Error ? err.message : String(err))\n }\n },\n /*\n * Notification fast path — fire-and-forget writes with no response\n * bookkeeping. Used for per-keystroke draft sync, where a\n * request/response round-trip per key would be pure overhead.\n * Routed through the SAME handler table as requests; errors are\n * swallowed (there is nowhere to send them) and pre-handshake\n * notifications are ignored.\n */\n onNotification: async (message) => {\n if (!peer.ready)\n return\n const handler = (methods as Partial<Record<string, (params: unknown, peer: Peer) => unknown>>)[message.method]\n if (!handler)\n return\n try {\n await handler(message.params, peer)\n }\n catch {\n // fire-and-forget by contract\n }\n },\n onError: err => log(`[ipc-daemon] peer error: ${err.message}`),\n })\n\n peer.connection = connection\n peers.add(peer)\n connection.start()\n log(`[ipc-daemon] peer connected (${peers.size} total)`)\n\n const drop = (): void => {\n if (!peers.delete(peer))\n return\n connection.close()\n log(`[ipc-daemon] peer disconnected (${peers.size} left)`)\n // The window detached — its session may have left the \"active\" set.\n if (peer.watching)\n broadcastPresence()\n if (peers.size === 0)\n armIdleTimer()\n }\n\n socket.on('close', drop)\n socket.on('error', drop)\n }\n\n /*\n * Stale-socket recovery: a previous daemon that died without cleanup\n * leaves the socket file behind and `listen` would fail EADDRINUSE.\n * Probe before unlinking so a LIVE daemon is never yanked out from\n * under its peers.\n */\n if (existsSync(socketPath)) {\n if (await probeSocket(socketPath))\n throw new Error(`A zidane daemon is already listening on ${socketPath}.`)\n /*\n * Narrow the concurrent-spawn race: another daemon may have bound the\n * socket between the probe above and the unlink below (each probe is a\n * full connect attempt). Re-probe immediately before unlinking and\n * abort startup if something now answers — never yank a live daemon's\n * socket out from under its peers.\n */\n if (await probeSocket(socketPath))\n throw new Error(`A zidane daemon is already listening on ${socketPath}.`)\n unlinkSync(socketPath)\n }\n /*\n * The daemon speaks an unauthenticated RPC surface that can execute tools\n * as the daemon user — restrict access to the owner. The containing dir\n * is created 0700 (belt) and the socket is chmod'd to 0600 right after\n * `listen` (suspenders) so connectability never depends on ambient umask.\n *\n * `mkdirSync`'s mode only applies on CREATION — a pre-existing dir keeps\n * its old mode. When the dir is the DEDICATED daemon dir (the `daemon/`\n * subdir `defaultSocketPath` uses, or one we created fresh for an\n * explicit socket path) chmod it 0700 explicitly. A shared, pre-existing\n * dir (tests pointing the socket at a tmpdir) is left alone — never\n * tighten a directory that also holds unrelated state.\n */\n const socketDir = dirname(socketPath)\n const dedicatedSocketDir = basename(socketDir) === 'daemon' || !existsSync(socketDir)\n mkdirSync(socketDir, { recursive: true, mode: 0o700 })\n if (dedicatedSocketDir) {\n try {\n chmodSync(socketDir, 0o700)\n }\n catch {\n // Best-effort — mirrors the socket chmod below.\n }\n }\n\n const server: Server = createServer(handleConnection)\n await new Promise<void>((resolvePromise, reject) => {\n server.once('error', reject)\n server.listen(socketPath, () => {\n server.off('error', reject)\n resolvePromise()\n })\n })\n try {\n chmodSync(socketPath, 0o600)\n }\n catch {\n // Best-effort on platforms where socket chmod is a no-op (e.g. some\n // network filesystems); the 0700 dir still constrains access.\n }\n log(`[ipc-daemon] listening on ${socketPath}`)\n // We bound the socket, so we own the file — close() unlinks it exactly\n // once. Cleared after the unlink so a file later re-created by a NEW\n // daemon on the same path is never yanked by a stale close.\n let ownsSocketFile = true\n armIdleTimer()\n\n /*\n * One-time v6 migration: drain any daemon still serving the pre-v6\n * socket path (`<stateDir>/daemon.sock`). New clients never probe it,\n * so a `--idle-exit 0` service daemon there would run forever alongside\n * this one — two sets of live agents, run guards, and heartbeat writers\n * against the same sessions.db. Fire-and-forget: version mismatch is\n * the EXPECTED case (a legacy daemon speaks an older protocol) and\n * `drainOnMismatch: true` performs the shutdown for us; a same-version\n * survivor (explicit env overrides) gets an explicit daemon.shutdown.\n */\n const legacyPath = legacySocketPath(socketPath)\n if (legacyPath) {\n void (async () => {\n if (!(await probeSocket(legacyPath)))\n return\n log(`[ipc-daemon] draining legacy daemon on ${legacyPath}`)\n try {\n const survivor = await connectIpcClient({ socketPath: legacyPath, appVersion: `${appVersion}/legacy-drain`, drainOnMismatch: true })\n await survivor.request('daemon.shutdown', {}).catch(() => undefined)\n survivor.close()\n }\n catch {\n // IpcVersionMismatchError = drained by the handshake path; anything\n // else (raced exit, wedged socket) is best-effort — leave it.\n }\n })()\n }\n\n async function doClose(): Promise<void> {\n clearTimeout(idleTimer)\n await host.drain()\n for (const peer of peers) {\n peer.connection.close()\n peer.socket.destroy()\n }\n peers.clear()\n await new Promise<void>((resolvePromise) => {\n server.close(() => resolvePromise())\n })\n if (ownsSocketFile) {\n ownsSocketFile = false\n try {\n unlinkSync(socketPath)\n }\n catch {\n // already gone\n }\n }\n }\n\n /*\n * Idempotent close: `daemon.shutdown` runs close() and then onIdle, whose\n * CLI implementation calls daemon.close() again — both callers share one\n * drain instead of double-unlinking the socket. Memoized rather than\n * flag-guarded so the second caller awaits actual completion.\n */\n let closePromise: Promise<void> | null = null\n function close(): Promise<void> {\n closePromise ??= doClose()\n return closePromise\n }\n\n return {\n socketPath,\n host,\n peerCount: () => peers.size,\n close,\n }\n}\n","/**\n * Login-shell PATH for processes not started from a terminal.\n *\n * A Dock/Finder-launched GUI, or a daemon it spawns, inherits launchd's PATH\n * (`/usr/bin:/bin:/usr/sbin:/sbin`), so `node` / `bun` / `yarn` are invisible\n * to agent shell commands. Ask the user's login shell for its PATH and merge\n * it into `process.env.PATH`.\n *\n * Merge, don't replace: a terminal launch may have prepended entries the rc\n * files know nothing about (`nvm use`, direnv, `PATH=... zidane`), and those\n * must survive. Idempotent, so every surface (GUI, daemon) can call it and a\n * daemon spawned by an already-fixed GUI ends up with the same PATH.\n */\nimport { spawnSync } from 'node:child_process'\nimport { userInfo } from 'node:os'\nimport { delimiter } from 'node:path'\nimport process from 'node:process'\n\nconst DELIMITER = '_ZIDANE_SHELL_ENV_'\n\n/** A slow rc file costs startup time; a prompting one must never hang the process. */\nconst DEFAULT_TIMEOUT_MS = 3_000\n\nfunction defaultShell(env: NodeJS.ProcessEnv): string {\n try {\n const { shell } = userInfo()\n if (shell)\n return shell\n }\n catch {}\n return env.SHELL || (process.platform === 'darwin' ? '/bin/zsh' : '/bin/sh')\n}\n\nfunction parsePath(stdout: string): string | undefined {\n const body = stdout.split(DELIMITER)[1]\n if (body === undefined)\n return undefined\n // eslint-disable-next-line no-control-regex\n const clean = body.replace(/\\u001B\\[[0-9;]*[A-Z]/gi, '')\n for (const line of clean.split('\\n')) {\n if (line.startsWith('PATH='))\n return line.slice('PATH='.length) || undefined\n }\n return undefined\n}\n\n/**\n * PATH as seen by an interactive login shell, or `undefined` when no shell\n * answers in time. Tries the user's shell first, then POSIX fallbacks for\n * non-POSIX defaults (nushell, xonsh) whose `-ilc` differs. An explicit\n * `shell` is tried alone.\n */\nexport function shellPath(options: { env?: NodeJS.ProcessEnv, timeoutMs?: number, shell?: string } = {}): string | undefined {\n if (process.platform === 'win32')\n return undefined\n const env = options.env ?? process.env\n const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const shells = options.shell ? [options.shell] : [...new Set([defaultShell(env), '/bin/zsh', '/bin/bash'])]\n for (const shell of shells) {\n const result = spawnSync(\n shell,\n // `command env` sidesteps aliases/functions named `env`.\n ['-ilc', `echo -n \"${DELIMITER}\"; command env; echo -n \"${DELIMITER}\"; exit`],\n {\n encoding: 'utf8',\n timeout,\n stdio: ['ignore', 'pipe', 'ignore'],\n env: {\n ...env,\n // Keep oh-my-zsh from blocking on its update prompt / tmux autostart.\n DISABLE_AUTO_UPDATE: 'true',\n ZSH_TMUX_AUTOSTARTED: 'true',\n ZSH_TMUX_AUTOSTART: 'false',\n },\n },\n )\n if (result.error || result.status !== 0)\n continue\n const path = parsePath(result.stdout)\n if (path)\n return path\n }\n return undefined\n}\n\n/**\n * Entries of `current` the shell doesn't know about (ad-hoc prepends from the\n * launching terminal) stay in front; then the shell's PATH in its own order.\n * From the Dock every launchd entry is already in the shell PATH, so the\n * result is exactly the shell PATH, with the shell's precedence intact.\n */\nexport function mergePath(current: string | undefined, shell: string | undefined): string {\n const split = (path: string | undefined): string[] => (path ?? '').split(delimiter).filter(Boolean)\n const fromShell = split(shell)\n const known = new Set(fromShell)\n return [...new Set([...split(current).filter(entry => !known.has(entry)), ...fromShell])].join(delimiter)\n}\n\n/** Merge the login shell's PATH into `process.env.PATH`. No-op on Windows or when no shell answers. */\nexport function fixPath(): void {\n const extra = shellPath()\n if (extra)\n process.env.PATH = mergePath(process.env.PATH, extra)\n}\n"],"mappings":"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"}
package/dist/tui.js CHANGED
@@ -21,9 +21,9 @@ import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
21
21
  import { n as JsonRpcRemoteError, t as JsonRpcConnectionClosedError } from "./json-rpc-rSWMUh9b.js";
22
22
  import { $ as pickChainPrimary, $t as fitRunReceiptCells, An as createChatsCompletionProvider, At as selectableTurnIds, C as resetLabel, Cn as parseEditOutcomesFromResult, Ct as isUserBashEvent, D as splitPromptSegments, Dn as summarizeOutcomes, E as usageTone, En as stripEditOutcomesAnnotation, Et as promptHistoryFromEvents, F as codeTokenHasClosingFence, H as liveSessionCost, I as normalizeMarkdownCodeFences, K as DAEMON_HEALTH_STYLE, Kt as sessionDirLabel, L as splitMarkdownCodeBlocks, Lt as SESSION_EVENT_STATUS_STYLE, Mt as streamEventSearchText, N as chunkMarkdownForRender, Nt as turnSelectionOwnership, O as buildModelCatalog, Ot as retagUserPromptEvents, P as closeDanglingCodeFence, Q as filterChainFallbacks, R as stripHtmlCommentsOutsideCode, S as mostConstrainedWindow, Sn as mergeApprovalAndBodyOutcomes, St as isTurnHighlighted, T as updatedLabel, Tn as rewriteMultiEditHeader, Tt as isVisibleDuringSearch, V as compactingLabel, Vt as isMainSession, W as streamingLabel, X as chainsEqual, Xt as shortSessionId, Y as chainPositionOf, Yt as sessionEventMeta, Zt as truncateTitle, _ as turnContextSize, _t as buildLinearRamp, a as TOOL_DISPLAY, bn as buildEditOutcomesAnnotation, bt as EDIT_TOOL_NAMES, d as finalizeStreamingMarkdown, dn as clipDiffForRender, en as formatCostUsd, f as finalizeStreamingMarkdownForOwner, ft as createFilesCompletionProvider, g as revealEarlierTranscriptEvents, gn as previewEditPayload, gt as blendHsl, h as replaceTranscriptTurnEvents, hn as filetypeFromPath, ht as createCommandsCompletionProvider, i as turnAsText, in as formatTokenCount, it as restoreChainFallbacks, j as parseCustomModelQuery, jt as selectionAfterTurnRemoval, k as filterModelCatalog, kt as retagUserPromptEventsFromRefs, ln as buildContextualDiff, lt as createSkillsCompletionProvider, mn as extractEditPayload, n as deleteTurnSafely, nn as formatRunReceiptCells, nt as providerKeySetChanged, o as displayNameFor, on as hasRunReceipt, ot as toggleChainEntry, p as hiddenEarlierTranscriptCount, q as buildRunModel, qt as sessionEventExcerpt, r as truncateTurnsAt, rn as formatRunReceiptLine, rt as reorderKeyed, s as formatToolCall, sn as runReceiptFromRun, u as capTranscriptEvents, un as buildUnifiedDiff, ut as uniqueSkillNamesFromReferences, vn as summarizeEditPayload, vt as BASH_CALL_ID_PREFIX, w as showsAccount, wn as resolveApprovalForPayload, wt as isVisible, x as formatUsagePercent, xt as isEditErrorResult, y as formatBalance, z as cacheBreakChip } from "./turn-operations-DrbJlpq9.js";
23
23
  import { a as searchHighlightRanges, c as searchSessionHistoryIndexAsync, i as highlightSegments, n as buildSessionSearchIndexAsync, r as filterSessionSearchCatalog, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
24
- import { $ as runOAuthLogin, $n as dedupMcpServersByName, $t as discoverProjectPlugins, A as useSafeModeQueue, Ai as lastContextSizeFromTurns, Bi as toolCallPreview, Bn as resolveConfig, Ci as createChatTurnsReadTool, Cn as resolveChipColor, Ct as buildResumedToolResultsTurn, Di as deriveSessionTitle, Dt as isModelInteraction, Fn as DiscoveryProvider, G as providerUsageSupported, Gn as useCompletion, Gt as modelChainMetadata, H as fetchAllProviderUsage, Hi as updateToolEventOutcomes, Hr as accentColor, Ht as createBuiltinCommands, Ii as serverToolResultSummary, In as useDiscovery, It as installProviderDiagnosticHook, J as formatPathForCwd, Jn as importFromClaudeCode, Jt as createFileMcpCredentialStore, K as usageChecksOptedOut, Kn as countClaudeSessionFiles, Li as stripSpawnTokensLine, Ln as useDiscoveryOptional, M as addToSafelist, Mi as loadState, Mn as parseEditorCommand, Mr as groupArtifacts, Mt as useInteractionsQueue, N as getSafelist, Ni as marginTopFor, Nn as quoteForShell, O as SafeModeProvider, Oi as eventsFromTurns, Ot as makeRequestInteraction, P as isOnSafelist, Pi as noticeTurnContent, Pn as createDiscoverySlot, Pr as discoverAgentsMd, Q as oauthUsesManualCodePaste, Qn as buildMcpServers, Qt as buildPluginContributions, R as suggestSafelistEntries, Ri as sumRunCosts, Rn as ConfigProvider, Si as chatReferenceTokensForPrompt, St as PRESENT_PLAN_TOOL, Ti as compactSummaryEvent, Tt as createInteractionTools, U as fetchProviderUsage, Ui as usageByModel, Un as createArtifactsCompletionProvider, Ut as applyThinkingBindingSetting, V as clearProviderUsageCache, Vi as toolResultText, Vn as resolveStoragePaths, Wt as createMainSession, Xn as removeMcpServer, Y as fetchOAuthRedirect, Yn as mcpsWritePath, Yr as TODO_STATUS_GLYPHS, Z as redirectUriFromAuthUrl, Zn as upsertMcpServer, Zr as applyTodosSnapshot, Zt as runEndNotice, _ as buildSpawnEndEvent, _n as SettingsProvider, _r as useRestartRequired, ai as selectActiveTodos, an as clipHintsToWidth, b as defaultSkillScanPaths, bi as buildPlanSystem, bn as useSettings, bt as HOST_INTERACTION_ID_PREFIX, c as useSelectStyle, ci as useActiveTodos, cn as buildHints, cr as buildRestartHint, ct as useMcpToolToggleMap, d as useTheme, dn as useEnabledToggleSet, dt as indexOfServerRow, en as parsePluginScanPathEnv, et as supportsOAuth, f as discoverSystemMd, fn as DEFAULT_SETTINGS, ft as parentServerName, g as useStreamBuffer, gn as SETTINGS_TOGGLES, gt as getMcpAuthStatus, h as userSystemMdPath, hn as SETTINGS_INPUTS, ht as useMcpAuthState, in as EMPTY_HINTS, ir as tryRevealPath, it as refreshMcpToolsCatalog, ji as listSessionMeta, jr as chatWindowBehavior, jt as useInteractionsActions, k as useSafeModeActions, ki as getSessionMeta, kt as pendingInteractionsFromTurns, l as useSurfaces, lr as buildUpdateHint, m as userScopeOverridesLoad, mn as SETTINGS_CHOICES, mt as useMcpAuthDispatch, ni as pickActiveRunId, nt as loadMcpToolsCache, o as ThemeProvider, oi as setTodosForRun, on as hintsLength, or as bootTick, ot as subscribeMcpToolsCache, p as renderSystemMdSeed, pn as SETTINGS_CATEGORIES, pt as McpAuthProvider, qn as hasClaudeCodeSetup, qt as resolveSandboxFromSettings, r as computeTurnAnchors, ri as pruneTodosByRun, rr as tryOpenBrowser, rt as mcpToolsCachePath, s as useColors, si as todoProgressLabel, sn as truncateTrailing, tn as attachMcpWarnLog, tr as discoverProjectMcps, un as listProjectFiles, ur as useUpdateCheck, ut as buildVisibleMcpRows, v as buildSpawnStartEvent, vn as clampFps, vt as ASK_USER_TOOL, w as writeSessionExport, wn as resolveTheme, wt as createAutomaticInteractionResponse, x as discoverProjectSkills, xi as envSection, xt as InteractionsProvider, y as buildSkillsConfig, yi as buildBuildSystem, zn as useConfig } from "./approvals-CnE_8JVZ.js";
24
+ import { $ as runOAuthLogin, $n as dedupMcpServersByName, $t as discoverProjectPlugins, A as useSafeModeQueue, Ai as lastContextSizeFromTurns, Bi as toolCallPreview, Bn as resolveConfig, Ci as createChatTurnsReadTool, Cn as resolveChipColor, Ct as buildResumedToolResultsTurn, Di as deriveSessionTitle, Dt as isModelInteraction, Fn as DiscoveryProvider, G as providerUsageSupported, Gn as useCompletion, Gt as modelChainMetadata, H as fetchAllProviderUsage, Hi as updateToolEventOutcomes, Hr as accentColor, Ht as createBuiltinCommands, Ii as serverToolResultSummary, In as useDiscovery, It as installProviderDiagnosticHook, J as formatPathForCwd, Jn as importFromClaudeCode, Jt as createFileMcpCredentialStore, K as usageChecksOptedOut, Kn as countClaudeSessionFiles, Li as stripSpawnTokensLine, Ln as useDiscoveryOptional, M as addToSafelist, Mi as loadState, Mn as parseEditorCommand, Mr as groupArtifacts, Mt as useInteractionsQueue, N as getSafelist, Ni as marginTopFor, Nn as quoteForShell, O as SafeModeProvider, Oi as eventsFromTurns, Ot as makeRequestInteraction, P as isOnSafelist, Pi as noticeTurnContent, Pn as createDiscoverySlot, Pr as discoverAgentsMd, Q as oauthUsesManualCodePaste, Qn as buildMcpServers, Qt as buildPluginContributions, R as suggestSafelistEntries, Ri as sumRunCosts, Rn as ConfigProvider, Si as chatReferenceTokensForPrompt, St as PRESENT_PLAN_TOOL, Ti as compactSummaryEvent, Tt as createInteractionTools, U as fetchProviderUsage, Ui as usageByModel, Un as createArtifactsCompletionProvider, Ut as applyThinkingBindingSetting, V as clearProviderUsageCache, Vi as toolResultText, Vn as resolveStoragePaths, Wt as createMainSession, Xn as removeMcpServer, Y as fetchOAuthRedirect, Yn as mcpsWritePath, Yr as TODO_STATUS_GLYPHS, Z as redirectUriFromAuthUrl, Zn as upsertMcpServer, Zr as applyTodosSnapshot, Zt as runEndNotice, _ as buildSpawnEndEvent, _n as SettingsProvider, _r as useRestartRequired, ai as selectActiveTodos, an as clipHintsToWidth, b as defaultSkillScanPaths, bi as buildPlanSystem, bn as useSettings, bt as HOST_INTERACTION_ID_PREFIX, c as useSelectStyle, ci as useActiveTodos, cn as buildHints, cr as buildRestartHint, ct as useMcpToolToggleMap, d as useTheme, dn as useEnabledToggleSet, dt as indexOfServerRow, en as parsePluginScanPathEnv, et as supportsOAuth, f as discoverSystemMd, fn as DEFAULT_SETTINGS, ft as parentServerName, g as useStreamBuffer, gn as SETTINGS_TOGGLES, gt as getMcpAuthStatus, h as userSystemMdPath, hn as SETTINGS_INPUTS, ht as useMcpAuthState, in as EMPTY_HINTS, ir as tryRevealPath, it as refreshMcpToolsCatalog, ji as listSessionMeta, jr as chatWindowBehavior, jt as useInteractionsActions, k as useSafeModeActions, ki as getSessionMeta, kt as pendingInteractionsFromTurns, l as useSurfaces, lr as buildUpdateHint, m as userScopeOverridesLoad, mn as SETTINGS_CHOICES, mt as useMcpAuthDispatch, ni as pickActiveRunId, nt as loadMcpToolsCache, o as ThemeProvider, oi as setTodosForRun, on as hintsLength, or as bootTick, ot as subscribeMcpToolsCache, p as renderSystemMdSeed, pn as SETTINGS_CATEGORIES, pt as McpAuthProvider, qn as hasClaudeCodeSetup, qt as resolveSandboxFromSettings, r as computeTurnAnchors, ri as pruneTodosByRun, rr as tryOpenBrowser, rt as mcpToolsCachePath, s as useColors, si as todoProgressLabel, sn as truncateTrailing, tn as attachMcpWarnLog, tr as discoverProjectMcps, un as listProjectFiles, ur as useUpdateCheck, ut as buildVisibleMcpRows, v as buildSpawnStartEvent, vn as clampFps, vt as ASK_USER_TOOL, w as writeSessionExport, wn as resolveTheme, wt as createAutomaticInteractionResponse, x as discoverProjectSkills, xi as envSection, xt as InteractionsProvider, y as buildSkillsConfig, yi as buildBuildSystem, zn as useConfig } from "./approvals-DKaJZYlm.js";
25
25
  import { A as renderDefaultCallbackPage, C as modelOptionsFor, O as piIdOf, b as getContextWindow, d as setPreferredCredential, f as setProviderCredential, k as restoreModelOptions, l as removeStoredCredential, n as applyApiKeyEnv, s as readProviderCredentials, t as detectAuth, u as setCredentialLabel, w as modelSupportsReasoning, y as enabledModelOptions } from "./auth-BG2rVKSt.js";
26
- import { _ as selfDaemonSpawnCommand, c as connectIpcClient, h as ensureDaemonRunning, l as connectOrSpawnDaemon, m as defaultSocketPath } from "./shell-path-C-fcAmH-.js";
26
+ import { _ as selfDaemonSpawnCommand, c as connectIpcClient, h as ensureDaemonRunning, l as connectOrSpawnDaemon, m as defaultSocketPath } from "./shell-path-h5czzllR.js";
27
27
  import { createDockerContext } from "./contexts/docker.js";
28
28
  import { createTuiStore } from "./session/sqlite.js";
29
29
  import { A as buildVisibleInstructionRows, C as togglePanel, I as parentProviderKey, L as selectableSources, M as instructionPreviewLines, N as buildVisibleAuthRows, O as validateMcpForm, P as indexOfProviderRow, S as resolveActiveTab, T as MCP_TRANSPORTS, _ as cycleTab, a as editTodo, b as pickTab, h as buildPanelCatalog, i as cycleTodoStatus, j as instructionOriginBadge, n as buildTodoRetryMessage, p as INITIAL_PANEL_STATE, r as clearTodos, s as removeTodo, t as addTodo, w as NOTICE_GLYPH } from "./todo-edits-UOwgxGKu.js";
@@ -9332,6 +9332,9 @@ async function connectDaemonSession(options) {
9332
9332
  async rebuildAgent() {
9333
9333
  await optional(() => client.request("chat.rebuildAgent", { sessionId }));
9334
9334
  },
9335
+ async reloadRuntime() {
9336
+ await optional(() => client.request("runtime.reload", {}));
9337
+ },
9335
9338
  async fetchPresence() {
9336
9339
  return optional(() => client.request("presence.list", {}));
9337
9340
  },
@@ -23000,12 +23003,20 @@ function AppShell() {
23000
23003
  * so agent-side contributions recompose on the next agent build, exactly
23001
23004
  * like a cwd-change swap.
23002
23005
  */
23006
+ /**
23007
+ * Daemon runtime controller (experimental, `useDaemon` setting). When
23008
+ * set, execution lives in the shared daemon and `agentRef` stays null —
23009
+ * every `agentRef.current?.` consumer degrades soft by construction.
23010
+ * Mutually exclusive with `agentRef` per activation.
23011
+ */
23012
+ const daemonRef = useRef(null);
23003
23013
  const extensionRegistryLiveRef = useRef(extensionRegistry);
23004
23014
  extensionRegistryLiveRef.current = extensionRegistry;
23005
23015
  const reloadExtensions = useCallback(async () => {
23006
23016
  const next = await discoverAndBuildExtensionRegistry(registryBuildOptions(cwdRef.current));
23007
23017
  setExtensionRegistry(next);
23008
23018
  extensionRegistryLiveRef.current = next;
23019
+ await daemonRef.current?.reloadRuntime();
23009
23020
  return {
23010
23021
  active: next.loaded.filter((e) => e.setupRan).length,
23011
23022
  inactive: next.inactive.length
@@ -23497,13 +23508,6 @@ function AppShell() {
23497
23508
  * activation (new or resumed session) picks up the current day.
23498
23509
  */
23499
23510
  const envDateRef = useRef((/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
23500
- /**
23501
- * Daemon runtime controller (experimental, `useDaemon` setting). When
23502
- * set, execution lives in the shared daemon and `agentRef` stays null —
23503
- * every `agentRef.current?.` consumer degrades soft by construction.
23504
- * Mutually exclusive with `agentRef` per activation.
23505
- */
23506
- const daemonRef = useRef(null);
23507
23511
  useEffect(() => getTuiBus()?.onEmit((envelope) => {
23508
23512
  daemonRef.current?.emitExtension(envelope);
23509
23513
  }), []);