zidane 8.2.3 → 8.2.5

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 (166) hide show
  1. package/dist/{acp-oYRjlh71.js → acp-BIInKhVI.js} +6 -6
  2. package/dist/{acp-oYRjlh71.js.map → acp-BIInKhVI.js.map} +1 -1
  3. package/dist/acp-cli.js +4 -4
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-CBQglXtH.js → agent-CmhCOmyO.js} +10 -5
  7. package/dist/{agent-CBQglXtH.js.map → agent-CmhCOmyO.js.map} +1 -1
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/chat/pure.d.ts +4 -4
  11. package/dist/chat/pure.js +7 -7
  12. package/dist/chat.d.ts +3 -3
  13. package/dist/chat.js +16 -16
  14. package/dist/{cli-shared-E5m_pI8A.js → cli-shared-GOehTeqk.js} +2 -2
  15. package/dist/{cli-shared-E5m_pI8A.js.map → cli-shared-GOehTeqk.js.map} +1 -1
  16. package/dist/{client-core-EEEX_u6D.d.ts → client-core-Dz8hELs4.d.ts} +15 -5
  17. package/dist/client-core-Dz8hELs4.d.ts.map +1 -0
  18. package/dist/codemode-worker.js +1 -1
  19. package/dist/codemode.js +1 -1
  20. package/dist/{connection-loop-CpgfUmuw.d.ts → connection-loop-dm_TN-QD.d.ts} +3 -3
  21. package/dist/{connection-loop-CpgfUmuw.d.ts.map → connection-loop-dm_TN-QD.d.ts.map} +1 -1
  22. package/dist/contexts/daytona.d.ts +1 -1
  23. package/dist/contexts/e2b.d.ts +1 -1
  24. package/dist/{credentials-B9DfBJP9.js → credentials-DxOQmset.js} +2 -2
  25. package/dist/{credentials-B9DfBJP9.js.map → credentials-DxOQmset.js.map} +1 -1
  26. package/dist/daemon-cli.js +2 -2
  27. package/dist/{dispatcher-Bv9-c6I2.js → dispatcher-D_9_QFO3.js} +22 -18
  28. package/dist/dispatcher-D_9_QFO3.js.map +1 -0
  29. package/dist/{engine-XmoPPB6U.js → engine-BsWBWgTB.js} +6 -6
  30. package/dist/{engine-XmoPPB6U.js.map → engine-BsWBWgTB.js.map} +1 -1
  31. package/dist/{eval-CfQlBR81.js → eval-CIHIF_Ns.js} +3 -3
  32. package/dist/{eval-CfQlBR81.js.map → eval-CIHIF_Ns.js.map} +1 -1
  33. package/dist/eval.d.ts +1 -1
  34. package/dist/eval.js +1 -1
  35. package/dist/{executor-BhthPE1q.js → executor-BSesPPcU.js} +3 -3
  36. package/dist/{executor-BhthPE1q.js.map → executor-BSesPPcU.js.map} +1 -1
  37. package/dist/{extensions-BgBwogRW.js → extensions-BJof-2mi.js} +3 -3
  38. package/dist/{extensions-BgBwogRW.js.map → extensions-BJof-2mi.js.map} +1 -1
  39. package/dist/extensions.d.ts +1 -1
  40. package/dist/extensions.js +1 -1
  41. package/dist/{fold-rules-DSypi36k.d.ts → fold-rules-CMpWNPqk.d.ts} +2 -2
  42. package/dist/{fold-rules-DSypi36k.d.ts.map → fold-rules-CMpWNPqk.d.ts.map} +1 -1
  43. package/dist/{headless-R4pbpTkm.js → headless-Bs9Lb4vs.js} +4 -4
  44. package/dist/{headless-R4pbpTkm.js.map → headless-Bs9Lb4vs.js.map} +1 -1
  45. package/dist/headless.d.ts +1 -1
  46. package/dist/headless.js +1 -1
  47. package/dist/history-worker.js +1 -1
  48. package/dist/{host-scoped-settings-C-Nmb6vr.js → host-scoped-settings-DxxI_yRo.js} +2 -2
  49. package/dist/{host-scoped-settings-C-Nmb6vr.js.map → host-scoped-settings-DxxI_yRo.js.map} +1 -1
  50. package/dist/{host-signals-BFudPyCT.js → host-signals-CUKPZZxC.js} +5 -5
  51. package/dist/{host-signals-BFudPyCT.js.map → host-signals-CUKPZZxC.js.map} +1 -1
  52. package/dist/{index-DyQyDLX1.d.ts → index-4mDRyTGJ.d.ts} +1722 -1691
  53. package/dist/index-4mDRyTGJ.d.ts.map +1 -0
  54. package/dist/{index-CDS7YdUC.d.ts → index-Dlmu8HnG.d.ts} +5 -5
  55. package/dist/{index-CDS7YdUC.d.ts.map → index-Dlmu8HnG.d.ts.map} +1 -1
  56. package/dist/{index-AqRZ3gEz.d.ts → index-QsAN9yjZ.d.ts} +3 -3
  57. package/dist/{index-AqRZ3gEz.d.ts.map → index-QsAN9yjZ.d.ts.map} +1 -1
  58. package/dist/index.d.ts +3 -3
  59. package/dist/index.js +8 -8
  60. package/dist/ipc/remote.d.ts +5 -5
  61. package/dist/ipc/remote.js +3 -3
  62. package/dist/ipc.d.ts +5 -5
  63. package/dist/ipc.js +9 -9
  64. package/dist/{links-rFaWqS2Y.js → links-Bto-5dVu.js} +10 -10
  65. package/dist/{links-rFaWqS2Y.js.map → links-Bto-5dVu.js.map} +1 -1
  66. package/dist/{mcp-allowlist-CLXweyxk.js → mcp-allowlist-BbnTBKNN.js} +2 -2
  67. package/dist/{mcp-allowlist-CLXweyxk.js.map → mcp-allowlist-BbnTBKNN.js.map} +1 -1
  68. package/dist/mcp.d.ts +1 -1
  69. package/dist/{oauth-7_48zR7n.js → oauth-BwPiX0hL.js} +66 -32
  70. package/dist/oauth-BwPiX0hL.js.map +1 -0
  71. package/dist/output/stream-json.d.ts +1 -1
  72. package/dist/output/terminal.d.ts +1 -1
  73. package/dist/plugins.d.ts +1 -1
  74. package/dist/{prelude-HzY_JEGq.js → prelude-qjPUkw4f.js} +26 -2
  75. package/dist/prelude-qjPUkw4f.js.map +1 -0
  76. package/dist/{presets-DXeHsd24.js → presets-C4hkykN-.js} +2 -2
  77. package/dist/{presets-DXeHsd24.js.map → presets-C4hkykN-.js.map} +1 -1
  78. package/dist/presets.d.ts +1 -1
  79. package/dist/presets.js +1 -1
  80. package/dist/{protocol-BIv1Yuvl.js → protocol-Do_i6Ras.js} +3 -1
  81. package/dist/protocol-Do_i6Ras.js.map +1 -0
  82. package/dist/providers/anthropic.d.ts +1 -1
  83. package/dist/providers/arcee.d.ts +1 -1
  84. package/dist/providers/baseten.d.ts +1 -1
  85. package/dist/providers/cerebras.d.ts +1 -1
  86. package/dist/providers/google.d.ts +1 -1
  87. package/dist/providers/local.d.ts +1 -1
  88. package/dist/providers/neuralwatt.d.ts +1 -1
  89. package/dist/providers/openai-compat.d.ts +1 -1
  90. package/dist/providers/openai.d.ts +1 -1
  91. package/dist/providers/openrouter.d.ts +1 -1
  92. package/dist/providers/xai.d.ts +1 -1
  93. package/dist/providers.d.ts +1 -1
  94. package/dist/{registry-eourRLY2.js → registry-DxPP_tAt.js} +3 -3
  95. package/dist/{registry-eourRLY2.js.map → registry-DxPP_tAt.js.map} +1 -1
  96. package/dist/{remote-hosts-DY2J07ut.js → remote-hosts-DcCh7fqp.js} +4 -4
  97. package/dist/{remote-hosts-DY2J07ut.js.map → remote-hosts-DcCh7fqp.js.map} +1 -1
  98. package/dist/restate.d.ts +1 -1
  99. package/dist/{secret-shapes-BjW4l6U0.js → secret-shapes-HvaYVzwc.js} +10 -2
  100. package/dist/secret-shapes-HvaYVzwc.js.map +1 -0
  101. package/dist/session/sqlite.d.ts +1 -1
  102. package/dist/{session-7TUHs42_.js → session-CCsKXID6.js} +6 -2
  103. package/dist/session-CCsKXID6.js.map +1 -0
  104. package/dist/{session-factory-CMwIXE-7.js → session-factory-Ct7J9CK1.js} +6 -6
  105. package/dist/{session-factory-CMwIXE-7.js.map → session-factory-Ct7J9CK1.js.map} +1 -1
  106. package/dist/session.d.ts +2 -2
  107. package/dist/session.js +1 -1
  108. package/dist/{settings-keys-CPdmkTz7.js → settings-keys-CnrJgT1T.js} +4 -4
  109. package/dist/{settings-keys-CPdmkTz7.js.map → settings-keys-CnrJgT1T.js.map} +1 -1
  110. package/dist/{shell-path-B8b3EOtK.js → shell-path-rJDa0OMv.js} +4 -4
  111. package/dist/{shell-path-B8b3EOtK.js.map → shell-path-rJDa0OMv.js.map} +1 -1
  112. package/dist/skills.d.ts +1 -1
  113. package/dist/{spaces-CyjMh5Zf.js → spaces-C_x2bvEl.js} +14 -4
  114. package/dist/spaces-C_x2bvEl.js.map +1 -0
  115. package/dist/{store-L01W0gGI.js → store-BpkWIaSH.js} +10 -30
  116. package/dist/store-BpkWIaSH.js.map +1 -0
  117. package/dist/{todo-edits-BERpCRPj.js → todo-edits-DKoxu7Ss.js} +3 -3
  118. package/dist/{todo-edits-BERpCRPj.js.map → todo-edits-DKoxu7Ss.js.map} +1 -1
  119. package/dist/tools/fetch-url.d.ts +1 -1
  120. package/dist/tools/web-search.d.ts +1 -1
  121. package/dist/{tools-pZVFIJ_K.js → tools-FHEIKGkh.js} +2 -2
  122. package/dist/{tools-pZVFIJ_K.js.map → tools-FHEIKGkh.js.map} +1 -1
  123. package/dist/tools.d.ts +1 -1
  124. package/dist/tools.js +2 -2
  125. package/dist/{transcript-anchors-jHrg-r-H.js → transcript-anchors-BcFLh3ZA.js} +10 -10
  126. package/dist/{transcript-anchors-jHrg-r-H.js.map → transcript-anchors-BcFLh3ZA.js.map} +1 -1
  127. package/dist/{transcript-cache-DXdgVuoy.js → transcript-cache-CdHKtx9y.js} +4 -4
  128. package/dist/{transcript-cache-DXdgVuoy.js.map → transcript-cache-CdHKtx9y.js.map} +1 -1
  129. package/dist/{transcript-reconcile-CPdrNbbR.js → transcript-reconcile-D_SWw6dX.js} +11 -3
  130. package/dist/transcript-reconcile-D_SWw6dX.js.map +1 -0
  131. package/dist/tui.d.ts +7 -7
  132. package/dist/tui.js +24 -23
  133. package/dist/tui.js.map +1 -1
  134. package/dist/{turn-operations-DuGtKuc_.d.ts → turn-operations-Dbk3xPKu.d.ts} +11 -3
  135. package/dist/{turn-operations-DuGtKuc_.d.ts.map → turn-operations-Dbk3xPKu.d.ts.map} +1 -1
  136. package/dist/{types-C0PJmBaO.d.ts → types-CMXR7A0b.d.ts} +2 -2
  137. package/dist/{types-C0PJmBaO.d.ts.map → types-CMXR7A0b.d.ts.map} +1 -1
  138. package/dist/types-CzmghX7C.js.map +1 -1
  139. package/dist/types.d.ts +2 -2
  140. package/dist/{url-D8KZ7Edw.js → url-D4vG7lzO.js} +2 -2
  141. package/dist/{url-D8KZ7Edw.js.map → url-D4vG7lzO.js.map} +1 -1
  142. package/dist/{version-bd3DQJkc.js → version-DOvIuU8M.js} +2 -2
  143. package/dist/{version-bd3DQJkc.js.map → version-DOvIuU8M.js.map} +1 -1
  144. package/dist/{workspace-exec-stream-CyfYgqAh.js → workspace-exec-stream-Bzxczzu2.js} +5 -5
  145. package/dist/{workspace-exec-stream-CyfYgqAh.js.map → workspace-exec-stream-Bzxczzu2.js.map} +1 -1
  146. package/dist/{write-file-QYrcDdl3.js → write-file-ChtRPdEL.js} +3 -3
  147. package/dist/{write-file-QYrcDdl3.js.map → write-file-ChtRPdEL.js.map} +1 -1
  148. package/dist/{ws-CURQ2igk.js → ws-CrD-4Mx6.js} +6 -6
  149. package/dist/{ws-CURQ2igk.js.map → ws-CrD-4Mx6.js.map} +1 -1
  150. package/dist/{ws-client-BVljYbdb.js → ws-client-Dond8vT3.js} +4 -4
  151. package/dist/{ws-client-BVljYbdb.js.map → ws-client-Dond8vT3.js.map} +1 -1
  152. package/docs/CODEMODE.md +6 -0
  153. package/docs/IPC.md +2 -0
  154. package/docs/ONBOARDING.md +1 -1
  155. package/package.json +1 -1
  156. package/dist/client-core-EEEX_u6D.d.ts.map +0 -1
  157. package/dist/dispatcher-Bv9-c6I2.js.map +0 -1
  158. package/dist/index-DyQyDLX1.d.ts.map +0 -1
  159. package/dist/oauth-7_48zR7n.js.map +0 -1
  160. package/dist/prelude-HzY_JEGq.js.map +0 -1
  161. package/dist/protocol-BIv1Yuvl.js.map +0 -1
  162. package/dist/secret-shapes-BjW4l6U0.js.map +0 -1
  163. package/dist/session-7TUHs42_.js.map +0 -1
  164. package/dist/spaces-CyjMh5Zf.js.map +0 -1
  165. package/dist/store-L01W0gGI.js.map +0 -1
  166. package/dist/transcript-reconcile-CPdrNbbR.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"extensions-BgBwogRW.js","names":["DANGEROUS_RECORD_KEYS","readJson"],"sources":["../src/extensions/bus.ts","../src/extensions/config.ts","../src/extensions/define.ts","../src/extensions/host-modules.ts","../src/extensions/host-version.ts","../src/extensions/with-timeout.ts","../src/extensions/discovery.ts","../src/chat/keybindings.ts","../src/extensions/provider-oauth.ts","../src/extensions/host-runtime.ts","../src/extensions/hud.ts","../src/extensions/mcp-host.ts","../src/extensions/register-context-shared.ts","../src/extensions/register-context-agent.ts","../src/extensions/register-context-transforms.ts","../src/extensions/register-context-ui.ts","../src/extensions/register-context.ts","../src/extensions/register.ts","../src/extensions/discover-and-build.ts","../src/extensions/install.ts","../src/extensions/prompt-bridge.ts"],"sourcesContent":["/**\n * Extension bus — the permission-gated cross-window message channel behind\n * `ctx.bus` (`'ipc'` permission). One bus per host process; per-extension\n * PRIVATE channels: a subscriber only ever receives its own extension's\n * messages, and the extension id on every envelope is stamped by the host\n * from the registration identity — never caller-supplied.\n *\n * Delivery model (mirrors the HUD holder pattern):\n * - `emit` delivers to the extension's LOCAL subscribers synchronously\n * (loopback — fully functional with zero IPC) and then fires `onEmit`\n * so the embedding host can `env.broadcast` the envelope to peers.\n * - `deliver` re-injects a remote envelope (daemon fan-out, TUI bridge)\n * into the local subscribers with `origin: 'remote'`. Unknown\n * extension ids are dropped; a per-extension last-seen `(nonce, seq)`\n * window dedupes self-echoes and multi-connection duplicates — same\n * echo-safety family as the drafts `broadcastExcept` contract.\n * Envelopes without a nonce (older peers) fall back to a seq-only key.\n */\n\nimport type { JsonValue } from '../types'\nimport type { ExtensionBusEmitOptions, ExtensionBusMessage } from './types'\nimport { Buffer } from 'node:buffer'\n\n/** Serialized-payload cap, enforced at emit AND at the daemon trust boundary. */\nexport const EXTENSION_BUS_MAX_PAYLOAD_BYTES = 64 * 1024\n\n/** Last-seen seq window per extension — bounds dedupe memory. */\nconst SEEN_WINDOW = 128\n\n/** Wire/broadcast shape of one bus message (host-stamped identity). */\nexport interface ExtensionBusEnvelope {\n /** Id of the extension that emitted the message. */\n extensionId: string\n /** Session the emitter scoped the message to, when any. */\n sessionId?: string\n /** JSON value the extension sent. */\n payload: JsonValue\n /** Sequence number of the message from its emitter. */\n seq: number\n /**\n * Per-process emitter nonce (additive). Dedupe keys on\n * `(extensionId, nonce, seq)` so two processes emitting the same seq\n * can't shadow each other — the random seq base alone is only\n * probabilistically collision-free. Absent on envelopes from older\n * peers, which fall back to the legacy `(extensionId, seq)` key.\n */\n nonce?: string\n}\n\n/** Per-extension channel claimed by a registry build (generation-scoped). */\nexport interface ExtensionBusChannel {\n /** `false` when the message was dropped (over cap / unserializable / superseded claim). */\n emit: (payload: JsonValue, opts?: ExtensionBusEmitOptions) => boolean\n /** Subscribes to messages on this channel. Returns a function that removes the subscription. */\n onMessage: (fn: (msg: ExtensionBusMessage) => void) => () => void\n /** Registry dispose — drops this generation's subscribers. */\n close: () => void\n}\n\n/** Routes messages between the instances of one extension, in this process and across windows through the host transport. */\nexport interface ExtensionBus {\n /**\n * Claim the channel for one extension id. A later claim (registry\n * rebuild / reload) supersedes: the old generation's emits no-op and\n * its subscribers are dropped, so a disposed build can't keep talking.\n */\n claim: (extensionId: string) => ExtensionBusChannel\n /**\n * Re-inject a remote envelope. Returns `false` when dropped: unknown\n * (not locally claimed) extension id, or a `(nonce, seq)` key already\n * seen for that extension (self-echo / duplicate connection).\n */\n deliver: (envelope: ExtensionBusEnvelope) => boolean\n /** Outbound hook — the host forwards envelopes to its transport (broadcast / notify). */\n onEmit: (fn: (envelope: ExtensionBusEnvelope) => void) => () => void\n}\n\n/**\n * Byte length of the JSON serialization, or `null` when not serializable.\n *\n * @param payload - Value to measure.\n */\nexport function serializedPayloadBytes(payload: unknown): number | null {\n let text: string | undefined\n try {\n text = JSON.stringify(payload)\n }\n catch {\n return null\n }\n // `undefined` / functions serialize to undefined — not a JSON value.\n if (typeof text !== 'string')\n return null\n return Buffer.byteLength(text, 'utf8')\n}\n\ninterface ChannelState {\n generation: number\n subscribers: Set<(msg: ExtensionBusMessage) => void>\n /** FIFO of recently emitted/delivered `(nonce, seq)` keys (dedupe window). */\n seenOrder: string[]\n seenSet: Set<string>\n /** Monotonic seq counter, random-based so cross-process ids rarely collide. */\n nextSeq: number\n}\n\n/**\n * Dedupe key for one envelope. Nonce-stamped envelopes key on\n * `(nonce, seq)`; nonce-less ones (older peers) on seq alone — the nonced\n * key always contains a `:` and a stringified seq never does, so the two\n * namespaces can't collide.\n */\nfunction seenKey(nonce: string | undefined, seq: number): string {\n return nonce === undefined ? String(seq) : `${nonce}:${seq}`\n}\n\n/** Creates an empty bus. Extensions claim a channel on it when their registry is built. */\nexport function createExtensionBus(): ExtensionBus {\n const channels = new Map<string, ChannelState>()\n const emitListeners = new Set<(envelope: ExtensionBusEnvelope) => void>()\n // Per-process emitter identity — stamped on every outbound envelope so\n // receivers dedupe on (extensionId, nonce, seq) instead of trusting the\n // random seq base never to collide across processes.\n const nonce = crypto.randomUUID()\n\n function markSeen(state: ChannelState, key: string): void {\n state.seenSet.add(key)\n state.seenOrder.push(key)\n while (state.seenOrder.length > SEEN_WINDOW) {\n const evicted = state.seenOrder.shift()\n if (evicted !== undefined)\n state.seenSet.delete(evicted)\n }\n }\n\n function fanOut(state: ChannelState, msg: ExtensionBusMessage): void {\n for (const fn of [...state.subscribers]) {\n try {\n fn(msg)\n }\n catch {\n // A throwing subscriber must not break the channel or its siblings.\n }\n }\n }\n\n return {\n claim(extensionId) {\n let state = channels.get(extensionId)\n if (state) {\n // New generation supersedes: prior build's subscribers are dropped,\n // but the seen-window survives so a reload can't re-deliver echoes.\n state.generation += 1\n state.subscribers = new Set()\n }\n else {\n state = {\n generation: 0,\n subscribers: new Set(),\n seenOrder: [],\n seenSet: new Set(),\n // Random base: seqs from different processes must not collide\n // inside a receiver's dedupe window (see `deliver`).\n nextSeq: Math.floor(Math.random() * 0x7FFFFFFF),\n }\n channels.set(extensionId, state)\n }\n const generation = state.generation\n const live = (): boolean => channels.get(extensionId)?.generation === generation\n\n return {\n emit(payload, opts) {\n if (!live())\n return false\n const bytes = serializedPayloadBytes(payload)\n if (bytes === null || bytes > EXTENSION_BUS_MAX_PAYLOAD_BYTES)\n return false\n const current = channels.get(extensionId)!\n const seq = current.nextSeq++\n // Record BEFORE fan-out: the broadcast copy of this very emit can\n // loop back through another connection of this same process.\n markSeen(current, seenKey(nonce, seq))\n const sessionId = typeof opts?.sessionId === 'string' ? opts.sessionId : undefined\n fanOut(current, { payload, ...(sessionId !== undefined ? { sessionId } : {}), origin: 'local' })\n const envelope: ExtensionBusEnvelope = { extensionId, ...(sessionId !== undefined ? { sessionId } : {}), payload, seq, nonce }\n for (const fn of [...emitListeners]) {\n try {\n fn(envelope)\n }\n catch {\n // Transport hook failures must never reach extension code.\n }\n }\n return true\n },\n onMessage(fn) {\n if (!live() || typeof fn !== 'function')\n return () => {}\n const current = channels.get(extensionId)!\n current.subscribers.add(fn)\n return () => {\n channels.get(extensionId)?.subscribers.delete(fn)\n }\n },\n close() {\n if (live())\n channels.get(extensionId)!.subscribers.clear()\n },\n }\n },\n\n deliver(envelope) {\n const state = channels.get(envelope.extensionId)\n // Drop envelopes for extensions not locally registered — the channel\n // is private per extension id, and an unclaimed id has no audience.\n if (!state)\n return false\n const key = seenKey(envelope.nonce, envelope.seq)\n if (state.seenSet.has(key)) {\n // Expected for self-echoes / duplicate connections, but traced so\n // an investigation into legit message loss has a lead.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/extensions] bus dedupe drop: ${envelope.extensionId} key=${key}\\n`)\n return false\n }\n markSeen(state, key)\n fanOut(state, {\n payload: envelope.payload,\n ...(envelope.sessionId !== undefined ? { sessionId: envelope.sessionId } : {}),\n origin: 'remote',\n })\n return true\n },\n\n onEmit(fn) {\n emitListeners.add(fn)\n return () => {\n emitListeners.delete(fn)\n }\n },\n }\n}\n","/**\n * Typed, extension-contributed configuration.\n *\n * Extensions declare config CATEGORIES and typed config ITEMS during\n * `setup(ctx)` via `ctx.registerConfigCategory` / `ctx.registerConfigItem`.\n * Values are persisted in the user's `Settings.extensionConfig` bag\n * (`extName → itemKey → value`) and read back — default-first, with\n * graceful degradation on missing / malformed values — through a single\n * resolver ({@link resolveExtensionConfigValue}). The same resolver\n * powers `ctx.config.get(key)` inside the extension (hooks / tools) and\n * both Settings UIs (TUI + GUI).\n *\n * Design intent:\n * - **One chokepoint for graceful degradation.** Every read — host,\n * extension, TUI render, GUI render — funnels through\n * {@link resolveExtensionConfigValue}; a value that's missing, the\n * wrong type, out of range, off the enum, or failing the author's\n * `validate` collapses to the item's `default`. The UI therefore\n * never has to special-case a broken `state.json` / DB row.\n * - **Namespacing by extension.** Items are keyed `extName → itemKey`\n * so two extensions can both ship a `verbose` knob without colliding.\n * Categories are a flat global registry (an extension may add items\n * to another extension's category, or to its own implicit category\n * whose id is the extension name).\n * - **Wire-safe schema.** The optional main-side `validate` callback is\n * the only non-serializable field; {@link toWireConfigSchema} drops it\n * so the schema crosses an IPC boundary (the GUI) cleanly.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { ExtensionRegistry } from './types'\n\n// ---------------------------------------------------------------------------\n// Schema types\n// ---------------------------------------------------------------------------\n\n/** Concrete value a config item can hold. */\nexport type ExtensionConfigValue = boolean | number | string\n\n/** Where a config value should be persisted. */\nexport type ExtensionConfigScope = 'user' | 'project' | 'session'\n/** How a host applies a changed config value: read live, rebuild the agent, or restart. */\nexport type ExtensionConfigChangeBehavior = 'live' | 'agent-rebuild' | 'restart'\n\n/** Fields shared by every config-item variant. */\nexport interface ExtensionConfigItemBase {\n /**\n * Stable key, unique within the declaring extension. Used as the\n * persistence key (`extensionConfig[extName][key]`) and the\n * `ctx.config.get(key)` lookup — pick a stable identifier.\n */\n key: string\n /** Short display label rendered in the Settings UIs. */\n label: string\n /** Longer one-line gloss shown under the label. */\n description?: string\n /**\n * Optional main-side validator, run at READ time AFTER the built-in\n * type / range / enum / pattern checks (both must pass). Throwing is\n * treated as \"invalid\" — the read degrades to the item's `default`. It\n * is **never invoked at registration** (declaring an item has no side\n * effects) and is **never serialized over IPC** ({@link toWireConfigSchema}\n * strips it), so it only runs in-process (TUI host + extension reads);\n * the GUI renderer is optimistic and relies on the authoritative core read.\n */\n validate?: (v: unknown) => boolean\n /**\n * Desired persistence scope. Current stores still persist through the\n * existing per-extension settings bag unless a host implements scoped writes;\n * this metadata lets UIs and future stores present the right ownership.\n */\n scope?: ExtensionConfigScope\n /** Whether changing this value requires rebuilding runtime objects. */\n requiresRestart?: boolean\n /** How hosts should apply a changed value. Defaults to live reads through `ctx.config.get`. */\n onChange?: ExtensionConfigChangeBehavior\n}\n\n/** A boolean config item — rendered as a toggle. */\nexport interface ExtensionConfigBooleanItem extends ExtensionConfigItemBase {\n /** Discriminant for a boolean item. */\n type: 'boolean'\n /** Value used until the user sets one. */\n default: boolean\n}\n\n/**\n * A numeric config item — rendered as a free input row, clamped to `[min, max]`\n * and snapped to `step`. The `default` must itself lie within `[min, max]` or\n * the item is rejected at registration.\n */\nexport interface ExtensionConfigNumberItem extends ExtensionConfigItemBase {\n /** Discriminant for a number item. */\n type: 'number'\n /** Value used until the user sets one. Must lie within `[min, max]`. */\n default: number\n /** Smallest accepted value. */\n min?: number\n /** Largest accepted value. */\n max?: number\n /** Increment values snap to. */\n step?: number\n}\n\n/** A string config item — rendered as a free input row (optionally `multiline`), validated against `pattern`. */\nexport interface ExtensionConfigStringItem extends ExtensionConfigItemBase {\n /** Discriminant for a string item. */\n type: 'string'\n /** Value used until the user sets one. */\n default: string\n /**\n * Anchored or unanchored JS-regex source gating user-entered values at read\n * time. An invalid pattern is ignored (treated as \"no constraint\"). The\n * pattern is intentionally NOT applied to the `default`, so an empty / unset\n * default (`default: ''`) for an optional patterned field is valid.\n */\n pattern?: string\n /** Renders a multi-line editor instead of a single-line input. */\n multiline?: boolean\n /**\n * Treat the value as a credential: Settings UIs render it masked\n * (`••••••••`, last 4 characters kept for recognition when the value is\n * longer than 8) and seed the editor\n * EMPTY rather than with the stored value, so the secret never appears on\n * screen or in a screen share.\n *\n * Presentation only — the value still lives in the plain settings bag, so\n * this is for \"don't shoulder-surf my service token\", not for secrets that\n * warrant the credential store. Ignored for `multiline` items.\n */\n secret?: boolean\n}\n\n/**\n * An enum config item — rendered as a cycling choice row. The `default` must be\n * one of the `options` values or the item is rejected at registration.\n */\nexport interface ExtensionConfigEnumItem extends ExtensionConfigItemBase {\n /** Discriminant for an enum item. */\n type: 'enum'\n /** Value used until the user picks one. Must be one of `options`. */\n default: string\n /** Choices the user can pick from. */\n options: readonly { value: string, label: string }[]\n}\n\n/**\n * A model-id config item — rendered as an inline searchable picker over the\n * host's credentialed model catalog (the same interaction grammar as the\n * ctrl+m cross-provider picker: type to filter, `↵` picks — or toggles, in\n * `multi` mode, where `esc` applies the selection).\n *\n * The persisted value is a plain model id, or a comma-separated id list\n * when `multi` — a string either way, so {@link ExtensionConfigValue} and\n * every settings store stay unchanged, and hosts without a catalog\n * (headless, no authed providers) degrade to the free string input.\n * Deliberately catalog-UNvalidated at read time: a stored id whose\n * provider is temporarily unauthed must survive, not silently reset to\n * the default — catalog validation is the consuming extension's call.\n */\nexport interface ExtensionConfigModelItem extends ExtensionConfigItemBase {\n /** Discriminant for a model item. */\n type: 'model'\n /** Value used until the user picks one: a model id, or a comma-separated list when `multi`. */\n default: string\n /** Allow picking MORE than one model (comma-separated persisted value). */\n multi?: boolean\n}\n\n/**\n * What a config ACTION reports back to the host after running. Actions\n * cannot write settings directly — there is deliberately no `config.set`\n * surface for extensions — so a successful run RETURNS the values it wants\n * persisted and the host writes them through its own validated settings\n * path ({@link sanitizeExtensionConfigActionPatch} gates the patch).\n */\nexport interface ExtensionConfigActionResult {\n /** One-line feedback shown in the Settings UI (e.g. \"proxy running at …\"). */\n message?: string\n /**\n * Config values of the DECLARING extension to persist on success, keyed\n * by item key. Entries naming unknown keys, action items, or values that\n * fail the item's schema are dropped (not errors) — a buggy action can\n * degrade but never corrupt the settings bag.\n */\n config?: Record<string, ExtensionConfigValue>\n}\n\n/**\n * An imperative config item — rendered as a \"press ↵ to run\" row instead\n * of a value editor. `run` executes in the extension's own process (it is\n * never serialized — {@link toWireConfigSchema} omits action items\n * entirely), so it can close over the extension's `setup(ctx)` scope.\n *\n * Contract for `run`:\n * - Resolve with an {@link ExtensionConfigActionResult} (or `void`) on\n * success — `message` becomes the row's feedback line, `config`\n * values are persisted by the host.\n * - Throw / reject on failure — the error message is shown on the row.\n * - Bound your own runtime: the host serializes actions (one in flight\n * per modal) but imposes no timeout, so a hanging `run` holds the\n * \"running…\" state until it settles.\n *\n * Actions hold no persisted value: `ctx.config.get(key)` returns\n * `undefined` for an action key and the resolver never yields a value.\n */\nexport interface ExtensionConfigActionItem extends Pick<ExtensionConfigItemBase, 'key' | 'label' | 'description'> {\n /** Discriminant for an action item. */\n type: 'action'\n /** Runs the action. May return values to persist. Throwing shows the error on the row. */\n run: () => ExtensionConfigActionResult | void | Promise<ExtensionConfigActionResult | void>\n /**\n * Optional passive probe — called by the host when the settings panel\n * surfaces the row (and re-called after any action of the panel\n * settles), so the row can show live state (\"running at …\") WITHOUT\n * the user pressing ↵. Contract:\n *\n * - Resolve an {@link ExtensionConfigActionStatus} to show it, or\n * `undefined` for \"no status\" (e.g. the probed tool isn't\n * installed — stay quiet rather than nag).\n * - Throwing is treated as `undefined` — probes must be safe to call\n * at any time and MUST NOT mutate anything.\n * - Be fast: the TUI caps each probe at 3 seconds and discards\n * late results.\n */\n status?: () => ExtensionConfigActionStatus | undefined | Promise<ExtensionConfigActionStatus | undefined>\n}\n\n/**\n * Result of an action's passive `status` probe. `state` drives the row\n * color (`ok` accent / `off` mute / `error` error); `message` is the\n * one-line text rendered in the row's value slot.\n */\nexport interface ExtensionConfigActionStatus {\n /** Row state: `ok`, `off` or `error`. */\n state: 'ok' | 'off' | 'error'\n /** One-line text shown in the row's value slot. */\n message: string\n}\n\n/**\n * The value-holding config items — everything except `action`. Functions\n * that produce or validate persisted VALUES take this narrower union;\n * {@link ExtensionConfigItem} is the full declarable set.\n */\nexport type ExtensionConfigValueItem\n = | ExtensionConfigBooleanItem\n | ExtensionConfigNumberItem\n | ExtensionConfigStringItem\n | ExtensionConfigEnumItem\n | ExtensionConfigModelItem\n\n/**\n * Discriminated union of the typed config items an extension can declare.\n * The `type` tag drives both validation ({@link resolveExtensionConfigValue})\n * and rendering (toggle / choice / input / action row).\n */\nexport type ExtensionConfigItem = ExtensionConfigValueItem | ExtensionConfigActionItem\n\n/**\n * A config category — a labelled grouping the Settings UIs render as a\n * sub-header. Declared via `ctx.registerConfigCategory`. An item that\n * names no category lands in its extension's implicit category (id =\n * extension name); register a category with that id to give the implicit\n * grouping a friendlier label.\n */\nexport interface ExtensionConfigCategory {\n /** Category id. */\n id: string\n /** Sub-header text. */\n label: string\n /** One-line gloss shown under the label. */\n description?: string\n}\n\n/**\n * A config item as STORED on the registry / contributions — the authored\n * item plus the resolved `category` id it belongs to. The owning\n * extension is implied by the namespacing key on\n * {@link ExtensionRegistry.configItems}.\n */\nexport type StoredExtensionConfigItem = ExtensionConfigItem & { category: string }\n\n/** The `ctx.config` surface handed to extensions for reading their own config. */\nexport interface ExtensionConfigSurface {\n /**\n * Read the current value of one of THIS extension's config items,\n * default-first. Returns the persisted value when present + valid, the\n * item's `default` when missing / malformed, or `undefined` when `key`\n * was never registered via `ctx.registerConfigItem`.\n *\n * Reads through a host-supplied live settings getter: during `setup`\n * (before the host wires live settings) it returns the default (or the\n * boot snapshot); inside hooks / tools (post-boot) it returns the live\n * value.\n */\n get: (key: string) => ExtensionConfigValue | undefined\n}\n\n// ---------------------------------------------------------------------------\n// Authored-schema validation (registration-time guard)\n// ---------------------------------------------------------------------------\n\n/**\n * Exhaustiveness guard for the `ExtensionConfigItem` union: a future item\n * `type` left unhandled is a COMPILE error (the `never` parameter), while at\n * runtime an unrecognized / missing type is treated as invalid (`false`) —\n * malformed items reach here via `as never` casts from untyped `.js` extensions.\n */\nfunction unreachableInvalidItem(_item: never): false {\n return false\n}\n\n/**\n * Whether an authored item is well-formed for its declared `type` — used\n * by `ctx.registerConfigItem` to drop malformed declarations (wrong /\n * missing `default`, an `enum` without `options`) before they reach the\n * registry. Cheaper than letting a broken item ride to render time.\n *\n * @param item - Item declared by an extension.\n */\nexport function isValidExtensionConfigItem(item: ExtensionConfigItem): boolean {\n switch (item.type) {\n case 'boolean':\n return typeof item.default === 'boolean'\n case 'number':\n // The default must be finite AND within the declared range, so the\n // resolver never surfaces an out-of-range value (the module's \"every read\n // yields a valid value\" invariant). `step` is a UI affordance, not a hard\n // constraint, so it is NOT enforced on the default.\n return typeof item.default === 'number'\n && Number.isFinite(item.default)\n && (item.min === undefined || item.default >= item.min)\n && (item.max === undefined || item.default <= item.max)\n case 'string':\n // A string `pattern` is intentionally NOT enforced against the default:\n // an empty / unset default (`default: ''`) for an optional patterned\n // field is a legitimate idiom. The pattern still gates user-entered\n // values at read time via `resolveExtensionConfigValue`. The author\n // `validate` is likewise deferred to read time, so registration stays\n // side-effect-free (no `validate(default)` call at register time).\n return typeof item.default === 'string'\n case 'enum':\n // The default MUST be one of the options — otherwise the picker can't\n // represent it and the resolver would surface an off-list value.\n return typeof item.default === 'string'\n && Array.isArray(item.options)\n && item.options.length > 0\n && item.options.every(o => o && typeof o.value === 'string' && typeof o.label === 'string')\n && item.options.some(o => o.value === item.default)\n case 'model':\n // Ids are free-form strings (catalogs are host- and auth-dependent),\n // so only the shape is checked — see the type's docstring for why\n // values are never catalog-validated.\n return typeof item.default === 'string'\n case 'action':\n // No `default` — requires a callable `run`; `status` is optional\n // but must be callable when present.\n return typeof item.run === 'function'\n && (item.status === undefined || typeof item.status === 'function')\n default:\n return unreachableInvalidItem(item)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation + resolution — the single graceful-degradation chokepoint\n// ---------------------------------------------------------------------------\n\n/** Build a RegExp from `pattern`, returning `null` when the source is invalid. */\nfunction safeRegExp(pattern: string): RegExp | null {\n try {\n return new RegExp(pattern)\n }\n catch {\n return null\n }\n}\n\n/**\n * Whether `value` is a valid concrete value for `item` — type match plus\n * the item's constraints (number range, enum membership, string pattern)\n * and the optional author `validate`. `step` is intentionally NOT enforced\n * here (it's a UI affordance, not a hard constraint — a persisted value or\n * default that doesn't align to the step is still honored).\n *\n * @param item - Item that defines the type and constraints.\n * @param value - Candidate value.\n */\nexport function isValidExtensionConfigValue(item: ExtensionConfigItem, value: unknown): value is ExtensionConfigValue {\n switch (item.type) {\n case 'boolean':\n if (typeof value !== 'boolean')\n return false\n break\n case 'number':\n if (typeof value !== 'number' || !Number.isFinite(value))\n return false\n if (item.min !== undefined && value < item.min)\n return false\n if (item.max !== undefined && value > item.max)\n return false\n break\n case 'string': {\n if (typeof value !== 'string')\n return false\n if (item.pattern) {\n const re = safeRegExp(item.pattern)\n // A broken author pattern is treated as \"no constraint\" rather\n // than rejecting every value the user types.\n if (re && !re.test(value))\n return false\n }\n break\n }\n case 'enum':\n if (typeof value !== 'string' || !item.options.some(o => o.value === value))\n return false\n break\n case 'model':\n if (typeof value !== 'string')\n return false\n break\n case 'action':\n // Actions hold no persisted value — nothing is ever valid for them.\n return false\n default:\n return unreachableInvalidItem(item)\n }\n if (item.validate) {\n try {\n return item.validate(value)\n }\n catch {\n return false\n }\n }\n return true\n}\n\n/**\n * Resolve the effective value of a config item from its persisted value.\n * THE single place graceful degradation lives: a `persisted` that's\n * `undefined` (never set) OR fails {@link isValidExtensionConfigValue}\n * collapses to `item.default`. Action items hold no value and resolve to\n * `undefined` — the overloads keep value-item call sites exact.\n *\n * @param item - Item that defines the type and constraints.\n * @param persisted - Stored value, if any.\n */\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigValueItem,\n persisted: unknown,\n): ExtensionConfigValue\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigItem,\n persisted: unknown,\n): ExtensionConfigValue | undefined\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigItem,\n persisted: unknown,\n): ExtensionConfigValue | undefined {\n if (item.type === 'action')\n return undefined\n if (persisted === undefined)\n return item.default\n if (!isValidExtensionConfigValue(item, persisted))\n return item.default\n return persisted\n}\n\n/**\n * Clamp a number to the item's `[min, max]` and snap to `step` (when set).\n * Used by the input-row commit paths so a user-typed value is coerced into\n * range before it's persisted.\n *\n * @param item - Number item that supplies `min`, `max` and `step`.\n * @param n - Value to clamp.\n */\nexport function clampExtensionConfigNumber(item: ExtensionConfigNumberItem, n: number): number {\n let v = n\n if (item.min !== undefined)\n v = Math.max(item.min, v)\n if (item.max !== undefined)\n v = Math.min(item.max, v)\n if (item.step !== undefined && item.step > 0) {\n const base = item.min ?? 0\n v = base + Math.round((v - base) / item.step) * item.step\n // Snapping can nudge a boundary value out of range — re-clamp.\n if (item.max !== undefined)\n v = Math.min(item.max, v)\n if (item.min !== undefined)\n v = Math.max(item.min, v)\n }\n return v\n}\n\n/**\n * Coerce a raw input string (from a free number / string input row) into a\n * valid persisted value, or `undefined` when it can't be made valid.\n * Numbers are parsed + clamped; strings are taken verbatim and checked\n * against the pattern; enum / boolean items aren't edited via free input\n * (they use choice / toggle rows) so they return `undefined` here.\n *\n * @param item - Item the input belongs to.\n * @param raw - Text the user typed.\n */\nexport function coerceExtensionConfigInput(\n item: ExtensionConfigItem,\n raw: string,\n): ExtensionConfigValue | undefined {\n switch (item.type) {\n case 'number': {\n const n = Number(raw.trim())\n if (!Number.isFinite(n))\n return undefined\n const clamped = clampExtensionConfigNumber(item, n)\n return isValidExtensionConfigValue(item, clamped) ? clamped : undefined\n }\n case 'string':\n return isValidExtensionConfigValue(item, raw) ? raw : undefined\n case 'model':\n // Fallback path for hosts without a catalog — the value is typed as\n // free text (a comma-separated id list when `multi`).\n return isValidExtensionConfigValue(item, raw) ? raw : undefined\n default:\n // Enum / boolean use choice / toggle rows; actions have no value.\n return undefined\n }\n}\n\n/**\n * Convenience read against a merged {@link ExtensionRegistry} + the user's\n * `Settings`. Looks up the item schema (`extName → key`), reads the\n * persisted value out of `settings.extensionConfig`, and runs it through\n * {@link resolveExtensionConfigValue}. Returns `undefined` only when the\n * `(extName, key)` pair names no registered item.\n *\n * @param registry - Merged registry holding the declared items.\n * @param settings - User settings holding the persisted values.\n * @param extName - Name of the declaring extension.\n * @param key - Item key.\n */\nexport function resolveExtensionConfig(\n registry: Pick<ExtensionRegistry, 'configItems'>,\n settings: Pick<Settings, 'extensionConfig'> | undefined,\n extName: string,\n key: string,\n): ExtensionConfigValue | undefined {\n const item = registry.configItems[extName]?.[key]\n if (!item)\n return undefined\n return resolveExtensionConfigValue(item, settings?.extensionConfig?.[extName]?.[key])\n}\n\n/**\n * Gate the `config` patch an action returned before the host persists it.\n * Per-entry, an item is accepted only when it names a registered VALUE\n * item of the declaring extension and passes that item's schema\n * ({@link isValidExtensionConfigValue}); everything else lands in\n * `rejected` for logging. Pure — the host applies `accepted` through its\n * own settings write path.\n *\n * @param items - Declared items of the extension, keyed by item key.\n * @param patch - Values the action returned.\n */\nexport function sanitizeExtensionConfigActionPatch(\n items: Readonly<Record<string, StoredExtensionConfigItem>> | undefined,\n patch: Readonly<Record<string, ExtensionConfigValue>> | undefined,\n): { accepted: Record<string, ExtensionConfigValue>, rejected: string[] } {\n const accepted: Record<string, ExtensionConfigValue> = {}\n const rejected: string[] = []\n if (!patch || typeof patch !== 'object')\n return { accepted, rejected }\n for (const [key, value] of Object.entries(patch)) {\n const item = items?.[key]\n if (!item || item.type === 'action' || !isValidExtensionConfigValue(item, value)) {\n rejected.push(key)\n continue\n }\n accepted[key] = value\n }\n return { accepted, rejected }\n}\n\n// ---------------------------------------------------------------------------\n// Wire-safe serialization (GUI IPC)\n// ---------------------------------------------------------------------------\n\n/** Fields the wire shape adds on top of the authored item (minus `validate`). */\ninterface WireConfigExtras {\n category: string\n extension: string\n}\n\n/** A config item flattened for the wire — carries `category` + `extension`, drops `validate`. */\nexport type WireExtensionConfigItem\n = | (Omit<ExtensionConfigBooleanItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigNumberItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigStringItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigEnumItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigModelItem, 'validate'> & WireConfigExtras)\n\n/** The full extension-config schema in IPC-safe form. */\nexport interface WireExtensionConfigSchema {\n /** Declared categories. */\n categories: readonly ExtensionConfigCategory[]\n /** Declared items, each tagged with its extension and category. */\n items: readonly WireExtensionConfigItem[]\n}\n\n/**\n * Project the merged registry's config schema into a wire-safe shape:\n * categories as a flat array, items flattened to a single list carrying\n * their `extension` + `category`, with the non-serializable `validate`\n * callback stripped. Action items are omitted entirely — their `run`\n * callback only exists in the main process, so a remote renderer (the\n * GUI) has nothing it could do with the row. Pure — factored out so its\n * serialization is unit testable without an Electron / IPC harness.\n *\n * @param registry - Merged registry holding the declared categories and items.\n */\nexport function toWireConfigSchema(\n registry: Pick<ExtensionRegistry, 'configCategories' | 'configItems'>,\n): WireExtensionConfigSchema {\n const items: WireExtensionConfigItem[] = []\n for (const [extension, byKey] of Object.entries(registry.configItems)) {\n for (const stored of Object.values(byKey)) {\n if (stored.type === 'action')\n continue\n // Drop the main-only `validate` callback; keep everything else.\n const { validate: _validate, ...rest } = stored\n items.push({ ...rest, extension } as WireExtensionConfigItem)\n }\n }\n return {\n categories: Object.values(registry.configCategories),\n items,\n }\n}\n","/**\n * `defineExtension` — identity helper for authoring extensions.\n *\n * Mirrors {@link definePreset} / {@link defineSkill}: returns its\n * argument verbatim, but its presence in the source gives TypeScript\n * an inference anchor and the import surface a stable name.\n *\n * Authoring example (drop in `.zidane/extensions/hello/index.ts`):\n *\n * ```ts\n * import { defineExtension } from 'zidane'\n *\n * export default defineExtension({\n * name: 'hello',\n * version: '0.1.0',\n * setup(ctx) {\n * ctx.registerHook('tool:after', ({ name }) =>\n * ctx.logger.info('tool ran', { name }))\n * },\n * })\n * ```\n *\n * No validation runs here — that lives in the loader so a malformed\n * definition surfaces in the Settings panel rather than throwing\n * during a module's top-level evaluation.\n *\n * **Zero-import alternative.** The loader accepts a plain object too,\n * so extensions running against the compiled `zidane-tui` binary\n * (which doesn't expose `node_modules/zidane` to dynamic imports\n * outside the bundle) can skip the import entirely:\n *\n * ```ts\n * // .zidane/extensions/hello/index.ts — no imports needed\n * export default {\n * name: 'hello',\n * setup(ctx) {\n * ctx.registerHook('tool:after', e => ctx.logger.info('ran', e))\n * },\n * }\n * ```\n *\n * You lose TypeScript inference on `ctx` this way; pair with\n * `/// <reference types=\"zidane\" />` or an `import type { ExtensionContext } from 'zidane'`\n * (type-only imports don't need a runtime resolution) to recover it.\n */\n\nimport type { ExtensionDefinition } from './types'\n\n/**\n * Returns `definition` unchanged. It gives TypeScript an inference anchor so `ctx` in `setup` is typed.\n * Validation happens in the loader, not here.\n *\n * @param definition - The extension: its `name` and a `setup(ctx)` function, plus optional manifest fields.\n * @returns The same `definition`.\n * @example\n * ```ts\n * import { defineExtension } from 'zidane'\n *\n * export default defineExtension({\n * name: 'hello',\n * setup(ctx) {\n * ctx.registerHook('tool:after', ({ name }) => ctx.logger.info('tool ran', { name }))\n * },\n * })\n * ```\n */\nexport function defineExtension(definition: ExtensionDefinition): ExtensionDefinition {\n return definition\n}\n","/**\n * Host-module sharing for extension imports — what makes \"an extension\n * loaded into the app uses the app's modules\" ACTUALLY true.\n *\n * By default it is not: a dynamically imported extension file resolves\n * its bare imports (`react`, `@opentui/react`, …) by walking\n * node_modules from the EXTENSION file's directory — not from the host.\n * Depending on where the extension lives, `import 'react'` finds\n * nothing, or worse, a SECOND react copy — elements still render (host\n * tags are strings and the element symbol is a global `Symbol.for`),\n * but hooks hit a null dispatcher and throw \"Invalid hook call\", which\n * surfaces as a failed renderer. This bit the dev loop itself: the dev\n * example resolved `tui/node_modules/react` while the reconciler ran\n * the root copy.\n *\n * The fix is interception, not resolution: {@link registerExtensionHostModules}\n * registers a Bun virtual-module plugin that maps the given specifiers\n * to the host's LIVE module objects (`loader: 'object'` — the actual\n * instances, no paths involved, so it works identically for installed\n * extensions and inside compiled binaries). Extensions then write plain\n * `import { useState } from 'react'` and get the host's react, period.\n *\n * Layering: this module is renderer-neutral (no react import here) —\n * the HOST hands its modules in. The TUI registers react + OpenTUI at\n * startup, before the boot registry's discovery imports run; headless\n * hosts simply don't register UI modules (a UI-importing extension then\n * fails to load there). First registration per\n * specifier wins; later calls for the same specifier are no-ops.\n *\n * Node hosts (the Electron GUI) have no `Bun.plugin`: the same map is\n * kept here and handed to the Node extension loader as jiti\n * `virtualModules` (see {@link resolveExtensionHostModules}).\n */\n\n/** Bun's plugin surface, narrowed from `globalThis` (same pattern as `contexts/glob.ts`) — this module must typecheck and no-op cleanly on non-Bun runtimes. */\ninterface BunPluginGlobal {\n plugin: (plugin: { name: string, setup: (build: { module: (specifier: string, cb: () => { exports: Record<string, unknown>, loader: 'object' } | Promise<{ exports: Record<string, unknown>, loader: 'object' }>) => void }) => void }) => void\n}\n\n/**\n * A host module, eager or lazy. Lazy (`() => Promise<module>`) exists for\n * self-referencing hosts: `zidane/tui` maps to ITSELF, which can only be\n * expressed as a deferred dynamic import (the module is still evaluating\n * when it registers).\n */\nexport type HostModuleSource = object | (() => Promise<object>)\n\n/** Specifier → source, first registration wins. Bounded by the distinct specifiers hosts register. */\nconst sources = new Map<string, HostModuleSource>()\n\n/** Resolved exports for every registered specifier; reset when a registration adds one. */\nlet resolved: Promise<Record<string, Record<string, unknown>>> | undefined\n\nfunction toExports(mod: object): Record<string, unknown> {\n // Spread the namespace into a plain record; module namespaces don't\n // expose `default` enumerably in every case, so carry it explicitly\n // (falling back to the namespace itself for consumers doing\n // `import React from 'react'` against a CJS interop shape).\n return { ...(mod as Record<string, unknown>), default: (mod as { default?: unknown }).default ?? mod }\n}\n\n/**\n * Map bare import specifiers to the host's live module objects for every\n * subsequently-imported module (extensions included). No-op outside Bun\n * and for already-registered specifiers. Call BEFORE the first extension\n * import (the TUI does, at startup).\n *\n * @param modules - Bare import specifiers mapped to a module object, or to a function that resolves one.\n * @example\n * ```ts\n * import * as React from 'react'\n * import { registerExtensionHostModules } from 'zidane/extensions'\n *\n * registerExtensionHostModules({ react: React })\n * ```\n */\nexport function registerExtensionHostModules(modules: Record<string, HostModuleSource>): void {\n const fresh = Object.entries(modules).filter(([specifier]) => !sources.has(specifier))\n if (fresh.length === 0)\n return\n for (const [specifier, source] of fresh)\n sources.set(specifier, source)\n resolved = undefined\n const bun = (globalThis as { Bun?: Partial<BunPluginGlobal> }).Bun\n if (typeof bun?.plugin !== 'function')\n return\n bun.plugin({\n name: `zidane-host-modules(${fresh.map(([s]) => s).join(',')})`,\n setup(build) {\n for (const [specifier, source] of fresh) {\n build.module(specifier, async () => {\n const mod = typeof source === 'function' ? await source() : source\n return { exports: toExports(mod), loader: 'object' as const }\n })\n }\n },\n })\n}\n\n/**\n * Every registered host module, resolved to its exports record (lazy\n * sources awaited once and memoized). The Node extension loader passes\n * this as jiti `virtualModules`; Bun hosts never need it. A rejected\n * lazy source clears the memo so the next load retries.\n */\nexport function resolveExtensionHostModules(): Promise<Record<string, Record<string, unknown>>> {\n const pending = resolved ?? (async () => {\n const entries = await Promise.all([...sources].map(async ([specifier, source]) =>\n [specifier, toExports(typeof source === 'function' ? await source() : source)] as const))\n return Object.fromEntries(entries)\n })()\n resolved = pending\n pending.catch(() => {\n if (resolved === pending)\n resolved = undefined\n })\n return pending\n}\n","/**\n * The zidane an extension needs, checked before it loads.\n *\n * Extensions declare it the npm way, `peerDependencies.zidane` in their\n * package.json. Discovery and install read it BEFORE importing the module, so\n * an extension written against a newer zidane fails with \"needs zidane\n * >=X\" on its Settings row instead of a TypeError from an export the host\n * doesn't have yet. A zidane newer than the range only warns: the extension\n * still loads.\n *\n * A source checkout (zidane running from its .ts sources) skips the check:\n * its package.json version lags the unreleased code it runs, so the\n * extensions developed alongside it would read as too new.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { ZIDANE_VERSION } from '../version'\n\n/** zidane running from its TypeScript sources, not a build or a compiled binary. */\nconst RUNNING_FROM_SOURCE = import.meta.url.endsWith('.ts')\n && !import.meta.url.includes('$bunfs')\n && !import.meta.url.includes('~BUN')\n\ntype Version = [number, number, number]\n\nfunction parseVersion(text: string): Version | null {\n const m = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:[-+].*)?$/.exec(text.trim())\n return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null\n}\n\nfunction compare(a: Version, b: Version): number {\n return a[0] - b[0] || a[1] - b[1] || a[2] - b[2]\n}\n\n/** `8`, `8.1`, `8.x`, `8.1.*`: the parts given, and how many. */\nfunction parsePartial(text: string): { parts: number[] } | null {\n const m = /^v?(\\d+)(?:\\.(\\d+|[x*]))?(?:\\.(\\d+|[x*]))?(?:[-+].*)?$/i.exec(text.trim())\n if (!m)\n return null\n const parts: number[] = []\n for (const part of [m[1], m[2], m[3]]) {\n if (part === undefined || /^[x*]$/i.test(part))\n break\n parts.push(Number(part))\n }\n return { parts }\n}\n\ninterface Comparator {\n /** `>=`, `>`, `<=`, `<`, `^`, `~`, `=` or `''` (a bare version or partial). */\n op: string\n /** The version given, missing parts as 0. */\n floor: Version\n /** How many parts were given (0 for `*` / `x`). */\n given: number\n}\n\n/** One comparator (`>=8.1.0`, `^8.1`, `~8.1.2`, `8.x`, `*`), or `undefined` when it isn't one. */\nfunction parseComparator(comparator: string): Comparator | undefined {\n if (comparator === '*' || /^x$/i.test(comparator))\n return { op: '', floor: [0, 0, 0], given: 0 }\n const m = /^([<>]=?|[=^~])?\\s*(\\S.*)$/.exec(comparator)\n if (!m)\n return undefined\n const [, op = '', rest = ''] = m\n const partial = parsePartial(rest)\n if (!partial)\n return undefined\n const [major = 0, minor = 0, patch = 0] = partial.parts\n return { op, floor: [major, minor, patch], given: partial.parts.length }\n}\n\n/** `version` is at or above the lowest version `comparator` allows (always true for `<`, `<=` and `*`). */\nfunction meetsFloor(version: Version, comparator: Comparator): boolean {\n if (comparator.op === '<' || comparator.op === '<=')\n return true\n if (comparator.op === '>')\n return satisfiesComparator(version, comparator)\n return compare(version, comparator.floor) >= 0\n}\n\nfunction satisfiesComparator(version: Version, { op, floor, given }: Comparator): boolean {\n const [major, minor, patch] = floor\n switch (op) {\n case '>=': return compare(version, floor) >= 0\n case '>': return given === 3 ? compare(version, floor) > 0 : compare(version, [given === 1 ? major + 1 : major, given === 1 ? 0 : minor + 1, 0]) >= 0\n case '<=': return given === 3 ? compare(version, floor) <= 0 : compare(version, [given === 1 ? major + 1 : major, given === 1 ? 0 : minor + 1, 0]) < 0\n case '<': return compare(version, floor) < 0\n case '^': {\n const ceiling: Version = major > 0 || given === 1 ? [major + 1, 0, 0] : minor > 0 || given === 2 ? [0, minor + 1, 0] : [0, 0, patch + 1]\n return compare(version, floor) >= 0 && compare(version, ceiling) < 0\n }\n case '~': {\n const ceiling: Version = given === 1 ? [major + 1, 0, 0] : [major, minor + 1, 0]\n return compare(version, floor) >= 0 && compare(version, ceiling) < 0\n }\n default: {\n // `=8.1.0` / `8.1.0` exactly; `8.1` / `8.x` as the range they name.\n if (given === 3)\n return compare(version, floor) === 0\n const ceiling: Version = given === 1 ? [major + 1, 0, 0] : given === 2 ? [major, minor + 1, 0] : [Infinity, 0, 0]\n return compare(version, floor) >= 0 && compare(version, ceiling) < 0\n }\n }\n}\n\n/**\n * Whether `version` satisfies an npm-style `range`: comparators joined by\n * spaces (all must hold) and `||` (any alternative), with caret, tilde,\n * x-ranges and hyphen-free partials. Pre-release tags are ignored, like the\n * daemon's version fence. `undefined` when either side doesn't parse.\n *\n * @param version - A version, e.g. `8.1.0`.\n * @param range - A range, e.g. `>=8.1.0`, `^8.1 || ^9`.\n */\nexport function satisfiesVersionRange(version: string, range: string): boolean | undefined {\n const v = parseVersion(version)\n const alternatives = parseRange(range)\n if (!v || alternatives.length === 0)\n return undefined\n return alternatives.some(comparators => comparators.every(c => satisfiesComparator(v, c)))\n}\n\n/**\n * Whether `version` is older than every alternative of `range` allows: below\n * a floor (`>=`, `>`, `^`, `~`, `=` or a bare version) in each one. A\n * version that fails only an upper bound (`^7`, `~8.0.0`, `<9`) is newer\n * than the range, not below it. `undefined` when either side doesn't parse.\n *\n * @param version - A version, e.g. `8.1.0`.\n * @param range - A range, e.g. `>=8.1.0`, `^8.1 || ^9`.\n */\nexport function belowVersionRange(version: string, range: string): boolean | undefined {\n const v = parseVersion(version)\n const alternatives = parseRange(range)\n if (!v || alternatives.length === 0)\n return undefined\n return alternatives.every(comparators => comparators.some(c => !meetsFloor(v, c)))\n}\n\n/** The `||` alternatives of `range` that parse, each as its comparators. */\nfunction parseRange(range: string): Comparator[][] {\n const alternatives: Comparator[][] = []\n for (const alternative of range.split('||')) {\n // `>= 8.1.0` → `>=8.1.0`: an operator binds to the version after it.\n const comparators = alternative.trim().replace(/([<>]=?|[=^~])\\s+/g, '$1').split(/\\s+/).filter(Boolean).map(parseComparator)\n if (comparators.length > 0 && comparators.every(c => c !== undefined))\n alternatives.push(comparators)\n }\n return alternatives\n}\n\n/**\n * The `peerDependencies.zidane` range in an extension's package.json.\n *\n * @param dir - The extension's directory.\n */\nexport function requiredZidaneRange(dir: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf8')) as { peerDependencies?: Record<string, unknown> }\n const range = pkg?.peerDependencies?.zidane\n return typeof range === 'string' && range.trim() ? range.trim() : undefined\n }\n catch {\n return undefined\n }\n}\n\n/** Options for {@link checkZidaneVersion}. Both are test seams. */\nexport interface ZidaneVersionCheckOptions {\n /** Version to check against. Defaults to this zidane's. */\n hostVersion?: string\n /** Treat the host as a source checkout and skip the check. Defaults to detecting it. */\n fromSource?: boolean\n}\n\n/** What {@link checkZidaneVersion} found: refuse the extension, or load it with a warning. */\nexport interface ZidaneVersionProblem {\n /** `error`: this zidane is older than the extension needs. `warning`: newer than its range, which may still work. */\n level: 'error' | 'warning'\n /** Says what the extension declares and what this is. */\n message: string\n}\n\n/**\n * Whether the extension in `dir` fits this zidane. `null` when it does: no\n * declared range, a range this version satisfies, a range that doesn't parse\n * (never block on garbage), or a source checkout. An `error` when this\n * zidane is older than the range's floor (the extension may use exports it\n * lacks); only a `warning` when it is newer than the range (`^7`, `~8.0.0`),\n * which usually still works.\n *\n * @param dir - The extension's directory.\n * @param options - Test seams.\n */\nexport function checkZidaneVersion(dir: string, options: ZidaneVersionCheckOptions = {}): ZidaneVersionProblem | null {\n if (options.fromSource ?? RUNNING_FROM_SOURCE)\n return null\n const range = requiredZidaneRange(dir)\n if (!range)\n return null\n const host = options.hostVersion ?? ZIDANE_VERSION\n if (satisfiesVersionRange(host, range) !== false)\n return null\n if (belowVersionRange(host, range))\n return { level: 'error', message: `Needs zidane ${range}. This is zidane ${host}. Update zidane, or install an extension release made for it.` }\n return { level: 'warning', message: `Made for zidane ${range}. This is zidane ${host}. Loaded anyway. If it misbehaves, update the extension.` }\n}\n","/**\n * Internal: race a promise against a timeout. Shared by the discovery\n * loader (module imports) and the registry builder (extension `setup()`).\n * The timer is always cleared so a settled promise doesn't pin the\n * event loop for the full timeout.\n */\nexport function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string,\n onTimeout?: () => void,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n onTimeout?.()\n reject(new Error(message))\n }, timeoutMs)\n })\n return Promise.race([promise, timeout]).finally(() => {\n if (timer)\n clearTimeout(timer)\n })\n}\n","/**\n * Extension discovery.\n *\n * Walks the standard project + user roots\n * (`{project,user}/.{agents,zidane}/extensions/<name>/`) using the\n * shared {@link projectUserPaths} convention, then dynamic-imports only\n * trusted `<name>/index.{ts,tsx,mjs,js}` files (probed in\n * that preference order — see {@link ENTRY_CANDIDATES}). The first valid\n * module per name wins — project beats user, `.agents/` beats\n * `.<prefix>/` within a single scope (see `projectUserPaths` for the\n * full order).\n *\n * Loading uses native `await import(pathToFileURL(...))` under Bun, which\n * handles `.ts`/`.tsx` and JS variants in `bun run` and in\n * `bun build --compile` binaries; Node hosts load through jiti (see\n * {@link importExtensionEntry}).\n *\n * Before each import the extension directory's module graph is EVICTED\n * from the module cache (`require.cache`: Bun's covers ESM too, jiti's\n * Node graph lives there), so\n * re-running discovery — the `/reload` command, a cwd-change rebuild, an\n * overwrite-install — loads the current on-disk code, deep imports\n * included. Module-level state resets per reload; the teardown contract\n * covers cleanup of the outgoing instance.\n *\n * Failures are isolation-first: a broken module produces a\n * {@link LoadedExtension} with `error` set instead of throwing — one\n * vibe-coded syntax error mustn't keep the TUI from booting.\n */\n\nimport type { LogSink } from '../logger'\nimport type { ExtensionDefinition, ExtensionManifest, ExtensionSource, LoadedExtension } from './types'\nimport { lstatSync, readdirSync, readFileSync, realpathSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { delimiter, dirname, resolve, sep } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { createDebugLog } from '../debug-log'\nimport { errorMessage } from '../errors'\nimport { consoleSink, createLogger } from '../logger'\nimport { resolveExtensionHostModules } from './host-modules'\nimport { checkZidaneVersion } from './host-version'\nimport { withTimeout } from './with-timeout'\n\n/**\n * Scoped `ZIDANE_DEBUG` logger. A `const` rather than a hoisted function\n * declaration, so it must precede its call sites.\n */\nconst debugLog = createDebugLog('extensions')\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Entry-point filenames probed inside each `<name>/` directory, in\n * preference order. Bun loads `.ts` / `.tsx` natively; `.mjs` / `.js`\n * are accepted so pre-compiled extensions (or vibe-coded JS-only ones)\n * work too. `index.js` is last because most authors will reach for\n * `.ts` first.\n */\nconst ENTRY_CANDIDATES = ['index.ts', 'index.tsx', 'index.mjs', 'index.js'] as const\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\n/** A discovered (but not yet loaded) extension entry. */\ninterface ExtensionScanEntry {\n /** Folder name — the canonical persisted extension identity. */\n name: string\n /** Absolute path to the extension directory. */\n dir: string\n /** Absolute path to the entry file picked from `ENTRY_CANDIDATES`. */\n entry: string\n /** Manifest read from the extension, when it has a valid one. */\n manifest?: ExtensionManifest\n /** Where the discovery root lives. */\n source: ExtensionSource\n}\n\ninterface ExtensionScanRoot {\n path: string\n source: 'project' | 'user'\n}\n\n// ---------------------------------------------------------------------------\n// Path resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Default scan roots for extension discovery. Mirrors\n * {@link defaultSkillScanPaths} / {@link defaultMcpsConfigPaths}; the\n * subPath is `'extensions'` so each root expands to\n * `<scope>/.<convention>/extensions/`.\n *\n * @param opts - Overrides for the working directory (`cwd`), home directory (`home`) and storage prefix (`prefix`).\n */\nexport function defaultExtensionScanPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n} = {}): ExtensionScanRoot[] {\n return projectUserPaths({ subPath: 'extensions', ...opts })\n}\n\n/**\n * Parse a platform-delimited list of additional extension scan roots.\n * Useful for local dogfooding / host-provided packages:\n *\n * ZIDANE_EXTENSION_DIRS=/repo/extensions bun run tui\n *\n * Each path is a scan ROOT whose children are extension dirs/packages\n * (`<root>/<name>/index.ts`), not a single extension directory.\n *\n * @param value - Delimited list of scan roots. Default: `ZIDANE_EXTENSION_DIRS`.\n */\nexport function parseExtensionScanPathEnv(value: string | undefined = process.env.ZIDANE_EXTENSION_DIRS): string[] {\n if (!value)\n return []\n return value.split(delimiter).map(p => p.trim()).filter(Boolean)\n}\n\n/**\n * Walk every scan root, collecting one {@link ExtensionScanEntry} per\n * `<root>/<name>/index.*` hit. First-found per `name` wins (matches the\n * project-user precedence used elsewhere — see `projectUserPaths` JSDoc).\n *\n * Non-existent roots are skipped silently. Stat errors inside a present\n * root are surfaced under `ZIDANE_DEBUG` only — they shouldn't bring\n * boot to a halt.\n *\n * @param opts - Overrides for `cwd`, `home` and `prefix`, plus extra scan roots in `extensionScanPaths`.\n */\nexport function scanExtensionDirectories(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n} = {}): ExtensionScanEntry[] {\n const seen = new Map<string, ExtensionScanEntry>()\n for (const entry of scanAllExtensionDirectories(opts)) {\n if (!seen.has(entry.name))\n seen.set(entry.name, entry)\n }\n return [...seen.values()]\n}\n\nfunction scanAllExtensionDirectories(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n} = {}): ExtensionScanEntry[] {\n const roots: ExtensionScanRoot[] = [\n ...(opts.extensionScanPaths ?? []).map(path => ({ path: resolve(path), source: 'user' as const })),\n ...defaultExtensionScanPaths(opts),\n ]\n const entriesOut: ExtensionScanEntry[] = []\n\n for (const root of roots) {\n try {\n // The scan root and its parent (`.zidane` / `.agents`) are part of\n // the trust boundary too — a symlinked parent would let every entry\n // resolve outside the configured roots while the per-entry lstat\n // checks below still pass.\n if (lstatSync(dirname(root.path)).isSymbolicLink())\n continue\n if (!lstatSync(root.path).isDirectory())\n continue\n }\n catch {\n continue\n }\n let realRoot: string\n try {\n realRoot = realpathSync(root.path)\n }\n catch (err) {\n debugLog(`realpath ${root.path}`, err)\n continue\n }\n let entries: string[]\n try {\n entries = readdirSync(root.path)\n }\n catch (err) {\n debugLog(`readdir ${root.path}`, err)\n continue\n }\n for (const name of entries) {\n if (name.startsWith('.'))\n continue\n const dir = resolve(root.path, name)\n try {\n // Extension roots and entry files are trust boundaries. Do not follow\n // symlinks out of the configured scan roots.\n if (!lstatSync(dir).isDirectory())\n continue\n }\n catch (err) {\n debugLog(`stat ${dir}`, err)\n continue\n }\n const entry = findEntryFile(dir)\n if (!entry)\n continue\n // Containment: the canonical entry file must stay under the\n // canonical scan root. Catches symlinks at any level the lstat\n // checks above didn't cover.\n try {\n const realEntry = realpathSync(entry)\n if (!realEntry.startsWith(realRoot + sep)) {\n debugLog(`skip ${entry}`, new Error(`resolves outside scan root ${realRoot}`))\n continue\n }\n }\n catch (err) {\n debugLog(`realpath ${entry}`, err)\n continue\n }\n entriesOut.push({ name, dir, entry, source: root.source, ...readExtensionManifest(dir) })\n }\n }\n return entriesOut\n}\n\n/**\n * Pick the first existing `index.*` file inside `dir`, or `null` if none.\n * Exported for the installer, which probes fetched trees with the same rule.\n */\nexport function findEntryFile(dir: string): string | null {\n for (const candidate of ENTRY_CANDIDATES) {\n const full = resolve(dir, candidate)\n try {\n if (lstatSync(full).isFile())\n return full\n }\n catch {\n continue\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Loading\n// ---------------------------------------------------------------------------\n\n/**\n * Discover + load every trusted extension reachable from the default\n * scan paths — including ones the user's enable allowlist has toggled\n * OFF. Disabled extensions still import (and later run `setup()`) so\n * their config schema is browsable in Settings before enabling; the\n * allowlist gates their CONTRIBUTIONS at registry level, not their code.\n * The trust boundary stays with `trustProjectExtensions`. Returns one {@link LoadedExtension} per scan entry:\n * skipped entries have no definition, failed entries carry `error`.\n * Loaders never throw — the host can decide what to do with load errors\n * (typically: ship them to the Settings panel).\n *\n * Imports run in parallel: a slow extension can't block the rest. The\n * returned array preserves the original scan order (project-first,\n * then user) so first-found-wins precedence stays intact.\n *\n * `signal` is honored opportunistically — if the abort fires BEFORE\n * we dispatch imports, the whole set is skipped. Once `Promise.all` is\n * in flight, individual imports can't be cancelled (the module system\n * has no abort affordance).\n *\n * Project-scoped extensions execute arbitrary local code, so they're\n * skipped unless `trustProjectExtensions` resolves to `true` — either\n * via the option or the `ZIDANE_TRUST_PROJECT_EXTENSIONS=1` env var\n * fallback. User-scoped extensions always load.\n *\n * Each extension's module graph is evicted from the runtime cache\n * before import, so calling this again (e.g. `/reload`) re-evaluates\n * the current on-disk code — edits, overwrites, and updates apply\n * without a restart.\n *\n * @param opts - Scan overrides (`cwd`, `home`, `prefix`, `extensionScanPaths`), an abort `signal`, `trustProjectExtensions`, `importTimeoutMs` (default 10000) as the per-extension import limit, and the `sink` non-blocking load warnings go to (default: `warn`+ to the console, like `buildExtensionRegistry`).\n */\nexport async function discoverExtensions(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n signal?: AbortSignal\n trustProjectExtensions?: boolean\n importTimeoutMs?: number\n sink?: LogSink\n} = {}): Promise<LoadedExtension[]> {\n const entries = selectExtensionEntries(scanAllExtensionDirectories(opts), opts)\n if (opts.signal?.aborted || entries.length === 0)\n return []\n const sink = opts.sink ?? consoleSink({ minLevel: 'warn' })\n return Promise.all(entries.map((entry) => {\n if (!('entry' in entry))\n return Promise.resolve(entry)\n return loadExtensionFromDisk(entry, opts.importTimeoutMs ?? 10_000, sink)\n }))\n}\n\nfunction selectExtensionEntries(\n entries: readonly ExtensionScanEntry[],\n opts: { trustProjectExtensions?: boolean },\n): Array<ExtensionScanEntry | LoadedExtension> {\n // Safe by default: project extensions are untrusted unless the host\n // opts in explicitly or the user sets the env var the error message\n // below points at.\n const trustProjectExtensions = opts.trustProjectExtensions\n ?? (process.env.ZIDANE_TRUST_PROJECT_EXTENSIONS === '1')\n const selected = new Map<string, ExtensionScanEntry | LoadedExtension>()\n const locked = new Set<string>()\n\n for (const entry of entries) {\n if (locked.has(entry.name))\n continue\n if (entry.source === 'project' && !trustProjectExtensions) {\n if (!selected.has(entry.name)) {\n selected.set(entry.name, {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: 'Project extensions are disabled until this project is trusted. Set ZIDANE_TRUST_PROJECT_EXTENSIONS=1 or pass trustProjectExtensions to enable them.',\n })\n }\n continue\n }\n selected.set(entry.name, entry)\n locked.add(entry.name)\n }\n\n return [...selected.values()]\n}\n\nconst requireForCache = createRequire(import.meta.url)\n\n/**\n * Bun only: monotonic import generation, appended as a query so each\n * discovery pass gets a fresh ENTRY module. The query alone is not enough: relative\n * imports resolve to plain file paths and hit the cache, which is why\n * {@link evictModuleGraph} runs first. Both together are what makes\n * `/reload` a true hot-reload (verified empirically: query-only keeps\n * deep imports stale; eviction-only keeps the entry stale).\n */\nlet importGeneration = 0\n\n/**\n * Evict everything under `dir` from the module cache. Under Bun,\n * `require.cache` deletion covers the ES registry for these keys and the\n * entry itself is refreshed by the generation query above; under Node,\n * jiti keeps the whole graph there, entry included, so eviction alone\n * refreshes it. The extension's own `node_modules` are evicted\n * too so a dependency bump applies on update without a restart.\n */\nfunction evictModuleGraph(dir: string): void {\n const cache = requireForCache.cache\n if (!cache)\n return\n // Cache keys are PHYSICAL paths — realpath the dir or the prefix match\n // silently misses through symlinks (macOS `/var` → `/private/var`).\n let physical: string\n try {\n physical = realpathSync(dir)\n }\n catch {\n physical = dir\n }\n for (const root of physical === dir ? [dir] : [dir, physical]) {\n const prefix = root.endsWith(sep) ? root : root + sep\n for (const key of Object.keys(cache)) {\n if (key.startsWith(prefix))\n delete cache[key]\n }\n }\n}\n\n/**\n * Import an extension entry fresh from disk, its relative import graph\n * included. Every import of an extension goes through here (discovery and\n * install validation), so both runtimes get the same reload semantics.\n *\n * Bun: native `import()` — `.ts`/`.tsx`, extensionless relative imports\n * and host modules (`Bun.plugin`) all resolve natively.\n *\n * Node (the Electron GUI): jiti, NOT native `import()`. Node's ESM cache\n * can't be evicted, so a cache-busting query per reload would pin every\n * generation's module graph for the life of the process. jiti evaluates\n * the graph as CJS into `require.cache`, which {@link evictModuleGraph}\n * clears — old generations become collectible once their registry is\n * disposed (asserted by test/extensions/node-reload.test.ts). Stable URLs\n * also keep the cache flat. jiti additionally gives Node full TS/TSX and\n * `./foo` → `foo.ts` resolution, and maps the host's modules\n * (`'zidane'`, …) through `virtualModules` to the LIVE instances.\n * `fsCache: false`: no transpile output accumulating in tmp per edit.\n */\nexport async function importExtensionEntry(entryPath: string, dir: string): Promise<unknown> {\n evictModuleGraph(dir)\n if (typeof Bun !== 'undefined') {\n importGeneration += 1\n return import(`${pathToFileURL(entryPath).href}?zidane-gen=${importGeneration}`)\n }\n // Non-literal specifier: keeps `bun build --compile` from bundling a\n // loader the Bun binary never runs. Node hosts resolve it from zidane's\n // dependencies.\n const jitiSpecifier = 'jiti'\n const [{ createJiti }, virtualModules] = await Promise.all([\n import(jitiSpecifier) as Promise<typeof import('jiti')>,\n resolveExtensionHostModules(),\n ])\n const jiti = createJiti(entryPath, { moduleCache: true, fsCache: false, interopDefault: false, virtualModules })\n return jiti.import(entryPath)\n}\n\n/**\n * Drop an extension directory's modules from the cache without importing\n * again — for a one-off import whose directory is about to move or be\n * deleted (install validation), so nothing stays pinned under that path.\n */\nexport function releaseExtensionModules(dir: string): void {\n evictModuleGraph(dir)\n}\n\n/**\n * Import + validate a single extension entry. Always returns a\n * {@link LoadedExtension} — the load error (if any) lives on the\n * returned object, never as a thrown exception.\n */\nasync function loadExtensionFromDisk(entry: ExtensionScanEntry, timeoutMs: number, sink: LogSink): Promise<LoadedExtension> {\n // Before the import: a module written against a newer zidane would fail on\n // a missing export, which says nothing about the actual problem.\n const version = checkZidaneVersion(entry.dir)\n if (version?.level === 'error') {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: version.message,\n }\n }\n if (version)\n createLogger(sink, { extension: entry.name }).warn(version.message)\n try {\n const mod = await withTimeout(\n importExtensionEntry(entry.entry, entry.dir),\n timeoutMs,\n `Timed out importing ${entry.entry} after ${timeoutMs}ms. The module's top-level code may still be running; an import can't be cancelled.`,\n )\n const definition = pickExtensionDefinition(mod)\n if (!definition) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Module at ${entry.entry} does not export an ExtensionDefinition (expected \\`default\\` or named \\`extension\\`).`,\n }\n }\n if (entry.manifest?.apiVersion !== undefined && entry.manifest.apiVersion !== 1) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n manifest: entry.manifest,\n error: `Unsupported extension API version \"${entry.manifest.apiVersion}\". This runtime supports apiVersion 1.`,\n }\n }\n if (definition.apiVersion !== undefined && definition.apiVersion !== 1) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Unsupported extension API version \"${definition.apiVersion}\". This runtime supports apiVersion 1.`,\n }\n }\n if (definition.name !== entry.name) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Extension name mismatch: folder is \"${entry.name}\" but module exports name \"${definition.name}\". Rename one so the persisted identity is stable.`,\n }\n }\n if (entry.manifest?.name && entry.manifest.name !== entry.name) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n manifest: entry.manifest,\n error: `Extension manifest name mismatch: folder is \"${entry.name}\" but manifest declares \"${entry.manifest.name}\".`,\n }\n }\n return {\n definition,\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n }\n }\n catch (err) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Failed to import ${entry.entry}: ${errorMessage(err)}`,\n }\n }\n}\n\n/**\n * Extract the `ExtensionDefinition` from an imported module. Accepts\n * either the `default` export (preferred) or a named `extension`\n * export. Anything else — including a module that happens to have\n * top-level `name` + `setup` exports — is rejected so the loader's\n * surface stays unambiguous.\n */\nfunction pickExtensionDefinition(mod: unknown): ExtensionDefinition | null {\n if (!mod || typeof mod !== 'object')\n return null\n const m = mod as Record<string, unknown>\n if (isExtensionDefinition(m.default))\n return m.default\n if (isExtensionDefinition(m.extension))\n return m.extension\n return null\n}\n\n/**\n * Structural type-guard for an {@link ExtensionDefinition}. Kept lax:\n * `name` + `setup` are required; everything else is optional. A non-\n * function `setup` is rejected outright so the loader doesn't have\n * to second-guess it later.\n *\n * @param value - Value to test.\n */\nexport function isExtensionDefinition(value: unknown): value is ExtensionDefinition {\n if (!value || typeof value !== 'object')\n return false\n const v = value as Record<string, unknown>\n if (typeof v.name !== 'string' || v.name.length === 0)\n return false\n if (DANGEROUS_RECORD_KEYS.has(v.name))\n return false\n if (typeof v.setup !== 'function')\n return false\n return true\n}\n\n/**\n * Tag an inline (host-passed) extension into the same shape disk-\n * loaded ones produce, so host-bundled extensions (e.g. those passed\n * via `RunTuiOptions.extensions`) flow through `buildExtensionRegistry`\n * identically to disk-discovered ones.\n *\n * @param definition - Extension definition the host built in code.\n */\nexport function inlineLoadedExtension(definition: ExtensionDefinition): LoadedExtension {\n return {\n definition,\n name: definition.name,\n source: 'inline',\n }\n}\n\n/**\n * Read `extension.json` (or `package.json#zidane`) as a shape-validated\n * manifest. Exported for the installer so \"what counts as a manifest\" has\n * exactly one definition — a malformed manifest is ignored identically at\n * install-validation time and at discovery time.\n */\nexport function readExtensionManifest(dir: string): { manifest?: ExtensionManifest } {\n const fromExtensionJson = readJson(resolve(dir, 'extension.json'))\n if (isExtensionManifest(fromExtensionJson))\n return { manifest: fromExtensionJson }\n const pkg = readJson(resolve(dir, 'package.json'))\n if (pkg && typeof pkg === 'object' && !Array.isArray(pkg)) {\n const manifest = (pkg as { zidane?: unknown }).zidane\n if (isExtensionManifest(manifest))\n return { manifest }\n }\n return {}\n}\n\nfunction readJson(path: string): unknown {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch {\n return null\n }\n}\n\nfunction isExtensionManifest(value: unknown): value is ExtensionManifest {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return false\n const v = value as Record<string, unknown>\n // `apiVersion`, when present, must be a number. We still recognize an\n // unsupported version (e.g. 2) as a manifest so the loader can surface a\n // clear \"unsupported API version\" error rather than silently ignoring it —\n // but the rest of the shape is still validated.\n if (v.apiVersion !== undefined && typeof v.apiVersion !== 'number')\n return false\n const isStringArray = (x: unknown): boolean => Array.isArray(x) && x.every(el => typeof el === 'string')\n return (v.name === undefined || typeof v.name === 'string')\n && (v.displayName === undefined || typeof v.displayName === 'string')\n && (v.description === undefined || typeof v.description === 'string')\n // Element-checked, not just Array.isArray — the predicate claims\n // `ExtensionManifest` whose lists are `string[]`, so honor that claim.\n && (v.permissions === undefined || isStringArray(v.permissions))\n && (v.capabilities === undefined || isStringArray(v.capabilities))\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/** Re-export for tests / advanced hosts that want to log the scan results before loading. */\nexport type { ExtensionScanEntry }\n/** Re-export so callers can read the canonical entry file list. */\nexport { ENTRY_CANDIDATES }\n","/**\n * Keybindings — a single source of truth for the app's global shortcuts.\n *\n * Every customizable action is declared once in {@link KEYBINDING_DEFS}\n * with its default trigger, label, and description. The TUI imports\n * `DEFAULT_KEYBINDINGS` for the baked-in defaults; on launch we load\n * `<userDir>/keybindings.json` (when present) and merge user overrides\n * on top so the user can rebind any action without recompiling.\n *\n * Scope (deliberate):\n *\n * - We expose the **global, action-opening** shortcuts (\"open the\n * Settings modal\", \"cycle agent profile\", \"enter select-turn\n * mode\", etc.), the message-queue keys, and the letter shortcuts of\n * the turn-details and session-details modals (`f` fork, `d` delete,\n * `c` copy). Universal navigation (`↑/↓/↵/⎋`) stays hardcoded — it\n * lives too close to a renderable's input contract to remap safely.\n *\n * - Storage lives at the **user level** (`<userDir>/keybindings.json`),\n * never inside a project's `.{prefix}/`. Shortcut muscle memory is\n * a per-user concern, not a per-repo one.\n *\n * Format on disk: JSON with `//` and `/* … *\\/` comments allowed\n * (parsed via a small JSONC stripper). Each entry maps an action id\n * to a binding spec string. Specs are parsed by {@link parseBindingSpec};\n * unknown / malformed entries fall back to the default for that action,\n * so a syntax slip never bricks the TUI.\n */\n\nimport type { ParsedBinding } from './keybinding-spec'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { bindingAlternatives, parseBindingSpec, parseBindingSpecs } from './keybinding-spec'\n\nexport { bindingAlternatives, parseBindingSpec, parseBindingSpecs, type ParsedBinding } from './keybinding-spec'\n\n// ---------------------------------------------------------------------------\n// Action catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Every action the keybindings system can drive. Adding a new action\n * here forces every consumer (TUI keyboard handler, hint rows, settings\n * UI) to handle it, and ships a default that the user can override.\n */\nexport type KeyAction\n // Global action shortcuts — fire from the top-level keyboard\n // handler (`handleAppKey` in `tui/shell/app-keyboard.tsx`), gated on screen / busy state.\n = | 'openSettings'\n | 'openSessionDetails'\n | 'newSession'\n | 'openModelPicker'\n | 'openEffortPicker'\n | 'openTodos'\n | 'openArtifacts'\n | 'panelNext'\n | 'panelPrev'\n | 'nextSpace'\n | 'prevSpace'\n | 'openContextPanel'\n | 'openMainSession'\n | 'openUsagePanel'\n | 'openKeybindings'\n | 'openPalette'\n | 'cycleAgent'\n | 'enterSelectTurnMode'\n | 'cancelToolCall'\n | 'abortRun'\n | 'changeCwd'\n | 'attachClipboardImage'\n | 'openExternalEditor'\n // Message-queue navigation — only meaningful when the user has\n // type-ahead prompts waiting in the queue box above the prompt\n // input. `enterQueueSelection` moves the focus from the textarea\n // into the queue; while there, `pushQueuedMessage` (steers the\n // selected entry into the live run) and `dropQueuedMessage`\n // (removes the entry) act on the highlighted row.\n | 'enterQueueSelection'\n | 'pushQueuedMessage'\n | 'dropQueuedMessage'\n // Turn-details modal actions (`ctrl+s` select-turn mode → enter).\n // Each binding only matches while that modal is mounted, so the\n // single-letter defaults can clash with letters used elsewhere\n // without conflict.\n | 'turnFork'\n | 'turnDelete'\n | 'turnCopy'\n | 'turnEdit'\n // Session-details modal actions (opened via `ctrl+shift+x`).\n | 'sessionDelete'\n | 'sessionCopyId'\n | 'sessionGenerateTitle'\n | 'sessionExportMarkdown'\n | 'sessionExportJson'\n | 'sessionCompact'\n | 'sessionMoveSpace'\n | 'sessionFavorite'\n\n/** Resolved keybindings — every action has a current spec string. */\nexport type KeyBindings = Record<KeyAction, string>\n\n/**\n * Resolved spec string for one extension-contributed keybinding. Same\n * shape as a built-in {@link KeyBindings} entry but the `id` is\n * extension-owned (no `KeyAction` union member). The TUI dispatcher\n * iterates these alongside the built-ins; user overrides flow through\n * the same `keybindings.json` file by matching on `id`.\n */\nexport interface ResolvedExtensionKeybinding {\n /** Extension-owned id (matches `ExtensionKeybinding.id`). */\n id: string\n /** Active spec string — user override when present, default otherwise. May be empty (= unbound). */\n spec: string\n}\n\n/**\n * Logical grouping for one action — used by the keybindings panel to\n * render section headers. Stable strings (not an enum) so future\n * groups can be added without churning every existing entry.\n */\nexport type KeyBindingGroup\n = | 'Global'\n | 'Message queue'\n | 'Turn details'\n | 'Session details'\n\n/** Static metadata for one action — used by the loader, hint rows, settings UI. */\nexport interface KeyBindingDef {\n /** Stable identifier — JSON key + TS union member. */\n action: KeyAction\n /** Default trigger spec (e.g. `'ctrl+o'`). */\n default: string\n /** Short label used in shortcut hint rows. */\n label: string\n /** Longer description for the settings UI and the annotated JSON file. */\n description: string\n /**\n * Group label rendered as a section header in the keybindings panel.\n * Items are listed in {@link KEYBINDING_DEFS} order; the panel just\n * inserts a header whenever this field changes between adjacent\n * entries — so groups stay contiguous and the order here is the\n * order on screen.\n */\n group: KeyBindingGroup\n}\n\n/**\n * The canonical action catalog. Order is the source of truth for the\n * generated keybindings file and the settings UI.\n */\nexport const KEYBINDING_DEFS: readonly KeyBindingDef[] = [\n {\n action: 'openSettings',\n default: 'ctrl+o',\n label: 'settings',\n description: 'open settings',\n group: 'Global',\n },\n {\n // ctrl+shift+x keeps ctrl+x free (the conventional \"cut\" key in most\n // editors). Like the context panel, it relies on the kitty keyboard\n // protocol to stay distinct from ctrl+x; on terminals without it\n // (e.g. macOS Terminal.app) shift is dropped and it collapses to\n // ctrl+x — rebind there.\n action: 'openSessionDetails',\n default: 'ctrl+shift+x',\n label: 'session',\n description: 'open session details: stats, export, rename, delete',\n group: 'Global',\n },\n {\n action: 'newSession',\n default: 'ctrl+n',\n label: 'new session',\n description: 'start a new session with the current model',\n group: 'Global',\n },\n {\n action: 'openModelPicker',\n default: 'ctrl+m',\n label: 'model',\n description: 'open the model picker',\n group: 'Global',\n },\n {\n action: 'openEffortPicker',\n default: 'ctrl+l',\n label: 'options',\n description: 'open model options, like reasoning effort and fast mode',\n group: 'Global',\n },\n {\n action: 'openTodos',\n default: 'ctrl+t',\n label: 'todos',\n description: 'open the run\\'s todo list',\n group: 'Global',\n },\n {\n // ctrl+shift+a: \"artifacts\" — what the run produced, next to todos (ctrl+t),\n // what it planned. Kitty-protocol dependent like the other ctrl+shift\n // chords — collapses to ctrl+a on Terminal.app; rebind there.\n action: 'openArtifacts',\n default: 'ctrl+shift+a',\n label: 'panel',\n description: 'show or hide the side panel',\n group: 'Global',\n },\n {\n action: 'panelNext',\n default: 'ctrl+shift+]',\n label: 'next panel',\n description: 'open the next side-panel tab',\n group: 'Global',\n },\n {\n action: 'panelPrev',\n default: 'ctrl+shift+[',\n label: 'previous panel',\n description: 'open the previous side-panel tab',\n group: 'Global',\n },\n // Spaces (opt-in): the arrows of the sessions list's ←/→ and the GUI's ⌥⌘←/→.\n // kitty and WezTerm keep ctrl+shift+arrows for their own tabs / panes by default: rebind there.\n {\n action: 'nextSpace',\n default: 'ctrl+shift+right',\n label: 'next space',\n description: 'show the next space (Spaces on); from a chat, the sessions list opens on it',\n group: 'Global',\n },\n {\n action: 'prevSpace',\n default: 'ctrl+shift+left',\n label: 'previous space',\n description: 'show the previous space (Spaces on); from a chat, the sessions list opens on it',\n group: 'Global',\n },\n {\n // ctrl+shift+g leans on the kitty keyboard protocol (the renderer\n // enables it) to stay distinct from ctrl+g (`openExternalEditor`). On\n // terminals without it — e.g. macOS Terminal.app — shift is dropped and\n // the keypress collapses to ctrl+g (the editor); rebind there.\n action: 'openContextPanel',\n default: 'ctrl+shift+g',\n label: 'context',\n description: 'show what fills the context window',\n group: 'Global',\n },\n {\n // ctrl+shift+m (\"Main\"): one chord for the TUI and the GUI. Leans on the\n // kitty keyboard protocol (the renderer enables it) to stay distinct from\n // ctrl+m (the model picker); on terminals without it — e.g. macOS\n // Terminal.app — shift is dropped and the keypress collapses to ctrl+m;\n // rebind there. NOT ctrl+u: that is the textarea's kill-to-line-start,\n // which is also what several terminals send for ⌃⌫, so deleting a word\n // would jump to the main session.\n action: 'openMainSession',\n default: 'ctrl+shift+m',\n label: 'main session',\n description: 'open the Main session for this directory, or create it. Needs the \"Main session\" and \"Use daemon runtime\" settings',\n group: 'Global',\n },\n {\n // Plain ctrl+p on purpose: free in OpenTUI's textarea keymap (which owns\n // ctrl+a/e/f/b/w/k/u/d) and in every other global binding, so it behaves\n // the same on kitty and legacy terminals. A ctrl+shift+<x> default would\n // collapse to its unshifted chord on Terminal.app and eat a text-editing\n // key.\n action: 'openUsagePanel',\n default: 'ctrl+p',\n label: 'usage',\n description: 'open plan limits and credits for each provider',\n group: 'Global',\n },\n {\n action: 'openKeybindings',\n default: 'ctrl+y',\n label: 'keybindings',\n description: 'list every shortcut',\n group: 'Global',\n },\n {\n action: 'openPalette',\n // Terminals cannot report cmd, and ctrl+k belongs to cancelToolCall;\n // ctrl+shift+p is the palette chord editors made familiar. The GUI\n // catalog binds this to cmd+k (a declared divergence).\n default: 'ctrl+shift+p',\n label: 'palette',\n description: 'search actions, commands, sessions and settings',\n group: 'Global',\n },\n {\n action: 'cycleAgent',\n default: 'shift+tab',\n label: 'cycle agent',\n description: 'switch to the next agent profile',\n group: 'Global',\n },\n {\n action: 'enterSelectTurnMode',\n default: 'ctrl+s',\n label: 'messages',\n description: 'select a previous message',\n group: 'Global',\n },\n {\n action: 'cancelToolCall',\n default: 'ctrl+k',\n label: 'cancel tool',\n description: 'cancel one tool call and keep the run going',\n group: 'Global',\n },\n {\n action: 'abortRun',\n default: 'ctrl+escape',\n label: 'abort',\n description: 'stop the run and deny pending approvals. If your terminal cannot send ctrl+esc, rebind it',\n group: 'Global',\n },\n {\n action: 'changeCwd',\n default: 'ctrl+h',\n label: 'cwd',\n description: 'change the working directory',\n group: 'Global',\n },\n {\n // The id keeps its original name so existing keybindings.json overrides\n // work; the shortcut pastes text as well as images.\n action: 'attachClipboardImage',\n default: 'ctrl+v',\n label: 'paste clipboard',\n description: 'paste text or an image from the system clipboard',\n group: 'Global',\n },\n {\n // Handled inside `PromptBlock`'s onKeyDown (it owns the textarea ref),\n // NOT the App-level keyboard handler — so it only ever fires while the\n // prompt composer is focused. Gated by the `externalEditor` setting:\n // disabled there, the key falls through to its normal textarea behavior.\n // ctrl+g (Claude Code's muscle-memory key) needs no kitty keyboard\n // protocol; the context panel takes ctrl+shift+g to keep it free.\n action: 'openExternalEditor',\n default: 'ctrl+g',\n label: 'edit in $EDITOR',\n description: 'write the prompt in $EDITOR or $VISUAL. Needs the \"Edit prompt in $EDITOR\" setting',\n group: 'Global',\n },\n // Message-queue navigation — only matches when the queue box above\n // the prompt has at least one entry. Defaults pick keys the prompt\n // textarea doesn't already claim (no default for enter — see below).\n //\n // `enterQueueSelection` is intentionally unbound by default: the\n // natural way to enter the queue is to press plain `↑` on an empty\n // prompt buffer (handled in `PromptBlock`'s onKeyDown), which doesn't\n // need a global shortcut. The action stays in the catalog so users\n // who want a dedicated key — e.g. for entering the queue WITHOUT\n // emptying their draft first — can rebind it in `keybindings.json`.\n // We don't ship a default because no arrow / modifier combo is both\n // free in OpenTUI's textarea AND forwarded by every common terminal\n // (ctrl+arrow → macOS Mission Control; cmd+arrow → terminal scroll /\n // tab nav; alt+arrow → terminal word-nav).\n {\n action: 'enterQueueSelection',\n default: '',\n label: 'select queue',\n description: 'select the queued messages (or press ↑ on an empty prompt)',\n group: 'Message queue',\n },\n {\n action: 'pushQueuedMessage',\n default: 'ctrl+return',\n label: 'push',\n description: 'send the selected queued message into the running turn',\n group: 'Message queue',\n },\n {\n action: 'dropQueuedMessage',\n default: 'backspace',\n label: 'drop',\n description: 'remove the selected queued message',\n group: 'Message queue',\n },\n // Turn-details modal — only fires while that modal is mounted.\n {\n action: 'turnFork',\n default: 'f',\n label: 'fork',\n description: 'fork the session at this turn (press twice)',\n group: 'Turn details',\n },\n {\n action: 'turnDelete',\n default: 'd',\n label: 'delete',\n description: 'delete this turn (press twice)',\n group: 'Turn details',\n },\n {\n action: 'turnCopy',\n default: 'c',\n label: 'copy',\n description: 'copy this turn',\n group: 'Turn details',\n },\n {\n action: 'turnEdit',\n default: 'e',\n label: 'edit',\n description: 'edit this turn\\'s text',\n group: 'Turn details',\n },\n // Session-details modal — only fires while that modal is mounted.\n {\n action: 'sessionDelete',\n default: 'd',\n label: 'delete',\n description: 'delete the session (press twice)',\n group: 'Session details',\n },\n {\n action: 'sessionCopyId',\n default: 'c',\n label: 'copy id',\n description: 'copy the session id',\n group: 'Session details',\n },\n {\n action: 'sessionGenerateTitle',\n default: 'g',\n label: 'generate title',\n description: 'title the session with the current model',\n group: 'Session details',\n },\n {\n action: 'sessionExportMarkdown',\n default: 'e',\n label: 'export md',\n description: 'export the session as Markdown under .{prefix}/sessions/',\n group: 'Session details',\n },\n {\n action: 'sessionExportJson',\n default: 'j',\n label: 'export json',\n description: 'export the session as JSON under .{prefix}/sessions/',\n group: 'Session details',\n },\n {\n action: 'sessionCompact',\n default: 'k',\n label: 'compact',\n description: 'summarize older turns to free context',\n group: 'Session details',\n },\n {\n action: 'sessionMoveSpace',\n default: 's',\n label: 'space',\n description: 'move the session to the next space (needs Spaces)',\n group: 'Session details',\n },\n {\n action: 'sessionFavorite',\n default: 'f',\n label: 'favorite',\n description: 'add the session to favorites, or remove it',\n group: 'Session details',\n },\n]\n\n/** Index by action for O(1) lookup of label / description / default. */\nexport const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>\n = KEYBINDING_DEFS.reduce(\n (acc, def) => Object.assign(acc, { [def.action]: def }),\n {} as Record<KeyAction, KeyBindingDef>,\n )\n\n/** Defaults, derived from {@link KEYBINDING_DEFS} so the two never drift. */\nexport const DEFAULT_KEYBINDINGS: KeyBindings\n = KEYBINDING_DEFS.reduce(\n (acc, def) => Object.assign(acc, { [def.action]: def.default }),\n {} as KeyBindings,\n )\n\n// ---------------------------------------------------------------------------\n// Spec parsing + matching\n// ---------------------------------------------------------------------------\n\n/**\n * Test a KeyEvent against a parsed-or-string binding. Accepts either\n * form so callers can pre-parse (hot path) or pass the raw spec for\n * one-off checks.\n *\n * Modifier semantics: ALL declared modifiers must be pressed AND no\n * extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,\n * and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises\n * where a binding that should be specific accidentally fires for a\n * superset of key combinations.\n *\n * @param event - Key event to test.\n * @param spec - Binding spec string, or a chord already parsed with {@link parseBindingSpec}.\n */\nexport function matchesBinding(\n event: { name?: string, ctrl?: boolean, shift?: boolean, meta?: boolean, option?: boolean, alt?: boolean },\n spec: string | ParsedBinding,\n): boolean {\n if (typeof spec === 'string')\n return parseBindingSpecs(spec).some(parsed => matchesBinding(event, parsed))\n const parsed = spec\n // OpenTUI's `ParsedKey` exposes `option` for Alt; some terminals\n // also report it as `alt`. Accept either.\n const altPressed = !!event.option || !!event.alt\n if (parsed.ctrl !== !!event.ctrl)\n return false\n if (parsed.shift !== !!event.shift)\n return false\n if (parsed.alt !== altPressed)\n return false\n if (parsed.meta !== !!event.meta)\n return false\n if ((event.name ?? '').toLowerCase() !== parsed.name)\n return false\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Display helpers\n// ---------------------------------------------------------------------------\n\n/** Verbose key names → compact single-cell glyphs used by hint rows. */\nconst KEY_GLYPHS: Readonly<Record<string, string>> = {\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n return: '↵',\n enter: '↵',\n delete: '⌫',\n backspace: '⌫',\n escape: 'esc',\n space: '␣',\n tab: '⇥',\n}\n\n/**\n * Render a binding spec (`\"ctrl+return\"`, `\"backspace\"`, `\"delete\"`) as a\n * compact display string the user recognizes at a glance. Substitutes\n * arrow / enter / backspace glyphs for their verbose names so the hint\n * row stays narrow; modifier names + plain keys pass through unchanged.\n *\n * Empty / missing specs return an empty string — callers decide whether\n * to render a placeholder (e.g. `'—'` for an unbound action in the\n * keybindings panel) or hide the surface entirely (e.g. hint rows that\n * fall back to a different chord when the binding is empty).\n *\n * @param spec - Binding spec, possibly listing several chords separated by `|`.\n */\nexport function formatBindingForDisplay(spec: string | null | undefined): string {\n return bindingAlternatives(spec).map(formatOneBinding).join(' / ')\n}\n\nfunction formatOneBinding(spec: string): string {\n const segments = spec.toLowerCase().split('+')\n const key = segments.pop() ?? ''\n const modifiers = segments.join('+')\n const glyph = KEY_GLYPHS[key] ?? key\n return modifiers ? `${modifiers}+${glyph}` : glyph\n}\n\n// ---------------------------------------------------------------------------\n// Merge + file IO\n// ---------------------------------------------------------------------------\n\n/**\n * Merge a partial map of user overrides into the defaults. Unknown\n * action keys are dropped (a future TUI version may have retired the\n * action); invalid spec strings (parse fails) fall back to the default\n * for that action.\n *\n * @param overrides - Parsed user overrides keyed by action name.\n */\nexport function mergeKeybindings(\n overrides: Readonly<Record<string, unknown>> | null | undefined,\n): KeyBindings {\n if (!overrides || typeof overrides !== 'object')\n return { ...DEFAULT_KEYBINDINGS }\n const merged: KeyBindings = { ...DEFAULT_KEYBINDINGS }\n for (const def of KEYBINDING_DEFS) {\n const raw = normalizeBindingValue(overrides[def.action])\n if (raw === null)\n continue // invalid spec → keep the default\n merged[def.action] = raw\n }\n return merged\n}\n\n/**\n * A `keybindings.json` value as one spec: a string, or an array of chords\n * (`[\"ctrl+w\", \"cmd+w\"]`) joined as alternatives. Chords that don't parse\n * are dropped; `null` when none is left.\n *\n * @param raw - Raw value from `keybindings.json`: a spec string or an array of them.\n */\nexport function normalizeBindingValue(raw: unknown): string | null {\n const parts = typeof raw === 'string'\n ? bindingAlternatives(raw)\n : Array.isArray(raw) ? raw.filter((r): r is string => typeof r === 'string').flatMap(bindingAlternatives) : []\n const valid = parts.filter(part => parseBindingSpec(part) !== null)\n return valid.length > 0 ? valid.join(' | ') : null\n}\n\n/**\n * Path of the user-level keybindings file.\n *\n * @param userDir - User-level config directory.\n */\nexport function keybindingsPath(userDir: string): string {\n return resolve(userDir, 'keybindings.json')\n}\n\n/**\n * One contiguous section of the catalog — every adjacent action of a single\n * `group` rendered together with the section header inserted once.\n * Non-adjacent runs of the same group are NOT merged: catalog order\n * wins so the on-screen story matches the on-disk file.\n *\n * `rows` may carry built-in `KeyBindingDef`s (the common case) or\n * extension-owned ones (when an extension contributes a keybinding).\n * Both share label / description / spec — see {@link KeyBindingRow}.\n */\nexport interface KeyBindingSection {\n /** Group name shared by every row in the section. */\n group: string\n /** Rows in the section, in catalog order. */\n rows: ReadonlyArray<KeyBindingRow>\n}\n\n/**\n * One row in the keybindings catalog. Either a built-in action (with\n * its `def`) or an extension-contributed one (with `id` instead). The\n * renderer reads `label` / `description` / `spec` uniformly and only\n * needs the `kind` discriminator when offering action-specific UI\n * (e.g. \"edit in keybindings.json\" by action name).\n */\nexport type KeyBindingRow\n = | { kind: 'builtin', def: KeyBindingDef, spec: string }\n | { kind: 'extension', id: string, label: string, description: string, spec: string }\n\n/**\n * Walk {@link KEYBINDING_DEFS} in order and bucket rows into contiguous\n * sections by `group`. Shared between the standalone `KeybindingsModal`\n * and the `Keybindings` tab inside `SettingsModal` so both surfaces\n * render the same catalog with the same ordering rules.\n *\n * Pass `extensionRows` (resolved extension keybindings paired with\n * their definitions) to append an \"Extensions\" section at the end so\n * extension-contributed shortcuts are discoverable in the same UI.\n *\n * @param bindings - Resolved built-in bindings, keyed by action.\n * @param extensionRows - Extension-contributed keybindings to list in a trailing section.\n */\nexport function groupBindings(\n bindings: KeyBindings,\n extensionRows: readonly { id: string, label: string, description: string, spec: string }[] = [],\n): readonly KeyBindingSection[] {\n const sections: Array<{ group: string, rows: KeyBindingRow[] }> = []\n for (const def of KEYBINDING_DEFS) {\n const last = sections[sections.length - 1]\n const spec = bindings[def.action] ?? ''\n const row: KeyBindingRow = { kind: 'builtin', def, spec }\n if (last && last.group === def.group) {\n last.rows.push(row)\n continue\n }\n sections.push({ group: def.group, rows: [row] })\n }\n if (extensionRows.length > 0) {\n sections.push({\n group: 'Extensions',\n rows: extensionRows.map(r => ({\n kind: 'extension',\n id: r.id,\n label: r.label,\n description: r.description,\n spec: r.spec,\n })),\n })\n }\n return sections\n}\n\n/**\n * Width (in cells) of the fixed key column used by every binding-row\n * renderer. Derived once from {@link KEYBINDING_DEFS} so a new action\n * with a wider default spec (`ctrl+shift+x`, …) automatically grows\n * the column instead of truncating the label.\n */\nexport const KEYBINDING_KEY_COL_WIDTH = (() => {\n let max = 8\n for (const def of KEYBINDING_DEFS) {\n const width = formatBindingForDisplay(def.default).length\n if (width > max)\n max = width\n }\n return max + 2 // breathing room before the label column\n})()\n\n/**\n * Load + merge the user's keybindings file (when present). Always\n * returns a fully-resolved {@link KeyBindings} — defaults fill in for\n * any missing / invalid entries so consumers can treat the result as\n * total.\n *\n * Failure modes (all tolerated, all fall back to defaults):\n * - File missing\n * - File not valid JSON / JSONC (after comment stripping)\n * - Top-level is not an object\n * - Individual entries have invalid spec strings\n *\n * @param userDir - User-level config directory that holds `keybindings.json`.\n */\nexport function readKeybindings(userDir: string): KeyBindings {\n return mergeKeybindings(readKeybindingsRaw(userDir))\n}\n\n/**\n * Create the user-level `keybindings.json` if it doesn't exist,\n * prefilled with every default. Returns the absolute file path either\n * way so the caller can open it in `$EDITOR`. Idempotent: an existing\n * user file is NEVER overwritten — the user's customizations are\n * sacred.\n *\n * @param userDir - User-level config directory that holds `keybindings.json`.\n */\nexport function ensureKeybindingsFile(userDir: string): string {\n const path = keybindingsPath(userDir)\n if (existsSync(path))\n return path\n writeFileAtomic(path, renderDefaultFile(), { ensureDir: true })\n return path\n}\n\n/**\n * Read the raw on-disk JSON object from `keybindings.json` after the\n * JSONC stripper. Used by extension-keybinding resolution so the\n * user's overrides for extension-owned ids flow through the same file\n * the built-in actions use.\n *\n * Returns an empty object on missing / malformed / non-object files\n * — same fault-tolerance contract as {@link readKeybindings}.\n *\n * @param userDir - User-level config directory that holds `keybindings.json`.\n */\nexport function readKeybindingsRaw(userDir: string): Readonly<Record<string, unknown>> {\n const path = keybindingsPath(userDir)\n if (!existsSync(path))\n return {}\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(stripJsonComments(raw)) as unknown\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n return parsed as Readonly<Record<string, unknown>>\n }\n catch {\n return {}\n }\n}\n\n/**\n * Resolve the live spec string for each extension-contributed\n * keybinding. The user's `keybindings.json` is consulted for an\n * override on the binding's `id`; absent / invalid entries fall back\n * to the binding's `default`.\n *\n * A chord that is reserved or a bare printable character\n * (see {@link isReservedExtensionBinding}, {@link isBareCharExtensionBinding})\n * is refused: a user override falls back to the default, and such a default\n * resolves to unbound.\n *\n * Pass the raw object (from {@link readKeybindingsRaw}) — passing\n * `KeyBindings` here wouldn't help because the typed map only carries\n * built-in `KeyAction` entries.\n *\n * Returns one entry per input binding, in source order. Empty `spec`\n * (binding declared `default: ''` and the user didn't bind it) is a\n * legitimate \"unbound\" state — the dispatcher never matches an empty\n * spec, so the action stays accessible via re-binding through\n * `keybindings.json`.\n *\n * @param bindings - Extension keybindings with their ids and default specs.\n * @param raw - Raw object from {@link readKeybindingsRaw}.\n */\nexport function resolveExtensionKeybindings(\n bindings: readonly { id: string, default: string }[],\n raw: Readonly<Record<string, unknown>>,\n): ResolvedExtensionKeybinding[] {\n return bindings.map((binding) => {\n const candidate = raw[binding.id]\n if (\n typeof candidate === 'string'\n && parseBindingSpec(candidate)\n && !isReservedExtensionBinding(candidate)\n && !isBareCharExtensionBinding(candidate)\n ) {\n return { id: binding.id, spec: candidate }\n }\n return {\n id: binding.id,\n spec: isReservedExtensionBinding(binding.default) || isBareCharExtensionBinding(binding.default)\n ? ''\n : binding.default,\n }\n })\n}\n\n/**\n * `true` when `spec` is one of the chords the framework reserves for\n * itself — `escape` (back / exit), `ctrl+escape` (the `abortRun`\n * default — the one built-in that must never be shadowed) and `ctrl+c`\n * (quit). Extensions\n * can never claim these, neither via their declared `default` nor via a\n * user override in `keybindings.json`. Single source of truth shared by\n * `resolveExtensionKeybindings` above and the registration-time refusal\n * in `extensions/register.ts`.\n */\nexport function isReservedExtensionBinding(spec: string): boolean {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return false\n const noExtraModifiers = !parsed.shift && !parsed.alt && !parsed.meta\n if (parsed.name === 'escape' && noExtraModifiers)\n return true\n return parsed.name === 'c' && parsed.ctrl && noExtraModifiers\n}\n\n/**\n * `true` when `spec` is a single printable character without a gating\n * modifier (ctrl / alt / meta — shift alone is just a capital letter).\n * Such a chord would fire on every matching keystroke while the user\n * types in the prompt, so extension bindings can't use one; they're\n * registered unbound instead (rebindable to a modified chord via\n * `keybindings.json`).\n */\nexport function isBareCharExtensionBinding(spec: string): boolean {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return false\n return parsed.name.length === 1 && !parsed.ctrl && !parsed.alt && !parsed.meta\n}\n\n/**\n * Render the default `keybindings.json` body. JSONC with a header\n * explaining the format + per-action comments derived from\n * {@link KEYBINDING_DEFS}'s descriptions. The user sees the catalog +\n * defaults at a glance the moment they open the file.\n *\n * Extension-contributed bindings live alongside the built-ins in the\n * SAME file (keyed by their `id`, e.g. `\"my-ext.openPanel\"`) — they\n * don't appear in the scaffold because extensions are discovered at\n * runtime, but `resolveExtensionKeybindings` reads the same file so\n * a user-added entry overrides the extension's default. The header of\n * the scaffold tells the user how.\n */\nfunction renderDefaultFile(): string {\n const lines: string[] = []\n lines.push('// Zidane keybindings. Edit the values below to customize.')\n lines.push('//')\n lines.push('// Format:')\n lines.push('// \"<action>\": \"<modifier>+<modifier>+<key>\"')\n lines.push('//')\n lines.push('// Modifiers: ctrl, shift, alt (option), meta (cmd).')\n lines.push('// Keys: a-z, 0-9, tab, return, escape, space, delete, …')\n lines.push('//')\n lines.push('// Comments (`//` line, `/* block */`) are stripped before parsing.')\n lines.push('// Restart the TUI to apply changes.')\n lines.push('//')\n lines.push('// Extensions (.zidane/extensions/<name>/index.ts) can register')\n lines.push('// their own keybindings; rebind any of them by adding a line')\n lines.push('// using the extension binding\\'s id, e.g.')\n lines.push('// \"my-ext.openPanel\": \"ctrl+shift+j\"')\n lines.push('// The Settings → Keybindings tab lists every registered id.')\n lines.push('{')\n KEYBINDING_DEFS.forEach((def, i) => {\n const trailingComma = i < KEYBINDING_DEFS.length - 1 ? ',' : ''\n lines.push(` // ${def.description}`)\n lines.push(` ${JSON.stringify(def.action)}: ${JSON.stringify(def.default)}${trailingComma}`)\n })\n lines.push('}')\n lines.push('')\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// JSONC parsing — `//` line + `/* block */` comments, single pass\n// ---------------------------------------------------------------------------\n\n/**\n * Strip `//` line comments and `/* … *\\/` block comments from a JSONC\n * source string. Stays out of string literals so a `\"foo // bar\"` value\n * keeps its slashes. Exported for unit-tests; the only runtime caller\n * is {@link readKeybindingsRaw}.\n *\n * Trade-off: this is a tiny state-machine, not a full JSONC parser. It\n * doesn't understand `\\\\` outside strings (irrelevant for JSON) and\n * doesn't track line numbers in error messages. Good enough for a\n * config file — corruption in production gracefully falls back to the\n * defaults via the try/catch in `readKeybindingsRaw`.\n *\n * @param input - JSONC source text.\n */\nexport function stripJsonComments(input: string): string {\n let out = ''\n let i = 0\n let inString = false\n let escape = false\n while (i < input.length) {\n const c = input[i]\n if (inString) {\n out += c\n if (escape)\n escape = false\n else if (c === '\\\\')\n escape = true\n else if (c === '\"')\n inString = false\n i++\n continue\n }\n if (c === '\"') {\n inString = true\n out += c\n i++\n continue\n }\n if (c === '/' && input[i + 1] === '/') {\n while (i < input.length && input[i] !== '\\n')\n i++\n continue\n }\n if (c === '/' && input[i + 1] === '*') {\n i += 2\n while (i < input.length && !(input[i] === '*' && input[i + 1] === '/'))\n i++\n i += 2\n continue\n }\n out += c\n i++\n }\n return out\n}\n","/**\n * A provider's OAuth flow rides its provider contribution.\n *\n * `ctx.registerProvider(descriptor)` with a `descriptor.oauthProvider` is all\n * an extension declares. When the registry builds, the flow joins the\n * process-wide OAuth registry (`providers/oauth-registry`) that lazy token\n * refresh and usage probes resolve through; when the registry disposes, it\n * leaves. Registering the flow from `setup` instead would run even while the\n * extension is disabled, with nothing to undo it.\n *\n * - Active extensions only. A disabled extension's descriptor is dropped\n * with the rest of its contributions, and its flow with it.\n * - Built-in flow ids (`anthropic`, `openai-codex`, …) are refused: the\n * user's own subscription tokens never refresh through extension code.\n * - Claims are counted per flow object. A host handing the same inline\n * definition to several live registries (or building the next one before\n * disposing the last) keeps the flow until the last claim goes; a reload\n * that re-imports the extension registers a NEW object, which an older\n * registry's release can't remove.\n *\n * `descriptor.oauthProvider` is read once, when the registry builds.\n */\n\nimport type { LogSink } from '../logger'\nimport type { OAuthProviderInterface } from '../providers/oauth-registry'\nimport type { LoadedExtension } from './types'\nimport { createLogger } from '../logger'\nimport { BUILTIN_OAUTH_PROVIDER_IDS, getOAuthProvider, registerOAuthProvider, unregisterOAuthProvider } from '../providers/oauth-registry'\n\n/** Process-wide, like the registry itself: every zidane copy counts the same claims. */\nconst CLAIMS_SLOT = Symbol.for('zidane.extension-oauth-claims')\n\nfunction claims(): WeakMap<object, number> {\n const slots = globalThis as { [key: symbol]: WeakMap<object, number> | undefined }\n slots[CLAIMS_SLOT] ??= new WeakMap()\n return slots[CLAIMS_SLOT]\n}\n\nfunction isFlow(value: unknown): value is OAuthProviderInterface {\n const flow = value as Partial<OAuthProviderInterface> | null\n return !!flow\n && typeof flow.id === 'string'\n && flow.id.length > 0\n && typeof flow.login === 'function'\n && typeof flow.refreshToken === 'function'\n && typeof flow.getApiKey === 'function'\n}\n\n/**\n * Register the OAuth flows of the active extensions' providers.\n *\n * @param active - Extensions whose contributions are live.\n * @param sink - Log sink for the per-extension warnings.\n * @returns Releases this registry's claims. Idempotent.\n */\nexport function claimProviderOAuthFlows(active: readonly LoadedExtension[], sink: LogSink): () => void {\n const claimed: OAuthProviderInterface[] = []\n for (const entry of active) {\n const providers = entry.contributions?.providers\n if (!providers)\n continue\n const logger = createLogger(sink, { extension: entry.name })\n for (const descriptor of Object.values(providers)) {\n const flow: unknown = descriptor.oauthProvider\n if (flow === undefined)\n continue\n if (!isFlow(flow)) {\n logger.warn('registerProvider: oauthProvider needs an id, login, refreshToken and getApiKey; its sign-in won\\'t refresh', { provider: descriptor.key })\n continue\n }\n if (BUILTIN_OAUTH_PROVIDER_IDS.has(flow.id)) {\n if (getOAuthProvider(flow.id) !== flow)\n logger.warn(`registerProvider: OAuth flow id \"${flow.id}\" belongs to a built-in sign-in; refresh keeps using the built-in flow`, { provider: descriptor.key })\n continue\n }\n const credentialKey = descriptor.credentialFileKey ?? descriptor.key\n if (flow.id !== credentialKey)\n logger.warn(`registerProvider: OAuth flow id \"${flow.id}\" differs from the credential key \"${credentialKey}\"; usage probes look the flow up by the credential key`, { provider: descriptor.key })\n const current = getOAuthProvider(flow.id)\n if (current && current !== flow && claims().has(current))\n logger.warn(`registerProvider: OAuth flow \"${flow.id}\" replaces another extension's`, { provider: descriptor.key })\n registerOAuthProvider(flow)\n claims().set(flow, (claims().get(flow) ?? 0) + 1)\n claimed.push(flow)\n }\n }\n\n let released = false\n return () => {\n if (released)\n return\n released = true\n for (const flow of claimed) {\n const left = (claims().get(flow) ?? 1) - 1\n if (left > 0) {\n claims().set(flow, left)\n continue\n }\n claims().delete(flow)\n unregisterOAuthProvider(flow)\n }\n }\n}\n","/**\n * Host runtime bridge — the late-bound capability surface behind\n * `ctx.runtime`, `ctx.sessions`, `ctx.appendNote`, and `ctx.workspace`.\n *\n * Extension contexts are built before the host mounts, so these methods\n * route through a holder the host populates post-mount (the same idiom as\n * the HUD and prompt bridge). Detached degradation is split by stakes:\n * queries and toggles (`runtime` / `sessions` / `appendNote`) return\n * honest `null` / `false` — extensions guard on the return value instead\n * of try/catching host plumbing — while `workspace` operations REJECT,\n * because a silent no-op `writeFile` masquerading as success is worse\n * than an error (see {@link ExtensionWorkspaceSurface}).\n *\n * Notes deliberately reuse the command-output vocabulary\n * ({@link CommandOutputBlock}: full markdown or collapsed diff-colorable\n * details, or `custom` blocks bound to a registered view) and persist as\n * display-only turns — excluded from the model by the same wire guard as\n * command output. What you append is what reloads: even `custom` blocks\n * persist DATA + a mandatory markdown fallback, never rendered output —\n * the renderer registry (`ctx.tui.registerRenderer`) is a pure view\n * layer resolved at render time, so a missing/disabled renderer degrades\n * to the fallback instead of corrupting history.\n */\n\nimport type { CommandOutputBlock } from '../chat/commands'\nimport type { ModelInfo } from '../chat/providers'\nimport type { ExecResult, ExecutionContext, ExecutionHandle, ListFilesEntry, ListFilesOptions } from '../contexts'\nimport type { SessionStore } from '../session'\nimport type { SessionArtifact } from '../session/artifacts'\nimport type { JsonValue, ThinkingLevel } from '../types'\nimport type { ExtensionArtifactsSurface } from './types'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport { normalizeArtifactInput } from '../session/artifacts'\n\n/**\n * One row of `ctx.runtime.models()` — a model offered by a provider the\n * user has credentials for. `provider.key` is the registry key (stable id,\n * e.g. `anthropic`); `provider.name` is the display name. `model` mirrors\n * the host's {@link ModelInfo} (id, context window, cost, …).\n */\nexport interface ExtensionModelEntry {\n /** Provider offering the model: registry `key` and display `name`. */\n provider: { key: string, name: string }\n /** Catalog entry for the model. */\n model: ModelInfo\n}\n\n/** One row of `ctx.sessions.list()`. */\nexport interface ExtensionSessionSummary {\n /** Session id. */\n id: string\n /** Session title, when it has one. */\n title?: string\n /** Last update time in epoch ms. */\n updatedAt?: number\n}\n\n/**\n * The live `(execution, handle)` pair behind `ctx.workspace`. Resolved\n * per call — never cached by the facade — because the pair changes on\n * session switch, cwd change, and registry rebuilds; a captured pair\n * would point at a destroyed container.\n */\nexport interface ExtensionWorkspacePair {\n /** Execution context the commands run in. */\n execution: ExecutionContext\n /** Handle to the live execution. */\n handle: ExecutionHandle\n}\n\n/**\n * `ctx.workspace`'s operations already bound to a session, for a host whose\n * execution lives in another process (the TUI in daemon mode reaches the\n * session's workspace over IPC). Same contract as the facade's methods.\n */\nexport type ExtensionWorkspaceOps = Omit<ExtensionWorkspaceSurface, 'available'>\n\n/**\n * What `ctx.artifacts` resolves queries against: the host's artifacts (its\n * session store, or a client's view of a host's) plus the active session's\n * identity, so `scope: 'session'` / `'project'` can be answered without the\n * extension knowing either id.\n */\nexport interface ExtensionArtifactScope {\n /** Store the artifacts are read from and written to. */\n store: Pick<SessionStore, 'listArtifacts' | 'upsertArtifact'>\n /** Active session id. Needed to answer `scope: 'session'` queries. */\n sessionId?: string\n /** Project root of the active session. Needed to answer `scope: 'project'` queries. */\n projectRoot?: string\n /** Active run id, when a run is in flight — stamped on `record`ed artifacts. */\n runId?: string\n /**\n * Host-owned write path for `ctx.artifacts.record`. Defaults to\n * `store.upsertArtifact`; hosts override to route through a daemon RPC or\n * to fan out a change signal after the write.\n */\n record?: (artifact: SessionArtifact) => Promise<SessionArtifact | null>\n}\n\n/** What a host attaches to power the runtime surfaces. */\nexport interface ExtensionHostRuntimeImpl {\n /** Returns the active model id, or `null` when none is selected. */\n model: () => string | null\n /**\n * Model catalog across every provider the user has credentials for.\n * Optional so pre-catalog host impls keep typechecking; the facade\n * treats absence as an empty catalog.\n */\n models?: () => readonly ExtensionModelEntry[]\n /** Returns the active thinking level, or `null` when none applies. */\n thinking: () => ThinkingLevel | null\n /** Sets the thinking level. Returns whether the host accepted it. */\n setThinking: (level: ThinkingLevel) => boolean\n /** Lists the sessions the user can switch to. */\n listSessions: () => Promise<readonly ExtensionSessionSummary[]>\n /** Switches to session `id`. Resolves to whether the switch happened. */\n switchSession: (id: string) => Promise<boolean>\n /** Starts a new session. Resolves to whether one was created. */\n createSession: () => Promise<boolean>\n /** Appends output blocks to the active transcript. Returns whether the host accepted them. */\n appendNote: (blocks: readonly CommandOutputBlock[]) => boolean\n /**\n * Resolve the ACTIVE session's execution pair, spawning the handle if\n * no run has created it yet (`agent.ensureExecution()`). `null` when\n * no agent/session is live; REJECT when a live agent's spawn fails\n * (daemon down, spawn timeout) so the facade surfaces the real error\n * instead of a generic \"detached\". Optional so pre-workspace host\n * impls keep typechecking; the facade treats absence as detached.\n * A host whose execution lives in another process returns session-bound\n * {@link ExtensionWorkspaceOps} instead of a pair.\n */\n workspace?: () => Promise<ExtensionWorkspacePair | ExtensionWorkspaceOps | null>\n /**\n * Side-effect-free availability signal (\"is there a live agent?\").\n * MUST NOT spawn: `workspace()` may boot a container on first call,\n * and `ctx.workspace.available()` — a guard extensions call casually,\n * even at setup — must never pay that price to answer a boolean.\n * Optional; when absent the facade falls back to `workspace()` (with\n * its side effects — implement this on any host with heavy spawns).\n */\n workspaceAvailable?: () => boolean\n /**\n * Backing for `ctx.artifacts`. Optional so pre-artifact host impls keep\n * typechecking; the facade treats absence (or a store without artifact\n * methods) as an empty store.\n */\n artifacts?: () => ExtensionArtifactScope | null\n}\n\n/**\n * Host-level session control behind `ctx.sessions.send` / `getData` /\n * `setData`: addresses any session by id, inside a run or not.\n */\nexport interface ExtensionSessionControl {\n /**\n * Queue `prompt` as the session's next user turn: runs now when idle, after\n * the current run otherwise. `false` when the host declines: the user\n * stopped the session (Esc) and has not sent since, their own queued\n * prompts are waiting, or the session is unknown.\n */\n send: (sessionId: string, prompt: string) => Promise<boolean>\n /** One value of `extension`'s per-session state (`ctx.session`), live or stored. */\n getData: (sessionId: string, extension: string, key: string) => Promise<JsonValue | undefined>\n /** Write one value of `extension`'s per-session state; `undefined` deletes it. */\n setData: (sessionId: string, extension: string, key: string, value: JsonValue | undefined) => Promise<void>\n}\n\n/** Connects a host to the runtime surfaces extensions see as `ctx.runtime`, `ctx.sessions`, `ctx.workspace` and `ctx.artifacts`. */\nexport interface ExtensionHostRuntimeBridge {\n /**\n * Process-wide attachment — a single-session host (the TUI) attaches the\n * active session's impl once and swaps it on session change.\n */\n attach: (impl: ExtensionHostRuntimeImpl) => () => void\n /**\n * Async-scoped attachment — a multi-session host (daemon, GUI) runs a\n * command for session X inside `runWith(implForX, …)`; every\n * `ctx.runtime` / `ctx.sessions` / `ctx.workspace` call made anywhere in\n * that async tree resolves to X, even while another session's command\n * runs concurrently. Takes precedence over `attach` inside the scope.\n */\n runWith: <T>(impl: ExtensionHostRuntimeImpl, fn: () => Promise<T>) => Promise<T>\n /** Returns the implementation in effect: the innermost `runWith` scope, else the attached one, else `null`. */\n current: () => ExtensionHostRuntimeImpl | null\n /** Attach the chat host's session control (one per bridge; the newest wins). Returns a detach. */\n attachSessionControl: (control: ExtensionSessionControl) => () => void\n /** The attached session control, or `null`. */\n sessionControl: () => ExtensionSessionControl | null\n}\n\n/** Creates a bridge that hosts attach their runtime implementation to. */\nexport function createExtensionHostRuntimeBridge(): ExtensionHostRuntimeBridge {\n let impl: ExtensionHostRuntimeImpl | null = null\n let control: ExtensionSessionControl | null = null\n const scoped = new AsyncLocalStorage<ExtensionHostRuntimeImpl>()\n return {\n attachSessionControl(next) {\n control = next\n return () => {\n if (control === next)\n control = null\n }\n },\n sessionControl: () => control,\n attach(next) {\n impl = next\n return () => {\n if (impl === next)\n impl = null\n }\n },\n runWith: (next, fn) => scoped.run(next, fn),\n current: () => scoped.getStore() ?? impl,\n }\n}\n\n/** `ctx.runtime` — reads/controls over the host's active model state. */\nexport interface ExtensionRuntimeSurface {\n /** Active model id, or `null` when detached / none picked. */\n model: () => string | null\n /**\n * Every model available across the user's credentialed providers.\n * Empty when detached (headless, or before the renderer mounts).\n * Use it to build model pickers (e.g. a review command letting the\n * user pin subagents to specific models).\n */\n models: () => readonly ExtensionModelEntry[]\n /** Active thinking level, or `null` when detached / not applicable. */\n thinking: () => ThinkingLevel | null\n /** Set the thinking level. `false` when detached or unsupported by the model. */\n setThinking: (level: ThinkingLevel) => boolean\n}\n\n/** `ctx.sessions` — minimal, imperative-and-done session control. */\nexport interface ExtensionSessionsSurface {\n /** Sessions visible to the host's picker. Empty when detached. */\n list: () => Promise<readonly ExtensionSessionSummary[]>\n /** Switch to a session by id. `false` when detached or the switch failed. */\n switch: (id: string) => Promise<boolean>\n /** Start a fresh session. `false` when detached. */\n create: () => Promise<boolean>\n /**\n * Queue `prompt` as session `sessionId`'s next user turn (now when idle,\n * after the current run otherwise), from anywhere: a run-settled handler,\n * a timer. `false` when detached or declined: the user stopped that\n * session with Esc and has not sent since, or their own queued prompts\n * are waiting (they go first).\n */\n send: (sessionId: string, prompt: string) => Promise<boolean>\n /** This extension's per-session value `key` (`ctx.session`'s store) of any session, inside a run or not. `undefined` when unset. Rejects when detached. */\n getData: (sessionId: string, key: string) => Promise<JsonValue | undefined>\n /** Write this extension's per-session value `key` of any session; `undefined` deletes it. Rejects when detached. */\n setData: (sessionId: string, key: string, value: JsonValue | undefined) => Promise<void>\n}\n\n/**\n * `ctx.workspace` — shell + file operations routed through the SESSION'S\n * execution context, so extension work happens wherever the agent's tools\n * run (host process, docker, remote sandbox) with the same transport\n * output caps and containment. The curated \"do work in the workspace\"\n * subset — lifecycle methods (`spawn` / `destroy` / background tasks)\n * are deliberately absent: the pair belongs to the agent.\n *\n * Unlike `ctx.runtime` / `ctx.sessions` (queries and toggles that degrade\n * to `null` / `false`), these are imperative operations whose silent\n * failure would corrupt caller logic — a no-op `writeFile` must not look\n * like success. Detached ⇒ every method REJECTS with a clear error.\n * Guard with {@link ExtensionWorkspaceSurface.available} or try/catch.\n */\nexport interface ExtensionWorkspaceSurface {\n /** `true` when a live session's execution pair is reachable. */\n available: () => Promise<boolean>\n /**\n * Run a shell command in the workspace. Resolves with the full\n * ExecResult (non-zero exits RESOLVE — inspect `exitCode`); rejects\n * only when detached or the transport itself fails. Output is\n * byte-capped by the context's transport (10MB default).\n */\n exec: (command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal }) => Promise<ExecResult>\n /** Read a file from the workspace's filesystem (whole-file — probe `fileSize` first for anything potentially large). */\n readFile: (path: string) => Promise<string>\n /** Write a file into the workspace's filesystem (parent dirs created). */\n writeFile: (path: string, content: string) => Promise<void>\n /** List a directory in the workspace — `ListFilesOptions` semantics (`glob: true` treats `path` as a pattern). */\n listFiles: (path: string, options?: ListFilesOptions) => Promise<ListFilesEntry[]>\n /** Byte size of a file, or `null` when unknowable — same contract as `ExecutionContext.fileSize`. */\n fileSize: (path: string) => Promise<number | null>\n}\n\n/**\n * Build the `ctx.artifacts` facade over a host-runtime bridge. Read-only;\n * every call re-resolves the scope (session switches swap it). Detached,\n * or a store without artifact support, answers `[]` — the same honest\n * degradation as `ctx.sessions`.\n *\n * @param resolve - Returns the current artifact scope, or `null`/`undefined` when detached. Called on every query.\n */\nexport function createArtifactsSurface(\n resolve: () => ExtensionArtifactScope | null | undefined,\n): ExtensionArtifactsSurface {\n return {\n list: async (query) => {\n const scope = resolve()\n if (!scope?.store.listArtifacts)\n return []\n const mode = query?.scope ?? 'session'\n const base = {\n ...(query?.kind !== undefined ? { kind: query.kind } : {}),\n ...(query?.since !== undefined ? { since: query.since } : {}),\n ...(query?.limit !== undefined ? { limit: query.limit } : {}),\n }\n if (mode === 'session') {\n if (!scope.sessionId)\n return []\n return scope.store.listArtifacts({ ...base, sessionId: scope.sessionId })\n }\n if (mode === 'project') {\n if (!scope.projectRoot)\n return []\n return scope.store.listArtifacts({ ...base, projectRoot: scope.projectRoot })\n }\n return scope.store.listArtifacts(base)\n },\n find: async (kind, uri) => {\n const scope = resolve()\n if (!scope?.store.listArtifacts)\n return []\n return scope.store.listArtifacts({ kind, uri })\n },\n record: async (input) => {\n const scope = resolve()\n if (!scope?.sessionId)\n return undefined\n const write = scope.record ?? scope.store.upsertArtifact?.bind(scope.store)\n if (!write)\n return undefined\n // Extension input is a trust boundary: same normalization the loop\n // applies to tool metadata (kind shape, bounds, meta size).\n const normalized = normalizeArtifactInput(input)\n if (!normalized)\n return undefined\n const now = Date.now()\n const projectRoot = normalized.projectRoot ?? scope.projectRoot\n try {\n const stored = await write({\n id: crypto.randomUUID(),\n sessionId: scope.sessionId,\n ...(scope.runId ? { runId: scope.runId } : {}),\n kind: normalized.kind,\n uri: normalized.uri,\n label: normalized.label,\n ...(projectRoot ? { projectRoot } : {}),\n ...(normalized.meta !== undefined ? { meta: normalized.meta } : {}),\n createdAt: now,\n updatedAt: now,\n })\n return stored ?? undefined\n }\n catch {\n return undefined\n }\n },\n }\n}\n\n/**\n * Build the `ctx.workspace` facade over a host-runtime bridge. Every call\n * re-resolves the live pair (see {@link ExtensionWorkspacePair}); detached\n * calls reject with a message naming the guard.\n *\n * `isAvailable` is the host's side-effect-free availability signal,\n * tri-state by design: `true`/`false` answer directly, `undefined` means\n * \"no light signal wired\" and falls back to `resolve()` — accepting its\n * side effects (a container spawn, on heavy hosts). Wire it wherever\n * spawning is non-trivial.\n *\n * @param resolve - Returns the live execution pair, or `null` when detached. Called on every operation.\n * @param isAvailable - Side-effect-free availability signal. When omitted, availability falls back to `resolve`.\n */\nexport function createWorkspaceSurface(\n resolve: () => Promise<ExtensionWorkspacePair | ExtensionWorkspaceOps | null> | ExtensionWorkspacePair | ExtensionWorkspaceOps | null,\n isAvailable?: () => boolean | undefined,\n): ExtensionWorkspaceSurface {\n const ops = async (): Promise<ExtensionWorkspaceOps> => {\n // Spawn/transport failures from `resolve` propagate as-is — a docker\n // daemon being down must read as that, not as \"detached\".\n const resolved = await resolve()\n if (!resolved)\n throw new Error('ctx.workspace is detached (no live session or host). Guard with `await ctx.workspace.available()`.')\n return 'execution' in resolved ? pairOps(resolved) : resolved\n }\n return {\n available: async () => {\n // Prefer the host's side-effect-free signal — `resolve()` may spawn\n // a container, which a boolean guard must never trigger.\n const light = isAvailable?.()\n if (light !== undefined)\n return light\n try {\n return await resolve() !== null\n }\n catch {\n return false\n }\n },\n exec: async (command, options) => (await ops()).exec(command, options),\n readFile: async path => (await ops()).readFile(path),\n writeFile: async (path, content) => (await ops()).writeFile(path, content),\n listFiles: async (path, options) => (await ops()).listFiles(path, options),\n fileSize: async path => (await ops()).fileSize(path),\n }\n}\n\n/** The workspace operations over an in-process execution pair. */\nfunction pairOps({ execution, handle }: ExtensionWorkspacePair): ExtensionWorkspaceOps {\n return {\n exec: (command, options) => execution.exec(handle, command, options),\n readFile: path => execution.readFile(handle, path),\n writeFile: (path, content) => execution.writeFile(handle, path, content),\n listFiles: (path, options) => execution.listFiles(handle, path, options),\n fileSize: async path => await execution.fileSize?.(handle, path) ?? null,\n }\n}\n","/**\n * Extension HUD — the fire-and-forget presence an extension paints around\n * the transcript, behind `ctx.tui.notify` / `setStatus` / `setBanner` and\n * `ctx.composer.setBadge`.\n *\n * A small subscribable channel (same pattern as the log ring buffer and\n * `live-settings`): extension contexts WRITE at any point in their\n * lifetime (setup, hooks, commands, timers), the host renderer SUBSCRIBES\n * and paints:\n *\n * - status → one dim line per extension in the transcript-tail slot\n * (next to the streaming throbber), optionally with a\n * loader glyph and an auto-clear timeout,\n * - banner → one block of lines per extension in the above-prompt slot\n * (stacked with the todo indicator), optionally auto-clearing,\n * - notify → a one-shot transcript line (info/warn/error toned),\n * - badges → per session, the extension's badges above that session's\n * composer (the desktop app's badge strip), keyed by an id the\n * extension picks, at most {@link MAX_COMPOSER_BADGES} each.\n *\n * Status and banner are keyed by extension name — an extension owns exactly\n * one status and one banner (set `null` to clear), so there are no ids to\n * leak; badges add their own id, capped per extension and session.\n * Writes go through generation-scoped {@link ExtensionHudWriter}s claimed\n * per registry build: registry swaps (cwd change, `/reload`) re-claim\n * under the same names, superseding the outgoing generation so its late\n * writes, timers, and dispose-time `clear` can't touch the fresh paint.\n * Notifications raised before a renderer attaches (e.g. during pre-mount\n * `setup`) are buffered (bounded) and flushed on attach.\n *\n * Hosts without a UI simply never create a HUD — the ctx methods degrade\n * to no-ops.\n */\n\nimport type { ExtensionBadgeIcon, ExtensionBadgeTone } from './types'\n\n/** The tone a one-shot notification line (`ctx.tui.notify`) is drawn with. */\nexport type ExtensionNotifyTone = 'info' | 'warn' | 'error'\n\n/** A one-shot transcript line raised by an extension through `ctx.tui.notify`. */\nexport interface ExtensionNotification {\n /** Name of the extension that raised it. */\n extension: string\n /** Line text. */\n text: string\n /** Tone the line is drawn with. */\n tone: ExtensionNotifyTone\n}\n\n/** One extension's status-line entry. */\nexport interface ExtensionHudStatus {\n /** Status text. */\n text: string\n /** Render a working glyph next to the text (long-running work). */\n loader: boolean\n}\n\n/** Snapshot of what extensions currently show in the HUD. */\nexport interface ExtensionHudState {\n /** Extension name → its single status entry. Insertion-ordered. */\n readonly statuses: Readonly<Record<string, ExtensionHudStatus>>\n /** Extension name → its banner lines. Insertion-ordered. */\n readonly banners: Readonly<Record<string, readonly string[]>>\n /**\n * Session id → extension name → that session's status (`setStatus` with\n * `sessionId`). The GUI shows them above the session's composer.\n */\n readonly sessions: Readonly<Record<string, Readonly<Record<string, ExtensionHudStatus>>>>\n /**\n * Session id → that session's composer badges (`ctx.composer.setBadge`),\n * in the order they were first set. The GUI shows them in the session's\n * composer.\n */\n readonly badges: Readonly<Record<string, readonly ExtensionHudBadge[]>>\n}\n\n/**\n * A badge an extension shows in a session's composer, through\n * `ctx.composer.setBadge`: a short label, an optional count, icon, tone and\n * loader, and the panel a click opens. JSON-safe, it crosses IPC as is. The\n * desktop app paints it in the badge strip at the top of the composer; the\n * terminal app does not draw composer badges.\n */\nexport interface ExtensionComposerBadge {\n /** The badge's name (`Goal`, `Deploys`). Cut to 40 characters. */\n label: string\n /**\n * A count or a short value after the label (`3`, `2/10`), drawn as a count\n * badge. A string is cut to 12 characters, a number past 999 shows `999+`.\n */\n count?: number | string\n /** Leading icon. The desktop app paints the icons of its badge set only (see the extensions docs). */\n icon?: ExtensionBadgeIcon\n /** Tints the icon. Default `muted`. */\n tone?: ExtensionBadgeTone\n /** Work in progress: a running arc in place of the icon. */\n loader?: boolean\n /**\n * Something waits on the user: the badge is drawn raised in the warn tone,\n * its count pulses, and the strip scrolls it into view. Set it while the\n * wait lasts, not as decoration.\n */\n highlight?: boolean\n /** Hover text and accessible description. Cut to 200 characters. */\n title?: string\n /**\n * The panel a click opens and focuses: one this extension registered (a\n * bare id, `deploys`), a full `<extension>/<id>`, or a built-in panel\n * (`todos`, `artifacts`). Without one, or naming a panel the session does\n * not have, the badge only informs.\n */\n panel?: string\n /**\n * Left-to-right position among the session's badges, lower first (default\n * 100). The desktop app's own Inbox, Todos and Queue badges are 0, 10 and\n * 20. An order the user dragged wins over it.\n */\n order?: number\n}\n\n/** Options for {@link ExtensionHudWriter.setBadge}. */\nexport interface ExtensionComposerBadgeOptions {\n /** The session whose composer shows the badge. A badge always belongs to one conversation. */\n sessionId: string\n /** Auto-clear after this many ms. */\n timeout?: number\n}\n\n/**\n * A composer badge as the HUD state holds it: normalized (strings cut,\n * `count` as text, unknown fields dropped), with its owner and id.\n */\nexport interface ExtensionHudBadge {\n /** Name of the extension that set it. */\n extension: string\n /** The id the extension gave it, unique per extension and session. */\n id: string\n /** The badge's name. */\n label: string\n /** The count or short value after the label. */\n count?: string\n /** Leading icon. */\n icon?: ExtensionBadgeIcon\n /** Tints the icon. */\n tone?: ExtensionBadgeTone\n /** A running arc in place of the icon. */\n loader?: true\n /** Something waits on the user. */\n highlight?: true\n /** Hover text and accessible description. */\n title?: string\n /** The panel a click opens, as the extension named it (the host resolves it against its catalog). */\n panel?: string\n /** Left-to-right position, lower first. */\n order?: number\n}\n\n/** How many badges one extension may show in one session's composer. */\nexport const MAX_COMPOSER_BADGES = 8\n\nconst BADGE_TONES: readonly ExtensionBadgeTone[] = ['muted', 'subtle', 'accent', 'success', 'warn', 'danger']\nconst BADGE_ID = /^[\\w.:-]{1,64}$/\n\nfunction cut(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string')\n return undefined\n const text = value.trim()\n if (!text)\n return undefined\n return text.length > max ? `${text.slice(0, max - 1)}…` : text\n}\n\n/**\n * Reduce what an extension passed to `setBadge` to a well-formed badge:\n * strings trimmed and cut, `count` as text, unknown tones and fields\n * dropped. `null` when there is no label to show.\n */\nfunction normalizeComposerBadge(raw: unknown): Omit<ExtensionHudBadge, 'extension' | 'id'> | null {\n if (!raw || typeof raw !== 'object')\n return null\n const o = raw as Record<string, unknown>\n const label = cut(o.label, 40)\n if (!label)\n return null\n const count = typeof o.count === 'number' && Number.isFinite(o.count)\n ? (o.count > 999 ? '999+' : String(o.count))\n : cut(o.count, 12)\n const iconRaw = o.icon && typeof o.icon === 'object' ? o.icon as Record<string, unknown> : undefined\n const gui = cut(iconRaw?.gui, 128)\n const tui = cut(iconRaw?.tui, 8)\n const tone = BADGE_TONES.find(t => t === o.tone)\n const title = cut(o.title, 200)\n const panel = cut(o.panel, 200)\n return {\n label,\n ...(count !== undefined ? { count } : {}),\n ...(gui || tui ? { icon: { ...(gui ? { gui } : {}), ...(tui ? { tui } : {}) } } : {}),\n ...(tone ? { tone } : {}),\n ...(o.loader === true ? { loader: true as const } : {}),\n ...(o.highlight === true ? { highlight: true as const } : {}),\n ...(title ? { title } : {}),\n ...(panel ? { panel } : {}),\n ...(typeof o.order === 'number' && Number.isFinite(o.order) ? { order: o.order } : {}),\n }\n}\n\nconst MAX_BUFFERED_NOTIFICATIONS = 20\n\n/** Options for {@link ExtensionHudWriter.setStatus}. */\nexport interface ExtensionStatusOptions {\n /** Show a working glyph next to the text. Default false. */\n loader?: boolean\n /** Auto-clear after this many ms (e.g. a transient \"✓ done\"). */\n timeout?: number\n /**\n * Scope the status to one session: it shows with that session only, and\n * each session keeps its own (`null` with the same id clears it).\n */\n sessionId?: string\n}\n\n/** Options for {@link ExtensionHudWriter.setBanner}. */\nexport interface ExtensionBannerOptions {\n /** Auto-clear after this many ms. */\n timeout?: number\n}\n\n/** Options for {@link ExtensionHudWriter.notify}. */\nexport interface ExtensionNotifyOptions {\n /** Line tone. Default `'info'`. */\n tone?: ExtensionNotifyTone\n}\n\n/**\n * A generation-scoped writer over one extension's HUD slots. A NEWER claim\n * for the same extension supersedes this one: its status/banner writes,\n * pending auto-clear timers, and `clear` become no-ops (one-shot `notify`\n * stays live — a superseded instance saying \"shutting down\" is still worth\n * showing).\n */\nexport interface ExtensionHudWriter {\n /** Sets the extension's status-line entry. `null` clears it. */\n setStatus: (text: string | null, opts?: ExtensionStatusOptions) => void\n /** Sets the extension's banner lines. `null` clears them. */\n setBanner: (lines: readonly string[] | null, opts?: ExtensionBannerOptions) => void\n /** Raises a one-shot transcript line. */\n notify: (text: string, opts?: ExtensionNotifyOptions) => void\n /**\n * Sets one of the extension's composer badges for a session; `null`\n * clears it. Returns `false`, changing nothing, when the input is not\n * valid: no `sessionId`, an id outside `[A-Za-z0-9_.:-]{1,64}`, a badge\n * without a label, or a new id past {@link MAX_COMPOSER_BADGES}. A\n * superseded or retired writer ignores the call and returns `true`.\n */\n setBadge: (id: string, badge: ExtensionComposerBadge | null, opts: ExtensionComposerBadgeOptions) => boolean\n /**\n * Wipe this extension's footprint AND retire this writer — subsequent\n * status/banner writes become inert (`notify` stays live for farewell\n * lines). Called at registry dispose so teardown closures and leaked\n * timers can't repaint after the extension is gone. No-op once\n * superseded by a newer claim.\n */\n clear: () => void\n}\n\n/** Shared HUD state that extensions write to and the host renders: statuses, banners and one-shot notifications. */\nexport interface ExtensionHud {\n /**\n * Claim the (single) writer for an extension. Claimed once per registry\n * build; supersedes any prior claim for the same name, which is what\n * makes swap-then-dispose ordering safe.\n */\n claim: (extension: string) => ExtensionHudWriter\n /** Force-wipe an extension's footprint regardless of generation. */\n clearExtension: (extension: string) => void\n /** Drop every status and badge scoped to `sessionId` (the session was deleted). */\n clearSession: (sessionId: string) => void\n /** Returns the current statuses, banners and badges. */\n getState: () => ExtensionHudState\n /** Re-render trigger for state changes (status, banner, badge). */\n subscribe: (listener: () => void) => () => void\n /**\n * Attach the one-shot notification sink (the TUI's transcript appender).\n * Buffered pre-attach notifications flush immediately, oldest first.\n * Returns a detach fn; only one sink at a time (last attach wins).\n */\n onNotify: (sink: (notification: ExtensionNotification) => void) => () => void\n}\n\n/**\n * What a `setBadge` call asks for, validated: the session, and the badge to\n * set (`null`: clear it). `null` for a call that cannot apply.\n */\nfunction readBadgeCall(id: unknown, badge: unknown, opts: unknown): { sessionId: string, badge: Omit<ExtensionHudBadge, 'extension' | 'id'> | null } | null {\n const raw = opts && typeof opts === 'object' ? (opts as Record<string, unknown>).sessionId : undefined\n const sessionId = typeof raw === 'string' && raw.length <= 512 ? raw : ''\n if (!sessionId || typeof id !== 'string' || !BADGE_ID.test(id))\n return null\n if (badge === null)\n return { sessionId, badge: null }\n const normalized = normalizeComposerBadge(badge)\n return normalized ? { sessionId, badge: normalized } : null\n}\n\n/**\n * The HUD's composer badges: session id → `<extension>\\u0000<id>` → badge.\n * A Map per session keeps the first-set order through updates.\n */\nfunction createBadgeBook() {\n // No prototype: a session id is any string (`__proto__`, `constructor`).\n const bySession: Record<string, Map<string, ExtensionHudBadge>> = Object.create(null)\n const keyOf = (extension: string, id: string): string => `${extension}\\u0000${id}`\n return {\n /** Whether `extension` may set `id` in `sessionId`: an update, or a new badge within the cap. */\n fits(sessionId: string, extension: string, id: string): boolean {\n const byKey = bySession[sessionId]\n if (!byKey || byKey.has(keyOf(extension, id)))\n return true\n let mine = 0\n for (const badge of byKey.values()) {\n if (badge.extension === extension)\n mine++\n }\n return mine < MAX_COMPOSER_BADGES\n },\n set(sessionId: string, badge: ExtensionHudBadge): void {\n (bySession[sessionId] ??= new Map()).set(keyOf(badge.extension, badge.id), badge)\n },\n /** Drop one badge (`id`) or every badge of `extension` in `sessionId`; whether any went. */\n drop(sessionId: string, extension: string, id?: string): boolean {\n const byKey = bySession[sessionId]\n if (!byKey)\n return false\n let had = false\n for (const [key, badge] of byKey) {\n if (badge.extension === extension && (id === undefined || badge.id === id)) {\n byKey.delete(key)\n had = true\n }\n }\n if (byKey.size === 0)\n delete bySession[sessionId]\n return had\n },\n /** Drop every session's badges of `extension`; whether any went. */\n dropExtension(extension: string): boolean {\n let had = false\n for (const sessionId of Object.keys(bySession))\n had = this.drop(sessionId, extension) || had\n return had\n },\n /** Drop a session's badges; whether it had any. */\n dropSession(sessionId: string): boolean {\n const had = sessionId in bySession\n delete bySession[sessionId]\n return had\n },\n snapshot(): Record<string, ExtensionHudBadge[]> {\n return Object.fromEntries(Object.entries(bySession).map(([id, byKey]) => [id, [...byKey.values()]]))\n },\n }\n}\n\n/** Creates an empty HUD for a host to pass to the extension registry. */\nexport function createExtensionHud(): ExtensionHud {\n const statuses: Record<string, ExtensionHudStatus> = {}\n const banners: Record<string, readonly string[]> = {}\n const sessionStatuses: Record<string, Record<string, ExtensionHudStatus>> = {}\n const badges = createBadgeBook()\n /** Current writer generation per extension — the newest claim owns the slots. */\n const owners: Record<string, symbol> = {}\n let state: ExtensionHudState = { statuses: {}, banners: {}, sessions: {}, badges: {} }\n const listeners = new Set<() => void>()\n let notifySink: ((n: ExtensionNotification) => void) | null = null\n const buffered: ExtensionNotification[] = []\n\n const commit = (): void => {\n state = {\n statuses: { ...statuses },\n banners: { ...banners },\n sessions: Object.fromEntries(Object.entries(sessionStatuses).map(([id, byExtension]) => [id, { ...byExtension }])),\n badges: badges.snapshot(),\n }\n for (const listener of listeners) {\n try {\n listener()\n }\n catch {\n // A renderer callback must never break extension writes.\n }\n }\n }\n\n const dropSessionStatus = (sessionId: string, extension: string): boolean => {\n const byExtension = sessionStatuses[sessionId]\n if (!byExtension || !(extension in byExtension))\n return false\n delete byExtension[extension]\n if (Object.keys(byExtension).length === 0)\n delete sessionStatuses[sessionId]\n return true\n }\n\n const wipe = (extension: string): void => {\n let had = extension in statuses || extension in banners\n delete statuses[extension]\n delete banners[extension]\n for (const sessionId of Object.keys(sessionStatuses))\n had = dropSessionStatus(sessionId, extension) || had\n had = badges.dropExtension(extension) || had\n if (had)\n commit()\n }\n\n const emitNotification = (notification: ExtensionNotification): void => {\n if (notifySink) {\n try {\n notifySink(notification)\n }\n catch {\n // The transcript appender must never throw into extension code.\n }\n return\n }\n buffered.push(notification)\n if (buffered.length > MAX_BUFFERED_NOTIFICATIONS)\n buffered.splice(0, buffered.length - MAX_BUFFERED_NOTIFICATIONS)\n }\n\n return {\n claim(extension) {\n const generation = Symbol(extension)\n owners[extension] = generation\n const owns = (): boolean => owners[extension] === generation\n // Auto-clear timers, per slot. Rearmed on every write, cancelled on\n // clear, and generation-guarded like every other write path.\n let statusTimer: ReturnType<typeof setTimeout> | undefined\n let bannerTimer: ReturnType<typeof setTimeout> | undefined\n const sessionTimers = new Map<string, ReturnType<typeof setTimeout>>()\n // Keyed `<sessionId>\\u0000<badge id>`.\n const badgeTimers = new Map<string, ReturnType<typeof setTimeout>>()\n const disarm = (timer: ReturnType<typeof setTimeout> | undefined): void => {\n if (timer !== undefined)\n clearTimeout(timer)\n }\n\n const setSessionStatus = (sessionId: string, text: string | null, opts?: ExtensionStatusOptions): void => {\n disarm(sessionTimers.get(sessionId))\n sessionTimers.delete(sessionId)\n if (text === null || text === '')\n dropSessionStatus(sessionId, extension)\n else\n (sessionStatuses[sessionId] ??= {})[extension] = { text, loader: opts?.loader ?? false }\n commit()\n if (text && opts?.timeout !== undefined && opts.timeout > 0)\n sessionTimers.set(sessionId, setTimeout(() => owns() && setSessionStatus(sessionId, null), opts.timeout))\n }\n\n const setStatus = (text: string | null, opts?: ExtensionStatusOptions): void => {\n if (!owns())\n return\n if (opts?.sessionId)\n return setSessionStatus(opts.sessionId, text, opts)\n disarm(statusTimer)\n statusTimer = undefined\n if (text === null || text === '')\n delete statuses[extension]\n else\n statuses[extension] = { text, loader: opts?.loader ?? false }\n commit()\n if (text && opts?.timeout !== undefined && opts.timeout > 0)\n statusTimer = setTimeout(setStatus, opts.timeout, null)\n }\n\n const setBadge = (id: string, badge: ExtensionComposerBadge | null, opts: ExtensionComposerBadgeOptions): boolean => {\n if (!owns())\n return true\n const call = readBadgeCall(id, badge, opts)\n if (!call || (call.badge && !badges.fits(call.sessionId, extension, id)))\n return false\n const { sessionId } = call\n const timerKey = `${sessionId}\\u0000${id}`\n disarm(badgeTimers.get(timerKey))\n badgeTimers.delete(timerKey)\n if (!call.badge) {\n if (badges.drop(sessionId, extension, id))\n commit()\n return true\n }\n badges.set(sessionId, { extension, id, ...call.badge })\n commit()\n if (opts.timeout !== undefined && opts.timeout > 0)\n badgeTimers.set(timerKey, setTimeout(() => owns() && setBadge(id, null, { sessionId }), opts.timeout))\n return true\n }\n\n const setBanner = (lines: readonly string[] | null, opts?: ExtensionBannerOptions): void => {\n if (!owns())\n return\n disarm(bannerTimer)\n bannerTimer = undefined\n const kept = lines?.filter(line => line.length > 0)\n if (!kept || kept.length === 0)\n delete banners[extension]\n else\n banners[extension] = kept\n commit()\n if (kept && kept.length > 0 && opts?.timeout !== undefined && opts.timeout > 0)\n bannerTimer = setTimeout(setBanner, opts.timeout, null)\n }\n\n return {\n setStatus,\n setBanner,\n setBadge,\n notify(text, opts) {\n emitNotification({ extension, text, tone: opts?.tone ?? 'info' })\n },\n clear() {\n disarm(statusTimer)\n disarm(bannerTimer)\n for (const timer of [...sessionTimers.values(), ...badgeTimers.values()])\n disarm(timer)\n sessionTimers.clear()\n badgeTimers.clear()\n statusTimer = undefined\n bannerTimer = undefined\n if (owns()) {\n // Retire the generation: a disposed extension's late writes\n // (teardown closures, leaked intervals) must be inert. On a\n // swap/reload the successor already owns the name, so this\n // branch never runs and the fresh paint survives.\n delete owners[extension]\n wipe(extension)\n }\n },\n }\n },\n clearExtension: wipe,\n clearSession(sessionId) {\n const hadStatuses = sessionId in sessionStatuses\n delete sessionStatuses[sessionId]\n if (badges.dropSession(sessionId) || hadStatuses)\n commit()\n },\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n onNotify(sink) {\n notifySink = sink\n while (buffered.length > 0) {\n const next = buffered.shift()\n if (next) {\n try {\n sink(next)\n }\n catch {}\n }\n }\n return () => {\n if (notifySink === sink)\n notifySink = null\n }\n },\n }\n}\n","/**\n * `ctx.mcp` — what an extension sees of the user's MCP servers.\n *\n * The host owns discovery, the allowlist, connections, and credentials\n * ({@link ExtensionMcpHost}; `createExtensionMcpHost` in `chat/` is the\n * standard implementation every bundled host passes). This module only\n * binds a host to one extension's cwd and normalizes what crosses back.\n * Renderer-neutral: no connection code lives here.\n */\n\nimport type { ExtensionMcpServerInfo, ExtensionMcpSurface, ExtensionMcpToolResult } from './types'\n\n/** The host side of `ctx.mcp`, shared by every extension of a registry build. */\nexport interface ExtensionMcpHost {\n /** Enabled servers for `cwd` (discovered `mcps.json` entries through the allowlist). */\n servers: (cwd: string) => Promise<readonly ExtensionMcpServerInfo[]>\n /** Call one of `server`'s tools by its own (un-namespaced) name. */\n callTool: (\n cwd: string,\n server: string,\n tool: string,\n args: Record<string, unknown>,\n opts: { timeout?: number, signal?: AbortSignal },\n ) => Promise<ExtensionMcpToolResult>\n /** Close every connection this host opened. */\n dispose?: () => Promise<void>\n}\n\n/**\n * Bind `host` to an extension's cwd. Host failures in `servers()` read as \"none\".\n *\n * @param host - Host that owns MCP discovery and connections.\n * @param cwd - Working directory the extension runs in. Servers are resolved for it.\n * @param signal - Aborts when the registry is disposed. Tool calls after that reject.\n */\nexport function createMcpSurface(host: ExtensionMcpHost, cwd: string, signal: AbortSignal): ExtensionMcpSurface {\n return {\n servers: async () => {\n try {\n return await host.servers(cwd)\n }\n catch {\n return []\n }\n },\n callTool: async (server, tool, args = {}, opts = {}) => {\n if (signal.aborted)\n throw new Error('ctx.mcp is closed: the extension registry was disposed')\n const combined = opts.signal ? AbortSignal.any([opts.signal, signal]) : signal\n const result = await host.callTool(cwd, server, tool, args, { ...opts, signal: combined })\n return { text: String(result.text ?? ''), isError: result.isError === true }\n },\n }\n}\n","/**\n * Shared state + helpers for the extension context builders\n * (`register-context*.ts`): per-extension sinks, the late-call gate, the\n * safe-record-key guards, and the storage-path / provenance helpers.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { Command } from '../chat/commands'\nimport type { CompletionProvider } from '../chat/completion'\nimport type { InlineAutocompleteProvider } from '../chat/inline-autocomplete'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { Theme } from '../chat/theme'\nimport type { Logger } from '../logger'\nimport type { McpServerConfig } from '../mcp'\nimport type { LoadedPlugin } from '../plugins'\nimport type { SkillConfig } from '../skills'\nimport type { SubagentDef, SubagentOverride } from '../tools/spawn'\nimport type { ToolDef } from '../tools/types'\nimport type { ExtensionConfigCategory, StoredExtensionConfigItem } from './config'\nimport type { ExtensionPanelEntry } from './panels'\nimport type {\n ExtensionArtifactRendererEntry,\n ExtensionContext,\n ExtensionContributions,\n ExtensionCustomRendererEntry,\n ExtensionDefinition,\n ExtensionFooterHint,\n ExtensionKeybinding,\n ExtensionModal,\n ExtensionOrderedContribution,\n ExtensionPaletteCommand,\n ExtensionPaletteProviderEntry,\n ExtensionRunSettledHandler,\n ExtensionSource,\n ExtensionStorageScope,\n ExtensionToolRendererEntry,\n LoadedPluginRecord,\n} from './types'\nimport { resolve, sep } from 'node:path'\n\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\nexport function createRecord<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>\n}\n\nexport function isSafeRecordKey(key: string): boolean {\n return key.length > 0 && !DANGEROUS_RECORD_KEYS.has(key)\n}\n\nexport function warnUnsafeKey(logger: Logger, method: string, key: string): void {\n logger.warn(`${method}: refused unsafe key`, { key })\n}\n\n/**\n * Resolve a path inside one extension's storage root for the given scope.\n *\n * A module-level function (rather than a closure inside `makeContext`) so\n * `ctx.loadAgentPlugin` can derive its `${PLUGIN_DATA}` parent from the same\n * rules without depending on declaration order within the context builder.\n */\nexport function extensionStoragePath(\n paths: ExtensionContext['paths'],\n extensionName: string,\n scope: ExtensionStorageScope,\n ...segments: string[]\n): string {\n const base = scope === 'cache'\n ? paths.cacheDir\n : scope === 'data'\n ? paths.dataDir\n : scope === 'project' || scope === 'session'\n ? (paths.projectRoot ?? paths.dataDir)\n : (paths.configDir ?? paths.dataDir)\n const root = resolve(base, 'extensions', extensionName)\n const target = resolve(root, ...segments)\n // Containment guard: `..` segments must not let an extension escape its\n // own storage root. Clamp back to the root instead of handing out a path\n // outside it.\n if (target !== root && !target.startsWith(root + sep))\n return root\n return target\n}\n\n/**\n * Stamp the contributing extension onto a skill.\n *\n * `SkillConfig.source` carries the scope (project / user / inline) and is\n * defaulted here for extensions that don't set it. The extension NAME goes in\n * `metadata` under the `zidane.` prefix the Agent Skills spec reserves for\n * client-specific keys, so the skill stays spec-conformant while a host UI can\n * still say which extension shipped it.\n */\nexport function withProvenance(skill: SkillConfig, source: ExtensionSource, extensionName: string): SkillConfig {\n return {\n ...skill,\n source: skill.source ?? source,\n metadata: { ...skill.metadata, 'zidane.extension': extensionName },\n }\n}\n\n/**\n * Rejected-plugin placeholder for `ctx.loadAgentPlugin` calls that never\n * reach the loader (missing `dir`, or a late call after `setup` resolved).\n * Returning a `LoadedPlugin` with `ok: false` rather than throwing keeps the\n * method's contract uniform — callers branch on `ok`, never on exceptions.\n */\nexport function emptyPluginHandle(dir: string): LoadedPlugin {\n return {\n ok: false,\n dir,\n root: dir,\n skills: [],\n mcpServers: [],\n components: { skills: 'unsupported', mcp: 'unsupported' },\n diagnostics: [],\n }\n}\n\nexport interface GateState { open: boolean }\n\nexport interface ContextSinks {\n tools: Record<string, ToolDef>\n hooks: { [E in keyof AgentHooks]?: Array<AgentHooks[E]> }\n mcpServers: McpServerConfig[]\n skills: SkillConfig[]\n plugins: LoadedPluginRecord[]\n systemFragments: string[]\n runSettled: ExtensionRunSettledHandler[]\n contextBreakdownSections: ExtensionContributions['contextBreakdownSections'][number][]\n contextPanelSections: ExtensionContributions['contextPanelSections'][number][]\n toolResultMetadata: ExtensionContributions['toolResultMetadata'][number][]\n artifactKinds: ExtensionContributions['artifactKinds'][number][]\n references: ExtensionContributions['references'][number][]\n badgeActions: ExtensionContributions['badgeActions'][number][]\n runTransforms: ExtensionContributions['runTransforms'][number][]\n behaviorTransforms: ExtensionContributions['behaviorTransforms'][number][]\n providerMiddlewares: ExtensionContributions['providerMiddlewares'][number][]\n toolCatalogTransforms: ExtensionContributions['toolCatalogTransforms'][number][]\n mcpConfigTransforms: ExtensionContributions['mcpConfigTransforms'][number][]\n skillPolicies: ExtensionContributions['skillPolicies'][number][]\n promptTransforms: ExtensionContributions['promptTransforms'][number][]\n completionProviders: CompletionProvider[]\n inlineAutocompleteProviders: InlineAutocompleteProvider[]\n commands: Command[]\n themes: Record<string, Theme>\n agents: Record<string, AgentProfile>\n providers: Record<string, ProviderDescriptor>\n subagents: Record<string, SubagentDef>\n subagentOverrides: Record<string, SubagentOverride>\n configCategories: Record<string, ExtensionConfigCategory>\n configItems: Record<string, StoredExtensionConfigItem>\n keybindings: ExtensionKeybinding[]\n footerHints: ExtensionFooterHint[]\n modals: ExtensionModal[]\n renderers: ExtensionCustomRendererEntry[]\n toolRenderers: ExtensionToolRendererEntry[]\n artifactRenderers: ExtensionArtifactRendererEntry[]\n guiPanels: ExtensionPanelEntry[]\n /** Static palette commands; folded into one provider per extension at the end of setup. */\n paletteCommands: ExtensionPaletteCommand[]\n paletteProviders: ExtensionPaletteProviderEntry[]\n}\n\n/** Fresh, empty per-extension sinks (one set per `setup` call). */\nexport function createEmptySinks(): ContextSinks {\n return {\n tools: createRecord(),\n hooks: {},\n mcpServers: [],\n skills: [],\n plugins: [],\n systemFragments: [],\n runSettled: [],\n contextBreakdownSections: [],\n contextPanelSections: [],\n toolResultMetadata: [],\n artifactKinds: [],\n references: [],\n badgeActions: [],\n runTransforms: [],\n behaviorTransforms: [],\n providerMiddlewares: [],\n toolCatalogTransforms: [],\n mcpConfigTransforms: [],\n skillPolicies: [],\n promptTransforms: [],\n completionProviders: [],\n inlineAutocompleteProviders: [],\n commands: [],\n themes: createRecord(),\n agents: createRecord(),\n providers: createRecord(),\n subagents: createRecord(),\n subagentOverrides: createRecord(),\n configCategories: createRecord(),\n configItems: createRecord(),\n keybindings: [],\n footerHints: [],\n modals: [],\n renderers: [],\n toolRenderers: [],\n artifactRenderers: [],\n guiPanels: [],\n paletteCommands: [],\n paletteProviders: [],\n }\n}\n\n/**\n * The per-extension closure constants `makeContext` hands to each area\n * builder. Every field is fixed for the context's lifetime; `guardOpen`\n * reads the shared gate live.\n */\nexport interface ContextScope {\n definition: ExtensionDefinition\n source: ExtensionSource\n dir: string | undefined\n logger: Logger\n sinks: ContextSinks\n paths: ExtensionContext['paths']\n guardOpen: (method: string) => boolean\n ordered: <T>(value: T) => ExtensionOrderedContribution<T>\n}\n","/**\n * Agent-surface registration members of the extension context: tools, hooks,\n * MCP servers, skills, Agent Plugins, completion / commands / themes / agents\n * / subagents / providers, prompt fragments, context sections, tool-result\n * metadata and artifact kinds.\n */\n\nimport type { ContextExtensionRow, ContextExtensionSection } from '../chat/context-breakdown'\nimport type { ContextScope } from './register-context-shared'\nimport type { ExtensionContext, ExtensionContributions } from './types'\nimport { basename, isAbsolute, resolve } from 'node:path'\nimport { CRED_KEY_SLOT_SEPARATOR, isReservedCredKey } from '../chat/cred-keys'\nimport { loadAgentPlugin } from '../plugins'\nimport { ARTIFACT_KIND_PATTERN } from '../session/artifacts'\nimport { createArtifactDetectHook } from './artifact-detect'\nimport { compileReferencePattern } from './references'\nimport { emptyPluginHandle, extensionStoragePath, isSafeRecordKey, warnUnsafeKey, withProvenance } from './register-context-shared'\n\nexport function buildCoreRegistrations(scope: ContextScope) {\n const { definition, source, dir, logger, sinks, paths, guardOpen } = scope\n\n const registerTool: ExtensionContext['registerTool'] = (name, tool) => {\n if (!guardOpen('registerTool'))\n return\n if (typeof name !== 'string' || !name) {\n logger.warn('registerTool: name must be a non-empty string')\n return\n }\n if (!isSafeRecordKey(name)) {\n warnUnsafeKey(logger, 'registerTool', name)\n return\n }\n sinks.tools[name] = tool\n }\n\n const registerHook: ExtensionContext['registerHook'] = (event, handler) => {\n if (!guardOpen('registerHook'))\n return\n if (typeof handler !== 'function') {\n logger.warn('registerHook: handler must be a function', { event: String(event) })\n return\n }\n // Bucket per event. Each bucket holds handlers compatible with this\n // event's signature; the cast collapses the union member down to its\n // own array slot.\n const bucket = (sinks.hooks[event] ??= []) as Array<typeof handler>\n bucket.push(handler)\n }\n\n const registerMcpServer: ExtensionContext['registerMcpServer'] = (config) => {\n if (!guardOpen('registerMcpServer'))\n return\n if (!config || typeof config.name !== 'string') {\n logger.warn('registerMcpServer: missing name')\n return\n }\n sinks.mcpServers.push(config)\n }\n\n const registerSkill: ExtensionContext['registerSkill'] = (skill) => {\n if (!guardOpen('registerSkill'))\n return\n if (!skill || typeof skill.name !== 'string') {\n logger.warn('registerSkill: missing name')\n return\n }\n // Default the skill's source to the extension's own scope so a\n // project / user extension's skills carry the right badge —\n // `ExtensionSource` and `SkillSource` share the same vocabulary.\n //\n // `source` is the SCOPE, not the identity: two extensions both loaded from\n // `~/.agents/extensions` are indistinguishable by it. The settings Skills\n // tab needs to name the contributor, so stamp that separately under the\n // `zidane.` metadata prefix the Agent Skills spec reserves for clients.\n sinks.skills.push(withProvenance(skill, source, definition.name))\n }\n\n const loadPlugin: ExtensionContext['loadAgentPlugin'] = async (spec) => {\n const options = typeof spec === 'string' ? { dir: spec } : spec\n const requested = options?.dir\n if (typeof requested !== 'string' || requested.length === 0) {\n logger.warn('loadAgentPlugin: missing dir')\n return emptyPluginHandle('')\n }\n if (!guardOpen('loadAgentPlugin'))\n return emptyPluginHandle(requested)\n\n // Relative specs resolve against the extension's own directory so an\n // extension can vendor plugins inside itself. Inline extensions have no\n // directory — fall back to the runtime cwd.\n const target = isAbsolute(requested) ? requested : resolve(dir ?? paths.cwd, requested)\n\n const loaded = await loadAgentPlugin(target, {\n // Plugin components inherit the extension's own scope, exactly as\n // `registerSkill` does — a project extension's plugin is project-scoped.\n source,\n // `${PLUGIN_DATA}` lands inside the LOADING EXTENSION's storage root, so\n // it inherits that root's containment guard and two extensions loading a\n // same-named plugin don't silently share a data directory.\n dataRoot: extensionStoragePath(paths, definition.name, 'data', 'plugin-data'),\n ...(options.skills !== undefined ? { skills: options.skills } : {}),\n ...(options.mcp !== undefined ? { mcp: options.mcp } : {}),\n ...(options.namePrefix !== undefined ? { namePrefix: options.namePrefix } : {}),\n })\n\n // Diagnostics are logged, never thrown: a bad third-party package must\n // not fail the extension that merely tried to load it.\n for (const diagnostic of loaded.diagnostics) {\n const detail = { plugin: loaded.manifest?.name ?? target, code: diagnostic.code, ...(diagnostic.path ? { path: diagnostic.path } : {}) }\n if (diagnostic.severity === 'error')\n logger.error(`loadAgentPlugin: ${diagnostic.message}`, detail)\n else\n logger.warn(`loadAgentPlugin: ${diagnostic.message}`, detail)\n }\n // Re-check the gate AFTER the await. `sinks` arrays are the same\n // references published on `LoadedExtension.contributions`, so pushing\n // into them once the registry is sealed would mutate live state behind\n // the host's back. A load still in flight when `setup()` resolves is\n // dropped with the same warning any other late call gets.\n if (!guardOpen('loadAgentPlugin (completed after setup resolved)'))\n return loaded\n\n if (loaded.ok) {\n // A plugin's skills already carry `zidane.plugin`; adding\n // `zidane.extension` records that THIS extension is what pulled the\n // plugin in, which is the \"via extension: <name>\" origin the plugin\n // rows show. Both keys together describe the full path.\n for (const skill of loaded.skills)\n sinks.skills.push(withProvenance(skill, source, definition.name))\n for (const config of loaded.mcpServers)\n sinks.mcpServers.push(config)\n }\n // Recorded so hosts can tell a plugin's contributions apart from ones the\n // extension author wrote by hand — the settings list needs a row and a\n // toggle key per plugin, and neither is recoverable from the fan-out.\n //\n // A REJECTED plugin is recorded too, contributing nothing. Same reasoning\n // as an errored extension keeping its row: \"the plugin you pointed at is\n // broken\" is more useful than the plugin silently not existing, and disk\n // discovery surfaces rejected plugins the same way.\n sinks.plugins.push({\n name: loaded.manifest?.name ?? basename(target),\n extensionName: definition.name,\n dir: target,\n plugin: loaded,\n })\n\n logger.debug('loadAgentPlugin: registered', {\n plugin: loaded.manifest?.name,\n skills: loaded.skills.length,\n mcpServers: loaded.mcpServers.length,\n })\n return loaded\n }\n\n return { registerTool, registerHook, registerMcpServer, registerSkill, loadPlugin }\n}\n\nexport function buildCatalogRegistrations(scope: ContextScope) {\n const { logger, sinks, guardOpen } = scope\n\n const registerCompletionProvider: ExtensionContext['registerCompletionProvider'] = (provider) => {\n if (!guardOpen('registerCompletionProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || typeof provider.trigger !== 'string') {\n logger.warn('registerCompletionProvider: missing id or trigger')\n return\n }\n if (Array.from(provider.trigger).length !== 1) {\n logger.warn('registerCompletionProvider: trigger must be exactly one character', { id: provider.id, trigger: provider.trigger })\n return\n }\n sinks.completionProviders.push(provider)\n }\n\n const registerInlineAutocompleteProvider: ExtensionContext['registerInlineAutocompleteProvider'] = (provider) => {\n if (!guardOpen('registerInlineAutocompleteProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || provider.id.length === 0) {\n logger.warn('registerInlineAutocompleteProvider: missing id')\n return\n }\n if (typeof provider.suggest !== 'function') {\n logger.warn('registerInlineAutocompleteProvider: needs a suggest()', { id: provider.id })\n return\n }\n sinks.inlineAutocompleteProviders.push(provider)\n }\n\n const registerCommand: ExtensionContext['registerCommand'] = (command) => {\n if (!guardOpen('registerCommand'))\n return\n if (!command || typeof command.name !== 'string' || command.name.length === 0) {\n logger.warn('registerCommand: missing name')\n return\n }\n if (/\\s/.test(command.name) || command.name.startsWith('/')) {\n logger.warn('registerCommand: name must be a single token without a leading slash', { name: command.name })\n return\n }\n if (typeof command.run !== 'function' && !(Array.isArray(command.steps) && command.steps.length > 0)) {\n logger.warn('registerCommand: needs a run() or a non-empty steps[]', { name: command.name })\n return\n }\n if (command.aliases !== undefined) {\n const bad = !Array.isArray(command.aliases)\n || command.aliases.some(a => typeof a !== 'string' || a.length === 0 || /\\s/.test(a) || a.startsWith('/'))\n if (bad) {\n logger.warn('registerCommand: aliases must be single tokens without a leading slash', { name: command.name })\n return\n }\n }\n sinks.commands.push(command)\n }\n\n const registerTheme: ExtensionContext['registerTheme'] = (theme) => {\n if (!guardOpen('registerTheme'))\n return\n if (!theme || typeof theme.id !== 'string') {\n logger.warn('registerTheme: missing id')\n return\n }\n if (!isSafeRecordKey(theme.id)) {\n warnUnsafeKey(logger, 'registerTheme', theme.id)\n return\n }\n sinks.themes[theme.id] = theme\n }\n\n const registerAgent: ExtensionContext['registerAgent'] = (profile) => {\n if (!guardOpen('registerAgent'))\n return\n if (!profile || typeof profile.id !== 'string') {\n logger.warn('registerAgent: missing id')\n return\n }\n if (!isSafeRecordKey(profile.id)) {\n warnUnsafeKey(logger, 'registerAgent', profile.id)\n return\n }\n sinks.agents[profile.id] = profile\n }\n\n const registerSubagent: ExtensionContext['registerSubagent'] = (def) => {\n if (!guardOpen('registerSubagent'))\n return\n if (!def || typeof def.name !== 'string' || !def.name) {\n logger.warn('registerSubagent: missing name')\n return\n }\n if (!isSafeRecordKey(def.name)) {\n warnUnsafeKey(logger, 'registerSubagent', def.name)\n return\n }\n // Store keyed by name; drop the redundant `name` field from the\n // stored def so the registry map key is the single source of truth.\n const { name, ...rest } = def\n sinks.subagents[name] = rest\n }\n\n const registerSubagentOverride: ExtensionContext['registerSubagentOverride'] = (name, fn) => {\n if (!guardOpen('registerSubagentOverride'))\n return\n if (typeof name !== 'string' || !name) {\n logger.warn('registerSubagentOverride: name must be a non-empty string')\n return\n }\n if (!isSafeRecordKey(name)) {\n warnUnsafeKey(logger, 'registerSubagentOverride', name)\n return\n }\n if (typeof fn !== 'function') {\n logger.warn('registerSubagentOverride: fn must be a function', { name })\n return\n }\n // Compose within this extension in registration order — a later\n // override for the same name wraps the earlier one (`fn(prev(def))`).\n const prev = sinks.subagentOverrides[name]\n sinks.subagentOverrides[name] = prev ? def => fn(prev(def)) : fn\n }\n\n const registerProvider: ExtensionContext['registerProvider'] = (descriptor) => {\n if (!guardOpen('registerProvider'))\n return\n if (!descriptor || typeof descriptor.key !== 'string') {\n logger.warn('registerProvider: missing key')\n return\n }\n if (!isSafeRecordKey(descriptor.key)) {\n warnUnsafeKey(logger, 'registerProvider', descriptor.key)\n return\n }\n // The credential store appends `#`-qualified slots (`#alt`, `#account:N`)\n // for a provider's extra credentials, so a descriptor key containing the\n // separator could share storage with another provider's slot. Refuse here\n // rather than let the writers throw once the user authenticates.\n const credKey = descriptor.credentialFileKey ?? descriptor.key\n if (isReservedCredKey(credKey)) {\n logger.warn(\n `registerProvider: refused reserved credential key \"${credKey}\" `\n + `(\"${CRED_KEY_SLOT_SEPARATOR}\" separates a provider's extra credential slots)`,\n )\n return\n }\n sinks.providers[descriptor.key] = descriptor\n }\n\n return {\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n }\n}\n\nexport function buildContextRegistrations(scope: ContextScope) {\n const { definition, logger, sinks, guardOpen, ordered } = scope\n\n const registerSystemPromptFragment: ExtensionContext['registerSystemPromptFragment'] = (text) => {\n if (!guardOpen('registerSystemPromptFragment'))\n return\n if (typeof text !== 'string' || text.length === 0)\n return\n sinks.systemFragments.push(text)\n }\n\n const registerContextBreakdownSection: ExtensionContext['registerContextBreakdownSection'] = (contributor) => {\n if (!guardOpen('registerContextBreakdownSection'))\n return\n if (typeof contributor !== 'function') {\n logger.warn('registerContextBreakdownSection: contributor must be a function')\n return\n }\n const wrapped = (ctx => contributor(ctx)) as typeof contributor & { __zidaneExtensionName?: string }\n Object.defineProperty(wrapped, '__zidaneExtensionName', {\n value: definition.name,\n enumerable: false,\n })\n sinks.contextBreakdownSections.push(wrapped)\n }\n\n const registerContextPanelSection: ExtensionContext['registerContextPanelSection'] = (section) => {\n if (!guardOpen('registerContextPanelSection'))\n return\n if (!section || typeof section.id !== 'string' || !section.id || typeof section.title !== 'string') {\n logger.warn('registerContextPanelSection: missing id or title')\n return\n }\n if (!isSafeRecordKey(section.id)) {\n warnUnsafeKey(logger, 'registerContextPanelSection', section.id)\n return\n }\n const contributor: ExtensionContributions['contextPanelSections'][number] = async (ctx) => {\n const sections: ContextExtensionSection[] = []\n const rows = typeof section.rows === 'function' ? section.rows(ctx) : section.rows\n const loaded = section.load ? await section.load(ctx) : undefined\n if (rows && rows.length > 0) {\n sections.push({ id: section.id, label: section.title, ...(section.accentColor ? { accentColor: section.accentColor } : {}), rows })\n }\n if (!loaded)\n return sections\n if (Array.isArray(loaded)) {\n if (loaded.every(row => row && typeof row === 'object' && 'value' in row)) {\n sections.push({ id: section.id, label: section.title, ...(section.accentColor ? { accentColor: section.accentColor } : {}), rows: loaded as readonly ContextExtensionRow[] })\n }\n else {\n sections.push(...loaded as readonly ContextExtensionSection[])\n }\n return sections\n }\n sections.push(loaded as ContextExtensionSection)\n return sections\n }\n Object.defineProperty(contributor, '__zidaneExtensionName', {\n value: definition.name,\n enumerable: false,\n })\n sinks.contextPanelSections.push(contributor)\n }\n\n const registerToolResultMetadata: ExtensionContext['registerToolResultMetadata'] = (registration) => {\n if (!guardOpen('registerToolResultMetadata'))\n return\n if (!registration || typeof registration.namespace !== 'string' || !registration.namespace) {\n logger.warn('registerToolResultMetadata: missing namespace')\n return\n }\n if (!isSafeRecordKey(registration.namespace)) {\n warnUnsafeKey(logger, 'registerToolResultMetadata', registration.namespace)\n return\n }\n sinks.toolResultMetadata.push(ordered(registration))\n }\n\n const registerArtifactKind: ExtensionContext['registerArtifactKind'] = (registration) => {\n if (!guardOpen('registerArtifactKind'))\n return\n if (!registration || typeof registration.kind !== 'string' || !ARTIFACT_KIND_PATTERN.test(registration.kind)) {\n logger.warn('registerArtifactKind: kind must match [\\\\w.:-]+', { kind: String(registration?.kind) })\n return\n }\n if (typeof registration.label !== 'string' || !registration.label) {\n logger.warn('registerArtifactKind: missing label', { kind: registration.kind })\n return\n }\n for (const fn of ['detect', 'validateMeta', 'render', 'resolve', 'badge'] as const) {\n if (registration[fn] !== undefined && typeof registration[fn] !== 'function') {\n logger.warn(`registerArtifactKind: ${fn} must be a function`, { kind: registration.kind })\n return\n }\n }\n sinks.artifactKinds.push(ordered(registration))\n // `detect` rides `tool:transform` but is restricted to APPENDING to the\n // reserved `artifacts` metadata namespace — it can never touch\n // `result` / `isError`, so a detector can't alter what the model sees.\n const hook = createArtifactDetectHook(registration, { warn: (message, fields) => logger.warn(message, fields) })\n if (hook) {\n (sinks.hooks['tool:transform'] ??= []).push(hook)\n // Subagents inherit no hooks; their results arrive as the same-ref\n // `child:tool:transform` bubble (see `presets/artifacts.ts`).\n ;(sinks.hooks['child:tool:transform'] ??= []).push(hook)\n }\n }\n\n const registerReferences: ExtensionContext['registerReferences'] = (provider) => {\n if (!guardOpen('registerReferences'))\n return\n if (!provider || typeof provider.id !== 'string' || !provider.id) {\n logger.warn('registerReferences: missing id')\n return\n }\n if (typeof provider.resolve !== 'function') {\n logger.warn('registerReferences: resolve must be a function', { id: provider.id })\n return\n }\n const patterns = Array.isArray(provider.patterns)\n ? provider.patterns.filter(p => typeof p?.regex === 'string' && compileReferencePattern(p) !== null)\n : []\n if (patterns.length === 0) {\n logger.warn('registerReferences: no usable patterns', { id: provider.id })\n return\n }\n if (patterns.length < provider.patterns.length)\n logger.warn('registerReferences: dropped patterns that do not compile', { id: provider.id })\n sinks.references.push(ordered({ ...provider, patterns }))\n }\n\n const registerBadgeActions: ExtensionContext['registerBadgeActions'] = (registration) => {\n if (!guardOpen('registerBadgeActions'))\n return\n if (!registration || typeof registration.id !== 'string' || !registration.id) {\n logger.warn('registerBadgeActions: missing id')\n return\n }\n if (typeof registration.actions !== 'function' || typeof registration.run !== 'function') {\n logger.warn('registerBadgeActions: actions and run must be functions', { id: registration.id })\n return\n }\n sinks.badgeActions.push(ordered(registration))\n }\n\n const registerRunSettled: ExtensionContext['registerRunSettled'] = (handler) => {\n if (!guardOpen('registerRunSettled'))\n return\n if (typeof handler !== 'function') {\n logger.warn('registerRunSettled: handler must be a function')\n return\n }\n sinks.runSettled.push(handler)\n }\n\n return {\n registerSystemPromptFragment,\n registerRunSettled,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerArtifactKind,\n registerReferences,\n registerBadgeActions,\n }\n}\n","/**\n * Transform / policy registration members of the extension context, plus\n * the config schema registrations and the config / storage / session\n * surfaces.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { Settings } from '../chat/types'\nimport type { SessionMessage } from '../types'\nimport type { ExtensionConfigItem, StoredExtensionConfigItem } from './config'\nimport type { ContextScope } from './register-context-shared'\nimport type { ExtensionContext } from './types'\nimport { currentExtensionSession, deleteExtensionSessionState, getExtensionSessionState, setExtensionSessionState } from '../session'\nimport { isValidExtensionConfigItem, resolveExtensionConfigValue } from './config'\nimport { extensionStoragePath, isSafeRecordKey, warnUnsafeKey } from './register-context-shared'\n\nexport function buildTransformRegistrations(scope: ContextScope) {\n const { logger, sinks, guardOpen, ordered } = scope\n\n const registerRunTransform: ExtensionContext['registerRunTransform'] = (transform) => {\n if (!guardOpen('registerRunTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerRunTransform: transform must be a function')\n return\n }\n sinks.runTransforms.push(ordered(transform))\n }\n\n const registerBehaviorTransform: ExtensionContext['registerBehaviorTransform'] = (transform) => {\n if (!guardOpen('registerBehaviorTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerBehaviorTransform: transform must be a function')\n return\n }\n sinks.behaviorTransforms.push(ordered(transform))\n }\n\n const registerProviderMiddleware: ExtensionContext['registerProviderMiddleware'] = (middleware) => {\n if (!guardOpen('registerProviderMiddleware'))\n return\n if (typeof middleware !== 'function') {\n logger.warn('registerProviderMiddleware: middleware must be a function')\n return\n }\n sinks.providerMiddlewares.push(ordered(middleware))\n }\n\n const registerToolCatalogTransform: ExtensionContext['registerToolCatalogTransform'] = (transform) => {\n if (!guardOpen('registerToolCatalogTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerToolCatalogTransform: transform must be a function')\n return\n }\n sinks.toolCatalogTransforms.push(ordered(transform))\n }\n\n const registerMcpConfigTransform: ExtensionContext['registerMcpConfigTransform'] = (transform) => {\n if (!guardOpen('registerMcpConfigTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerMcpConfigTransform: transform must be a function')\n return\n }\n sinks.mcpConfigTransforms.push(ordered(transform))\n }\n\n const registerSkillPolicy: ExtensionContext['registerSkillPolicy'] = (policy) => {\n if (!guardOpen('registerSkillPolicy'))\n return\n if (!policy || typeof policy.name !== 'string' || !policy.name) {\n logger.warn('registerSkillPolicy: missing name')\n return\n }\n sinks.skillPolicies.push(ordered(policy))\n }\n\n const registerToolPolicy: ExtensionContext['registerToolPolicy'] = (policy) => {\n if (!guardOpen('registerToolPolicy'))\n return\n if (!policy || typeof policy.decide !== 'function') {\n logger.warn('registerToolPolicy: decide must be a function')\n return\n }\n const only = policy.tools ? new Set(policy.tools) : null\n // Rides `tool:gate`: earlier verdicts (built-in guards, approvals,\n // earlier policies) win, and any `patch` is re-validated against the\n // tool's inputSchema by the loop AFTER gating — an invalid patch\n // becomes a validation error, never an execution with unsound args.\n const gate: AgentHooks['tool:gate'] = async (gateCtx) => {\n if (gateCtx.block || gateCtx.result !== undefined)\n return\n if (only && !only.has(gateCtx.name))\n return\n let verdict: Awaited<ReturnType<typeof policy.decide>>\n try {\n verdict = await policy.decide({\n tool: gateCtx.name,\n input: gateCtx.input,\n turnId: gateCtx.turnId,\n callId: gateCtx.callId,\n })\n }\n catch (err) {\n // Isolation: a throwing policy must not take the run down.\n logger.warn('tool policy threw; allowing call', { tool: gateCtx.name, error: String(err) })\n return\n }\n if (verdict === undefined || verdict === 'allow')\n return\n if ('block' in verdict) {\n gateCtx.block = true\n gateCtx.reason = verdict.block\n return\n }\n // Full input replacement — in place, per the tool:gate mutation contract.\n for (const key of Object.keys(gateCtx.input))\n delete gateCtx.input[key]\n Object.assign(gateCtx.input, verdict.patch)\n }\n const bucket = (sinks.hooks['tool:gate'] ??= [])\n bucket.push(gate)\n }\n\n const registerToolResultTransform: ExtensionContext['registerToolResultTransform'] = (transform) => {\n if (!guardOpen('registerToolResultTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerToolResultTransform: transform must be a function')\n return\n }\n const hook: AgentHooks['tool:transform'] = async (transformCtx) => {\n let patch: Awaited<ReturnType<typeof transform>>\n try {\n patch = await transform({\n tool: transformCtx.name,\n input: transformCtx.input,\n result: transformCtx.result,\n isError: transformCtx.isError,\n })\n }\n catch (err) {\n logger.warn('tool result transform threw; keeping result', { tool: transformCtx.name, error: String(err) })\n return\n }\n if (!patch)\n return\n if (patch.result !== undefined)\n transformCtx.result = patch.result\n if (patch.isError !== undefined)\n transformCtx.isError = patch.isError\n }\n const bucket = (sinks.hooks['tool:transform'] ??= [])\n bucket.push(hook)\n }\n\n const registerContextTransform: ExtensionContext['registerContextTransform'] = (transform) => {\n if (!guardOpen('registerContextTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerContextTransform: transform must be a function')\n return\n }\n // Sugar over a provider middleware: rewrite `options.messages` on the\n // way into every stream call. Failures keep the original messages.\n registerProviderMiddleware(provider => ({\n ...provider,\n stream: async (streamOptions, callbacks) => {\n let next: readonly SessionMessage[] = streamOptions.messages\n try {\n next = await transform(streamOptions.messages) ?? streamOptions.messages\n }\n catch (err) {\n logger.warn('context transform threw; keeping messages', { error: String(err) })\n }\n return provider.stream(next === streamOptions.messages ? streamOptions : { ...streamOptions, messages: [...next] }, callbacks)\n },\n }))\n }\n\n const registerPromptTransform: ExtensionContext['registerPromptTransform'] = (transform) => {\n if (!guardOpen('registerPromptTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerPromptTransform: transform must be a function')\n return\n }\n sinks.promptTransforms.push(ordered(transform))\n }\n\n return {\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n }\n}\n\nexport function buildConfigMembers(scope: ContextScope, readSettings: (() => Partial<Settings> | undefined) | undefined) {\n const { definition, logger, sinks, paths, guardOpen } = scope\n\n const registerConfigCategory: ExtensionContext['registerConfigCategory'] = (category) => {\n if (!guardOpen('registerConfigCategory'))\n return\n if (!category || typeof category.id !== 'string' || !category.id || typeof category.label !== 'string') {\n logger.warn('registerConfigCategory: missing id or label')\n return\n }\n if (!isSafeRecordKey(category.id)) {\n warnUnsafeKey(logger, 'registerConfigCategory', category.id)\n return\n }\n sinks.configCategories[category.id] = category\n }\n\n const registerConfigItem: ExtensionContext['registerConfigItem'] = (categoryIdOrItem, maybeItem) => {\n if (!guardOpen('registerConfigItem'))\n return\n // Two call forms: `registerConfigItem(item)` (implicit category =\n // extension name) and `registerConfigItem(categoryId, item)`.\n const explicitCategory = typeof categoryIdOrItem === 'string' ? categoryIdOrItem : undefined\n const item = (typeof categoryIdOrItem === 'string' ? maybeItem : categoryIdOrItem) as ExtensionConfigItem | undefined\n if (!item || typeof item.key !== 'string' || !item.key) {\n logger.warn('registerConfigItem: missing item key')\n return\n }\n if (!isSafeRecordKey(item.key)) {\n warnUnsafeKey(logger, 'registerConfigItem', item.key)\n return\n }\n if (typeof item.label !== 'string') {\n logger.warn('registerConfigItem: missing label', { key: item.key })\n return\n }\n if (!isValidExtensionConfigItem(item)) {\n logger.warn('registerConfigItem: invalid item shape for its type', { key: item.key, type: (item as { type?: unknown }).type })\n return\n }\n const category = explicitCategory && explicitCategory.length > 0 ? explicitCategory : definition.name\n if (!isSafeRecordKey(category)) {\n warnUnsafeKey(logger, 'registerConfigItem: category', category)\n return\n }\n sinks.configItems[item.key] = { ...item, category } as StoredExtensionConfigItem\n }\n\n const configSurface: ExtensionContext['config'] = {\n get: (key) => {\n const item = sinks.configItems[key]\n if (!item) {\n logger.warn('config.get: unknown key, register it with registerConfigItem first', { key })\n return undefined\n }\n const persisted = readSettings?.()?.extensionConfig?.[definition.name]?.[key]\n return resolveExtensionConfigValue(item, persisted)\n },\n }\n\n const storageSurface: ExtensionContext['storage'] = {\n path: (scope, ...segments) => extensionStoragePath(paths, definition.name, scope, ...segments),\n }\n\n const sessionSurface: ExtensionContext['session'] = {\n get: key => getExtensionSessionState(currentExtensionSession(), definition.name, key),\n set: (key, value) => setExtensionSessionState(currentExtensionSession(), definition.name, key, value),\n delete: key => deleteExtensionSessionState(currentExtensionSession(), definition.name, key),\n }\n\n return { registerConfigCategory, registerConfigItem, configSurface, storageSurface, sessionSurface }\n}\n","/**\n * UI registration members of the extension context: TUI keybindings /\n * footer hints / modals / renderers, side-panel tabs, and command-palette\n * commands + providers.\n */\n\nimport type { PaletteItem } from '../chat/palette'\nimport type { ExtensionPanelEntry } from './panels'\nimport type { ContextScope } from './register-context-shared'\nimport type { ExtensionContext, ExtensionPaletteItem, ExtensionPaletteProviderEntry } from './types'\nimport {\n isBareCharExtensionBinding,\n isReservedExtensionBinding,\n parseBindingSpec,\n} from '../chat/keybindings'\nimport { isExtensionPanelScope } from './panels'\n\nexport function buildTuiRegistrations(scope: ContextScope) {\n const { definition, logger, sinks, guardOpen } = scope\n\n const registerKeybinding: ExtensionContext['tui']['registerKeybinding'] = (binding) => {\n if (!guardOpen('tui.registerKeybinding'))\n return\n if (!binding || typeof binding.id !== 'string' || typeof binding.onTrigger !== 'function') {\n logger.warn('tui.registerKeybinding: missing id or onTrigger')\n return\n }\n // The framework reserves the chords it owns — `escape` (abort /\n // back / exit semantics) and `ctrl+c` (quit). If an extension\n // bound those, the user could get trapped with no working \"out\".\n // We refuse the registration outright + log; the binding's\n // metadata (label, description) is dropped, the runtime stays\n // unaffected.\n if (binding.default && isReservedExtensionBinding(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: refused, `escape` and `ctrl+c` are reserved',\n { id: binding.id, chord: binding.default },\n )\n return\n }\n // A default that doesn't parse, or a bare printable character\n // (which would fire on every matching keystroke while the user\n // types), degrades to unbound-but-rebindable: the id stays\n // registered so the user can bind a valid chord in\n // `keybindings.json`.\n if (binding.default && !parseBindingSpec(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: default chord could not be parsed, registered unbound (set it in keybindings.json)',\n { id: binding.id, chord: binding.default },\n )\n sinks.keybindings.push({ ...binding, default: '' })\n return\n }\n if (binding.default && isBareCharExtensionBinding(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: a bare-character default would fire while typing, registered unbound (rebind with ctrl/alt/meta via keybindings.json)',\n { id: binding.id, chord: binding.default },\n )\n sinks.keybindings.push({ ...binding, default: '' })\n return\n }\n sinks.keybindings.push(binding)\n }\n\n const registerFooterHint: ExtensionContext['tui']['registerFooterHint'] = (hint) => {\n if (!guardOpen('tui.registerFooterHint'))\n return\n if (!hint || typeof hint.key !== 'string' || typeof hint.label !== 'string') {\n logger.warn('tui.registerFooterHint: missing key or label')\n return\n }\n sinks.footerHints.push(hint)\n }\n\n const registerModal: ExtensionContext['tui']['registerModal'] = (modal) => {\n if (!guardOpen('tui.registerModal'))\n return\n if (!modal || typeof modal.id !== 'string') {\n logger.warn('tui.registerModal: missing id')\n return\n }\n sinks.modals.push(modal)\n }\n\n const registerRenderer: ExtensionContext['tui']['registerRenderer'] = (id, render) => {\n if (!guardOpen('tui.registerRenderer'))\n return\n if (typeof id !== 'string' || !/^[\\w.-]+$/.test(id)) {\n logger.warn('tui.registerRenderer: id must be a bare [A-Za-z0-9_.-]+ token (it is auto-namespaced)', { id })\n return\n }\n if (typeof render !== 'function') {\n logger.warn('tui.registerRenderer: render must be a function', { id })\n return\n }\n // Namespacing makes cross-extension collisions structurally\n // impossible — blocks reference `<extension>/<id>`.\n sinks.renderers.push({ id: `${definition.name}/${id}`, render })\n }\n\n const registerToolRenderer: ExtensionContext['tui']['registerToolRenderer'] = (registration) => {\n if (!guardOpen('tui.registerToolRenderer'))\n return\n if (!registration || typeof registration.render !== 'function') {\n logger.warn('tui.registerToolRenderer: render must be a function')\n return\n }\n if (registration.tools !== undefined && (!Array.isArray(registration.tools) || registration.tools.some(t => typeof t !== 'string'))) {\n logger.warn('tui.registerToolRenderer: tools must be an array of tool names')\n return\n }\n // Owner attached here (not author-provided): `useSessionState` inside\n // an anonymous tool renderer persists under the extension's namespace.\n sinks.toolRenderers.push({ ...registration, owner: definition.name })\n }\n\n const registerArtifactRenderer: ExtensionContext['tui']['registerArtifactRenderer'] = (registration) => {\n if (!guardOpen('tui.registerArtifactRenderer'))\n return\n if (!registration || typeof registration.kind !== 'string' || !registration.kind) {\n logger.warn('tui.registerArtifactRenderer: missing kind')\n return\n }\n if (typeof registration.renderDetail !== 'function') {\n logger.warn('tui.registerArtifactRenderer: renderDetail must be a function', { kind: registration.kind })\n return\n }\n sinks.artifactRenderers.push({ ...registration, owner: definition.name })\n }\n\n return {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n registerArtifactRenderer,\n }\n}\n\nexport function buildPanelAndPaletteRegistrations(scope: ContextScope) {\n const { definition, logger, sinks, guardOpen } = scope\n\n const registerPanel: ExtensionContext['registerPanel'] = (panel) => {\n if (!guardOpen('registerPanel'))\n return\n if (!panel || typeof panel.id !== 'string' || !panel.id.trim()) {\n logger.warn('registerPanel: missing id')\n return\n }\n if (typeof panel.label !== 'string' || !panel.label.trim()) {\n logger.warn('registerPanel: missing label', { id: panel.id })\n return\n }\n if (typeof panel.load !== 'function') {\n logger.warn('registerPanel: load must be a function', { id: panel.id })\n return\n }\n if (panel.scope !== undefined && !isExtensionPanelScope(panel.scope)) {\n logger.warn('registerPanel: scope must be \\'session\\', \\'project\\' or \\'both\\'', { id: panel.id, scope: String(panel.scope) })\n return\n }\n // Namespace like renderers: bare ids get `<extension>/`, already-qualified ids pass through.\n const id = panel.id.includes('/') ? panel.id : `${definition.name}/${panel.id}`\n const idx = sinks.guiPanels.findIndex(p => p.id === id)\n const entry: ExtensionPanelEntry = { ...panel, id, owner: definition.name }\n if (idx === -1)\n sinks.guiPanels.push(entry)\n else\n sinks.guiPanels[idx] = entry // last-write-wins within one extension\n }\n\n const paletteGroup = definition.displayName ?? definition.name\n const namespaced = (id: string) => (id.includes('/') ? id : `${definition.name}/${id}`)\n\n const registerPaletteCommand: ExtensionContext['registerPaletteCommand'] = (command) => {\n if (!guardOpen('registerPaletteCommand'))\n return\n if (!command || typeof command.id !== 'string' || !command.id.trim()) {\n logger.warn('registerPaletteCommand: missing id')\n return\n }\n if (typeof command.title !== 'string' || !command.title.trim()) {\n logger.warn('registerPaletteCommand: missing title', { id: command.id })\n return\n }\n if (typeof command.run !== 'function') {\n logger.warn('registerPaletteCommand: run must be a function', { id: command.id })\n return\n }\n if (command.keybinding && !parseBindingSpec(command.keybinding)) {\n logger.warn('registerPaletteCommand: keybinding does not parse, ignoring it', { id: command.id, keybinding: command.keybinding })\n command = { ...command, keybinding: undefined }\n }\n const entry = { ...command, id: namespaced(command.id) }\n const idx = sinks.paletteCommands.findIndex(c => c.id === entry.id)\n if (idx === -1)\n sinks.paletteCommands.push(entry)\n else\n sinks.paletteCommands[idx] = entry // last-write-wins within one extension\n }\n\n const registerPaletteProvider: ExtensionContext['registerPaletteProvider'] = (provider) => {\n if (!guardOpen('registerPaletteProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || !provider.id.trim()) {\n logger.warn('registerPaletteProvider: missing id')\n return\n }\n if (typeof provider.items !== 'function' || typeof provider.run !== 'function') {\n logger.warn('registerPaletteProvider: items and run must be functions', { id: provider.id })\n return\n }\n const id = namespaced(provider.id)\n const group = provider.group?.trim() || paletteGroup\n // Item ids are namespaced on the way out (two extensions' `main` never\n // collide) and stripped on the way back in, so the author only ever\n // handles the bare ids they chose. Nested lists get the same treatment.\n const prefix = `${definition.name}/`\n const stamp = (items: readonly ExtensionPaletteItem[]): PaletteItem[] =>\n items.map(item => ({ ...item, id: namespaced(item.id), group: item.group ?? group, source: id }))\n const entry: ExtensionPaletteProviderEntry = {\n id,\n owner: definition.name,\n group,\n items: async ctx => stamp(await provider.items(ctx)),\n run: async (itemId, ctx) => {\n const result = await provider.run(itemId.startsWith(prefix) ? itemId.slice(prefix.length) : itemId, ctx)\n return result && result.kind === 'items' ? { ...result, items: stamp(result.items) } : result\n },\n ...(provider.subscribe ? { subscribe: provider.subscribe } : {}),\n }\n const idx = sinks.paletteProviders.findIndex(p => p.id === id)\n if (idx === -1)\n sinks.paletteProviders.push(entry)\n else\n sinks.paletteProviders[idx] = entry\n }\n\n return { registerPanel, registerPaletteCommand, registerPaletteProvider }\n}\n","/**\n * The per-extension `ExtensionContext` factory. Each area's members are\n * built by a sibling `register-context-*.ts` builder over a shared\n * {@link ContextScope}; the context object itself is assembled here so its\n * property order stays in one place.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { Logger } from '../logger'\nimport type { ExtensionBusChannel } from './bus'\nimport type { ExtensionHostRuntimeBridge, ExtensionSessionControl } from './host-runtime'\nimport type { ExtensionHudWriter } from './hud'\nimport type { ExtensionMcpHost } from './mcp-host'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ContextScope, ContextSinks, GateState } from './register-context-shared'\nimport type {\n ExtensionBusSurface,\n ExtensionContext,\n ExtensionDefinition,\n ExtensionOrderedContribution,\n ExtensionSource,\n} from './types'\nimport { EXTENSION_BUS_MAX_PAYLOAD_BYTES } from './bus'\nimport { createArtifactsSurface, createWorkspaceSurface } from './host-runtime'\nimport { MAX_COMPOSER_BADGES } from './hud'\nimport { createMcpSurface } from './mcp-host'\nimport { buildCatalogRegistrations, buildContextRegistrations, buildCoreRegistrations } from './register-context-agent'\nimport { buildConfigMembers, buildTransformRegistrations } from './register-context-transforms'\nimport { buildPanelAndPaletteRegistrations, buildTuiRegistrations } from './register-context-ui'\n\nexport interface MakeContextOptions {\n definition: ExtensionDefinition\n source: ExtensionSource\n dir?: string\n logger: Logger\n gate: GateState\n signal: AbortSignal\n sinks: ContextSinks\n /** Allowlist-off: `ctx.disabled` is true and runtime bridges are withheld. */\n disabled?: boolean\n /** Live settings getter backing `ctx.config.get` (see `BuildExtensionRegistryOptions.readSettings` in `./register`). */\n readSettings?: () => Partial<Settings> | undefined\n paths: ExtensionContext['paths']\n /** This extension's generation-scoped HUD writer (claimed by the registry build). */\n hudWriter?: ExtensionHudWriter\n /** Prompt bridge (see `BuildExtensionRegistryOptions.promptBridge` in `./register`). */\n promptBridge?: ExtensionPromptBridge\n /** Host runtime bridge (see `BuildExtensionRegistryOptions.hostRuntime` in `./register`). */\n hostRuntime?: ExtensionHostRuntimeBridge\n /** This extension's claimed bus channel — present iff the `'ipc'` permission is declared. */\n busChannel?: ExtensionBusChannel\n /** MCP access — present iff the `'mcp:call'` permission is declared and the host provides it. */\n mcp?: ExtensionMcpHost\n}\n\nexport function makeContext(opts: MakeContextOptions): ExtensionContext {\n const { definition, source, dir, logger, gate, signal, sinks, readSettings, paths, hudWriter, promptBridge, hostRuntime, busChannel, mcp, disabled = false } = opts\n\n function guardOpen(method: string): boolean {\n if (gate.open)\n return true\n logger.warn('registered after setup() finished, ignored', { method })\n return false\n }\n\n function ordered<T>(value: T): ExtensionOrderedContribution<T> {\n return { extensionName: definition.name, source, value }\n }\n\n /** The chat host's session control behind `ctx.sessions.getData` / `setData`; throws while none is attached. */\n function sessionControl(method: string): ExtensionSessionControl {\n const control = hostRuntime?.sessionControl()\n if (!control)\n throw new Error(`sessions.${method}: no chat host is attached (extension \"${definition.name}\").`)\n return control\n }\n\n const scope: ContextScope = { definition, source, dir, logger, sinks, paths, guardOpen, ordered }\n const { registerTool, registerHook, registerMcpServer, registerSkill, loadPlugin } = buildCoreRegistrations(scope)\n const {\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n } = buildCatalogRegistrations(scope)\n const {\n registerSystemPromptFragment,\n registerRunSettled,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerArtifactKind,\n registerReferences,\n registerBadgeActions,\n } = buildContextRegistrations(scope)\n const {\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n } = buildTransformRegistrations(scope)\n const { registerConfigCategory, registerConfigItem, configSurface, storageSurface, sessionSurface } = buildConfigMembers(scope, readSettings)\n const {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n registerArtifactRenderer,\n } = buildTuiRegistrations(scope)\n const { registerPanel, registerPaletteCommand, registerPaletteProvider } = buildPanelAndPaletteRegistrations(scope)\n\n const ctx: ExtensionContext = {\n name: definition.name,\n ...(definition.version !== undefined ? { version: definition.version } : {}),\n ...(dir !== undefined ? { dir } : {}),\n scope: source,\n logger,\n signal,\n paths,\n storage: storageSurface,\n session: sessionSurface,\n registerTool,\n registerHook,\n registerMcpServer,\n registerSkill,\n loadAgentPlugin: loadPlugin,\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n registerSystemPromptFragment,\n registerRunSettled,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerArtifactKind,\n // Read-only store access, late-bound through the host runtime like\n // `sessions` — detached / unsupported stores answer `[]`.\n artifacts: createArtifactsSurface(() => hostRuntime?.current()?.artifacts?.() ?? null),\n registerReferences,\n registerBadgeActions,\n // Runtime affordance like `workspace` (not gate-guarded); bound to this\n // extension's cwd, closed with the registry's abort signal.\n ...(mcp ? { mcp: createMcpSurface(mcp, paths.cwd, signal) } : {}),\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n registerConfigCategory,\n registerConfigItem,\n config: configSurface,\n disabled,\n tui: {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n registerArtifactRenderer,\n // HUD surface — deliberately NOT gate-guarded: unlike\n // registrations, these are runtime affordances meant to be called\n // from hooks / commands / timers for the extension's whole lifetime.\n notify: (text, opts) => {\n hudWriter?.notify(text, opts)\n },\n setStatus: (text, opts) => {\n hudWriter?.setStatus(text, opts)\n },\n setBanner: (lines, opts) => {\n hudWriter?.setBanner(lines, opts)\n },\n },\n // Like the HUD surface it writes to: a runtime affordance, not gate-guarded.\n composer: {\n setBadge: (id, badge, opts) => {\n if (hudWriter && !hudWriter.setBadge(id, badge, opts)) {\n logger.warn(\n `composer.setBadge: ignored. It needs opts.sessionId, an id of letters, digits, _ . : - (64 at most), a badge with a label, and at most ${MAX_COMPOSER_BADGES} badges per session`,\n { id: String(id) },\n )\n }\n },\n },\n registerPanel,\n registerPaletteCommand,\n registerPaletteProvider,\n gui: {\n registerPanel, // deprecated alias\n },\n // Runtime affordances like the HUD surface — not gate-guarded.\n submitPrompt: (text, submitOpts) => promptBridge?.submit(text, submitOpts) ?? false,\n runtime: {\n model: () => hostRuntime?.current()?.model() ?? null,\n models: () => hostRuntime?.current()?.models?.() ?? [],\n thinking: () => hostRuntime?.current()?.thinking() ?? null,\n setThinking: level => hostRuntime?.current()?.setThinking(level) ?? false,\n },\n sessions: {\n list: async () => hostRuntime?.current()?.listSessions() ?? [],\n switch: async id => hostRuntime?.current()?.switchSession(id) ?? false,\n create: async () => hostRuntime?.current()?.createSession() ?? false,\n // Host-level, by id: usable outside a run (run-settled handlers, timers).\n send: async (sessionId, prompt) => hostRuntime?.sessionControl()?.send(sessionId, prompt) ?? false,\n getData: async (sessionId, key) => sessionControl('getData').getData(sessionId, definition.name, key),\n setData: async (sessionId, key, value) => sessionControl('setData').setData(sessionId, definition.name, key, value),\n },\n appendNote: (blocks) => {\n // Ergonomics: bare renderer ids in custom blocks are auto-prefixed\n // with THIS extension's namespace (command `emit` requires the full\n // form — the executor has no extension identity there).\n const namespaced = blocks.map(block =>\n block.custom && !block.custom.renderer.includes('/')\n ? { ...block, custom: { ...block.custom, renderer: `${definition.name}/${block.custom.renderer}` } }\n : block)\n return hostRuntime?.current()?.appendNote(namespaced) ?? false\n },\n // Shell + file ops through the SESSION'S execution context — the same\n // transports (and output caps / containment) the agent's tools use,\n // resolved live per call. Not gate-guarded: a runtime affordance for\n // the extension's whole lifetime, like the HUD and submitPrompt.\n workspace: createWorkspaceSurface(\n async () => await hostRuntime?.current()?.workspace?.() ?? null,\n () => hostRuntime?.current()?.workspaceAvailable?.(),\n ),\n // Cross-window bus — permission-gated at claim time: no `'ipc'` in the\n // definition means `busChannel` is undefined and `ctx.bus` stays absent\n // entirely, so the emit path is unreachable. Like the HUD, a runtime\n // affordance (not gate-guarded); the channel itself stamps the\n // extension id from the registration name.\n ...(busChannel\n ? {\n bus: {\n emit: (payload, emitOpts) => {\n if (!busChannel.emit(payload, emitOpts))\n logger.warn(`bus.emit dropped: payload not JSON-serializable or over ${EXTENSION_BUS_MAX_PAYLOAD_BYTES} bytes`)\n },\n onMessage: fn => busChannel.onMessage(fn),\n } satisfies ExtensionBusSurface,\n }\n : {}),\n }\n return ctx\n}\n","/**\n * Run each loaded extension's `setup(ctx)` and fold its contributions\n * into a single {@link ExtensionRegistry}.\n *\n * Two contracts the rest of zidane relies on:\n *\n * - **Isolation** — a throw inside `setup` lands on the offending\n * extension as `LoadedExtension.error` (so the Settings panel can\n * surface it) but never propagates. Every other extension still\n * gets to register.\n * - **Late-call gating** — once `setup` resolves, the context's\n * registration methods become no-ops. Extensions that schedule\n * work for later (a `setTimeout`, a Promise resolved by an\n * external event) can't smuggle contributions in after the\n * registry is sealed. A `logger.warn` is emitted so the author\n * can spot the misuse.\n *\n * Cross-extension collisions are resolved here too: last-write-wins\n * for keyed maps (tools / themes / agents / providers), append-order\n * for arrays (mcpServers / skills / system fragments / completion\n * providers / TUI / GUI surfaces). Hook handlers are bucketed per\n * event so every extension fires.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { Command } from '../chat/commands'\nimport type { CompletionProvider } from '../chat/completion'\nimport type { InlineAutocompleteProvider } from '../chat/inline-autocomplete'\nimport type { PaletteContext, PaletteItem } from '../chat/palette'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { Theme } from '../chat/theme'\nimport type { Settings } from '../chat/types'\nimport type { Logger, LogSink } from '../logger'\nimport type { McpServerConfig } from '../mcp'\nimport type { SkillConfig } from '../skills'\nimport type { SubagentDef, SubagentOverride } from '../tools/spawn'\nimport type { ToolDef } from '../tools/types'\nimport type { ExtensionBus, ExtensionBusChannel } from './bus'\nimport type {\n ExtensionConfigCategory,\n StoredExtensionConfigItem,\n} from './config'\nimport type { ExtensionHostRuntimeBridge } from './host-runtime'\nimport type { ExtensionHud, ExtensionHudWriter } from './hud'\nimport type { ExtensionMcpHost } from './mcp-host'\nimport type { ExtensionPanelEntry } from './panels'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ContextSinks, GateState } from './register-context-shared'\nimport type {\n ExtensionArtifactRendererEntry,\n ExtensionContext,\n ExtensionContributions,\n ExtensionCustomRendererEntry,\n ExtensionDefinition,\n ExtensionFooterHint,\n ExtensionKeybinding,\n ExtensionModal,\n ExtensionOrderedContribution,\n ExtensionPaletteCommand,\n ExtensionPaletteProviderEntry,\n ExtensionRegistry,\n ExtensionRunSettledHandler,\n ExtensionToolRendererEntry,\n LoadedExtension,\n LoadedPluginRecord,\n} from './types'\nimport { homedir } from 'node:os'\nimport {\n DEFAULT_KEYBINDINGS,\n parseBindingSpec,\n} from '../chat/keybindings'\nimport { resolveStorageDirs } from '../chat/xdg'\nimport { errorMessage } from '../errors'\nimport { consoleSink, createLogger } from '../logger'\nimport { createExtensionBus } from './bus'\nimport { claimProviderOAuthFlows } from './provider-oauth'\nimport { makeContext } from './register-context'\nimport { createEmptySinks, createRecord } from './register-context-shared'\nimport { withTimeout } from './with-timeout'\n\n/** Options for {@link buildExtensionRegistry}. */\nexport interface BuildExtensionRegistryOptions {\n /**\n * Names the user has explicitly disabled. Discovered + present in\n * `loaded` but skipped during setup; surfaces in `inactive` so the\n * Settings panel can list them.\n */\n disabled?: ReadonlySet<string>\n /** Maximum time one extension's setup may take before it is marked inactive. Default: 10000 ms. */\n setupTimeoutMs?: number\n /**\n * Override the log sink. Defaults to a `consoleSink` writing to\n * stderr (which the TUI suppresses to keep the renderer clean —\n * only `ZIDANE_DEBUG=1` surfaces the lines).\n */\n sink?: LogSink\n /**\n * Extension HUD backing `ctx.tui.notify` / `setStatus` / `setBanner`.\n * Hosts with a live renderer (the TUI) pass one; headless hosts omit it\n * and the methods no-op. Extension footprints are wiped from it on\n * disposal.\n */\n hud?: ExtensionHud\n /**\n * Prompt bridge backing `ctx.submitPrompt`. Hosts with a submit pipeline\n * attach their handler post-mount; omitted → the method returns `false`.\n */\n promptBridge?: ExtensionPromptBridge\n /**\n * Host runtime bridge backing `ctx.runtime` / `ctx.sessions` /\n * `ctx.appendNote`. Late-bound: the host attaches its implementation\n * post-mount; detached methods return `null`/`false`.\n */\n hostRuntime?: ExtensionHostRuntimeBridge\n /**\n * MCP access backing `ctx.mcp` for extensions declaring the `'mcp:call'`\n * permission (`createExtensionMcpHost` builds the standard one). Omitted\n * → `ctx.mcp` is absent.\n */\n mcp?: ExtensionMcpHost\n /**\n * Extension bus backing `ctx.bus` for extensions declaring the `'ipc'`\n * permission. Hosts with a cross-window transport pass their shared bus\n * (daemon environment, TUI, GUI main); omitted → a build-local bus is\n * created so loopback delivery still works with zero IPC.\n */\n bus?: ExtensionBus\n /**\n * Live settings getter backing `ctx.config.get`. `setup()` runs at\n * boot BEFORE the host's live settings are wired, so the context can't\n * capture a snapshot — it reads through this getter on every call.\n * During `setup` it typically returns a boot snapshot (or `undefined`);\n * inside hooks / tools it returns the host's live `Settings`. Omitted\n * → `ctx.config.get` always returns item defaults.\n */\n readSettings?: () => Partial<Settings> | undefined\n /** Runtime cwd used for ctx.paths / ctx.storage helpers. */\n cwd?: string\n /** Project root exposed as `ctx.paths.projectRoot`. */\n projectRoot?: string\n /** Storage directories exposed through `ctx.paths`. Default: resolved from `prefix` and the platform conventions. */\n storageDirs?: {\n configDir?: string\n dataDir: string\n stateDir?: string\n cacheDir: string\n }\n /** Storage prefix used to resolve the storage directories. Default: `ZIDANE_PREFIX`, else `.zidane`. */\n prefix?: string\n}\n\nconst DEFAULT_TEARDOWN_TIMEOUT_MS = 5_000\n\n/**\n * Build the merged registry. `loaded` typically comes from\n * `discoverExtensions()` plus any inline ones via `inlineLoadedExtension`.\n *\n * Disabled extensions stay in `inactive` (alongside any load errors)\n * so the Settings panel shows the full picture. The collapse-to-empty\n * `EMPTY_EXTENSION_REGISTRY` is used for the \"nothing loaded\" case.\n *\n * @param loaded - Discovered and inline extensions.\n * @param options - Options for logging, the HUD and bridges, storage paths and setup limits.\n * @example\n * ```ts\n * import { buildExtensionRegistry, discoverExtensions } from 'zidane/extensions'\n *\n * const loaded = await discoverExtensions({ cwd: process.cwd() })\n * const registry = await buildExtensionRegistry(loaded, { cwd: process.cwd() })\n * console.log(Object.keys(registry.tools))\n * ```\n */\nexport async function buildExtensionRegistry(\n loaded: readonly LoadedExtension[],\n options: BuildExtensionRegistryOptions = {},\n): Promise<ExtensionRegistry> {\n const sink = options.sink ?? consoleSink({ minLevel: 'warn' })\n const disabled = options.disabled ?? new Set<string>()\n const setupTimeoutMs = options.setupTimeoutMs ?? 10_000\n\n const active: LoadedExtension[] = []\n const inactive: LoadedExtension[] = []\n const candidates: SetupCandidate[] = []\n /** Generation-scoped HUD writers, cleared (guarded) at dispose. */\n const hudWriters: ExtensionHudWriter[] = []\n /**\n * Bus channels claimed by THIS build (`'ipc'`-permitted extensions only),\n * closed at dispose so a retired generation's subscribers are dropped.\n * Without a host-supplied bus, a build-local one keeps loopback working.\n */\n const bus = options.bus ?? createExtensionBus()\n const busChannels: ExtensionBusChannel[] = []\n const extensionNameOwners = new Set<string>()\n\n for (const entry of loaded) {\n if (extensionNameOwners.has(entry.name)) {\n inactive.push({ ...entry, error: `Duplicate extension name \"${entry.name}\" ignored; the earlier registration wins.` })\n continue\n }\n // Only entries that actually loaded a definition claim the name —\n // a disk entry that failed to load (disabled, untrusted, broken)\n // must not knock a later loadable extension into `inactive` as a\n // \"duplicate\".\n if (!entry.definition) {\n inactive.push(entry)\n continue\n }\n if (entry.definition.apiVersion !== undefined && entry.definition.apiVersion !== 1) {\n inactive.push({ ...entry, error: `Unsupported extension API version \"${entry.definition.apiVersion}\". This runtime supports apiVersion 1.` })\n continue\n }\n extensionNameOwners.add(entry.name)\n // Disabled (allowlist-off) extensions still run `setup()` — they are\n // trusted code that already imported — so their config schema is\n // declared and browsable in Settings before enabling. They classify\n // as `inactive` below, which keeps every behavioral contribution out\n // of the registry's active surface.\n const isDisabled = disabled.has(entry.name)\n\n const logger = createLogger(sink, { extension: entry.name })\n const gate: GateState = { open: true }\n // Per-extension sinks — captured on the LoadedExtension as\n // `contributions` so the host can filter on enable/disable\n // without re-running setup.\n const perExtSinks = createEmptySinks()\n const abort = new AbortController()\n // Generation-scoped writer: this registry build claims the extension's\n // HUD slots; a later build re-claims them, so THIS generation's\n // late writes / dispose-time clear can't touch the successor's paint.\n // Disabled extensions get NO writer: their setup runs (to declare the\n // config schema) but an inactive extension painting status lines or\n // transcript notifications would contradict its toggle.\n // Disabled extensions get NO runtime bridges: their setup runs only to\n // declare the config schema, so the HUD writer, prompt bridge, and host\n // runtime are all withheld. The context surfaces already `?.`-guard\n // these, so every runtime affordance (`tui.*`, `submitPrompt`,\n // `runtime`, `sessions`, `workspace`) degrades to an inert no-op — an\n // inactive extension can't paint status, inject prompts, or touch the\n // session. `ctx.disabled` lets the author also gate side-effecting\n // setup work (network probes, cache warm-up) that doesn't go through\n // ctx at all.\n const hudWriter = isDisabled ? undefined : options.hud?.claim(entry.name)\n if (hudWriter)\n hudWriters.push(hudWriter)\n // Bus channel: permission-gated at the source — no `'ipc'` in the\n // definition means no channel, so `ctx.bus` is undefined and no emit\n // path exists. The extension id is stamped from the REGISTRATION name\n // here (spoof-proof: never caller-supplied).\n const busChannel = !isDisabled && entry.definition.permissions?.includes('ipc')\n ? bus.claim(entry.name)\n : undefined\n if (busChannel)\n busChannels.push(busChannel)\n const ctx = makeContext({\n definition: entry.definition,\n source: entry.source,\n dir: entry.dir,\n logger,\n gate,\n signal: abort.signal,\n sinks: perExtSinks,\n disabled: isDisabled,\n ...(options.readSettings !== undefined ? { readSettings: options.readSettings } : {}),\n ...(hudWriter !== undefined ? { hudWriter } : {}),\n ...(busChannel !== undefined ? { busChannel } : {}),\n ...(!isDisabled && options.promptBridge !== undefined ? { promptBridge: options.promptBridge } : {}),\n ...(!isDisabled && options.hostRuntime !== undefined ? { hostRuntime: options.hostRuntime } : {}),\n // Permission-gated like the bus: no `'mcp:call'`, no `ctx.mcp`.\n ...(!isDisabled && options.mcp !== undefined && entry.definition.permissions?.includes('mcp:call') ? { mcp: options.mcp } : {}),\n paths: resolveContextPaths(options),\n })\n candidates.push({\n entry: { ...entry, definition: entry.definition },\n logger,\n gate,\n abort,\n sinks: perExtSinks,\n ctx,\n setupTimeoutMs,\n disabled: isDisabled,\n })\n }\n\n const settled = await Promise.all(candidates.map(setupExtension))\n\n // Cross-extension state for collision warnings. Both maps are\n // populated in discovery order after setup settles, preserving the\n // original precedence while letting slow setup calls run in parallel.\n const keybindingIdOwners = new Map<string, string>()\n const keybindingChordOwners = new Map<string, { ext: string, id: string }>()\n const mcpServerNameOwners = new Map<string, string>()\n const rendererIdOwners = new Map<string, string>()\n\n for (const result of settled) {\n const { entry, logger, sinks: perExtSinks } = result.candidate\n if (result.ok) {\n // Disabled extensions skip the collision bookkeeping below — their\n // contributions never merge, so warning about (or claiming) chords,\n // ids, and server names on behalf of an inactive extension would\n // shadow the active ones that actually render.\n if (result.candidate.disabled) {\n inactive.push({ ...entry, contributions: captureContributions(perExtSinks, entry), setupRan: true })\n continue\n }\n // Warn on keybinding collisions so a vibe-coded extension\n // doesn't ship an unreachable shortcut. Three buckets, all\n // resolved by \"first claimant wins, later loses\":\n // 1. Collisions with a built-in action's default chord — the\n // TUI dispatcher matches built-ins first and short-circuits.\n // 2. Collisions on the binding `id` (within or across\n // extensions) — `mergeContributions` keeps the first\n // registration in `active` order.\n // 3. Collisions on the default chord — the dispatcher would\n // fire the first match on that chord.\n // Users can resolve any of these via `keybindings.json`; we only\n // flag the AUTHOR's chosen default since that's what the\n // extension's docs likely advertise. Warnings only — the actual\n // id dedupe lives in `mergeContributions` so toggle-time\n // re-merges resolve against whatever subset is active.\n for (const binding of perExtSinks.keybindings) {\n const idOwner = keybindingIdOwners.get(binding.id)\n if (idOwner === entry.name) {\n logger.warn(\n 'duplicate keybinding id in this extension, keeping the first',\n { id: binding.id },\n )\n continue\n }\n if (idOwner) {\n logger.warn(\n 'keybinding id already registered by another extension, keeping the first',\n { id: binding.id, earlierExtension: idOwner },\n )\n continue\n }\n if (binding.default) {\n const builtin = findBuiltinByChord(binding.default)\n if (builtin) {\n logger.warn(\n 'keybinding default is taken by a built-in action, rebind it in keybindings.json',\n { id: binding.id, chord: binding.default, builtin },\n )\n }\n const normalized = canonicalChord(binding.default)\n const chordOwner = normalized ? keybindingChordOwners.get(normalized) : undefined\n if (chordOwner && chordOwner.ext === entry.name) {\n logger.warn(\n 'keybinding default is used twice in this extension, only the first fires',\n { id: binding.id, chord: binding.default, earlierId: chordOwner.id },\n )\n }\n else if (chordOwner) {\n logger.warn(\n 'keybinding default is taken by another extension, keeping the first',\n { id: binding.id, chord: binding.default, earlierExtension: chordOwner.ext, earlierId: chordOwner.id },\n )\n }\n if (normalized && !chordOwner)\n keybindingChordOwners.set(normalized, { ext: entry.name, id: binding.id })\n }\n keybindingIdOwners.set(binding.id, entry.name)\n }\n // Cross-extension MCP server name collisions — the merged preset\n // dedupes by name keeping the LATEST entry (see\n // `buildExtensionPreset`), so the later registration wins and the\n // earlier extension's server is shadowed. Same-extension\n // re-registrations are treated as deliberate overrides.\n for (const server of perExtSinks.mcpServers) {\n const owner = mcpServerNameOwners.get(server.name)\n if (owner && owner !== entry.name) {\n logger.warn(\n 'mcp server name already registered by another extension, using this one',\n { name: server.name, earlierExtension: owner },\n )\n }\n mcpServerNameOwners.set(server.name, entry.name)\n }\n // Renderer-id collision warning — same registration-time split as\n // keybindings (merge dedupes silently; this is the author-facing\n // signal). Ids are namespaced, so only same-extension duplicates\n // can collide. Tool-renderer OVERLAP is deliberately not warned:\n // renderers chain (consulted in registration order, first non-null\n // wins, `null` falls through), so several extensions claiming the\n // same tool is a designed composition, not a conflict.\n for (const renderer of perExtSinks.renderers) {\n const owner = rendererIdOwners.get(renderer.id)\n if (owner)\n logger.warn('duplicate renderer id, keeping the first', { id: renderer.id })\n else\n rendererIdOwners.set(renderer.id, entry.name)\n }\n active.push({ ...entry, contributions: captureContributions(perExtSinks, entry), setupRan: true })\n }\n else {\n const message = result.error\n logger.error('setup threw', { error: message })\n // A DISABLED extension that throws during its schema-only setup run\n // stays toggleable: it isn't a load failure (its contributions were\n // going to be dropped regardless), and enabling triggers a full\n // reload that retries setup with real runtime bridges. Recording\n // `error` here would strand it in the errored bucket, which the\n // Settings toggle list excludes — making it impossible to enable.\n if (result.candidate.disabled)\n inactive.push({ ...entry, setupRan: true })\n else\n inactive.push({ ...entry, error: message, setupRan: true })\n }\n }\n\n // Providers' OAuth flows ride their contribution: registered for the active\n // extensions now, released on dispose (see ./provider-oauth).\n const releaseOAuthFlows = claimProviderOAuthFlows(active, sink)\n\n let disposed = false\n const registry = mergeContributions(active, inactive, {\n dispose: async () => {\n if (disposed)\n return\n disposed = true\n releaseOAuthFlows()\n // Wipe every extension's HUD footprint (status/banner) before\n // teardown — a disposed extension must leave no painted residue.\n // Writer `clear` is generation-guarded: if a newer registry already\n // re-claimed a name (swap/reload), its fresh paint survives.\n for (const writer of hudWriters)\n writer.clear()\n // Same generation guard as HUD writers: `close` no-ops when a newer\n // build already re-claimed the channel, so a reload's fresh\n // subscribers survive this (older) dispose.\n for (const channel of busChannels)\n channel.close()\n await disposeLoadedExtensions([...active, ...inactive], sink)\n },\n })\n return registry\n}\n\ninterface SetupCandidate {\n entry: LoadedExtension & { definition: ExtensionDefinition }\n logger: Logger\n gate: GateState\n abort: AbortController\n sinks: ContextSinks\n ctx: ExtensionContext\n setupTimeoutMs: number\n /** Allowlist-off: setup runs, contributions stay out of the active surface. */\n disabled: boolean\n}\n\n/** Freeze one extension's sinks into its `contributions` snapshot. */\nfunction captureContributions(perExtSinks: ContextSinks, entry: LoadedExtension): ExtensionContributions {\n return {\n tools: perExtSinks.tools,\n hooks: perExtSinks.hooks,\n mcpServers: perExtSinks.mcpServers,\n skills: perExtSinks.skills,\n plugins: perExtSinks.plugins,\n systemFragments: perExtSinks.systemFragments,\n runSettled: perExtSinks.runSettled,\n contextBreakdownSections: perExtSinks.contextBreakdownSections,\n contextPanelSections: perExtSinks.contextPanelSections,\n toolResultMetadata: perExtSinks.toolResultMetadata,\n artifactKinds: perExtSinks.artifactKinds,\n references: perExtSinks.references,\n badgeActions: perExtSinks.badgeActions,\n runTransforms: perExtSinks.runTransforms,\n behaviorTransforms: perExtSinks.behaviorTransforms,\n providerMiddlewares: perExtSinks.providerMiddlewares,\n toolCatalogTransforms: perExtSinks.toolCatalogTransforms,\n mcpConfigTransforms: perExtSinks.mcpConfigTransforms,\n skillPolicies: perExtSinks.skillPolicies,\n promptTransforms: perExtSinks.promptTransforms,\n completionProviders: perExtSinks.completionProviders,\n inlineAutocompleteProviders: perExtSinks.inlineAutocompleteProviders,\n commands: perExtSinks.commands,\n themes: perExtSinks.themes,\n agents: perExtSinks.agents,\n providers: perExtSinks.providers,\n subagents: perExtSinks.subagents,\n subagentOverrides: perExtSinks.subagentOverrides,\n configCategories: perExtSinks.configCategories,\n configItems: perExtSinks.configItems,\n tui: {\n keybindings: perExtSinks.keybindings,\n footerHints: perExtSinks.footerHints,\n modals: perExtSinks.modals,\n renderers: perExtSinks.renderers,\n toolRenderers: perExtSinks.toolRenderers,\n artifactRenderers: perExtSinks.artifactRenderers,\n },\n gui: { panels: perExtSinks.guiPanels },\n panels: perExtSinks.guiPanels,\n palette: [\n ...(perExtSinks.paletteCommands.length > 0\n ? [staticPaletteProvider(entry.name, entry.definition?.displayName ?? entry.name, perExtSinks.paletteCommands)]\n : []),\n ...perExtSinks.paletteProviders,\n ],\n }\n}\n\n/**\n * Static `registerPaletteCommand` entries become one provider per\n * extension: `items()` projects the metadata (JSON-safe, `when` resolved\n * against the context into `disabled`), `run` dispatches by id.\n */\nfunction staticPaletteProvider(owner: string, group: string, commands: readonly ExtensionPaletteCommand[]): ExtensionPaletteProviderEntry {\n const byId = new Map(commands.map(c => [c.id, c]))\n const id = `${owner}/commands`\n return {\n id,\n owner,\n group,\n items: (ctx: PaletteContext): PaletteItem[] => commands.map((c) => {\n const disabled = c.when === 'session' && !ctx.sessionId\n ? 'Needs an open session'\n : c.when === 'idle' && (!ctx.sessionId || ctx.busy)\n ? (ctx.sessionId ? 'Wait for the run to finish' : 'Needs an open session')\n : undefined\n return {\n id: c.id,\n title: c.title,\n group: c.group?.trim() || group,\n source: id,\n ...(c.description ? { description: c.description } : {}),\n ...(c.keywords ? { keywords: c.keywords } : {}),\n ...(c.icon ? { icon: c.icon } : {}),\n ...(c.keybinding ? { keybinding: c.keybinding } : {}),\n ...(disabled ? { disabled } : {}),\n }\n }),\n run: async (itemId, ctx) => {\n const command = byId.get(itemId)\n if (!command)\n return undefined\n const result = await command.run(ctx)\n return result && result.kind === 'items'\n ? { ...result, items: result.items.map(item => ({ ...item, id: `${owner}/${item.id}`, group: item.group ?? group, source: id })) }\n : result\n },\n }\n}\n\ntype SetupResult\n = | { ok: true, candidate: SetupCandidate }\n | { ok: false, candidate: SetupCandidate, error: string }\n\nasync function setupExtension(candidate: SetupCandidate): Promise<SetupResult> {\n const timeoutMessage = `setup timed out after ${candidate.setupTimeoutMs}ms`\n try {\n await withTimeout(\n Promise.resolve(candidate.entry.definition.setup(candidate.ctx)),\n candidate.setupTimeoutMs,\n timeoutMessage,\n () => {\n candidate.gate.open = false\n candidate.abort.abort(new Error(timeoutMessage))\n },\n )\n return { ok: true, candidate }\n }\n catch (err) {\n return { ok: false, candidate, error: errorMessage(err) }\n }\n finally {\n candidate.gate.open = false\n }\n}\n\nasync function disposeLoadedExtensions(entries: readonly LoadedExtension[], sink: LogSink): Promise<void> {\n const seen = new Set<string>()\n await Promise.all(entries.map(async (entry) => {\n if (!entry.setupRan || !entry.definition?.teardown || seen.has(entry.name))\n return\n seen.add(entry.name)\n const logger = createLogger(sink, { extension: entry.name })\n try {\n await withTimeout(\n Promise.resolve(entry.definition.teardown()),\n DEFAULT_TEARDOWN_TIMEOUT_MS,\n `teardown timed out after ${DEFAULT_TEARDOWN_TIMEOUT_MS}ms`,\n )\n }\n catch (err) {\n logger.error('teardown threw', { error: errorMessage(err) })\n }\n }))\n}\n\n/** Append the contributions whose `value.id` is new (first registration wins, in `active` order). */\nfunction pushFirstById<T extends { value: { id: string } }>(into: T[], seen: Set<string>, items: readonly T[]): void {\n for (const item of items) {\n if (seen.has(item.value.id))\n continue\n seen.add(item.value.id)\n into.push(item)\n }\n}\n\n/**\n * Fold every active extension's contributions into the merged\n * {@link ExtensionRegistry}. Pure given the input — call this with\n * any subset of `active` to recompute the merged surface (typically\n * after the user toggles an extension on/off, without re-running\n * setup).\n *\n * @param active - Extensions whose contributions are merged.\n * @param inactive - Extensions to list as inactive. They contribute nothing.\n * @param options - Options. `options.dispose` runs when the registry is disposed.\n */\nexport function mergeContributions(\n active: readonly LoadedExtension[],\n inactive: readonly LoadedExtension[] = [],\n options: { dispose?: () => Promise<void> } = {},\n): ExtensionRegistry {\n const tools: Record<string, ToolDef> = createRecord()\n const hooks: { [E in keyof AgentHooks]?: Array<AgentHooks[E]> } = {}\n const mcpServers: McpServerConfig[] = []\n const skills: SkillConfig[] = []\n const plugins: LoadedPluginRecord[] = []\n const systemFragments: string[] = []\n const runSettled: ExtensionOrderedContribution<ExtensionRunSettledHandler>[] = []\n const contextBreakdownSections: NonNullable<ExtensionContributions['contextBreakdownSections']>[number][] = []\n const contextPanelSections: NonNullable<ExtensionContributions['contextPanelSections']>[number][] = []\n const toolResultMetadata: ExtensionContributions['toolResultMetadata'][number][] = []\n const artifactKinds: ExtensionContributions['artifactKinds'][number][] = []\n const seenArtifactKinds = new Set<string>()\n const references: ExtensionContributions['references'][number][] = []\n const seenReferenceIds = new Set<string>()\n const badgeActions: ExtensionContributions['badgeActions'][number][] = []\n const seenBadgeActionIds = new Set<string>()\n const runTransforms: ExtensionContributions['runTransforms'][number][] = []\n const behaviorTransforms: ExtensionContributions['behaviorTransforms'][number][] = []\n const providerMiddlewares: ExtensionContributions['providerMiddlewares'][number][] = []\n const toolCatalogTransforms: ExtensionContributions['toolCatalogTransforms'][number][] = []\n const mcpConfigTransforms: ExtensionContributions['mcpConfigTransforms'][number][] = []\n const skillPolicies: ExtensionContributions['skillPolicies'][number][] = []\n const promptTransforms: ExtensionContributions['promptTransforms'][number][] = []\n const completionProviders: CompletionProvider[] = []\n const inlineAutocompleteProviders: InlineAutocompleteProvider[] = []\n const seenInlineAutocompleteIds = new Set<string>()\n const commands: Command[] = []\n const seenCommandNames = new Set<string>()\n const themes: Record<string, Theme> = createRecord()\n const agents: Record<string, AgentProfile> = createRecord()\n const providers: Record<string, ProviderDescriptor> = createRecord()\n const subagents: Record<string, SubagentDef> = createRecord()\n const subagentOverrides: Record<string, SubagentOverride> = createRecord()\n const configCategories: Record<string, ExtensionConfigCategory> = createRecord()\n const configItems: Record<string, Record<string, StoredExtensionConfigItem>> = createRecord()\n const keybindings: ExtensionKeybinding[] = []\n const seenKeybindingIds = new Set<string>()\n const footerHints: ExtensionFooterHint[] = []\n const modals: ExtensionModal[] = []\n const renderers: ExtensionCustomRendererEntry[] = []\n const seenRendererIds = new Set<string>()\n const toolRenderers: ExtensionToolRendererEntry[] = []\n const artifactRenderers: ExtensionArtifactRendererEntry[] = []\n const seenArtifactRendererKinds = new Set<string>()\n const guiPanels: ExtensionPanelEntry[] = []\n const palette: ExtensionPaletteProviderEntry[] = []\n\n for (const entry of active) {\n const c = entry.contributions\n if (!c)\n continue\n Object.assign(tools, c.tools)\n for (const [event, list] of Object.entries(c.hooks)) {\n if (!list || list.length === 0)\n continue\n // Object.entries erases the per-event key↔handler-type correlation of\n // the hook map, so these casts re-assert what the source map's type\n // already guaranteed — both sides come from the same typed record, so\n // no unsound value can actually flow through.\n const key = event as keyof AgentHooks\n const bucket = (hooks[key] ??= []) as Array<typeof list[number]>\n bucket.push(...(list as Array<typeof list[number]>))\n }\n mcpServers.push(...c.mcpServers)\n skills.push(...c.skills)\n plugins.push(...c.plugins)\n systemFragments.push(...c.systemFragments)\n for (const handler of c.runSettled)\n runSettled.push({ extensionName: entry.name, source: entry.source, value: handler })\n contextBreakdownSections.push(...c.contextBreakdownSections)\n contextPanelSections.push(...c.contextPanelSections)\n toolResultMetadata.push(...c.toolResultMetadata)\n // Artifact kinds are a global namespace: first-wins by kind in `active`\n // order (same split as commands — merge dedupes quietly, build warns).\n for (const contribution of c.artifactKinds) {\n if (seenArtifactKinds.has(contribution.value.kind))\n continue\n seenArtifactKinds.add(contribution.value.kind)\n artifactKinds.push(contribution)\n }\n pushFirstById(references, seenReferenceIds, c.references)\n pushFirstById(badgeActions, seenBadgeActionIds, c.badgeActions)\n runTransforms.push(...c.runTransforms)\n behaviorTransforms.push(...c.behaviorTransforms)\n providerMiddlewares.push(...c.providerMiddlewares)\n toolCatalogTransforms.push(...c.toolCatalogTransforms)\n mcpConfigTransforms.push(...c.mcpConfigTransforms)\n skillPolicies.push(...c.skillPolicies)\n promptTransforms.push(...c.promptTransforms)\n completionProviders.push(...c.completionProviders)\n // First-wins dedupe by id (mirrors the command-name dedupe below).\n for (const provider of c.inlineAutocompleteProviders) {\n if (seenInlineAutocompleteIds.has(provider.id))\n continue\n seenInlineAutocompleteIds.add(provider.id)\n inlineAutocompleteProviders.push(provider)\n }\n // First-wins dedupe by name in `active` order, so a toggle-time re-merge\n // lets a previously-shadowed command surface once its winner is disabled\n // (mirrors the keybinding-id dedupe below). Case-insensitive to match\n // `parseCommandInvocation`.\n for (const command of c.commands) {\n const key = command.name.toLowerCase()\n if (seenCommandNames.has(key))\n continue\n seenCommandNames.add(key)\n // Aliases share the name namespace: reserve them so a later command\n // named like an earlier alias doesn't register unreachable.\n for (const alias of command.aliases ?? [])\n seenCommandNames.add(alias.toLowerCase())\n // Stamp the source extension as the completion group (unless the\n // command chose its own) — lets `/<extension>` list its commands.\n commands.push(command.group === undefined ? { ...command, group: entry.name } : command)\n }\n Object.assign(themes, c.themes)\n Object.assign(agents, c.agents)\n Object.assign(providers, c.providers)\n // Subagent adds — last-wins on name collision (mirrors tools / agents).\n Object.assign(subagents, c.subagents)\n // Subagent overrides — compose across extensions in registration\n // order, so a later extension's edit wraps the earlier one\n // (`fn(prev(def))`). Applied (by the host) after adds, last.\n for (const [name, fn] of Object.entries(c.subagentOverrides)) {\n const prev = subagentOverrides[name]\n subagentOverrides[name] = prev ? def => fn(prev(def)) : fn\n }\n // Config categories merge globally (last-wins on id collision,\n // mirrors themes). Config items stay namespaced under the owning\n // extension's name so two extensions can ship the same item key and\n // the Settings UIs can group by extension.\n Object.assign(configCategories, c.configCategories)\n if (Object.keys(c.configItems).length > 0) {\n const items = createRecord<StoredExtensionConfigItem>()\n Object.assign(items, c.configItems)\n configItems[entry.name] = items\n }\n // First-wins id dedupe in `active` order — applied here (not at\n // build time) so a toggle-time re-merge lets a previously-shadowed\n // binding surface once its winner is disabled. Collision warnings\n // are emitted at build time in `buildExtensionRegistry`.\n for (const binding of c.tui.keybindings) {\n if (seenKeybindingIds.has(binding.id))\n continue\n seenKeybindingIds.add(binding.id)\n keybindings.push(binding)\n }\n footerHints.push(...c.tui.footerHints)\n modals.push(...c.tui.modals)\n // Renderer ids are `<extension>/<id>`-namespaced at registration, so\n // cross-extension collisions are impossible; a duplicate here means\n // one extension registered the same id twice. Silent first-wins —\n // the WARNING fires at registration time in `buildExtensionRegistry`\n // (same split as keybindings: merge dedupes, build warns), so\n // toggle-time re-merges stay quiet.\n for (const renderer of c.tui.renderers) {\n if (seenRendererIds.has(renderer.id))\n continue\n seenRendererIds.add(renderer.id)\n renderers.push(renderer)\n }\n // Tool renderers merge in `active` order; the render-time resolver\n // walks in order and takes the first non-null — so \"first registered\n // wins\" falls out of ordering, no dedupe needed here.\n toolRenderers.push(...c.tui.toolRenderers)\n for (const renderer of c.tui.artifactRenderers) {\n if (seenArtifactRendererKinds.has(renderer.kind))\n continue\n seenArtifactRendererKinds.add(renderer.kind)\n artifactRenderers.push(renderer)\n }\n guiPanels.push(...c.panels)\n palette.push(...c.palette)\n }\n\n // Disabled-but-set-up extensions keep their CONFIG SCHEMA visible —\n // categories + items are inert metadata, and exposing them is what lets\n // the Settings UIs render (and persist values for) an extension's\n // settings BEFORE it's enabled. Behavioral contributions (tools, hooks,\n // providers, commands, …) stay gated to `active` above. First-wins\n // against active registrations so an inactive extension can never\n // relabel an active extension's category.\n for (const entry of inactive) {\n const c = entry.contributions\n if (entry.error || !c)\n continue\n for (const [id, category] of Object.entries(c.configCategories)) {\n if (!(id in configCategories))\n configCategories[id] = category\n }\n if (Object.keys(c.configItems).length > 0 && !(entry.name in configItems)) {\n const items = createRecord<StoredExtensionConfigItem>()\n Object.assign(items, c.configItems)\n configItems[entry.name] = items\n }\n }\n\n return {\n loaded: active,\n inactive,\n tools,\n hooks,\n mcpServers,\n skills,\n plugins,\n systemFragments,\n runSettled,\n contextBreakdownSections,\n contextPanelSections,\n toolResultMetadata,\n artifactKinds,\n references,\n badgeActions,\n runTransforms,\n behaviorTransforms,\n providerMiddlewares,\n toolCatalogTransforms,\n mcpConfigTransforms,\n skillPolicies,\n promptTransforms,\n completionProviders,\n inlineAutocompleteProviders,\n commands,\n themes,\n agents,\n providers,\n subagents,\n subagentOverrides,\n configCategories,\n configItems,\n tuiContributions: { keybindings, footerHints, modals, renderers, toolRenderers, artifactRenderers },\n guiContributions: { panels: guiPanels },\n panels: guiPanels,\n palette,\n dispose: options.dispose ?? (async () => {}),\n }\n}\n\n/**\n * Find a built-in `KeyAction` whose default chord matches `chord`. Used\n * by the collision warning above. Returns `null` when no built-in\n * uses that chord. Comparison goes through the same parser as runtime\n * dispatch, so aliases like `control` / `esc` canonicalize correctly.\n */\nfunction findBuiltinByChord(chord: string): string | null {\n const target = canonicalChord(chord)\n if (!target)\n return null\n for (const [action, builtinChord] of Object.entries(DEFAULT_KEYBINDINGS)) {\n if (!builtinChord)\n continue\n if (canonicalChord(builtinChord) === target)\n return action\n }\n return null\n}\n\nfunction canonicalChord(spec: string): string | null {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return null\n return JSON.stringify(parsed)\n}\n\n/**\n * Filter the extension registry by the user's enable allowlist. Returns\n * the original registry by reference when `enabled` is `undefined` (the\n * fresh-install default — every loaded extension is active), or a fresh\n * registry rebuilt via {@link mergeContributions} when the user has\n * explicitly toggled something off.\n *\n * The identity check is intentional — callers can compare the result\n * against the input registry to skip recomposing the preset when\n * nothing has changed.\n *\n * @param registry - Registry to filter.\n * @param enabled - Names of the enabled extensions. `undefined` keeps every extension.\n */\nexport function filterExtensionRegistry(\n registry: ExtensionRegistry,\n enabled: readonly string[] | undefined,\n): ExtensionRegistry {\n if (enabled === undefined)\n return registry\n const allow = new Set(enabled)\n const filteredActive = registry.loaded.filter(ext => allow.has(ext.name))\n // No change → return original. This is the common case once the\n // user has a stable allowlist (or a brand-new extension hasn't\n // been added yet); skipping the rebuild keeps re-renders cheap.\n if (filteredActive.length === registry.loaded.length)\n return registry\n // Build the inactive set: original inactive (load errors / pre-\n // existing disabled) plus the newly-filtered-out actives. We keep\n // them around so the Settings panel still surfaces every known\n // extension regardless of toggle state.\n const filteredOut = registry.loaded.filter(ext => !allow.has(ext.name))\n return mergeContributions(filteredActive, [...registry.inactive, ...filteredOut])\n}\n\n/**\n * Drop the contributions of Agent Plugins the user has disabled.\n *\n * `filterExtensionRegistry` keys on EXTENSION names, so it can't express\n * \"this extension runs, but the plugin it loaded does not\". Plugin skills and\n * MCP servers are fanned into the loading extension's sinks by\n * `ctx.loadAgentPlugin`, which means without this pass `enabledPlugins` would\n * render a checkbox that changes nothing for extension-loaded plugins.\n *\n * Skills are matched by `metadata['zidane.plugin']` (stamped by the loader);\n * MCP servers carry no such marker, so they're matched by identity against\n * the server list on each {@link LoadedPluginRecord}.\n *\n * Returns the input registry unchanged when nothing is disabled, so callers\n * can compare identity to skip recomposing.\n *\n * @param registry - Registry to filter.\n * @param enabledPlugins - Names of the enabled plugins. `undefined` keeps every plugin.\n */\nexport function filterRegistryPlugins(\n registry: ExtensionRegistry,\n enabledPlugins: readonly string[] | undefined,\n /**\n * Plugin names already provided by another source (in practice: disk\n * discovery). Pruned regardless of the allowlist.\n *\n * The settings list dedupes a plugin found both on disk and inside an\n * extension down to ONE row, preferring the disk copy. Contributions have to\n * agree, or that single checkbox governs two live copies: both sets of skills\n * reach the catalog, where one shadows the other and earns a spurious\n * `name-collision-shadowed` warning about a collision the user never created.\n *\n * The MCP half is less severe but the same shape — both configs reach the\n * merge, where `sessionMcpServerList` (chat/host/mcp/index.ts, first-wins by name) collapses them\n * to one. So there's no double spawn; what's wrong is that one checkbox\n * nominally governs two records, and which copy survives is decided by merge\n * order rather than by anything the user chose.\n */\n shadowed?: ReadonlySet<string>,\n): ExtensionRegistry {\n if (registry.plugins.length === 0)\n return registry\n // `undefined` allowlist means \"every discovered plugin\", matching the\n // polarity of every other enabled-allowlist setting — but shadowing still\n // applies, so this can't short-circuit on it.\n const allow = enabledPlugins === undefined ? null : new Set(enabledPlugins)\n const dropped = registry.plugins.filter(record =>\n (allow !== null && !allow.has(record.name)) || shadowed?.has(record.name) === true)\n if (dropped.length === 0)\n return registry\n\n const droppedNames = new Set(dropped.map(record => record.name))\n // Identity, not name: two plugins may legitimately ship a server with the\n // same name, and only the dropped one's config should fall out.\n const droppedServers = new Set(dropped.flatMap(record => record.plugin.mcpServers))\n\n const prune = (ext: LoadedExtension): LoadedExtension => {\n const contributions = ext.contributions\n if (!contributions || contributions.plugins.length === 0)\n return ext\n return {\n ...ext,\n contributions: {\n ...contributions,\n skills: contributions.skills.filter((skill) => {\n const owner = skill.metadata?.['zidane.plugin']\n return owner === undefined || !droppedNames.has(owner)\n }),\n mcpServers: contributions.mcpServers.filter(server => !droppedServers.has(server)),\n },\n }\n }\n\n return mergeContributions(registry.loaded.map(prune), registry.inactive)\n}\n\n/**\n * Dispose resources owned by an extension registry. Idempotent per registry.\n *\n * @param registry - Registry to dispose.\n */\nexport async function disposeExtensionRegistry(registry: ExtensionRegistry): Promise<void> {\n await registry.dispose()\n}\n\n/** Shape returned by {@link partitionExtensionCatalog}. */\nexport interface ExtensionCatalogPartition {\n /**\n * Rows the user can toggle: active extensions plus disabled-but-\n * loadable ones, sorted by name so the list order is stable across\n * boots regardless of where each entry landed in the registry.\n */\n toggleable: readonly LoadedExtension[]\n /** Entries whose import or `setup()` failed — read-only diagnostics. */\n errored: readonly LoadedExtension[]\n /**\n * Every known extension name — toggleable AND errored. Use this as\n * the GC key set for the persisted `enabledExtensions` allowlist so\n * an untrusted / temporarily-broken extension isn't permanently\n * dropped from the allowlist by an unrelated toggle.\n */\n allNames: readonly string[]\n}\n\n/**\n * Partition a registry's `loaded` + `inactive` lists into the shape the\n * Settings toggle UIs render. Pure; shared by the TUI's standalone\n * extensions modal and the Settings-modal tab so the rules (and the\n * allowlist-GC key set) stay in one place.\n *\n * @param loaded - Active extensions.\n * @param inactive - Disabled or failed extensions.\n */\nexport function partitionExtensionCatalog(\n loaded: readonly LoadedExtension[],\n inactive: readonly LoadedExtension[],\n): ExtensionCatalogPartition {\n const errored = inactive.filter(e => !!e.error)\n const disabledOk = inactive.filter(e => !e.error)\n const toggleable = [...loaded, ...disabledOk].sort((a, b) => a.name.localeCompare(b.name))\n return {\n toggleable,\n errored,\n allNames: [...toggleable.map(e => e.name), ...errored.map(e => e.name)],\n }\n}\n\nfunction resolveContextPaths(options: BuildExtensionRegistryOptions): ExtensionContext['paths'] {\n const cwd = options.cwd ?? process.cwd()\n const dirs = options.storageDirs ?? resolveStorageDirs({\n prefix: options.prefix ?? process.env.ZIDANE_PREFIX ?? '.zidane',\n home: homedir(),\n })\n return {\n cwd,\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n dataDir: dirs.dataDir,\n cacheDir: dirs.cacheDir,\n ...(dirs.configDir ? { configDir: dirs.configDir } : {}),\n ...(dirs.stateDir ? { stateDir: dirs.stateDir } : {}),\n }\n}\n","/**\n * Shared discover → disabled-set → build pipeline.\n *\n * Every host runs the identical sequence: the TUI launcher (`runTui`),\n * the TUI's cwd-change rebuild, the daemon's per-cwd runtime cache and the\n * GUI's per-cwd registry cache.\n * Centralizing it keeps the allowlist-gating contract — the `disabled`\n * set spans BOTH disk-discovered and inline candidates — in one place\n * so the callers can't drift on it.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { LogSink } from '../logger'\nimport type { ExtensionBus } from './bus'\nimport type { ExtensionHostRuntimeBridge } from './host-runtime'\nimport type { ExtensionHud } from './hud'\nimport type { ExtensionMcpHost } from './mcp-host'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ExtensionRegistry, LoadedExtension } from './types'\nimport { discoverExtensions } from './discovery'\nimport { registerExtensionHostModules } from './host-modules'\nimport { buildExtensionRegistry } from './register'\n\n/** Options for {@link discoverAndBuildExtensionRegistry}. */\nexport interface DiscoverAndBuildExtensionRegistryOptions {\n /** Discovery cwd — project roots are walked up from here. Defaults to `process.cwd()`. */\n cwd?: string\n /** User home directory. Defaults to `os.homedir()`. Mainly a test seam. */\n home?: string\n /** Storage prefix (e.g. `.zidane`). Defaults to `.zidane` inside discovery. */\n prefix?: string\n /**\n * Extra extension scan roots, prepended ahead of the default project/user\n * roots. Each root contains extension package dirs (`<root>/<name>/index.ts`).\n * Treated as user-scoped/trusted because the host/user opted into them\n * explicitly.\n */\n extensionScanPaths?: readonly string[]\n /**\n * User enable allowlist (`Settings.enabledExtensions`). `undefined`\n * means \"fresh install — everything enabled\". When set it gates BOTH\n * disk and inline candidates: a name absent from the list lands in the\n * build's `disabled` set, so it classifies as `inactive` and its\n * contributions never reach the registry's active surface. Its\n * `setup()` still runs (trusted code) so its config schema is\n * browsable in Settings before enabling.\n */\n enabledAllowlist?: readonly string[]\n /** Whether project-scoped extensions are trusted (allowed to run their code). */\n trustProjectExtensions?: boolean\n /** Host-supplied inline extensions, already `inlineLoadedExtension()`-wrapped. */\n inlineExtensions?: readonly LoadedExtension[]\n /** Log sink for `setup()` lifecycle. Defaults to stderr inside `buildExtensionRegistry`. */\n sink?: LogSink\n /** Extension HUD for `ctx.tui.notify` / `setStatus` / `setBanner` (TUI hosts). */\n hud?: ExtensionHud\n /** Prompt bridge for `ctx.submitPrompt` (hosts with a submit pipeline). */\n promptBridge?: ExtensionPromptBridge\n /** Host runtime bridge for `ctx.runtime` / `ctx.sessions` / `ctx.appendNote`. */\n hostRuntime?: ExtensionHostRuntimeBridge\n /** Shared extension bus for `ctx.bus` (hosts with a cross-window transport). */\n bus?: ExtensionBus\n /** MCP access for `ctx.mcp` (`createExtensionMcpHost`). */\n mcp?: ExtensionMcpHost\n /**\n * Live settings getter backing `ctx.config.get` — threaded verbatim to\n * {@link BuildExtensionRegistryOptions.readSettings}. Hosts pass a getter\n * returning their current `Settings` (TUI: a live mirror; GUI: the DB\n * reader) so extension config reads inside hooks / tools are live.\n */\n readSettings?: () => Partial<Settings> | undefined\n /** Abort discovery before its imports dispatch. */\n signal?: AbortSignal\n}\n\n/**\n * Discover disk extensions, fold in the inline ones, compute the\n * allowlist-driven `disabled` set, and build the merged\n * {@link ExtensionRegistry}.\n *\n * The `disabled` set honors the allowlist for inline extensions too:\n * the TUI treats inline as toggleable everywhere else (Settings tab,\n * live filtering), so both ride the same inactive-classification path.\n *\n * @param opts - Discovery, trust, allowlist and bridge options.\n */\nexport async function discoverAndBuildExtensionRegistry(\n opts: DiscoverAndBuildExtensionRegistryOptions = {},\n): Promise<ExtensionRegistry> {\n // Core entrypoints, mapped BEFORE discovery's imports dispatch. An extension\n // contributing a provider (`registerProvider`) needs the host's live\n // factories and credential helpers at runtime; a bare `import { anthropic }\n // from 'zidane'` would otherwise resolve from the EXTENSION's directory —\n // nothing inside a compiled binary, or a second zidane copy with its own\n // module state (and its own OAuth registry). Renderer-neutral, so it lives\n // here rather than in the TUI: headless / ACP / GUI hosts get it too.\n // pi-ai is NOT mapped: a lazy mapping would re-import the very specifier it\n // intercepts. Extensions reach the host's pi-ai through zidane instead\n // (`loadGoogleWireKit`).\n registerExtensionHostModules({\n 'zidane': () => import('../index'),\n 'zidane/providers': () => import('../providers'),\n 'zidane/providers/google': () => import('../providers/google'),\n 'zidane/oauth': () => import('../oauth-entry'),\n })\n\n const discovered = await discoverExtensions({\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.extensionScanPaths !== undefined ? { extensionScanPaths: opts.extensionScanPaths } : {}),\n ...(opts.trustProjectExtensions !== undefined ? { trustProjectExtensions: opts.trustProjectExtensions } : {}),\n ...(opts.signal !== undefined ? { signal: opts.signal } : {}),\n ...(opts.sink !== undefined ? { sink: opts.sink } : {}),\n })\n const candidates = [...discovered, ...(opts.inlineExtensions ?? [])]\n const allowlist = opts.enabledAllowlist\n const disabled = allowlist === undefined\n ? undefined\n : new Set(candidates.map(e => e.name).filter(name => !allowlist.includes(name)))\n return buildExtensionRegistry(candidates, {\n ...(opts.sink !== undefined ? { sink: opts.sink } : {}),\n ...(opts.hud !== undefined ? { hud: opts.hud } : {}),\n ...(opts.promptBridge !== undefined ? { promptBridge: opts.promptBridge } : {}),\n ...(opts.hostRuntime !== undefined ? { hostRuntime: opts.hostRuntime } : {}),\n ...(opts.bus !== undefined ? { bus: opts.bus } : {}),\n ...(opts.mcp !== undefined ? { mcp: opts.mcp } : {}),\n ...(disabled ? { disabled } : {}),\n ...(opts.readSettings !== undefined ? { readSettings: opts.readSettings } : {}),\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n })\n}\n","/**\n * Extension import — fetch from GitHub or npm, validate, and install into\n * the roots {@link discoverExtensions} already scans.\n *\n * Host-agnostic by construction: no UI, no settings access, no renderer.\n * The TUI's `Settings → Extensions` install flow (`ctrl+g`) and the GUI\n * call the same {@link installExtensionFromSpec} orchestrator; hosts own scope\n * selection, overwrite confirmation, allowlist updates, and the\n * post-install registry rebuild (the `/reload` path).\n *\n * Pipeline invariants:\n *\n * - **Validate before touching user roots.** The candidate is imported\n * and type-checked (same `isExtensionDefinition` + apiVersion rules\n * as discovery) inside the temp dir; a failure installs nothing.\n * Importing executes the extension's top-level code once — the\n * explicit install request is the consent; it's the same code that\n * would run at next boot.\n * - **Folder name = definition name, by construction.** The target dir\n * is named from the validated `definition.name`, so discovery's\n * name-match invariant can't be violated by an install.\n * - **Dependency installs never run lifecycle scripts** (bun's default;\n * the npm/pnpm/yarn fallbacks get `--ignore-scripts`).\n * - Failures throw {@link ExtensionInstallError} with a machine-readable\n * `code` so hosts can react (e.g. `exists` → confirm-and-retry with\n * `overwrite`), never half-install.\n *\n * Recognized package shapes (both fetch targets):\n *\n * - root `index.{ts,tsx,mjs,js}` → a single-extension repo/package\n * - `extensions/<name>/index.*` → a multi-extension collection;\n * `extension` selects one (mandatory when several exist)\n */\n\nimport type { FetchSourceResult, InstallIo, InstallScope, InstallSource } from '../install/source'\nimport { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { errorMessage } from '../errors'\nimport { defaultExec, defaultWhich, fetchInstallSource, InstallSourceError, lastLine, parseInstallSource, resolveNpmVersion, resolveSubdirRoot } from '../install/source'\nimport { findEntryFile, importExtensionEntry, isExtensionDefinition, readExtensionManifest, releaseExtensionModules } from './discovery'\nimport { checkZidaneVersion } from './host-version'\nimport { withTimeout } from './with-timeout'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Where an extension is installed: the project or the user directory. */\nexport type ExtensionInstallScope = InstallScope\n\n/** The shared install-source shape — see `src/install/source.ts` for the grammar. */\nexport type ExtensionInstallSource = InstallSource\n\n/** Reason an install, update or removal failed. Matches {@link ExtensionInstallError.code}. */\nexport type ExtensionInstallErrorCode\n = | 'invalid-source'\n | 'fetch-failed'\n | 'no-extension'\n | 'multiple-candidates'\n | 'deps-failed'\n | 'validate-failed'\n | 'exists'\n | 'no-package-manager'\n | 'not-installed'\n | 'no-provenance'\n\n/** Error thrown by the extension installer. `code` says which step failed. */\nexport class ExtensionInstallError extends InstallSourceError {\n /** Machine-readable reason for the failure. */\n declare readonly code: ExtensionInstallErrorCode\n /**\n * Creates an install error.\n * @param code - Reason for the failure.\n * @param message - Human-readable description.\n * @param candidates - Extension names found in the source, set for `multiple-candidates`.\n */\n constructor(code: ExtensionInstallErrorCode, message: string, candidates?: readonly string[]) {\n super(code, message, candidates)\n this.name = 'ExtensionInstallError'\n }\n}\n\n/**\n * Re-raise a shared-layer {@link InstallSourceError} in the extension\n * error vocabulary so every consumer keeps a single `err.code` switch\n * over {@link ExtensionInstallErrorCode}. The base layer only throws\n * `'invalid-source'` / `'fetch-failed'` — both members of the extension\n * union.\n */\nfunction rethrowAsExtensionError(err: unknown): never {\n if (err instanceof InstallSourceError && !(err instanceof ExtensionInstallError))\n throw new ExtensionInstallError(err.code as ExtensionInstallErrorCode, err.message, err.candidates)\n throw err\n}\n\nconst PROVENANCE_FILE = '.install.json'\n\n/** What `.install.json` records — enough to replay the install for updates. */\nexport interface ExtensionInstallProvenance {\n /** Resolved provenance, e.g. `npm:foo@1.2.3` / `github:owner/repo@main`. */\n source: string\n /** The raw spec the user passed — what an update re-resolves. */\n spec: string\n /** Scope the extension was installed into. */\n scope: ExtensionInstallScope\n /** Candidate selected from a multi-extension source, when one was. */\n extension?: string\n /** Exact commit SHA (GitHub via git clone) — anchors the update no-op check. */\n commit?: string\n /** ISO 8601 timestamp of the install. */\n installedAt: string\n}\n\n/** One installable extension found inside a fetched tree. */\nexport interface ExtensionInstallCandidate {\n /** Proposed name (manifest name, else collection dirname, else repo/pkg basename). */\n name: string\n /** Absolute directory inside the fetched tree. */\n dir: string\n}\n\n/** Outcome of an install or update. */\nexport interface ExtensionInstallResult {\n /** Final name — the validated `definition.name`. */\n name: string\n /** Absolute installed directory. */\n dir: string\n /** Scope the extension was installed into. */\n scope: ExtensionInstallScope\n /** Human-readable provenance, e.g. `npm:foo@1.2.3` / `github:owner/repo@main`. */\n provenance: string\n /** Exact commit SHA when known (GitHub via git clone). */\n commit?: string\n /** Non-fatal problems met during the install, such as dependency install notices. */\n warnings: readonly string[]\n}\n\n/**\n * Injectable side-effect seams — tests swap these to stay offline; hosts\n * normally omit the whole object. Shared shape with the skills installer.\n */\nexport type ExtensionInstallIo = InstallIo\n\n/** Options for {@link installExtensionFromSpec}. */\nexport interface InstallExtensionFromSpecOptions {\n /** Raw source spec — see {@link parseExtensionInstallSource}. */\n source: string\n /** Candidate name to install when the source contains several extensions. */\n extension?: string\n /** Scope to install into. */\n scope: ExtensionInstallScope\n /** Replace an existing install of the same name. Default false → `exists` error. */\n overwrite?: boolean\n /** Working directory used to find the project root. @default process.cwd() */\n cwd?: string\n /** User home directory. @default os.homedir() */\n home?: string\n /** Storage prefix (e.g. `.zidane`). Matches discovery's default. */\n prefix?: string\n /** Called with a short status message as each step runs. */\n onProgress?: (message: string) => void\n /** Overrides for fetch, process execution and executable lookup. Mainly for tests. */\n io?: ExtensionInstallIo\n /** Timeout for the validation import. Default 10s (matches discovery). */\n importTimeoutMs?: number\n /**\n * Advanced: exact extensions root to install into, bypassing the\n * scope → `.{prefix}/extensions` resolution. Used by\n * {@link updateInstalledExtension} to replace an install in place\n * (e.g. one living under `.agents/extensions`).\n */\n installRoot?: string\n}\n\n// ---------------------------------------------------------------------------\n// Source spec parsing + fetch — thin wrappers over the shared install\n// layer (`src/install/source.ts`), re-raised in the extension error\n// vocabulary. The grammar, fetch pipeline, caps, and hardening live\n// there so the skills installer shares them verbatim.\n// ---------------------------------------------------------------------------\n\n/**\n * Classify a raw source string. See `parseInstallSource` for the grammar.\n *\n * @param raw - Source spec such as `owner/repo`, `npm:pkg` or a git URL.\n */\nexport function parseExtensionInstallSource(raw: string): ExtensionInstallSource {\n try {\n return parseInstallSource(raw)\n }\n catch (err) {\n rethrowAsExtensionError(err)\n }\n}\n\n/** Result of {@link fetchExtensionSource}: the provenance string and, for git clones, the commit SHA. */\nexport type ExtensionFetchResult = FetchSourceResult\n\n/**\n * Materialize the source into `destDir` (created by the caller, assumed\n * empty). Returns the provenance string plus, for git clones, the exact\n * commit SHA. See `fetchInstallSource` for transport details and caps.\n *\n * @param source - Parsed install source.\n * @param destDir - Empty directory to fetch into.\n * @param io - Overrides for fetch, process execution and executable lookup.\n * @param onProgress - Called with a short status message as the fetch runs.\n */\nexport async function fetchExtensionSource(\n source: ExtensionInstallSource,\n destDir: string,\n io: ExtensionInstallIo = {},\n onProgress?: (message: string) => void,\n): Promise<ExtensionFetchResult> {\n try {\n return await fetchInstallSource(source, destDir, io, onProgress)\n }\n catch (err) {\n rethrowAsExtensionError(err)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Candidate enumeration\n// ---------------------------------------------------------------------------\n\n/**\n * List the installable extensions inside a fetched tree. `fallbackName`\n * seeds the single-extension case when no manifest names it (repo or\n * unscoped package basename).\n *\n * @param dir - Root of the fetched tree.\n * @param fallbackName - Name to use when no manifest names the extension.\n */\nexport function listExtensionCandidates(dir: string, fallbackName: string): ExtensionInstallCandidate[] {\n if (findEntryFile(dir))\n return [{ name: readExtensionManifest(dir).manifest?.name ?? sanitizeName(fallbackName), dir }]\n const collection = join(dir, 'extensions')\n const out: ExtensionInstallCandidate[] = []\n let children: string[]\n try {\n children = readdirSync(collection)\n }\n catch {\n return out\n }\n for (const child of children.sort()) {\n if (child.startsWith('.'))\n continue\n const childDir = join(collection, child)\n try {\n if (!lstatSync(childDir).isDirectory())\n continue\n }\n catch {\n continue\n }\n if (findEntryFile(childDir))\n out.push({ name: child, dir: childDir })\n }\n return out\n}\n\n/** Basename-ish fallback: strip npm scope, keep folder-safe chars. */\nfunction sanitizeName(raw: string): string {\n const base = raw.includes('/') ? (raw.split('/').pop() as string) : raw\n return base.replace(/[^\\w.-]+/g, '-').replace(/^[.-]+/, '') || 'extension'\n}\n\n// ---------------------------------------------------------------------------\n// Orchestrator\n// ---------------------------------------------------------------------------\n\n/**\n * The full fetch → enumerate → deps → validate → copy pipeline. See the\n * module doc for invariants. Throws {@link ExtensionInstallError}; a throw\n * means nothing was written to the target roots.\n *\n * @param opts - Source, scope and install options.\n */\nexport async function installExtensionFromSpec(opts: InstallExtensionFromSpecOptions): Promise<ExtensionInstallResult> {\n const io = opts.io ?? {}\n const onProgress = opts.onProgress\n const source = parseExtensionInstallSource(opts.source)\n const warnings: string[] = []\n\n const stage = mkdtempSync(join(tmpdir(), 'zidane-ext-install-'))\n try {\n const fetchedInto = join(stage, 'fetched')\n mkdirSync(fetchedInto, { recursive: true })\n const { provenance, commit } = await fetchExtensionSource(source, fetchedInto, io, onProgress)\n\n // A `:subdir` source re-roots the candidate search. Physical (not\n // just string-level) containment lives in `resolveSubdirRoot` — a\n // malicious repo shipping a symlink (`evil -> /`) must not walk the\n // candidate search (and the validation IMPORT) onto the local\n // filesystem. Structural escapes are already rejected at parse time;\n // this is the belt-and-braces layer.\n let candidateRoot = fetchedInto\n let fallbackName = source.kind === 'github' ? source.repo : source.pkg\n if (source.kind === 'github' && source.subdir) {\n const rooted = resolveSubdirRoot(fetchedInto, source.subdir)\n if (!rooted.ok) {\n if (rooted.reason === 'missing')\n throw new ExtensionInstallError('no-extension', `\"${source.subdir}\" is not a directory in ${source.owner}/${source.repo}${source.ref ? `@${source.ref}` : ''}.`)\n throw new ExtensionInstallError(\n 'invalid-source',\n rooted.reason === 'escape-symlink'\n ? `Subdirectory \"${source.subdir}\" escapes the repository root through a symlink.`\n : `Subdirectory \"${source.subdir}\" escapes the repository root.`,\n )\n }\n candidateRoot = rooted.dir\n fallbackName = basename(resolve(fetchedInto, source.subdir))\n }\n const candidates = listExtensionCandidates(candidateRoot, fallbackName)\n if (candidates.length === 0) {\n throw new ExtensionInstallError('no-extension', `No extension found in ${opts.source}. Expected a root index.{ts,tsx,mjs,js} or extensions/<name>/index.* directories.`)\n }\n let candidate: ExtensionInstallCandidate\n if (opts.extension) {\n const match = candidates.find(c => c.name === opts.extension)\n if (!match) {\n throw new ExtensionInstallError(\n 'no-extension',\n `No extension named \"${opts.extension}\" in ${opts.source}. Available: ${candidates.map(c => c.name).join(', ')}`,\n candidates.map(c => c.name),\n )\n }\n candidate = match\n }\n else if (candidates.length > 1) {\n throw new ExtensionInstallError(\n 'multiple-candidates',\n `${opts.source} contains ${candidates.length} extensions. Pass one of: ${candidates.map(c => c.name).join(', ')}`,\n candidates.map(c => c.name),\n )\n }\n else {\n candidate = candidates[0] as ExtensionInstallCandidate\n }\n\n // Dependencies BEFORE validation — the import may need them.\n const pm = await installDependencies(candidate.dir, io, onProgress)\n if (pm)\n warnings.push(...pm.warnings)\n\n onProgress?.('validating extension module…')\n const definition = await validateCandidate(candidate, opts.importTimeoutMs ?? 10_000)\n if (definition.warning)\n warnings.push(definition.warning)\n\n const targetRoot = opts.installRoot ?? resolveInstallRoot(opts)\n const targetDir = join(targetRoot, definition.name)\n // Overwrites are fully live: discovery evicts the extension's module\n // graph before each import, so the next reload runs the new code.\n if (existsSync(targetDir) && !opts.overwrite)\n throw new ExtensionInstallError('exists', `${targetDir} already exists. Pass overwrite to replace it.`)\n\n onProgress?.(`installing to ${targetDir}…`)\n mkdirSync(targetRoot, { recursive: true })\n // Transactional target phase — the \"never half-install\" invariant. An\n // overwritten previous version is parked under a dot-name (invisible\n // to the scanner) and restored if anything past this point throws\n // (e.g. the in-place deps install failing on a full disk); on success\n // it is deleted. A fresh install that fails is wiped back to absence.\n const backupDir = existsSync(targetDir)\n ? join(targetRoot, `.${definition.name}.prev-${process.pid}-${Date.now()}`)\n : null\n if (backupDir)\n renameSync(targetDir, backupDir)\n try {\n cpSync(candidate.dir, targetDir, {\n recursive: true,\n // Materialize symlinked files: the stage dir they'd point into is\n // deleted after install, so copied links would dangle. (The\n // extension's code already ran at validation with the user's\n // consent — dereferencing discloses nothing new.)\n dereference: true,\n filter: src => !/(?:^|[\\\\/])(?:\\.git|node_modules)(?:$|[\\\\/])/.test(src),\n })\n // Re-install deps in place (node_modules excluded from the copy —\n // symlink layouts don't survive a cp).\n await installDependencies(targetDir, io, onProgress)\n\n const provenanceRecord: ExtensionInstallProvenance = {\n source: provenance,\n spec: opts.source,\n scope: opts.scope,\n ...(opts.extension !== undefined ? { extension: opts.extension } : {}),\n ...(commit !== undefined ? { commit } : {}),\n installedAt: new Date().toISOString(),\n }\n writeFileSync(join(targetDir, PROVENANCE_FILE), `${JSON.stringify(provenanceRecord, null, 2)}\\n`)\n }\n catch (err) {\n rmSync(targetDir, { recursive: true, force: true })\n if (backupDir)\n renameSync(backupDir, targetDir)\n throw err\n }\n if (backupDir)\n rmSync(backupDir, { recursive: true, force: true })\n\n return { name: definition.name, dir: targetDir, scope: opts.scope, provenance, ...(commit !== undefined ? { commit } : {}), warnings }\n }\n finally {\n rmSync(stage, { recursive: true, force: true })\n }\n}\n\n/**\n * Read `.install.json` provenance from an installed extension dir, or null.\n *\n * @param dir - Installed extension directory.\n */\nexport function readExtensionInstallProvenance(dir: string): ExtensionInstallProvenance | null {\n const raw = readJson(join(dir, PROVENANCE_FILE)) as Partial<ExtensionInstallProvenance> | null\n if (!raw || typeof raw.spec !== 'string' || typeof raw.source !== 'string')\n return null\n return {\n source: raw.source,\n spec: raw.spec,\n scope: raw.scope === 'project' ? 'project' : 'user',\n ...(typeof raw.extension === 'string' ? { extension: raw.extension } : {}),\n ...(typeof raw.commit === 'string' ? { commit: raw.commit } : {}),\n installedAt: typeof raw.installedAt === 'string' ? raw.installedAt : '',\n }\n}\n\n/** Identifies an installed extension and where to look for extension folders. Used by {@link updateInstalledExtension} and {@link removeInstalledExtension}. */\nexport interface InstalledExtensionTargetOptions {\n /** The installed extension's directory (e.g. `LoadedExtension.dir`). */\n dir: string\n /** Working directory used to find the project root. @default process.cwd() */\n cwd?: string\n /** User home directory. @default os.homedir() */\n home?: string\n /** Storage prefix. @default '.zidane' */\n prefix?: string\n /** Extra trusted roots (`ZIDANE_EXTENSION_DIRS` / host scan paths). */\n extensionScanPaths?: readonly string[]\n}\n\n/**\n * Containment guard shared by update/remove: `dir` must be a DIRECT child\n * of a recognized extensions root (the four `.{agents,prefix}/extensions`\n * search paths, plus any host scan roots). Refuses anything else — these\n * functions delete directories and must never be pointable at arbitrary\n * paths (an inline/builtin extension has no dir; a hand-typed path is not\n * an install).\n */\nfunction assertInstalledExtensionDir(opts: InstalledExtensionTargetOptions): { dir: string, root: string, scope: ExtensionInstallScope } {\n const dir = resolve(opts.dir)\n const roots: Array<{ path: string, scope: ExtensionInstallScope }> = [\n ...projectUserPaths({\n subPath: 'extensions',\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n }).map(p => ({ path: p.path, scope: p.source })),\n // Host scan roots are user-opted-in — user scope by convention.\n ...(opts.extensionScanPaths ?? []).map(p => ({ path: resolve(p), scope: 'user' as const })),\n ]\n const root = roots.find(r => dirname(dir) === r.path)\n if (!root)\n throw new ExtensionInstallError('not-installed', `${dir} is not inside an extensions folder, so it was left alone.`)\n return { dir, root: root.path, scope: root.scope }\n}\n\n/**\n * Delete an installed extension's directory. Works on any on-disk\n * extension (hand-authored ones too — no provenance required); the\n * containment guard is the safety boundary. The caller owns follow-up\n * (allowlist cleanup, registry reload).\n *\n * @param opts - The installed extension to remove.\n */\nexport function removeInstalledExtension(opts: InstalledExtensionTargetOptions): { dir: string } {\n const { dir } = assertInstalledExtensionDir(opts)\n if (!existsSync(dir))\n throw new ExtensionInstallError('not-installed', `${dir} does not exist.`)\n rmSync(dir, { recursive: true, force: true })\n return { dir }\n}\n\n/**\n * Cheap \"is there anything new?\" probe, no fetch of content:\n *\n * - GitHub (with a recorded commit): `git ls-remote` resolves the ref's\n * current SHA in one round-trip — equal SHA means up to date.\n * - npm: one registry metadata GET re-resolves the version/dist-tag —\n * an unchanged resolved provenance string means up to date.\n *\n * `null` = \"can't tell cheaply\" (no recorded commit, git missing,\n * ls-remote/parse failure) — the caller falls through to a full update,\n * which errors honestly if the network is actually down.\n */\nasync function checkRemoteUnchanged(\n source: ExtensionInstallSource,\n previous: ExtensionInstallProvenance,\n io: ExtensionInstallIo,\n): Promise<boolean | null> {\n if (source.kind === 'npm') {\n try {\n const { version } = await resolveNpmVersion(source, io.fetch ?? fetch)\n return `npm:${source.pkg}@${version}` === previous.source\n }\n catch {\n return null\n }\n }\n if (!previous.commit)\n return null\n const which = io.which ?? defaultWhich\n const exec = io.exec ?? defaultExec\n if (!which('git'))\n return null\n const url = `https://github.com/${source.owner}/${source.repo}.git`\n const refspecs = source.ref ? [`refs/heads/${source.ref}`, `refs/tags/${source.ref}`] : ['HEAD']\n // GIT_TERMINAL_PROMPT=0 — never let git prompt for credentials from\n // inside the TUI; an unreachable remote degrades to \"can't tell\" (null).\n const res = await exec('git', ['ls-remote', url, ...refspecs], { env: { GIT_TERMINAL_PROMPT: '0' } })\n if (!res.ok)\n return null\n const sha = res.stdout.trim().split('\\n')[0]?.split('\\t')[0]?.trim()\n if (!sha || !/^[0-9a-f]{40}$/.test(sha))\n return null\n return sha === previous.commit\n}\n\n/**\n * Re-run the recorded install for an extension: re-resolves the original\n * spec (so `owner/repo` follows the branch and `pkg` follows `latest`,\n * while pinned refs/versions stay pinned) and replaces the install\n * IN PLACE (same root the extension currently lives in, even a\n * `.agents/extensions` one). Requires `.install.json` provenance —\n * hand-authored extensions have nothing to update from.\n *\n * When the remote is provably unchanged (recorded commit matches\n * `git ls-remote`, or the npm version re-resolves identically), the\n * reinstall is skipped entirely and the result carries `upToDate: true`.\n *\n * @param opts - The installed extension to update, plus progress and IO overrides.\n */\nexport async function updateInstalledExtension(\n opts: InstalledExtensionTargetOptions & {\n onProgress?: (message: string) => void\n io?: ExtensionInstallIo\n },\n): Promise<ExtensionInstallResult & { previous: ExtensionInstallProvenance, upToDate: boolean }> {\n const { dir, root, scope } = assertInstalledExtensionDir(opts)\n const provenance = readExtensionInstallProvenance(dir)\n if (!provenance)\n throw new ExtensionInstallError('no-provenance', `${dir} has no .install.json. It was not installed from a source, so there is nothing to update from.`)\n\n const source = parseExtensionInstallSource(provenance.spec)\n opts.onProgress?.(`checking ${provenance.source} for changes…`)\n if (await checkRemoteUnchanged(source, provenance, opts.io ?? {}) === true) {\n return {\n name: basename(dir),\n dir,\n scope,\n provenance: provenance.source,\n ...(provenance.commit !== undefined ? { commit: provenance.commit } : {}),\n warnings: [],\n previous: provenance,\n upToDate: true,\n }\n }\n\n const result = await installExtensionFromSpec({\n source: provenance.spec,\n ...(provenance.extension !== undefined ? { extension: provenance.extension } : {}),\n // Scope from where the extension ACTUALLY lives (the containment\n // root), not the recorded field — a stale/corrupt provenance scope\n // would otherwise self-perpetuate through every future update.\n scope,\n overwrite: true,\n installRoot: root,\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.onProgress !== undefined ? { onProgress: opts.onProgress } : {}),\n ...(opts.io !== undefined ? { io: opts.io } : {}),\n })\n // The upstream may have renamed its definition — the fresh install then\n // landed in a sibling folder. Remove the superseded dir so an update\n // never leaves two generations installed side by side.\n if (result.dir !== dir)\n rmSync(dir, { recursive: true, force: true })\n return { ...result, previous: provenance, upToDate: false }\n}\n\n/** The scope's canonical `.{prefix}/extensions` write root. */\nfunction resolveInstallRoot(opts: { scope: ExtensionInstallScope, cwd?: string, home?: string, prefix?: string }): string {\n const prefix = (opts.prefix ?? '.zidane').replace(/^\\./, '')\n const paths = projectUserPaths({\n subPath: 'extensions',\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n })\n const root = paths.find(p => p.source === opts.scope && p.path.includes(`.${prefix}`))\n if (!root)\n throw new Error(`unreachable: projectUserPaths yields a .${prefix} root per scope`)\n return root.path\n}\n\nasync function validateCandidate(\n candidate: ExtensionInstallCandidate,\n timeoutMs: number,\n): Promise<{ name: string, warning?: string }> {\n const entry = findEntryFile(candidate.dir)\n if (!entry)\n throw new ExtensionInstallError('no-extension', `${candidate.dir} has no index.{ts,tsx,mjs,js} entry.`)\n const version = checkZidaneVersion(candidate.dir)\n if (version?.level === 'error')\n throw new ExtensionInstallError('validate-failed', version.message)\n let mod: unknown\n try {\n mod = await withTimeout(\n importExtensionEntry(entry, candidate.dir),\n timeoutMs,\n `Timed out importing ${entry} after ${timeoutMs}ms.`,\n )\n }\n catch (err) {\n throw new ExtensionInstallError('validate-failed', `Cannot import the extension: ${errorMessage(err)}`)\n }\n finally {\n // The staged dir moves or is deleted next: nothing may stay cached under it.\n releaseExtensionModules(candidate.dir)\n }\n const m = (mod ?? {}) as Record<string, unknown>\n const definition = isExtensionDefinition(m.default) ? m.default : isExtensionDefinition(m.extension) ? m.extension : null\n if (!definition)\n throw new ExtensionInstallError('validate-failed', `Module at ${entry} does not export an ExtensionDefinition (expected \\`default\\` or named \\`extension\\`).`)\n if (definition.apiVersion !== undefined && definition.apiVersion !== 1)\n throw new ExtensionInstallError('validate-failed', `Unsupported extension API version \"${definition.apiVersion}\". This runtime supports apiVersion 1.`)\n if (!/^[\\w.-]+$/.test(definition.name) || definition.name.startsWith('.'))\n throw new ExtensionInstallError('validate-failed', `Extension name \"${definition.name}\" is not folder-safe ([A-Za-z0-9_.-], no leading dot).`)\n const manifest = readExtensionManifest(candidate.dir).manifest\n if (manifest?.apiVersion !== undefined && manifest.apiVersion !== 1)\n throw new ExtensionInstallError('validate-failed', `Unsupported extension API version \"${manifest.apiVersion}\" in manifest. This runtime supports apiVersion 1.`)\n if (manifest?.name && manifest.name !== definition.name)\n throw new ExtensionInstallError('validate-failed', `Manifest name \"${manifest.name}\" does not match the module's exported name \"${definition.name}\".`)\n return { name: definition.name, ...(version ? { warning: version.message } : {}) }\n}\n\n// ---------------------------------------------------------------------------\n// Dependency install\n// ---------------------------------------------------------------------------\n\nconst PM_COMMANDS: ReadonlyArray<{ cmd: string, args: readonly string[] }> = [\n // bun skips lifecycle scripts by default (trustedDependencies opt-in).\n { cmd: 'bun', args: ['install'] },\n { cmd: 'npm', args: ['install', '--ignore-scripts', '--no-audit', '--no-fund'] },\n { cmd: 'pnpm', args: ['install', '--ignore-scripts'] },\n { cmd: 'yarn', args: ['install', '--ignore-scripts'] },\n]\n\n/** Install deps when `dir/package.json` declares any. Returns null when skipped. */\nasync function installDependencies(\n dir: string,\n io: ExtensionInstallIo,\n onProgress?: (message: string) => void,\n): Promise<{ warnings: string[] } | null> {\n const pkg = readJson(join(dir, 'package.json')) as { dependencies?: Record<string, string> } | null\n if (!pkg?.dependencies || Object.keys(pkg.dependencies).length === 0)\n return null\n const exec = io.exec ?? defaultExec\n const which = io.which ?? defaultWhich\n const pm = PM_COMMANDS.find(p => which(p.cmd))\n if (!pm) {\n throw new ExtensionInstallError('no-package-manager', 'The extension has dependencies, but no package manager (bun, npm, pnpm or yarn) is on PATH.')\n }\n onProgress?.(`installing dependencies with ${pm.cmd}…`)\n const res = await exec(pm.cmd, pm.args, { cwd: dir })\n if (!res.ok)\n throw new ExtensionInstallError('deps-failed', `${pm.cmd} install failed: ${lastLine(res.stderr) || lastLine(res.stdout)}`)\n return { warnings: [] }\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction readJson(path: string): unknown {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch {\n return null\n }\n}\n","/**\n * Prompt bridge — the host hand-off behind `ctx.submitPrompt`.\n *\n * Extensions submit prompts at any point in their lifetime; the host (the\n * TUI, post-mount) attaches the single handler that routes them into its\n * real submit machinery (run-or-queue gate, `Agent.steer`, `Agent.followUp`).\n * Same holder idiom as the HUD. Prompts are deliberately NOT\n * buffered pre-attach: silently replaying stale prompts after mount would\n * be surprising — `submit` returns `false` so callers can react.\n */\n\nexport interface ExtensionPromptDelivery {\n /**\n * `'auto'` (default) — run now when idle, join the follow-up queue when a\n * run is active. `'steer'` — inject into the CURRENT run between turns\n * (falls back to `auto` when idle). `'followUp'` — queue until the current\n * run finishes (falls back to `auto` when idle).\n */\n delivery?: 'auto' | 'steer' | 'followUp'\n}\n\n/** Connects `ctx.submitPrompt` to the host's prompt pipeline. Hosts attach their handler after the UI mounts. */\nexport interface ExtensionPromptBridge {\n /** Route a prompt to the host. `false` when no host handler is attached, the text is blank, or the handler throws. */\n submit: (text: string, opts?: ExtensionPromptDelivery) => boolean\n /** Attach the host handler (last attach wins). Returns a detach fn. */\n attach: (handler: (text: string, opts?: ExtensionPromptDelivery) => void) => () => void\n}\n\n/** Creates a bridge with no handler attached. `submit` returns `false` until a host attaches one. */\nexport function createExtensionPromptBridge(): ExtensionPromptBridge {\n let handler: ((text: string, opts?: ExtensionPromptDelivery) => void) | null = null\n return {\n submit(text, opts) {\n if (!handler || text.trim().length === 0)\n return false\n try {\n handler(text, opts)\n return true\n }\n catch {\n // The host submit path must never throw into extension code.\n return false\n }\n },\n attach(next) {\n handler = next\n return () => {\n if (handler === next)\n handler = null\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,kCAAkC;;AAG/C,MAAM,cAAc;;;;;;AAuDpB,SAAgB,uBAAuB,SAAiC;CACtE,IAAI;CACJ,IAAI;EACF,OAAO,KAAK,UAAU,OAAO;CAC/B,QACM;EACJ,OAAO;CACT;CAEA,IAAI,OAAO,SAAS,UAClB,OAAO;CACT,OAAO,OAAO,WAAW,MAAM,MAAM;AACvC;;;;;;;AAkBA,SAAS,QAAQ,OAA2B,KAAqB;CAC/D,OAAO,UAAU,KAAA,IAAY,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG;AACzD;;AAGA,SAAgB,qBAAmC;CACjD,MAAM,2BAAW,IAAI,IAA0B;CAC/C,MAAM,gCAAgB,IAAI,IAA8C;CAIxE,MAAM,QAAQ,OAAO,WAAW;CAEhC,SAAS,SAAS,OAAqB,KAAmB;EACxD,MAAM,QAAQ,IAAI,GAAG;EACrB,MAAM,UAAU,KAAK,GAAG;EACxB,OAAO,MAAM,UAAU,SAAS,aAAa;GAC3C,MAAM,UAAU,MAAM,UAAU,MAAM;GACtC,IAAI,YAAY,KAAA,GACd,MAAM,QAAQ,OAAO,OAAO;EAChC;CACF;CAEA,SAAS,OAAO,OAAqB,KAAgC;EACnE,KAAK,MAAM,MAAM,CAAC,GAAG,MAAM,WAAW,GACpC,IAAI;GACF,GAAG,GAAG;EACR,QACM,CAEN;CAEJ;CAEA,OAAO;EACL,MAAM,aAAa;GACjB,IAAI,QAAQ,SAAS,IAAI,WAAW;GACpC,IAAI,OAAO;IAGT,MAAM,cAAc;IACpB,MAAM,8BAAc,IAAI,IAAI;GAC9B,OACK;IACH,QAAQ;KACN,YAAY;KACZ,6BAAa,IAAI,IAAI;KACrB,WAAW,CAAC;KACZ,yBAAS,IAAI,IAAI;KAGjB,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU;IAChD;IACA,SAAS,IAAI,aAAa,KAAK;GACjC;GACA,MAAM,aAAa,MAAM;GACzB,MAAM,aAAsB,SAAS,IAAI,WAAW,CAAC,EAAE,eAAe;GAEtE,OAAO;IACL,KAAK,SAAS,MAAM;KAClB,IAAI,CAAC,KAAK,GACR,OAAO;KACT,MAAM,QAAQ,uBAAuB,OAAO;KAC5C,IAAI,UAAU,QAAQ,QAAA,OACpB,OAAO;KACT,MAAM,UAAU,SAAS,IAAI,WAAW;KACxC,MAAM,MAAM,QAAQ;KAGpB,SAAS,SAAS,QAAQ,OAAO,GAAG,CAAC;KACrC,MAAM,YAAY,OAAO,MAAM,cAAc,WAAW,KAAK,YAAY,KAAA;KACzE,OAAO,SAAS;MAAE;MAAS,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;MAAI,QAAQ;KAAQ,CAAC;KAC/F,MAAM,WAAiC;MAAE;MAAa,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;MAAI;MAAS;MAAK;KAAM;KAC7H,KAAK,MAAM,MAAM,CAAC,GAAG,aAAa,GAChC,IAAI;MACF,GAAG,QAAQ;KACb,QACM,CAEN;KAEF,OAAO;IACT;IACA,UAAU,IAAI;KACZ,IAAI,CAAC,KAAK,KAAK,OAAO,OAAO,YAC3B,aAAa,CAAC;KAEhB,SADyB,IAAI,WACvB,CAAC,CAAC,YAAY,IAAI,EAAE;KAC1B,aAAa;MACX,SAAS,IAAI,WAAW,CAAC,EAAE,YAAY,OAAO,EAAE;KAClD;IACF;IACA,QAAQ;KACN,IAAI,KAAK,GACP,SAAS,IAAI,WAAW,CAAC,CAAE,YAAY,MAAM;IACjD;GACF;EACF;EAEA,QAAQ,UAAU;GAChB,MAAM,QAAQ,SAAS,IAAI,SAAS,WAAW;GAG/C,IAAI,CAAC,OACH,OAAO;GACT,MAAM,MAAM,QAAQ,SAAS,OAAO,SAAS,GAAG;GAChD,IAAI,MAAM,QAAQ,IAAI,GAAG,GAAG;IAG1B,IAAI,QAAQ,IAAI,cACd,QAAQ,OAAO,MAAM,wCAAwC,SAAS,YAAY,OAAO,IAAI,GAAG;IAClG,OAAO;GACT;GACA,SAAS,OAAO,GAAG;GACnB,OAAO,OAAO;IACZ,SAAS,SAAS;IAClB,GAAI,SAAS,cAAc,KAAA,IAAY,EAAE,WAAW,SAAS,UAAU,IAAI,CAAC;IAC5E,QAAQ;GACV,CAAC;GACD,OAAO;EACT;EAEA,OAAO,IAAI;GACT,cAAc,IAAI,EAAE;GACpB,aAAa;IACX,cAAc,OAAO,EAAE;GACzB;EACF;CACF;AACF;;;;;;;;;ACoEA,SAAS,uBAAuB,OAAqB;CACnD,OAAO;AACT;;;;;;;;;AAUA,SAAgB,2BAA2B,MAAoC;CAC7E,QAAQ,KAAK,MAAb;EACE,KAAK,WACH,OAAO,OAAO,KAAK,YAAY;EACjC,KAAK,UAKH,OAAO,OAAO,KAAK,YAAY,YAC1B,OAAO,SAAS,KAAK,OAAO,MAC3B,KAAK,QAAQ,KAAA,KAAa,KAAK,WAAW,KAAK,SAC/C,KAAK,QAAQ,KAAA,KAAa,KAAK,WAAW,KAAK;EACvD,KAAK,UAOH,OAAO,OAAO,KAAK,YAAY;EACjC,KAAK,QAGH,OAAO,OAAO,KAAK,YAAY,YAC1B,MAAM,QAAQ,KAAK,OAAO,KAC1B,KAAK,QAAQ,SAAS,KACtB,KAAK,QAAQ,OAAM,MAAK,KAAK,OAAO,EAAE,UAAU,YAAY,OAAO,EAAE,UAAU,QAAQ,KACvF,KAAK,QAAQ,MAAK,MAAK,EAAE,UAAU,KAAK,OAAO;EACtD,KAAK,SAIH,OAAO,OAAO,KAAK,YAAY;EACjC,KAAK,UAGH,OAAO,OAAO,KAAK,QAAQ,eACrB,KAAK,WAAW,KAAA,KAAa,OAAO,KAAK,WAAW;EAC5D,SACE,OAAO,uBAAuB,IAAI;CACtC;AACF;;AAOA,SAAS,WAAW,SAAgC;CAClD,IAAI;EACF,OAAO,IAAI,OAAO,OAAO;CAC3B,QACM;EACJ,OAAO;CACT;AACF;;;;;;;;;;;AAYA,SAAgB,4BAA4B,MAA2B,OAA+C;CACpH,QAAQ,KAAK,MAAb;EACE,KAAK;GACH,IAAI,OAAO,UAAU,WACnB,OAAO;GACT;EACF,KAAK;GACH,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GACrD,OAAO;GACT,IAAI,KAAK,QAAQ,KAAA,KAAa,QAAQ,KAAK,KACzC,OAAO;GACT,IAAI,KAAK,QAAQ,KAAA,KAAa,QAAQ,KAAK,KACzC,OAAO;GACT;EACF,KAAK;GACH,IAAI,OAAO,UAAU,UACnB,OAAO;GACT,IAAI,KAAK,SAAS;IAChB,MAAM,KAAK,WAAW,KAAK,OAAO;IAGlC,IAAI,MAAM,CAAC,GAAG,KAAK,KAAK,GACtB,OAAO;GACX;GACA;EAEF,KAAK;GACH,IAAI,OAAO,UAAU,YAAY,CAAC,KAAK,QAAQ,MAAK,MAAK,EAAE,UAAU,KAAK,GACxE,OAAO;GACT;EACF,KAAK;GACH,IAAI,OAAO,UAAU,UACnB,OAAO;GACT;EACF,KAAK,UAEH,OAAO;EACT,SACE,OAAO,uBAAuB,IAAI;CACtC;CACA,IAAI,KAAK,UACP,IAAI;EACF,OAAO,KAAK,SAAS,KAAK;CAC5B,QACM;EACJ,OAAO;CACT;CAEF,OAAO;AACT;AAoBA,SAAgB,4BACd,MACA,WACkC;CAClC,IAAI,KAAK,SAAS,UAChB,OAAO,KAAA;CACT,IAAI,cAAc,KAAA,GAChB,OAAO,KAAK;CACd,IAAI,CAAC,4BAA4B,MAAM,SAAS,GAC9C,OAAO,KAAK;CACd,OAAO;AACT;;;;;;;;;AAUA,SAAgB,2BAA2B,MAAiC,GAAmB;CAC7F,IAAI,IAAI;CACR,IAAI,KAAK,QAAQ,KAAA,GACf,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;CAC1B,IAAI,KAAK,QAAQ,KAAA,GACf,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;CAC1B,IAAI,KAAK,SAAS,KAAA,KAAa,KAAK,OAAO,GAAG;EAC5C,MAAM,OAAO,KAAK,OAAO;EACzB,IAAI,OAAO,KAAK,OAAO,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK;EAErD,IAAI,KAAK,QAAQ,KAAA,GACf,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;EAC1B,IAAI,KAAK,QAAQ,KAAA,GACf,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;CAC5B;CACA,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,2BACd,MACA,KACkC;CAClC,QAAQ,KAAK,MAAb;EACE,KAAK,UAAU;GACb,MAAM,IAAI,OAAO,IAAI,KAAK,CAAC;GAC3B,IAAI,CAAC,OAAO,SAAS,CAAC,GACpB,OAAO,KAAA;GACT,MAAM,UAAU,2BAA2B,MAAM,CAAC;GAClD,OAAO,4BAA4B,MAAM,OAAO,IAAI,UAAU,KAAA;EAChE;EACA,KAAK,UACH,OAAO,4BAA4B,MAAM,GAAG,IAAI,MAAM,KAAA;EACxD,KAAK,SAGH,OAAO,4BAA4B,MAAM,GAAG,IAAI,MAAM,KAAA;EACxD,SAEE;CACJ;AACF;;;;;;;;;;;;;AAcA,SAAgB,uBACd,UACA,UACA,SACA,KACkC;CAClC,MAAM,OAAO,SAAS,YAAY,QAAQ,GAAG;CAC7C,IAAI,CAAC,MACH,OAAO,KAAA;CACT,OAAO,4BAA4B,MAAM,UAAU,kBAAkB,QAAQ,GAAG,IAAI;AACtF;;;;;;;;;;;;AAaA,SAAgB,mCACd,OACA,OACwE;CACxE,MAAM,WAAiD,CAAC;CACxD,MAAM,WAAqB,CAAC;CAC5B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;EAAE;EAAU;CAAS;CAC9B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;EAChD,MAAM,OAAO,QAAQ;EACrB,IAAI,CAAC,QAAQ,KAAK,SAAS,YAAY,CAAC,4BAA4B,MAAM,KAAK,GAAG;GAChF,SAAS,KAAK,GAAG;GACjB;EACF;EACA,SAAS,OAAO;CAClB;CACA,OAAO;EAAE;EAAU;CAAS;AAC9B;;;;;;;;;;;;AAuCA,SAAgB,mBACd,UAC2B;CAC3B,MAAM,QAAmC,CAAC;CAC1C,KAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,SAAS,WAAW,GAClE,KAAK,MAAM,UAAU,OAAO,OAAO,KAAK,GAAG;EACzC,IAAI,OAAO,SAAS,UAClB;EAEF,MAAM,EAAE,UAAU,WAAW,GAAG,SAAS;EACzC,MAAM,KAAK;GAAE,GAAG;GAAM;EAAU,CAA4B;CAC9D;CAEF,OAAO;EACL,YAAY,OAAO,OAAO,SAAS,gBAAgB;EACnD;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AC7jBA,SAAgB,gBAAgB,YAAsD;CACpF,OAAO;AACT;;;;ACpBA,MAAM,0BAAU,IAAI,IAA8B;;AAGlD,IAAI;AAEJ,SAAS,UAAU,KAAsC;CAKvD,OAAO;EAAE,GAAI;EAAiC,SAAU,IAA8B,WAAW;CAAI;AACvG;;;;;;;;;;;;;;;;AAiBA,SAAgB,6BAA6B,SAAiD;CAC5F,MAAM,QAAQ,OAAO,QAAQ,OAAO,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,QAAQ,IAAI,SAAS,CAAC;CACrF,IAAI,MAAM,WAAW,GACnB;CACF,KAAK,MAAM,CAAC,WAAW,WAAW,OAChC,QAAQ,IAAI,WAAW,MAAM;CAC/B,WAAW,KAAA;CACX,MAAM,MAAO,WAAkD;CAC/D,IAAI,OAAO,KAAK,WAAW,YACzB;CACF,IAAI,OAAO;EACT,MAAM,uBAAuB,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;EAC7D,MAAM,OAAO;GACX,KAAK,MAAM,CAAC,WAAW,WAAW,OAChC,MAAM,OAAO,WAAW,YAAY;IAElC,OAAO;KAAE,SAAS,UADN,OAAO,WAAW,aAAa,MAAM,OAAO,IAAI,MAC7B;KAAG,QAAQ;IAAkB;GAC9D,CAAC;EAEL;CACF,CAAC;AACH;;;;;;;AAQA,SAAgB,8BAAgF;CAC9F,MAAM,UAAU,aAAa,YAAY;EACvC,MAAM,UAAU,MAAM,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,WAAW,YACpE,CAAC,WAAW,UAAU,OAAO,WAAW,aAAa,MAAM,OAAO,IAAI,MAAM,CAAC,CAAU,CAAC;EAC1F,OAAO,OAAO,YAAY,OAAO;CACnC,EAAA,CAAG;CACH,WAAW;CACX,QAAQ,YAAY;EAClB,IAAI,aAAa,SACf,WAAW,KAAA;CACf,CAAC;CACD,OAAO;AACT;;;;;;;;;;;;;;;;;;ACjGA,MAAM,sBAAsB,YAAY,IAAI,SAAS,KAAK,KACrD,CAAC,YAAY,IAAI,SAAS,QAAQ,KAClC,CAAC,YAAY,IAAI,SAAS,MAAM;AAIrC,SAAS,aAAa,MAA8B;CAClD,MAAM,IAAI,qCAAqC,KAAK,KAAK,KAAK,CAAC;CAC/D,OAAO,IAAI;EAAC,OAAO,EAAE,EAAE;EAAG,OAAO,EAAE,EAAE;EAAG,OAAO,EAAE,EAAE;CAAC,IAAI;AAC1D;AAEA,SAAS,QAAQ,GAAY,GAAoB;CAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;AAChD;;AAGA,SAAS,aAAa,MAA0C;CAC9D,MAAM,IAAI,0DAA0D,KAAK,KAAK,KAAK,CAAC;CACpF,IAAI,CAAC,GACH,OAAO;CACT,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,QAAQ;EAAC,EAAE;EAAI,EAAE;EAAI,EAAE;CAAE,GAAG;EACrC,IAAI,SAAS,KAAA,KAAa,UAAU,KAAK,IAAI,GAC3C;EACF,MAAM,KAAK,OAAO,IAAI,CAAC;CACzB;CACA,OAAO,EAAE,MAAM;AACjB;;AAYA,SAAS,gBAAgB,YAA4C;CACnE,IAAI,eAAe,OAAO,OAAO,KAAK,UAAU,GAC9C,OAAO;EAAE,IAAI;EAAI,OAAO;GAAC;GAAG;GAAG;EAAC;EAAG,OAAO;CAAE;CAC9C,MAAM,IAAI,6BAA6B,KAAK,UAAU;CACtD,IAAI,CAAC,GACH,OAAO,KAAA;CACT,MAAM,GAAG,KAAK,IAAI,OAAO,MAAM;CAC/B,MAAM,UAAU,aAAa,IAAI;CACjC,IAAI,CAAC,SACH,OAAO,KAAA;CACT,MAAM,CAAC,QAAQ,GAAG,QAAQ,GAAG,QAAQ,KAAK,QAAQ;CAClD,OAAO;EAAE;EAAI,OAAO;GAAC;GAAO;GAAO;EAAK;EAAG,OAAO,QAAQ,MAAM;CAAO;AACzE;;AAGA,SAAS,WAAW,SAAkB,YAAiC;CACrE,IAAI,WAAW,OAAO,OAAO,WAAW,OAAO,MAC7C,OAAO;CACT,IAAI,WAAW,OAAO,KACpB,OAAO,oBAAoB,SAAS,UAAU;CAChD,OAAO,QAAQ,SAAS,WAAW,KAAK,KAAK;AAC/C;AAEA,SAAS,oBAAoB,SAAkB,EAAE,IAAI,OAAO,SAA8B;CACxF,MAAM,CAAC,OAAO,OAAO,SAAS;CAC9B,QAAQ,IAAR;EACE,KAAK,MAAM,OAAO,QAAQ,SAAS,KAAK,KAAK;EAC7C,KAAK,KAAK,OAAO,UAAU,IAAI,QAAQ,SAAS,KAAK,IAAI,IAAI,QAAQ,SAAS;GAAC,UAAU,IAAI,QAAQ,IAAI;GAAO,UAAU,IAAI,IAAI,QAAQ;GAAG;EAAC,CAAC,KAAK;EACpJ,KAAK,MAAM,OAAO,UAAU,IAAI,QAAQ,SAAS,KAAK,KAAK,IAAI,QAAQ,SAAS;GAAC,UAAU,IAAI,QAAQ,IAAI;GAAO,UAAU,IAAI,IAAI,QAAQ;GAAG;EAAC,CAAC,IAAI;EACrJ,KAAK,KAAK,OAAO,QAAQ,SAAS,KAAK,IAAI;EAC3C,KAAK,KAAK;GACR,MAAM,UAAmB,QAAQ,KAAK,UAAU,IAAI;IAAC,QAAQ;IAAG;IAAG;GAAC,IAAI,QAAQ,KAAK,UAAU,IAAI;IAAC;IAAG,QAAQ;IAAG;GAAC,IAAI;IAAC;IAAG;IAAG,QAAQ;GAAC;GACvI,OAAO,QAAQ,SAAS,KAAK,KAAK,KAAK,QAAQ,SAAS,OAAO,IAAI;EACrE;EACA,KAAK,KAAK;GACR,MAAM,UAAmB,UAAU,IAAI;IAAC,QAAQ;IAAG;IAAG;GAAC,IAAI;IAAC;IAAO,QAAQ;IAAG;GAAC;GAC/E,OAAO,QAAQ,SAAS,KAAK,KAAK,KAAK,QAAQ,SAAS,OAAO,IAAI;EACrE;EACA,SAAS;GAEP,IAAI,UAAU,GACZ,OAAO,QAAQ,SAAS,KAAK,MAAM;GACrC,MAAM,UAAmB,UAAU,IAAI;IAAC,QAAQ;IAAG;IAAG;GAAC,IAAI,UAAU,IAAI;IAAC;IAAO,QAAQ;IAAG;GAAC,IAAI;IAAC;IAAU;IAAG;GAAC;GAChH,OAAO,QAAQ,SAAS,KAAK,KAAK,KAAK,QAAQ,SAAS,OAAO,IAAI;EACrE;CACF;AACF;;;;;;;;;;AAWA,SAAgB,sBAAsB,SAAiB,OAAoC;CACzF,MAAM,IAAI,aAAa,OAAO;CAC9B,MAAM,eAAe,WAAW,KAAK;CACrC,IAAI,CAAC,KAAK,aAAa,WAAW,GAChC,OAAO,KAAA;CACT,OAAO,aAAa,MAAK,gBAAe,YAAY,OAAM,MAAK,oBAAoB,GAAG,CAAC,CAAC,CAAC;AAC3F;;;;;;;;;;AAWA,SAAgB,kBAAkB,SAAiB,OAAoC;CACrF,MAAM,IAAI,aAAa,OAAO;CAC9B,MAAM,eAAe,WAAW,KAAK;CACrC,IAAI,CAAC,KAAK,aAAa,WAAW,GAChC,OAAO,KAAA;CACT,OAAO,aAAa,OAAM,gBAAe,YAAY,MAAK,MAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;AACnF;;AAGA,SAAS,WAAW,OAA+B;CACjD,MAAM,eAA+B,CAAC;CACtC,KAAK,MAAM,eAAe,MAAM,MAAM,IAAI,GAAG;EAE3C,MAAM,cAAc,YAAY,KAAK,CAAC,CAAC,QAAQ,sBAAsB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,IAAI,eAAe;EAC3H,IAAI,YAAY,SAAS,KAAK,YAAY,OAAM,MAAK,MAAM,KAAA,CAAS,GAClE,aAAa,KAAK,WAAW;CACjC;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,oBAAoB,KAAiC;CACnE,IAAI;EAEF,MAAM,QADM,KAAK,MAAM,aAAa,QAAQ,KAAK,cAAc,GAAG,MAAM,CACxD,CAAC,EAAE,kBAAkB;EACrC,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI,KAAA;CACpE,QACM;EACJ;CACF;AACF;;;;;;;;;;;;AA6BA,SAAgB,mBAAmB,KAAa,UAAqC,CAAC,GAAgC;CACpH,IAAI,QAAQ,cAAc,qBACxB,OAAO;CACT,MAAM,QAAQ,oBAAoB,GAAG;CACrC,IAAI,CAAC,OACH,OAAO;CACT,MAAM,OAAO,QAAQ,eAAe;CACpC,IAAI,sBAAsB,MAAM,KAAK,MAAM,OACzC,OAAO;CACT,IAAI,kBAAkB,MAAM,KAAK,GAC/B,OAAO;EAAE,OAAO;EAAS,SAAS,gBAAgB,MAAM,mBAAmB,KAAK;CAA+D;CACjJ,OAAO;EAAE,OAAO;EAAW,SAAS,mBAAmB,MAAM,mBAAmB,KAAK;CAA0D;AACjJ;;;;;;;;;AC1MA,SAAgB,YACd,SACA,WACA,SACA,WACY;CACZ,IAAI;CACJ,MAAM,UAAU,IAAI,SAAgB,GAAG,WAAW;EAChD,QAAQ,iBAAiB;GACvB,YAAY;GACZ,OAAO,IAAI,MAAM,OAAO,CAAC;EAC3B,GAAG,SAAS;CACd,CAAC;CACD,OAAO,QAAQ,KAAK,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,cAAc;EACpD,IAAI,OACF,aAAa,KAAK;CACtB,CAAC;AACH;;;;;;;ACyBA,MAAM,WAAW,eAAe,YAAY;;;;;;;;AAa5C,MAAM,mBAAmB;CAAC;CAAY;CAAa;CAAa;AAAU;AAC1E,MAAMA,0CAAwB,IAAI,IAAI;CAAC;CAAa;CAAa;AAAa,CAAC;;;;;;;;;AAiC/E,SAAgB,0BAA0B,OAItC,CAAC,GAAwB;CAC3B,OAAO,iBAAiB;EAAE,SAAS;EAAc,GAAG;CAAK,CAAC;AAC5D;;;;;;;;;;;;AAaA,SAAgB,0BAA0B,QAA4B,QAAQ,IAAI,uBAAiC;CACjH,IAAI,CAAC,OACH,OAAO,CAAC;CACV,OAAO,MAAM,MAAM,SAAS,CAAC,CAAC,KAAI,MAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO;AACjE;;;;;;;;;;;;AAaA,SAAgB,yBAAyB,OAKrC,CAAC,GAAyB;CAC5B,MAAM,uBAAO,IAAI,IAAgC;CACjD,KAAK,MAAM,SAAS,4BAA4B,IAAI,GAClD,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,GACtB,KAAK,IAAI,MAAM,MAAM,KAAK;CAE9B,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC;AAC1B;AAEA,SAAS,4BAA4B,OAKjC,CAAC,GAAyB;CAC5B,MAAM,QAA6B,CACjC,IAAI,KAAK,sBAAsB,CAAC,EAAA,CAAG,KAAI,UAAS;EAAE,MAAM,QAAQ,IAAI;EAAG,QAAQ;CAAgB,EAAE,GACjG,GAAG,0BAA0B,IAAI,CACnC;CACA,MAAM,aAAmC,CAAC;CAE1C,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI;GAKF,IAAI,UAAU,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,GAC/C;GACF,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,YAAY,GACpC;EACJ,QACM;GACJ;EACF;EACA,IAAI;EACJ,IAAI;GACF,WAAW,aAAa,KAAK,IAAI;EACnC,SACO,KAAK;GACV,SAAS,YAAY,KAAK,QAAQ,GAAG;GACrC;EACF;EACA,IAAI;EACJ,IAAI;GACF,UAAU,YAAY,KAAK,IAAI;EACjC,SACO,KAAK;GACV,SAAS,WAAW,KAAK,QAAQ,GAAG;GACpC;EACF;EACA,KAAK,MAAM,QAAQ,SAAS;GAC1B,IAAI,KAAK,WAAW,GAAG,GACrB;GACF,MAAM,MAAM,QAAQ,KAAK,MAAM,IAAI;GACnC,IAAI;IAGF,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,YAAY,GAC9B;GACJ,SACO,KAAK;IACV,SAAS,QAAQ,OAAO,GAAG;IAC3B;GACF;GACA,MAAM,QAAQ,cAAc,GAAG;GAC/B,IAAI,CAAC,OACH;GAIF,IAAI;IAEF,IAAI,CADc,aAAa,KAClB,CAAC,CAAC,WAAW,WAAW,GAAG,GAAG;KACzC,SAAS,QAAQ,yBAAS,IAAI,MAAM,8BAA8B,UAAU,CAAC;KAC7E;IACF;GACF,SACO,KAAK;IACV,SAAS,YAAY,SAAS,GAAG;IACjC;GACF;GACA,WAAW,KAAK;IAAE;IAAM;IAAK;IAAO,QAAQ,KAAK;IAAQ,GAAG,sBAAsB,GAAG;GAAE,CAAC;EAC1F;CACF;CACA,OAAO;AACT;;;;;AAMA,SAAgB,cAAc,KAA4B;CACxD,KAAK,MAAM,aAAa,kBAAkB;EACxC,MAAM,OAAO,QAAQ,KAAK,SAAS;EACnC,IAAI;GACF,IAAI,UAAU,IAAI,CAAC,CAAC,OAAO,GACzB,OAAO;EACX,QACM;GACJ;EACF;CACF;CACA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,eAAsB,mBAAmB,OASrC,CAAC,GAA+B;CAClC,MAAM,UAAU,uBAAuB,4BAA4B,IAAI,GAAG,IAAI;CAC9E,IAAI,KAAK,QAAQ,WAAW,QAAQ,WAAW,GAC7C,OAAO,CAAC;CACV,MAAM,OAAO,KAAK,QAAQ,YAAY,EAAE,UAAU,OAAO,CAAC;CAC1D,OAAO,QAAQ,IAAI,QAAQ,KAAK,UAAU;EACxC,IAAI,EAAE,WAAW,QACf,OAAO,QAAQ,QAAQ,KAAK;EAC9B,OAAO,sBAAsB,OAAO,KAAK,mBAAmB,KAAQ,IAAI;CAC1E,CAAC,CAAC;AACJ;AAEA,SAAS,uBACP,SACA,MAC6C;CAI7C,MAAM,yBAAyB,KAAK,0BAC9B,QAAQ,IAAI,oCAAoC;CACtD,MAAM,2BAAW,IAAI,IAAkD;CACvE,MAAM,yBAAS,IAAI,IAAY;CAE/B,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,OAAO,IAAI,MAAM,IAAI,GACvB;EACF,IAAI,MAAM,WAAW,aAAa,CAAC,wBAAwB;GACzD,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,GAC1B,SAAS,IAAI,MAAM,MAAM;IACvB,MAAM,MAAM;IACZ,KAAK,MAAM;IACX,QAAQ,MAAM;IACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;IACrD,OAAO;GACT,CAAC;GAEH;EACF;EACA,SAAS,IAAI,MAAM,MAAM,KAAK;EAC9B,OAAO,IAAI,MAAM,IAAI;CACvB;CAEA,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC;AAC9B;AAEA,MAAM,kBAAkB,cAAc,YAAY,GAAG;;;;;;;;;AAUrD,IAAI,mBAAmB;;;;;;;;;AAUvB,SAAS,iBAAiB,KAAmB;CAC3C,MAAM,QAAQ,gBAAgB;CAC9B,IAAI,CAAC,OACH;CAGF,IAAI;CACJ,IAAI;EACF,WAAW,aAAa,GAAG;CAC7B,QACM;EACJ,WAAW;CACb;CACA,KAAK,MAAM,QAAQ,aAAa,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,QAAQ,GAAG;EAC7D,MAAM,SAAS,KAAK,SAAS,GAAG,IAAI,OAAO,OAAO;EAClD,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,GACjC,IAAI,IAAI,WAAW,MAAM,GACvB,OAAO,MAAM;CAEnB;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,qBAAqB,WAAmB,KAA+B;CAC3F,iBAAiB,GAAG;CACpB,IAAI,OAAO,QAAQ,aAAa;EAC9B,oBAAoB;EACpB,OAAO,OAAO,GAAG,cAAc,SAAS,CAAC,CAAC,KAAK,cAAc;CAC/D;CAKA,MAAM,CAAC,EAAE,cAAc,kBAAkB,MAAM,QAAQ,IAAI,CACzD,OAAO,SACP,4BAA4B,CAC9B,CAAC;CAED,OADa,WAAW,WAAW;EAAE,aAAa;EAAM,SAAS;EAAO,gBAAgB;EAAO;CAAe,CACpG,CAAC,CAAC,OAAO,SAAS;AAC9B;;;;;;AAOA,SAAgB,wBAAwB,KAAmB;CACzD,iBAAiB,GAAG;AACtB;;;;;;AAOA,eAAe,sBAAsB,OAA2B,WAAmB,MAAyC;CAG1H,MAAM,UAAU,mBAAmB,MAAM,GAAG;CAC5C,IAAI,SAAS,UAAU,SACrB,OAAO;EACL,MAAM,MAAM;EACZ,KAAK,MAAM;EACX,QAAQ,MAAM;EACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;EACrD,OAAO,QAAQ;CACjB;CAEF,IAAI,SACF,aAAa,MAAM,EAAE,WAAW,MAAM,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,OAAO;CACpE,IAAI;EAMF,MAAM,aAAa,wBAAwB,MALzB,YAChB,qBAAqB,MAAM,OAAO,MAAM,GAAG,GAC3C,WACA,uBAAuB,MAAM,MAAM,SAAS,UAAU,oFACxD,CAC8C;EAC9C,IAAI,CAAC,YACH,OAAO;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;GACrD,OAAO,aAAa,MAAM,MAAM;EAClC;EAEF,IAAI,MAAM,UAAU,eAAe,KAAA,KAAa,MAAM,SAAS,eAAe,GAC5E,OAAO;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,UAAU,MAAM;GAChB,OAAO,sCAAsC,MAAM,SAAS,WAAW;EACzE;EAEF,IAAI,WAAW,eAAe,KAAA,KAAa,WAAW,eAAe,GACnE,OAAO;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;GACrD,OAAO,sCAAsC,WAAW,WAAW;EACrE;EAEF,IAAI,WAAW,SAAS,MAAM,MAC5B,OAAO;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;GACrD,OAAO,uCAAuC,MAAM,KAAK,6BAA6B,WAAW,KAAK;EACxG;EAEF,IAAI,MAAM,UAAU,QAAQ,MAAM,SAAS,SAAS,MAAM,MACxD,OAAO;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,UAAU,MAAM;GAChB,OAAO,gDAAgD,MAAM,KAAK,2BAA2B,MAAM,SAAS,KAAK;EACnH;EAEF,OAAO;GACL;GACA,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;EACvD;CACF,SACO,KAAK;EACV,OAAO;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,QAAQ,MAAM;GACd,GAAI,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;GACrD,OAAO,oBAAoB,MAAM,MAAM,IAAI,aAAa,GAAG;EAC7D;CACF;AACF;;;;;;;;AASA,SAAS,wBAAwB,KAA0C;CACzE,IAAI,CAAC,OAAO,OAAO,QAAQ,UACzB,OAAO;CACT,MAAM,IAAI;CACV,IAAI,sBAAsB,EAAE,OAAO,GACjC,OAAO,EAAE;CACX,IAAI,sBAAsB,EAAE,SAAS,GACnC,OAAO,EAAE;CACX,OAAO;AACT;;;;;;;;;AAUA,SAAgB,sBAAsB,OAA8C;CAClF,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,OAAO;CACT,MAAM,IAAI;CACV,IAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,WAAW,GAClD,OAAO;CACT,IAAIA,wBAAsB,IAAI,EAAE,IAAI,GAClC,OAAO;CACT,IAAI,OAAO,EAAE,UAAU,YACrB,OAAO;CACT,OAAO;AACT;;;;;;;;;AAUA,SAAgB,sBAAsB,YAAkD;CACtF,OAAO;EACL;EACA,MAAM,WAAW;EACjB,QAAQ;CACV;AACF;;;;;;;AAQA,SAAgB,sBAAsB,KAA+C;CACnF,MAAM,oBAAoBC,WAAS,QAAQ,KAAK,gBAAgB,CAAC;CACjE,IAAI,oBAAoB,iBAAiB,GACvC,OAAO,EAAE,UAAU,kBAAkB;CACvC,MAAM,MAAMA,WAAS,QAAQ,KAAK,cAAc,CAAC;CACjD,IAAI,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,GAAG;EACzD,MAAM,WAAY,IAA6B;EAC/C,IAAI,oBAAoB,QAAQ,GAC9B,OAAO,EAAE,SAAS;CACtB;CACA,OAAO,CAAC;AACV;AAEA,SAASA,WAAS,MAAuB;CACvC,IAAI;EACF,OAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;CAC9C,QACM;EACJ,OAAO;CACT;AACF;AAEA,SAAS,oBAAoB,OAA4C;CACvE,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAC5D,OAAO;CACT,MAAM,IAAI;CAKV,IAAI,EAAE,eAAe,KAAA,KAAa,OAAO,EAAE,eAAe,UACxD,OAAO;CACT,MAAM,iBAAiB,MAAwB,MAAM,QAAQ,CAAC,KAAK,EAAE,OAAM,OAAM,OAAO,OAAO,QAAQ;CACvG,QAAQ,EAAE,SAAS,KAAA,KAAa,OAAO,EAAE,SAAS,cAC5C,EAAE,gBAAgB,KAAA,KAAa,OAAO,EAAE,gBAAgB,cACxD,EAAE,gBAAgB,KAAA,KAAa,OAAO,EAAE,gBAAgB,cAGxD,EAAE,gBAAgB,KAAA,KAAa,cAAc,EAAE,WAAW,OAC1D,EAAE,iBAAiB,KAAA,KAAa,cAAc,EAAE,YAAY;AACpE;;;;;;;AC9cA,MAAa,kBAA4C;CACvD;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAME,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAIE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAGA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAKE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAQE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAME,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EAIR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAGE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAOE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAeA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAEA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAEA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SA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"}
1
+ {"version":3,"file":"extensions-BJof-2mi.js","names":["DANGEROUS_RECORD_KEYS","readJson"],"sources":["../src/extensions/bus.ts","../src/extensions/config.ts","../src/extensions/define.ts","../src/extensions/host-modules.ts","../src/extensions/host-version.ts","../src/extensions/with-timeout.ts","../src/extensions/discovery.ts","../src/chat/keybindings.ts","../src/extensions/provider-oauth.ts","../src/extensions/host-runtime.ts","../src/extensions/hud.ts","../src/extensions/mcp-host.ts","../src/extensions/register-context-shared.ts","../src/extensions/register-context-agent.ts","../src/extensions/register-context-transforms.ts","../src/extensions/register-context-ui.ts","../src/extensions/register-context.ts","../src/extensions/register.ts","../src/extensions/discover-and-build.ts","../src/extensions/install.ts","../src/extensions/prompt-bridge.ts"],"sourcesContent":["/**\n * Extension bus — the permission-gated cross-window message channel behind\n * `ctx.bus` (`'ipc'` permission). One bus per host process; per-extension\n * PRIVATE channels: a subscriber only ever receives its own extension's\n * messages, and the extension id on every envelope is stamped by the host\n * from the registration identity — never caller-supplied.\n *\n * Delivery model (mirrors the HUD holder pattern):\n * - `emit` delivers to the extension's LOCAL subscribers synchronously\n * (loopback — fully functional with zero IPC) and then fires `onEmit`\n * so the embedding host can `env.broadcast` the envelope to peers.\n * - `deliver` re-injects a remote envelope (daemon fan-out, TUI bridge)\n * into the local subscribers with `origin: 'remote'`. Unknown\n * extension ids are dropped; a per-extension last-seen `(nonce, seq)`\n * window dedupes self-echoes and multi-connection duplicates — same\n * echo-safety family as the drafts `broadcastExcept` contract.\n * Envelopes without a nonce (older peers) fall back to a seq-only key.\n */\n\nimport type { JsonValue } from '../types'\nimport type { ExtensionBusEmitOptions, ExtensionBusMessage } from './types'\nimport { Buffer } from 'node:buffer'\n\n/** Serialized-payload cap, enforced at emit AND at the daemon trust boundary. */\nexport const EXTENSION_BUS_MAX_PAYLOAD_BYTES = 64 * 1024\n\n/** Last-seen seq window per extension — bounds dedupe memory. */\nconst SEEN_WINDOW = 128\n\n/** Wire/broadcast shape of one bus message (host-stamped identity). */\nexport interface ExtensionBusEnvelope {\n /** Id of the extension that emitted the message. */\n extensionId: string\n /** Session the emitter scoped the message to, when any. */\n sessionId?: string\n /** JSON value the extension sent. */\n payload: JsonValue\n /** Sequence number of the message from its emitter. */\n seq: number\n /**\n * Per-process emitter nonce (additive). Dedupe keys on\n * `(extensionId, nonce, seq)` so two processes emitting the same seq\n * can't shadow each other — the random seq base alone is only\n * probabilistically collision-free. Absent on envelopes from older\n * peers, which fall back to the legacy `(extensionId, seq)` key.\n */\n nonce?: string\n}\n\n/** Per-extension channel claimed by a registry build (generation-scoped). */\nexport interface ExtensionBusChannel {\n /** `false` when the message was dropped (over cap / unserializable / superseded claim). */\n emit: (payload: JsonValue, opts?: ExtensionBusEmitOptions) => boolean\n /** Subscribes to messages on this channel. Returns a function that removes the subscription. */\n onMessage: (fn: (msg: ExtensionBusMessage) => void) => () => void\n /** Registry dispose — drops this generation's subscribers. */\n close: () => void\n}\n\n/** Routes messages between the instances of one extension, in this process and across windows through the host transport. */\nexport interface ExtensionBus {\n /**\n * Claim the channel for one extension id. A later claim (registry\n * rebuild / reload) supersedes: the old generation's emits no-op and\n * its subscribers are dropped, so a disposed build can't keep talking.\n */\n claim: (extensionId: string) => ExtensionBusChannel\n /**\n * Re-inject a remote envelope. Returns `false` when dropped: unknown\n * (not locally claimed) extension id, or a `(nonce, seq)` key already\n * seen for that extension (self-echo / duplicate connection).\n */\n deliver: (envelope: ExtensionBusEnvelope) => boolean\n /** Outbound hook — the host forwards envelopes to its transport (broadcast / notify). */\n onEmit: (fn: (envelope: ExtensionBusEnvelope) => void) => () => void\n}\n\n/**\n * Byte length of the JSON serialization, or `null` when not serializable.\n *\n * @param payload - Value to measure.\n */\nexport function serializedPayloadBytes(payload: unknown): number | null {\n let text: string | undefined\n try {\n text = JSON.stringify(payload)\n }\n catch {\n return null\n }\n // `undefined` / functions serialize to undefined — not a JSON value.\n if (typeof text !== 'string')\n return null\n return Buffer.byteLength(text, 'utf8')\n}\n\ninterface ChannelState {\n generation: number\n subscribers: Set<(msg: ExtensionBusMessage) => void>\n /** FIFO of recently emitted/delivered `(nonce, seq)` keys (dedupe window). */\n seenOrder: string[]\n seenSet: Set<string>\n /** Monotonic seq counter, random-based so cross-process ids rarely collide. */\n nextSeq: number\n}\n\n/**\n * Dedupe key for one envelope. Nonce-stamped envelopes key on\n * `(nonce, seq)`; nonce-less ones (older peers) on seq alone — the nonced\n * key always contains a `:` and a stringified seq never does, so the two\n * namespaces can't collide.\n */\nfunction seenKey(nonce: string | undefined, seq: number): string {\n return nonce === undefined ? String(seq) : `${nonce}:${seq}`\n}\n\n/** Creates an empty bus. Extensions claim a channel on it when their registry is built. */\nexport function createExtensionBus(): ExtensionBus {\n const channels = new Map<string, ChannelState>()\n const emitListeners = new Set<(envelope: ExtensionBusEnvelope) => void>()\n // Per-process emitter identity — stamped on every outbound envelope so\n // receivers dedupe on (extensionId, nonce, seq) instead of trusting the\n // random seq base never to collide across processes.\n const nonce = crypto.randomUUID()\n\n function markSeen(state: ChannelState, key: string): void {\n state.seenSet.add(key)\n state.seenOrder.push(key)\n while (state.seenOrder.length > SEEN_WINDOW) {\n const evicted = state.seenOrder.shift()\n if (evicted !== undefined)\n state.seenSet.delete(evicted)\n }\n }\n\n function fanOut(state: ChannelState, msg: ExtensionBusMessage): void {\n for (const fn of [...state.subscribers]) {\n try {\n fn(msg)\n }\n catch {\n // A throwing subscriber must not break the channel or its siblings.\n }\n }\n }\n\n return {\n claim(extensionId) {\n let state = channels.get(extensionId)\n if (state) {\n // New generation supersedes: prior build's subscribers are dropped,\n // but the seen-window survives so a reload can't re-deliver echoes.\n state.generation += 1\n state.subscribers = new Set()\n }\n else {\n state = {\n generation: 0,\n subscribers: new Set(),\n seenOrder: [],\n seenSet: new Set(),\n // Random base: seqs from different processes must not collide\n // inside a receiver's dedupe window (see `deliver`).\n nextSeq: Math.floor(Math.random() * 0x7FFFFFFF),\n }\n channels.set(extensionId, state)\n }\n const generation = state.generation\n const live = (): boolean => channels.get(extensionId)?.generation === generation\n\n return {\n emit(payload, opts) {\n if (!live())\n return false\n const bytes = serializedPayloadBytes(payload)\n if (bytes === null || bytes > EXTENSION_BUS_MAX_PAYLOAD_BYTES)\n return false\n const current = channels.get(extensionId)!\n const seq = current.nextSeq++\n // Record BEFORE fan-out: the broadcast copy of this very emit can\n // loop back through another connection of this same process.\n markSeen(current, seenKey(nonce, seq))\n const sessionId = typeof opts?.sessionId === 'string' ? opts.sessionId : undefined\n fanOut(current, { payload, ...(sessionId !== undefined ? { sessionId } : {}), origin: 'local' })\n const envelope: ExtensionBusEnvelope = { extensionId, ...(sessionId !== undefined ? { sessionId } : {}), payload, seq, nonce }\n for (const fn of [...emitListeners]) {\n try {\n fn(envelope)\n }\n catch {\n // Transport hook failures must never reach extension code.\n }\n }\n return true\n },\n onMessage(fn) {\n if (!live() || typeof fn !== 'function')\n return () => {}\n const current = channels.get(extensionId)!\n current.subscribers.add(fn)\n return () => {\n channels.get(extensionId)?.subscribers.delete(fn)\n }\n },\n close() {\n if (live())\n channels.get(extensionId)!.subscribers.clear()\n },\n }\n },\n\n deliver(envelope) {\n const state = channels.get(envelope.extensionId)\n // Drop envelopes for extensions not locally registered — the channel\n // is private per extension id, and an unclaimed id has no audience.\n if (!state)\n return false\n const key = seenKey(envelope.nonce, envelope.seq)\n if (state.seenSet.has(key)) {\n // Expected for self-echoes / duplicate connections, but traced so\n // an investigation into legit message loss has a lead.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/extensions] bus dedupe drop: ${envelope.extensionId} key=${key}\\n`)\n return false\n }\n markSeen(state, key)\n fanOut(state, {\n payload: envelope.payload,\n ...(envelope.sessionId !== undefined ? { sessionId: envelope.sessionId } : {}),\n origin: 'remote',\n })\n return true\n },\n\n onEmit(fn) {\n emitListeners.add(fn)\n return () => {\n emitListeners.delete(fn)\n }\n },\n }\n}\n","/**\n * Typed, extension-contributed configuration.\n *\n * Extensions declare config CATEGORIES and typed config ITEMS during\n * `setup(ctx)` via `ctx.registerConfigCategory` / `ctx.registerConfigItem`.\n * Values are persisted in the user's `Settings.extensionConfig` bag\n * (`extName → itemKey → value`) and read back — default-first, with\n * graceful degradation on missing / malformed values — through a single\n * resolver ({@link resolveExtensionConfigValue}). The same resolver\n * powers `ctx.config.get(key)` inside the extension (hooks / tools) and\n * both Settings UIs (TUI + GUI).\n *\n * Design intent:\n * - **One chokepoint for graceful degradation.** Every read — host,\n * extension, TUI render, GUI render — funnels through\n * {@link resolveExtensionConfigValue}; a value that's missing, the\n * wrong type, out of range, off the enum, or failing the author's\n * `validate` collapses to the item's `default`. The UI therefore\n * never has to special-case a broken `state.json` / DB row.\n * - **Namespacing by extension.** Items are keyed `extName → itemKey`\n * so two extensions can both ship a `verbose` knob without colliding.\n * Categories are a flat global registry (an extension may add items\n * to another extension's category, or to its own implicit category\n * whose id is the extension name).\n * - **Wire-safe schema.** The optional main-side `validate` callback is\n * the only non-serializable field; {@link toWireConfigSchema} drops it\n * so the schema crosses an IPC boundary (the GUI) cleanly.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { ExtensionRegistry } from './types'\n\n// ---------------------------------------------------------------------------\n// Schema types\n// ---------------------------------------------------------------------------\n\n/** Concrete value a config item can hold. */\nexport type ExtensionConfigValue = boolean | number | string\n\n/** Where a config value should be persisted. */\nexport type ExtensionConfigScope = 'user' | 'project' | 'session'\n/** How a host applies a changed config value: read live, rebuild the agent, or restart. */\nexport type ExtensionConfigChangeBehavior = 'live' | 'agent-rebuild' | 'restart'\n\n/** Fields shared by every config-item variant. */\nexport interface ExtensionConfigItemBase {\n /**\n * Stable key, unique within the declaring extension. Used as the\n * persistence key (`extensionConfig[extName][key]`) and the\n * `ctx.config.get(key)` lookup — pick a stable identifier.\n */\n key: string\n /** Short display label rendered in the Settings UIs. */\n label: string\n /** Longer one-line gloss shown under the label. */\n description?: string\n /**\n * Optional main-side validator, run at READ time AFTER the built-in\n * type / range / enum / pattern checks (both must pass). Throwing is\n * treated as \"invalid\" — the read degrades to the item's `default`. It\n * is **never invoked at registration** (declaring an item has no side\n * effects) and is **never serialized over IPC** ({@link toWireConfigSchema}\n * strips it), so it only runs in-process (TUI host + extension reads);\n * the GUI renderer is optimistic and relies on the authoritative core read.\n */\n validate?: (v: unknown) => boolean\n /**\n * Desired persistence scope. Current stores still persist through the\n * existing per-extension settings bag unless a host implements scoped writes;\n * this metadata lets UIs and future stores present the right ownership.\n */\n scope?: ExtensionConfigScope\n /** Whether changing this value requires rebuilding runtime objects. */\n requiresRestart?: boolean\n /** How hosts should apply a changed value. Defaults to live reads through `ctx.config.get`. */\n onChange?: ExtensionConfigChangeBehavior\n}\n\n/** A boolean config item — rendered as a toggle. */\nexport interface ExtensionConfigBooleanItem extends ExtensionConfigItemBase {\n /** Discriminant for a boolean item. */\n type: 'boolean'\n /** Value used until the user sets one. */\n default: boolean\n}\n\n/**\n * A numeric config item — rendered as a free input row, clamped to `[min, max]`\n * and snapped to `step`. The `default` must itself lie within `[min, max]` or\n * the item is rejected at registration.\n */\nexport interface ExtensionConfigNumberItem extends ExtensionConfigItemBase {\n /** Discriminant for a number item. */\n type: 'number'\n /** Value used until the user sets one. Must lie within `[min, max]`. */\n default: number\n /** Smallest accepted value. */\n min?: number\n /** Largest accepted value. */\n max?: number\n /** Increment values snap to. */\n step?: number\n}\n\n/** A string config item — rendered as a free input row (optionally `multiline`), validated against `pattern`. */\nexport interface ExtensionConfigStringItem extends ExtensionConfigItemBase {\n /** Discriminant for a string item. */\n type: 'string'\n /** Value used until the user sets one. */\n default: string\n /**\n * Anchored or unanchored JS-regex source gating user-entered values at read\n * time. An invalid pattern is ignored (treated as \"no constraint\"). The\n * pattern is intentionally NOT applied to the `default`, so an empty / unset\n * default (`default: ''`) for an optional patterned field is valid.\n */\n pattern?: string\n /** Renders a multi-line editor instead of a single-line input. */\n multiline?: boolean\n /**\n * Treat the value as a credential: Settings UIs render it masked\n * (`••••••••`, last 4 characters kept for recognition when the value is\n * longer than 8) and seed the editor\n * EMPTY rather than with the stored value, so the secret never appears on\n * screen or in a screen share.\n *\n * Presentation only — the value still lives in the plain settings bag, so\n * this is for \"don't shoulder-surf my service token\", not for secrets that\n * warrant the credential store. Ignored for `multiline` items.\n */\n secret?: boolean\n}\n\n/**\n * An enum config item — rendered as a cycling choice row. The `default` must be\n * one of the `options` values or the item is rejected at registration.\n */\nexport interface ExtensionConfigEnumItem extends ExtensionConfigItemBase {\n /** Discriminant for an enum item. */\n type: 'enum'\n /** Value used until the user picks one. Must be one of `options`. */\n default: string\n /** Choices the user can pick from. */\n options: readonly { value: string, label: string }[]\n}\n\n/**\n * A model-id config item — rendered as an inline searchable picker over the\n * host's credentialed model catalog (the same interaction grammar as the\n * ctrl+m cross-provider picker: type to filter, `↵` picks — or toggles, in\n * `multi` mode, where `esc` applies the selection).\n *\n * The persisted value is a plain model id, or a comma-separated id list\n * when `multi` — a string either way, so {@link ExtensionConfigValue} and\n * every settings store stay unchanged, and hosts without a catalog\n * (headless, no authed providers) degrade to the free string input.\n * Deliberately catalog-UNvalidated at read time: a stored id whose\n * provider is temporarily unauthed must survive, not silently reset to\n * the default — catalog validation is the consuming extension's call.\n */\nexport interface ExtensionConfigModelItem extends ExtensionConfigItemBase {\n /** Discriminant for a model item. */\n type: 'model'\n /** Value used until the user picks one: a model id, or a comma-separated list when `multi`. */\n default: string\n /** Allow picking MORE than one model (comma-separated persisted value). */\n multi?: boolean\n}\n\n/**\n * What a config ACTION reports back to the host after running. Actions\n * cannot write settings directly — there is deliberately no `config.set`\n * surface for extensions — so a successful run RETURNS the values it wants\n * persisted and the host writes them through its own validated settings\n * path ({@link sanitizeExtensionConfigActionPatch} gates the patch).\n */\nexport interface ExtensionConfigActionResult {\n /** One-line feedback shown in the Settings UI (e.g. \"proxy running at …\"). */\n message?: string\n /**\n * Config values of the DECLARING extension to persist on success, keyed\n * by item key. Entries naming unknown keys, action items, or values that\n * fail the item's schema are dropped (not errors) — a buggy action can\n * degrade but never corrupt the settings bag.\n */\n config?: Record<string, ExtensionConfigValue>\n}\n\n/**\n * An imperative config item — rendered as a \"press ↵ to run\" row instead\n * of a value editor. `run` executes in the extension's own process (it is\n * never serialized — {@link toWireConfigSchema} omits action items\n * entirely), so it can close over the extension's `setup(ctx)` scope.\n *\n * Contract for `run`:\n * - Resolve with an {@link ExtensionConfigActionResult} (or `void`) on\n * success — `message` becomes the row's feedback line, `config`\n * values are persisted by the host.\n * - Throw / reject on failure — the error message is shown on the row.\n * - Bound your own runtime: the host serializes actions (one in flight\n * per modal) but imposes no timeout, so a hanging `run` holds the\n * \"running…\" state until it settles.\n *\n * Actions hold no persisted value: `ctx.config.get(key)` returns\n * `undefined` for an action key and the resolver never yields a value.\n */\nexport interface ExtensionConfigActionItem extends Pick<ExtensionConfigItemBase, 'key' | 'label' | 'description'> {\n /** Discriminant for an action item. */\n type: 'action'\n /** Runs the action. May return values to persist. Throwing shows the error on the row. */\n run: () => ExtensionConfigActionResult | void | Promise<ExtensionConfigActionResult | void>\n /**\n * Optional passive probe — called by the host when the settings panel\n * surfaces the row (and re-called after any action of the panel\n * settles), so the row can show live state (\"running at …\") WITHOUT\n * the user pressing ↵. Contract:\n *\n * - Resolve an {@link ExtensionConfigActionStatus} to show it, or\n * `undefined` for \"no status\" (e.g. the probed tool isn't\n * installed — stay quiet rather than nag).\n * - Throwing is treated as `undefined` — probes must be safe to call\n * at any time and MUST NOT mutate anything.\n * - Be fast: the TUI caps each probe at 3 seconds and discards\n * late results.\n */\n status?: () => ExtensionConfigActionStatus | undefined | Promise<ExtensionConfigActionStatus | undefined>\n}\n\n/**\n * Result of an action's passive `status` probe. `state` drives the row\n * color (`ok` accent / `off` mute / `error` error); `message` is the\n * one-line text rendered in the row's value slot.\n */\nexport interface ExtensionConfigActionStatus {\n /** Row state: `ok`, `off` or `error`. */\n state: 'ok' | 'off' | 'error'\n /** One-line text shown in the row's value slot. */\n message: string\n}\n\n/**\n * The value-holding config items — everything except `action`. Functions\n * that produce or validate persisted VALUES take this narrower union;\n * {@link ExtensionConfigItem} is the full declarable set.\n */\nexport type ExtensionConfigValueItem\n = | ExtensionConfigBooleanItem\n | ExtensionConfigNumberItem\n | ExtensionConfigStringItem\n | ExtensionConfigEnumItem\n | ExtensionConfigModelItem\n\n/**\n * Discriminated union of the typed config items an extension can declare.\n * The `type` tag drives both validation ({@link resolveExtensionConfigValue})\n * and rendering (toggle / choice / input / action row).\n */\nexport type ExtensionConfigItem = ExtensionConfigValueItem | ExtensionConfigActionItem\n\n/**\n * A config category — a labelled grouping the Settings UIs render as a\n * sub-header. Declared via `ctx.registerConfigCategory`. An item that\n * names no category lands in its extension's implicit category (id =\n * extension name); register a category with that id to give the implicit\n * grouping a friendlier label.\n */\nexport interface ExtensionConfigCategory {\n /** Category id. */\n id: string\n /** Sub-header text. */\n label: string\n /** One-line gloss shown under the label. */\n description?: string\n}\n\n/**\n * A config item as STORED on the registry / contributions — the authored\n * item plus the resolved `category` id it belongs to. The owning\n * extension is implied by the namespacing key on\n * {@link ExtensionRegistry.configItems}.\n */\nexport type StoredExtensionConfigItem = ExtensionConfigItem & { category: string }\n\n/** The `ctx.config` surface handed to extensions for reading their own config. */\nexport interface ExtensionConfigSurface {\n /**\n * Read the current value of one of THIS extension's config items,\n * default-first. Returns the persisted value when present + valid, the\n * item's `default` when missing / malformed, or `undefined` when `key`\n * was never registered via `ctx.registerConfigItem`.\n *\n * Reads through a host-supplied live settings getter: during `setup`\n * (before the host wires live settings) it returns the default (or the\n * boot snapshot); inside hooks / tools (post-boot) it returns the live\n * value.\n */\n get: (key: string) => ExtensionConfigValue | undefined\n}\n\n// ---------------------------------------------------------------------------\n// Authored-schema validation (registration-time guard)\n// ---------------------------------------------------------------------------\n\n/**\n * Exhaustiveness guard for the `ExtensionConfigItem` union: a future item\n * `type` left unhandled is a COMPILE error (the `never` parameter), while at\n * runtime an unrecognized / missing type is treated as invalid (`false`) —\n * malformed items reach here via `as never` casts from untyped `.js` extensions.\n */\nfunction unreachableInvalidItem(_item: never): false {\n return false\n}\n\n/**\n * Whether an authored item is well-formed for its declared `type` — used\n * by `ctx.registerConfigItem` to drop malformed declarations (wrong /\n * missing `default`, an `enum` without `options`) before they reach the\n * registry. Cheaper than letting a broken item ride to render time.\n *\n * @param item - Item declared by an extension.\n */\nexport function isValidExtensionConfigItem(item: ExtensionConfigItem): boolean {\n switch (item.type) {\n case 'boolean':\n return typeof item.default === 'boolean'\n case 'number':\n // The default must be finite AND within the declared range, so the\n // resolver never surfaces an out-of-range value (the module's \"every read\n // yields a valid value\" invariant). `step` is a UI affordance, not a hard\n // constraint, so it is NOT enforced on the default.\n return typeof item.default === 'number'\n && Number.isFinite(item.default)\n && (item.min === undefined || item.default >= item.min)\n && (item.max === undefined || item.default <= item.max)\n case 'string':\n // A string `pattern` is intentionally NOT enforced against the default:\n // an empty / unset default (`default: ''`) for an optional patterned\n // field is a legitimate idiom. The pattern still gates user-entered\n // values at read time via `resolveExtensionConfigValue`. The author\n // `validate` is likewise deferred to read time, so registration stays\n // side-effect-free (no `validate(default)` call at register time).\n return typeof item.default === 'string'\n case 'enum':\n // The default MUST be one of the options — otherwise the picker can't\n // represent it and the resolver would surface an off-list value.\n return typeof item.default === 'string'\n && Array.isArray(item.options)\n && item.options.length > 0\n && item.options.every(o => o && typeof o.value === 'string' && typeof o.label === 'string')\n && item.options.some(o => o.value === item.default)\n case 'model':\n // Ids are free-form strings (catalogs are host- and auth-dependent),\n // so only the shape is checked — see the type's docstring for why\n // values are never catalog-validated.\n return typeof item.default === 'string'\n case 'action':\n // No `default` — requires a callable `run`; `status` is optional\n // but must be callable when present.\n return typeof item.run === 'function'\n && (item.status === undefined || typeof item.status === 'function')\n default:\n return unreachableInvalidItem(item)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation + resolution — the single graceful-degradation chokepoint\n// ---------------------------------------------------------------------------\n\n/** Build a RegExp from `pattern`, returning `null` when the source is invalid. */\nfunction safeRegExp(pattern: string): RegExp | null {\n try {\n return new RegExp(pattern)\n }\n catch {\n return null\n }\n}\n\n/**\n * Whether `value` is a valid concrete value for `item` — type match plus\n * the item's constraints (number range, enum membership, string pattern)\n * and the optional author `validate`. `step` is intentionally NOT enforced\n * here (it's a UI affordance, not a hard constraint — a persisted value or\n * default that doesn't align to the step is still honored).\n *\n * @param item - Item that defines the type and constraints.\n * @param value - Candidate value.\n */\nexport function isValidExtensionConfigValue(item: ExtensionConfigItem, value: unknown): value is ExtensionConfigValue {\n switch (item.type) {\n case 'boolean':\n if (typeof value !== 'boolean')\n return false\n break\n case 'number':\n if (typeof value !== 'number' || !Number.isFinite(value))\n return false\n if (item.min !== undefined && value < item.min)\n return false\n if (item.max !== undefined && value > item.max)\n return false\n break\n case 'string': {\n if (typeof value !== 'string')\n return false\n if (item.pattern) {\n const re = safeRegExp(item.pattern)\n // A broken author pattern is treated as \"no constraint\" rather\n // than rejecting every value the user types.\n if (re && !re.test(value))\n return false\n }\n break\n }\n case 'enum':\n if (typeof value !== 'string' || !item.options.some(o => o.value === value))\n return false\n break\n case 'model':\n if (typeof value !== 'string')\n return false\n break\n case 'action':\n // Actions hold no persisted value — nothing is ever valid for them.\n return false\n default:\n return unreachableInvalidItem(item)\n }\n if (item.validate) {\n try {\n return item.validate(value)\n }\n catch {\n return false\n }\n }\n return true\n}\n\n/**\n * Resolve the effective value of a config item from its persisted value.\n * THE single place graceful degradation lives: a `persisted` that's\n * `undefined` (never set) OR fails {@link isValidExtensionConfigValue}\n * collapses to `item.default`. Action items hold no value and resolve to\n * `undefined` — the overloads keep value-item call sites exact.\n *\n * @param item - Item that defines the type and constraints.\n * @param persisted - Stored value, if any.\n */\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigValueItem,\n persisted: unknown,\n): ExtensionConfigValue\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigItem,\n persisted: unknown,\n): ExtensionConfigValue | undefined\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigItem,\n persisted: unknown,\n): ExtensionConfigValue | undefined {\n if (item.type === 'action')\n return undefined\n if (persisted === undefined)\n return item.default\n if (!isValidExtensionConfigValue(item, persisted))\n return item.default\n return persisted\n}\n\n/**\n * Clamp a number to the item's `[min, max]` and snap to `step` (when set).\n * Used by the input-row commit paths so a user-typed value is coerced into\n * range before it's persisted.\n *\n * @param item - Number item that supplies `min`, `max` and `step`.\n * @param n - Value to clamp.\n */\nexport function clampExtensionConfigNumber(item: ExtensionConfigNumberItem, n: number): number {\n let v = n\n if (item.min !== undefined)\n v = Math.max(item.min, v)\n if (item.max !== undefined)\n v = Math.min(item.max, v)\n if (item.step !== undefined && item.step > 0) {\n const base = item.min ?? 0\n v = base + Math.round((v - base) / item.step) * item.step\n // Snapping can nudge a boundary value out of range — re-clamp.\n if (item.max !== undefined)\n v = Math.min(item.max, v)\n if (item.min !== undefined)\n v = Math.max(item.min, v)\n }\n return v\n}\n\n/**\n * Coerce a raw input string (from a free number / string input row) into a\n * valid persisted value, or `undefined` when it can't be made valid.\n * Numbers are parsed + clamped; strings are taken verbatim and checked\n * against the pattern; enum / boolean items aren't edited via free input\n * (they use choice / toggle rows) so they return `undefined` here.\n *\n * @param item - Item the input belongs to.\n * @param raw - Text the user typed.\n */\nexport function coerceExtensionConfigInput(\n item: ExtensionConfigItem,\n raw: string,\n): ExtensionConfigValue | undefined {\n switch (item.type) {\n case 'number': {\n const n = Number(raw.trim())\n if (!Number.isFinite(n))\n return undefined\n const clamped = clampExtensionConfigNumber(item, n)\n return isValidExtensionConfigValue(item, clamped) ? clamped : undefined\n }\n case 'string':\n return isValidExtensionConfigValue(item, raw) ? raw : undefined\n case 'model':\n // Fallback path for hosts without a catalog — the value is typed as\n // free text (a comma-separated id list when `multi`).\n return isValidExtensionConfigValue(item, raw) ? raw : undefined\n default:\n // Enum / boolean use choice / toggle rows; actions have no value.\n return undefined\n }\n}\n\n/**\n * Convenience read against a merged {@link ExtensionRegistry} + the user's\n * `Settings`. Looks up the item schema (`extName → key`), reads the\n * persisted value out of `settings.extensionConfig`, and runs it through\n * {@link resolveExtensionConfigValue}. Returns `undefined` only when the\n * `(extName, key)` pair names no registered item.\n *\n * @param registry - Merged registry holding the declared items.\n * @param settings - User settings holding the persisted values.\n * @param extName - Name of the declaring extension.\n * @param key - Item key.\n */\nexport function resolveExtensionConfig(\n registry: Pick<ExtensionRegistry, 'configItems'>,\n settings: Pick<Settings, 'extensionConfig'> | undefined,\n extName: string,\n key: string,\n): ExtensionConfigValue | undefined {\n const item = registry.configItems[extName]?.[key]\n if (!item)\n return undefined\n return resolveExtensionConfigValue(item, settings?.extensionConfig?.[extName]?.[key])\n}\n\n/**\n * Gate the `config` patch an action returned before the host persists it.\n * Per-entry, an item is accepted only when it names a registered VALUE\n * item of the declaring extension and passes that item's schema\n * ({@link isValidExtensionConfigValue}); everything else lands in\n * `rejected` for logging. Pure — the host applies `accepted` through its\n * own settings write path.\n *\n * @param items - Declared items of the extension, keyed by item key.\n * @param patch - Values the action returned.\n */\nexport function sanitizeExtensionConfigActionPatch(\n items: Readonly<Record<string, StoredExtensionConfigItem>> | undefined,\n patch: Readonly<Record<string, ExtensionConfigValue>> | undefined,\n): { accepted: Record<string, ExtensionConfigValue>, rejected: string[] } {\n const accepted: Record<string, ExtensionConfigValue> = {}\n const rejected: string[] = []\n if (!patch || typeof patch !== 'object')\n return { accepted, rejected }\n for (const [key, value] of Object.entries(patch)) {\n const item = items?.[key]\n if (!item || item.type === 'action' || !isValidExtensionConfigValue(item, value)) {\n rejected.push(key)\n continue\n }\n accepted[key] = value\n }\n return { accepted, rejected }\n}\n\n// ---------------------------------------------------------------------------\n// Wire-safe serialization (GUI IPC)\n// ---------------------------------------------------------------------------\n\n/** Fields the wire shape adds on top of the authored item (minus `validate`). */\ninterface WireConfigExtras {\n category: string\n extension: string\n}\n\n/** A config item flattened for the wire — carries `category` + `extension`, drops `validate`. */\nexport type WireExtensionConfigItem\n = | (Omit<ExtensionConfigBooleanItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigNumberItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigStringItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigEnumItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigModelItem, 'validate'> & WireConfigExtras)\n\n/** The full extension-config schema in IPC-safe form. */\nexport interface WireExtensionConfigSchema {\n /** Declared categories. */\n categories: readonly ExtensionConfigCategory[]\n /** Declared items, each tagged with its extension and category. */\n items: readonly WireExtensionConfigItem[]\n}\n\n/**\n * Project the merged registry's config schema into a wire-safe shape:\n * categories as a flat array, items flattened to a single list carrying\n * their `extension` + `category`, with the non-serializable `validate`\n * callback stripped. Action items are omitted entirely — their `run`\n * callback only exists in the main process, so a remote renderer (the\n * GUI) has nothing it could do with the row. Pure — factored out so its\n * serialization is unit testable without an Electron / IPC harness.\n *\n * @param registry - Merged registry holding the declared categories and items.\n */\nexport function toWireConfigSchema(\n registry: Pick<ExtensionRegistry, 'configCategories' | 'configItems'>,\n): WireExtensionConfigSchema {\n const items: WireExtensionConfigItem[] = []\n for (const [extension, byKey] of Object.entries(registry.configItems)) {\n for (const stored of Object.values(byKey)) {\n if (stored.type === 'action')\n continue\n // Drop the main-only `validate` callback; keep everything else.\n const { validate: _validate, ...rest } = stored\n items.push({ ...rest, extension } as WireExtensionConfigItem)\n }\n }\n return {\n categories: Object.values(registry.configCategories),\n items,\n }\n}\n","/**\n * `defineExtension` — identity helper for authoring extensions.\n *\n * Mirrors {@link definePreset} / {@link defineSkill}: returns its\n * argument verbatim, but its presence in the source gives TypeScript\n * an inference anchor and the import surface a stable name.\n *\n * Authoring example (drop in `.zidane/extensions/hello/index.ts`):\n *\n * ```ts\n * import { defineExtension } from 'zidane'\n *\n * export default defineExtension({\n * name: 'hello',\n * version: '0.1.0',\n * setup(ctx) {\n * ctx.registerHook('tool:after', ({ name }) =>\n * ctx.logger.info('tool ran', { name }))\n * },\n * })\n * ```\n *\n * No validation runs here — that lives in the loader so a malformed\n * definition surfaces in the Settings panel rather than throwing\n * during a module's top-level evaluation.\n *\n * **Zero-import alternative.** The loader accepts a plain object too,\n * so extensions running against the compiled `zidane-tui` binary\n * (which doesn't expose `node_modules/zidane` to dynamic imports\n * outside the bundle) can skip the import entirely:\n *\n * ```ts\n * // .zidane/extensions/hello/index.ts — no imports needed\n * export default {\n * name: 'hello',\n * setup(ctx) {\n * ctx.registerHook('tool:after', e => ctx.logger.info('ran', e))\n * },\n * }\n * ```\n *\n * You lose TypeScript inference on `ctx` this way; pair with\n * `/// <reference types=\"zidane\" />` or an `import type { ExtensionContext } from 'zidane'`\n * (type-only imports don't need a runtime resolution) to recover it.\n */\n\nimport type { ExtensionDefinition } from './types'\n\n/**\n * Returns `definition` unchanged. It gives TypeScript an inference anchor so `ctx` in `setup` is typed.\n * Validation happens in the loader, not here.\n *\n * @param definition - The extension: its `name` and a `setup(ctx)` function, plus optional manifest fields.\n * @returns The same `definition`.\n * @example\n * ```ts\n * import { defineExtension } from 'zidane'\n *\n * export default defineExtension({\n * name: 'hello',\n * setup(ctx) {\n * ctx.registerHook('tool:after', ({ name }) => ctx.logger.info('tool ran', { name }))\n * },\n * })\n * ```\n */\nexport function defineExtension(definition: ExtensionDefinition): ExtensionDefinition {\n return definition\n}\n","/**\n * Host-module sharing for extension imports — what makes \"an extension\n * loaded into the app uses the app's modules\" ACTUALLY true.\n *\n * By default it is not: a dynamically imported extension file resolves\n * its bare imports (`react`, `@opentui/react`, …) by walking\n * node_modules from the EXTENSION file's directory — not from the host.\n * Depending on where the extension lives, `import 'react'` finds\n * nothing, or worse, a SECOND react copy — elements still render (host\n * tags are strings and the element symbol is a global `Symbol.for`),\n * but hooks hit a null dispatcher and throw \"Invalid hook call\", which\n * surfaces as a failed renderer. This bit the dev loop itself: the dev\n * example resolved `tui/node_modules/react` while the reconciler ran\n * the root copy.\n *\n * The fix is interception, not resolution: {@link registerExtensionHostModules}\n * registers a Bun virtual-module plugin that maps the given specifiers\n * to the host's LIVE module objects (`loader: 'object'` — the actual\n * instances, no paths involved, so it works identically for installed\n * extensions and inside compiled binaries). Extensions then write plain\n * `import { useState } from 'react'` and get the host's react, period.\n *\n * Layering: this module is renderer-neutral (no react import here) —\n * the HOST hands its modules in. The TUI registers react + OpenTUI at\n * startup, before the boot registry's discovery imports run; headless\n * hosts simply don't register UI modules (a UI-importing extension then\n * fails to load there). First registration per\n * specifier wins; later calls for the same specifier are no-ops.\n *\n * Node hosts (the Electron GUI) have no `Bun.plugin`: the same map is\n * kept here and handed to the Node extension loader as jiti\n * `virtualModules` (see {@link resolveExtensionHostModules}).\n */\n\n/** Bun's plugin surface, narrowed from `globalThis` (same pattern as `contexts/glob.ts`) — this module must typecheck and no-op cleanly on non-Bun runtimes. */\ninterface BunPluginGlobal {\n plugin: (plugin: { name: string, setup: (build: { module: (specifier: string, cb: () => { exports: Record<string, unknown>, loader: 'object' } | Promise<{ exports: Record<string, unknown>, loader: 'object' }>) => void }) => void }) => void\n}\n\n/**\n * A host module, eager or lazy. Lazy (`() => Promise<module>`) exists for\n * self-referencing hosts: `zidane/tui` maps to ITSELF, which can only be\n * expressed as a deferred dynamic import (the module is still evaluating\n * when it registers).\n */\nexport type HostModuleSource = object | (() => Promise<object>)\n\n/** Specifier → source, first registration wins. Bounded by the distinct specifiers hosts register. */\nconst sources = new Map<string, HostModuleSource>()\n\n/** Resolved exports for every registered specifier; reset when a registration adds one. */\nlet resolved: Promise<Record<string, Record<string, unknown>>> | undefined\n\nfunction toExports(mod: object): Record<string, unknown> {\n // Spread the namespace into a plain record; module namespaces don't\n // expose `default` enumerably in every case, so carry it explicitly\n // (falling back to the namespace itself for consumers doing\n // `import React from 'react'` against a CJS interop shape).\n return { ...(mod as Record<string, unknown>), default: (mod as { default?: unknown }).default ?? mod }\n}\n\n/**\n * Map bare import specifiers to the host's live module objects for every\n * subsequently-imported module (extensions included). No-op outside Bun\n * and for already-registered specifiers. Call BEFORE the first extension\n * import (the TUI does, at startup).\n *\n * @param modules - Bare import specifiers mapped to a module object, or to a function that resolves one.\n * @example\n * ```ts\n * import * as React from 'react'\n * import { registerExtensionHostModules } from 'zidane/extensions'\n *\n * registerExtensionHostModules({ react: React })\n * ```\n */\nexport function registerExtensionHostModules(modules: Record<string, HostModuleSource>): void {\n const fresh = Object.entries(modules).filter(([specifier]) => !sources.has(specifier))\n if (fresh.length === 0)\n return\n for (const [specifier, source] of fresh)\n sources.set(specifier, source)\n resolved = undefined\n const bun = (globalThis as { Bun?: Partial<BunPluginGlobal> }).Bun\n if (typeof bun?.plugin !== 'function')\n return\n bun.plugin({\n name: `zidane-host-modules(${fresh.map(([s]) => s).join(',')})`,\n setup(build) {\n for (const [specifier, source] of fresh) {\n build.module(specifier, async () => {\n const mod = typeof source === 'function' ? await source() : source\n return { exports: toExports(mod), loader: 'object' as const }\n })\n }\n },\n })\n}\n\n/**\n * Every registered host module, resolved to its exports record (lazy\n * sources awaited once and memoized). The Node extension loader passes\n * this as jiti `virtualModules`; Bun hosts never need it. A rejected\n * lazy source clears the memo so the next load retries.\n */\nexport function resolveExtensionHostModules(): Promise<Record<string, Record<string, unknown>>> {\n const pending = resolved ?? (async () => {\n const entries = await Promise.all([...sources].map(async ([specifier, source]) =>\n [specifier, toExports(typeof source === 'function' ? await source() : source)] as const))\n return Object.fromEntries(entries)\n })()\n resolved = pending\n pending.catch(() => {\n if (resolved === pending)\n resolved = undefined\n })\n return pending\n}\n","/**\n * The zidane an extension needs, checked before it loads.\n *\n * Extensions declare it the npm way, `peerDependencies.zidane` in their\n * package.json. Discovery and install read it BEFORE importing the module, so\n * an extension written against a newer zidane fails with \"needs zidane\n * >=X\" on its Settings row instead of a TypeError from an export the host\n * doesn't have yet. A zidane newer than the range only warns: the extension\n * still loads.\n *\n * A source checkout (zidane running from its .ts sources) skips the check:\n * its package.json version lags the unreleased code it runs, so the\n * extensions developed alongside it would read as too new.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { ZIDANE_VERSION } from '../version'\n\n/** zidane running from its TypeScript sources, not a build or a compiled binary. */\nconst RUNNING_FROM_SOURCE = import.meta.url.endsWith('.ts')\n && !import.meta.url.includes('$bunfs')\n && !import.meta.url.includes('~BUN')\n\ntype Version = [number, number, number]\n\nfunction parseVersion(text: string): Version | null {\n const m = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:[-+].*)?$/.exec(text.trim())\n return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null\n}\n\nfunction compare(a: Version, b: Version): number {\n return a[0] - b[0] || a[1] - b[1] || a[2] - b[2]\n}\n\n/** `8`, `8.1`, `8.x`, `8.1.*`: the parts given, and how many. */\nfunction parsePartial(text: string): { parts: number[] } | null {\n const m = /^v?(\\d+)(?:\\.(\\d+|[x*]))?(?:\\.(\\d+|[x*]))?(?:[-+].*)?$/i.exec(text.trim())\n if (!m)\n return null\n const parts: number[] = []\n for (const part of [m[1], m[2], m[3]]) {\n if (part === undefined || /^[x*]$/i.test(part))\n break\n parts.push(Number(part))\n }\n return { parts }\n}\n\ninterface Comparator {\n /** `>=`, `>`, `<=`, `<`, `^`, `~`, `=` or `''` (a bare version or partial). */\n op: string\n /** The version given, missing parts as 0. */\n floor: Version\n /** How many parts were given (0 for `*` / `x`). */\n given: number\n}\n\n/** One comparator (`>=8.1.0`, `^8.1`, `~8.1.2`, `8.x`, `*`), or `undefined` when it isn't one. */\nfunction parseComparator(comparator: string): Comparator | undefined {\n if (comparator === '*' || /^x$/i.test(comparator))\n return { op: '', floor: [0, 0, 0], given: 0 }\n const m = /^([<>]=?|[=^~])?\\s*(\\S.*)$/.exec(comparator)\n if (!m)\n return undefined\n const [, op = '', rest = ''] = m\n const partial = parsePartial(rest)\n if (!partial)\n return undefined\n const [major = 0, minor = 0, patch = 0] = partial.parts\n return { op, floor: [major, minor, patch], given: partial.parts.length }\n}\n\n/** `version` is at or above the lowest version `comparator` allows (always true for `<`, `<=` and `*`). */\nfunction meetsFloor(version: Version, comparator: Comparator): boolean {\n if (comparator.op === '<' || comparator.op === '<=')\n return true\n if (comparator.op === '>')\n return satisfiesComparator(version, comparator)\n return compare(version, comparator.floor) >= 0\n}\n\nfunction satisfiesComparator(version: Version, { op, floor, given }: Comparator): boolean {\n const [major, minor, patch] = floor\n switch (op) {\n case '>=': return compare(version, floor) >= 0\n case '>': return given === 3 ? compare(version, floor) > 0 : compare(version, [given === 1 ? major + 1 : major, given === 1 ? 0 : minor + 1, 0]) >= 0\n case '<=': return given === 3 ? compare(version, floor) <= 0 : compare(version, [given === 1 ? major + 1 : major, given === 1 ? 0 : minor + 1, 0]) < 0\n case '<': return compare(version, floor) < 0\n case '^': {\n const ceiling: Version = major > 0 || given === 1 ? [major + 1, 0, 0] : minor > 0 || given === 2 ? [0, minor + 1, 0] : [0, 0, patch + 1]\n return compare(version, floor) >= 0 && compare(version, ceiling) < 0\n }\n case '~': {\n const ceiling: Version = given === 1 ? [major + 1, 0, 0] : [major, minor + 1, 0]\n return compare(version, floor) >= 0 && compare(version, ceiling) < 0\n }\n default: {\n // `=8.1.0` / `8.1.0` exactly; `8.1` / `8.x` as the range they name.\n if (given === 3)\n return compare(version, floor) === 0\n const ceiling: Version = given === 1 ? [major + 1, 0, 0] : given === 2 ? [major, minor + 1, 0] : [Infinity, 0, 0]\n return compare(version, floor) >= 0 && compare(version, ceiling) < 0\n }\n }\n}\n\n/**\n * Whether `version` satisfies an npm-style `range`: comparators joined by\n * spaces (all must hold) and `||` (any alternative), with caret, tilde,\n * x-ranges and hyphen-free partials. Pre-release tags are ignored, like the\n * daemon's version fence. `undefined` when either side doesn't parse.\n *\n * @param version - A version, e.g. `8.1.0`.\n * @param range - A range, e.g. `>=8.1.0`, `^8.1 || ^9`.\n */\nexport function satisfiesVersionRange(version: string, range: string): boolean | undefined {\n const v = parseVersion(version)\n const alternatives = parseRange(range)\n if (!v || alternatives.length === 0)\n return undefined\n return alternatives.some(comparators => comparators.every(c => satisfiesComparator(v, c)))\n}\n\n/**\n * Whether `version` is older than every alternative of `range` allows: below\n * a floor (`>=`, `>`, `^`, `~`, `=` or a bare version) in each one. A\n * version that fails only an upper bound (`^7`, `~8.0.0`, `<9`) is newer\n * than the range, not below it. `undefined` when either side doesn't parse.\n *\n * @param version - A version, e.g. `8.1.0`.\n * @param range - A range, e.g. `>=8.1.0`, `^8.1 || ^9`.\n */\nexport function belowVersionRange(version: string, range: string): boolean | undefined {\n const v = parseVersion(version)\n const alternatives = parseRange(range)\n if (!v || alternatives.length === 0)\n return undefined\n return alternatives.every(comparators => comparators.some(c => !meetsFloor(v, c)))\n}\n\n/** The `||` alternatives of `range` that parse, each as its comparators. */\nfunction parseRange(range: string): Comparator[][] {\n const alternatives: Comparator[][] = []\n for (const alternative of range.split('||')) {\n // `>= 8.1.0` → `>=8.1.0`: an operator binds to the version after it.\n const comparators = alternative.trim().replace(/([<>]=?|[=^~])\\s+/g, '$1').split(/\\s+/).filter(Boolean).map(parseComparator)\n if (comparators.length > 0 && comparators.every(c => c !== undefined))\n alternatives.push(comparators)\n }\n return alternatives\n}\n\n/**\n * The `peerDependencies.zidane` range in an extension's package.json.\n *\n * @param dir - The extension's directory.\n */\nexport function requiredZidaneRange(dir: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf8')) as { peerDependencies?: Record<string, unknown> }\n const range = pkg?.peerDependencies?.zidane\n return typeof range === 'string' && range.trim() ? range.trim() : undefined\n }\n catch {\n return undefined\n }\n}\n\n/** Options for {@link checkZidaneVersion}. Both are test seams. */\nexport interface ZidaneVersionCheckOptions {\n /** Version to check against. Defaults to this zidane's. */\n hostVersion?: string\n /** Treat the host as a source checkout and skip the check. Defaults to detecting it. */\n fromSource?: boolean\n}\n\n/** What {@link checkZidaneVersion} found: refuse the extension, or load it with a warning. */\nexport interface ZidaneVersionProblem {\n /** `error`: this zidane is older than the extension needs. `warning`: newer than its range, which may still work. */\n level: 'error' | 'warning'\n /** Says what the extension declares and what this is. */\n message: string\n}\n\n/**\n * Whether the extension in `dir` fits this zidane. `null` when it does: no\n * declared range, a range this version satisfies, a range that doesn't parse\n * (never block on garbage), or a source checkout. An `error` when this\n * zidane is older than the range's floor (the extension may use exports it\n * lacks); only a `warning` when it is newer than the range (`^7`, `~8.0.0`),\n * which usually still works.\n *\n * @param dir - The extension's directory.\n * @param options - Test seams.\n */\nexport function checkZidaneVersion(dir: string, options: ZidaneVersionCheckOptions = {}): ZidaneVersionProblem | null {\n if (options.fromSource ?? RUNNING_FROM_SOURCE)\n return null\n const range = requiredZidaneRange(dir)\n if (!range)\n return null\n const host = options.hostVersion ?? ZIDANE_VERSION\n if (satisfiesVersionRange(host, range) !== false)\n return null\n if (belowVersionRange(host, range))\n return { level: 'error', message: `Needs zidane ${range}. This is zidane ${host}. Update zidane, or install an extension release made for it.` }\n return { level: 'warning', message: `Made for zidane ${range}. This is zidane ${host}. Loaded anyway. If it misbehaves, update the extension.` }\n}\n","/**\n * Internal: race a promise against a timeout. Shared by the discovery\n * loader (module imports) and the registry builder (extension `setup()`).\n * The timer is always cleared so a settled promise doesn't pin the\n * event loop for the full timeout.\n */\nexport function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string,\n onTimeout?: () => void,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n onTimeout?.()\n reject(new Error(message))\n }, timeoutMs)\n })\n return Promise.race([promise, timeout]).finally(() => {\n if (timer)\n clearTimeout(timer)\n })\n}\n","/**\n * Extension discovery.\n *\n * Walks the standard project + user roots\n * (`{project,user}/.{agents,zidane}/extensions/<name>/`) using the\n * shared {@link projectUserPaths} convention, then dynamic-imports only\n * trusted `<name>/index.{ts,tsx,mjs,js}` files (probed in\n * that preference order — see {@link ENTRY_CANDIDATES}). The first valid\n * module per name wins — project beats user, `.agents/` beats\n * `.<prefix>/` within a single scope (see `projectUserPaths` for the\n * full order).\n *\n * Loading uses native `await import(pathToFileURL(...))` under Bun, which\n * handles `.ts`/`.tsx` and JS variants in `bun run` and in\n * `bun build --compile` binaries; Node hosts load through jiti (see\n * {@link importExtensionEntry}).\n *\n * Before each import the extension directory's module graph is EVICTED\n * from the module cache (`require.cache`: Bun's covers ESM too, jiti's\n * Node graph lives there), so\n * re-running discovery — the `/reload` command, a cwd-change rebuild, an\n * overwrite-install — loads the current on-disk code, deep imports\n * included. Module-level state resets per reload; the teardown contract\n * covers cleanup of the outgoing instance.\n *\n * Failures are isolation-first: a broken module produces a\n * {@link LoadedExtension} with `error` set instead of throwing — one\n * vibe-coded syntax error mustn't keep the TUI from booting.\n */\n\nimport type { LogSink } from '../logger'\nimport type { ExtensionDefinition, ExtensionManifest, ExtensionSource, LoadedExtension } from './types'\nimport { lstatSync, readdirSync, readFileSync, realpathSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { delimiter, dirname, resolve, sep } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { createDebugLog } from '../debug-log'\nimport { errorMessage } from '../errors'\nimport { consoleSink, createLogger } from '../logger'\nimport { resolveExtensionHostModules } from './host-modules'\nimport { checkZidaneVersion } from './host-version'\nimport { withTimeout } from './with-timeout'\n\n/**\n * Scoped `ZIDANE_DEBUG` logger. A `const` rather than a hoisted function\n * declaration, so it must precede its call sites.\n */\nconst debugLog = createDebugLog('extensions')\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Entry-point filenames probed inside each `<name>/` directory, in\n * preference order. Bun loads `.ts` / `.tsx` natively; `.mjs` / `.js`\n * are accepted so pre-compiled extensions (or vibe-coded JS-only ones)\n * work too. `index.js` is last because most authors will reach for\n * `.ts` first.\n */\nconst ENTRY_CANDIDATES = ['index.ts', 'index.tsx', 'index.mjs', 'index.js'] as const\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\n/** A discovered (but not yet loaded) extension entry. */\ninterface ExtensionScanEntry {\n /** Folder name — the canonical persisted extension identity. */\n name: string\n /** Absolute path to the extension directory. */\n dir: string\n /** Absolute path to the entry file picked from `ENTRY_CANDIDATES`. */\n entry: string\n /** Manifest read from the extension, when it has a valid one. */\n manifest?: ExtensionManifest\n /** Where the discovery root lives. */\n source: ExtensionSource\n}\n\ninterface ExtensionScanRoot {\n path: string\n source: 'project' | 'user'\n}\n\n// ---------------------------------------------------------------------------\n// Path resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Default scan roots for extension discovery. Mirrors\n * {@link defaultSkillScanPaths} / {@link defaultMcpsConfigPaths}; the\n * subPath is `'extensions'` so each root expands to\n * `<scope>/.<convention>/extensions/`.\n *\n * @param opts - Overrides for the working directory (`cwd`), home directory (`home`) and storage prefix (`prefix`).\n */\nexport function defaultExtensionScanPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n} = {}): ExtensionScanRoot[] {\n return projectUserPaths({ subPath: 'extensions', ...opts })\n}\n\n/**\n * Parse a platform-delimited list of additional extension scan roots.\n * Useful for local dogfooding / host-provided packages:\n *\n * ZIDANE_EXTENSION_DIRS=/repo/extensions bun run tui\n *\n * Each path is a scan ROOT whose children are extension dirs/packages\n * (`<root>/<name>/index.ts`), not a single extension directory.\n *\n * @param value - Delimited list of scan roots. Default: `ZIDANE_EXTENSION_DIRS`.\n */\nexport function parseExtensionScanPathEnv(value: string | undefined = process.env.ZIDANE_EXTENSION_DIRS): string[] {\n if (!value)\n return []\n return value.split(delimiter).map(p => p.trim()).filter(Boolean)\n}\n\n/**\n * Walk every scan root, collecting one {@link ExtensionScanEntry} per\n * `<root>/<name>/index.*` hit. First-found per `name` wins (matches the\n * project-user precedence used elsewhere — see `projectUserPaths` JSDoc).\n *\n * Non-existent roots are skipped silently. Stat errors inside a present\n * root are surfaced under `ZIDANE_DEBUG` only — they shouldn't bring\n * boot to a halt.\n *\n * @param opts - Overrides for `cwd`, `home` and `prefix`, plus extra scan roots in `extensionScanPaths`.\n */\nexport function scanExtensionDirectories(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n} = {}): ExtensionScanEntry[] {\n const seen = new Map<string, ExtensionScanEntry>()\n for (const entry of scanAllExtensionDirectories(opts)) {\n if (!seen.has(entry.name))\n seen.set(entry.name, entry)\n }\n return [...seen.values()]\n}\n\nfunction scanAllExtensionDirectories(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n} = {}): ExtensionScanEntry[] {\n const roots: ExtensionScanRoot[] = [\n ...(opts.extensionScanPaths ?? []).map(path => ({ path: resolve(path), source: 'user' as const })),\n ...defaultExtensionScanPaths(opts),\n ]\n const entriesOut: ExtensionScanEntry[] = []\n\n for (const root of roots) {\n try {\n // The scan root and its parent (`.zidane` / `.agents`) are part of\n // the trust boundary too — a symlinked parent would let every entry\n // resolve outside the configured roots while the per-entry lstat\n // checks below still pass.\n if (lstatSync(dirname(root.path)).isSymbolicLink())\n continue\n if (!lstatSync(root.path).isDirectory())\n continue\n }\n catch {\n continue\n }\n let realRoot: string\n try {\n realRoot = realpathSync(root.path)\n }\n catch (err) {\n debugLog(`realpath ${root.path}`, err)\n continue\n }\n let entries: string[]\n try {\n entries = readdirSync(root.path)\n }\n catch (err) {\n debugLog(`readdir ${root.path}`, err)\n continue\n }\n for (const name of entries) {\n if (name.startsWith('.'))\n continue\n const dir = resolve(root.path, name)\n try {\n // Extension roots and entry files are trust boundaries. Do not follow\n // symlinks out of the configured scan roots.\n if (!lstatSync(dir).isDirectory())\n continue\n }\n catch (err) {\n debugLog(`stat ${dir}`, err)\n continue\n }\n const entry = findEntryFile(dir)\n if (!entry)\n continue\n // Containment: the canonical entry file must stay under the\n // canonical scan root. Catches symlinks at any level the lstat\n // checks above didn't cover.\n try {\n const realEntry = realpathSync(entry)\n if (!realEntry.startsWith(realRoot + sep)) {\n debugLog(`skip ${entry}`, new Error(`resolves outside scan root ${realRoot}`))\n continue\n }\n }\n catch (err) {\n debugLog(`realpath ${entry}`, err)\n continue\n }\n entriesOut.push({ name, dir, entry, source: root.source, ...readExtensionManifest(dir) })\n }\n }\n return entriesOut\n}\n\n/**\n * Pick the first existing `index.*` file inside `dir`, or `null` if none.\n * Exported for the installer, which probes fetched trees with the same rule.\n */\nexport function findEntryFile(dir: string): string | null {\n for (const candidate of ENTRY_CANDIDATES) {\n const full = resolve(dir, candidate)\n try {\n if (lstatSync(full).isFile())\n return full\n }\n catch {\n continue\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Loading\n// ---------------------------------------------------------------------------\n\n/**\n * Discover + load every trusted extension reachable from the default\n * scan paths — including ones the user's enable allowlist has toggled\n * OFF. Disabled extensions still import (and later run `setup()`) so\n * their config schema is browsable in Settings before enabling; the\n * allowlist gates their CONTRIBUTIONS at registry level, not their code.\n * The trust boundary stays with `trustProjectExtensions`. Returns one {@link LoadedExtension} per scan entry:\n * skipped entries have no definition, failed entries carry `error`.\n * Loaders never throw — the host can decide what to do with load errors\n * (typically: ship them to the Settings panel).\n *\n * Imports run in parallel: a slow extension can't block the rest. The\n * returned array preserves the original scan order (project-first,\n * then user) so first-found-wins precedence stays intact.\n *\n * `signal` is honored opportunistically — if the abort fires BEFORE\n * we dispatch imports, the whole set is skipped. Once `Promise.all` is\n * in flight, individual imports can't be cancelled (the module system\n * has no abort affordance).\n *\n * Project-scoped extensions execute arbitrary local code, so they're\n * skipped unless `trustProjectExtensions` resolves to `true` — either\n * via the option or the `ZIDANE_TRUST_PROJECT_EXTENSIONS=1` env var\n * fallback. User-scoped extensions always load.\n *\n * Each extension's module graph is evicted from the runtime cache\n * before import, so calling this again (e.g. `/reload`) re-evaluates\n * the current on-disk code — edits, overwrites, and updates apply\n * without a restart.\n *\n * @param opts - Scan overrides (`cwd`, `home`, `prefix`, `extensionScanPaths`), an abort `signal`, `trustProjectExtensions`, `importTimeoutMs` (default 10000) as the per-extension import limit, and the `sink` non-blocking load warnings go to (default: `warn`+ to the console, like `buildExtensionRegistry`).\n */\nexport async function discoverExtensions(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n signal?: AbortSignal\n trustProjectExtensions?: boolean\n importTimeoutMs?: number\n sink?: LogSink\n} = {}): Promise<LoadedExtension[]> {\n const entries = selectExtensionEntries(scanAllExtensionDirectories(opts), opts)\n if (opts.signal?.aborted || entries.length === 0)\n return []\n const sink = opts.sink ?? consoleSink({ minLevel: 'warn' })\n return Promise.all(entries.map((entry) => {\n if (!('entry' in entry))\n return Promise.resolve(entry)\n return loadExtensionFromDisk(entry, opts.importTimeoutMs ?? 10_000, sink)\n }))\n}\n\nfunction selectExtensionEntries(\n entries: readonly ExtensionScanEntry[],\n opts: { trustProjectExtensions?: boolean },\n): Array<ExtensionScanEntry | LoadedExtension> {\n // Safe by default: project extensions are untrusted unless the host\n // opts in explicitly or the user sets the env var the error message\n // below points at.\n const trustProjectExtensions = opts.trustProjectExtensions\n ?? (process.env.ZIDANE_TRUST_PROJECT_EXTENSIONS === '1')\n const selected = new Map<string, ExtensionScanEntry | LoadedExtension>()\n const locked = new Set<string>()\n\n for (const entry of entries) {\n if (locked.has(entry.name))\n continue\n if (entry.source === 'project' && !trustProjectExtensions) {\n if (!selected.has(entry.name)) {\n selected.set(entry.name, {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: 'Project extensions are disabled until this project is trusted. Set ZIDANE_TRUST_PROJECT_EXTENSIONS=1 or pass trustProjectExtensions to enable them.',\n })\n }\n continue\n }\n selected.set(entry.name, entry)\n locked.add(entry.name)\n }\n\n return [...selected.values()]\n}\n\nconst requireForCache = createRequire(import.meta.url)\n\n/**\n * Bun only: monotonic import generation, appended as a query so each\n * discovery pass gets a fresh ENTRY module. The query alone is not enough: relative\n * imports resolve to plain file paths and hit the cache, which is why\n * {@link evictModuleGraph} runs first. Both together are what makes\n * `/reload` a true hot-reload (verified empirically: query-only keeps\n * deep imports stale; eviction-only keeps the entry stale).\n */\nlet importGeneration = 0\n\n/**\n * Evict everything under `dir` from the module cache. Under Bun,\n * `require.cache` deletion covers the ES registry for these keys and the\n * entry itself is refreshed by the generation query above; under Node,\n * jiti keeps the whole graph there, entry included, so eviction alone\n * refreshes it. The extension's own `node_modules` are evicted\n * too so a dependency bump applies on update without a restart.\n */\nfunction evictModuleGraph(dir: string): void {\n const cache = requireForCache.cache\n if (!cache)\n return\n // Cache keys are PHYSICAL paths — realpath the dir or the prefix match\n // silently misses through symlinks (macOS `/var` → `/private/var`).\n let physical: string\n try {\n physical = realpathSync(dir)\n }\n catch {\n physical = dir\n }\n for (const root of physical === dir ? [dir] : [dir, physical]) {\n const prefix = root.endsWith(sep) ? root : root + sep\n for (const key of Object.keys(cache)) {\n if (key.startsWith(prefix))\n delete cache[key]\n }\n }\n}\n\n/**\n * Import an extension entry fresh from disk, its relative import graph\n * included. Every import of an extension goes through here (discovery and\n * install validation), so both runtimes get the same reload semantics.\n *\n * Bun: native `import()` — `.ts`/`.tsx`, extensionless relative imports\n * and host modules (`Bun.plugin`) all resolve natively.\n *\n * Node (the Electron GUI): jiti, NOT native `import()`. Node's ESM cache\n * can't be evicted, so a cache-busting query per reload would pin every\n * generation's module graph for the life of the process. jiti evaluates\n * the graph as CJS into `require.cache`, which {@link evictModuleGraph}\n * clears — old generations become collectible once their registry is\n * disposed (asserted by test/extensions/node-reload.test.ts). Stable URLs\n * also keep the cache flat. jiti additionally gives Node full TS/TSX and\n * `./foo` → `foo.ts` resolution, and maps the host's modules\n * (`'zidane'`, …) through `virtualModules` to the LIVE instances.\n * `fsCache: false`: no transpile output accumulating in tmp per edit.\n */\nexport async function importExtensionEntry(entryPath: string, dir: string): Promise<unknown> {\n evictModuleGraph(dir)\n if (typeof Bun !== 'undefined') {\n importGeneration += 1\n return import(`${pathToFileURL(entryPath).href}?zidane-gen=${importGeneration}`)\n }\n // Non-literal specifier: keeps `bun build --compile` from bundling a\n // loader the Bun binary never runs. Node hosts resolve it from zidane's\n // dependencies.\n const jitiSpecifier = 'jiti'\n const [{ createJiti }, virtualModules] = await Promise.all([\n import(jitiSpecifier) as Promise<typeof import('jiti')>,\n resolveExtensionHostModules(),\n ])\n const jiti = createJiti(entryPath, { moduleCache: true, fsCache: false, interopDefault: false, virtualModules })\n return jiti.import(entryPath)\n}\n\n/**\n * Drop an extension directory's modules from the cache without importing\n * again — for a one-off import whose directory is about to move or be\n * deleted (install validation), so nothing stays pinned under that path.\n */\nexport function releaseExtensionModules(dir: string): void {\n evictModuleGraph(dir)\n}\n\n/**\n * Import + validate a single extension entry. Always returns a\n * {@link LoadedExtension} — the load error (if any) lives on the\n * returned object, never as a thrown exception.\n */\nasync function loadExtensionFromDisk(entry: ExtensionScanEntry, timeoutMs: number, sink: LogSink): Promise<LoadedExtension> {\n // Before the import: a module written against a newer zidane would fail on\n // a missing export, which says nothing about the actual problem.\n const version = checkZidaneVersion(entry.dir)\n if (version?.level === 'error') {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: version.message,\n }\n }\n if (version)\n createLogger(sink, { extension: entry.name }).warn(version.message)\n try {\n const mod = await withTimeout(\n importExtensionEntry(entry.entry, entry.dir),\n timeoutMs,\n `Timed out importing ${entry.entry} after ${timeoutMs}ms. The module's top-level code may still be running; an import can't be cancelled.`,\n )\n const definition = pickExtensionDefinition(mod)\n if (!definition) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Module at ${entry.entry} does not export an ExtensionDefinition (expected \\`default\\` or named \\`extension\\`).`,\n }\n }\n if (entry.manifest?.apiVersion !== undefined && entry.manifest.apiVersion !== 1) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n manifest: entry.manifest,\n error: `Unsupported extension API version \"${entry.manifest.apiVersion}\". This runtime supports apiVersion 1.`,\n }\n }\n if (definition.apiVersion !== undefined && definition.apiVersion !== 1) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Unsupported extension API version \"${definition.apiVersion}\". This runtime supports apiVersion 1.`,\n }\n }\n if (definition.name !== entry.name) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Extension name mismatch: folder is \"${entry.name}\" but module exports name \"${definition.name}\". Rename one so the persisted identity is stable.`,\n }\n }\n if (entry.manifest?.name && entry.manifest.name !== entry.name) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n manifest: entry.manifest,\n error: `Extension manifest name mismatch: folder is \"${entry.name}\" but manifest declares \"${entry.manifest.name}\".`,\n }\n }\n return {\n definition,\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n }\n }\n catch (err) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Failed to import ${entry.entry}: ${errorMessage(err)}`,\n }\n }\n}\n\n/**\n * Extract the `ExtensionDefinition` from an imported module. Accepts\n * either the `default` export (preferred) or a named `extension`\n * export. Anything else — including a module that happens to have\n * top-level `name` + `setup` exports — is rejected so the loader's\n * surface stays unambiguous.\n */\nfunction pickExtensionDefinition(mod: unknown): ExtensionDefinition | null {\n if (!mod || typeof mod !== 'object')\n return null\n const m = mod as Record<string, unknown>\n if (isExtensionDefinition(m.default))\n return m.default\n if (isExtensionDefinition(m.extension))\n return m.extension\n return null\n}\n\n/**\n * Structural type-guard for an {@link ExtensionDefinition}. Kept lax:\n * `name` + `setup` are required; everything else is optional. A non-\n * function `setup` is rejected outright so the loader doesn't have\n * to second-guess it later.\n *\n * @param value - Value to test.\n */\nexport function isExtensionDefinition(value: unknown): value is ExtensionDefinition {\n if (!value || typeof value !== 'object')\n return false\n const v = value as Record<string, unknown>\n if (typeof v.name !== 'string' || v.name.length === 0)\n return false\n if (DANGEROUS_RECORD_KEYS.has(v.name))\n return false\n if (typeof v.setup !== 'function')\n return false\n return true\n}\n\n/**\n * Tag an inline (host-passed) extension into the same shape disk-\n * loaded ones produce, so host-bundled extensions (e.g. those passed\n * via `RunTuiOptions.extensions`) flow through `buildExtensionRegistry`\n * identically to disk-discovered ones.\n *\n * @param definition - Extension definition the host built in code.\n */\nexport function inlineLoadedExtension(definition: ExtensionDefinition): LoadedExtension {\n return {\n definition,\n name: definition.name,\n source: 'inline',\n }\n}\n\n/**\n * Read `extension.json` (or `package.json#zidane`) as a shape-validated\n * manifest. Exported for the installer so \"what counts as a manifest\" has\n * exactly one definition — a malformed manifest is ignored identically at\n * install-validation time and at discovery time.\n */\nexport function readExtensionManifest(dir: string): { manifest?: ExtensionManifest } {\n const fromExtensionJson = readJson(resolve(dir, 'extension.json'))\n if (isExtensionManifest(fromExtensionJson))\n return { manifest: fromExtensionJson }\n const pkg = readJson(resolve(dir, 'package.json'))\n if (pkg && typeof pkg === 'object' && !Array.isArray(pkg)) {\n const manifest = (pkg as { zidane?: unknown }).zidane\n if (isExtensionManifest(manifest))\n return { manifest }\n }\n return {}\n}\n\nfunction readJson(path: string): unknown {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch {\n return null\n }\n}\n\nfunction isExtensionManifest(value: unknown): value is ExtensionManifest {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return false\n const v = value as Record<string, unknown>\n // `apiVersion`, when present, must be a number. We still recognize an\n // unsupported version (e.g. 2) as a manifest so the loader can surface a\n // clear \"unsupported API version\" error rather than silently ignoring it —\n // but the rest of the shape is still validated.\n if (v.apiVersion !== undefined && typeof v.apiVersion !== 'number')\n return false\n const isStringArray = (x: unknown): boolean => Array.isArray(x) && x.every(el => typeof el === 'string')\n return (v.name === undefined || typeof v.name === 'string')\n && (v.displayName === undefined || typeof v.displayName === 'string')\n && (v.description === undefined || typeof v.description === 'string')\n // Element-checked, not just Array.isArray — the predicate claims\n // `ExtensionManifest` whose lists are `string[]`, so honor that claim.\n && (v.permissions === undefined || isStringArray(v.permissions))\n && (v.capabilities === undefined || isStringArray(v.capabilities))\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\n/** Re-export for tests / advanced hosts that want to log the scan results before loading. */\nexport type { ExtensionScanEntry }\n/** Re-export so callers can read the canonical entry file list. */\nexport { ENTRY_CANDIDATES }\n","/**\n * Keybindings — a single source of truth for the app's global shortcuts.\n *\n * Every customizable action is declared once in {@link KEYBINDING_DEFS}\n * with its default trigger, label, and description. The TUI imports\n * `DEFAULT_KEYBINDINGS` for the baked-in defaults; on launch we load\n * `<userDir>/keybindings.json` (when present) and merge user overrides\n * on top so the user can rebind any action without recompiling.\n *\n * Scope (deliberate):\n *\n * - We expose the **global, action-opening** shortcuts (\"open the\n * Settings modal\", \"cycle agent profile\", \"enter select-turn\n * mode\", etc.), the message-queue keys, and the letter shortcuts of\n * the turn-details and session-details modals (`f` fork, `d` delete,\n * `c` copy). Universal navigation (`↑/↓/↵/⎋`) stays hardcoded — it\n * lives too close to a renderable's input contract to remap safely.\n *\n * - Storage lives at the **user level** (`<userDir>/keybindings.json`),\n * never inside a project's `.{prefix}/`. Shortcut muscle memory is\n * a per-user concern, not a per-repo one.\n *\n * Format on disk: JSON with `//` and `/* … *\\/` comments allowed\n * (parsed via a small JSONC stripper). Each entry maps an action id\n * to a binding spec string. Specs are parsed by {@link parseBindingSpec};\n * unknown / malformed entries fall back to the default for that action,\n * so a syntax slip never bricks the TUI.\n */\n\nimport type { ParsedBinding } from './keybinding-spec'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { bindingAlternatives, parseBindingSpec, parseBindingSpecs } from './keybinding-spec'\n\nexport { bindingAlternatives, parseBindingSpec, parseBindingSpecs, type ParsedBinding } from './keybinding-spec'\n\n// ---------------------------------------------------------------------------\n// Action catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Every action the keybindings system can drive. Adding a new action\n * here forces every consumer (TUI keyboard handler, hint rows, settings\n * UI) to handle it, and ships a default that the user can override.\n */\nexport type KeyAction\n // Global action shortcuts — fire from the top-level keyboard\n // handler (`handleAppKey` in `tui/shell/app-keyboard.tsx`), gated on screen / busy state.\n = | 'openSettings'\n | 'openSessionDetails'\n | 'newSession'\n | 'openModelPicker'\n | 'openEffortPicker'\n | 'openTodos'\n | 'openArtifacts'\n | 'panelNext'\n | 'panelPrev'\n | 'nextSpace'\n | 'prevSpace'\n | 'openContextPanel'\n | 'openMainSession'\n | 'openUsagePanel'\n | 'openKeybindings'\n | 'openPalette'\n | 'cycleAgent'\n | 'enterSelectTurnMode'\n | 'cancelToolCall'\n | 'abortRun'\n | 'changeCwd'\n | 'attachClipboardImage'\n | 'openExternalEditor'\n // Message-queue navigation — only meaningful when the user has\n // type-ahead prompts waiting in the queue box above the prompt\n // input. `enterQueueSelection` moves the focus from the textarea\n // into the queue; while there, `pushQueuedMessage` (steers the\n // selected entry into the live run) and `dropQueuedMessage`\n // (removes the entry) act on the highlighted row.\n | 'enterQueueSelection'\n | 'pushQueuedMessage'\n | 'dropQueuedMessage'\n // Turn-details modal actions (`ctrl+s` select-turn mode → enter).\n // Each binding only matches while that modal is mounted, so the\n // single-letter defaults can clash with letters used elsewhere\n // without conflict.\n | 'turnFork'\n | 'turnDelete'\n | 'turnCopy'\n | 'turnEdit'\n // Session-details modal actions (opened via `ctrl+shift+x`).\n | 'sessionDelete'\n | 'sessionCopyId'\n | 'sessionGenerateTitle'\n | 'sessionExportMarkdown'\n | 'sessionExportJson'\n | 'sessionCompact'\n | 'sessionMoveSpace'\n | 'sessionFavorite'\n\n/** Resolved keybindings — every action has a current spec string. */\nexport type KeyBindings = Record<KeyAction, string>\n\n/**\n * Resolved spec string for one extension-contributed keybinding. Same\n * shape as a built-in {@link KeyBindings} entry but the `id` is\n * extension-owned (no `KeyAction` union member). The TUI dispatcher\n * iterates these alongside the built-ins; user overrides flow through\n * the same `keybindings.json` file by matching on `id`.\n */\nexport interface ResolvedExtensionKeybinding {\n /** Extension-owned id (matches `ExtensionKeybinding.id`). */\n id: string\n /** Active spec string — user override when present, default otherwise. May be empty (= unbound). */\n spec: string\n}\n\n/**\n * Logical grouping for one action — used by the keybindings panel to\n * render section headers. Stable strings (not an enum) so future\n * groups can be added without churning every existing entry.\n */\nexport type KeyBindingGroup\n = | 'Global'\n | 'Message queue'\n | 'Turn details'\n | 'Session details'\n\n/** Static metadata for one action — used by the loader, hint rows, settings UI. */\nexport interface KeyBindingDef {\n /** Stable identifier — JSON key + TS union member. */\n action: KeyAction\n /** Default trigger spec (e.g. `'ctrl+o'`). */\n default: string\n /** Short label used in shortcut hint rows. */\n label: string\n /** Longer description for the settings UI and the annotated JSON file. */\n description: string\n /**\n * Group label rendered as a section header in the keybindings panel.\n * Items are listed in {@link KEYBINDING_DEFS} order; the panel just\n * inserts a header whenever this field changes between adjacent\n * entries — so groups stay contiguous and the order here is the\n * order on screen.\n */\n group: KeyBindingGroup\n}\n\n/**\n * The canonical action catalog. Order is the source of truth for the\n * generated keybindings file and the settings UI.\n */\nexport const KEYBINDING_DEFS: readonly KeyBindingDef[] = [\n {\n action: 'openSettings',\n default: 'ctrl+o',\n label: 'settings',\n description: 'open settings',\n group: 'Global',\n },\n {\n // ctrl+shift+x keeps ctrl+x free (the conventional \"cut\" key in most\n // editors). Like the context panel, it relies on the kitty keyboard\n // protocol to stay distinct from ctrl+x; on terminals without it\n // (e.g. macOS Terminal.app) shift is dropped and it collapses to\n // ctrl+x — rebind there.\n action: 'openSessionDetails',\n default: 'ctrl+shift+x',\n label: 'session',\n description: 'open session details: stats, export, rename, delete',\n group: 'Global',\n },\n {\n action: 'newSession',\n default: 'ctrl+n',\n label: 'new session',\n description: 'start a new session with the current model',\n group: 'Global',\n },\n {\n action: 'openModelPicker',\n default: 'ctrl+m',\n label: 'model',\n description: 'open the model picker',\n group: 'Global',\n },\n {\n action: 'openEffortPicker',\n default: 'ctrl+l',\n label: 'options',\n description: 'open model options, like reasoning effort and fast mode',\n group: 'Global',\n },\n {\n action: 'openTodos',\n default: 'ctrl+t',\n label: 'todos',\n description: 'open the run\\'s todo list',\n group: 'Global',\n },\n {\n // ctrl+shift+a: \"artifacts\" — what the run produced, next to todos (ctrl+t),\n // what it planned. Kitty-protocol dependent like the other ctrl+shift\n // chords — collapses to ctrl+a on Terminal.app; rebind there.\n action: 'openArtifacts',\n default: 'ctrl+shift+a',\n label: 'panel',\n description: 'show or hide the side panel',\n group: 'Global',\n },\n {\n action: 'panelNext',\n default: 'ctrl+shift+]',\n label: 'next panel',\n description: 'open the next side-panel tab',\n group: 'Global',\n },\n {\n action: 'panelPrev',\n default: 'ctrl+shift+[',\n label: 'previous panel',\n description: 'open the previous side-panel tab',\n group: 'Global',\n },\n // Spaces (opt-in): the arrows of the sessions list's ←/→ and the GUI's ⌥⌘←/→.\n // kitty and WezTerm keep ctrl+shift+arrows for their own tabs / panes by default: rebind there.\n {\n action: 'nextSpace',\n default: 'ctrl+shift+right',\n label: 'next space',\n description: 'show the next space (Spaces on); from a chat, the sessions list opens on it',\n group: 'Global',\n },\n {\n action: 'prevSpace',\n default: 'ctrl+shift+left',\n label: 'previous space',\n description: 'show the previous space (Spaces on); from a chat, the sessions list opens on it',\n group: 'Global',\n },\n {\n // ctrl+shift+g leans on the kitty keyboard protocol (the renderer\n // enables it) to stay distinct from ctrl+g (`openExternalEditor`). On\n // terminals without it — e.g. macOS Terminal.app — shift is dropped and\n // the keypress collapses to ctrl+g (the editor); rebind there.\n action: 'openContextPanel',\n default: 'ctrl+shift+g',\n label: 'context',\n description: 'show what fills the context window',\n group: 'Global',\n },\n {\n // ctrl+shift+m (\"Main\"): one chord for the TUI and the GUI. Leans on the\n // kitty keyboard protocol (the renderer enables it) to stay distinct from\n // ctrl+m (the model picker); on terminals without it — e.g. macOS\n // Terminal.app — shift is dropped and the keypress collapses to ctrl+m;\n // rebind there. NOT ctrl+u: that is the textarea's kill-to-line-start,\n // which is also what several terminals send for ⌃⌫, so deleting a word\n // would jump to the main session.\n action: 'openMainSession',\n default: 'ctrl+shift+m',\n label: 'main session',\n description: 'open the Main session for this directory, or create it. Needs the \"Main session\" and \"Use daemon runtime\" settings',\n group: 'Global',\n },\n {\n // Plain ctrl+p on purpose: free in OpenTUI's textarea keymap (which owns\n // ctrl+a/e/f/b/w/k/u/d) and in every other global binding, so it behaves\n // the same on kitty and legacy terminals. A ctrl+shift+<x> default would\n // collapse to its unshifted chord on Terminal.app and eat a text-editing\n // key.\n action: 'openUsagePanel',\n default: 'ctrl+p',\n label: 'usage',\n description: 'open plan limits and credits for each provider',\n group: 'Global',\n },\n {\n action: 'openKeybindings',\n default: 'ctrl+y',\n label: 'keybindings',\n description: 'list every shortcut',\n group: 'Global',\n },\n {\n action: 'openPalette',\n // Terminals cannot report cmd, and ctrl+k belongs to cancelToolCall;\n // ctrl+shift+p is the palette chord editors made familiar. The GUI\n // catalog binds this to cmd+k (a declared divergence).\n default: 'ctrl+shift+p',\n label: 'palette',\n description: 'search actions, commands, sessions and settings',\n group: 'Global',\n },\n {\n action: 'cycleAgent',\n default: 'shift+tab',\n label: 'cycle agent',\n description: 'switch to the next agent profile',\n group: 'Global',\n },\n {\n action: 'enterSelectTurnMode',\n default: 'ctrl+s',\n label: 'messages',\n description: 'select a previous message',\n group: 'Global',\n },\n {\n action: 'cancelToolCall',\n default: 'ctrl+k',\n label: 'cancel tool',\n description: 'cancel one tool call and keep the run going',\n group: 'Global',\n },\n {\n action: 'abortRun',\n default: 'ctrl+escape',\n label: 'abort',\n description: 'stop the run and deny pending approvals. If your terminal cannot send ctrl+esc, rebind it',\n group: 'Global',\n },\n {\n action: 'changeCwd',\n default: 'ctrl+h',\n label: 'cwd',\n description: 'change the working directory',\n group: 'Global',\n },\n {\n // The id keeps its original name so existing keybindings.json overrides\n // work; the shortcut pastes text as well as images.\n action: 'attachClipboardImage',\n default: 'ctrl+v',\n label: 'paste clipboard',\n description: 'paste text or an image from the system clipboard',\n group: 'Global',\n },\n {\n // Handled inside `PromptBlock`'s onKeyDown (it owns the textarea ref),\n // NOT the App-level keyboard handler — so it only ever fires while the\n // prompt composer is focused. Gated by the `externalEditor` setting:\n // disabled there, the key falls through to its normal textarea behavior.\n // ctrl+g (Claude Code's muscle-memory key) needs no kitty keyboard\n // protocol; the context panel takes ctrl+shift+g to keep it free.\n action: 'openExternalEditor',\n default: 'ctrl+g',\n label: 'edit in $EDITOR',\n description: 'write the prompt in $EDITOR or $VISUAL. Needs the \"Edit prompt in $EDITOR\" setting',\n group: 'Global',\n },\n // Message-queue navigation — only matches when the queue box above\n // the prompt has at least one entry. Defaults pick keys the prompt\n // textarea doesn't already claim (no default for enter — see below).\n //\n // `enterQueueSelection` is intentionally unbound by default: the\n // natural way to enter the queue is to press plain `↑` on an empty\n // prompt buffer (handled in `PromptBlock`'s onKeyDown), which doesn't\n // need a global shortcut. The action stays in the catalog so users\n // who want a dedicated key — e.g. for entering the queue WITHOUT\n // emptying their draft first — can rebind it in `keybindings.json`.\n // We don't ship a default because no arrow / modifier combo is both\n // free in OpenTUI's textarea AND forwarded by every common terminal\n // (ctrl+arrow → macOS Mission Control; cmd+arrow → terminal scroll /\n // tab nav; alt+arrow → terminal word-nav).\n {\n action: 'enterQueueSelection',\n default: '',\n label: 'select queue',\n description: 'select the queued messages (or press ↑ on an empty prompt)',\n group: 'Message queue',\n },\n {\n action: 'pushQueuedMessage',\n default: 'ctrl+return',\n label: 'push',\n description: 'send the selected queued message into the running turn',\n group: 'Message queue',\n },\n {\n action: 'dropQueuedMessage',\n default: 'backspace',\n label: 'drop',\n description: 'remove the selected queued message',\n group: 'Message queue',\n },\n // Turn-details modal — only fires while that modal is mounted.\n {\n action: 'turnFork',\n default: 'f',\n label: 'fork',\n description: 'fork the session at this turn (press twice)',\n group: 'Turn details',\n },\n {\n action: 'turnDelete',\n default: 'd',\n label: 'delete',\n description: 'delete this turn (press twice)',\n group: 'Turn details',\n },\n {\n action: 'turnCopy',\n default: 'c',\n label: 'copy',\n description: 'copy this turn',\n group: 'Turn details',\n },\n {\n action: 'turnEdit',\n default: 'e',\n label: 'edit',\n description: 'edit this turn\\'s text',\n group: 'Turn details',\n },\n // Session-details modal — only fires while that modal is mounted.\n {\n action: 'sessionDelete',\n default: 'd',\n label: 'delete',\n description: 'delete the session (press twice)',\n group: 'Session details',\n },\n {\n action: 'sessionCopyId',\n default: 'c',\n label: 'copy id',\n description: 'copy the session id',\n group: 'Session details',\n },\n {\n action: 'sessionGenerateTitle',\n default: 'g',\n label: 'generate title',\n description: 'title the session with the current model',\n group: 'Session details',\n },\n {\n action: 'sessionExportMarkdown',\n default: 'e',\n label: 'export md',\n description: 'export the session as Markdown under .{prefix}/sessions/',\n group: 'Session details',\n },\n {\n action: 'sessionExportJson',\n default: 'j',\n label: 'export json',\n description: 'export the session as JSON under .{prefix}/sessions/',\n group: 'Session details',\n },\n {\n action: 'sessionCompact',\n default: 'k',\n label: 'compact',\n description: 'summarize older turns to free context',\n group: 'Session details',\n },\n {\n action: 'sessionMoveSpace',\n default: 's',\n label: 'space',\n description: 'move the session to the next space (needs Spaces)',\n group: 'Session details',\n },\n {\n action: 'sessionFavorite',\n default: 'f',\n label: 'favorite',\n description: 'add the session to favorites, or remove it',\n group: 'Session details',\n },\n]\n\n/** Index by action for O(1) lookup of label / description / default. */\nexport const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>\n = KEYBINDING_DEFS.reduce(\n (acc, def) => Object.assign(acc, { [def.action]: def }),\n {} as Record<KeyAction, KeyBindingDef>,\n )\n\n/** Defaults, derived from {@link KEYBINDING_DEFS} so the two never drift. */\nexport const DEFAULT_KEYBINDINGS: KeyBindings\n = KEYBINDING_DEFS.reduce(\n (acc, def) => Object.assign(acc, { [def.action]: def.default }),\n {} as KeyBindings,\n )\n\n// ---------------------------------------------------------------------------\n// Spec parsing + matching\n// ---------------------------------------------------------------------------\n\n/**\n * Test a KeyEvent against a parsed-or-string binding. Accepts either\n * form so callers can pre-parse (hot path) or pass the raw spec for\n * one-off checks.\n *\n * Modifier semantics: ALL declared modifiers must be pressed AND no\n * extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,\n * and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises\n * where a binding that should be specific accidentally fires for a\n * superset of key combinations.\n *\n * @param event - Key event to test.\n * @param spec - Binding spec string, or a chord already parsed with {@link parseBindingSpec}.\n */\nexport function matchesBinding(\n event: { name?: string, ctrl?: boolean, shift?: boolean, meta?: boolean, option?: boolean, alt?: boolean },\n spec: string | ParsedBinding,\n): boolean {\n if (typeof spec === 'string')\n return parseBindingSpecs(spec).some(parsed => matchesBinding(event, parsed))\n const parsed = spec\n // OpenTUI's `ParsedKey` exposes `option` for Alt; some terminals\n // also report it as `alt`. Accept either.\n const altPressed = !!event.option || !!event.alt\n if (parsed.ctrl !== !!event.ctrl)\n return false\n if (parsed.shift !== !!event.shift)\n return false\n if (parsed.alt !== altPressed)\n return false\n if (parsed.meta !== !!event.meta)\n return false\n if ((event.name ?? '').toLowerCase() !== parsed.name)\n return false\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Display helpers\n// ---------------------------------------------------------------------------\n\n/** Verbose key names → compact single-cell glyphs used by hint rows. */\nconst KEY_GLYPHS: Readonly<Record<string, string>> = {\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n return: '↵',\n enter: '↵',\n delete: '⌫',\n backspace: '⌫',\n escape: 'esc',\n space: '␣',\n tab: '⇥',\n}\n\n/**\n * Render a binding spec (`\"ctrl+return\"`, `\"backspace\"`, `\"delete\"`) as a\n * compact display string the user recognizes at a glance. Substitutes\n * arrow / enter / backspace glyphs for their verbose names so the hint\n * row stays narrow; modifier names + plain keys pass through unchanged.\n *\n * Empty / missing specs return an empty string — callers decide whether\n * to render a placeholder (e.g. `'—'` for an unbound action in the\n * keybindings panel) or hide the surface entirely (e.g. hint rows that\n * fall back to a different chord when the binding is empty).\n *\n * @param spec - Binding spec, possibly listing several chords separated by `|`.\n */\nexport function formatBindingForDisplay(spec: string | null | undefined): string {\n return bindingAlternatives(spec).map(formatOneBinding).join(' / ')\n}\n\nfunction formatOneBinding(spec: string): string {\n const segments = spec.toLowerCase().split('+')\n const key = segments.pop() ?? ''\n const modifiers = segments.join('+')\n const glyph = KEY_GLYPHS[key] ?? key\n return modifiers ? `${modifiers}+${glyph}` : glyph\n}\n\n// ---------------------------------------------------------------------------\n// Merge + file IO\n// ---------------------------------------------------------------------------\n\n/**\n * Merge a partial map of user overrides into the defaults. Unknown\n * action keys are dropped (a future TUI version may have retired the\n * action); invalid spec strings (parse fails) fall back to the default\n * for that action.\n *\n * @param overrides - Parsed user overrides keyed by action name.\n */\nexport function mergeKeybindings(\n overrides: Readonly<Record<string, unknown>> | null | undefined,\n): KeyBindings {\n if (!overrides || typeof overrides !== 'object')\n return { ...DEFAULT_KEYBINDINGS }\n const merged: KeyBindings = { ...DEFAULT_KEYBINDINGS }\n for (const def of KEYBINDING_DEFS) {\n const raw = normalizeBindingValue(overrides[def.action])\n if (raw === null)\n continue // invalid spec → keep the default\n merged[def.action] = raw\n }\n return merged\n}\n\n/**\n * A `keybindings.json` value as one spec: a string, or an array of chords\n * (`[\"ctrl+w\", \"cmd+w\"]`) joined as alternatives. Chords that don't parse\n * are dropped; `null` when none is left.\n *\n * @param raw - Raw value from `keybindings.json`: a spec string or an array of them.\n */\nexport function normalizeBindingValue(raw: unknown): string | null {\n const parts = typeof raw === 'string'\n ? bindingAlternatives(raw)\n : Array.isArray(raw) ? raw.filter((r): r is string => typeof r === 'string').flatMap(bindingAlternatives) : []\n const valid = parts.filter(part => parseBindingSpec(part) !== null)\n return valid.length > 0 ? valid.join(' | ') : null\n}\n\n/**\n * Path of the user-level keybindings file.\n *\n * @param userDir - User-level config directory.\n */\nexport function keybindingsPath(userDir: string): string {\n return resolve(userDir, 'keybindings.json')\n}\n\n/**\n * One contiguous section of the catalog — every adjacent action of a single\n * `group` rendered together with the section header inserted once.\n * Non-adjacent runs of the same group are NOT merged: catalog order\n * wins so the on-screen story matches the on-disk file.\n *\n * `rows` may carry built-in `KeyBindingDef`s (the common case) or\n * extension-owned ones (when an extension contributes a keybinding).\n * Both share label / description / spec — see {@link KeyBindingRow}.\n */\nexport interface KeyBindingSection {\n /** Group name shared by every row in the section. */\n group: string\n /** Rows in the section, in catalog order. */\n rows: ReadonlyArray<KeyBindingRow>\n}\n\n/**\n * One row in the keybindings catalog. Either a built-in action (with\n * its `def`) or an extension-contributed one (with `id` instead). The\n * renderer reads `label` / `description` / `spec` uniformly and only\n * needs the `kind` discriminator when offering action-specific UI\n * (e.g. \"edit in keybindings.json\" by action name).\n */\nexport type KeyBindingRow\n = | { kind: 'builtin', def: KeyBindingDef, spec: string }\n | { kind: 'extension', id: string, label: string, description: string, spec: string }\n\n/**\n * Walk {@link KEYBINDING_DEFS} in order and bucket rows into contiguous\n * sections by `group`. Shared between the standalone `KeybindingsModal`\n * and the `Keybindings` tab inside `SettingsModal` so both surfaces\n * render the same catalog with the same ordering rules.\n *\n * Pass `extensionRows` (resolved extension keybindings paired with\n * their definitions) to append an \"Extensions\" section at the end so\n * extension-contributed shortcuts are discoverable in the same UI.\n *\n * @param bindings - Resolved built-in bindings, keyed by action.\n * @param extensionRows - Extension-contributed keybindings to list in a trailing section.\n */\nexport function groupBindings(\n bindings: KeyBindings,\n extensionRows: readonly { id: string, label: string, description: string, spec: string }[] = [],\n): readonly KeyBindingSection[] {\n const sections: Array<{ group: string, rows: KeyBindingRow[] }> = []\n for (const def of KEYBINDING_DEFS) {\n const last = sections[sections.length - 1]\n const spec = bindings[def.action] ?? ''\n const row: KeyBindingRow = { kind: 'builtin', def, spec }\n if (last && last.group === def.group) {\n last.rows.push(row)\n continue\n }\n sections.push({ group: def.group, rows: [row] })\n }\n if (extensionRows.length > 0) {\n sections.push({\n group: 'Extensions',\n rows: extensionRows.map(r => ({\n kind: 'extension',\n id: r.id,\n label: r.label,\n description: r.description,\n spec: r.spec,\n })),\n })\n }\n return sections\n}\n\n/**\n * Width (in cells) of the fixed key column used by every binding-row\n * renderer. Derived once from {@link KEYBINDING_DEFS} so a new action\n * with a wider default spec (`ctrl+shift+x`, …) automatically grows\n * the column instead of truncating the label.\n */\nexport const KEYBINDING_KEY_COL_WIDTH = (() => {\n let max = 8\n for (const def of KEYBINDING_DEFS) {\n const width = formatBindingForDisplay(def.default).length\n if (width > max)\n max = width\n }\n return max + 2 // breathing room before the label column\n})()\n\n/**\n * Load + merge the user's keybindings file (when present). Always\n * returns a fully-resolved {@link KeyBindings} — defaults fill in for\n * any missing / invalid entries so consumers can treat the result as\n * total.\n *\n * Failure modes (all tolerated, all fall back to defaults):\n * - File missing\n * - File not valid JSON / JSONC (after comment stripping)\n * - Top-level is not an object\n * - Individual entries have invalid spec strings\n *\n * @param userDir - User-level config directory that holds `keybindings.json`.\n */\nexport function readKeybindings(userDir: string): KeyBindings {\n return mergeKeybindings(readKeybindingsRaw(userDir))\n}\n\n/**\n * Create the user-level `keybindings.json` if it doesn't exist,\n * prefilled with every default. Returns the absolute file path either\n * way so the caller can open it in `$EDITOR`. Idempotent: an existing\n * user file is NEVER overwritten — the user's customizations are\n * sacred.\n *\n * @param userDir - User-level config directory that holds `keybindings.json`.\n */\nexport function ensureKeybindingsFile(userDir: string): string {\n const path = keybindingsPath(userDir)\n if (existsSync(path))\n return path\n writeFileAtomic(path, renderDefaultFile(), { ensureDir: true })\n return path\n}\n\n/**\n * Read the raw on-disk JSON object from `keybindings.json` after the\n * JSONC stripper. Used by extension-keybinding resolution so the\n * user's overrides for extension-owned ids flow through the same file\n * the built-in actions use.\n *\n * Returns an empty object on missing / malformed / non-object files\n * — same fault-tolerance contract as {@link readKeybindings}.\n *\n * @param userDir - User-level config directory that holds `keybindings.json`.\n */\nexport function readKeybindingsRaw(userDir: string): Readonly<Record<string, unknown>> {\n const path = keybindingsPath(userDir)\n if (!existsSync(path))\n return {}\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(stripJsonComments(raw)) as unknown\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n return parsed as Readonly<Record<string, unknown>>\n }\n catch {\n return {}\n }\n}\n\n/**\n * Resolve the live spec string for each extension-contributed\n * keybinding. The user's `keybindings.json` is consulted for an\n * override on the binding's `id`; absent / invalid entries fall back\n * to the binding's `default`.\n *\n * A chord that is reserved or a bare printable character\n * (see {@link isReservedExtensionBinding}, {@link isBareCharExtensionBinding})\n * is refused: a user override falls back to the default, and such a default\n * resolves to unbound.\n *\n * Pass the raw object (from {@link readKeybindingsRaw}) — passing\n * `KeyBindings` here wouldn't help because the typed map only carries\n * built-in `KeyAction` entries.\n *\n * Returns one entry per input binding, in source order. Empty `spec`\n * (binding declared `default: ''` and the user didn't bind it) is a\n * legitimate \"unbound\" state — the dispatcher never matches an empty\n * spec, so the action stays accessible via re-binding through\n * `keybindings.json`.\n *\n * @param bindings - Extension keybindings with their ids and default specs.\n * @param raw - Raw object from {@link readKeybindingsRaw}.\n */\nexport function resolveExtensionKeybindings(\n bindings: readonly { id: string, default: string }[],\n raw: Readonly<Record<string, unknown>>,\n): ResolvedExtensionKeybinding[] {\n return bindings.map((binding) => {\n const candidate = raw[binding.id]\n if (\n typeof candidate === 'string'\n && parseBindingSpec(candidate)\n && !isReservedExtensionBinding(candidate)\n && !isBareCharExtensionBinding(candidate)\n ) {\n return { id: binding.id, spec: candidate }\n }\n return {\n id: binding.id,\n spec: isReservedExtensionBinding(binding.default) || isBareCharExtensionBinding(binding.default)\n ? ''\n : binding.default,\n }\n })\n}\n\n/**\n * `true` when `spec` is one of the chords the framework reserves for\n * itself — `escape` (back / exit), `ctrl+escape` (the `abortRun`\n * default — the one built-in that must never be shadowed) and `ctrl+c`\n * (quit). Extensions\n * can never claim these, neither via their declared `default` nor via a\n * user override in `keybindings.json`. Single source of truth shared by\n * `resolveExtensionKeybindings` above and the registration-time refusal\n * in `extensions/register.ts`.\n */\nexport function isReservedExtensionBinding(spec: string): boolean {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return false\n const noExtraModifiers = !parsed.shift && !parsed.alt && !parsed.meta\n if (parsed.name === 'escape' && noExtraModifiers)\n return true\n return parsed.name === 'c' && parsed.ctrl && noExtraModifiers\n}\n\n/**\n * `true` when `spec` is a single printable character without a gating\n * modifier (ctrl / alt / meta — shift alone is just a capital letter).\n * Such a chord would fire on every matching keystroke while the user\n * types in the prompt, so extension bindings can't use one; they're\n * registered unbound instead (rebindable to a modified chord via\n * `keybindings.json`).\n */\nexport function isBareCharExtensionBinding(spec: string): boolean {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return false\n return parsed.name.length === 1 && !parsed.ctrl && !parsed.alt && !parsed.meta\n}\n\n/**\n * Render the default `keybindings.json` body. JSONC with a header\n * explaining the format + per-action comments derived from\n * {@link KEYBINDING_DEFS}'s descriptions. The user sees the catalog +\n * defaults at a glance the moment they open the file.\n *\n * Extension-contributed bindings live alongside the built-ins in the\n * SAME file (keyed by their `id`, e.g. `\"my-ext.openPanel\"`) — they\n * don't appear in the scaffold because extensions are discovered at\n * runtime, but `resolveExtensionKeybindings` reads the same file so\n * a user-added entry overrides the extension's default. The header of\n * the scaffold tells the user how.\n */\nfunction renderDefaultFile(): string {\n const lines: string[] = []\n lines.push('// Zidane keybindings. Edit the values below to customize.')\n lines.push('//')\n lines.push('// Format:')\n lines.push('// \"<action>\": \"<modifier>+<modifier>+<key>\"')\n lines.push('//')\n lines.push('// Modifiers: ctrl, shift, alt (option), meta (cmd).')\n lines.push('// Keys: a-z, 0-9, tab, return, escape, space, delete, …')\n lines.push('//')\n lines.push('// Comments (`//` line, `/* block */`) are stripped before parsing.')\n lines.push('// Restart the TUI to apply changes.')\n lines.push('//')\n lines.push('// Extensions (.zidane/extensions/<name>/index.ts) can register')\n lines.push('// their own keybindings; rebind any of them by adding a line')\n lines.push('// using the extension binding\\'s id, e.g.')\n lines.push('// \"my-ext.openPanel\": \"ctrl+shift+j\"')\n lines.push('// The Settings → Keybindings tab lists every registered id.')\n lines.push('{')\n KEYBINDING_DEFS.forEach((def, i) => {\n const trailingComma = i < KEYBINDING_DEFS.length - 1 ? ',' : ''\n lines.push(` // ${def.description}`)\n lines.push(` ${JSON.stringify(def.action)}: ${JSON.stringify(def.default)}${trailingComma}`)\n })\n lines.push('}')\n lines.push('')\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// JSONC parsing — `//` line + `/* block */` comments, single pass\n// ---------------------------------------------------------------------------\n\n/**\n * Strip `//` line comments and `/* … *\\/` block comments from a JSONC\n * source string. Stays out of string literals so a `\"foo // bar\"` value\n * keeps its slashes. Exported for unit-tests; the only runtime caller\n * is {@link readKeybindingsRaw}.\n *\n * Trade-off: this is a tiny state-machine, not a full JSONC parser. It\n * doesn't understand `\\\\` outside strings (irrelevant for JSON) and\n * doesn't track line numbers in error messages. Good enough for a\n * config file — corruption in production gracefully falls back to the\n * defaults via the try/catch in `readKeybindingsRaw`.\n *\n * @param input - JSONC source text.\n */\nexport function stripJsonComments(input: string): string {\n let out = ''\n let i = 0\n let inString = false\n let escape = false\n while (i < input.length) {\n const c = input[i]\n if (inString) {\n out += c\n if (escape)\n escape = false\n else if (c === '\\\\')\n escape = true\n else if (c === '\"')\n inString = false\n i++\n continue\n }\n if (c === '\"') {\n inString = true\n out += c\n i++\n continue\n }\n if (c === '/' && input[i + 1] === '/') {\n while (i < input.length && input[i] !== '\\n')\n i++\n continue\n }\n if (c === '/' && input[i + 1] === '*') {\n i += 2\n while (i < input.length && !(input[i] === '*' && input[i + 1] === '/'))\n i++\n i += 2\n continue\n }\n out += c\n i++\n }\n return out\n}\n","/**\n * A provider's OAuth flow rides its provider contribution.\n *\n * `ctx.registerProvider(descriptor)` with a `descriptor.oauthProvider` is all\n * an extension declares. When the registry builds, the flow joins the\n * process-wide OAuth registry (`providers/oauth-registry`) that lazy token\n * refresh and usage probes resolve through; when the registry disposes, it\n * leaves. Registering the flow from `setup` instead would run even while the\n * extension is disabled, with nothing to undo it.\n *\n * - Active extensions only. A disabled extension's descriptor is dropped\n * with the rest of its contributions, and its flow with it.\n * - Built-in flow ids (`anthropic`, `openai-codex`, …) are refused: the\n * user's own subscription tokens never refresh through extension code.\n * - Claims are counted per flow object. A host handing the same inline\n * definition to several live registries (or building the next one before\n * disposing the last) keeps the flow until the last claim goes; a reload\n * that re-imports the extension registers a NEW object, which an older\n * registry's release can't remove.\n *\n * `descriptor.oauthProvider` is read once, when the registry builds.\n */\n\nimport type { LogSink } from '../logger'\nimport type { OAuthProviderInterface } from '../providers/oauth-registry'\nimport type { LoadedExtension } from './types'\nimport { createLogger } from '../logger'\nimport { BUILTIN_OAUTH_PROVIDER_IDS, getOAuthProvider, registerOAuthProvider, unregisterOAuthProvider } from '../providers/oauth-registry'\n\n/** Process-wide, like the registry itself: every zidane copy counts the same claims. */\nconst CLAIMS_SLOT = Symbol.for('zidane.extension-oauth-claims')\n\nfunction claims(): WeakMap<object, number> {\n const slots = globalThis as { [key: symbol]: WeakMap<object, number> | undefined }\n slots[CLAIMS_SLOT] ??= new WeakMap()\n return slots[CLAIMS_SLOT]\n}\n\nfunction isFlow(value: unknown): value is OAuthProviderInterface {\n const flow = value as Partial<OAuthProviderInterface> | null\n return !!flow\n && typeof flow.id === 'string'\n && flow.id.length > 0\n && typeof flow.login === 'function'\n && typeof flow.refreshToken === 'function'\n && typeof flow.getApiKey === 'function'\n}\n\n/**\n * Register the OAuth flows of the active extensions' providers.\n *\n * @param active - Extensions whose contributions are live.\n * @param sink - Log sink for the per-extension warnings.\n * @returns Releases this registry's claims. Idempotent.\n */\nexport function claimProviderOAuthFlows(active: readonly LoadedExtension[], sink: LogSink): () => void {\n const claimed: OAuthProviderInterface[] = []\n for (const entry of active) {\n const providers = entry.contributions?.providers\n if (!providers)\n continue\n const logger = createLogger(sink, { extension: entry.name })\n for (const descriptor of Object.values(providers)) {\n const flow: unknown = descriptor.oauthProvider\n if (flow === undefined)\n continue\n if (!isFlow(flow)) {\n logger.warn('registerProvider: oauthProvider needs an id, login, refreshToken and getApiKey; its sign-in won\\'t refresh', { provider: descriptor.key })\n continue\n }\n if (BUILTIN_OAUTH_PROVIDER_IDS.has(flow.id)) {\n if (getOAuthProvider(flow.id) !== flow)\n logger.warn(`registerProvider: OAuth flow id \"${flow.id}\" belongs to a built-in sign-in; refresh keeps using the built-in flow`, { provider: descriptor.key })\n continue\n }\n const credentialKey = descriptor.credentialFileKey ?? descriptor.key\n if (flow.id !== credentialKey)\n logger.warn(`registerProvider: OAuth flow id \"${flow.id}\" differs from the credential key \"${credentialKey}\"; usage probes look the flow up by the credential key`, { provider: descriptor.key })\n const current = getOAuthProvider(flow.id)\n if (current && current !== flow && claims().has(current))\n logger.warn(`registerProvider: OAuth flow \"${flow.id}\" replaces another extension's`, { provider: descriptor.key })\n registerOAuthProvider(flow)\n claims().set(flow, (claims().get(flow) ?? 0) + 1)\n claimed.push(flow)\n }\n }\n\n let released = false\n return () => {\n if (released)\n return\n released = true\n for (const flow of claimed) {\n const left = (claims().get(flow) ?? 1) - 1\n if (left > 0) {\n claims().set(flow, left)\n continue\n }\n claims().delete(flow)\n unregisterOAuthProvider(flow)\n }\n }\n}\n","/**\n * Host runtime bridge — the late-bound capability surface behind\n * `ctx.runtime`, `ctx.sessions`, `ctx.appendNote`, and `ctx.workspace`.\n *\n * Extension contexts are built before the host mounts, so these methods\n * route through a holder the host populates post-mount (the same idiom as\n * the HUD and prompt bridge). Detached degradation is split by stakes:\n * queries and toggles (`runtime` / `sessions` / `appendNote`) return\n * honest `null` / `false` — extensions guard on the return value instead\n * of try/catching host plumbing — while `workspace` operations REJECT,\n * because a silent no-op `writeFile` masquerading as success is worse\n * than an error (see {@link ExtensionWorkspaceSurface}).\n *\n * Notes deliberately reuse the command-output vocabulary\n * ({@link CommandOutputBlock}: full markdown or collapsed diff-colorable\n * details, or `custom` blocks bound to a registered view) and persist as\n * display-only turns — excluded from the model by the same wire guard as\n * command output. What you append is what reloads: even `custom` blocks\n * persist DATA + a mandatory markdown fallback, never rendered output —\n * the renderer registry (`ctx.tui.registerRenderer`) is a pure view\n * layer resolved at render time, so a missing/disabled renderer degrades\n * to the fallback instead of corrupting history.\n */\n\nimport type { CommandOutputBlock } from '../chat/commands'\nimport type { ModelInfo } from '../chat/providers'\nimport type { ExecResult, ExecutionContext, ExecutionHandle, ListFilesEntry, ListFilesOptions } from '../contexts'\nimport type { SessionStore } from '../session'\nimport type { SessionArtifact } from '../session/artifacts'\nimport type { JsonValue, ThinkingLevel } from '../types'\nimport type { ExtensionArtifactsSurface } from './types'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport { normalizeArtifactInput } from '../session/artifacts'\n\n/**\n * One row of `ctx.runtime.models()` — a model offered by a provider the\n * user has credentials for. `provider.key` is the registry key (stable id,\n * e.g. `anthropic`); `provider.name` is the display name. `model` mirrors\n * the host's {@link ModelInfo} (id, context window, cost, …).\n */\nexport interface ExtensionModelEntry {\n /** Provider offering the model: registry `key` and display `name`. */\n provider: { key: string, name: string }\n /** Catalog entry for the model. */\n model: ModelInfo\n}\n\n/** One row of `ctx.sessions.list()`. */\nexport interface ExtensionSessionSummary {\n /** Session id. */\n id: string\n /** Session title, when it has one. */\n title?: string\n /** Last update time in epoch ms. */\n updatedAt?: number\n}\n\n/**\n * The live `(execution, handle)` pair behind `ctx.workspace`. Resolved\n * per call — never cached by the facade — because the pair changes on\n * session switch, cwd change, and registry rebuilds; a captured pair\n * would point at a destroyed container.\n */\nexport interface ExtensionWorkspacePair {\n /** Execution context the commands run in. */\n execution: ExecutionContext\n /** Handle to the live execution. */\n handle: ExecutionHandle\n}\n\n/**\n * `ctx.workspace`'s operations already bound to a session, for a host whose\n * execution lives in another process (the TUI in daemon mode reaches the\n * session's workspace over IPC). Same contract as the facade's methods.\n */\nexport type ExtensionWorkspaceOps = Omit<ExtensionWorkspaceSurface, 'available'>\n\n/**\n * What `ctx.artifacts` resolves queries against: the host's artifacts (its\n * session store, or a client's view of a host's) plus the active session's\n * identity, so `scope: 'session'` / `'project'` can be answered without the\n * extension knowing either id.\n */\nexport interface ExtensionArtifactScope {\n /** Store the artifacts are read from and written to. */\n store: Pick<SessionStore, 'listArtifacts' | 'upsertArtifact'>\n /** Active session id. Needed to answer `scope: 'session'` queries. */\n sessionId?: string\n /** Project root of the active session. Needed to answer `scope: 'project'` queries. */\n projectRoot?: string\n /** Active run id, when a run is in flight — stamped on `record`ed artifacts. */\n runId?: string\n /**\n * Host-owned write path for `ctx.artifacts.record`. Defaults to\n * `store.upsertArtifact`; hosts override to route through a daemon RPC or\n * to fan out a change signal after the write.\n */\n record?: (artifact: SessionArtifact) => Promise<SessionArtifact | null>\n}\n\n/** What a host attaches to power the runtime surfaces. */\nexport interface ExtensionHostRuntimeImpl {\n /** Returns the active model id, or `null` when none is selected. */\n model: () => string | null\n /**\n * Model catalog across every provider the user has credentials for.\n * Optional so pre-catalog host impls keep typechecking; the facade\n * treats absence as an empty catalog.\n */\n models?: () => readonly ExtensionModelEntry[]\n /** Returns the active thinking level, or `null` when none applies. */\n thinking: () => ThinkingLevel | null\n /** Sets the thinking level. Returns whether the host accepted it. */\n setThinking: (level: ThinkingLevel) => boolean\n /** Lists the sessions the user can switch to. */\n listSessions: () => Promise<readonly ExtensionSessionSummary[]>\n /** Switches to session `id`. Resolves to whether the switch happened. */\n switchSession: (id: string) => Promise<boolean>\n /** Starts a new session. Resolves to whether one was created. */\n createSession: () => Promise<boolean>\n /** Appends output blocks to the active transcript. Returns whether the host accepted them. */\n appendNote: (blocks: readonly CommandOutputBlock[]) => boolean\n /**\n * Resolve the ACTIVE session's execution pair, spawning the handle if\n * no run has created it yet (`agent.ensureExecution()`). `null` when\n * no agent/session is live; REJECT when a live agent's spawn fails\n * (daemon down, spawn timeout) so the facade surfaces the real error\n * instead of a generic \"detached\". Optional so pre-workspace host\n * impls keep typechecking; the facade treats absence as detached.\n * A host whose execution lives in another process returns session-bound\n * {@link ExtensionWorkspaceOps} instead of a pair.\n */\n workspace?: () => Promise<ExtensionWorkspacePair | ExtensionWorkspaceOps | null>\n /**\n * Side-effect-free availability signal (\"is there a live agent?\").\n * MUST NOT spawn: `workspace()` may boot a container on first call,\n * and `ctx.workspace.available()` — a guard extensions call casually,\n * even at setup — must never pay that price to answer a boolean.\n * Optional; when absent the facade falls back to `workspace()` (with\n * its side effects — implement this on any host with heavy spawns).\n */\n workspaceAvailable?: () => boolean\n /**\n * Backing for `ctx.artifacts`. Optional so pre-artifact host impls keep\n * typechecking; the facade treats absence (or a store without artifact\n * methods) as an empty store.\n */\n artifacts?: () => ExtensionArtifactScope | null\n}\n\n/**\n * Host-level session control behind `ctx.sessions.send` / `getData` /\n * `setData`: addresses any session by id, inside a run or not.\n */\nexport interface ExtensionSessionControl {\n /**\n * Queue `prompt` as the session's next user turn: runs now when idle, after\n * the current run otherwise. `false` when the host declines: the user\n * stopped the session (Esc) and has not sent since, their own queued\n * prompts are waiting, or the session is unknown.\n */\n send: (sessionId: string, prompt: string) => Promise<boolean>\n /** One value of `extension`'s per-session state (`ctx.session`), live or stored. */\n getData: (sessionId: string, extension: string, key: string) => Promise<JsonValue | undefined>\n /** Write one value of `extension`'s per-session state; `undefined` deletes it. */\n setData: (sessionId: string, extension: string, key: string, value: JsonValue | undefined) => Promise<void>\n}\n\n/** Connects a host to the runtime surfaces extensions see as `ctx.runtime`, `ctx.sessions`, `ctx.workspace` and `ctx.artifacts`. */\nexport interface ExtensionHostRuntimeBridge {\n /**\n * Process-wide attachment — a single-session host (the TUI) attaches the\n * active session's impl once and swaps it on session change.\n */\n attach: (impl: ExtensionHostRuntimeImpl) => () => void\n /**\n * Async-scoped attachment — a multi-session host (daemon, GUI) runs a\n * command for session X inside `runWith(implForX, …)`; every\n * `ctx.runtime` / `ctx.sessions` / `ctx.workspace` call made anywhere in\n * that async tree resolves to X, even while another session's command\n * runs concurrently. Takes precedence over `attach` inside the scope.\n */\n runWith: <T>(impl: ExtensionHostRuntimeImpl, fn: () => Promise<T>) => Promise<T>\n /** Returns the implementation in effect: the innermost `runWith` scope, else the attached one, else `null`. */\n current: () => ExtensionHostRuntimeImpl | null\n /** Attach the chat host's session control (one per bridge; the newest wins). Returns a detach. */\n attachSessionControl: (control: ExtensionSessionControl) => () => void\n /** The attached session control, or `null`. */\n sessionControl: () => ExtensionSessionControl | null\n}\n\n/** Creates a bridge that hosts attach their runtime implementation to. */\nexport function createExtensionHostRuntimeBridge(): ExtensionHostRuntimeBridge {\n let impl: ExtensionHostRuntimeImpl | null = null\n let control: ExtensionSessionControl | null = null\n const scoped = new AsyncLocalStorage<ExtensionHostRuntimeImpl>()\n return {\n attachSessionControl(next) {\n control = next\n return () => {\n if (control === next)\n control = null\n }\n },\n sessionControl: () => control,\n attach(next) {\n impl = next\n return () => {\n if (impl === next)\n impl = null\n }\n },\n runWith: (next, fn) => scoped.run(next, fn),\n current: () => scoped.getStore() ?? impl,\n }\n}\n\n/** `ctx.runtime` — reads/controls over the host's active model state. */\nexport interface ExtensionRuntimeSurface {\n /** Active model id, or `null` when detached / none picked. */\n model: () => string | null\n /**\n * Every model available across the user's credentialed providers.\n * Empty when detached (headless, or before the renderer mounts).\n * Use it to build model pickers (e.g. a review command letting the\n * user pin subagents to specific models).\n */\n models: () => readonly ExtensionModelEntry[]\n /** Active thinking level, or `null` when detached / not applicable. */\n thinking: () => ThinkingLevel | null\n /** Set the thinking level. `false` when detached or unsupported by the model. */\n setThinking: (level: ThinkingLevel) => boolean\n}\n\n/** `ctx.sessions` — minimal, imperative-and-done session control. */\nexport interface ExtensionSessionsSurface {\n /** Sessions visible to the host's picker. Empty when detached. */\n list: () => Promise<readonly ExtensionSessionSummary[]>\n /** Switch to a session by id. `false` when detached or the switch failed. */\n switch: (id: string) => Promise<boolean>\n /** Start a fresh session. `false` when detached. */\n create: () => Promise<boolean>\n /**\n * Queue `prompt` as session `sessionId`'s next user turn (now when idle,\n * after the current run otherwise), from anywhere: a run-settled handler,\n * a timer. `false` when detached or declined: the user stopped that\n * session with Esc and has not sent since, or their own queued prompts\n * are waiting (they go first).\n */\n send: (sessionId: string, prompt: string) => Promise<boolean>\n /** This extension's per-session value `key` (`ctx.session`'s store) of any session, inside a run or not. `undefined` when unset. Rejects when detached. */\n getData: (sessionId: string, key: string) => Promise<JsonValue | undefined>\n /** Write this extension's per-session value `key` of any session; `undefined` deletes it. Rejects when detached. */\n setData: (sessionId: string, key: string, value: JsonValue | undefined) => Promise<void>\n}\n\n/**\n * `ctx.workspace` — shell + file operations routed through the SESSION'S\n * execution context, so extension work happens wherever the agent's tools\n * run (host process, docker, remote sandbox) with the same transport\n * output caps and containment. The curated \"do work in the workspace\"\n * subset — lifecycle methods (`spawn` / `destroy` / background tasks)\n * are deliberately absent: the pair belongs to the agent.\n *\n * Unlike `ctx.runtime` / `ctx.sessions` (queries and toggles that degrade\n * to `null` / `false`), these are imperative operations whose silent\n * failure would corrupt caller logic — a no-op `writeFile` must not look\n * like success. Detached ⇒ every method REJECTS with a clear error.\n * Guard with {@link ExtensionWorkspaceSurface.available} or try/catch.\n */\nexport interface ExtensionWorkspaceSurface {\n /** `true` when a live session's execution pair is reachable. */\n available: () => Promise<boolean>\n /**\n * Run a shell command in the workspace. Resolves with the full\n * ExecResult (non-zero exits RESOLVE — inspect `exitCode`); rejects\n * only when detached or the transport itself fails. Output is\n * byte-capped by the context's transport (10MB default).\n */\n exec: (command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal }) => Promise<ExecResult>\n /** Read a file from the workspace's filesystem (whole-file — probe `fileSize` first for anything potentially large). */\n readFile: (path: string) => Promise<string>\n /** Write a file into the workspace's filesystem (parent dirs created). */\n writeFile: (path: string, content: string) => Promise<void>\n /** List a directory in the workspace — `ListFilesOptions` semantics (`glob: true` treats `path` as a pattern). */\n listFiles: (path: string, options?: ListFilesOptions) => Promise<ListFilesEntry[]>\n /** Byte size of a file, or `null` when unknowable — same contract as `ExecutionContext.fileSize`. */\n fileSize: (path: string) => Promise<number | null>\n}\n\n/**\n * Build the `ctx.artifacts` facade over a host-runtime bridge. Read-only;\n * every call re-resolves the scope (session switches swap it). Detached,\n * or a store without artifact support, answers `[]` — the same honest\n * degradation as `ctx.sessions`.\n *\n * @param resolve - Returns the current artifact scope, or `null`/`undefined` when detached. Called on every query.\n */\nexport function createArtifactsSurface(\n resolve: () => ExtensionArtifactScope | null | undefined,\n): ExtensionArtifactsSurface {\n return {\n list: async (query) => {\n const scope = resolve()\n if (!scope?.store.listArtifacts)\n return []\n const mode = query?.scope ?? 'session'\n const base = {\n ...(query?.kind !== undefined ? { kind: query.kind } : {}),\n ...(query?.since !== undefined ? { since: query.since } : {}),\n ...(query?.limit !== undefined ? { limit: query.limit } : {}),\n }\n if (mode === 'session') {\n if (!scope.sessionId)\n return []\n return scope.store.listArtifacts({ ...base, sessionId: scope.sessionId })\n }\n if (mode === 'project') {\n if (!scope.projectRoot)\n return []\n return scope.store.listArtifacts({ ...base, projectRoot: scope.projectRoot })\n }\n return scope.store.listArtifacts(base)\n },\n find: async (kind, uri) => {\n const scope = resolve()\n if (!scope?.store.listArtifacts)\n return []\n return scope.store.listArtifacts({ kind, uri })\n },\n record: async (input) => {\n const scope = resolve()\n if (!scope?.sessionId)\n return undefined\n const write = scope.record ?? scope.store.upsertArtifact?.bind(scope.store)\n if (!write)\n return undefined\n // Extension input is a trust boundary: same normalization the loop\n // applies to tool metadata (kind shape, bounds, meta size).\n const normalized = normalizeArtifactInput(input)\n if (!normalized)\n return undefined\n const now = Date.now()\n const projectRoot = normalized.projectRoot ?? scope.projectRoot\n try {\n const stored = await write({\n id: crypto.randomUUID(),\n sessionId: scope.sessionId,\n ...(scope.runId ? { runId: scope.runId } : {}),\n kind: normalized.kind,\n uri: normalized.uri,\n label: normalized.label,\n ...(projectRoot ? { projectRoot } : {}),\n ...(normalized.meta !== undefined ? { meta: normalized.meta } : {}),\n createdAt: now,\n updatedAt: now,\n })\n return stored ?? undefined\n }\n catch {\n return undefined\n }\n },\n }\n}\n\n/**\n * Build the `ctx.workspace` facade over a host-runtime bridge. Every call\n * re-resolves the live pair (see {@link ExtensionWorkspacePair}); detached\n * calls reject with a message naming the guard.\n *\n * `isAvailable` is the host's side-effect-free availability signal,\n * tri-state by design: `true`/`false` answer directly, `undefined` means\n * \"no light signal wired\" and falls back to `resolve()` — accepting its\n * side effects (a container spawn, on heavy hosts). Wire it wherever\n * spawning is non-trivial.\n *\n * @param resolve - Returns the live execution pair, or `null` when detached. Called on every operation.\n * @param isAvailable - Side-effect-free availability signal. When omitted, availability falls back to `resolve`.\n */\nexport function createWorkspaceSurface(\n resolve: () => Promise<ExtensionWorkspacePair | ExtensionWorkspaceOps | null> | ExtensionWorkspacePair | ExtensionWorkspaceOps | null,\n isAvailable?: () => boolean | undefined,\n): ExtensionWorkspaceSurface {\n const ops = async (): Promise<ExtensionWorkspaceOps> => {\n // Spawn/transport failures from `resolve` propagate as-is — a docker\n // daemon being down must read as that, not as \"detached\".\n const resolved = await resolve()\n if (!resolved)\n throw new Error('ctx.workspace is detached (no live session or host). Guard with `await ctx.workspace.available()`.')\n return 'execution' in resolved ? pairOps(resolved) : resolved\n }\n return {\n available: async () => {\n // Prefer the host's side-effect-free signal — `resolve()` may spawn\n // a container, which a boolean guard must never trigger.\n const light = isAvailable?.()\n if (light !== undefined)\n return light\n try {\n return await resolve() !== null\n }\n catch {\n return false\n }\n },\n exec: async (command, options) => (await ops()).exec(command, options),\n readFile: async path => (await ops()).readFile(path),\n writeFile: async (path, content) => (await ops()).writeFile(path, content),\n listFiles: async (path, options) => (await ops()).listFiles(path, options),\n fileSize: async path => (await ops()).fileSize(path),\n }\n}\n\n/** The workspace operations over an in-process execution pair. */\nfunction pairOps({ execution, handle }: ExtensionWorkspacePair): ExtensionWorkspaceOps {\n return {\n exec: (command, options) => execution.exec(handle, command, options),\n readFile: path => execution.readFile(handle, path),\n writeFile: (path, content) => execution.writeFile(handle, path, content),\n listFiles: (path, options) => execution.listFiles(handle, path, options),\n fileSize: async path => await execution.fileSize?.(handle, path) ?? null,\n }\n}\n","/**\n * Extension HUD — the fire-and-forget presence an extension paints around\n * the transcript, behind `ctx.tui.notify` / `setStatus` / `setBanner` and\n * `ctx.composer.setBadge`.\n *\n * A small subscribable channel (same pattern as the log ring buffer and\n * `live-settings`): extension contexts WRITE at any point in their\n * lifetime (setup, hooks, commands, timers), the host renderer SUBSCRIBES\n * and paints:\n *\n * - status → one dim line per extension in the transcript-tail slot\n * (next to the streaming throbber), optionally with a\n * loader glyph and an auto-clear timeout,\n * - banner → one block of lines per extension in the above-prompt slot\n * (stacked with the todo indicator), optionally auto-clearing,\n * - notify → a one-shot transcript line (info/warn/error toned),\n * - badges → per session, the extension's badges above that session's\n * composer (the desktop app's badge strip), keyed by an id the\n * extension picks, at most {@link MAX_COMPOSER_BADGES} each.\n *\n * Status and banner are keyed by extension name — an extension owns exactly\n * one status and one banner (set `null` to clear), so there are no ids to\n * leak; badges add their own id, capped per extension and session.\n * Writes go through generation-scoped {@link ExtensionHudWriter}s claimed\n * per registry build: registry swaps (cwd change, `/reload`) re-claim\n * under the same names, superseding the outgoing generation so its late\n * writes, timers, and dispose-time `clear` can't touch the fresh paint.\n * Notifications raised before a renderer attaches (e.g. during pre-mount\n * `setup`) are buffered (bounded) and flushed on attach.\n *\n * Hosts without a UI simply never create a HUD — the ctx methods degrade\n * to no-ops.\n */\n\nimport type { ExtensionBadgeIcon, ExtensionBadgeTone } from './types'\n\n/** The tone a one-shot notification line (`ctx.tui.notify`) is drawn with. */\nexport type ExtensionNotifyTone = 'info' | 'warn' | 'error'\n\n/** A one-shot transcript line raised by an extension through `ctx.tui.notify`. */\nexport interface ExtensionNotification {\n /** Name of the extension that raised it. */\n extension: string\n /** Line text. */\n text: string\n /** Tone the line is drawn with. */\n tone: ExtensionNotifyTone\n}\n\n/** One extension's status-line entry. */\nexport interface ExtensionHudStatus {\n /** Status text. */\n text: string\n /** Render a working glyph next to the text (long-running work). */\n loader: boolean\n}\n\n/** Snapshot of what extensions currently show in the HUD. */\nexport interface ExtensionHudState {\n /** Extension name → its single status entry. Insertion-ordered. */\n readonly statuses: Readonly<Record<string, ExtensionHudStatus>>\n /** Extension name → its banner lines. Insertion-ordered. */\n readonly banners: Readonly<Record<string, readonly string[]>>\n /**\n * Session id → extension name → that session's status (`setStatus` with\n * `sessionId`). The GUI shows them above the session's composer.\n */\n readonly sessions: Readonly<Record<string, Readonly<Record<string, ExtensionHudStatus>>>>\n /**\n * Session id → that session's composer badges (`ctx.composer.setBadge`),\n * in the order they were first set. The GUI shows them in the session's\n * composer.\n */\n readonly badges: Readonly<Record<string, readonly ExtensionHudBadge[]>>\n}\n\n/**\n * A badge an extension shows in a session's composer, through\n * `ctx.composer.setBadge`: a short label, an optional count, icon, tone and\n * loader, and the panel a click opens. JSON-safe, it crosses IPC as is. The\n * desktop app paints it in the badge strip at the top of the composer; the\n * terminal app does not draw composer badges.\n */\nexport interface ExtensionComposerBadge {\n /** The badge's name (`Goal`, `Deploys`). Cut to 40 characters. */\n label: string\n /**\n * A count or a short value after the label (`3`, `2/10`), drawn as a count\n * badge. A string is cut to 12 characters, a number past 999 shows `999+`.\n */\n count?: number | string\n /** Leading icon. The desktop app paints the icons of its badge set only (see the extensions docs). */\n icon?: ExtensionBadgeIcon\n /** Tints the icon. Default `muted`. */\n tone?: ExtensionBadgeTone\n /** Work in progress: a running arc in place of the icon. */\n loader?: boolean\n /**\n * Something waits on the user: the badge is drawn raised in the warn tone,\n * its count pulses, and the strip scrolls it into view. Set it while the\n * wait lasts, not as decoration.\n */\n highlight?: boolean\n /** Hover text and accessible description. Cut to 200 characters. */\n title?: string\n /**\n * The panel a click opens and focuses: one this extension registered (a\n * bare id, `deploys`), a full `<extension>/<id>`, or a built-in panel\n * (`todos`, `artifacts`). Without one, or naming a panel the session does\n * not have, the badge only informs.\n */\n panel?: string\n /**\n * Left-to-right position among the session's badges, lower first (default\n * 100). The desktop app's own Inbox, Todos and Queue badges are 0, 10 and\n * 20. An order the user dragged wins over it.\n */\n order?: number\n}\n\n/** Options for {@link ExtensionHudWriter.setBadge}. */\nexport interface ExtensionComposerBadgeOptions {\n /** The session whose composer shows the badge. A badge always belongs to one conversation. */\n sessionId: string\n /** Auto-clear after this many ms. */\n timeout?: number\n}\n\n/**\n * A composer badge as the HUD state holds it: normalized (strings cut,\n * `count` as text, unknown fields dropped), with its owner and id.\n */\nexport interface ExtensionHudBadge {\n /** Name of the extension that set it. */\n extension: string\n /** The id the extension gave it, unique per extension and session. */\n id: string\n /** The badge's name. */\n label: string\n /** The count or short value after the label. */\n count?: string\n /** Leading icon. */\n icon?: ExtensionBadgeIcon\n /** Tints the icon. */\n tone?: ExtensionBadgeTone\n /** A running arc in place of the icon. */\n loader?: true\n /** Something waits on the user. */\n highlight?: true\n /** Hover text and accessible description. */\n title?: string\n /** The panel a click opens, as the extension named it (the host resolves it against its catalog). */\n panel?: string\n /** Left-to-right position, lower first. */\n order?: number\n}\n\n/** How many badges one extension may show in one session's composer. */\nexport const MAX_COMPOSER_BADGES = 8\n\nconst BADGE_TONES: readonly ExtensionBadgeTone[] = ['muted', 'subtle', 'accent', 'success', 'warn', 'danger']\nconst BADGE_ID = /^[\\w.:-]{1,64}$/\n\nfunction cut(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string')\n return undefined\n const text = value.trim()\n if (!text)\n return undefined\n return text.length > max ? `${text.slice(0, max - 1)}…` : text\n}\n\n/**\n * Reduce what an extension passed to `setBadge` to a well-formed badge:\n * strings trimmed and cut, `count` as text, unknown tones and fields\n * dropped. `null` when there is no label to show.\n */\nfunction normalizeComposerBadge(raw: unknown): Omit<ExtensionHudBadge, 'extension' | 'id'> | null {\n if (!raw || typeof raw !== 'object')\n return null\n const o = raw as Record<string, unknown>\n const label = cut(o.label, 40)\n if (!label)\n return null\n const count = typeof o.count === 'number' && Number.isFinite(o.count)\n ? (o.count > 999 ? '999+' : String(o.count))\n : cut(o.count, 12)\n const iconRaw = o.icon && typeof o.icon === 'object' ? o.icon as Record<string, unknown> : undefined\n const gui = cut(iconRaw?.gui, 128)\n const tui = cut(iconRaw?.tui, 8)\n const tone = BADGE_TONES.find(t => t === o.tone)\n const title = cut(o.title, 200)\n const panel = cut(o.panel, 200)\n return {\n label,\n ...(count !== undefined ? { count } : {}),\n ...(gui || tui ? { icon: { ...(gui ? { gui } : {}), ...(tui ? { tui } : {}) } } : {}),\n ...(tone ? { tone } : {}),\n ...(o.loader === true ? { loader: true as const } : {}),\n ...(o.highlight === true ? { highlight: true as const } : {}),\n ...(title ? { title } : {}),\n ...(panel ? { panel } : {}),\n ...(typeof o.order === 'number' && Number.isFinite(o.order) ? { order: o.order } : {}),\n }\n}\n\nconst MAX_BUFFERED_NOTIFICATIONS = 20\n\n/** Options for {@link ExtensionHudWriter.setStatus}. */\nexport interface ExtensionStatusOptions {\n /** Show a working glyph next to the text. Default false. */\n loader?: boolean\n /** Auto-clear after this many ms (e.g. a transient \"✓ done\"). */\n timeout?: number\n /**\n * Scope the status to one session: it shows with that session only, and\n * each session keeps its own (`null` with the same id clears it).\n */\n sessionId?: string\n}\n\n/** Options for {@link ExtensionHudWriter.setBanner}. */\nexport interface ExtensionBannerOptions {\n /** Auto-clear after this many ms. */\n timeout?: number\n}\n\n/** Options for {@link ExtensionHudWriter.notify}. */\nexport interface ExtensionNotifyOptions {\n /** Line tone. Default `'info'`. */\n tone?: ExtensionNotifyTone\n}\n\n/**\n * A generation-scoped writer over one extension's HUD slots. A NEWER claim\n * for the same extension supersedes this one: its status/banner writes,\n * pending auto-clear timers, and `clear` become no-ops (one-shot `notify`\n * stays live — a superseded instance saying \"shutting down\" is still worth\n * showing).\n */\nexport interface ExtensionHudWriter {\n /** Sets the extension's status-line entry. `null` clears it. */\n setStatus: (text: string | null, opts?: ExtensionStatusOptions) => void\n /** Sets the extension's banner lines. `null` clears them. */\n setBanner: (lines: readonly string[] | null, opts?: ExtensionBannerOptions) => void\n /** Raises a one-shot transcript line. */\n notify: (text: string, opts?: ExtensionNotifyOptions) => void\n /**\n * Sets one of the extension's composer badges for a session; `null`\n * clears it. Returns `false`, changing nothing, when the input is not\n * valid: no `sessionId`, an id outside `[A-Za-z0-9_.:-]{1,64}`, a badge\n * without a label, or a new id past {@link MAX_COMPOSER_BADGES}. A\n * superseded or retired writer ignores the call and returns `true`.\n */\n setBadge: (id: string, badge: ExtensionComposerBadge | null, opts: ExtensionComposerBadgeOptions) => boolean\n /**\n * Wipe this extension's footprint AND retire this writer — subsequent\n * status/banner writes become inert (`notify` stays live for farewell\n * lines). Called at registry dispose so teardown closures and leaked\n * timers can't repaint after the extension is gone. No-op once\n * superseded by a newer claim.\n */\n clear: () => void\n}\n\n/** Shared HUD state that extensions write to and the host renders: statuses, banners and one-shot notifications. */\nexport interface ExtensionHud {\n /**\n * Claim the (single) writer for an extension. Claimed once per registry\n * build; supersedes any prior claim for the same name, which is what\n * makes swap-then-dispose ordering safe.\n */\n claim: (extension: string) => ExtensionHudWriter\n /** Force-wipe an extension's footprint regardless of generation. */\n clearExtension: (extension: string) => void\n /** Drop every status and badge scoped to `sessionId` (the session was deleted). */\n clearSession: (sessionId: string) => void\n /** Returns the current statuses, banners and badges. */\n getState: () => ExtensionHudState\n /** Re-render trigger for state changes (status, banner, badge). */\n subscribe: (listener: () => void) => () => void\n /**\n * Attach the one-shot notification sink (the TUI's transcript appender).\n * Buffered pre-attach notifications flush immediately, oldest first.\n * Returns a detach fn; only one sink at a time (last attach wins).\n */\n onNotify: (sink: (notification: ExtensionNotification) => void) => () => void\n}\n\n/**\n * What a `setBadge` call asks for, validated: the session, and the badge to\n * set (`null`: clear it). `null` for a call that cannot apply.\n */\nfunction readBadgeCall(id: unknown, badge: unknown, opts: unknown): { sessionId: string, badge: Omit<ExtensionHudBadge, 'extension' | 'id'> | null } | null {\n const raw = opts && typeof opts === 'object' ? (opts as Record<string, unknown>).sessionId : undefined\n const sessionId = typeof raw === 'string' && raw.length <= 512 ? raw : ''\n if (!sessionId || typeof id !== 'string' || !BADGE_ID.test(id))\n return null\n if (badge === null)\n return { sessionId, badge: null }\n const normalized = normalizeComposerBadge(badge)\n return normalized ? { sessionId, badge: normalized } : null\n}\n\n/**\n * The HUD's composer badges: session id → `<extension>\\u0000<id>` → badge.\n * A Map per session keeps the first-set order through updates.\n */\nfunction createBadgeBook() {\n // No prototype: a session id is any string (`__proto__`, `constructor`).\n const bySession: Record<string, Map<string, ExtensionHudBadge>> = Object.create(null)\n const keyOf = (extension: string, id: string): string => `${extension}\\u0000${id}`\n return {\n /** Whether `extension` may set `id` in `sessionId`: an update, or a new badge within the cap. */\n fits(sessionId: string, extension: string, id: string): boolean {\n const byKey = bySession[sessionId]\n if (!byKey || byKey.has(keyOf(extension, id)))\n return true\n let mine = 0\n for (const badge of byKey.values()) {\n if (badge.extension === extension)\n mine++\n }\n return mine < MAX_COMPOSER_BADGES\n },\n set(sessionId: string, badge: ExtensionHudBadge): void {\n (bySession[sessionId] ??= new Map()).set(keyOf(badge.extension, badge.id), badge)\n },\n /** Drop one badge (`id`) or every badge of `extension` in `sessionId`; whether any went. */\n drop(sessionId: string, extension: string, id?: string): boolean {\n const byKey = bySession[sessionId]\n if (!byKey)\n return false\n let had = false\n for (const [key, badge] of byKey) {\n if (badge.extension === extension && (id === undefined || badge.id === id)) {\n byKey.delete(key)\n had = true\n }\n }\n if (byKey.size === 0)\n delete bySession[sessionId]\n return had\n },\n /** Drop every session's badges of `extension`; whether any went. */\n dropExtension(extension: string): boolean {\n let had = false\n for (const sessionId of Object.keys(bySession))\n had = this.drop(sessionId, extension) || had\n return had\n },\n /** Drop a session's badges; whether it had any. */\n dropSession(sessionId: string): boolean {\n const had = sessionId in bySession\n delete bySession[sessionId]\n return had\n },\n snapshot(): Record<string, ExtensionHudBadge[]> {\n return Object.fromEntries(Object.entries(bySession).map(([id, byKey]) => [id, [...byKey.values()]]))\n },\n }\n}\n\n/** Creates an empty HUD for a host to pass to the extension registry. */\nexport function createExtensionHud(): ExtensionHud {\n const statuses: Record<string, ExtensionHudStatus> = {}\n const banners: Record<string, readonly string[]> = {}\n const sessionStatuses: Record<string, Record<string, ExtensionHudStatus>> = {}\n const badges = createBadgeBook()\n /** Current writer generation per extension — the newest claim owns the slots. */\n const owners: Record<string, symbol> = {}\n let state: ExtensionHudState = { statuses: {}, banners: {}, sessions: {}, badges: {} }\n const listeners = new Set<() => void>()\n let notifySink: ((n: ExtensionNotification) => void) | null = null\n const buffered: ExtensionNotification[] = []\n\n const commit = (): void => {\n state = {\n statuses: { ...statuses },\n banners: { ...banners },\n sessions: Object.fromEntries(Object.entries(sessionStatuses).map(([id, byExtension]) => [id, { ...byExtension }])),\n badges: badges.snapshot(),\n }\n for (const listener of listeners) {\n try {\n listener()\n }\n catch {\n // A renderer callback must never break extension writes.\n }\n }\n }\n\n const dropSessionStatus = (sessionId: string, extension: string): boolean => {\n const byExtension = sessionStatuses[sessionId]\n if (!byExtension || !(extension in byExtension))\n return false\n delete byExtension[extension]\n if (Object.keys(byExtension).length === 0)\n delete sessionStatuses[sessionId]\n return true\n }\n\n const wipe = (extension: string): void => {\n let had = extension in statuses || extension in banners\n delete statuses[extension]\n delete banners[extension]\n for (const sessionId of Object.keys(sessionStatuses))\n had = dropSessionStatus(sessionId, extension) || had\n had = badges.dropExtension(extension) || had\n if (had)\n commit()\n }\n\n const emitNotification = (notification: ExtensionNotification): void => {\n if (notifySink) {\n try {\n notifySink(notification)\n }\n catch {\n // The transcript appender must never throw into extension code.\n }\n return\n }\n buffered.push(notification)\n if (buffered.length > MAX_BUFFERED_NOTIFICATIONS)\n buffered.splice(0, buffered.length - MAX_BUFFERED_NOTIFICATIONS)\n }\n\n return {\n claim(extension) {\n const generation = Symbol(extension)\n owners[extension] = generation\n const owns = (): boolean => owners[extension] === generation\n // Auto-clear timers, per slot. Rearmed on every write, cancelled on\n // clear, and generation-guarded like every other write path.\n let statusTimer: ReturnType<typeof setTimeout> | undefined\n let bannerTimer: ReturnType<typeof setTimeout> | undefined\n const sessionTimers = new Map<string, ReturnType<typeof setTimeout>>()\n // Keyed `<sessionId>\\u0000<badge id>`.\n const badgeTimers = new Map<string, ReturnType<typeof setTimeout>>()\n const disarm = (timer: ReturnType<typeof setTimeout> | undefined): void => {\n if (timer !== undefined)\n clearTimeout(timer)\n }\n\n const setSessionStatus = (sessionId: string, text: string | null, opts?: ExtensionStatusOptions): void => {\n disarm(sessionTimers.get(sessionId))\n sessionTimers.delete(sessionId)\n if (text === null || text === '')\n dropSessionStatus(sessionId, extension)\n else\n (sessionStatuses[sessionId] ??= {})[extension] = { text, loader: opts?.loader ?? false }\n commit()\n if (text && opts?.timeout !== undefined && opts.timeout > 0)\n sessionTimers.set(sessionId, setTimeout(() => owns() && setSessionStatus(sessionId, null), opts.timeout))\n }\n\n const setStatus = (text: string | null, opts?: ExtensionStatusOptions): void => {\n if (!owns())\n return\n if (opts?.sessionId)\n return setSessionStatus(opts.sessionId, text, opts)\n disarm(statusTimer)\n statusTimer = undefined\n if (text === null || text === '')\n delete statuses[extension]\n else\n statuses[extension] = { text, loader: opts?.loader ?? false }\n commit()\n if (text && opts?.timeout !== undefined && opts.timeout > 0)\n statusTimer = setTimeout(setStatus, opts.timeout, null)\n }\n\n const setBadge = (id: string, badge: ExtensionComposerBadge | null, opts: ExtensionComposerBadgeOptions): boolean => {\n if (!owns())\n return true\n const call = readBadgeCall(id, badge, opts)\n if (!call || (call.badge && !badges.fits(call.sessionId, extension, id)))\n return false\n const { sessionId } = call\n const timerKey = `${sessionId}\\u0000${id}`\n disarm(badgeTimers.get(timerKey))\n badgeTimers.delete(timerKey)\n if (!call.badge) {\n if (badges.drop(sessionId, extension, id))\n commit()\n return true\n }\n badges.set(sessionId, { extension, id, ...call.badge })\n commit()\n if (opts.timeout !== undefined && opts.timeout > 0)\n badgeTimers.set(timerKey, setTimeout(() => owns() && setBadge(id, null, { sessionId }), opts.timeout))\n return true\n }\n\n const setBanner = (lines: readonly string[] | null, opts?: ExtensionBannerOptions): void => {\n if (!owns())\n return\n disarm(bannerTimer)\n bannerTimer = undefined\n const kept = lines?.filter(line => line.length > 0)\n if (!kept || kept.length === 0)\n delete banners[extension]\n else\n banners[extension] = kept\n commit()\n if (kept && kept.length > 0 && opts?.timeout !== undefined && opts.timeout > 0)\n bannerTimer = setTimeout(setBanner, opts.timeout, null)\n }\n\n return {\n setStatus,\n setBanner,\n setBadge,\n notify(text, opts) {\n emitNotification({ extension, text, tone: opts?.tone ?? 'info' })\n },\n clear() {\n disarm(statusTimer)\n disarm(bannerTimer)\n for (const timer of [...sessionTimers.values(), ...badgeTimers.values()])\n disarm(timer)\n sessionTimers.clear()\n badgeTimers.clear()\n statusTimer = undefined\n bannerTimer = undefined\n if (owns()) {\n // Retire the generation: a disposed extension's late writes\n // (teardown closures, leaked intervals) must be inert. On a\n // swap/reload the successor already owns the name, so this\n // branch never runs and the fresh paint survives.\n delete owners[extension]\n wipe(extension)\n }\n },\n }\n },\n clearExtension: wipe,\n clearSession(sessionId) {\n const hadStatuses = sessionId in sessionStatuses\n delete sessionStatuses[sessionId]\n if (badges.dropSession(sessionId) || hadStatuses)\n commit()\n },\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n onNotify(sink) {\n notifySink = sink\n while (buffered.length > 0) {\n const next = buffered.shift()\n if (next) {\n try {\n sink(next)\n }\n catch {}\n }\n }\n return () => {\n if (notifySink === sink)\n notifySink = null\n }\n },\n }\n}\n","/**\n * `ctx.mcp` — what an extension sees of the user's MCP servers.\n *\n * The host owns discovery, the allowlist, connections, and credentials\n * ({@link ExtensionMcpHost}; `createExtensionMcpHost` in `chat/` is the\n * standard implementation every bundled host passes). This module only\n * binds a host to one extension's cwd and normalizes what crosses back.\n * Renderer-neutral: no connection code lives here.\n */\n\nimport type { ExtensionMcpServerInfo, ExtensionMcpSurface, ExtensionMcpToolResult } from './types'\n\n/** The host side of `ctx.mcp`, shared by every extension of a registry build. */\nexport interface ExtensionMcpHost {\n /** Enabled servers for `cwd` (discovered `mcps.json` entries through the allowlist). */\n servers: (cwd: string) => Promise<readonly ExtensionMcpServerInfo[]>\n /** Call one of `server`'s tools by its own (un-namespaced) name. */\n callTool: (\n cwd: string,\n server: string,\n tool: string,\n args: Record<string, unknown>,\n opts: { timeout?: number, signal?: AbortSignal },\n ) => Promise<ExtensionMcpToolResult>\n /** Close every connection this host opened. */\n dispose?: () => Promise<void>\n}\n\n/**\n * Bind `host` to an extension's cwd. Host failures in `servers()` read as \"none\".\n *\n * @param host - Host that owns MCP discovery and connections.\n * @param cwd - Working directory the extension runs in. Servers are resolved for it.\n * @param signal - Aborts when the registry is disposed. Tool calls after that reject.\n */\nexport function createMcpSurface(host: ExtensionMcpHost, cwd: string, signal: AbortSignal): ExtensionMcpSurface {\n return {\n servers: async () => {\n try {\n return await host.servers(cwd)\n }\n catch {\n return []\n }\n },\n callTool: async (server, tool, args = {}, opts = {}) => {\n if (signal.aborted)\n throw new Error('ctx.mcp is closed: the extension registry was disposed')\n const combined = opts.signal ? AbortSignal.any([opts.signal, signal]) : signal\n const result = await host.callTool(cwd, server, tool, args, { ...opts, signal: combined })\n return { text: String(result.text ?? ''), isError: result.isError === true }\n },\n }\n}\n","/**\n * Shared state + helpers for the extension context builders\n * (`register-context*.ts`): per-extension sinks, the late-call gate, the\n * safe-record-key guards, and the storage-path / provenance helpers.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { Command } from '../chat/commands'\nimport type { CompletionProvider } from '../chat/completion'\nimport type { InlineAutocompleteProvider } from '../chat/inline-autocomplete'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { Theme } from '../chat/theme'\nimport type { Logger } from '../logger'\nimport type { McpServerConfig } from '../mcp'\nimport type { LoadedPlugin } from '../plugins'\nimport type { SkillConfig } from '../skills'\nimport type { SubagentDef, SubagentOverride } from '../tools/spawn'\nimport type { ToolDef } from '../tools/types'\nimport type { ExtensionConfigCategory, StoredExtensionConfigItem } from './config'\nimport type { ExtensionPanelEntry } from './panels'\nimport type {\n ExtensionArtifactRendererEntry,\n ExtensionContext,\n ExtensionContributions,\n ExtensionCustomRendererEntry,\n ExtensionDefinition,\n ExtensionFooterHint,\n ExtensionKeybinding,\n ExtensionModal,\n ExtensionOrderedContribution,\n ExtensionPaletteCommand,\n ExtensionPaletteProviderEntry,\n ExtensionRunSettledHandler,\n ExtensionSource,\n ExtensionStorageScope,\n ExtensionToolRendererEntry,\n LoadedPluginRecord,\n} from './types'\nimport { resolve, sep } from 'node:path'\n\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\nexport function createRecord<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>\n}\n\nexport function isSafeRecordKey(key: string): boolean {\n return key.length > 0 && !DANGEROUS_RECORD_KEYS.has(key)\n}\n\nexport function warnUnsafeKey(logger: Logger, method: string, key: string): void {\n logger.warn(`${method}: refused unsafe key`, { key })\n}\n\n/**\n * Resolve a path inside one extension's storage root for the given scope.\n *\n * A module-level function (rather than a closure inside `makeContext`) so\n * `ctx.loadAgentPlugin` can derive its `${PLUGIN_DATA}` parent from the same\n * rules without depending on declaration order within the context builder.\n */\nexport function extensionStoragePath(\n paths: ExtensionContext['paths'],\n extensionName: string,\n scope: ExtensionStorageScope,\n ...segments: string[]\n): string {\n const base = scope === 'cache'\n ? paths.cacheDir\n : scope === 'data'\n ? paths.dataDir\n : scope === 'project' || scope === 'session'\n ? (paths.projectRoot ?? paths.dataDir)\n : (paths.configDir ?? paths.dataDir)\n const root = resolve(base, 'extensions', extensionName)\n const target = resolve(root, ...segments)\n // Containment guard: `..` segments must not let an extension escape its\n // own storage root. Clamp back to the root instead of handing out a path\n // outside it.\n if (target !== root && !target.startsWith(root + sep))\n return root\n return target\n}\n\n/**\n * Stamp the contributing extension onto a skill.\n *\n * `SkillConfig.source` carries the scope (project / user / inline) and is\n * defaulted here for extensions that don't set it. The extension NAME goes in\n * `metadata` under the `zidane.` prefix the Agent Skills spec reserves for\n * client-specific keys, so the skill stays spec-conformant while a host UI can\n * still say which extension shipped it.\n */\nexport function withProvenance(skill: SkillConfig, source: ExtensionSource, extensionName: string): SkillConfig {\n return {\n ...skill,\n source: skill.source ?? source,\n metadata: { ...skill.metadata, 'zidane.extension': extensionName },\n }\n}\n\n/**\n * Rejected-plugin placeholder for `ctx.loadAgentPlugin` calls that never\n * reach the loader (missing `dir`, or a late call after `setup` resolved).\n * Returning a `LoadedPlugin` with `ok: false` rather than throwing keeps the\n * method's contract uniform — callers branch on `ok`, never on exceptions.\n */\nexport function emptyPluginHandle(dir: string): LoadedPlugin {\n return {\n ok: false,\n dir,\n root: dir,\n skills: [],\n mcpServers: [],\n components: { skills: 'unsupported', mcp: 'unsupported' },\n diagnostics: [],\n }\n}\n\nexport interface GateState { open: boolean }\n\nexport interface ContextSinks {\n tools: Record<string, ToolDef>\n hooks: { [E in keyof AgentHooks]?: Array<AgentHooks[E]> }\n mcpServers: McpServerConfig[]\n skills: SkillConfig[]\n plugins: LoadedPluginRecord[]\n systemFragments: string[]\n runSettled: ExtensionRunSettledHandler[]\n contextBreakdownSections: ExtensionContributions['contextBreakdownSections'][number][]\n contextPanelSections: ExtensionContributions['contextPanelSections'][number][]\n toolResultMetadata: ExtensionContributions['toolResultMetadata'][number][]\n artifactKinds: ExtensionContributions['artifactKinds'][number][]\n references: ExtensionContributions['references'][number][]\n badgeActions: ExtensionContributions['badgeActions'][number][]\n runTransforms: ExtensionContributions['runTransforms'][number][]\n behaviorTransforms: ExtensionContributions['behaviorTransforms'][number][]\n providerMiddlewares: ExtensionContributions['providerMiddlewares'][number][]\n toolCatalogTransforms: ExtensionContributions['toolCatalogTransforms'][number][]\n mcpConfigTransforms: ExtensionContributions['mcpConfigTransforms'][number][]\n skillPolicies: ExtensionContributions['skillPolicies'][number][]\n promptTransforms: ExtensionContributions['promptTransforms'][number][]\n completionProviders: CompletionProvider[]\n inlineAutocompleteProviders: InlineAutocompleteProvider[]\n commands: Command[]\n themes: Record<string, Theme>\n agents: Record<string, AgentProfile>\n providers: Record<string, ProviderDescriptor>\n subagents: Record<string, SubagentDef>\n subagentOverrides: Record<string, SubagentOverride>\n configCategories: Record<string, ExtensionConfigCategory>\n configItems: Record<string, StoredExtensionConfigItem>\n keybindings: ExtensionKeybinding[]\n footerHints: ExtensionFooterHint[]\n modals: ExtensionModal[]\n renderers: ExtensionCustomRendererEntry[]\n toolRenderers: ExtensionToolRendererEntry[]\n artifactRenderers: ExtensionArtifactRendererEntry[]\n guiPanels: ExtensionPanelEntry[]\n /** Static palette commands; folded into one provider per extension at the end of setup. */\n paletteCommands: ExtensionPaletteCommand[]\n paletteProviders: ExtensionPaletteProviderEntry[]\n}\n\n/** Fresh, empty per-extension sinks (one set per `setup` call). */\nexport function createEmptySinks(): ContextSinks {\n return {\n tools: createRecord(),\n hooks: {},\n mcpServers: [],\n skills: [],\n plugins: [],\n systemFragments: [],\n runSettled: [],\n contextBreakdownSections: [],\n contextPanelSections: [],\n toolResultMetadata: [],\n artifactKinds: [],\n references: [],\n badgeActions: [],\n runTransforms: [],\n behaviorTransforms: [],\n providerMiddlewares: [],\n toolCatalogTransforms: [],\n mcpConfigTransforms: [],\n skillPolicies: [],\n promptTransforms: [],\n completionProviders: [],\n inlineAutocompleteProviders: [],\n commands: [],\n themes: createRecord(),\n agents: createRecord(),\n providers: createRecord(),\n subagents: createRecord(),\n subagentOverrides: createRecord(),\n configCategories: createRecord(),\n configItems: createRecord(),\n keybindings: [],\n footerHints: [],\n modals: [],\n renderers: [],\n toolRenderers: [],\n artifactRenderers: [],\n guiPanels: [],\n paletteCommands: [],\n paletteProviders: [],\n }\n}\n\n/**\n * The per-extension closure constants `makeContext` hands to each area\n * builder. Every field is fixed for the context's lifetime; `guardOpen`\n * reads the shared gate live.\n */\nexport interface ContextScope {\n definition: ExtensionDefinition\n source: ExtensionSource\n dir: string | undefined\n logger: Logger\n sinks: ContextSinks\n paths: ExtensionContext['paths']\n guardOpen: (method: string) => boolean\n ordered: <T>(value: T) => ExtensionOrderedContribution<T>\n}\n","/**\n * Agent-surface registration members of the extension context: tools, hooks,\n * MCP servers, skills, Agent Plugins, completion / commands / themes / agents\n * / subagents / providers, prompt fragments, context sections, tool-result\n * metadata and artifact kinds.\n */\n\nimport type { ContextExtensionRow, ContextExtensionSection } from '../chat/context-breakdown'\nimport type { ContextScope } from './register-context-shared'\nimport type { ExtensionContext, ExtensionContributions } from './types'\nimport { basename, isAbsolute, resolve } from 'node:path'\nimport { CRED_KEY_SLOT_SEPARATOR, isReservedCredKey } from '../chat/cred-keys'\nimport { loadAgentPlugin } from '../plugins'\nimport { ARTIFACT_KIND_PATTERN } from '../session/artifacts'\nimport { createArtifactDetectHook } from './artifact-detect'\nimport { compileReferencePattern } from './references'\nimport { emptyPluginHandle, extensionStoragePath, isSafeRecordKey, warnUnsafeKey, withProvenance } from './register-context-shared'\n\nexport function buildCoreRegistrations(scope: ContextScope) {\n const { definition, source, dir, logger, sinks, paths, guardOpen } = scope\n\n const registerTool: ExtensionContext['registerTool'] = (name, tool) => {\n if (!guardOpen('registerTool'))\n return\n if (typeof name !== 'string' || !name) {\n logger.warn('registerTool: name must be a non-empty string')\n return\n }\n if (!isSafeRecordKey(name)) {\n warnUnsafeKey(logger, 'registerTool', name)\n return\n }\n sinks.tools[name] = tool\n }\n\n const registerHook: ExtensionContext['registerHook'] = (event, handler) => {\n if (!guardOpen('registerHook'))\n return\n if (typeof handler !== 'function') {\n logger.warn('registerHook: handler must be a function', { event: String(event) })\n return\n }\n // Bucket per event. Each bucket holds handlers compatible with this\n // event's signature; the cast collapses the union member down to its\n // own array slot.\n const bucket = (sinks.hooks[event] ??= []) as Array<typeof handler>\n bucket.push(handler)\n }\n\n const registerMcpServer: ExtensionContext['registerMcpServer'] = (config) => {\n if (!guardOpen('registerMcpServer'))\n return\n if (!config || typeof config.name !== 'string') {\n logger.warn('registerMcpServer: missing name')\n return\n }\n sinks.mcpServers.push(config)\n }\n\n const registerSkill: ExtensionContext['registerSkill'] = (skill) => {\n if (!guardOpen('registerSkill'))\n return\n if (!skill || typeof skill.name !== 'string') {\n logger.warn('registerSkill: missing name')\n return\n }\n // Default the skill's source to the extension's own scope so a\n // project / user extension's skills carry the right badge —\n // `ExtensionSource` and `SkillSource` share the same vocabulary.\n //\n // `source` is the SCOPE, not the identity: two extensions both loaded from\n // `~/.agents/extensions` are indistinguishable by it. The settings Skills\n // tab needs to name the contributor, so stamp that separately under the\n // `zidane.` metadata prefix the Agent Skills spec reserves for clients.\n sinks.skills.push(withProvenance(skill, source, definition.name))\n }\n\n const loadPlugin: ExtensionContext['loadAgentPlugin'] = async (spec) => {\n const options = typeof spec === 'string' ? { dir: spec } : spec\n const requested = options?.dir\n if (typeof requested !== 'string' || requested.length === 0) {\n logger.warn('loadAgentPlugin: missing dir')\n return emptyPluginHandle('')\n }\n if (!guardOpen('loadAgentPlugin'))\n return emptyPluginHandle(requested)\n\n // Relative specs resolve against the extension's own directory so an\n // extension can vendor plugins inside itself. Inline extensions have no\n // directory — fall back to the runtime cwd.\n const target = isAbsolute(requested) ? requested : resolve(dir ?? paths.cwd, requested)\n\n const loaded = await loadAgentPlugin(target, {\n // Plugin components inherit the extension's own scope, exactly as\n // `registerSkill` does — a project extension's plugin is project-scoped.\n source,\n // `${PLUGIN_DATA}` lands inside the LOADING EXTENSION's storage root, so\n // it inherits that root's containment guard and two extensions loading a\n // same-named plugin don't silently share a data directory.\n dataRoot: extensionStoragePath(paths, definition.name, 'data', 'plugin-data'),\n ...(options.skills !== undefined ? { skills: options.skills } : {}),\n ...(options.mcp !== undefined ? { mcp: options.mcp } : {}),\n ...(options.namePrefix !== undefined ? { namePrefix: options.namePrefix } : {}),\n })\n\n // Diagnostics are logged, never thrown: a bad third-party package must\n // not fail the extension that merely tried to load it.\n for (const diagnostic of loaded.diagnostics) {\n const detail = { plugin: loaded.manifest?.name ?? target, code: diagnostic.code, ...(diagnostic.path ? { path: diagnostic.path } : {}) }\n if (diagnostic.severity === 'error')\n logger.error(`loadAgentPlugin: ${diagnostic.message}`, detail)\n else\n logger.warn(`loadAgentPlugin: ${diagnostic.message}`, detail)\n }\n // Re-check the gate AFTER the await. `sinks` arrays are the same\n // references published on `LoadedExtension.contributions`, so pushing\n // into them once the registry is sealed would mutate live state behind\n // the host's back. A load still in flight when `setup()` resolves is\n // dropped with the same warning any other late call gets.\n if (!guardOpen('loadAgentPlugin (completed after setup resolved)'))\n return loaded\n\n if (loaded.ok) {\n // A plugin's skills already carry `zidane.plugin`; adding\n // `zidane.extension` records that THIS extension is what pulled the\n // plugin in, which is the \"via extension: <name>\" origin the plugin\n // rows show. Both keys together describe the full path.\n for (const skill of loaded.skills)\n sinks.skills.push(withProvenance(skill, source, definition.name))\n for (const config of loaded.mcpServers)\n sinks.mcpServers.push(config)\n }\n // Recorded so hosts can tell a plugin's contributions apart from ones the\n // extension author wrote by hand — the settings list needs a row and a\n // toggle key per plugin, and neither is recoverable from the fan-out.\n //\n // A REJECTED plugin is recorded too, contributing nothing. Same reasoning\n // as an errored extension keeping its row: \"the plugin you pointed at is\n // broken\" is more useful than the plugin silently not existing, and disk\n // discovery surfaces rejected plugins the same way.\n sinks.plugins.push({\n name: loaded.manifest?.name ?? basename(target),\n extensionName: definition.name,\n dir: target,\n plugin: loaded,\n })\n\n logger.debug('loadAgentPlugin: registered', {\n plugin: loaded.manifest?.name,\n skills: loaded.skills.length,\n mcpServers: loaded.mcpServers.length,\n })\n return loaded\n }\n\n return { registerTool, registerHook, registerMcpServer, registerSkill, loadPlugin }\n}\n\nexport function buildCatalogRegistrations(scope: ContextScope) {\n const { logger, sinks, guardOpen } = scope\n\n const registerCompletionProvider: ExtensionContext['registerCompletionProvider'] = (provider) => {\n if (!guardOpen('registerCompletionProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || typeof provider.trigger !== 'string') {\n logger.warn('registerCompletionProvider: missing id or trigger')\n return\n }\n if (Array.from(provider.trigger).length !== 1) {\n logger.warn('registerCompletionProvider: trigger must be exactly one character', { id: provider.id, trigger: provider.trigger })\n return\n }\n sinks.completionProviders.push(provider)\n }\n\n const registerInlineAutocompleteProvider: ExtensionContext['registerInlineAutocompleteProvider'] = (provider) => {\n if (!guardOpen('registerInlineAutocompleteProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || provider.id.length === 0) {\n logger.warn('registerInlineAutocompleteProvider: missing id')\n return\n }\n if (typeof provider.suggest !== 'function') {\n logger.warn('registerInlineAutocompleteProvider: needs a suggest()', { id: provider.id })\n return\n }\n sinks.inlineAutocompleteProviders.push(provider)\n }\n\n const registerCommand: ExtensionContext['registerCommand'] = (command) => {\n if (!guardOpen('registerCommand'))\n return\n if (!command || typeof command.name !== 'string' || command.name.length === 0) {\n logger.warn('registerCommand: missing name')\n return\n }\n if (/\\s/.test(command.name) || command.name.startsWith('/')) {\n logger.warn('registerCommand: name must be a single token without a leading slash', { name: command.name })\n return\n }\n if (typeof command.run !== 'function' && !(Array.isArray(command.steps) && command.steps.length > 0)) {\n logger.warn('registerCommand: needs a run() or a non-empty steps[]', { name: command.name })\n return\n }\n if (command.aliases !== undefined) {\n const bad = !Array.isArray(command.aliases)\n || command.aliases.some(a => typeof a !== 'string' || a.length === 0 || /\\s/.test(a) || a.startsWith('/'))\n if (bad) {\n logger.warn('registerCommand: aliases must be single tokens without a leading slash', { name: command.name })\n return\n }\n }\n sinks.commands.push(command)\n }\n\n const registerTheme: ExtensionContext['registerTheme'] = (theme) => {\n if (!guardOpen('registerTheme'))\n return\n if (!theme || typeof theme.id !== 'string') {\n logger.warn('registerTheme: missing id')\n return\n }\n if (!isSafeRecordKey(theme.id)) {\n warnUnsafeKey(logger, 'registerTheme', theme.id)\n return\n }\n sinks.themes[theme.id] = theme\n }\n\n const registerAgent: ExtensionContext['registerAgent'] = (profile) => {\n if (!guardOpen('registerAgent'))\n return\n if (!profile || typeof profile.id !== 'string') {\n logger.warn('registerAgent: missing id')\n return\n }\n if (!isSafeRecordKey(profile.id)) {\n warnUnsafeKey(logger, 'registerAgent', profile.id)\n return\n }\n sinks.agents[profile.id] = profile\n }\n\n const registerSubagent: ExtensionContext['registerSubagent'] = (def) => {\n if (!guardOpen('registerSubagent'))\n return\n if (!def || typeof def.name !== 'string' || !def.name) {\n logger.warn('registerSubagent: missing name')\n return\n }\n if (!isSafeRecordKey(def.name)) {\n warnUnsafeKey(logger, 'registerSubagent', def.name)\n return\n }\n // Store keyed by name; drop the redundant `name` field from the\n // stored def so the registry map key is the single source of truth.\n const { name, ...rest } = def\n sinks.subagents[name] = rest\n }\n\n const registerSubagentOverride: ExtensionContext['registerSubagentOverride'] = (name, fn) => {\n if (!guardOpen('registerSubagentOverride'))\n return\n if (typeof name !== 'string' || !name) {\n logger.warn('registerSubagentOverride: name must be a non-empty string')\n return\n }\n if (!isSafeRecordKey(name)) {\n warnUnsafeKey(logger, 'registerSubagentOverride', name)\n return\n }\n if (typeof fn !== 'function') {\n logger.warn('registerSubagentOverride: fn must be a function', { name })\n return\n }\n // Compose within this extension in registration order — a later\n // override for the same name wraps the earlier one (`fn(prev(def))`).\n const prev = sinks.subagentOverrides[name]\n sinks.subagentOverrides[name] = prev ? def => fn(prev(def)) : fn\n }\n\n const registerProvider: ExtensionContext['registerProvider'] = (descriptor) => {\n if (!guardOpen('registerProvider'))\n return\n if (!descriptor || typeof descriptor.key !== 'string') {\n logger.warn('registerProvider: missing key')\n return\n }\n if (!isSafeRecordKey(descriptor.key)) {\n warnUnsafeKey(logger, 'registerProvider', descriptor.key)\n return\n }\n // The credential store appends `#`-qualified slots (`#alt`, `#account:N`)\n // for a provider's extra credentials, so a descriptor key containing the\n // separator could share storage with another provider's slot. Refuse here\n // rather than let the writers throw once the user authenticates.\n const credKey = descriptor.credentialFileKey ?? descriptor.key\n if (isReservedCredKey(credKey)) {\n logger.warn(\n `registerProvider: refused reserved credential key \"${credKey}\" `\n + `(\"${CRED_KEY_SLOT_SEPARATOR}\" separates a provider's extra credential slots)`,\n )\n return\n }\n sinks.providers[descriptor.key] = descriptor\n }\n\n return {\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n }\n}\n\nexport function buildContextRegistrations(scope: ContextScope) {\n const { definition, logger, sinks, guardOpen, ordered } = scope\n\n const registerSystemPromptFragment: ExtensionContext['registerSystemPromptFragment'] = (text) => {\n if (!guardOpen('registerSystemPromptFragment'))\n return\n if (typeof text !== 'string' || text.length === 0)\n return\n sinks.systemFragments.push(text)\n }\n\n const registerContextBreakdownSection: ExtensionContext['registerContextBreakdownSection'] = (contributor) => {\n if (!guardOpen('registerContextBreakdownSection'))\n return\n if (typeof contributor !== 'function') {\n logger.warn('registerContextBreakdownSection: contributor must be a function')\n return\n }\n const wrapped = (ctx => contributor(ctx)) as typeof contributor & { __zidaneExtensionName?: string }\n Object.defineProperty(wrapped, '__zidaneExtensionName', {\n value: definition.name,\n enumerable: false,\n })\n sinks.contextBreakdownSections.push(wrapped)\n }\n\n const registerContextPanelSection: ExtensionContext['registerContextPanelSection'] = (section) => {\n if (!guardOpen('registerContextPanelSection'))\n return\n if (!section || typeof section.id !== 'string' || !section.id || typeof section.title !== 'string') {\n logger.warn('registerContextPanelSection: missing id or title')\n return\n }\n if (!isSafeRecordKey(section.id)) {\n warnUnsafeKey(logger, 'registerContextPanelSection', section.id)\n return\n }\n const contributor: ExtensionContributions['contextPanelSections'][number] = async (ctx) => {\n const sections: ContextExtensionSection[] = []\n const rows = typeof section.rows === 'function' ? section.rows(ctx) : section.rows\n const loaded = section.load ? await section.load(ctx) : undefined\n if (rows && rows.length > 0) {\n sections.push({ id: section.id, label: section.title, ...(section.accentColor ? { accentColor: section.accentColor } : {}), rows })\n }\n if (!loaded)\n return sections\n if (Array.isArray(loaded)) {\n if (loaded.every(row => row && typeof row === 'object' && 'value' in row)) {\n sections.push({ id: section.id, label: section.title, ...(section.accentColor ? { accentColor: section.accentColor } : {}), rows: loaded as readonly ContextExtensionRow[] })\n }\n else {\n sections.push(...loaded as readonly ContextExtensionSection[])\n }\n return sections\n }\n sections.push(loaded as ContextExtensionSection)\n return sections\n }\n Object.defineProperty(contributor, '__zidaneExtensionName', {\n value: definition.name,\n enumerable: false,\n })\n sinks.contextPanelSections.push(contributor)\n }\n\n const registerToolResultMetadata: ExtensionContext['registerToolResultMetadata'] = (registration) => {\n if (!guardOpen('registerToolResultMetadata'))\n return\n if (!registration || typeof registration.namespace !== 'string' || !registration.namespace) {\n logger.warn('registerToolResultMetadata: missing namespace')\n return\n }\n if (!isSafeRecordKey(registration.namespace)) {\n warnUnsafeKey(logger, 'registerToolResultMetadata', registration.namespace)\n return\n }\n sinks.toolResultMetadata.push(ordered(registration))\n }\n\n const registerArtifactKind: ExtensionContext['registerArtifactKind'] = (registration) => {\n if (!guardOpen('registerArtifactKind'))\n return\n if (!registration || typeof registration.kind !== 'string' || !ARTIFACT_KIND_PATTERN.test(registration.kind)) {\n logger.warn('registerArtifactKind: kind must match [\\\\w.:-]+', { kind: String(registration?.kind) })\n return\n }\n if (typeof registration.label !== 'string' || !registration.label) {\n logger.warn('registerArtifactKind: missing label', { kind: registration.kind })\n return\n }\n for (const fn of ['detect', 'validateMeta', 'render', 'resolve', 'badge'] as const) {\n if (registration[fn] !== undefined && typeof registration[fn] !== 'function') {\n logger.warn(`registerArtifactKind: ${fn} must be a function`, { kind: registration.kind })\n return\n }\n }\n sinks.artifactKinds.push(ordered(registration))\n // `detect` rides `tool:transform` but is restricted to APPENDING to the\n // reserved `artifacts` metadata namespace — it can never touch\n // `result` / `isError`, so a detector can't alter what the model sees.\n const hook = createArtifactDetectHook(registration, { warn: (message, fields) => logger.warn(message, fields) })\n if (hook) {\n (sinks.hooks['tool:transform'] ??= []).push(hook)\n // Subagents inherit no hooks; their results arrive as the same-ref\n // `child:tool:transform` bubble (see `presets/artifacts.ts`).\n ;(sinks.hooks['child:tool:transform'] ??= []).push(hook)\n }\n }\n\n const registerReferences: ExtensionContext['registerReferences'] = (provider) => {\n if (!guardOpen('registerReferences'))\n return\n if (!provider || typeof provider.id !== 'string' || !provider.id) {\n logger.warn('registerReferences: missing id')\n return\n }\n if (typeof provider.resolve !== 'function') {\n logger.warn('registerReferences: resolve must be a function', { id: provider.id })\n return\n }\n const patterns = Array.isArray(provider.patterns)\n ? provider.patterns.filter(p => typeof p?.regex === 'string' && compileReferencePattern(p) !== null)\n : []\n if (patterns.length === 0) {\n logger.warn('registerReferences: no usable patterns', { id: provider.id })\n return\n }\n if (patterns.length < provider.patterns.length)\n logger.warn('registerReferences: dropped patterns that do not compile', { id: provider.id })\n sinks.references.push(ordered({ ...provider, patterns }))\n }\n\n const registerBadgeActions: ExtensionContext['registerBadgeActions'] = (registration) => {\n if (!guardOpen('registerBadgeActions'))\n return\n if (!registration || typeof registration.id !== 'string' || !registration.id) {\n logger.warn('registerBadgeActions: missing id')\n return\n }\n if (typeof registration.actions !== 'function' || typeof registration.run !== 'function') {\n logger.warn('registerBadgeActions: actions and run must be functions', { id: registration.id })\n return\n }\n sinks.badgeActions.push(ordered(registration))\n }\n\n const registerRunSettled: ExtensionContext['registerRunSettled'] = (handler) => {\n if (!guardOpen('registerRunSettled'))\n return\n if (typeof handler !== 'function') {\n logger.warn('registerRunSettled: handler must be a function')\n return\n }\n sinks.runSettled.push(handler)\n }\n\n return {\n registerSystemPromptFragment,\n registerRunSettled,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerArtifactKind,\n registerReferences,\n registerBadgeActions,\n }\n}\n","/**\n * Transform / policy registration members of the extension context, plus\n * the config schema registrations and the config / storage / session\n * surfaces.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { Settings } from '../chat/types'\nimport type { SessionMessage } from '../types'\nimport type { ExtensionConfigItem, StoredExtensionConfigItem } from './config'\nimport type { ContextScope } from './register-context-shared'\nimport type { ExtensionContext } from './types'\nimport { currentExtensionSession, deleteExtensionSessionState, getExtensionSessionState, setExtensionSessionState } from '../session'\nimport { isValidExtensionConfigItem, resolveExtensionConfigValue } from './config'\nimport { extensionStoragePath, isSafeRecordKey, warnUnsafeKey } from './register-context-shared'\n\nexport function buildTransformRegistrations(scope: ContextScope) {\n const { logger, sinks, guardOpen, ordered } = scope\n\n const registerRunTransform: ExtensionContext['registerRunTransform'] = (transform) => {\n if (!guardOpen('registerRunTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerRunTransform: transform must be a function')\n return\n }\n sinks.runTransforms.push(ordered(transform))\n }\n\n const registerBehaviorTransform: ExtensionContext['registerBehaviorTransform'] = (transform) => {\n if (!guardOpen('registerBehaviorTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerBehaviorTransform: transform must be a function')\n return\n }\n sinks.behaviorTransforms.push(ordered(transform))\n }\n\n const registerProviderMiddleware: ExtensionContext['registerProviderMiddleware'] = (middleware) => {\n if (!guardOpen('registerProviderMiddleware'))\n return\n if (typeof middleware !== 'function') {\n logger.warn('registerProviderMiddleware: middleware must be a function')\n return\n }\n sinks.providerMiddlewares.push(ordered(middleware))\n }\n\n const registerToolCatalogTransform: ExtensionContext['registerToolCatalogTransform'] = (transform) => {\n if (!guardOpen('registerToolCatalogTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerToolCatalogTransform: transform must be a function')\n return\n }\n sinks.toolCatalogTransforms.push(ordered(transform))\n }\n\n const registerMcpConfigTransform: ExtensionContext['registerMcpConfigTransform'] = (transform) => {\n if (!guardOpen('registerMcpConfigTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerMcpConfigTransform: transform must be a function')\n return\n }\n sinks.mcpConfigTransforms.push(ordered(transform))\n }\n\n const registerSkillPolicy: ExtensionContext['registerSkillPolicy'] = (policy) => {\n if (!guardOpen('registerSkillPolicy'))\n return\n if (!policy || typeof policy.name !== 'string' || !policy.name) {\n logger.warn('registerSkillPolicy: missing name')\n return\n }\n sinks.skillPolicies.push(ordered(policy))\n }\n\n const registerToolPolicy: ExtensionContext['registerToolPolicy'] = (policy) => {\n if (!guardOpen('registerToolPolicy'))\n return\n if (!policy || typeof policy.decide !== 'function') {\n logger.warn('registerToolPolicy: decide must be a function')\n return\n }\n const only = policy.tools ? new Set(policy.tools) : null\n // Rides `tool:gate`: earlier verdicts (built-in guards, approvals,\n // earlier policies) win, and any `patch` is re-validated against the\n // tool's inputSchema by the loop AFTER gating — an invalid patch\n // becomes a validation error, never an execution with unsound args.\n const gate: AgentHooks['tool:gate'] = async (gateCtx) => {\n if (gateCtx.block || gateCtx.result !== undefined)\n return\n if (only && !only.has(gateCtx.name))\n return\n let verdict: Awaited<ReturnType<typeof policy.decide>>\n try {\n verdict = await policy.decide({\n tool: gateCtx.name,\n input: gateCtx.input,\n turnId: gateCtx.turnId,\n callId: gateCtx.callId,\n })\n }\n catch (err) {\n // Isolation: a throwing policy must not take the run down.\n logger.warn('tool policy threw; allowing call', { tool: gateCtx.name, error: String(err) })\n return\n }\n if (verdict === undefined || verdict === 'allow')\n return\n if ('block' in verdict) {\n gateCtx.block = true\n gateCtx.reason = verdict.block\n return\n }\n // Full input replacement — in place, per the tool:gate mutation contract.\n for (const key of Object.keys(gateCtx.input))\n delete gateCtx.input[key]\n Object.assign(gateCtx.input, verdict.patch)\n }\n const bucket = (sinks.hooks['tool:gate'] ??= [])\n bucket.push(gate)\n }\n\n const registerToolResultTransform: ExtensionContext['registerToolResultTransform'] = (transform) => {\n if (!guardOpen('registerToolResultTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerToolResultTransform: transform must be a function')\n return\n }\n const hook: AgentHooks['tool:transform'] = async (transformCtx) => {\n let patch: Awaited<ReturnType<typeof transform>>\n try {\n patch = await transform({\n tool: transformCtx.name,\n input: transformCtx.input,\n result: transformCtx.result,\n isError: transformCtx.isError,\n })\n }\n catch (err) {\n logger.warn('tool result transform threw; keeping result', { tool: transformCtx.name, error: String(err) })\n return\n }\n if (!patch)\n return\n if (patch.result !== undefined)\n transformCtx.result = patch.result\n if (patch.isError !== undefined)\n transformCtx.isError = patch.isError\n }\n const bucket = (sinks.hooks['tool:transform'] ??= [])\n bucket.push(hook)\n }\n\n const registerContextTransform: ExtensionContext['registerContextTransform'] = (transform) => {\n if (!guardOpen('registerContextTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerContextTransform: transform must be a function')\n return\n }\n // Sugar over a provider middleware: rewrite `options.messages` on the\n // way into every stream call. Failures keep the original messages.\n registerProviderMiddleware(provider => ({\n ...provider,\n stream: async (streamOptions, callbacks) => {\n let next: readonly SessionMessage[] = streamOptions.messages\n try {\n next = await transform(streamOptions.messages) ?? streamOptions.messages\n }\n catch (err) {\n logger.warn('context transform threw; keeping messages', { error: String(err) })\n }\n return provider.stream(next === streamOptions.messages ? streamOptions : { ...streamOptions, messages: [...next] }, callbacks)\n },\n }))\n }\n\n const registerPromptTransform: ExtensionContext['registerPromptTransform'] = (transform) => {\n if (!guardOpen('registerPromptTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerPromptTransform: transform must be a function')\n return\n }\n sinks.promptTransforms.push(ordered(transform))\n }\n\n return {\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n }\n}\n\nexport function buildConfigMembers(scope: ContextScope, readSettings: (() => Partial<Settings> | undefined) | undefined) {\n const { definition, logger, sinks, paths, guardOpen } = scope\n\n const registerConfigCategory: ExtensionContext['registerConfigCategory'] = (category) => {\n if (!guardOpen('registerConfigCategory'))\n return\n if (!category || typeof category.id !== 'string' || !category.id || typeof category.label !== 'string') {\n logger.warn('registerConfigCategory: missing id or label')\n return\n }\n if (!isSafeRecordKey(category.id)) {\n warnUnsafeKey(logger, 'registerConfigCategory', category.id)\n return\n }\n sinks.configCategories[category.id] = category\n }\n\n const registerConfigItem: ExtensionContext['registerConfigItem'] = (categoryIdOrItem, maybeItem) => {\n if (!guardOpen('registerConfigItem'))\n return\n // Two call forms: `registerConfigItem(item)` (implicit category =\n // extension name) and `registerConfigItem(categoryId, item)`.\n const explicitCategory = typeof categoryIdOrItem === 'string' ? categoryIdOrItem : undefined\n const item = (typeof categoryIdOrItem === 'string' ? maybeItem : categoryIdOrItem) as ExtensionConfigItem | undefined\n if (!item || typeof item.key !== 'string' || !item.key) {\n logger.warn('registerConfigItem: missing item key')\n return\n }\n if (!isSafeRecordKey(item.key)) {\n warnUnsafeKey(logger, 'registerConfigItem', item.key)\n return\n }\n if (typeof item.label !== 'string') {\n logger.warn('registerConfigItem: missing label', { key: item.key })\n return\n }\n if (!isValidExtensionConfigItem(item)) {\n logger.warn('registerConfigItem: invalid item shape for its type', { key: item.key, type: (item as { type?: unknown }).type })\n return\n }\n const category = explicitCategory && explicitCategory.length > 0 ? explicitCategory : definition.name\n if (!isSafeRecordKey(category)) {\n warnUnsafeKey(logger, 'registerConfigItem: category', category)\n return\n }\n sinks.configItems[item.key] = { ...item, category } as StoredExtensionConfigItem\n }\n\n const configSurface: ExtensionContext['config'] = {\n get: (key) => {\n const item = sinks.configItems[key]\n if (!item) {\n logger.warn('config.get: unknown key, register it with registerConfigItem first', { key })\n return undefined\n }\n const persisted = readSettings?.()?.extensionConfig?.[definition.name]?.[key]\n return resolveExtensionConfigValue(item, persisted)\n },\n }\n\n const storageSurface: ExtensionContext['storage'] = {\n path: (scope, ...segments) => extensionStoragePath(paths, definition.name, scope, ...segments),\n }\n\n const sessionSurface: ExtensionContext['session'] = {\n get: key => getExtensionSessionState(currentExtensionSession(), definition.name, key),\n set: (key, value) => setExtensionSessionState(currentExtensionSession(), definition.name, key, value),\n delete: key => deleteExtensionSessionState(currentExtensionSession(), definition.name, key),\n }\n\n return { registerConfigCategory, registerConfigItem, configSurface, storageSurface, sessionSurface }\n}\n","/**\n * UI registration members of the extension context: TUI keybindings /\n * footer hints / modals / renderers, side-panel tabs, and command-palette\n * commands + providers.\n */\n\nimport type { PaletteItem } from '../chat/palette'\nimport type { ExtensionPanelEntry } from './panels'\nimport type { ContextScope } from './register-context-shared'\nimport type { ExtensionContext, ExtensionPaletteItem, ExtensionPaletteProviderEntry } from './types'\nimport {\n isBareCharExtensionBinding,\n isReservedExtensionBinding,\n parseBindingSpec,\n} from '../chat/keybindings'\nimport { isExtensionPanelScope } from './panels'\n\nexport function buildTuiRegistrations(scope: ContextScope) {\n const { definition, logger, sinks, guardOpen } = scope\n\n const registerKeybinding: ExtensionContext['tui']['registerKeybinding'] = (binding) => {\n if (!guardOpen('tui.registerKeybinding'))\n return\n if (!binding || typeof binding.id !== 'string' || typeof binding.onTrigger !== 'function') {\n logger.warn('tui.registerKeybinding: missing id or onTrigger')\n return\n }\n // The framework reserves the chords it owns — `escape` (abort /\n // back / exit semantics) and `ctrl+c` (quit). If an extension\n // bound those, the user could get trapped with no working \"out\".\n // We refuse the registration outright + log; the binding's\n // metadata (label, description) is dropped, the runtime stays\n // unaffected.\n if (binding.default && isReservedExtensionBinding(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: refused, `escape` and `ctrl+c` are reserved',\n { id: binding.id, chord: binding.default },\n )\n return\n }\n // A default that doesn't parse, or a bare printable character\n // (which would fire on every matching keystroke while the user\n // types), degrades to unbound-but-rebindable: the id stays\n // registered so the user can bind a valid chord in\n // `keybindings.json`.\n if (binding.default && !parseBindingSpec(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: default chord could not be parsed, registered unbound (set it in keybindings.json)',\n { id: binding.id, chord: binding.default },\n )\n sinks.keybindings.push({ ...binding, default: '' })\n return\n }\n if (binding.default && isBareCharExtensionBinding(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: a bare-character default would fire while typing, registered unbound (rebind with ctrl/alt/meta via keybindings.json)',\n { id: binding.id, chord: binding.default },\n )\n sinks.keybindings.push({ ...binding, default: '' })\n return\n }\n sinks.keybindings.push(binding)\n }\n\n const registerFooterHint: ExtensionContext['tui']['registerFooterHint'] = (hint) => {\n if (!guardOpen('tui.registerFooterHint'))\n return\n if (!hint || typeof hint.key !== 'string' || typeof hint.label !== 'string') {\n logger.warn('tui.registerFooterHint: missing key or label')\n return\n }\n sinks.footerHints.push(hint)\n }\n\n const registerModal: ExtensionContext['tui']['registerModal'] = (modal) => {\n if (!guardOpen('tui.registerModal'))\n return\n if (!modal || typeof modal.id !== 'string') {\n logger.warn('tui.registerModal: missing id')\n return\n }\n sinks.modals.push(modal)\n }\n\n const registerRenderer: ExtensionContext['tui']['registerRenderer'] = (id, render) => {\n if (!guardOpen('tui.registerRenderer'))\n return\n if (typeof id !== 'string' || !/^[\\w.-]+$/.test(id)) {\n logger.warn('tui.registerRenderer: id must be a bare [A-Za-z0-9_.-]+ token (it is auto-namespaced)', { id })\n return\n }\n if (typeof render !== 'function') {\n logger.warn('tui.registerRenderer: render must be a function', { id })\n return\n }\n // Namespacing makes cross-extension collisions structurally\n // impossible — blocks reference `<extension>/<id>`.\n sinks.renderers.push({ id: `${definition.name}/${id}`, render })\n }\n\n const registerToolRenderer: ExtensionContext['tui']['registerToolRenderer'] = (registration) => {\n if (!guardOpen('tui.registerToolRenderer'))\n return\n if (!registration || typeof registration.render !== 'function') {\n logger.warn('tui.registerToolRenderer: render must be a function')\n return\n }\n if (registration.tools !== undefined && (!Array.isArray(registration.tools) || registration.tools.some(t => typeof t !== 'string'))) {\n logger.warn('tui.registerToolRenderer: tools must be an array of tool names')\n return\n }\n // Owner attached here (not author-provided): `useSessionState` inside\n // an anonymous tool renderer persists under the extension's namespace.\n sinks.toolRenderers.push({ ...registration, owner: definition.name })\n }\n\n const registerArtifactRenderer: ExtensionContext['tui']['registerArtifactRenderer'] = (registration) => {\n if (!guardOpen('tui.registerArtifactRenderer'))\n return\n if (!registration || typeof registration.kind !== 'string' || !registration.kind) {\n logger.warn('tui.registerArtifactRenderer: missing kind')\n return\n }\n if (typeof registration.renderDetail !== 'function') {\n logger.warn('tui.registerArtifactRenderer: renderDetail must be a function', { kind: registration.kind })\n return\n }\n sinks.artifactRenderers.push({ ...registration, owner: definition.name })\n }\n\n return {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n registerArtifactRenderer,\n }\n}\n\nexport function buildPanelAndPaletteRegistrations(scope: ContextScope) {\n const { definition, logger, sinks, guardOpen } = scope\n\n const registerPanel: ExtensionContext['registerPanel'] = (panel) => {\n if (!guardOpen('registerPanel'))\n return\n if (!panel || typeof panel.id !== 'string' || !panel.id.trim()) {\n logger.warn('registerPanel: missing id')\n return\n }\n if (typeof panel.label !== 'string' || !panel.label.trim()) {\n logger.warn('registerPanel: missing label', { id: panel.id })\n return\n }\n if (typeof panel.load !== 'function') {\n logger.warn('registerPanel: load must be a function', { id: panel.id })\n return\n }\n if (panel.scope !== undefined && !isExtensionPanelScope(panel.scope)) {\n logger.warn('registerPanel: scope must be \\'session\\', \\'project\\' or \\'both\\'', { id: panel.id, scope: String(panel.scope) })\n return\n }\n // Namespace like renderers: bare ids get `<extension>/`, already-qualified ids pass through.\n const id = panel.id.includes('/') ? panel.id : `${definition.name}/${panel.id}`\n const idx = sinks.guiPanels.findIndex(p => p.id === id)\n const entry: ExtensionPanelEntry = { ...panel, id, owner: definition.name }\n if (idx === -1)\n sinks.guiPanels.push(entry)\n else\n sinks.guiPanels[idx] = entry // last-write-wins within one extension\n }\n\n const paletteGroup = definition.displayName ?? definition.name\n const namespaced = (id: string) => (id.includes('/') ? id : `${definition.name}/${id}`)\n\n const registerPaletteCommand: ExtensionContext['registerPaletteCommand'] = (command) => {\n if (!guardOpen('registerPaletteCommand'))\n return\n if (!command || typeof command.id !== 'string' || !command.id.trim()) {\n logger.warn('registerPaletteCommand: missing id')\n return\n }\n if (typeof command.title !== 'string' || !command.title.trim()) {\n logger.warn('registerPaletteCommand: missing title', { id: command.id })\n return\n }\n if (typeof command.run !== 'function') {\n logger.warn('registerPaletteCommand: run must be a function', { id: command.id })\n return\n }\n if (command.keybinding && !parseBindingSpec(command.keybinding)) {\n logger.warn('registerPaletteCommand: keybinding does not parse, ignoring it', { id: command.id, keybinding: command.keybinding })\n command = { ...command, keybinding: undefined }\n }\n const entry = { ...command, id: namespaced(command.id) }\n const idx = sinks.paletteCommands.findIndex(c => c.id === entry.id)\n if (idx === -1)\n sinks.paletteCommands.push(entry)\n else\n sinks.paletteCommands[idx] = entry // last-write-wins within one extension\n }\n\n const registerPaletteProvider: ExtensionContext['registerPaletteProvider'] = (provider) => {\n if (!guardOpen('registerPaletteProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || !provider.id.trim()) {\n logger.warn('registerPaletteProvider: missing id')\n return\n }\n if (typeof provider.items !== 'function' || typeof provider.run !== 'function') {\n logger.warn('registerPaletteProvider: items and run must be functions', { id: provider.id })\n return\n }\n const id = namespaced(provider.id)\n const group = provider.group?.trim() || paletteGroup\n // Item ids are namespaced on the way out (two extensions' `main` never\n // collide) and stripped on the way back in, so the author only ever\n // handles the bare ids they chose. Nested lists get the same treatment.\n const prefix = `${definition.name}/`\n const stamp = (items: readonly ExtensionPaletteItem[]): PaletteItem[] =>\n items.map(item => ({ ...item, id: namespaced(item.id), group: item.group ?? group, source: id }))\n const entry: ExtensionPaletteProviderEntry = {\n id,\n owner: definition.name,\n group,\n items: async ctx => stamp(await provider.items(ctx)),\n run: async (itemId, ctx) => {\n const result = await provider.run(itemId.startsWith(prefix) ? itemId.slice(prefix.length) : itemId, ctx)\n return result && result.kind === 'items' ? { ...result, items: stamp(result.items) } : result\n },\n ...(provider.subscribe ? { subscribe: provider.subscribe } : {}),\n }\n const idx = sinks.paletteProviders.findIndex(p => p.id === id)\n if (idx === -1)\n sinks.paletteProviders.push(entry)\n else\n sinks.paletteProviders[idx] = entry\n }\n\n return { registerPanel, registerPaletteCommand, registerPaletteProvider }\n}\n","/**\n * The per-extension `ExtensionContext` factory. Each area's members are\n * built by a sibling `register-context-*.ts` builder over a shared\n * {@link ContextScope}; the context object itself is assembled here so its\n * property order stays in one place.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { Logger } from '../logger'\nimport type { ExtensionBusChannel } from './bus'\nimport type { ExtensionHostRuntimeBridge, ExtensionSessionControl } from './host-runtime'\nimport type { ExtensionHudWriter } from './hud'\nimport type { ExtensionMcpHost } from './mcp-host'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ContextScope, ContextSinks, GateState } from './register-context-shared'\nimport type {\n ExtensionBusSurface,\n ExtensionContext,\n ExtensionDefinition,\n ExtensionOrderedContribution,\n ExtensionSource,\n} from './types'\nimport { EXTENSION_BUS_MAX_PAYLOAD_BYTES } from './bus'\nimport { createArtifactsSurface, createWorkspaceSurface } from './host-runtime'\nimport { MAX_COMPOSER_BADGES } from './hud'\nimport { createMcpSurface } from './mcp-host'\nimport { buildCatalogRegistrations, buildContextRegistrations, buildCoreRegistrations } from './register-context-agent'\nimport { buildConfigMembers, buildTransformRegistrations } from './register-context-transforms'\nimport { buildPanelAndPaletteRegistrations, buildTuiRegistrations } from './register-context-ui'\n\nexport interface MakeContextOptions {\n definition: ExtensionDefinition\n source: ExtensionSource\n dir?: string\n logger: Logger\n gate: GateState\n signal: AbortSignal\n sinks: ContextSinks\n /** Allowlist-off: `ctx.disabled` is true and runtime bridges are withheld. */\n disabled?: boolean\n /** Live settings getter backing `ctx.config.get` (see `BuildExtensionRegistryOptions.readSettings` in `./register`). */\n readSettings?: () => Partial<Settings> | undefined\n paths: ExtensionContext['paths']\n /** This extension's generation-scoped HUD writer (claimed by the registry build). */\n hudWriter?: ExtensionHudWriter\n /** Prompt bridge (see `BuildExtensionRegistryOptions.promptBridge` in `./register`). */\n promptBridge?: ExtensionPromptBridge\n /** Host runtime bridge (see `BuildExtensionRegistryOptions.hostRuntime` in `./register`). */\n hostRuntime?: ExtensionHostRuntimeBridge\n /** This extension's claimed bus channel — present iff the `'ipc'` permission is declared. */\n busChannel?: ExtensionBusChannel\n /** MCP access — present iff the `'mcp:call'` permission is declared and the host provides it. */\n mcp?: ExtensionMcpHost\n}\n\nexport function makeContext(opts: MakeContextOptions): ExtensionContext {\n const { definition, source, dir, logger, gate, signal, sinks, readSettings, paths, hudWriter, promptBridge, hostRuntime, busChannel, mcp, disabled = false } = opts\n\n function guardOpen(method: string): boolean {\n if (gate.open)\n return true\n logger.warn('registered after setup() finished, ignored', { method })\n return false\n }\n\n function ordered<T>(value: T): ExtensionOrderedContribution<T> {\n return { extensionName: definition.name, source, value }\n }\n\n /** The chat host's session control behind `ctx.sessions.getData` / `setData`; throws while none is attached. */\n function sessionControl(method: string): ExtensionSessionControl {\n const control = hostRuntime?.sessionControl()\n if (!control)\n throw new Error(`sessions.${method}: no chat host is attached (extension \"${definition.name}\").`)\n return control\n }\n\n const scope: ContextScope = { definition, source, dir, logger, sinks, paths, guardOpen, ordered }\n const { registerTool, registerHook, registerMcpServer, registerSkill, loadPlugin } = buildCoreRegistrations(scope)\n const {\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n } = buildCatalogRegistrations(scope)\n const {\n registerSystemPromptFragment,\n registerRunSettled,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerArtifactKind,\n registerReferences,\n registerBadgeActions,\n } = buildContextRegistrations(scope)\n const {\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n } = buildTransformRegistrations(scope)\n const { registerConfigCategory, registerConfigItem, configSurface, storageSurface, sessionSurface } = buildConfigMembers(scope, readSettings)\n const {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n registerArtifactRenderer,\n } = buildTuiRegistrations(scope)\n const { registerPanel, registerPaletteCommand, registerPaletteProvider } = buildPanelAndPaletteRegistrations(scope)\n\n const ctx: ExtensionContext = {\n name: definition.name,\n ...(definition.version !== undefined ? { version: definition.version } : {}),\n ...(dir !== undefined ? { dir } : {}),\n scope: source,\n logger,\n signal,\n paths,\n storage: storageSurface,\n session: sessionSurface,\n registerTool,\n registerHook,\n registerMcpServer,\n registerSkill,\n loadAgentPlugin: loadPlugin,\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n registerSystemPromptFragment,\n registerRunSettled,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerArtifactKind,\n // Read-only store access, late-bound through the host runtime like\n // `sessions` — detached / unsupported stores answer `[]`.\n artifacts: createArtifactsSurface(() => hostRuntime?.current()?.artifacts?.() ?? null),\n registerReferences,\n registerBadgeActions,\n // Runtime affordance like `workspace` (not gate-guarded); bound to this\n // extension's cwd, closed with the registry's abort signal.\n ...(mcp ? { mcp: createMcpSurface(mcp, paths.cwd, signal) } : {}),\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n registerConfigCategory,\n registerConfigItem,\n config: configSurface,\n disabled,\n tui: {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n registerArtifactRenderer,\n // HUD surface — deliberately NOT gate-guarded: unlike\n // registrations, these are runtime affordances meant to be called\n // from hooks / commands / timers for the extension's whole lifetime.\n notify: (text, opts) => {\n hudWriter?.notify(text, opts)\n },\n setStatus: (text, opts) => {\n hudWriter?.setStatus(text, opts)\n },\n setBanner: (lines, opts) => {\n hudWriter?.setBanner(lines, opts)\n },\n },\n // Like the HUD surface it writes to: a runtime affordance, not gate-guarded.\n composer: {\n setBadge: (id, badge, opts) => {\n if (hudWriter && !hudWriter.setBadge(id, badge, opts)) {\n logger.warn(\n `composer.setBadge: ignored. It needs opts.sessionId, an id of letters, digits, _ . : - (64 at most), a badge with a label, and at most ${MAX_COMPOSER_BADGES} badges per session`,\n { id: String(id) },\n )\n }\n },\n },\n registerPanel,\n registerPaletteCommand,\n registerPaletteProvider,\n gui: {\n registerPanel, // deprecated alias\n },\n // Runtime affordances like the HUD surface — not gate-guarded.\n submitPrompt: (text, submitOpts) => promptBridge?.submit(text, submitOpts) ?? false,\n runtime: {\n model: () => hostRuntime?.current()?.model() ?? null,\n models: () => hostRuntime?.current()?.models?.() ?? [],\n thinking: () => hostRuntime?.current()?.thinking() ?? null,\n setThinking: level => hostRuntime?.current()?.setThinking(level) ?? false,\n },\n sessions: {\n list: async () => hostRuntime?.current()?.listSessions() ?? [],\n switch: async id => hostRuntime?.current()?.switchSession(id) ?? false,\n create: async () => hostRuntime?.current()?.createSession() ?? false,\n // Host-level, by id: usable outside a run (run-settled handlers, timers).\n send: async (sessionId, prompt) => hostRuntime?.sessionControl()?.send(sessionId, prompt) ?? false,\n getData: async (sessionId, key) => sessionControl('getData').getData(sessionId, definition.name, key),\n setData: async (sessionId, key, value) => sessionControl('setData').setData(sessionId, definition.name, key, value),\n },\n appendNote: (blocks) => {\n // Ergonomics: bare renderer ids in custom blocks are auto-prefixed\n // with THIS extension's namespace (command `emit` requires the full\n // form — the executor has no extension identity there).\n const namespaced = blocks.map(block =>\n block.custom && !block.custom.renderer.includes('/')\n ? { ...block, custom: { ...block.custom, renderer: `${definition.name}/${block.custom.renderer}` } }\n : block)\n return hostRuntime?.current()?.appendNote(namespaced) ?? false\n },\n // Shell + file ops through the SESSION'S execution context — the same\n // transports (and output caps / containment) the agent's tools use,\n // resolved live per call. Not gate-guarded: a runtime affordance for\n // the extension's whole lifetime, like the HUD and submitPrompt.\n workspace: createWorkspaceSurface(\n async () => await hostRuntime?.current()?.workspace?.() ?? null,\n () => hostRuntime?.current()?.workspaceAvailable?.(),\n ),\n // Cross-window bus — permission-gated at claim time: no `'ipc'` in the\n // definition means `busChannel` is undefined and `ctx.bus` stays absent\n // entirely, so the emit path is unreachable. Like the HUD, a runtime\n // affordance (not gate-guarded); the channel itself stamps the\n // extension id from the registration name.\n ...(busChannel\n ? {\n bus: {\n emit: (payload, emitOpts) => {\n if (!busChannel.emit(payload, emitOpts))\n logger.warn(`bus.emit dropped: payload not JSON-serializable or over ${EXTENSION_BUS_MAX_PAYLOAD_BYTES} bytes`)\n },\n onMessage: fn => busChannel.onMessage(fn),\n } satisfies ExtensionBusSurface,\n }\n : {}),\n }\n return ctx\n}\n","/**\n * Run each loaded extension's `setup(ctx)` and fold its contributions\n * into a single {@link ExtensionRegistry}.\n *\n * Two contracts the rest of zidane relies on:\n *\n * - **Isolation** — a throw inside `setup` lands on the offending\n * extension as `LoadedExtension.error` (so the Settings panel can\n * surface it) but never propagates. Every other extension still\n * gets to register.\n * - **Late-call gating** — once `setup` resolves, the context's\n * registration methods become no-ops. Extensions that schedule\n * work for later (a `setTimeout`, a Promise resolved by an\n * external event) can't smuggle contributions in after the\n * registry is sealed. A `logger.warn` is emitted so the author\n * can spot the misuse.\n *\n * Cross-extension collisions are resolved here too: last-write-wins\n * for keyed maps (tools / themes / agents / providers), append-order\n * for arrays (mcpServers / skills / system fragments / completion\n * providers / TUI / GUI surfaces). Hook handlers are bucketed per\n * event so every extension fires.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { Command } from '../chat/commands'\nimport type { CompletionProvider } from '../chat/completion'\nimport type { InlineAutocompleteProvider } from '../chat/inline-autocomplete'\nimport type { PaletteContext, PaletteItem } from '../chat/palette'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { Theme } from '../chat/theme'\nimport type { Settings } from '../chat/types'\nimport type { Logger, LogSink } from '../logger'\nimport type { McpServerConfig } from '../mcp'\nimport type { SkillConfig } from '../skills'\nimport type { SubagentDef, SubagentOverride } from '../tools/spawn'\nimport type { ToolDef } from '../tools/types'\nimport type { ExtensionBus, ExtensionBusChannel } from './bus'\nimport type {\n ExtensionConfigCategory,\n StoredExtensionConfigItem,\n} from './config'\nimport type { ExtensionHostRuntimeBridge } from './host-runtime'\nimport type { ExtensionHud, ExtensionHudWriter } from './hud'\nimport type { ExtensionMcpHost } from './mcp-host'\nimport type { ExtensionPanelEntry } from './panels'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ContextSinks, GateState } from './register-context-shared'\nimport type {\n ExtensionArtifactRendererEntry,\n ExtensionContext,\n ExtensionContributions,\n ExtensionCustomRendererEntry,\n ExtensionDefinition,\n ExtensionFooterHint,\n ExtensionKeybinding,\n ExtensionModal,\n ExtensionOrderedContribution,\n ExtensionPaletteCommand,\n ExtensionPaletteProviderEntry,\n ExtensionRegistry,\n ExtensionRunSettledHandler,\n ExtensionToolRendererEntry,\n LoadedExtension,\n LoadedPluginRecord,\n} from './types'\nimport { homedir } from 'node:os'\nimport {\n DEFAULT_KEYBINDINGS,\n parseBindingSpec,\n} from '../chat/keybindings'\nimport { resolveStorageDirs } from '../chat/xdg'\nimport { errorMessage } from '../errors'\nimport { consoleSink, createLogger } from '../logger'\nimport { createExtensionBus } from './bus'\nimport { claimProviderOAuthFlows } from './provider-oauth'\nimport { makeContext } from './register-context'\nimport { createEmptySinks, createRecord } from './register-context-shared'\nimport { withTimeout } from './with-timeout'\n\n/** Options for {@link buildExtensionRegistry}. */\nexport interface BuildExtensionRegistryOptions {\n /**\n * Names the user has explicitly disabled. Discovered + present in\n * `loaded` but skipped during setup; surfaces in `inactive` so the\n * Settings panel can list them.\n */\n disabled?: ReadonlySet<string>\n /** Maximum time one extension's setup may take before it is marked inactive. Default: 10000 ms. */\n setupTimeoutMs?: number\n /**\n * Override the log sink. Defaults to a `consoleSink` writing to\n * stderr (which the TUI suppresses to keep the renderer clean —\n * only `ZIDANE_DEBUG=1` surfaces the lines).\n */\n sink?: LogSink\n /**\n * Extension HUD backing `ctx.tui.notify` / `setStatus` / `setBanner`.\n * Hosts with a live renderer (the TUI) pass one; headless hosts omit it\n * and the methods no-op. Extension footprints are wiped from it on\n * disposal.\n */\n hud?: ExtensionHud\n /**\n * Prompt bridge backing `ctx.submitPrompt`. Hosts with a submit pipeline\n * attach their handler post-mount; omitted → the method returns `false`.\n */\n promptBridge?: ExtensionPromptBridge\n /**\n * Host runtime bridge backing `ctx.runtime` / `ctx.sessions` /\n * `ctx.appendNote`. Late-bound: the host attaches its implementation\n * post-mount; detached methods return `null`/`false`.\n */\n hostRuntime?: ExtensionHostRuntimeBridge\n /**\n * MCP access backing `ctx.mcp` for extensions declaring the `'mcp:call'`\n * permission (`createExtensionMcpHost` builds the standard one). Omitted\n * → `ctx.mcp` is absent.\n */\n mcp?: ExtensionMcpHost\n /**\n * Extension bus backing `ctx.bus` for extensions declaring the `'ipc'`\n * permission. Hosts with a cross-window transport pass their shared bus\n * (daemon environment, TUI, GUI main); omitted → a build-local bus is\n * created so loopback delivery still works with zero IPC.\n */\n bus?: ExtensionBus\n /**\n * Live settings getter backing `ctx.config.get`. `setup()` runs at\n * boot BEFORE the host's live settings are wired, so the context can't\n * capture a snapshot — it reads through this getter on every call.\n * During `setup` it typically returns a boot snapshot (or `undefined`);\n * inside hooks / tools it returns the host's live `Settings`. Omitted\n * → `ctx.config.get` always returns item defaults.\n */\n readSettings?: () => Partial<Settings> | undefined\n /** Runtime cwd used for ctx.paths / ctx.storage helpers. */\n cwd?: string\n /** Project root exposed as `ctx.paths.projectRoot`. */\n projectRoot?: string\n /** Storage directories exposed through `ctx.paths`. Default: resolved from `prefix` and the platform conventions. */\n storageDirs?: {\n configDir?: string\n dataDir: string\n stateDir?: string\n cacheDir: string\n }\n /** Storage prefix used to resolve the storage directories. Default: `ZIDANE_PREFIX`, else `.zidane`. */\n prefix?: string\n}\n\nconst DEFAULT_TEARDOWN_TIMEOUT_MS = 5_000\n\n/**\n * Build the merged registry. `loaded` typically comes from\n * `discoverExtensions()` plus any inline ones via `inlineLoadedExtension`.\n *\n * Disabled extensions stay in `inactive` (alongside any load errors)\n * so the Settings panel shows the full picture. The collapse-to-empty\n * `EMPTY_EXTENSION_REGISTRY` is used for the \"nothing loaded\" case.\n *\n * @param loaded - Discovered and inline extensions.\n * @param options - Options for logging, the HUD and bridges, storage paths and setup limits.\n * @example\n * ```ts\n * import { buildExtensionRegistry, discoverExtensions } from 'zidane/extensions'\n *\n * const loaded = await discoverExtensions({ cwd: process.cwd() })\n * const registry = await buildExtensionRegistry(loaded, { cwd: process.cwd() })\n * console.log(Object.keys(registry.tools))\n * ```\n */\nexport async function buildExtensionRegistry(\n loaded: readonly LoadedExtension[],\n options: BuildExtensionRegistryOptions = {},\n): Promise<ExtensionRegistry> {\n const sink = options.sink ?? consoleSink({ minLevel: 'warn' })\n const disabled = options.disabled ?? new Set<string>()\n const setupTimeoutMs = options.setupTimeoutMs ?? 10_000\n\n const active: LoadedExtension[] = []\n const inactive: LoadedExtension[] = []\n const candidates: SetupCandidate[] = []\n /** Generation-scoped HUD writers, cleared (guarded) at dispose. */\n const hudWriters: ExtensionHudWriter[] = []\n /**\n * Bus channels claimed by THIS build (`'ipc'`-permitted extensions only),\n * closed at dispose so a retired generation's subscribers are dropped.\n * Without a host-supplied bus, a build-local one keeps loopback working.\n */\n const bus = options.bus ?? createExtensionBus()\n const busChannels: ExtensionBusChannel[] = []\n const extensionNameOwners = new Set<string>()\n\n for (const entry of loaded) {\n if (extensionNameOwners.has(entry.name)) {\n inactive.push({ ...entry, error: `Duplicate extension name \"${entry.name}\" ignored; the earlier registration wins.` })\n continue\n }\n // Only entries that actually loaded a definition claim the name —\n // a disk entry that failed to load (disabled, untrusted, broken)\n // must not knock a later loadable extension into `inactive` as a\n // \"duplicate\".\n if (!entry.definition) {\n inactive.push(entry)\n continue\n }\n if (entry.definition.apiVersion !== undefined && entry.definition.apiVersion !== 1) {\n inactive.push({ ...entry, error: `Unsupported extension API version \"${entry.definition.apiVersion}\". This runtime supports apiVersion 1.` })\n continue\n }\n extensionNameOwners.add(entry.name)\n // Disabled (allowlist-off) extensions still run `setup()` — they are\n // trusted code that already imported — so their config schema is\n // declared and browsable in Settings before enabling. They classify\n // as `inactive` below, which keeps every behavioral contribution out\n // of the registry's active surface.\n const isDisabled = disabled.has(entry.name)\n\n const logger = createLogger(sink, { extension: entry.name })\n const gate: GateState = { open: true }\n // Per-extension sinks — captured on the LoadedExtension as\n // `contributions` so the host can filter on enable/disable\n // without re-running setup.\n const perExtSinks = createEmptySinks()\n const abort = new AbortController()\n // Generation-scoped writer: this registry build claims the extension's\n // HUD slots; a later build re-claims them, so THIS generation's\n // late writes / dispose-time clear can't touch the successor's paint.\n // Disabled extensions get NO writer: their setup runs (to declare the\n // config schema) but an inactive extension painting status lines or\n // transcript notifications would contradict its toggle.\n // Disabled extensions get NO runtime bridges: their setup runs only to\n // declare the config schema, so the HUD writer, prompt bridge, and host\n // runtime are all withheld. The context surfaces already `?.`-guard\n // these, so every runtime affordance (`tui.*`, `submitPrompt`,\n // `runtime`, `sessions`, `workspace`) degrades to an inert no-op — an\n // inactive extension can't paint status, inject prompts, or touch the\n // session. `ctx.disabled` lets the author also gate side-effecting\n // setup work (network probes, cache warm-up) that doesn't go through\n // ctx at all.\n const hudWriter = isDisabled ? undefined : options.hud?.claim(entry.name)\n if (hudWriter)\n hudWriters.push(hudWriter)\n // Bus channel: permission-gated at the source — no `'ipc'` in the\n // definition means no channel, so `ctx.bus` is undefined and no emit\n // path exists. The extension id is stamped from the REGISTRATION name\n // here (spoof-proof: never caller-supplied).\n const busChannel = !isDisabled && entry.definition.permissions?.includes('ipc')\n ? bus.claim(entry.name)\n : undefined\n if (busChannel)\n busChannels.push(busChannel)\n const ctx = makeContext({\n definition: entry.definition,\n source: entry.source,\n dir: entry.dir,\n logger,\n gate,\n signal: abort.signal,\n sinks: perExtSinks,\n disabled: isDisabled,\n ...(options.readSettings !== undefined ? { readSettings: options.readSettings } : {}),\n ...(hudWriter !== undefined ? { hudWriter } : {}),\n ...(busChannel !== undefined ? { busChannel } : {}),\n ...(!isDisabled && options.promptBridge !== undefined ? { promptBridge: options.promptBridge } : {}),\n ...(!isDisabled && options.hostRuntime !== undefined ? { hostRuntime: options.hostRuntime } : {}),\n // Permission-gated like the bus: no `'mcp:call'`, no `ctx.mcp`.\n ...(!isDisabled && options.mcp !== undefined && entry.definition.permissions?.includes('mcp:call') ? { mcp: options.mcp } : {}),\n paths: resolveContextPaths(options),\n })\n candidates.push({\n entry: { ...entry, definition: entry.definition },\n logger,\n gate,\n abort,\n sinks: perExtSinks,\n ctx,\n setupTimeoutMs,\n disabled: isDisabled,\n })\n }\n\n const settled = await Promise.all(candidates.map(setupExtension))\n\n // Cross-extension state for collision warnings. Both maps are\n // populated in discovery order after setup settles, preserving the\n // original precedence while letting slow setup calls run in parallel.\n const keybindingIdOwners = new Map<string, string>()\n const keybindingChordOwners = new Map<string, { ext: string, id: string }>()\n const mcpServerNameOwners = new Map<string, string>()\n const rendererIdOwners = new Map<string, string>()\n\n for (const result of settled) {\n const { entry, logger, sinks: perExtSinks } = result.candidate\n if (result.ok) {\n // Disabled extensions skip the collision bookkeeping below — their\n // contributions never merge, so warning about (or claiming) chords,\n // ids, and server names on behalf of an inactive extension would\n // shadow the active ones that actually render.\n if (result.candidate.disabled) {\n inactive.push({ ...entry, contributions: captureContributions(perExtSinks, entry), setupRan: true })\n continue\n }\n // Warn on keybinding collisions so a vibe-coded extension\n // doesn't ship an unreachable shortcut. Three buckets, all\n // resolved by \"first claimant wins, later loses\":\n // 1. Collisions with a built-in action's default chord — the\n // TUI dispatcher matches built-ins first and short-circuits.\n // 2. Collisions on the binding `id` (within or across\n // extensions) — `mergeContributions` keeps the first\n // registration in `active` order.\n // 3. Collisions on the default chord — the dispatcher would\n // fire the first match on that chord.\n // Users can resolve any of these via `keybindings.json`; we only\n // flag the AUTHOR's chosen default since that's what the\n // extension's docs likely advertise. Warnings only — the actual\n // id dedupe lives in `mergeContributions` so toggle-time\n // re-merges resolve against whatever subset is active.\n for (const binding of perExtSinks.keybindings) {\n const idOwner = keybindingIdOwners.get(binding.id)\n if (idOwner === entry.name) {\n logger.warn(\n 'duplicate keybinding id in this extension, keeping the first',\n { id: binding.id },\n )\n continue\n }\n if (idOwner) {\n logger.warn(\n 'keybinding id already registered by another extension, keeping the first',\n { id: binding.id, earlierExtension: idOwner },\n )\n continue\n }\n if (binding.default) {\n const builtin = findBuiltinByChord(binding.default)\n if (builtin) {\n logger.warn(\n 'keybinding default is taken by a built-in action, rebind it in keybindings.json',\n { id: binding.id, chord: binding.default, builtin },\n )\n }\n const normalized = canonicalChord(binding.default)\n const chordOwner = normalized ? keybindingChordOwners.get(normalized) : undefined\n if (chordOwner && chordOwner.ext === entry.name) {\n logger.warn(\n 'keybinding default is used twice in this extension, only the first fires',\n { id: binding.id, chord: binding.default, earlierId: chordOwner.id },\n )\n }\n else if (chordOwner) {\n logger.warn(\n 'keybinding default is taken by another extension, keeping the first',\n { id: binding.id, chord: binding.default, earlierExtension: chordOwner.ext, earlierId: chordOwner.id },\n )\n }\n if (normalized && !chordOwner)\n keybindingChordOwners.set(normalized, { ext: entry.name, id: binding.id })\n }\n keybindingIdOwners.set(binding.id, entry.name)\n }\n // Cross-extension MCP server name collisions — the merged preset\n // dedupes by name keeping the LATEST entry (see\n // `buildExtensionPreset`), so the later registration wins and the\n // earlier extension's server is shadowed. Same-extension\n // re-registrations are treated as deliberate overrides.\n for (const server of perExtSinks.mcpServers) {\n const owner = mcpServerNameOwners.get(server.name)\n if (owner && owner !== entry.name) {\n logger.warn(\n 'mcp server name already registered by another extension, using this one',\n { name: server.name, earlierExtension: owner },\n )\n }\n mcpServerNameOwners.set(server.name, entry.name)\n }\n // Renderer-id collision warning — same registration-time split as\n // keybindings (merge dedupes silently; this is the author-facing\n // signal). Ids are namespaced, so only same-extension duplicates\n // can collide. Tool-renderer OVERLAP is deliberately not warned:\n // renderers chain (consulted in registration order, first non-null\n // wins, `null` falls through), so several extensions claiming the\n // same tool is a designed composition, not a conflict.\n for (const renderer of perExtSinks.renderers) {\n const owner = rendererIdOwners.get(renderer.id)\n if (owner)\n logger.warn('duplicate renderer id, keeping the first', { id: renderer.id })\n else\n rendererIdOwners.set(renderer.id, entry.name)\n }\n active.push({ ...entry, contributions: captureContributions(perExtSinks, entry), setupRan: true })\n }\n else {\n const message = result.error\n logger.error('setup threw', { error: message })\n // A DISABLED extension that throws during its schema-only setup run\n // stays toggleable: it isn't a load failure (its contributions were\n // going to be dropped regardless), and enabling triggers a full\n // reload that retries setup with real runtime bridges. Recording\n // `error` here would strand it in the errored bucket, which the\n // Settings toggle list excludes — making it impossible to enable.\n if (result.candidate.disabled)\n inactive.push({ ...entry, setupRan: true })\n else\n inactive.push({ ...entry, error: message, setupRan: true })\n }\n }\n\n // Providers' OAuth flows ride their contribution: registered for the active\n // extensions now, released on dispose (see ./provider-oauth).\n const releaseOAuthFlows = claimProviderOAuthFlows(active, sink)\n\n let disposed = false\n const registry = mergeContributions(active, inactive, {\n dispose: async () => {\n if (disposed)\n return\n disposed = true\n releaseOAuthFlows()\n // Wipe every extension's HUD footprint (status/banner) before\n // teardown — a disposed extension must leave no painted residue.\n // Writer `clear` is generation-guarded: if a newer registry already\n // re-claimed a name (swap/reload), its fresh paint survives.\n for (const writer of hudWriters)\n writer.clear()\n // Same generation guard as HUD writers: `close` no-ops when a newer\n // build already re-claimed the channel, so a reload's fresh\n // subscribers survive this (older) dispose.\n for (const channel of busChannels)\n channel.close()\n await disposeLoadedExtensions([...active, ...inactive], sink)\n },\n })\n return registry\n}\n\ninterface SetupCandidate {\n entry: LoadedExtension & { definition: ExtensionDefinition }\n logger: Logger\n gate: GateState\n abort: AbortController\n sinks: ContextSinks\n ctx: ExtensionContext\n setupTimeoutMs: number\n /** Allowlist-off: setup runs, contributions stay out of the active surface. */\n disabled: boolean\n}\n\n/** Freeze one extension's sinks into its `contributions` snapshot. */\nfunction captureContributions(perExtSinks: ContextSinks, entry: LoadedExtension): ExtensionContributions {\n return {\n tools: perExtSinks.tools,\n hooks: perExtSinks.hooks,\n mcpServers: perExtSinks.mcpServers,\n skills: perExtSinks.skills,\n plugins: perExtSinks.plugins,\n systemFragments: perExtSinks.systemFragments,\n runSettled: perExtSinks.runSettled,\n contextBreakdownSections: perExtSinks.contextBreakdownSections,\n contextPanelSections: perExtSinks.contextPanelSections,\n toolResultMetadata: perExtSinks.toolResultMetadata,\n artifactKinds: perExtSinks.artifactKinds,\n references: perExtSinks.references,\n badgeActions: perExtSinks.badgeActions,\n runTransforms: perExtSinks.runTransforms,\n behaviorTransforms: perExtSinks.behaviorTransforms,\n providerMiddlewares: perExtSinks.providerMiddlewares,\n toolCatalogTransforms: perExtSinks.toolCatalogTransforms,\n mcpConfigTransforms: perExtSinks.mcpConfigTransforms,\n skillPolicies: perExtSinks.skillPolicies,\n promptTransforms: perExtSinks.promptTransforms,\n completionProviders: perExtSinks.completionProviders,\n inlineAutocompleteProviders: perExtSinks.inlineAutocompleteProviders,\n commands: perExtSinks.commands,\n themes: perExtSinks.themes,\n agents: perExtSinks.agents,\n providers: perExtSinks.providers,\n subagents: perExtSinks.subagents,\n subagentOverrides: perExtSinks.subagentOverrides,\n configCategories: perExtSinks.configCategories,\n configItems: perExtSinks.configItems,\n tui: {\n keybindings: perExtSinks.keybindings,\n footerHints: perExtSinks.footerHints,\n modals: perExtSinks.modals,\n renderers: perExtSinks.renderers,\n toolRenderers: perExtSinks.toolRenderers,\n artifactRenderers: perExtSinks.artifactRenderers,\n },\n gui: { panels: perExtSinks.guiPanels },\n panels: perExtSinks.guiPanels,\n palette: [\n ...(perExtSinks.paletteCommands.length > 0\n ? [staticPaletteProvider(entry.name, entry.definition?.displayName ?? entry.name, perExtSinks.paletteCommands)]\n : []),\n ...perExtSinks.paletteProviders,\n ],\n }\n}\n\n/**\n * Static `registerPaletteCommand` entries become one provider per\n * extension: `items()` projects the metadata (JSON-safe, `when` resolved\n * against the context into `disabled`), `run` dispatches by id.\n */\nfunction staticPaletteProvider(owner: string, group: string, commands: readonly ExtensionPaletteCommand[]): ExtensionPaletteProviderEntry {\n const byId = new Map(commands.map(c => [c.id, c]))\n const id = `${owner}/commands`\n return {\n id,\n owner,\n group,\n items: (ctx: PaletteContext): PaletteItem[] => commands.map((c) => {\n const disabled = c.when === 'session' && !ctx.sessionId\n ? 'Needs an open session'\n : c.when === 'idle' && (!ctx.sessionId || ctx.busy)\n ? (ctx.sessionId ? 'Wait for the run to finish' : 'Needs an open session')\n : undefined\n return {\n id: c.id,\n title: c.title,\n group: c.group?.trim() || group,\n source: id,\n ...(c.description ? { description: c.description } : {}),\n ...(c.keywords ? { keywords: c.keywords } : {}),\n ...(c.icon ? { icon: c.icon } : {}),\n ...(c.keybinding ? { keybinding: c.keybinding } : {}),\n ...(disabled ? { disabled } : {}),\n }\n }),\n run: async (itemId, ctx) => {\n const command = byId.get(itemId)\n if (!command)\n return undefined\n const result = await command.run(ctx)\n return result && result.kind === 'items'\n ? { ...result, items: result.items.map(item => ({ ...item, id: `${owner}/${item.id}`, group: item.group ?? group, source: id })) }\n : result\n },\n }\n}\n\ntype SetupResult\n = | { ok: true, candidate: SetupCandidate }\n | { ok: false, candidate: SetupCandidate, error: string }\n\nasync function setupExtension(candidate: SetupCandidate): Promise<SetupResult> {\n const timeoutMessage = `setup timed out after ${candidate.setupTimeoutMs}ms`\n try {\n await withTimeout(\n Promise.resolve(candidate.entry.definition.setup(candidate.ctx)),\n candidate.setupTimeoutMs,\n timeoutMessage,\n () => {\n candidate.gate.open = false\n candidate.abort.abort(new Error(timeoutMessage))\n },\n )\n return { ok: true, candidate }\n }\n catch (err) {\n return { ok: false, candidate, error: errorMessage(err) }\n }\n finally {\n candidate.gate.open = false\n }\n}\n\nasync function disposeLoadedExtensions(entries: readonly LoadedExtension[], sink: LogSink): Promise<void> {\n const seen = new Set<string>()\n await Promise.all(entries.map(async (entry) => {\n if (!entry.setupRan || !entry.definition?.teardown || seen.has(entry.name))\n return\n seen.add(entry.name)\n const logger = createLogger(sink, { extension: entry.name })\n try {\n await withTimeout(\n Promise.resolve(entry.definition.teardown()),\n DEFAULT_TEARDOWN_TIMEOUT_MS,\n `teardown timed out after ${DEFAULT_TEARDOWN_TIMEOUT_MS}ms`,\n )\n }\n catch (err) {\n logger.error('teardown threw', { error: errorMessage(err) })\n }\n }))\n}\n\n/** Append the contributions whose `value.id` is new (first registration wins, in `active` order). */\nfunction pushFirstById<T extends { value: { id: string } }>(into: T[], seen: Set<string>, items: readonly T[]): void {\n for (const item of items) {\n if (seen.has(item.value.id))\n continue\n seen.add(item.value.id)\n into.push(item)\n }\n}\n\n/**\n * Fold every active extension's contributions into the merged\n * {@link ExtensionRegistry}. Pure given the input — call this with\n * any subset of `active` to recompute the merged surface (typically\n * after the user toggles an extension on/off, without re-running\n * setup).\n *\n * @param active - Extensions whose contributions are merged.\n * @param inactive - Extensions to list as inactive. They contribute nothing.\n * @param options - Options. `options.dispose` runs when the registry is disposed.\n */\nexport function mergeContributions(\n active: readonly LoadedExtension[],\n inactive: readonly LoadedExtension[] = [],\n options: { dispose?: () => Promise<void> } = {},\n): ExtensionRegistry {\n const tools: Record<string, ToolDef> = createRecord()\n const hooks: { [E in keyof AgentHooks]?: Array<AgentHooks[E]> } = {}\n const mcpServers: McpServerConfig[] = []\n const skills: SkillConfig[] = []\n const plugins: LoadedPluginRecord[] = []\n const systemFragments: string[] = []\n const runSettled: ExtensionOrderedContribution<ExtensionRunSettledHandler>[] = []\n const contextBreakdownSections: NonNullable<ExtensionContributions['contextBreakdownSections']>[number][] = []\n const contextPanelSections: NonNullable<ExtensionContributions['contextPanelSections']>[number][] = []\n const toolResultMetadata: ExtensionContributions['toolResultMetadata'][number][] = []\n const artifactKinds: ExtensionContributions['artifactKinds'][number][] = []\n const seenArtifactKinds = new Set<string>()\n const references: ExtensionContributions['references'][number][] = []\n const seenReferenceIds = new Set<string>()\n const badgeActions: ExtensionContributions['badgeActions'][number][] = []\n const seenBadgeActionIds = new Set<string>()\n const runTransforms: ExtensionContributions['runTransforms'][number][] = []\n const behaviorTransforms: ExtensionContributions['behaviorTransforms'][number][] = []\n const providerMiddlewares: ExtensionContributions['providerMiddlewares'][number][] = []\n const toolCatalogTransforms: ExtensionContributions['toolCatalogTransforms'][number][] = []\n const mcpConfigTransforms: ExtensionContributions['mcpConfigTransforms'][number][] = []\n const skillPolicies: ExtensionContributions['skillPolicies'][number][] = []\n const promptTransforms: ExtensionContributions['promptTransforms'][number][] = []\n const completionProviders: CompletionProvider[] = []\n const inlineAutocompleteProviders: InlineAutocompleteProvider[] = []\n const seenInlineAutocompleteIds = new Set<string>()\n const commands: Command[] = []\n const seenCommandNames = new Set<string>()\n const themes: Record<string, Theme> = createRecord()\n const agents: Record<string, AgentProfile> = createRecord()\n const providers: Record<string, ProviderDescriptor> = createRecord()\n const subagents: Record<string, SubagentDef> = createRecord()\n const subagentOverrides: Record<string, SubagentOverride> = createRecord()\n const configCategories: Record<string, ExtensionConfigCategory> = createRecord()\n const configItems: Record<string, Record<string, StoredExtensionConfigItem>> = createRecord()\n const keybindings: ExtensionKeybinding[] = []\n const seenKeybindingIds = new Set<string>()\n const footerHints: ExtensionFooterHint[] = []\n const modals: ExtensionModal[] = []\n const renderers: ExtensionCustomRendererEntry[] = []\n const seenRendererIds = new Set<string>()\n const toolRenderers: ExtensionToolRendererEntry[] = []\n const artifactRenderers: ExtensionArtifactRendererEntry[] = []\n const seenArtifactRendererKinds = new Set<string>()\n const guiPanels: ExtensionPanelEntry[] = []\n const palette: ExtensionPaletteProviderEntry[] = []\n\n for (const entry of active) {\n const c = entry.contributions\n if (!c)\n continue\n Object.assign(tools, c.tools)\n for (const [event, list] of Object.entries(c.hooks)) {\n if (!list || list.length === 0)\n continue\n // Object.entries erases the per-event key↔handler-type correlation of\n // the hook map, so these casts re-assert what the source map's type\n // already guaranteed — both sides come from the same typed record, so\n // no unsound value can actually flow through.\n const key = event as keyof AgentHooks\n const bucket = (hooks[key] ??= []) as Array<typeof list[number]>\n bucket.push(...(list as Array<typeof list[number]>))\n }\n mcpServers.push(...c.mcpServers)\n skills.push(...c.skills)\n plugins.push(...c.plugins)\n systemFragments.push(...c.systemFragments)\n for (const handler of c.runSettled)\n runSettled.push({ extensionName: entry.name, source: entry.source, value: handler })\n contextBreakdownSections.push(...c.contextBreakdownSections)\n contextPanelSections.push(...c.contextPanelSections)\n toolResultMetadata.push(...c.toolResultMetadata)\n // Artifact kinds are a global namespace: first-wins by kind in `active`\n // order (same split as commands — merge dedupes quietly, build warns).\n for (const contribution of c.artifactKinds) {\n if (seenArtifactKinds.has(contribution.value.kind))\n continue\n seenArtifactKinds.add(contribution.value.kind)\n artifactKinds.push(contribution)\n }\n pushFirstById(references, seenReferenceIds, c.references)\n pushFirstById(badgeActions, seenBadgeActionIds, c.badgeActions)\n runTransforms.push(...c.runTransforms)\n behaviorTransforms.push(...c.behaviorTransforms)\n providerMiddlewares.push(...c.providerMiddlewares)\n toolCatalogTransforms.push(...c.toolCatalogTransforms)\n mcpConfigTransforms.push(...c.mcpConfigTransforms)\n skillPolicies.push(...c.skillPolicies)\n promptTransforms.push(...c.promptTransforms)\n completionProviders.push(...c.completionProviders)\n // First-wins dedupe by id (mirrors the command-name dedupe below).\n for (const provider of c.inlineAutocompleteProviders) {\n if (seenInlineAutocompleteIds.has(provider.id))\n continue\n seenInlineAutocompleteIds.add(provider.id)\n inlineAutocompleteProviders.push(provider)\n }\n // First-wins dedupe by name in `active` order, so a toggle-time re-merge\n // lets a previously-shadowed command surface once its winner is disabled\n // (mirrors the keybinding-id dedupe below). Case-insensitive to match\n // `parseCommandInvocation`.\n for (const command of c.commands) {\n const key = command.name.toLowerCase()\n if (seenCommandNames.has(key))\n continue\n seenCommandNames.add(key)\n // Aliases share the name namespace: reserve them so a later command\n // named like an earlier alias doesn't register unreachable.\n for (const alias of command.aliases ?? [])\n seenCommandNames.add(alias.toLowerCase())\n // Stamp the source extension as the completion group (unless the\n // command chose its own) — lets `/<extension>` list its commands.\n commands.push(command.group === undefined ? { ...command, group: entry.name } : command)\n }\n Object.assign(themes, c.themes)\n Object.assign(agents, c.agents)\n Object.assign(providers, c.providers)\n // Subagent adds — last-wins on name collision (mirrors tools / agents).\n Object.assign(subagents, c.subagents)\n // Subagent overrides — compose across extensions in registration\n // order, so a later extension's edit wraps the earlier one\n // (`fn(prev(def))`). Applied (by the host) after adds, last.\n for (const [name, fn] of Object.entries(c.subagentOverrides)) {\n const prev = subagentOverrides[name]\n subagentOverrides[name] = prev ? def => fn(prev(def)) : fn\n }\n // Config categories merge globally (last-wins on id collision,\n // mirrors themes). Config items stay namespaced under the owning\n // extension's name so two extensions can ship the same item key and\n // the Settings UIs can group by extension.\n Object.assign(configCategories, c.configCategories)\n if (Object.keys(c.configItems).length > 0) {\n const items = createRecord<StoredExtensionConfigItem>()\n Object.assign(items, c.configItems)\n configItems[entry.name] = items\n }\n // First-wins id dedupe in `active` order — applied here (not at\n // build time) so a toggle-time re-merge lets a previously-shadowed\n // binding surface once its winner is disabled. Collision warnings\n // are emitted at build time in `buildExtensionRegistry`.\n for (const binding of c.tui.keybindings) {\n if (seenKeybindingIds.has(binding.id))\n continue\n seenKeybindingIds.add(binding.id)\n keybindings.push(binding)\n }\n footerHints.push(...c.tui.footerHints)\n modals.push(...c.tui.modals)\n // Renderer ids are `<extension>/<id>`-namespaced at registration, so\n // cross-extension collisions are impossible; a duplicate here means\n // one extension registered the same id twice. Silent first-wins —\n // the WARNING fires at registration time in `buildExtensionRegistry`\n // (same split as keybindings: merge dedupes, build warns), so\n // toggle-time re-merges stay quiet.\n for (const renderer of c.tui.renderers) {\n if (seenRendererIds.has(renderer.id))\n continue\n seenRendererIds.add(renderer.id)\n renderers.push(renderer)\n }\n // Tool renderers merge in `active` order; the render-time resolver\n // walks in order and takes the first non-null — so \"first registered\n // wins\" falls out of ordering, no dedupe needed here.\n toolRenderers.push(...c.tui.toolRenderers)\n for (const renderer of c.tui.artifactRenderers) {\n if (seenArtifactRendererKinds.has(renderer.kind))\n continue\n seenArtifactRendererKinds.add(renderer.kind)\n artifactRenderers.push(renderer)\n }\n guiPanels.push(...c.panels)\n palette.push(...c.palette)\n }\n\n // Disabled-but-set-up extensions keep their CONFIG SCHEMA visible —\n // categories + items are inert metadata, and exposing them is what lets\n // the Settings UIs render (and persist values for) an extension's\n // settings BEFORE it's enabled. Behavioral contributions (tools, hooks,\n // providers, commands, …) stay gated to `active` above. First-wins\n // against active registrations so an inactive extension can never\n // relabel an active extension's category.\n for (const entry of inactive) {\n const c = entry.contributions\n if (entry.error || !c)\n continue\n for (const [id, category] of Object.entries(c.configCategories)) {\n if (!(id in configCategories))\n configCategories[id] = category\n }\n if (Object.keys(c.configItems).length > 0 && !(entry.name in configItems)) {\n const items = createRecord<StoredExtensionConfigItem>()\n Object.assign(items, c.configItems)\n configItems[entry.name] = items\n }\n }\n\n return {\n loaded: active,\n inactive,\n tools,\n hooks,\n mcpServers,\n skills,\n plugins,\n systemFragments,\n runSettled,\n contextBreakdownSections,\n contextPanelSections,\n toolResultMetadata,\n artifactKinds,\n references,\n badgeActions,\n runTransforms,\n behaviorTransforms,\n providerMiddlewares,\n toolCatalogTransforms,\n mcpConfigTransforms,\n skillPolicies,\n promptTransforms,\n completionProviders,\n inlineAutocompleteProviders,\n commands,\n themes,\n agents,\n providers,\n subagents,\n subagentOverrides,\n configCategories,\n configItems,\n tuiContributions: { keybindings, footerHints, modals, renderers, toolRenderers, artifactRenderers },\n guiContributions: { panels: guiPanels },\n panels: guiPanels,\n palette,\n dispose: options.dispose ?? (async () => {}),\n }\n}\n\n/**\n * Find a built-in `KeyAction` whose default chord matches `chord`. Used\n * by the collision warning above. Returns `null` when no built-in\n * uses that chord. Comparison goes through the same parser as runtime\n * dispatch, so aliases like `control` / `esc` canonicalize correctly.\n */\nfunction findBuiltinByChord(chord: string): string | null {\n const target = canonicalChord(chord)\n if (!target)\n return null\n for (const [action, builtinChord] of Object.entries(DEFAULT_KEYBINDINGS)) {\n if (!builtinChord)\n continue\n if (canonicalChord(builtinChord) === target)\n return action\n }\n return null\n}\n\nfunction canonicalChord(spec: string): string | null {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return null\n return JSON.stringify(parsed)\n}\n\n/**\n * Filter the extension registry by the user's enable allowlist. Returns\n * the original registry by reference when `enabled` is `undefined` (the\n * fresh-install default — every loaded extension is active), or a fresh\n * registry rebuilt via {@link mergeContributions} when the user has\n * explicitly toggled something off.\n *\n * The identity check is intentional — callers can compare the result\n * against the input registry to skip recomposing the preset when\n * nothing has changed.\n *\n * @param registry - Registry to filter.\n * @param enabled - Names of the enabled extensions. `undefined` keeps every extension.\n */\nexport function filterExtensionRegistry(\n registry: ExtensionRegistry,\n enabled: readonly string[] | undefined,\n): ExtensionRegistry {\n if (enabled === undefined)\n return registry\n const allow = new Set(enabled)\n const filteredActive = registry.loaded.filter(ext => allow.has(ext.name))\n // No change → return original. This is the common case once the\n // user has a stable allowlist (or a brand-new extension hasn't\n // been added yet); skipping the rebuild keeps re-renders cheap.\n if (filteredActive.length === registry.loaded.length)\n return registry\n // Build the inactive set: original inactive (load errors / pre-\n // existing disabled) plus the newly-filtered-out actives. We keep\n // them around so the Settings panel still surfaces every known\n // extension regardless of toggle state.\n const filteredOut = registry.loaded.filter(ext => !allow.has(ext.name))\n return mergeContributions(filteredActive, [...registry.inactive, ...filteredOut])\n}\n\n/**\n * Drop the contributions of Agent Plugins the user has disabled.\n *\n * `filterExtensionRegistry` keys on EXTENSION names, so it can't express\n * \"this extension runs, but the plugin it loaded does not\". Plugin skills and\n * MCP servers are fanned into the loading extension's sinks by\n * `ctx.loadAgentPlugin`, which means without this pass `enabledPlugins` would\n * render a checkbox that changes nothing for extension-loaded plugins.\n *\n * Skills are matched by `metadata['zidane.plugin']` (stamped by the loader);\n * MCP servers carry no such marker, so they're matched by identity against\n * the server list on each {@link LoadedPluginRecord}.\n *\n * Returns the input registry unchanged when nothing is disabled, so callers\n * can compare identity to skip recomposing.\n *\n * @param registry - Registry to filter.\n * @param enabledPlugins - Names of the enabled plugins. `undefined` keeps every plugin.\n */\nexport function filterRegistryPlugins(\n registry: ExtensionRegistry,\n enabledPlugins: readonly string[] | undefined,\n /**\n * Plugin names already provided by another source (in practice: disk\n * discovery). Pruned regardless of the allowlist.\n *\n * The settings list dedupes a plugin found both on disk and inside an\n * extension down to ONE row, preferring the disk copy. Contributions have to\n * agree, or that single checkbox governs two live copies: both sets of skills\n * reach the catalog, where one shadows the other and earns a spurious\n * `name-collision-shadowed` warning about a collision the user never created.\n *\n * The MCP half is less severe but the same shape — both configs reach the\n * merge, where `sessionMcpServerList` (chat/host/mcp/index.ts, first-wins by name) collapses them\n * to one. So there's no double spawn; what's wrong is that one checkbox\n * nominally governs two records, and which copy survives is decided by merge\n * order rather than by anything the user chose.\n */\n shadowed?: ReadonlySet<string>,\n): ExtensionRegistry {\n if (registry.plugins.length === 0)\n return registry\n // `undefined` allowlist means \"every discovered plugin\", matching the\n // polarity of every other enabled-allowlist setting — but shadowing still\n // applies, so this can't short-circuit on it.\n const allow = enabledPlugins === undefined ? null : new Set(enabledPlugins)\n const dropped = registry.plugins.filter(record =>\n (allow !== null && !allow.has(record.name)) || shadowed?.has(record.name) === true)\n if (dropped.length === 0)\n return registry\n\n const droppedNames = new Set(dropped.map(record => record.name))\n // Identity, not name: two plugins may legitimately ship a server with the\n // same name, and only the dropped one's config should fall out.\n const droppedServers = new Set(dropped.flatMap(record => record.plugin.mcpServers))\n\n const prune = (ext: LoadedExtension): LoadedExtension => {\n const contributions = ext.contributions\n if (!contributions || contributions.plugins.length === 0)\n return ext\n return {\n ...ext,\n contributions: {\n ...contributions,\n skills: contributions.skills.filter((skill) => {\n const owner = skill.metadata?.['zidane.plugin']\n return owner === undefined || !droppedNames.has(owner)\n }),\n mcpServers: contributions.mcpServers.filter(server => !droppedServers.has(server)),\n },\n }\n }\n\n return mergeContributions(registry.loaded.map(prune), registry.inactive)\n}\n\n/**\n * Dispose resources owned by an extension registry. Idempotent per registry.\n *\n * @param registry - Registry to dispose.\n */\nexport async function disposeExtensionRegistry(registry: ExtensionRegistry): Promise<void> {\n await registry.dispose()\n}\n\n/** Shape returned by {@link partitionExtensionCatalog}. */\nexport interface ExtensionCatalogPartition {\n /**\n * Rows the user can toggle: active extensions plus disabled-but-\n * loadable ones, sorted by name so the list order is stable across\n * boots regardless of where each entry landed in the registry.\n */\n toggleable: readonly LoadedExtension[]\n /** Entries whose import or `setup()` failed — read-only diagnostics. */\n errored: readonly LoadedExtension[]\n /**\n * Every known extension name — toggleable AND errored. Use this as\n * the GC key set for the persisted `enabledExtensions` allowlist so\n * an untrusted / temporarily-broken extension isn't permanently\n * dropped from the allowlist by an unrelated toggle.\n */\n allNames: readonly string[]\n}\n\n/**\n * Partition a registry's `loaded` + `inactive` lists into the shape the\n * Settings toggle UIs render. Pure; shared by the TUI's standalone\n * extensions modal and the Settings-modal tab so the rules (and the\n * allowlist-GC key set) stay in one place.\n *\n * @param loaded - Active extensions.\n * @param inactive - Disabled or failed extensions.\n */\nexport function partitionExtensionCatalog(\n loaded: readonly LoadedExtension[],\n inactive: readonly LoadedExtension[],\n): ExtensionCatalogPartition {\n const errored = inactive.filter(e => !!e.error)\n const disabledOk = inactive.filter(e => !e.error)\n const toggleable = [...loaded, ...disabledOk].sort((a, b) => a.name.localeCompare(b.name))\n return {\n toggleable,\n errored,\n allNames: [...toggleable.map(e => e.name), ...errored.map(e => e.name)],\n }\n}\n\nfunction resolveContextPaths(options: BuildExtensionRegistryOptions): ExtensionContext['paths'] {\n const cwd = options.cwd ?? process.cwd()\n const dirs = options.storageDirs ?? resolveStorageDirs({\n prefix: options.prefix ?? process.env.ZIDANE_PREFIX ?? '.zidane',\n home: homedir(),\n })\n return {\n cwd,\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n dataDir: dirs.dataDir,\n cacheDir: dirs.cacheDir,\n ...(dirs.configDir ? { configDir: dirs.configDir } : {}),\n ...(dirs.stateDir ? { stateDir: dirs.stateDir } : {}),\n }\n}\n","/**\n * Shared discover → disabled-set → build pipeline.\n *\n * Every host runs the identical sequence: the TUI launcher (`runTui`),\n * the TUI's cwd-change rebuild, the daemon's per-cwd runtime cache and the\n * GUI's per-cwd registry cache.\n * Centralizing it keeps the allowlist-gating contract — the `disabled`\n * set spans BOTH disk-discovered and inline candidates — in one place\n * so the callers can't drift on it.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { LogSink } from '../logger'\nimport type { ExtensionBus } from './bus'\nimport type { ExtensionHostRuntimeBridge } from './host-runtime'\nimport type { ExtensionHud } from './hud'\nimport type { ExtensionMcpHost } from './mcp-host'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ExtensionRegistry, LoadedExtension } from './types'\nimport { discoverExtensions } from './discovery'\nimport { registerExtensionHostModules } from './host-modules'\nimport { buildExtensionRegistry } from './register'\n\n/** Options for {@link discoverAndBuildExtensionRegistry}. */\nexport interface DiscoverAndBuildExtensionRegistryOptions {\n /** Discovery cwd — project roots are walked up from here. Defaults to `process.cwd()`. */\n cwd?: string\n /** User home directory. Defaults to `os.homedir()`. Mainly a test seam. */\n home?: string\n /** Storage prefix (e.g. `.zidane`). Defaults to `.zidane` inside discovery. */\n prefix?: string\n /**\n * Extra extension scan roots, prepended ahead of the default project/user\n * roots. Each root contains extension package dirs (`<root>/<name>/index.ts`).\n * Treated as user-scoped/trusted because the host/user opted into them\n * explicitly.\n */\n extensionScanPaths?: readonly string[]\n /**\n * User enable allowlist (`Settings.enabledExtensions`). `undefined`\n * means \"fresh install — everything enabled\". When set it gates BOTH\n * disk and inline candidates: a name absent from the list lands in the\n * build's `disabled` set, so it classifies as `inactive` and its\n * contributions never reach the registry's active surface. Its\n * `setup()` still runs (trusted code) so its config schema is\n * browsable in Settings before enabling.\n */\n enabledAllowlist?: readonly string[]\n /** Whether project-scoped extensions are trusted (allowed to run their code). */\n trustProjectExtensions?: boolean\n /** Host-supplied inline extensions, already `inlineLoadedExtension()`-wrapped. */\n inlineExtensions?: readonly LoadedExtension[]\n /** Log sink for `setup()` lifecycle. Defaults to stderr inside `buildExtensionRegistry`. */\n sink?: LogSink\n /** Extension HUD for `ctx.tui.notify` / `setStatus` / `setBanner` (TUI hosts). */\n hud?: ExtensionHud\n /** Prompt bridge for `ctx.submitPrompt` (hosts with a submit pipeline). */\n promptBridge?: ExtensionPromptBridge\n /** Host runtime bridge for `ctx.runtime` / `ctx.sessions` / `ctx.appendNote`. */\n hostRuntime?: ExtensionHostRuntimeBridge\n /** Shared extension bus for `ctx.bus` (hosts with a cross-window transport). */\n bus?: ExtensionBus\n /** MCP access for `ctx.mcp` (`createExtensionMcpHost`). */\n mcp?: ExtensionMcpHost\n /**\n * Live settings getter backing `ctx.config.get` — threaded verbatim to\n * {@link BuildExtensionRegistryOptions.readSettings}. Hosts pass a getter\n * returning their current `Settings` (TUI: a live mirror; GUI: the DB\n * reader) so extension config reads inside hooks / tools are live.\n */\n readSettings?: () => Partial<Settings> | undefined\n /** Abort discovery before its imports dispatch. */\n signal?: AbortSignal\n}\n\n/**\n * Discover disk extensions, fold in the inline ones, compute the\n * allowlist-driven `disabled` set, and build the merged\n * {@link ExtensionRegistry}.\n *\n * The `disabled` set honors the allowlist for inline extensions too:\n * the TUI treats inline as toggleable everywhere else (Settings tab,\n * live filtering), so both ride the same inactive-classification path.\n *\n * @param opts - Discovery, trust, allowlist and bridge options.\n */\nexport async function discoverAndBuildExtensionRegistry(\n opts: DiscoverAndBuildExtensionRegistryOptions = {},\n): Promise<ExtensionRegistry> {\n // Core entrypoints, mapped BEFORE discovery's imports dispatch. An extension\n // contributing a provider (`registerProvider`) needs the host's live\n // factories and credential helpers at runtime; a bare `import { anthropic }\n // from 'zidane'` would otherwise resolve from the EXTENSION's directory —\n // nothing inside a compiled binary, or a second zidane copy with its own\n // module state (and its own OAuth registry). Renderer-neutral, so it lives\n // here rather than in the TUI: headless / ACP / GUI hosts get it too.\n // pi-ai is NOT mapped: a lazy mapping would re-import the very specifier it\n // intercepts. Extensions reach the host's pi-ai through zidane instead\n // (`loadGoogleWireKit`).\n registerExtensionHostModules({\n 'zidane': () => import('../index'),\n 'zidane/providers': () => import('../providers'),\n 'zidane/providers/google': () => import('../providers/google'),\n 'zidane/oauth': () => import('../oauth-entry'),\n })\n\n const discovered = await discoverExtensions({\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.extensionScanPaths !== undefined ? { extensionScanPaths: opts.extensionScanPaths } : {}),\n ...(opts.trustProjectExtensions !== undefined ? { trustProjectExtensions: opts.trustProjectExtensions } : {}),\n ...(opts.signal !== undefined ? { signal: opts.signal } : {}),\n ...(opts.sink !== undefined ? { sink: opts.sink } : {}),\n })\n const candidates = [...discovered, ...(opts.inlineExtensions ?? [])]\n const allowlist = opts.enabledAllowlist\n const disabled = allowlist === undefined\n ? undefined\n : new Set(candidates.map(e => e.name).filter(name => !allowlist.includes(name)))\n return buildExtensionRegistry(candidates, {\n ...(opts.sink !== undefined ? { sink: opts.sink } : {}),\n ...(opts.hud !== undefined ? { hud: opts.hud } : {}),\n ...(opts.promptBridge !== undefined ? { promptBridge: opts.promptBridge } : {}),\n ...(opts.hostRuntime !== undefined ? { hostRuntime: opts.hostRuntime } : {}),\n ...(opts.bus !== undefined ? { bus: opts.bus } : {}),\n ...(opts.mcp !== undefined ? { mcp: opts.mcp } : {}),\n ...(disabled ? { disabled } : {}),\n ...(opts.readSettings !== undefined ? { readSettings: opts.readSettings } : {}),\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n })\n}\n","/**\n * Extension import — fetch from GitHub or npm, validate, and install into\n * the roots {@link discoverExtensions} already scans.\n *\n * Host-agnostic by construction: no UI, no settings access, no renderer.\n * The TUI's `Settings → Extensions` install flow (`ctrl+g`) and the GUI\n * call the same {@link installExtensionFromSpec} orchestrator; hosts own scope\n * selection, overwrite confirmation, allowlist updates, and the\n * post-install registry rebuild (the `/reload` path).\n *\n * Pipeline invariants:\n *\n * - **Validate before touching user roots.** The candidate is imported\n * and type-checked (same `isExtensionDefinition` + apiVersion rules\n * as discovery) inside the temp dir; a failure installs nothing.\n * Importing executes the extension's top-level code once — the\n * explicit install request is the consent; it's the same code that\n * would run at next boot.\n * - **Folder name = definition name, by construction.** The target dir\n * is named from the validated `definition.name`, so discovery's\n * name-match invariant can't be violated by an install.\n * - **Dependency installs never run lifecycle scripts** (bun's default;\n * the npm/pnpm/yarn fallbacks get `--ignore-scripts`).\n * - Failures throw {@link ExtensionInstallError} with a machine-readable\n * `code` so hosts can react (e.g. `exists` → confirm-and-retry with\n * `overwrite`), never half-install.\n *\n * Recognized package shapes (both fetch targets):\n *\n * - root `index.{ts,tsx,mjs,js}` → a single-extension repo/package\n * - `extensions/<name>/index.*` → a multi-extension collection;\n * `extension` selects one (mandatory when several exist)\n */\n\nimport type { FetchSourceResult, InstallIo, InstallScope, InstallSource } from '../install/source'\nimport { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { errorMessage } from '../errors'\nimport { defaultExec, defaultWhich, fetchInstallSource, InstallSourceError, lastLine, parseInstallSource, resolveNpmVersion, resolveSubdirRoot } from '../install/source'\nimport { findEntryFile, importExtensionEntry, isExtensionDefinition, readExtensionManifest, releaseExtensionModules } from './discovery'\nimport { checkZidaneVersion } from './host-version'\nimport { withTimeout } from './with-timeout'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Where an extension is installed: the project or the user directory. */\nexport type ExtensionInstallScope = InstallScope\n\n/** The shared install-source shape — see `src/install/source.ts` for the grammar. */\nexport type ExtensionInstallSource = InstallSource\n\n/** Reason an install, update or removal failed. Matches {@link ExtensionInstallError.code}. */\nexport type ExtensionInstallErrorCode\n = | 'invalid-source'\n | 'fetch-failed'\n | 'no-extension'\n | 'multiple-candidates'\n | 'deps-failed'\n | 'validate-failed'\n | 'exists'\n | 'no-package-manager'\n | 'not-installed'\n | 'no-provenance'\n\n/** Error thrown by the extension installer. `code` says which step failed. */\nexport class ExtensionInstallError extends InstallSourceError {\n /** Machine-readable reason for the failure. */\n declare readonly code: ExtensionInstallErrorCode\n /**\n * Creates an install error.\n * @param code - Reason for the failure.\n * @param message - Human-readable description.\n * @param candidates - Extension names found in the source, set for `multiple-candidates`.\n */\n constructor(code: ExtensionInstallErrorCode, message: string, candidates?: readonly string[]) {\n super(code, message, candidates)\n this.name = 'ExtensionInstallError'\n }\n}\n\n/**\n * Re-raise a shared-layer {@link InstallSourceError} in the extension\n * error vocabulary so every consumer keeps a single `err.code` switch\n * over {@link ExtensionInstallErrorCode}. The base layer only throws\n * `'invalid-source'` / `'fetch-failed'` — both members of the extension\n * union.\n */\nfunction rethrowAsExtensionError(err: unknown): never {\n if (err instanceof InstallSourceError && !(err instanceof ExtensionInstallError))\n throw new ExtensionInstallError(err.code as ExtensionInstallErrorCode, err.message, err.candidates)\n throw err\n}\n\nconst PROVENANCE_FILE = '.install.json'\n\n/** What `.install.json` records — enough to replay the install for updates. */\nexport interface ExtensionInstallProvenance {\n /** Resolved provenance, e.g. `npm:foo@1.2.3` / `github:owner/repo@main`. */\n source: string\n /** The raw spec the user passed — what an update re-resolves. */\n spec: string\n /** Scope the extension was installed into. */\n scope: ExtensionInstallScope\n /** Candidate selected from a multi-extension source, when one was. */\n extension?: string\n /** Exact commit SHA (GitHub via git clone) — anchors the update no-op check. */\n commit?: string\n /** ISO 8601 timestamp of the install. */\n installedAt: string\n}\n\n/** One installable extension found inside a fetched tree. */\nexport interface ExtensionInstallCandidate {\n /** Proposed name (manifest name, else collection dirname, else repo/pkg basename). */\n name: string\n /** Absolute directory inside the fetched tree. */\n dir: string\n}\n\n/** Outcome of an install or update. */\nexport interface ExtensionInstallResult {\n /** Final name — the validated `definition.name`. */\n name: string\n /** Absolute installed directory. */\n dir: string\n /** Scope the extension was installed into. */\n scope: ExtensionInstallScope\n /** Human-readable provenance, e.g. `npm:foo@1.2.3` / `github:owner/repo@main`. */\n provenance: string\n /** Exact commit SHA when known (GitHub via git clone). */\n commit?: string\n /** Non-fatal problems met during the install, such as dependency install notices. */\n warnings: readonly string[]\n}\n\n/**\n * Injectable side-effect seams — tests swap these to stay offline; hosts\n * normally omit the whole object. Shared shape with the skills installer.\n */\nexport type ExtensionInstallIo = InstallIo\n\n/** Options for {@link installExtensionFromSpec}. */\nexport interface InstallExtensionFromSpecOptions {\n /** Raw source spec — see {@link parseExtensionInstallSource}. */\n source: string\n /** Candidate name to install when the source contains several extensions. */\n extension?: string\n /** Scope to install into. */\n scope: ExtensionInstallScope\n /** Replace an existing install of the same name. Default false → `exists` error. */\n overwrite?: boolean\n /** Working directory used to find the project root. @default process.cwd() */\n cwd?: string\n /** User home directory. @default os.homedir() */\n home?: string\n /** Storage prefix (e.g. `.zidane`). Matches discovery's default. */\n prefix?: string\n /** Called with a short status message as each step runs. */\n onProgress?: (message: string) => void\n /** Overrides for fetch, process execution and executable lookup. Mainly for tests. */\n io?: ExtensionInstallIo\n /** Timeout for the validation import. Default 10s (matches discovery). */\n importTimeoutMs?: number\n /**\n * Advanced: exact extensions root to install into, bypassing the\n * scope → `.{prefix}/extensions` resolution. Used by\n * {@link updateInstalledExtension} to replace an install in place\n * (e.g. one living under `.agents/extensions`).\n */\n installRoot?: string\n}\n\n// ---------------------------------------------------------------------------\n// Source spec parsing + fetch — thin wrappers over the shared install\n// layer (`src/install/source.ts`), re-raised in the extension error\n// vocabulary. The grammar, fetch pipeline, caps, and hardening live\n// there so the skills installer shares them verbatim.\n// ---------------------------------------------------------------------------\n\n/**\n * Classify a raw source string. See `parseInstallSource` for the grammar.\n *\n * @param raw - Source spec such as `owner/repo`, `npm:pkg` or a git URL.\n */\nexport function parseExtensionInstallSource(raw: string): ExtensionInstallSource {\n try {\n return parseInstallSource(raw)\n }\n catch (err) {\n rethrowAsExtensionError(err)\n }\n}\n\n/** Result of {@link fetchExtensionSource}: the provenance string and, for git clones, the commit SHA. */\nexport type ExtensionFetchResult = FetchSourceResult\n\n/**\n * Materialize the source into `destDir` (created by the caller, assumed\n * empty). Returns the provenance string plus, for git clones, the exact\n * commit SHA. See `fetchInstallSource` for transport details and caps.\n *\n * @param source - Parsed install source.\n * @param destDir - Empty directory to fetch into.\n * @param io - Overrides for fetch, process execution and executable lookup.\n * @param onProgress - Called with a short status message as the fetch runs.\n */\nexport async function fetchExtensionSource(\n source: ExtensionInstallSource,\n destDir: string,\n io: ExtensionInstallIo = {},\n onProgress?: (message: string) => void,\n): Promise<ExtensionFetchResult> {\n try {\n return await fetchInstallSource(source, destDir, io, onProgress)\n }\n catch (err) {\n rethrowAsExtensionError(err)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Candidate enumeration\n// ---------------------------------------------------------------------------\n\n/**\n * List the installable extensions inside a fetched tree. `fallbackName`\n * seeds the single-extension case when no manifest names it (repo or\n * unscoped package basename).\n *\n * @param dir - Root of the fetched tree.\n * @param fallbackName - Name to use when no manifest names the extension.\n */\nexport function listExtensionCandidates(dir: string, fallbackName: string): ExtensionInstallCandidate[] {\n if (findEntryFile(dir))\n return [{ name: readExtensionManifest(dir).manifest?.name ?? sanitizeName(fallbackName), dir }]\n const collection = join(dir, 'extensions')\n const out: ExtensionInstallCandidate[] = []\n let children: string[]\n try {\n children = readdirSync(collection)\n }\n catch {\n return out\n }\n for (const child of children.sort()) {\n if (child.startsWith('.'))\n continue\n const childDir = join(collection, child)\n try {\n if (!lstatSync(childDir).isDirectory())\n continue\n }\n catch {\n continue\n }\n if (findEntryFile(childDir))\n out.push({ name: child, dir: childDir })\n }\n return out\n}\n\n/** Basename-ish fallback: strip npm scope, keep folder-safe chars. */\nfunction sanitizeName(raw: string): string {\n const base = raw.includes('/') ? (raw.split('/').pop() as string) : raw\n return base.replace(/[^\\w.-]+/g, '-').replace(/^[.-]+/, '') || 'extension'\n}\n\n// ---------------------------------------------------------------------------\n// Orchestrator\n// ---------------------------------------------------------------------------\n\n/**\n * The full fetch → enumerate → deps → validate → copy pipeline. See the\n * module doc for invariants. Throws {@link ExtensionInstallError}; a throw\n * means nothing was written to the target roots.\n *\n * @param opts - Source, scope and install options.\n */\nexport async function installExtensionFromSpec(opts: InstallExtensionFromSpecOptions): Promise<ExtensionInstallResult> {\n const io = opts.io ?? {}\n const onProgress = opts.onProgress\n const source = parseExtensionInstallSource(opts.source)\n const warnings: string[] = []\n\n const stage = mkdtempSync(join(tmpdir(), 'zidane-ext-install-'))\n try {\n const fetchedInto = join(stage, 'fetched')\n mkdirSync(fetchedInto, { recursive: true })\n const { provenance, commit } = await fetchExtensionSource(source, fetchedInto, io, onProgress)\n\n // A `:subdir` source re-roots the candidate search. Physical (not\n // just string-level) containment lives in `resolveSubdirRoot` — a\n // malicious repo shipping a symlink (`evil -> /`) must not walk the\n // candidate search (and the validation IMPORT) onto the local\n // filesystem. Structural escapes are already rejected at parse time;\n // this is the belt-and-braces layer.\n let candidateRoot = fetchedInto\n let fallbackName = source.kind === 'github' ? source.repo : source.pkg\n if (source.kind === 'github' && source.subdir) {\n const rooted = resolveSubdirRoot(fetchedInto, source.subdir)\n if (!rooted.ok) {\n if (rooted.reason === 'missing')\n throw new ExtensionInstallError('no-extension', `\"${source.subdir}\" is not a directory in ${source.owner}/${source.repo}${source.ref ? `@${source.ref}` : ''}.`)\n throw new ExtensionInstallError(\n 'invalid-source',\n rooted.reason === 'escape-symlink'\n ? `Subdirectory \"${source.subdir}\" escapes the repository root through a symlink.`\n : `Subdirectory \"${source.subdir}\" escapes the repository root.`,\n )\n }\n candidateRoot = rooted.dir\n fallbackName = basename(resolve(fetchedInto, source.subdir))\n }\n const candidates = listExtensionCandidates(candidateRoot, fallbackName)\n if (candidates.length === 0) {\n throw new ExtensionInstallError('no-extension', `No extension found in ${opts.source}. Expected a root index.{ts,tsx,mjs,js} or extensions/<name>/index.* directories.`)\n }\n let candidate: ExtensionInstallCandidate\n if (opts.extension) {\n const match = candidates.find(c => c.name === opts.extension)\n if (!match) {\n throw new ExtensionInstallError(\n 'no-extension',\n `No extension named \"${opts.extension}\" in ${opts.source}. Available: ${candidates.map(c => c.name).join(', ')}`,\n candidates.map(c => c.name),\n )\n }\n candidate = match\n }\n else if (candidates.length > 1) {\n throw new ExtensionInstallError(\n 'multiple-candidates',\n `${opts.source} contains ${candidates.length} extensions. Pass one of: ${candidates.map(c => c.name).join(', ')}`,\n candidates.map(c => c.name),\n )\n }\n else {\n candidate = candidates[0] as ExtensionInstallCandidate\n }\n\n // Dependencies BEFORE validation — the import may need them.\n const pm = await installDependencies(candidate.dir, io, onProgress)\n if (pm)\n warnings.push(...pm.warnings)\n\n onProgress?.('validating extension module…')\n const definition = await validateCandidate(candidate, opts.importTimeoutMs ?? 10_000)\n if (definition.warning)\n warnings.push(definition.warning)\n\n const targetRoot = opts.installRoot ?? resolveInstallRoot(opts)\n const targetDir = join(targetRoot, definition.name)\n // Overwrites are fully live: discovery evicts the extension's module\n // graph before each import, so the next reload runs the new code.\n if (existsSync(targetDir) && !opts.overwrite)\n throw new ExtensionInstallError('exists', `${targetDir} already exists. Pass overwrite to replace it.`)\n\n onProgress?.(`installing to ${targetDir}…`)\n mkdirSync(targetRoot, { recursive: true })\n // Transactional target phase — the \"never half-install\" invariant. An\n // overwritten previous version is parked under a dot-name (invisible\n // to the scanner) and restored if anything past this point throws\n // (e.g. the in-place deps install failing on a full disk); on success\n // it is deleted. A fresh install that fails is wiped back to absence.\n const backupDir = existsSync(targetDir)\n ? join(targetRoot, `.${definition.name}.prev-${process.pid}-${Date.now()}`)\n : null\n if (backupDir)\n renameSync(targetDir, backupDir)\n try {\n cpSync(candidate.dir, targetDir, {\n recursive: true,\n // Materialize symlinked files: the stage dir they'd point into is\n // deleted after install, so copied links would dangle. (The\n // extension's code already ran at validation with the user's\n // consent — dereferencing discloses nothing new.)\n dereference: true,\n filter: src => !/(?:^|[\\\\/])(?:\\.git|node_modules)(?:$|[\\\\/])/.test(src),\n })\n // Re-install deps in place (node_modules excluded from the copy —\n // symlink layouts don't survive a cp).\n await installDependencies(targetDir, io, onProgress)\n\n const provenanceRecord: ExtensionInstallProvenance = {\n source: provenance,\n spec: opts.source,\n scope: opts.scope,\n ...(opts.extension !== undefined ? { extension: opts.extension } : {}),\n ...(commit !== undefined ? { commit } : {}),\n installedAt: new Date().toISOString(),\n }\n writeFileSync(join(targetDir, PROVENANCE_FILE), `${JSON.stringify(provenanceRecord, null, 2)}\\n`)\n }\n catch (err) {\n rmSync(targetDir, { recursive: true, force: true })\n if (backupDir)\n renameSync(backupDir, targetDir)\n throw err\n }\n if (backupDir)\n rmSync(backupDir, { recursive: true, force: true })\n\n return { name: definition.name, dir: targetDir, scope: opts.scope, provenance, ...(commit !== undefined ? { commit } : {}), warnings }\n }\n finally {\n rmSync(stage, { recursive: true, force: true })\n }\n}\n\n/**\n * Read `.install.json` provenance from an installed extension dir, or null.\n *\n * @param dir - Installed extension directory.\n */\nexport function readExtensionInstallProvenance(dir: string): ExtensionInstallProvenance | null {\n const raw = readJson(join(dir, PROVENANCE_FILE)) as Partial<ExtensionInstallProvenance> | null\n if (!raw || typeof raw.spec !== 'string' || typeof raw.source !== 'string')\n return null\n return {\n source: raw.source,\n spec: raw.spec,\n scope: raw.scope === 'project' ? 'project' : 'user',\n ...(typeof raw.extension === 'string' ? { extension: raw.extension } : {}),\n ...(typeof raw.commit === 'string' ? { commit: raw.commit } : {}),\n installedAt: typeof raw.installedAt === 'string' ? raw.installedAt : '',\n }\n}\n\n/** Identifies an installed extension and where to look for extension folders. Used by {@link updateInstalledExtension} and {@link removeInstalledExtension}. */\nexport interface InstalledExtensionTargetOptions {\n /** The installed extension's directory (e.g. `LoadedExtension.dir`). */\n dir: string\n /** Working directory used to find the project root. @default process.cwd() */\n cwd?: string\n /** User home directory. @default os.homedir() */\n home?: string\n /** Storage prefix. @default '.zidane' */\n prefix?: string\n /** Extra trusted roots (`ZIDANE_EXTENSION_DIRS` / host scan paths). */\n extensionScanPaths?: readonly string[]\n}\n\n/**\n * Containment guard shared by update/remove: `dir` must be a DIRECT child\n * of a recognized extensions root (the four `.{agents,prefix}/extensions`\n * search paths, plus any host scan roots). Refuses anything else — these\n * functions delete directories and must never be pointable at arbitrary\n * paths (an inline/builtin extension has no dir; a hand-typed path is not\n * an install).\n */\nfunction assertInstalledExtensionDir(opts: InstalledExtensionTargetOptions): { dir: string, root: string, scope: ExtensionInstallScope } {\n const dir = resolve(opts.dir)\n const roots: Array<{ path: string, scope: ExtensionInstallScope }> = [\n ...projectUserPaths({\n subPath: 'extensions',\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n }).map(p => ({ path: p.path, scope: p.source })),\n // Host scan roots are user-opted-in — user scope by convention.\n ...(opts.extensionScanPaths ?? []).map(p => ({ path: resolve(p), scope: 'user' as const })),\n ]\n const root = roots.find(r => dirname(dir) === r.path)\n if (!root)\n throw new ExtensionInstallError('not-installed', `${dir} is not inside an extensions folder, so it was left alone.`)\n return { dir, root: root.path, scope: root.scope }\n}\n\n/**\n * Delete an installed extension's directory. Works on any on-disk\n * extension (hand-authored ones too — no provenance required); the\n * containment guard is the safety boundary. The caller owns follow-up\n * (allowlist cleanup, registry reload).\n *\n * @param opts - The installed extension to remove.\n */\nexport function removeInstalledExtension(opts: InstalledExtensionTargetOptions): { dir: string } {\n const { dir } = assertInstalledExtensionDir(opts)\n if (!existsSync(dir))\n throw new ExtensionInstallError('not-installed', `${dir} does not exist.`)\n rmSync(dir, { recursive: true, force: true })\n return { dir }\n}\n\n/**\n * Cheap \"is there anything new?\" probe, no fetch of content:\n *\n * - GitHub (with a recorded commit): `git ls-remote` resolves the ref's\n * current SHA in one round-trip — equal SHA means up to date.\n * - npm: one registry metadata GET re-resolves the version/dist-tag —\n * an unchanged resolved provenance string means up to date.\n *\n * `null` = \"can't tell cheaply\" (no recorded commit, git missing,\n * ls-remote/parse failure) — the caller falls through to a full update,\n * which errors honestly if the network is actually down.\n */\nasync function checkRemoteUnchanged(\n source: ExtensionInstallSource,\n previous: ExtensionInstallProvenance,\n io: ExtensionInstallIo,\n): Promise<boolean | null> {\n if (source.kind === 'npm') {\n try {\n const { version } = await resolveNpmVersion(source, io.fetch ?? fetch)\n return `npm:${source.pkg}@${version}` === previous.source\n }\n catch {\n return null\n }\n }\n if (!previous.commit)\n return null\n const which = io.which ?? defaultWhich\n const exec = io.exec ?? defaultExec\n if (!which('git'))\n return null\n const url = `https://github.com/${source.owner}/${source.repo}.git`\n const refspecs = source.ref ? [`refs/heads/${source.ref}`, `refs/tags/${source.ref}`] : ['HEAD']\n // GIT_TERMINAL_PROMPT=0 — never let git prompt for credentials from\n // inside the TUI; an unreachable remote degrades to \"can't tell\" (null).\n const res = await exec('git', ['ls-remote', url, ...refspecs], { env: { GIT_TERMINAL_PROMPT: '0' } })\n if (!res.ok)\n return null\n const sha = res.stdout.trim().split('\\n')[0]?.split('\\t')[0]?.trim()\n if (!sha || !/^[0-9a-f]{40}$/.test(sha))\n return null\n return sha === previous.commit\n}\n\n/**\n * Re-run the recorded install for an extension: re-resolves the original\n * spec (so `owner/repo` follows the branch and `pkg` follows `latest`,\n * while pinned refs/versions stay pinned) and replaces the install\n * IN PLACE (same root the extension currently lives in, even a\n * `.agents/extensions` one). Requires `.install.json` provenance —\n * hand-authored extensions have nothing to update from.\n *\n * When the remote is provably unchanged (recorded commit matches\n * `git ls-remote`, or the npm version re-resolves identically), the\n * reinstall is skipped entirely and the result carries `upToDate: true`.\n *\n * @param opts - The installed extension to update, plus progress and IO overrides.\n */\nexport async function updateInstalledExtension(\n opts: InstalledExtensionTargetOptions & {\n onProgress?: (message: string) => void\n io?: ExtensionInstallIo\n },\n): Promise<ExtensionInstallResult & { previous: ExtensionInstallProvenance, upToDate: boolean }> {\n const { dir, root, scope } = assertInstalledExtensionDir(opts)\n const provenance = readExtensionInstallProvenance(dir)\n if (!provenance)\n throw new ExtensionInstallError('no-provenance', `${dir} has no .install.json. It was not installed from a source, so there is nothing to update from.`)\n\n const source = parseExtensionInstallSource(provenance.spec)\n opts.onProgress?.(`checking ${provenance.source} for changes…`)\n if (await checkRemoteUnchanged(source, provenance, opts.io ?? {}) === true) {\n return {\n name: basename(dir),\n dir,\n scope,\n provenance: provenance.source,\n ...(provenance.commit !== undefined ? { commit: provenance.commit } : {}),\n warnings: [],\n previous: provenance,\n upToDate: true,\n }\n }\n\n const result = await installExtensionFromSpec({\n source: provenance.spec,\n ...(provenance.extension !== undefined ? { extension: provenance.extension } : {}),\n // Scope from where the extension ACTUALLY lives (the containment\n // root), not the recorded field — a stale/corrupt provenance scope\n // would otherwise self-perpetuate through every future update.\n scope,\n overwrite: true,\n installRoot: root,\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.onProgress !== undefined ? { onProgress: opts.onProgress } : {}),\n ...(opts.io !== undefined ? { io: opts.io } : {}),\n })\n // The upstream may have renamed its definition — the fresh install then\n // landed in a sibling folder. Remove the superseded dir so an update\n // never leaves two generations installed side by side.\n if (result.dir !== dir)\n rmSync(dir, { recursive: true, force: true })\n return { ...result, previous: provenance, upToDate: false }\n}\n\n/** The scope's canonical `.{prefix}/extensions` write root. */\nfunction resolveInstallRoot(opts: { scope: ExtensionInstallScope, cwd?: string, home?: string, prefix?: string }): string {\n const prefix = (opts.prefix ?? '.zidane').replace(/^\\./, '')\n const paths = projectUserPaths({\n subPath: 'extensions',\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n })\n const root = paths.find(p => p.source === opts.scope && p.path.includes(`.${prefix}`))\n if (!root)\n throw new Error(`unreachable: projectUserPaths yields a .${prefix} root per scope`)\n return root.path\n}\n\nasync function validateCandidate(\n candidate: ExtensionInstallCandidate,\n timeoutMs: number,\n): Promise<{ name: string, warning?: string }> {\n const entry = findEntryFile(candidate.dir)\n if (!entry)\n throw new ExtensionInstallError('no-extension', `${candidate.dir} has no index.{ts,tsx,mjs,js} entry.`)\n const version = checkZidaneVersion(candidate.dir)\n if (version?.level === 'error')\n throw new ExtensionInstallError('validate-failed', version.message)\n let mod: unknown\n try {\n mod = await withTimeout(\n importExtensionEntry(entry, candidate.dir),\n timeoutMs,\n `Timed out importing ${entry} after ${timeoutMs}ms.`,\n )\n }\n catch (err) {\n throw new ExtensionInstallError('validate-failed', `Cannot import the extension: ${errorMessage(err)}`)\n }\n finally {\n // The staged dir moves or is deleted next: nothing may stay cached under it.\n releaseExtensionModules(candidate.dir)\n }\n const m = (mod ?? {}) as Record<string, unknown>\n const definition = isExtensionDefinition(m.default) ? m.default : isExtensionDefinition(m.extension) ? m.extension : null\n if (!definition)\n throw new ExtensionInstallError('validate-failed', `Module at ${entry} does not export an ExtensionDefinition (expected \\`default\\` or named \\`extension\\`).`)\n if (definition.apiVersion !== undefined && definition.apiVersion !== 1)\n throw new ExtensionInstallError('validate-failed', `Unsupported extension API version \"${definition.apiVersion}\". This runtime supports apiVersion 1.`)\n if (!/^[\\w.-]+$/.test(definition.name) || definition.name.startsWith('.'))\n throw new ExtensionInstallError('validate-failed', `Extension name \"${definition.name}\" is not folder-safe ([A-Za-z0-9_.-], no leading dot).`)\n const manifest = readExtensionManifest(candidate.dir).manifest\n if (manifest?.apiVersion !== undefined && manifest.apiVersion !== 1)\n throw new ExtensionInstallError('validate-failed', `Unsupported extension API version \"${manifest.apiVersion}\" in manifest. This runtime supports apiVersion 1.`)\n if (manifest?.name && manifest.name !== definition.name)\n throw new ExtensionInstallError('validate-failed', `Manifest name \"${manifest.name}\" does not match the module's exported name \"${definition.name}\".`)\n return { name: definition.name, ...(version ? { warning: version.message } : {}) }\n}\n\n// ---------------------------------------------------------------------------\n// Dependency install\n// ---------------------------------------------------------------------------\n\nconst PM_COMMANDS: ReadonlyArray<{ cmd: string, args: readonly string[] }> = [\n // bun skips lifecycle scripts by default (trustedDependencies opt-in).\n { cmd: 'bun', args: ['install'] },\n { cmd: 'npm', args: ['install', '--ignore-scripts', '--no-audit', '--no-fund'] },\n { cmd: 'pnpm', args: ['install', '--ignore-scripts'] },\n { cmd: 'yarn', args: ['install', '--ignore-scripts'] },\n]\n\n/** Install deps when `dir/package.json` declares any. Returns null when skipped. */\nasync function installDependencies(\n dir: string,\n io: ExtensionInstallIo,\n onProgress?: (message: string) => void,\n): Promise<{ warnings: string[] } | null> {\n const pkg = readJson(join(dir, 'package.json')) as { dependencies?: Record<string, string> } | null\n if (!pkg?.dependencies || Object.keys(pkg.dependencies).length === 0)\n return null\n const exec = io.exec ?? defaultExec\n const which = io.which ?? defaultWhich\n const pm = PM_COMMANDS.find(p => which(p.cmd))\n if (!pm) {\n throw new ExtensionInstallError('no-package-manager', 'The extension has dependencies, but no package manager (bun, npm, pnpm or yarn) is on PATH.')\n }\n onProgress?.(`installing dependencies with ${pm.cmd}…`)\n const res = await exec(pm.cmd, pm.args, { cwd: dir })\n if (!res.ok)\n throw new ExtensionInstallError('deps-failed', `${pm.cmd} install failed: ${lastLine(res.stderr) || lastLine(res.stdout)}`)\n return { warnings: [] }\n}\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction readJson(path: string): unknown {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch {\n return null\n }\n}\n","/**\n * Prompt bridge — the host hand-off behind `ctx.submitPrompt`.\n *\n * Extensions submit prompts at any point in their lifetime; the host (the\n * TUI, post-mount) attaches the single handler that routes them into its\n * real submit machinery (run-or-queue gate, `Agent.steer`, `Agent.followUp`).\n * Same holder idiom as the HUD. Prompts are deliberately NOT\n * buffered pre-attach: silently replaying stale prompts after mount would\n * be surprising — `submit` returns `false` so callers can react.\n */\n\nexport interface ExtensionPromptDelivery {\n /**\n * `'auto'` (default) — run now when idle, join the follow-up queue when a\n * run is active. `'steer'` — inject into the CURRENT run between turns\n * (falls back to `auto` when idle). `'followUp'` — queue until the current\n * run finishes (falls back to `auto` when idle).\n */\n delivery?: 'auto' | 'steer' | 'followUp'\n}\n\n/** Connects `ctx.submitPrompt` to the host's prompt pipeline. Hosts attach their handler after the UI mounts. */\nexport interface ExtensionPromptBridge {\n /** Route a prompt to the host. `false` when no host handler is attached, the text is blank, or the handler throws. */\n submit: (text: string, opts?: ExtensionPromptDelivery) => boolean\n /** Attach the host handler (last attach wins). Returns a detach fn. */\n attach: (handler: (text: string, opts?: ExtensionPromptDelivery) => void) => () => void\n}\n\n/** Creates a bridge with no handler attached. `submit` returns `false` until a host attaches one. */\nexport function createExtensionPromptBridge(): ExtensionPromptBridge {\n let handler: ((text: string, opts?: ExtensionPromptDelivery) => void) | null = null\n return {\n submit(text, opts) {\n if (!handler || text.trim().length === 0)\n return false\n try {\n handler(text, opts)\n return true\n }\n catch {\n // The host submit path must never throw into extension code.\n return false\n }\n },\n attach(next) {\n handler = next\n return () => {\n if (handler === next)\n handler = null\n }\n },\n }\n}\n"],"mappings":"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AS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAGE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EAOE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAeA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAEA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CAEA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SAAS;EACT,OAAO;EACP,aAAa;EACb,OAAO;CACT;CACA;EACE,QAAQ;EACR,SA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"}