zidane 7.0.28 → 7.0.30

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.
Files changed (59) hide show
  1. package/dist/{acp-4jF6btaq.js → acp-Cu879kus.js} +4 -4
  2. package/dist/{acp-4jF6btaq.js.map → acp-Cu879kus.js.map} +1 -1
  3. package/dist/acp-cli.js +5 -5
  4. package/dist/acp.js +1 -1
  5. package/dist/{agent-DLgyw1n5.js → agent-BipX4BBj.js} +35 -5
  6. package/dist/{agent-DLgyw1n5.js.map → agent-BipX4BBj.js.map} +1 -1
  7. package/dist/agent.js +1 -1
  8. package/dist/{anthropic-CUM2CjXS.js → anthropic-B4UmPm4H.js} +3 -3
  9. package/dist/{anthropic-CUM2CjXS.js.map → anthropic-B4UmPm4H.js.map} +1 -1
  10. package/dist/{approvals-YO3zmQ-s.js → approvals-staFZZ5o.js} +43 -8
  11. package/dist/approvals-staFZZ5o.js.map +1 -0
  12. package/dist/{auth-DXJ9hJmU.js → auth-D4MpTfvF.js} +5 -5
  13. package/dist/{auth-DXJ9hJmU.js.map → auth-D4MpTfvF.js.map} +1 -1
  14. package/dist/chat.d.ts +1 -1
  15. package/dist/chat.js +3 -3
  16. package/dist/daemon-cli.js +1 -1
  17. package/dist/eval.js +1 -1
  18. package/dist/extensions.js +1 -1
  19. package/dist/headless.js +3 -3
  20. package/dist/{index-Co4Fddtj.d.ts → index-CnXKeZ8p.d.ts} +33 -2
  21. package/dist/{index-Co4Fddtj.d.ts.map → index-CnXKeZ8p.d.ts.map} +1 -1
  22. package/dist/index.js +6 -6
  23. package/dist/{ipc-t6ygtrb5.js → ipc-CbkrpfkI.js} +2 -2
  24. package/dist/{ipc-t6ygtrb5.js.map → ipc-CbkrpfkI.js.map} +1 -1
  25. package/dist/ipc.d.ts +13 -8
  26. package/dist/ipc.d.ts.map +1 -1
  27. package/dist/ipc.js +3 -3
  28. package/dist/{messages-aORUmIKe.js → messages-2jcOA_5R.js} +2 -2
  29. package/dist/{messages-aORUmIKe.js.map → messages-2jcOA_5R.js.map} +1 -1
  30. package/dist/{openai-compat-cIeLSED_.js → openai-compat-Dy56weK8.js} +28 -15
  31. package/dist/openai-compat-Dy56weK8.js.map +1 -0
  32. package/dist/presets.js +1 -1
  33. package/dist/providers/anthropic.js +1 -1
  34. package/dist/providers/arcee.js +1 -1
  35. package/dist/providers/baseten.js +1 -1
  36. package/dist/providers/cerebras.js +1 -1
  37. package/dist/providers/local.js +1 -1
  38. package/dist/providers/openai-compat.js +1 -1
  39. package/dist/providers/openai.js +2 -2
  40. package/dist/providers/openrouter.js +1 -1
  41. package/dist/providers/xai.js +1 -1
  42. package/dist/{providers-DPygcIX3.js → providers-DP9kwXYo.js} +2 -2
  43. package/dist/{providers-DPygcIX3.js.map → providers-DP9kwXYo.js.map} +1 -1
  44. package/dist/providers.js +3 -3
  45. package/dist/restate.js +2 -2
  46. package/dist/{server-Dnc3BRS4.js → server-GJ2gNRt_.js} +17 -22
  47. package/dist/server-GJ2gNRt_.js.map +1 -0
  48. package/dist/{session-23Stp71-.js → session-CVy-2mAy.js} +2 -2
  49. package/dist/{session-23Stp71-.js.map → session-CVy-2mAy.js.map} +1 -1
  50. package/dist/session.js +2 -2
  51. package/dist/tools.js +1 -1
  52. package/dist/transcript-anchors-BHyrChoO.d.ts.map +1 -1
  53. package/dist/tui.d.ts.map +1 -1
  54. package/dist/tui.js +172 -86
  55. package/dist/tui.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/approvals-YO3zmQ-s.js.map +0 -1
  58. package/dist/openai-compat-cIeLSED_.js.map +0 -1
  59. package/dist/server-Dnc3BRS4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"providers-DPygcIX3.js","names":["BASE_URL","getApiKey"],"sources":["../src/providers/cursor.ts","../src/providers/mistral.ts","../src/providers/zai.ts"],"sourcesContent":["import type { Provider, StreamCallbacks } from '.'\nimport { CURSOR_OAUTH_PROVIDER_ID } from '../chat/oauth-page/cursor'\nimport { resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\n/**\n * Cursor provider (OAuth).\n *\n * Auth is fully wired: `bun run auth --cursor` (or the TUI wizard) logs in via\n * Cursor's poll-based PKCE flow and persists credentials; this provider resolves\n * + refreshes the OAuth token on every turn via {@link resolveOAuthApiKey}.\n *\n * Inference is **not** wired yet. Cursor does not expose an OpenAI-compatible\n * HTTP endpoint — it speaks a protobuf/Connect agent protocol over HTTP/2 to\n * `api2.cursor.sh`. Implementing `stream()` requires porting that transport\n * (generated protobuf schemas + an HTTP/2 streaming client) in-process, which\n * is intentionally left as a follow-up. Until then `stream()` resolves the\n * token (proving auth works) and then throws a clear, actionable error.\n *\n * The `openaiCompat` base supplies the message-format helpers (`formatTools`,\n * `userMessage`, `toolResultsMessage`, …) so the provider satisfies the full\n * `Provider` contract and slots into the registry; only `stream` is overridden.\n */\nexport interface CursorParams {\n /** Bypass OAuth resolution with an explicit token (mainly for tests). */\n apiKey?: string\n defaultModel?: string\n /**\n * Extra HTTP headers for the eventual Cursor transport. Accepted for API\n * parity with the other providers and forwarded to the openai-compat base,\n * but inert until Cursor inference is implemented (the `stream` override\n * currently throws before any request is made).\n */\n extraHeaders?: Record<string, string>\n}\n\nconst DEFAULT_MODEL = 'claude-4.6-sonnet'\n\nconst NOT_IMPLEMENTED_MESSAGE\n = 'Cursor OAuth login works, but inference over Cursor is not implemented yet. '\n + 'Cursor uses a protobuf/HTTP-2 agent protocol (not an OpenAI-compatible API), '\n + 'so the streaming transport still needs to be ported. Use another provider for now.'\n\nexport function cursor(params?: CursorParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n\n // Base only provides the format/serialization methods; `baseURL` is never\n // hit because we override `stream` below.\n const base = openaiCompat({\n name: 'cursor',\n apiKey: params?.apiKey ?? 'oauth',\n baseURL: 'https://api2.cursor.sh',\n defaultModel,\n capabilities: { vision: true, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n\n return {\n ...base,\n meta: { ...base.meta, defaultModel },\n async stream(options, callbacks: StreamCallbacks) {\n // Resolve (and lazily refresh) the OAuth token so auth failures surface\n // here with the right remediation, distinct from the not-implemented path.\n await resolveOAuthApiKey(\n {\n provider: 'cursor',\n providerId: CURSOR_OAUTH_PROVIDER_ID,\n params,\n missingError: 'No Cursor credentials found. Run `bun run auth --cursor` first.',\n refreshError: reason => `Cursor OAuth token refresh failed. Run \\`bun run auth --cursor\\` again. ${reason}`,\n },\n callbacks,\n )\n void options\n throw new Error(NOT_IMPLEMENTED_MESSAGE)\n },\n }\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Mistral La Plateforme — OpenAI-compatible `/v1/chat/completions`. Model ids\n// are plain slugs (`mistral-medium-latest`, `devstral-2512`, …). Reasoning is\n// NOT a separate wire field here: with `reasoning_effort: 'high'` the response\n// `content` becomes a list of `thinking` / `text` chunks (handled by\n// `openaiCompat`'s content-chunk path), and with `'none'` it stays a plain\n// string. See https://docs.mistral.ai/studio-api/conversations/reasoning.\nconst BASE_URL = 'https://api.mistral.ai/v1'\n\nexport interface MistralParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at a regional endpoint or a gateway in\n * front of La Plateforme.\n */\n baseURL?: string\n /**\n * Provider capability flags. The `mistral-medium` / `mistral-large` /\n * `pixtral` families accept `image_url` parts — default:\n * `{ vision: true, imageInToolResult: false }`. Set `vision: false` when\n * pinning a text-only model (`codestral`, `devstral`, `ministral`).\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: MistralParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.MISTRAL_API_KEY)\n return process.env.MISTRAL_API_KEY\n\n throw new Error('No Mistral API key found. Pass `apiKey` or set MISTRAL_API_KEY in your environment.')\n}\n\n/**\n * Model ids that accept `reasoning_effort`. Mistral rejects unknown request\n * fields on some routes, so the knob is sent only for the families that\n * document it (`mistral-small-*`, `mistral-medium-3.5` / `-2604`, and the\n * deprecated-but-live `magistral-*`); `codestral` / `devstral` / `ministral`\n * requests stay byte-identical to what they were without this planner.\n */\nconst REASONING_MODELS = /^(?:magistral-|mistral-small-|mistral-medium-(?:3\\.5|2604))/\n\n/**\n * Map zidane's thinking level to Mistral's `reasoning_effort`. Only the two\n * documented values are emitted — `'none'` (thinking chunk omitted, `content`\n * stays a plain string) and `'high'` (full thinking chunk). `adaptive` sends\n * nothing so the model's own default applies.\n *\n * Exported for tests.\n */\nexport function planMistralReasoning(\n ctx: { model: string, thinking?: ThinkingLevel },\n): Record<string, unknown> | undefined {\n if (!REASONING_MODELS.test(ctx.model))\n return undefined\n if (ctx.thinking === undefined || ctx.thinking === 'adaptive')\n return undefined\n return { reasoning_effort: ctx.thinking === 'off' ? 'none' : 'high' }\n}\n\n/**\n * Mistral provider (La Plateforme).\n *\n * Thin wrapper around {@link openaiCompat} with Mistral-specific defaults\n * (base URL, default model, `reasoning_effort` planner). `supportsReasoning`\n * stays off: Mistral carries thinking inside `content` chunks rather than a\n * `reasoning_content` field, so there is nothing to echo back upstream —\n * thinking streams to the UI and is dropped from subsequent requests.\n */\nexport function mistral(params?: MistralParams): Provider {\n return openaiCompat({\n name: 'mistral',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'mistral-medium-latest',\n capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false },\n planReasoningRequest: planMistralReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Z.ai (Zhipu) GLM Open Platform — OpenAI-compatible endpoint. Model ids are\n// plain lowercase slugs (`glm-4.7`, `glm-4.7-flash`, …), reasoning streams in\n// the `reasoning_content` delta field, and thinking is toggled via an\n// `extra_body.thinking` object rather than a `reasoning_effort` string.\n// See https://docs.z.ai/guides/develop/http/introduction and\n// https://docs.z.ai/guides/capabilities/thinking-mode.\nconst BASE_URL = 'https://api.z.ai/api/paas/v4'\n\nexport interface ZaiParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at the dedicated Coding Plan endpoint\n * instead of the standard API endpoint.\n */\n baseURL?: string\n /**\n * Provider capability flags. GLM text models are text-only — default:\n * `{ vision: false, imageInToolResult: false }`.\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: ZaiParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.ZAI_API_KEY)\n return process.env.ZAI_API_KEY\n\n throw new Error('No Z.ai API key found. Pass `apiKey` or set ZAI_API_KEY in your environment.')\n}\n\n/**\n * Map zidane's thinking level to Z.ai's `extra_body.thinking` request shape.\n * GLM has no depth knob — thinking is a binary switch — so any non-`off`\n * level enables it and `off` explicitly disables it (GLM-5.x/4.7 think by\n * default, so a silent omission would not turn thinking off).\n *\n * Exported for tests.\n */\nexport function planZaiReasoning(\n ctx: { thinking?: ThinkingLevel },\n): Record<string, unknown> {\n const enabled = !!ctx.thinking && ctx.thinking !== 'off'\n return { thinking: { type: enabled ? 'enabled' : 'disabled' } }\n}\n\n/**\n * Z.ai (Zhipu) GLM provider.\n *\n * Thin wrapper around {@link openaiCompat} pinned to the `reasoning_content`\n * round-trip mode: thinking arrives in the `reasoning_content` delta field and\n * is echoed back on assistant messages under the same name, keeping GLM's\n * interleaved/preserved thinking coherent across tool calls.\n */\nexport function zai(params?: ZaiParams): Provider {\n return openaiCompat({\n name: 'zai',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'glm-5.3-flash',\n capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false },\n supportsReasoning: true,\n reasoningMode: 'reasoning_content',\n reasoningContentField: 'reasoning_content',\n planReasoningRequest: planZaiReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n"],"mappings":";;;;AAoCA,MAAM,gBAAgB;AAEtB,MAAM,0BACF;AAIJ,SAAgB,OAAO,QAAiC;CACtD,MAAM,eAAe,QAAQ,gBAAgB;CAI7C,MAAM,OAAO,aAAa;EACxB,MAAM;EACN,QAAQ,QAAQ,UAAU;EAC1B,SAAS;EACT;EACA,cAAc;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EACvD,cAAc,QAAQ;CACxB,CAAC;CAED,OAAO;EACL,GAAG;EACH,MAAM;GAAE,GAAG,KAAK;GAAM;EAAa;EACnC,MAAM,OAAO,SAAS,WAA4B;GAGhD,MAAM,mBACJ;IACE,UAAU;IACV,YAAY;IACZ;IACA,cAAc;IACd,eAAc,WAAU,2EAA2E;GACrG,GACA,SACF;GAEA,MAAM,IAAI,MAAM,uBAAuB;EACzC;CACF;AACF;;;ACnEA,MAAMA,aAAW;AAwBjB,SAASC,YAAU,QAAgC;CAEjD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,iBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,qFAAqF;AACvG;;;;;;;;AASA,MAAM,mBAAmB;;;;;;;;;AAUzB,SAAgB,qBACd,KACqC;CACrC,IAAI,CAAC,iBAAiB,KAAK,IAAI,KAAK,GAClC,OAAO,KAAA;CACT,IAAI,IAAI,aAAa,KAAA,KAAa,IAAI,aAAa,YACjD,OAAO,KAAA;CACT,OAAO,EAAE,kBAAkB,IAAI,aAAa,QAAQ,SAAS,OAAO;AACtE;;;;;;;;;;AAWA,SAAgB,QAAQ,QAAkC;CACxD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQA,YAAU,MAAM;EACxB,SAAS,QAAQ,WAAWD;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EAC/E,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH;;;ACjFA,MAAM,WAAW;AAsBjB,SAAS,UAAU,QAA4B;CAE7C,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,aACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,8EAA8E;AAChG;;;;;;;;;AAUA,SAAgB,iBACd,KACyB;CAEzB,OAAO,EAAE,UAAU,EAAE,MADL,CAAC,CAAC,IAAI,YAAY,IAAI,aAAa,QACd,YAAY,WAAW,EAAE;AAChE;;;;;;;;;AAUA,SAAgB,IAAI,QAA8B;CAChD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQ,UAAU,MAAM;EACxB,SAAS,QAAQ,WAAW;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAO,mBAAmB;EAAM;EAChF,mBAAmB;EACnB,eAAe;EACf,uBAAuB;EACvB,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH"}
1
+ {"version":3,"file":"providers-DP9kwXYo.js","names":["BASE_URL","getApiKey"],"sources":["../src/providers/cursor.ts","../src/providers/mistral.ts","../src/providers/zai.ts"],"sourcesContent":["import type { Provider, StreamCallbacks } from '.'\nimport { CURSOR_OAUTH_PROVIDER_ID } from '../chat/oauth-page/cursor'\nimport { resolveOAuthApiKey } from './oauth'\nimport { openaiCompat } from './openai-compat'\n\n/**\n * Cursor provider (OAuth).\n *\n * Auth is fully wired: `bun run auth --cursor` (or the TUI wizard) logs in via\n * Cursor's poll-based PKCE flow and persists credentials; this provider resolves\n * + refreshes the OAuth token on every turn via {@link resolveOAuthApiKey}.\n *\n * Inference is **not** wired yet. Cursor does not expose an OpenAI-compatible\n * HTTP endpoint — it speaks a protobuf/Connect agent protocol over HTTP/2 to\n * `api2.cursor.sh`. Implementing `stream()` requires porting that transport\n * (generated protobuf schemas + an HTTP/2 streaming client) in-process, which\n * is intentionally left as a follow-up. Until then `stream()` resolves the\n * token (proving auth works) and then throws a clear, actionable error.\n *\n * The `openaiCompat` base supplies the message-format helpers (`formatTools`,\n * `userMessage`, `toolResultsMessage`, …) so the provider satisfies the full\n * `Provider` contract and slots into the registry; only `stream` is overridden.\n */\nexport interface CursorParams {\n /** Bypass OAuth resolution with an explicit token (mainly for tests). */\n apiKey?: string\n defaultModel?: string\n /**\n * Extra HTTP headers for the eventual Cursor transport. Accepted for API\n * parity with the other providers and forwarded to the openai-compat base,\n * but inert until Cursor inference is implemented (the `stream` override\n * currently throws before any request is made).\n */\n extraHeaders?: Record<string, string>\n}\n\nconst DEFAULT_MODEL = 'claude-4.6-sonnet'\n\nconst NOT_IMPLEMENTED_MESSAGE\n = 'Cursor OAuth login works, but inference over Cursor is not implemented yet. '\n + 'Cursor uses a protobuf/HTTP-2 agent protocol (not an OpenAI-compatible API), '\n + 'so the streaming transport still needs to be ported. Use another provider for now.'\n\nexport function cursor(params?: CursorParams): Provider {\n const defaultModel = params?.defaultModel || DEFAULT_MODEL\n\n // Base only provides the format/serialization methods; `baseURL` is never\n // hit because we override `stream` below.\n const base = openaiCompat({\n name: 'cursor',\n apiKey: params?.apiKey ?? 'oauth',\n baseURL: 'https://api2.cursor.sh',\n defaultModel,\n capabilities: { vision: true, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n\n return {\n ...base,\n meta: { ...base.meta, defaultModel },\n async stream(options, callbacks: StreamCallbacks) {\n // Resolve (and lazily refresh) the OAuth token so auth failures surface\n // here with the right remediation, distinct from the not-implemented path.\n await resolveOAuthApiKey(\n {\n provider: 'cursor',\n providerId: CURSOR_OAUTH_PROVIDER_ID,\n params,\n missingError: 'No Cursor credentials found. Run `bun run auth --cursor` first.',\n refreshError: reason => `Cursor OAuth token refresh failed. Run \\`bun run auth --cursor\\` again. ${reason}`,\n },\n callbacks,\n )\n void options\n throw new Error(NOT_IMPLEMENTED_MESSAGE)\n },\n }\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Mistral La Plateforme — OpenAI-compatible `/v1/chat/completions`. Model ids\n// are plain slugs (`mistral-medium-latest`, `devstral-2512`, …). Reasoning is\n// NOT a separate wire field here: with `reasoning_effort: 'high'` the response\n// `content` becomes a list of `thinking` / `text` chunks (handled by\n// `openaiCompat`'s content-chunk path), and with `'none'` it stays a plain\n// string. See https://docs.mistral.ai/studio-api/conversations/reasoning.\nconst BASE_URL = 'https://api.mistral.ai/v1'\n\nexport interface MistralParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at a regional endpoint or a gateway in\n * front of La Plateforme.\n */\n baseURL?: string\n /**\n * Provider capability flags. The `mistral-medium` / `mistral-large` /\n * `pixtral` families accept `image_url` parts — default:\n * `{ vision: true, imageInToolResult: false }`. Set `vision: false` when\n * pinning a text-only model (`codestral`, `devstral`, `ministral`).\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: MistralParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.MISTRAL_API_KEY)\n return process.env.MISTRAL_API_KEY\n\n throw new Error('No Mistral API key found. Pass `apiKey` or set MISTRAL_API_KEY in your environment.')\n}\n\n/**\n * Model ids that accept `reasoning_effort`. Mistral rejects unknown request\n * fields on some routes, so the knob is sent only for the families that\n * document it (`mistral-small-*`, `mistral-medium-3.5` / `-2604`, and the\n * deprecated-but-live `magistral-*`); `codestral` / `devstral` / `ministral`\n * requests stay byte-identical to what they were without this planner.\n */\nconst REASONING_MODELS = /^(?:magistral-|mistral-small-|mistral-medium-(?:3\\.5|2604))/\n\n/**\n * Map zidane's thinking level to Mistral's `reasoning_effort`. Only the two\n * documented values are emitted — `'none'` (thinking chunk omitted, `content`\n * stays a plain string) and `'high'` (full thinking chunk). `adaptive` sends\n * nothing so the model's own default applies.\n *\n * Exported for tests.\n */\nexport function planMistralReasoning(\n ctx: { model: string, thinking?: ThinkingLevel },\n): Record<string, unknown> | undefined {\n if (!REASONING_MODELS.test(ctx.model))\n return undefined\n if (ctx.thinking === undefined || ctx.thinking === 'adaptive')\n return undefined\n return { reasoning_effort: ctx.thinking === 'off' ? 'none' : 'high' }\n}\n\n/**\n * Mistral provider (La Plateforme).\n *\n * Thin wrapper around {@link openaiCompat} with Mistral-specific defaults\n * (base URL, default model, `reasoning_effort` planner). `supportsReasoning`\n * stays off: Mistral carries thinking inside `content` chunks rather than a\n * `reasoning_content` field, so there is nothing to echo back upstream —\n * thinking streams to the UI and is dropped from subsequent requests.\n */\nexport function mistral(params?: MistralParams): Provider {\n return openaiCompat({\n name: 'mistral',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'mistral-medium-latest',\n capabilities: params?.capabilities ?? { vision: true, imageInToolResult: false },\n planReasoningRequest: planMistralReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n","import type { Provider, ProviderCapabilities } from '.'\nimport type { ThinkingLevel } from '../types'\nimport { openaiCompat } from './openai-compat'\n\n// Z.ai (Zhipu) GLM Open Platform — OpenAI-compatible endpoint. Model ids are\n// plain lowercase slugs (`glm-4.7`, `glm-4.7-flash`, …), reasoning streams in\n// the `reasoning_content` delta field, and thinking is toggled via an\n// `extra_body.thinking` object rather than a `reasoning_effort` string.\n// See https://docs.z.ai/guides/develop/http/introduction and\n// https://docs.z.ai/guides/capabilities/thinking-mode.\nconst BASE_URL = 'https://api.z.ai/api/paas/v4'\n\nexport interface ZaiParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Override the base URL — e.g. point at the dedicated Coding Plan endpoint\n * instead of the standard API endpoint.\n */\n baseURL?: string\n /**\n * Provider capability flags. GLM text models are text-only — default:\n * `{ vision: false, imageInToolResult: false }`.\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: ZaiParams): string {\n // Resolution order aligned with the other providers: explicit > env > fail.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.ZAI_API_KEY)\n return process.env.ZAI_API_KEY\n\n throw new Error('No Z.ai API key found. Pass `apiKey` or set ZAI_API_KEY in your environment.')\n}\n\n/**\n * Map zidane's thinking level to Z.ai's `extra_body.thinking` request shape.\n * GLM has no depth knob — thinking is a binary switch — so any non-`off`\n * level enables it and `off` explicitly disables it (GLM-5.x/4.7 think by\n * default, so a silent omission would not turn thinking off).\n *\n * Exported for tests.\n */\nexport function planZaiReasoning(\n ctx: { thinking?: ThinkingLevel },\n): Record<string, unknown> {\n const enabled = !!ctx.thinking && ctx.thinking !== 'off'\n return { thinking: { type: enabled ? 'enabled' : 'disabled' } }\n}\n\n/**\n * Z.ai (Zhipu) GLM provider.\n *\n * Thin wrapper around {@link openaiCompat} pinned to the `reasoning_content`\n * round-trip mode: thinking arrives in the `reasoning_content` delta field and\n * is echoed back on assistant messages under the same name, keeping GLM's\n * interleaved/preserved thinking coherent across tool calls.\n */\nexport function zai(params?: ZaiParams): Provider {\n return openaiCompat({\n name: 'zai',\n apiKey: getApiKey(params),\n baseURL: params?.baseURL || BASE_URL,\n defaultModel: params?.defaultModel || 'glm-5.3-flash',\n capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false },\n supportsReasoning: true,\n reasoningMode: 'reasoning_content',\n reasoningContentField: 'reasoning_content',\n planReasoningRequest: planZaiReasoning,\n extraHeaders: params?.extraHeaders,\n })\n}\n"],"mappings":";;;;AAoCA,MAAM,gBAAgB;AAEtB,MAAM,0BACF;AAIJ,SAAgB,OAAO,QAAiC;CACtD,MAAM,eAAe,QAAQ,gBAAgB;CAI7C,MAAM,OAAO,aAAa;EACxB,MAAM;EACN,QAAQ,QAAQ,UAAU;EAC1B,SAAS;EACT;EACA,cAAc;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EACvD,cAAc,QAAQ;CACxB,CAAC;CAED,OAAO;EACL,GAAG;EACH,MAAM;GAAE,GAAG,KAAK;GAAM;EAAa;EACnC,MAAM,OAAO,SAAS,WAA4B;GAGhD,MAAM,mBACJ;IACE,UAAU;IACV,YAAY;IACZ;IACA,cAAc;IACd,eAAc,WAAU,2EAA2E;GACrG,GACA,SACF;GAEA,MAAM,IAAI,MAAM,uBAAuB;EACzC;CACF;AACF;;;ACnEA,MAAMA,aAAW;AAwBjB,SAASC,YAAU,QAAgC;CAEjD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,iBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,qFAAqF;AACvG;;;;;;;;AASA,MAAM,mBAAmB;;;;;;;;;AAUzB,SAAgB,qBACd,KACqC;CACrC,IAAI,CAAC,iBAAiB,KAAK,IAAI,KAAK,GAClC,OAAO,KAAA;CACT,IAAI,IAAI,aAAa,KAAA,KAAa,IAAI,aAAa,YACjD,OAAO,KAAA;CACT,OAAO,EAAE,kBAAkB,IAAI,aAAa,QAAQ,SAAS,OAAO;AACtE;;;;;;;;;;AAWA,SAAgB,QAAQ,QAAkC;CACxD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQA,YAAU,MAAM;EACxB,SAAS,QAAQ,WAAWD;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAM,mBAAmB;EAAM;EAC/E,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH;;;ACjFA,MAAM,WAAW;AAsBjB,SAAS,UAAU,QAA4B;CAE7C,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,aACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,8EAA8E;AAChG;;;;;;;;;AAUA,SAAgB,iBACd,KACyB;CAEzB,OAAO,EAAE,UAAU,EAAE,MADL,CAAC,CAAC,IAAI,YAAY,IAAI,aAAa,QACd,YAAY,WAAW,EAAE;AAChE;;;;;;;;;AAUA,SAAgB,IAAI,QAA8B;CAChD,OAAO,aAAa;EAClB,MAAM;EACN,QAAQ,UAAU,MAAM;EACxB,SAAS,QAAQ,WAAW;EAC5B,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAO,mBAAmB;EAAM;EAChF,mBAAmB;EACnB,eAAe;EACf,uBAAuB;EACvB,sBAAsB;EACtB,cAAc,QAAQ;CACxB,CAAC;AACH"}
package/dist/providers.js CHANGED
@@ -1,10 +1,10 @@
1
- import { E as getModelCatalog, S as resolvePromptCacheKey, b as sanitizeToolSpecs, f as mapOAIFinishReason, j as setModelCatalog, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, x as derivePromptCacheKey, y as sanitizeToolSchema } from "./openai-compat-cIeLSED_.js";
2
- import { n as anthropic, r as applyAnthropicCacheBreakpoints } from "./anthropic-CUM2CjXS.js";
1
+ import { E as getModelCatalog, S as resolvePromptCacheKey, b as sanitizeToolSpecs, f as mapOAIFinishReason, j as setModelCatalog, l as classifyOpenAICompatError, m as openaiCompat, n as OpenAICompatHttpError, x as derivePromptCacheKey, y as sanitizeToolSchema } from "./openai-compat-Dy56weK8.js";
2
+ import { n as anthropic, r as applyAnthropicCacheBreakpoints } from "./anthropic-B4UmPm4H.js";
3
3
  import { a as readOAuthCredentials, d as getOAuthApiKey, f as getOAuthProvider, h as registerOAuthProvider, i as isOAuthAccessToken, l as writeOAuthCredentials, m as refreshOpenAICodexToken, n as credentialsFromParams, o as registerCustomOAuthProvider, p as refreshAnthropicToken, r as extractRuntimeCredentials, s as resolveOAuthApiKey, t as credentialsFilePath, u as ensureBundledOAuthFlows } from "./oauth-B6F43fti.js";
4
4
  import { arcee } from "./providers/arcee.js";
5
5
  import { baseten, planBasetenReasoning } from "./providers/baseten.js";
6
6
  import { cerebras } from "./providers/cerebras.js";
7
- import { a as cursor, i as planMistralReasoning, n as zai, r as mistral, t as planZaiReasoning } from "./providers-DPygcIX3.js";
7
+ import { a as cursor, i as planMistralReasoning, n as zai, r as mistral, t as planZaiReasoning } from "./providers-DP9kwXYo.js";
8
8
  import { local } from "./providers/local.js";
9
9
  import { openai } from "./providers/openai.js";
10
10
  import { openrouter } from "./providers/openrouter.js";
package/dist/restate.js CHANGED
@@ -1,6 +1,6 @@
1
- import { L as buildAliasMaps } from "./openai-compat-cIeLSED_.js";
1
+ import { L as buildAliasMaps } from "./openai-compat-Dy56weK8.js";
2
2
  import { i as coerceToolResultContent, l as toolOutputByteLength, u as toolResultToText } from "./types-CWgENVLY.js";
3
- import { Ot as reserveChildLabel, a as stagedToolState, ar as hydrateReadStateFromSessionAsync, i as isToolStateOps } from "./agent-DLgyw1n5.js";
3
+ import { Ot as reserveChildLabel, a as stagedToolState, ar as hydrateReadStateFromSessionAsync, i as isToolStateOps } from "./agent-BipX4BBj.js";
4
4
  import { r as formatTokenUsage } from "./stats-DAKBEKjc.js";
5
5
  import { createHash } from "node:crypto";
6
6
  //#region src/restate/awakeable.ts
@@ -1,4 +1,4 @@
1
- import { Fn as createExtensionBus, In as serializedPayloadBytes, at as createExtensionHud, gt as createExtensionHostRuntimeBridge, ot as discoverAndBuildExtensionRegistry } from "./agent-DLgyw1n5.js";
1
+ import { Fn as createExtensionBus, In as serializedPayloadBytes, at as createExtensionHud, gt as createExtensionHostRuntimeBridge, ot as discoverAndBuildExtensionRegistry } from "./agent-BipX4BBj.js";
2
2
  import { S as resolveInlineSuggestion, b as createPathAutocompleteProvider } from "./src-DzzhE2YO.js";
3
3
  import { l as normalizeArtifactInput } from "./artifacts-CV2hUKnL.js";
4
4
  import { t as writeFileAtomic } from "./atomic-write-B7zr1ddF.js";
@@ -6,8 +6,8 @@ import { p as resolveStorageDirs } from "./plugins-B531Tz5b.js";
6
6
  import { a as successResponse, i as errorResponse, n as JsonRpcRemoteError, r as createJsonRpcConnection } from "./json-rpc-rSWMUh9b.js";
7
7
  import { It as isMainSession } from "./turn-operations-BgqpLlOG.js";
8
8
  import { c as searchSessionHistoryIndexAsync, n as buildSessionSearchIndexAsync, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
9
- import { It as createChatHost, Kt as modelChainMetadata, Ln as resolveConfig, Lt as toTodosSnapshot, ft as createFileMcpCredentialStore, gn as enforceSettingsInvariants, ln as DEFAULT_SETTINGS, mi as eventsFromTurns, pi as deriveSessionTitle, pn as SETTINGS_TOGGLES, qt as sessionModelFallbacks } from "./approvals-YO3zmQ-s.js";
10
- import { d as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath } from "./auth-DXJ9hJmU.js";
9
+ import { It as createChatHost, Ln as resolveConfig, Lt as toTodosSnapshot, ft as createFileMcpCredentialStore, gn as enforceSettingsInvariants, ln as DEFAULT_SETTINGS, mi as eventsFromTurns, pi as deriveSessionTitle, pn as SETTINGS_TOGGLES, qt as sessionModelFallbacks } from "./approvals-staFZZ5o.js";
10
+ import { d as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath } from "./auth-D4MpTfvF.js";
11
11
  import { basename, dirname, join } from "node:path";
12
12
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
13
13
  import { spawn } from "node:child_process";
@@ -26,7 +26,7 @@ import { connect, createServer } from "node:net";
26
26
  * retires it, so an upgrade can never leave windows silently executing on a
27
27
  * process started from the previous release.
28
28
  */
29
- const ZIDANE_VERSION = "7.0.28";
29
+ const ZIDANE_VERSION = "7.0.30";
30
30
  /**
31
31
  * Compare two dotted versions (`7.0.19`, `7.1.0-beta.1`): negative when `a`
32
32
  * is older, positive when newer, 0 when equal. Pre-release tags are ignored
@@ -234,7 +234,8 @@ async function startDaemon(options) {
234
234
  * - `chat.pushQueued` result gained the `'queued'` delivered variant.
235
235
  * - `sessions.setModel` now honors `fallbacks`: it persists the canonical
236
236
  * `metadata.modelChain` and CLEARS a previously persisted chain when
237
- * omitted/empty.
237
+ * omitted/empty. It also accepts `effort` / `modelOptions` and resets
238
+ * both when omitted (a pick is atomic — no stale per-model knobs).
238
239
  * - additive: `extension.emit` notification + `extension:signal` fan-out
239
240
  * (the permission-gated per-extension cross-window bus, `ctx.bus`).
240
241
  * Older daemons ignore the unknown notification; older clients drop the
@@ -844,18 +845,7 @@ async function startIpcDaemon(options) {
844
845
  * the row to the top of recency-sorted lists (mirrors the GUI).
845
846
  */
846
847
  async function patchSessionMeta(sessionId, patch, opts = {}) {
847
- if (await host.patchLiveSessionMeta(sessionId, patch)) {
848
- broadcast({
849
- kind: "sessions:changed",
850
- op: "update",
851
- sessionId
852
- });
853
- return;
854
- }
855
- const data = await loadOrThrow(sessionId);
856
- for (const [key, value] of Object.entries(patch)) data.metadata[key] = value;
857
- if (opts.bumpUpdatedAt !== false) data.updatedAt = Date.now();
858
- await store.save(data);
848
+ await host.patchSessionMeta(sessionId, patch, opts);
859
849
  broadcast({
860
850
  kind: "sessions:changed",
861
851
  op: "update",
@@ -1134,13 +1124,18 @@ async function startIpcDaemon(options) {
1134
1124
  },
1135
1125
  "sessions.setEffort": (p) => patchSessionMeta(p.sessionId, { effort: p.effort }),
1136
1126
  "sessions.setModel": async (p) => {
1137
- const chainPatch = p.fallbacks === void 0 ? {} : { modelChain: modelChainMetadata(p.providerKey, p.modelId, p.fallbacks) };
1138
- await patchSessionMeta(p.sessionId, {
1127
+ await host.setSessionModel(p.sessionId, {
1139
1128
  providerKey: p.providerKey,
1140
1129
  modelId: p.modelId,
1141
- ...chainPatch
1130
+ ...p.fallbacks ? { fallbacks: p.fallbacks } : {},
1131
+ ...p.effort ? { effort: p.effort } : {},
1132
+ ...p.modelOptions ? { modelOptions: p.modelOptions } : {}
1133
+ });
1134
+ broadcast({
1135
+ kind: "sessions:changed",
1136
+ op: "update",
1137
+ sessionId: p.sessionId
1142
1138
  });
1143
- host.disposeAgentForRebuild(p.sessionId);
1144
1139
  },
1145
1140
  "sessions.setModelOptions": (p) => patchSessionMeta(p.sessionId, { modelOptions: p.options }),
1146
1141
  "sessions.loadEvents": async (p) => {
@@ -1456,4 +1451,4 @@ async function startIpcDaemon(options) {
1456
1451
  //#endregion
1457
1452
  export { connectIpcClient as a, IpcErrorCode as c, defaultSocketPath as d, ensureDaemonRunning as f, IpcVersionMismatchError as i, IPC_BUILD_ID as l, selfDaemonSpawnCommand as m, createDaemonEnvironment as n, connectOrSpawnDaemon as o, probeSocket as p, createFileApprovalsStore as r, IPC_PROTOCOL_VERSION as s, startIpcDaemon as t, daemonSocketName as u };
1458
1453
 
1459
- //# sourceMappingURL=server-Dnc3BRS4.js.map
1454
+ //# sourceMappingURL=server-GJ2gNRt_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-GJ2gNRt_.js","names":["(pkg as { version?: string }).version"],"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"],"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 */\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 '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 '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"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { c as mergeArtifact, d as sortArtifacts, s as matchesArtifactFilter, u as selectArtifactEvictions } from "./artifacts-CV2hUKnL.js";
2
- import { o as dropInvalidToolPairings, v as toWireMessages } from "./messages-aORUmIKe.js";
2
+ import { o as dropInvalidToolPairings, v as toWireMessages } from "./messages-2jcOA_5R.js";
3
3
  import { Buffer } from "node:buffer";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
5
5
  //#region src/session/file-map.ts
@@ -786,4 +786,4 @@ function assertJsonBounded(value, maxBytes, label) {
786
786
  //#endregion
787
787
  export { getExtensionSessionState as a, setExtensionSessionState as c, createMemoryStore as d, createFileMapStore as f, demoteStaleRunningStatus as i, withExtensionSession as l, currentExtensionSession as n, loadSession as o, deleteExtensionSessionState as r, resumeChainRunIds as s, createSession as t, createRemoteStore as u };
788
788
 
789
- //# sourceMappingURL=session-23Stp71-.js.map
789
+ //# sourceMappingURL=session-CVy-2mAy.js.map