@hansjm10/volt-coding-agent 0.0.0-bootstrap.0 → 0.1.0

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 (1364) hide show
  1. package/BINARY-CAPABILITIES.md +28 -0
  2. package/CHANGELOG.md +4845 -0
  3. package/LICENSE +1 -1
  4. package/README.md +716 -3
  5. package/THIRD-PARTY-NOTICES.md +55 -0
  6. package/dist/LICENSES/clipboard-image-MIT.txt +9 -0
  7. package/dist/LICENSES/highlight.js-10.7.3-BSD-3-Clause.txt +29 -0
  8. package/dist/LICENSES/highlight.js-11.9.0-BSD-3-Clause.txt +29 -0
  9. package/dist/LICENSES/marked-18.0.5-LICENSE.txt +44 -0
  10. package/dist/LICENSES/run-jxa-MIT.txt +9 -0
  11. package/dist/cli/args.d.ts +60 -0
  12. package/dist/cli/args.d.ts.map +1 -0
  13. package/dist/cli/args.js +414 -0
  14. package/dist/cli/args.js.map +1 -0
  15. package/dist/cli/config-selector.d.ts +14 -0
  16. package/dist/cli/config-selector.d.ts.map +1 -0
  17. package/dist/cli/config-selector.js +31 -0
  18. package/dist/cli/config-selector.js.map +1 -0
  19. package/dist/cli/file-processor.d.ts +15 -0
  20. package/dist/cli/file-processor.d.ts.map +1 -0
  21. package/dist/cli/file-processor.js +85 -0
  22. package/dist/cli/file-processor.js.map +1 -0
  23. package/dist/cli/initial-message.d.ts +18 -0
  24. package/dist/cli/initial-message.d.ts.map +1 -0
  25. package/dist/cli/initial-message.js +22 -0
  26. package/dist/cli/initial-message.js.map +1 -0
  27. package/dist/cli/list-models.d.ts +9 -0
  28. package/dist/cli/list-models.d.ts.map +1 -0
  29. package/dist/cli/list-models.js +98 -0
  30. package/dist/cli/list-models.js.map +1 -0
  31. package/dist/cli/project-trust.d.ts +10 -0
  32. package/dist/cli/project-trust.d.ts.map +1 -0
  33. package/dist/cli/project-trust.js +48 -0
  34. package/dist/cli/project-trust.js.map +1 -0
  35. package/dist/cli/session-picker.d.ts +9 -0
  36. package/dist/cli/session-picker.d.ts.map +1 -0
  37. package/dist/cli/session-picker.js +35 -0
  38. package/dist/cli/session-picker.js.map +1 -0
  39. package/dist/cli/startup-ui.d.ts +17 -0
  40. package/dist/cli/startup-ui.d.ts.map +1 -0
  41. package/dist/cli/startup-ui.js +133 -0
  42. package/dist/cli/startup-ui.js.map +1 -0
  43. package/dist/cli.d.ts +3 -0
  44. package/dist/cli.d.ts.map +1 -0
  45. package/dist/cli.js +18 -0
  46. package/dist/cli.js.map +1 -0
  47. package/dist/config.d.ts +85 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/config.js +487 -0
  50. package/dist/config.js.map +1 -0
  51. package/dist/core/agent-session-runtime.d.ts +160 -0
  52. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  53. package/dist/core/agent-session-runtime.js +397 -0
  54. package/dist/core/agent-session-runtime.js.map +1 -0
  55. package/dist/core/agent-session-services.d.ts +98 -0
  56. package/dist/core/agent-session-services.d.ts.map +1 -0
  57. package/dist/core/agent-session-services.js +125 -0
  58. package/dist/core/agent-session-services.js.map +1 -0
  59. package/dist/core/agent-session.d.ts +739 -0
  60. package/dist/core/agent-session.d.ts.map +1 -0
  61. package/dist/core/agent-session.js +3158 -0
  62. package/dist/core/agent-session.js.map +1 -0
  63. package/dist/core/auth-guidance.d.ts +5 -0
  64. package/dist/core/auth-guidance.d.ts.map +1 -0
  65. package/dist/core/auth-guidance.js +21 -0
  66. package/dist/core/auth-guidance.js.map +1 -0
  67. package/dist/core/auth-storage.d.ts +159 -0
  68. package/dist/core/auth-storage.d.ts.map +1 -0
  69. package/dist/core/auth-storage.js +509 -0
  70. package/dist/core/auth-storage.js.map +1 -0
  71. package/dist/core/bash-executor.d.ts +32 -0
  72. package/dist/core/bash-executor.d.ts.map +1 -0
  73. package/dist/core/bash-executor.js +111 -0
  74. package/dist/core/bash-executor.js.map +1 -0
  75. package/dist/core/compaction/branch-summarization.d.ts +92 -0
  76. package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
  77. package/dist/core/compaction/branch-summarization.js +257 -0
  78. package/dist/core/compaction/branch-summarization.js.map +1 -0
  79. package/dist/core/compaction/compaction.d.ts +121 -0
  80. package/dist/core/compaction/compaction.d.ts.map +1 -0
  81. package/dist/core/compaction/compaction.js +658 -0
  82. package/dist/core/compaction/compaction.js.map +1 -0
  83. package/dist/core/compaction/index.d.ts +7 -0
  84. package/dist/core/compaction/index.d.ts.map +1 -0
  85. package/dist/core/compaction/index.js +7 -0
  86. package/dist/core/compaction/index.js.map +1 -0
  87. package/dist/core/compaction/utils.d.ts +56 -0
  88. package/dist/core/compaction/utils.d.ts.map +1 -0
  89. package/dist/core/compaction/utils.js +271 -0
  90. package/dist/core/compaction/utils.js.map +1 -0
  91. package/dist/core/defaults.d.ts +3 -0
  92. package/dist/core/defaults.d.ts.map +1 -0
  93. package/dist/core/defaults.js +2 -0
  94. package/dist/core/defaults.js.map +1 -0
  95. package/dist/core/diagnostics.d.ts +15 -0
  96. package/dist/core/diagnostics.d.ts.map +1 -0
  97. package/dist/core/diagnostics.js +2 -0
  98. package/dist/core/diagnostics.js.map +1 -0
  99. package/dist/core/event-bus.d.ts +9 -0
  100. package/dist/core/event-bus.d.ts.map +1 -0
  101. package/dist/core/event-bus.js +25 -0
  102. package/dist/core/event-bus.js.map +1 -0
  103. package/dist/core/exec.d.ts +29 -0
  104. package/dist/core/exec.d.ts.map +1 -0
  105. package/dist/core/exec.js +75 -0
  106. package/dist/core/exec.js.map +1 -0
  107. package/dist/core/experimental.d.ts +2 -0
  108. package/dist/core/experimental.d.ts.map +1 -0
  109. package/dist/core/experimental.js +4 -0
  110. package/dist/core/experimental.js.map +1 -0
  111. package/dist/core/export-html/ansi-to-html.d.ts +22 -0
  112. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  113. package/dist/core/export-html/ansi-to-html.js +249 -0
  114. package/dist/core/export-html/ansi-to-html.js.map +1 -0
  115. package/dist/core/export-html/index.d.ts +37 -0
  116. package/dist/core/export-html/index.d.ts.map +1 -0
  117. package/dist/core/export-html/index.js +234 -0
  118. package/dist/core/export-html/index.js.map +1 -0
  119. package/dist/core/export-html/template.css +1066 -0
  120. package/dist/core/export-html/template.html +55 -0
  121. package/dist/core/export-html/template.js +1864 -0
  122. package/dist/core/export-html/tool-renderer.d.ts +34 -0
  123. package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  124. package/dist/core/export-html/tool-renderer.js +108 -0
  125. package/dist/core/export-html/tool-renderer.js.map +1 -0
  126. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  127. package/dist/core/export-html/vendor/marked.min.js +78 -0
  128. package/dist/core/extensions/index.d.ts +12 -0
  129. package/dist/core/extensions/index.d.ts.map +1 -0
  130. package/dist/core/extensions/index.js +9 -0
  131. package/dist/core/extensions/index.js.map +1 -0
  132. package/dist/core/extensions/loader.d.ts +25 -0
  133. package/dist/core/extensions/loader.d.ts.map +1 -0
  134. package/dist/core/extensions/loader.js +516 -0
  135. package/dist/core/extensions/loader.js.map +1 -0
  136. package/dist/core/extensions/runner.d.ts +180 -0
  137. package/dist/core/extensions/runner.d.ts.map +1 -0
  138. package/dist/core/extensions/runner.js +938 -0
  139. package/dist/core/extensions/runner.js.map +1 -0
  140. package/dist/core/extensions/types.d.ts +1215 -0
  141. package/dist/core/extensions/types.d.ts.map +1 -0
  142. package/dist/core/extensions/types.js +48 -0
  143. package/dist/core/extensions/types.js.map +1 -0
  144. package/dist/core/extensions/wrapper.d.ts +20 -0
  145. package/dist/core/extensions/wrapper.d.ts.map +1 -0
  146. package/dist/core/extensions/wrapper.js +22 -0
  147. package/dist/core/extensions/wrapper.js.map +1 -0
  148. package/dist/core/footer-data-provider.d.ts +54 -0
  149. package/dist/core/footer-data-provider.d.ts.map +1 -0
  150. package/dist/core/footer-data-provider.js +338 -0
  151. package/dist/core/footer-data-provider.js.map +1 -0
  152. package/dist/core/host-actions.d.ts +107 -0
  153. package/dist/core/host-actions.d.ts.map +1 -0
  154. package/dist/core/host-actions.js +632 -0
  155. package/dist/core/host-actions.js.map +1 -0
  156. package/dist/core/host-interaction.d.ts +35 -0
  157. package/dist/core/host-interaction.d.ts.map +1 -0
  158. package/dist/core/host-interaction.js +2 -0
  159. package/dist/core/host-interaction.js.map +1 -0
  160. package/dist/core/http-dispatcher.d.ts +22 -0
  161. package/dist/core/http-dispatcher.d.ts.map +1 -0
  162. package/dist/core/http-dispatcher.js +64 -0
  163. package/dist/core/http-dispatcher.js.map +1 -0
  164. package/dist/core/index.d.ts +18 -0
  165. package/dist/core/index.d.ts.map +1 -0
  166. package/dist/core/index.js +17 -0
  167. package/dist/core/index.js.map +1 -0
  168. package/dist/core/keybindings.d.ts +373 -0
  169. package/dist/core/keybindings.d.ts.map +1 -0
  170. package/dist/core/keybindings.js +303 -0
  171. package/dist/core/keybindings.js.map +1 -0
  172. package/dist/core/lsp/client.d.ts +119 -0
  173. package/dist/core/lsp/client.d.ts.map +1 -0
  174. package/dist/core/lsp/client.js +711 -0
  175. package/dist/core/lsp/client.js.map +1 -0
  176. package/dist/core/lsp/config.d.ts +96 -0
  177. package/dist/core/lsp/config.d.ts.map +1 -0
  178. package/dist/core/lsp/config.js +206 -0
  179. package/dist/core/lsp/config.js.map +1 -0
  180. package/dist/core/lsp/index.d.ts +6 -0
  181. package/dist/core/lsp/index.d.ts.map +1 -0
  182. package/dist/core/lsp/index.js +6 -0
  183. package/dist/core/lsp/index.js.map +1 -0
  184. package/dist/core/lsp/manager.d.ts +122 -0
  185. package/dist/core/lsp/manager.d.ts.map +1 -0
  186. package/dist/core/lsp/manager.js +1136 -0
  187. package/dist/core/lsp/manager.js.map +1 -0
  188. package/dist/core/lsp/trace.d.ts +26 -0
  189. package/dist/core/lsp/trace.d.ts.map +1 -0
  190. package/dist/core/lsp/trace.js +98 -0
  191. package/dist/core/lsp/trace.js.map +1 -0
  192. package/dist/core/lsp/workspace-edit.d.ts +60 -0
  193. package/dist/core/lsp/workspace-edit.d.ts.map +1 -0
  194. package/dist/core/lsp/workspace-edit.js +71 -0
  195. package/dist/core/lsp/workspace-edit.js.map +1 -0
  196. package/dist/core/mcp/audit.d.ts +39 -0
  197. package/dist/core/mcp/audit.d.ts.map +1 -0
  198. package/dist/core/mcp/audit.js +35 -0
  199. package/dist/core/mcp/audit.js.map +1 -0
  200. package/dist/core/mcp/auth.d.ts +7 -0
  201. package/dist/core/mcp/auth.d.ts.map +1 -0
  202. package/dist/core/mcp/auth.js +56 -0
  203. package/dist/core/mcp/auth.js.map +1 -0
  204. package/dist/core/mcp/client-factory.d.ts +20 -0
  205. package/dist/core/mcp/client-factory.d.ts.map +1 -0
  206. package/dist/core/mcp/client-factory.js +200 -0
  207. package/dist/core/mcp/client-factory.js.map +1 -0
  208. package/dist/core/mcp/config-loader.d.ts +9 -0
  209. package/dist/core/mcp/config-loader.d.ts.map +1 -0
  210. package/dist/core/mcp/config-loader.js +104 -0
  211. package/dist/core/mcp/config-loader.js.map +1 -0
  212. package/dist/core/mcp/config-writer.d.ts +21 -0
  213. package/dist/core/mcp/config-writer.d.ts.map +1 -0
  214. package/dist/core/mcp/config-writer.js +124 -0
  215. package/dist/core/mcp/config-writer.js.map +1 -0
  216. package/dist/core/mcp/config.d.ts +32 -0
  217. package/dist/core/mcp/config.d.ts.map +1 -0
  218. package/dist/core/mcp/config.js +385 -0
  219. package/dist/core/mcp/config.js.map +1 -0
  220. package/dist/core/mcp/direct-tools.d.ts +11 -0
  221. package/dist/core/mcp/direct-tools.d.ts.map +1 -0
  222. package/dist/core/mcp/direct-tools.js +47 -0
  223. package/dist/core/mcp/direct-tools.js.map +1 -0
  224. package/dist/core/mcp/gateway-tool.d.ts +29 -0
  225. package/dist/core/mcp/gateway-tool.d.ts.map +1 -0
  226. package/dist/core/mcp/gateway-tool.js +108 -0
  227. package/dist/core/mcp/gateway-tool.js.map +1 -0
  228. package/dist/core/mcp/index.d.ts +19 -0
  229. package/dist/core/mcp/index.d.ts.map +1 -0
  230. package/dist/core/mcp/index.js +18 -0
  231. package/dist/core/mcp/index.js.map +1 -0
  232. package/dist/core/mcp/manager.d.ts +117 -0
  233. package/dist/core/mcp/manager.d.ts.map +1 -0
  234. package/dist/core/mcp/manager.js +819 -0
  235. package/dist/core/mcp/manager.js.map +1 -0
  236. package/dist/core/mcp/metadata-cache.d.ts +27 -0
  237. package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
  238. package/dist/core/mcp/metadata-cache.js +152 -0
  239. package/dist/core/mcp/metadata-cache.js.map +1 -0
  240. package/dist/core/mcp/oauth-flow.d.ts +94 -0
  241. package/dist/core/mcp/oauth-flow.d.ts.map +1 -0
  242. package/dist/core/mcp/oauth-flow.js +473 -0
  243. package/dist/core/mcp/oauth-flow.js.map +1 -0
  244. package/dist/core/mcp/oauth-provider.d.ts +39 -0
  245. package/dist/core/mcp/oauth-provider.d.ts.map +1 -0
  246. package/dist/core/mcp/oauth-provider.js +149 -0
  247. package/dist/core/mcp/oauth-provider.js.map +1 -0
  248. package/dist/core/mcp/oauth-store.d.ts +27 -0
  249. package/dist/core/mcp/oauth-store.d.ts.map +1 -0
  250. package/dist/core/mcp/oauth-store.js +111 -0
  251. package/dist/core/mcp/oauth-store.js.map +1 -0
  252. package/dist/core/mcp/output-store.d.ts +55 -0
  253. package/dist/core/mcp/output-store.d.ts.map +1 -0
  254. package/dist/core/mcp/output-store.js +206 -0
  255. package/dist/core/mcp/output-store.js.map +1 -0
  256. package/dist/core/mcp/rpc.d.ts +13 -0
  257. package/dist/core/mcp/rpc.d.ts.map +1 -0
  258. package/dist/core/mcp/rpc.js +29 -0
  259. package/dist/core/mcp/rpc.js.map +1 -0
  260. package/dist/core/mcp/safety.d.ts +6 -0
  261. package/dist/core/mcp/safety.d.ts.map +1 -0
  262. package/dist/core/mcp/safety.js +65 -0
  263. package/dist/core/mcp/safety.js.map +1 -0
  264. package/dist/core/mcp/search.d.ts +8 -0
  265. package/dist/core/mcp/search.d.ts.map +1 -0
  266. package/dist/core/mcp/search.js +85 -0
  267. package/dist/core/mcp/search.js.map +1 -0
  268. package/dist/core/mcp/server-supervisor.d.ts +59 -0
  269. package/dist/core/mcp/server-supervisor.d.ts.map +1 -0
  270. package/dist/core/mcp/server-supervisor.js +331 -0
  271. package/dist/core/mcp/server-supervisor.js.map +1 -0
  272. package/dist/core/mcp/types.d.ts +321 -0
  273. package/dist/core/mcp/types.d.ts.map +1 -0
  274. package/dist/core/mcp/types.js +2 -0
  275. package/dist/core/mcp/types.js.map +1 -0
  276. package/dist/core/messages.d.ts +78 -0
  277. package/dist/core/messages.d.ts.map +1 -0
  278. package/dist/core/messages.js +138 -0
  279. package/dist/core/messages.js.map +1 -0
  280. package/dist/core/model-catalog-watcher.d.ts +36 -0
  281. package/dist/core/model-catalog-watcher.d.ts.map +1 -0
  282. package/dist/core/model-catalog-watcher.js +101 -0
  283. package/dist/core/model-catalog-watcher.js.map +1 -0
  284. package/dist/core/model-registry.d.ts +160 -0
  285. package/dist/core/model-registry.d.ts.map +1 -0
  286. package/dist/core/model-registry.js +754 -0
  287. package/dist/core/model-registry.js.map +1 -0
  288. package/dist/core/model-resolver.d.ts +111 -0
  289. package/dist/core/model-resolver.d.ts.map +1 -0
  290. package/dist/core/model-resolver.js +534 -0
  291. package/dist/core/model-resolver.js.map +1 -0
  292. package/dist/core/output-guard.d.ts +7 -0
  293. package/dist/core/output-guard.d.ts.map +1 -0
  294. package/dist/core/output-guard.js +89 -0
  295. package/dist/core/output-guard.js.map +1 -0
  296. package/dist/core/package-manager.d.ts +220 -0
  297. package/dist/core/package-manager.d.ts.map +1 -0
  298. package/dist/core/package-manager.js +2217 -0
  299. package/dist/core/package-manager.js.map +1 -0
  300. package/dist/core/project-trust.d.ts +15 -0
  301. package/dist/core/project-trust.d.ts.map +1 -0
  302. package/dist/core/project-trust.js +58 -0
  303. package/dist/core/project-trust.js.map +1 -0
  304. package/dist/core/prompt-templates.d.ts +53 -0
  305. package/dist/core/prompt-templates.d.ts.map +1 -0
  306. package/dist/core/prompt-templates.js +236 -0
  307. package/dist/core/prompt-templates.js.map +1 -0
  308. package/dist/core/provider-attribution.d.ts +4 -0
  309. package/dist/core/provider-attribution.d.ts.map +1 -0
  310. package/dist/core/provider-attribution.js +81 -0
  311. package/dist/core/provider-attribution.js.map +1 -0
  312. package/dist/core/provider-display-names.d.ts +2 -0
  313. package/dist/core/provider-display-names.d.ts.map +1 -0
  314. package/dist/core/provider-display-names.js +36 -0
  315. package/dist/core/provider-display-names.js.map +1 -0
  316. package/dist/core/remote/iroh/access-grant.d.ts +40 -0
  317. package/dist/core/remote/iroh/access-grant.d.ts.map +1 -0
  318. package/dist/core/remote/iroh/access-grant.js +199 -0
  319. package/dist/core/remote/iroh/access-grant.js.map +1 -0
  320. package/dist/core/remote/iroh/active-stream-registry.d.ts +39 -0
  321. package/dist/core/remote/iroh/active-stream-registry.d.ts.map +1 -0
  322. package/dist/core/remote/iroh/active-stream-registry.js +109 -0
  323. package/dist/core/remote/iroh/active-stream-registry.js.map +1 -0
  324. package/dist/core/remote/iroh/audit.d.ts +67 -0
  325. package/dist/core/remote/iroh/audit.d.ts.map +1 -0
  326. package/dist/core/remote/iroh/audit.js +274 -0
  327. package/dist/core/remote/iroh/audit.js.map +1 -0
  328. package/dist/core/remote/iroh/authorization.d.ts +46 -0
  329. package/dist/core/remote/iroh/authorization.d.ts.map +1 -0
  330. package/dist/core/remote/iroh/authorization.js +327 -0
  331. package/dist/core/remote/iroh/authorization.js.map +1 -0
  332. package/dist/core/remote/iroh/control.d.ts +64 -0
  333. package/dist/core/remote/iroh/control.d.ts.map +1 -0
  334. package/dist/core/remote/iroh/control.js +326 -0
  335. package/dist/core/remote/iroh/control.js.map +1 -0
  336. package/dist/core/remote/iroh/device-log-rpc.d.ts +22 -0
  337. package/dist/core/remote/iroh/device-log-rpc.d.ts.map +1 -0
  338. package/dist/core/remote/iroh/device-log-rpc.js +152 -0
  339. package/dist/core/remote/iroh/device-log-rpc.js.map +1 -0
  340. package/dist/core/remote/iroh/engine.d.ts +148 -0
  341. package/dist/core/remote/iroh/engine.d.ts.map +1 -0
  342. package/dist/core/remote/iroh/engine.js +499 -0
  343. package/dist/core/remote/iroh/engine.js.map +1 -0
  344. package/dist/core/remote/iroh/handshake-reader.d.ts +19 -0
  345. package/dist/core/remote/iroh/handshake-reader.d.ts.map +1 -0
  346. package/dist/core/remote/iroh/handshake-reader.js +91 -0
  347. package/dist/core/remote/iroh/handshake-reader.js.map +1 -0
  348. package/dist/core/remote/iroh/handshake.d.ts +119 -0
  349. package/dist/core/remote/iroh/handshake.d.ts.map +1 -0
  350. package/dist/core/remote/iroh/handshake.js +621 -0
  351. package/dist/core/remote/iroh/handshake.js.map +1 -0
  352. package/dist/core/remote/iroh/host-policy.d.ts +13 -0
  353. package/dist/core/remote/iroh/host-policy.d.ts.map +1 -0
  354. package/dist/core/remote/iroh/host-policy.js +15 -0
  355. package/dist/core/remote/iroh/host-policy.js.map +1 -0
  356. package/dist/core/remote/iroh/index.d.ts +25 -0
  357. package/dist/core/remote/iroh/index.d.ts.map +1 -0
  358. package/dist/core/remote/iroh/index.js +25 -0
  359. package/dist/core/remote/iroh/index.js.map +1 -0
  360. package/dist/core/remote/iroh/metadata.d.ts +28 -0
  361. package/dist/core/remote/iroh/metadata.d.ts.map +1 -0
  362. package/dist/core/remote/iroh/metadata.js +16 -0
  363. package/dist/core/remote/iroh/metadata.js.map +1 -0
  364. package/dist/core/remote/iroh/outbound-filter.d.ts +29 -0
  365. package/dist/core/remote/iroh/outbound-filter.d.ts.map +1 -0
  366. package/dist/core/remote/iroh/outbound-filter.js +692 -0
  367. package/dist/core/remote/iroh/outbound-filter.js.map +1 -0
  368. package/dist/core/remote/iroh/protocol.d.ts +60 -0
  369. package/dist/core/remote/iroh/protocol.d.ts.map +1 -0
  370. package/dist/core/remote/iroh/protocol.js +204 -0
  371. package/dist/core/remote/iroh/protocol.js.map +1 -0
  372. package/dist/core/remote/iroh/push.d.ts +179 -0
  373. package/dist/core/remote/iroh/push.d.ts.map +1 -0
  374. package/dist/core/remote/iroh/push.js +662 -0
  375. package/dist/core/remote/iroh/push.js.map +1 -0
  376. package/dist/core/remote/iroh/qr.d.ts +12 -0
  377. package/dist/core/remote/iroh/qr.d.ts.map +1 -0
  378. package/dist/core/remote/iroh/qr.js +27 -0
  379. package/dist/core/remote/iroh/qr.js.map +1 -0
  380. package/dist/core/remote/iroh/rpc-command-filter.d.ts +47 -0
  381. package/dist/core/remote/iroh/rpc-command-filter.d.ts.map +1 -0
  382. package/dist/core/remote/iroh/rpc-command-filter.js +188 -0
  383. package/dist/core/remote/iroh/rpc-command-filter.js.map +1 -0
  384. package/dist/core/remote/iroh/rpc-transport.d.ts +21 -0
  385. package/dist/core/remote/iroh/rpc-transport.d.ts.map +1 -0
  386. package/dist/core/remote/iroh/rpc-transport.js +106 -0
  387. package/dist/core/remote/iroh/rpc-transport.js.map +1 -0
  388. package/dist/core/remote/iroh/state-manager.d.ts +141 -0
  389. package/dist/core/remote/iroh/state-manager.d.ts.map +1 -0
  390. package/dist/core/remote/iroh/state-manager.js +744 -0
  391. package/dist/core/remote/iroh/state-manager.js.map +1 -0
  392. package/dist/core/remote/iroh/state.d.ts +141 -0
  393. package/dist/core/remote/iroh/state.d.ts.map +1 -0
  394. package/dist/core/remote/iroh/state.js +433 -0
  395. package/dist/core/remote/iroh/state.js.map +1 -0
  396. package/dist/core/remote/iroh/ticket.d.ts +46 -0
  397. package/dist/core/remote/iroh/ticket.d.ts.map +1 -0
  398. package/dist/core/remote/iroh/ticket.js +267 -0
  399. package/dist/core/remote/iroh/ticket.js.map +1 -0
  400. package/dist/core/remote/iroh/transcript-text.d.ts +8 -0
  401. package/dist/core/remote/iroh/transcript-text.d.ts.map +1 -0
  402. package/dist/core/remote/iroh/transcript-text.js +26 -0
  403. package/dist/core/remote/iroh/transcript-text.js.map +1 -0
  404. package/dist/core/remote/iroh/workspace-rpc.d.ts +31 -0
  405. package/dist/core/remote/iroh/workspace-rpc.d.ts.map +1 -0
  406. package/dist/core/remote/iroh/workspace-rpc.js +73 -0
  407. package/dist/core/remote/iroh/workspace-rpc.js.map +1 -0
  408. package/dist/core/remote/iroh/workspace.d.ts +16 -0
  409. package/dist/core/remote/iroh/workspace.d.ts.map +1 -0
  410. package/dist/core/remote/iroh/workspace.js +104 -0
  411. package/dist/core/remote/iroh/workspace.js.map +1 -0
  412. package/dist/core/remote/iroh/worktree-rpc.d.ts +83 -0
  413. package/dist/core/remote/iroh/worktree-rpc.d.ts.map +1 -0
  414. package/dist/core/remote/iroh/worktree-rpc.js +148 -0
  415. package/dist/core/remote/iroh/worktree-rpc.js.map +1 -0
  416. package/dist/core/resolve-config-value.d.ts +30 -0
  417. package/dist/core/resolve-config-value.d.ts.map +1 -0
  418. package/dist/core/resolve-config-value.js +245 -0
  419. package/dist/core/resolve-config-value.js.map +1 -0
  420. package/dist/core/resource-loader.d.ts +214 -0
  421. package/dist/core/resource-loader.d.ts.map +1 -0
  422. package/dist/core/resource-loader.js +824 -0
  423. package/dist/core/resource-loader.js.map +1 -0
  424. package/dist/core/review.d.ts +318 -0
  425. package/dist/core/review.d.ts.map +1 -0
  426. package/dist/core/review.js +1271 -0
  427. package/dist/core/review.js.map +1 -0
  428. package/dist/core/rpc/duplex-write-gate.d.ts +29 -0
  429. package/dist/core/rpc/duplex-write-gate.d.ts.map +1 -0
  430. package/dist/core/rpc/duplex-write-gate.js +124 -0
  431. package/dist/core/rpc/duplex-write-gate.js.map +1 -0
  432. package/dist/core/rpc/index.d.ts +6 -0
  433. package/dist/core/rpc/index.d.ts.map +1 -0
  434. package/dist/core/rpc/index.js +5 -0
  435. package/dist/core/rpc/index.js.map +1 -0
  436. package/dist/core/rpc/iroh-transport.d.ts +58 -0
  437. package/dist/core/rpc/iroh-transport.d.ts.map +1 -0
  438. package/dist/core/rpc/iroh-transport.js +278 -0
  439. package/dist/core/rpc/iroh-transport.js.map +1 -0
  440. package/dist/core/rpc/jsonl.d.ts +17 -0
  441. package/dist/core/rpc/jsonl.d.ts.map +1 -0
  442. package/dist/core/rpc/jsonl.js +49 -0
  443. package/dist/core/rpc/jsonl.js.map +1 -0
  444. package/dist/core/rpc/loopback-transport.d.ts +14 -0
  445. package/dist/core/rpc/loopback-transport.d.ts.map +1 -0
  446. package/dist/core/rpc/loopback-transport.js +120 -0
  447. package/dist/core/rpc/loopback-transport.js.map +1 -0
  448. package/dist/core/rpc/transcript.d.ts +37 -0
  449. package/dist/core/rpc/transcript.d.ts.map +1 -0
  450. package/dist/core/rpc/transcript.js +624 -0
  451. package/dist/core/rpc/transcript.js.map +1 -0
  452. package/dist/core/rpc/transport.d.ts +41 -0
  453. package/dist/core/rpc/transport.d.ts.map +1 -0
  454. package/dist/core/rpc/transport.js +155 -0
  455. package/dist/core/rpc/transport.js.map +1 -0
  456. package/dist/core/rpc/types.d.ts +1254 -0
  457. package/dist/core/rpc/types.d.ts.map +1 -0
  458. package/dist/core/rpc/types.js +8 -0
  459. package/dist/core/rpc/types.js.map +1 -0
  460. package/dist/core/rpc/ui-action-args.d.ts +3 -0
  461. package/dist/core/rpc/ui-action-args.d.ts.map +1 -0
  462. package/dist/core/rpc/ui-action-args.js +66 -0
  463. package/dist/core/rpc/ui-action-args.js.map +1 -0
  464. package/dist/core/rpc/ui-actions.d.ts +43 -0
  465. package/dist/core/rpc/ui-actions.d.ts.map +1 -0
  466. package/dist/core/rpc/ui-actions.js +362 -0
  467. package/dist/core/rpc/ui-actions.js.map +1 -0
  468. package/dist/core/sdk.d.ts +138 -0
  469. package/dist/core/sdk.d.ts.map +1 -0
  470. package/dist/core/sdk.js +324 -0
  471. package/dist/core/sdk.js.map +1 -0
  472. package/dist/core/session-cwd.d.ts +19 -0
  473. package/dist/core/session-cwd.d.ts.map +1 -0
  474. package/dist/core/session-cwd.js +38 -0
  475. package/dist/core/session-cwd.js.map +1 -0
  476. package/dist/core/session-manager.d.ts +346 -0
  477. package/dist/core/session-manager.d.ts.map +1 -0
  478. package/dist/core/session-manager.js +1280 -0
  479. package/dist/core/session-manager.js.map +1 -0
  480. package/dist/core/settings-manager.d.ts +359 -0
  481. package/dist/core/settings-manager.d.ts.map +1 -0
  482. package/dist/core/settings-manager.js +1277 -0
  483. package/dist/core/settings-manager.js.map +1 -0
  484. package/dist/core/skills.d.ts +60 -0
  485. package/dist/core/skills.d.ts.map +1 -0
  486. package/dist/core/skills.js +387 -0
  487. package/dist/core/skills.js.map +1 -0
  488. package/dist/core/slash-commands.d.ts +14 -0
  489. package/dist/core/slash-commands.d.ts.map +1 -0
  490. package/dist/core/slash-commands.js +51 -0
  491. package/dist/core/slash-commands.js.map +1 -0
  492. package/dist/core/source-info.d.ts +18 -0
  493. package/dist/core/source-info.d.ts.map +1 -0
  494. package/dist/core/source-info.js +19 -0
  495. package/dist/core/source-info.js.map +1 -0
  496. package/dist/core/subagents/delegation-scope.d.ts +47 -0
  497. package/dist/core/subagents/delegation-scope.d.ts.map +1 -0
  498. package/dist/core/subagents/delegation-scope.js +134 -0
  499. package/dist/core/subagents/delegation-scope.js.map +1 -0
  500. package/dist/core/subagents/index.d.ts +46 -0
  501. package/dist/core/subagents/index.d.ts.map +1 -0
  502. package/dist/core/subagents/index.js +369 -0
  503. package/dist/core/subagents/index.js.map +1 -0
  504. package/dist/core/subagents/manager.d.ts +183 -0
  505. package/dist/core/subagents/manager.d.ts.map +1 -0
  506. package/dist/core/subagents/manager.js +820 -0
  507. package/dist/core/subagents/manager.js.map +1 -0
  508. package/dist/core/system-prompt.d.ts +28 -0
  509. package/dist/core/system-prompt.d.ts.map +1 -0
  510. package/dist/core/system-prompt.js +180 -0
  511. package/dist/core/system-prompt.js.map +1 -0
  512. package/dist/core/telemetry.d.ts +3 -0
  513. package/dist/core/telemetry.d.ts.map +1 -0
  514. package/dist/core/telemetry.js +9 -0
  515. package/dist/core/telemetry.js.map +1 -0
  516. package/dist/core/theme/dark.json +86 -0
  517. package/dist/core/theme/discovery.d.ts +48 -0
  518. package/dist/core/theme/discovery.d.ts.map +1 -0
  519. package/dist/core/theme/discovery.js +239 -0
  520. package/dist/core/theme/discovery.js.map +1 -0
  521. package/dist/core/theme/index.d.ts +6 -0
  522. package/dist/core/theme/index.d.ts.map +1 -0
  523. package/dist/core/theme/index.js +5 -0
  524. package/dist/core/theme/index.js.map +1 -0
  525. package/dist/core/theme/light.json +85 -0
  526. package/dist/core/theme/runtime.d.ts +55 -0
  527. package/dist/core/theme/runtime.d.ts.map +1 -0
  528. package/dist/core/theme/runtime.js +555 -0
  529. package/dist/core/theme/runtime.js.map +1 -0
  530. package/dist/core/theme/theme-schema.json +335 -0
  531. package/dist/core/theme/theme-service.d.ts +37 -0
  532. package/dist/core/theme/theme-service.d.ts.map +1 -0
  533. package/dist/core/theme/theme-service.js +222 -0
  534. package/dist/core/theme/theme-service.js.map +1 -0
  535. package/dist/core/theme/theme.d.ts +100 -0
  536. package/dist/core/theme/theme.d.ts.map +1 -0
  537. package/dist/core/theme/theme.js +243 -0
  538. package/dist/core/theme/theme.js.map +1 -0
  539. package/dist/core/theme/tokens.d.ts +20 -0
  540. package/dist/core/theme/tokens.d.ts.map +1 -0
  541. package/dist/core/theme/tokens.js +178 -0
  542. package/dist/core/theme/tokens.js.map +1 -0
  543. package/dist/core/theme/types.d.ts +21 -0
  544. package/dist/core/theme/types.d.ts.map +1 -0
  545. package/dist/core/theme/types.js +2 -0
  546. package/dist/core/theme/types.js.map +1 -0
  547. package/dist/core/timings.d.ts +8 -0
  548. package/dist/core/timings.d.ts.map +1 -0
  549. package/dist/core/timings.js +31 -0
  550. package/dist/core/timings.js.map +1 -0
  551. package/dist/core/tools/bash.d.ts +68 -0
  552. package/dist/core/tools/bash.d.ts.map +1 -0
  553. package/dist/core/tools/bash.js +345 -0
  554. package/dist/core/tools/bash.js.map +1 -0
  555. package/dist/core/tools/diagnostics-provider.d.ts +12 -0
  556. package/dist/core/tools/diagnostics-provider.d.ts.map +1 -0
  557. package/dist/core/tools/diagnostics-provider.js +2 -0
  558. package/dist/core/tools/diagnostics-provider.js.map +1 -0
  559. package/dist/core/tools/edit-diff.d.ts +77 -0
  560. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  561. package/dist/core/tools/edit-diff.js +506 -0
  562. package/dist/core/tools/edit-diff.js.map +1 -0
  563. package/dist/core/tools/edit.d.ts +56 -0
  564. package/dist/core/tools/edit.d.ts.map +1 -0
  565. package/dist/core/tools/edit.js +300 -0
  566. package/dist/core/tools/edit.js.map +1 -0
  567. package/dist/core/tools/file-mutation-queue.d.ts +6 -0
  568. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
  569. package/dist/core/tools/file-mutation-queue.js +52 -0
  570. package/dist/core/tools/file-mutation-queue.js.map +1 -0
  571. package/dist/core/tools/find.d.ts +35 -0
  572. package/dist/core/tools/find.d.ts.map +1 -0
  573. package/dist/core/tools/find.js +337 -0
  574. package/dist/core/tools/find.js.map +1 -0
  575. package/dist/core/tools/grep.d.ts +37 -0
  576. package/dist/core/tools/grep.d.ts.map +1 -0
  577. package/dist/core/tools/grep.js +305 -0
  578. package/dist/core/tools/grep.js.map +1 -0
  579. package/dist/core/tools/index.d.ts +56 -0
  580. package/dist/core/tools/index.d.ts.map +1 -0
  581. package/dist/core/tools/index.js +174 -0
  582. package/dist/core/tools/index.js.map +1 -0
  583. package/dist/core/tools/ls.d.ts +37 -0
  584. package/dist/core/tools/ls.d.ts.map +1 -0
  585. package/dist/core/tools/ls.js +167 -0
  586. package/dist/core/tools/ls.js.map +1 -0
  587. package/dist/core/tools/lsp.d.ts +59 -0
  588. package/dist/core/tools/lsp.d.ts.map +1 -0
  589. package/dist/core/tools/lsp.js +157 -0
  590. package/dist/core/tools/lsp.js.map +1 -0
  591. package/dist/core/tools/output-accumulator.d.ts +52 -0
  592. package/dist/core/tools/output-accumulator.d.ts.map +1 -0
  593. package/dist/core/tools/output-accumulator.js +184 -0
  594. package/dist/core/tools/output-accumulator.js.map +1 -0
  595. package/dist/core/tools/path-utils.d.ts +10 -0
  596. package/dist/core/tools/path-utils.d.ts.map +1 -0
  597. package/dist/core/tools/path-utils.js +99 -0
  598. package/dist/core/tools/path-utils.js.map +1 -0
  599. package/dist/core/tools/read.d.ts +35 -0
  600. package/dist/core/tools/read.d.ts.map +1 -0
  601. package/dist/core/tools/read.js +292 -0
  602. package/dist/core/tools/read.js.map +1 -0
  603. package/dist/core/tools/render-utils.d.ts +26 -0
  604. package/dist/core/tools/render-utils.d.ts.map +1 -0
  605. package/dist/core/tools/render-utils.js +69 -0
  606. package/dist/core/tools/render-utils.js.map +1 -0
  607. package/dist/core/tools/subagent.d.ts +219 -0
  608. package/dist/core/tools/subagent.d.ts.map +1 -0
  609. package/dist/core/tools/subagent.js +1445 -0
  610. package/dist/core/tools/subagent.js.map +1 -0
  611. package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
  612. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
  613. package/dist/core/tools/tool-definition-wrapper.js +34 -0
  614. package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
  615. package/dist/core/tools/truncate.d.ts +70 -0
  616. package/dist/core/tools/truncate.d.ts.map +1 -0
  617. package/dist/core/tools/truncate.js +215 -0
  618. package/dist/core/tools/truncate.js.map +1 -0
  619. package/dist/core/tools/web-search.d.ts +69 -0
  620. package/dist/core/tools/web-search.d.ts.map +1 -0
  621. package/dist/core/tools/web-search.js +634 -0
  622. package/dist/core/tools/web-search.js.map +1 -0
  623. package/dist/core/tools/write.d.ts +33 -0
  624. package/dist/core/tools/write.d.ts.map +1 -0
  625. package/dist/core/tools/write.js +210 -0
  626. package/dist/core/tools/write.js.map +1 -0
  627. package/dist/core/trust-manager.d.ts +36 -0
  628. package/dist/core/trust-manager.d.ts.map +1 -0
  629. package/dist/core/trust-manager.js +220 -0
  630. package/dist/core/trust-manager.js.map +1 -0
  631. package/dist/daemon/cli.d.ts +8 -0
  632. package/dist/daemon/cli.d.ts.map +1 -0
  633. package/dist/daemon/cli.js +468 -0
  634. package/dist/daemon/cli.js.map +1 -0
  635. package/dist/daemon/control-client.d.ts +60 -0
  636. package/dist/daemon/control-client.d.ts.map +1 -0
  637. package/dist/daemon/control-client.js +411 -0
  638. package/dist/daemon/control-client.js.map +1 -0
  639. package/dist/daemon/control-protocol.d.ts +507 -0
  640. package/dist/daemon/control-protocol.d.ts.map +1 -0
  641. package/dist/daemon/control-protocol.js +395 -0
  642. package/dist/daemon/control-protocol.js.map +1 -0
  643. package/dist/daemon/control-server.d.ts +77 -0
  644. package/dist/daemon/control-server.d.ts.map +1 -0
  645. package/dist/daemon/control-server.js +327 -0
  646. package/dist/daemon/control-server.js.map +1 -0
  647. package/dist/daemon/conversation-commands.d.ts +176 -0
  648. package/dist/daemon/conversation-commands.d.ts.map +1 -0
  649. package/dist/daemon/conversation-commands.js +1268 -0
  650. package/dist/daemon/conversation-commands.js.map +1 -0
  651. package/dist/daemon/daemon-lock.d.ts +27 -0
  652. package/dist/daemon/daemon-lock.d.ts.map +1 -0
  653. package/dist/daemon/daemon-lock.js +238 -0
  654. package/dist/daemon/daemon-lock.js.map +1 -0
  655. package/dist/daemon/handshake-responses.d.ts +47 -0
  656. package/dist/daemon/handshake-responses.d.ts.map +1 -0
  657. package/dist/daemon/handshake-responses.js +113 -0
  658. package/dist/daemon/handshake-responses.js.map +1 -0
  659. package/dist/daemon/integrated-runtimes.d.ts +137 -0
  660. package/dist/daemon/integrated-runtimes.d.ts.map +1 -0
  661. package/dist/daemon/integrated-runtimes.js +763 -0
  662. package/dist/daemon/integrated-runtimes.js.map +1 -0
  663. package/dist/daemon/iroh-native.d.ts +76 -0
  664. package/dist/daemon/iroh-native.d.ts.map +1 -0
  665. package/dist/daemon/iroh-native.js +18 -0
  666. package/dist/daemon/iroh-native.js.map +1 -0
  667. package/dist/daemon/iroh-resource-guard.d.ts +57 -0
  668. package/dist/daemon/iroh-resource-guard.d.ts.map +1 -0
  669. package/dist/daemon/iroh-resource-guard.js +134 -0
  670. package/dist/daemon/iroh-resource-guard.js.map +1 -0
  671. package/dist/daemon/iroh-service.d.ts +51 -0
  672. package/dist/daemon/iroh-service.d.ts.map +1 -0
  673. package/dist/daemon/iroh-service.js +2604 -0
  674. package/dist/daemon/iroh-service.js.map +1 -0
  675. package/dist/daemon/keep-awake.d.ts +62 -0
  676. package/dist/daemon/keep-awake.d.ts.map +1 -0
  677. package/dist/daemon/keep-awake.js +190 -0
  678. package/dist/daemon/keep-awake.js.map +1 -0
  679. package/dist/daemon/lease-broker.d.ts +139 -0
  680. package/dist/daemon/lease-broker.d.ts.map +1 -0
  681. package/dist/daemon/lease-broker.js +504 -0
  682. package/dist/daemon/lease-broker.js.map +1 -0
  683. package/dist/daemon/log.d.ts +16 -0
  684. package/dist/daemon/log.d.ts.map +1 -0
  685. package/dist/daemon/log.js +40 -0
  686. package/dist/daemon/log.js.map +1 -0
  687. package/dist/daemon/main.d.ts +70 -0
  688. package/dist/daemon/main.d.ts.map +1 -0
  689. package/dist/daemon/main.js +781 -0
  690. package/dist/daemon/main.js.map +1 -0
  691. package/dist/daemon/paths.d.ts +32 -0
  692. package/dist/daemon/paths.d.ts.map +1 -0
  693. package/dist/daemon/paths.js +208 -0
  694. package/dist/daemon/paths.js.map +1 -0
  695. package/dist/daemon/process-identity.d.ts +58 -0
  696. package/dist/daemon/process-identity.d.ts.map +1 -0
  697. package/dist/daemon/process-identity.js +185 -0
  698. package/dist/daemon/process-identity.js.map +1 -0
  699. package/dist/daemon/relay-stream.d.ts +89 -0
  700. package/dist/daemon/relay-stream.d.ts.map +1 -0
  701. package/dist/daemon/relay-stream.js +202 -0
  702. package/dist/daemon/relay-stream.js.map +1 -0
  703. package/dist/daemon/remote-cli.d.ts +7 -0
  704. package/dist/daemon/remote-cli.d.ts.map +1 -0
  705. package/dist/daemon/remote-cli.js +774 -0
  706. package/dist/daemon/remote-cli.js.map +1 -0
  707. package/dist/daemon/service-install.d.ts +43 -0
  708. package/dist/daemon/service-install.d.ts.map +1 -0
  709. package/dist/daemon/service-install.js +198 -0
  710. package/dist/daemon/service-install.js.map +1 -0
  711. package/dist/daemon/session-target.d.ts +62 -0
  712. package/dist/daemon/session-target.d.ts.map +1 -0
  713. package/dist/daemon/session-target.js +66 -0
  714. package/dist/daemon/session-target.js.map +1 -0
  715. package/dist/daemon/spawn.d.ts +53 -0
  716. package/dist/daemon/spawn.d.ts.map +1 -0
  717. package/dist/daemon/spawn.js +205 -0
  718. package/dist/daemon/spawn.js.map +1 -0
  719. package/dist/daemon/state.d.ts +145 -0
  720. package/dist/daemon/state.d.ts.map +1 -0
  721. package/dist/daemon/state.js +454 -0
  722. package/dist/daemon/state.js.map +1 -0
  723. package/dist/daemon/theme-push.d.ts +23 -0
  724. package/dist/daemon/theme-push.d.ts.map +1 -0
  725. package/dist/daemon/theme-push.js +30 -0
  726. package/dist/daemon/theme-push.js.map +1 -0
  727. package/dist/daemon/viewer-feed.d.ts +42 -0
  728. package/dist/daemon/viewer-feed.d.ts.map +1 -0
  729. package/dist/daemon/viewer-feed.js +138 -0
  730. package/dist/daemon/viewer-feed.js.map +1 -0
  731. package/dist/daemon/workspace-directory.d.ts +35 -0
  732. package/dist/daemon/workspace-directory.d.ts.map +1 -0
  733. package/dist/daemon/workspace-directory.js +105 -0
  734. package/dist/daemon/workspace-directory.js.map +1 -0
  735. package/dist/daemon/workspace-streams.d.ts +70 -0
  736. package/dist/daemon/workspace-streams.d.ts.map +1 -0
  737. package/dist/daemon/workspace-streams.js +289 -0
  738. package/dist/daemon/workspace-streams.js.map +1 -0
  739. package/dist/daemon/worktree-manager.d.ts +219 -0
  740. package/dist/daemon/worktree-manager.d.ts.map +1 -0
  741. package/dist/daemon/worktree-manager.js +1204 -0
  742. package/dist/daemon/worktree-manager.js.map +1 -0
  743. package/dist/index.d.ts +44 -0
  744. package/dist/index.d.ts.map +1 -0
  745. package/dist/index.js +57 -0
  746. package/dist/index.js.map +1 -0
  747. package/dist/main.d.ts +12 -0
  748. package/dist/main.d.ts.map +1 -0
  749. package/dist/main.js +773 -0
  750. package/dist/main.js.map +1 -0
  751. package/dist/mcp-cli.d.ts +6 -0
  752. package/dist/mcp-cli.d.ts.map +1 -0
  753. package/dist/mcp-cli.js +347 -0
  754. package/dist/mcp-cli.js.map +1 -0
  755. package/dist/migrations.d.ts +33 -0
  756. package/dist/migrations.d.ts.map +1 -0
  757. package/dist/migrations.js +281 -0
  758. package/dist/migrations.js.map +1 -0
  759. package/dist/modes/index.d.ts +13 -0
  760. package/dist/modes/index.d.ts.map +1 -0
  761. package/dist/modes/index.js +12 -0
  762. package/dist/modes/index.js.map +1 -0
  763. package/dist/modes/interactive/components/armin.d.ts +34 -0
  764. package/dist/modes/interactive/components/armin.d.ts.map +1 -0
  765. package/dist/modes/interactive/components/armin.js +333 -0
  766. package/dist/modes/interactive/components/armin.js.map +1 -0
  767. package/dist/modes/interactive/components/assistant-message.d.ts +22 -0
  768. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
  769. package/dist/modes/interactive/components/assistant-message.js +129 -0
  770. package/dist/modes/interactive/components/assistant-message.js.map +1 -0
  771. package/dist/modes/interactive/components/bash-execution.d.ts +39 -0
  772. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
  773. package/dist/modes/interactive/components/bash-execution.js +212 -0
  774. package/dist/modes/interactive/components/bash-execution.js.map +1 -0
  775. package/dist/modes/interactive/components/bordered-loader.d.ts +17 -0
  776. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
  777. package/dist/modes/interactive/components/bordered-loader.js +57 -0
  778. package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
  779. package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
  780. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
  781. package/dist/modes/interactive/components/branch-summary-message.js +44 -0
  782. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
  783. package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
  784. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
  785. package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
  786. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
  787. package/dist/modes/interactive/components/config-selector.d.ts +71 -0
  788. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
  789. package/dist/modes/interactive/components/config-selector.js +510 -0
  790. package/dist/modes/interactive/components/config-selector.js.map +1 -0
  791. package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  792. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  793. package/dist/modes/interactive/components/countdown-timer.js +33 -0
  794. package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  795. package/dist/modes/interactive/components/custom-editor.d.ts +21 -0
  796. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
  797. package/dist/modes/interactive/components/custom-editor.js +70 -0
  798. package/dist/modes/interactive/components/custom-editor.js.map +1 -0
  799. package/dist/modes/interactive/components/custom-message.d.ts +20 -0
  800. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
  801. package/dist/modes/interactive/components/custom-message.js +76 -0
  802. package/dist/modes/interactive/components/custom-message.js.map +1 -0
  803. package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
  804. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
  805. package/dist/modes/interactive/components/daxnuts.js +140 -0
  806. package/dist/modes/interactive/components/daxnuts.js.map +1 -0
  807. package/dist/modes/interactive/components/diff.d.ts +12 -0
  808. package/dist/modes/interactive/components/diff.d.ts.map +1 -0
  809. package/dist/modes/interactive/components/diff.js +133 -0
  810. package/dist/modes/interactive/components/diff.js.map +1 -0
  811. package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
  812. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
  813. package/dist/modes/interactive/components/dynamic-border.js +21 -0
  814. package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
  815. package/dist/modes/interactive/components/extension-editor.d.ts +20 -0
  816. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
  817. package/dist/modes/interactive/components/extension-editor.js +116 -0
  818. package/dist/modes/interactive/components/extension-editor.js.map +1 -0
  819. package/dist/modes/interactive/components/extension-input.d.ts +23 -0
  820. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
  821. package/dist/modes/interactive/components/extension-input.js +61 -0
  822. package/dist/modes/interactive/components/extension-input.js.map +1 -0
  823. package/dist/modes/interactive/components/extension-selector.d.ts +27 -0
  824. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
  825. package/dist/modes/interactive/components/extension-selector.js +98 -0
  826. package/dist/modes/interactive/components/extension-selector.js.map +1 -0
  827. package/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
  828. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
  829. package/dist/modes/interactive/components/first-time-setup.js +103 -0
  830. package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
  831. package/dist/modes/interactive/components/footer.d.ts +27 -0
  832. package/dist/modes/interactive/components/footer.d.ts.map +1 -0
  833. package/dist/modes/interactive/components/footer.js +180 -0
  834. package/dist/modes/interactive/components/footer.js.map +1 -0
  835. package/dist/modes/interactive/components/hotkeys.d.ts +24 -0
  836. package/dist/modes/interactive/components/hotkeys.d.ts.map +1 -0
  837. package/dist/modes/interactive/components/hotkeys.js +97 -0
  838. package/dist/modes/interactive/components/hotkeys.js.map +1 -0
  839. package/dist/modes/interactive/components/index.d.ts +35 -0
  840. package/dist/modes/interactive/components/index.d.ts.map +1 -0
  841. package/dist/modes/interactive/components/index.js +36 -0
  842. package/dist/modes/interactive/components/index.js.map +1 -0
  843. package/dist/modes/interactive/components/keybinding-hints.d.ts +19 -0
  844. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
  845. package/dist/modes/interactive/components/keybinding-hints.js +48 -0
  846. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
  847. package/dist/modes/interactive/components/login-dialog.d.ts +52 -0
  848. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
  849. package/dist/modes/interactive/components/login-dialog.js +179 -0
  850. package/dist/modes/interactive/components/login-dialog.js.map +1 -0
  851. package/dist/modes/interactive/components/logo.d.ts +25 -0
  852. package/dist/modes/interactive/components/logo.d.ts.map +1 -0
  853. package/dist/modes/interactive/components/logo.js +89 -0
  854. package/dist/modes/interactive/components/logo.js.map +1 -0
  855. package/dist/modes/interactive/components/model-selector.d.ts +47 -0
  856. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
  857. package/dist/modes/interactive/components/model-selector.js +278 -0
  858. package/dist/modes/interactive/components/model-selector.js.map +1 -0
  859. package/dist/modes/interactive/components/oauth-selector.d.ts +31 -0
  860. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
  861. package/dist/modes/interactive/components/oauth-selector.js +165 -0
  862. package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
  863. package/dist/modes/interactive/components/remote-control-center.d.ts +110 -0
  864. package/dist/modes/interactive/components/remote-control-center.d.ts.map +1 -0
  865. package/dist/modes/interactive/components/remote-control-center.js +1217 -0
  866. package/dist/modes/interactive/components/remote-control-center.js.map +1 -0
  867. package/dist/modes/interactive/components/review-tools-selector.d.ts +24 -0
  868. package/dist/modes/interactive/components/review-tools-selector.d.ts.map +1 -0
  869. package/dist/modes/interactive/components/review-tools-selector.js +93 -0
  870. package/dist/modes/interactive/components/review-tools-selector.js.map +1 -0
  871. package/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
  872. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
  873. package/dist/modes/interactive/components/scoped-models-selector.js +290 -0
  874. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
  875. package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
  876. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
  877. package/dist/modes/interactive/components/session-selector-search.js +155 -0
  878. package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
  879. package/dist/modes/interactive/components/session-selector.d.ts +95 -0
  880. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
  881. package/dist/modes/interactive/components/session-selector.js +856 -0
  882. package/dist/modes/interactive/components/session-selector.js.map +1 -0
  883. package/dist/modes/interactive/components/settings-selector.d.ts +77 -0
  884. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
  885. package/dist/modes/interactive/components/settings-selector.js +484 -0
  886. package/dist/modes/interactive/components/settings-selector.js.map +1 -0
  887. package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
  888. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
  889. package/dist/modes/interactive/components/show-images-selector.js +39 -0
  890. package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
  891. package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
  892. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
  893. package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
  894. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
  895. package/dist/modes/interactive/components/streaming-render-coalescer.d.ts +29 -0
  896. package/dist/modes/interactive/components/streaming-render-coalescer.d.ts.map +1 -0
  897. package/dist/modes/interactive/components/streaming-render-coalescer.js +98 -0
  898. package/dist/modes/interactive/components/streaming-render-coalescer.js.map +1 -0
  899. package/dist/modes/interactive/components/subagent-inspector.d.ts +32 -0
  900. package/dist/modes/interactive/components/subagent-inspector.d.ts.map +1 -0
  901. package/dist/modes/interactive/components/subagent-inspector.js +439 -0
  902. package/dist/modes/interactive/components/subagent-inspector.js.map +1 -0
  903. package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
  904. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
  905. package/dist/modes/interactive/components/theme-selector.js +50 -0
  906. package/dist/modes/interactive/components/theme-selector.js.map +1 -0
  907. package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
  908. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
  909. package/dist/modes/interactive/components/thinking-selector.js +52 -0
  910. package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
  911. package/dist/modes/interactive/components/tool-execution.d.ts +83 -0
  912. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
  913. package/dist/modes/interactive/components/tool-execution.js +421 -0
  914. package/dist/modes/interactive/components/tool-execution.js.map +1 -0
  915. package/dist/modes/interactive/components/tree-selector.d.ts +89 -0
  916. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
  917. package/dist/modes/interactive/components/tree-selector.js +1208 -0
  918. package/dist/modes/interactive/components/tree-selector.js.map +1 -0
  919. package/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  920. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
  921. package/dist/modes/interactive/components/trust-selector.js +91 -0
  922. package/dist/modes/interactive/components/trust-selector.js.map +1 -0
  923. package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
  924. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
  925. package/dist/modes/interactive/components/user-message-selector.js +114 -0
  926. package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
  927. package/dist/modes/interactive/components/user-message.d.ts +9 -0
  928. package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
  929. package/dist/modes/interactive/components/user-message.js +42 -0
  930. package/dist/modes/interactive/components/user-message.js.map +1 -0
  931. package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
  932. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
  933. package/dist/modes/interactive/components/visual-truncate.js +33 -0
  934. package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
  935. package/dist/modes/interactive/components/volt-announcement.d.ts +5 -0
  936. package/dist/modes/interactive/components/volt-announcement.d.ts.map +1 -0
  937. package/dist/modes/interactive/components/volt-announcement.js +15 -0
  938. package/dist/modes/interactive/components/volt-announcement.js.map +1 -0
  939. package/dist/modes/interactive/daemon-attach.d.ts +154 -0
  940. package/dist/modes/interactive/daemon-attach.d.ts.map +1 -0
  941. package/dist/modes/interactive/daemon-attach.js +552 -0
  942. package/dist/modes/interactive/daemon-attach.js.map +1 -0
  943. package/dist/modes/interactive/drain-viewer.d.ts +31 -0
  944. package/dist/modes/interactive/drain-viewer.d.ts.map +1 -0
  945. package/dist/modes/interactive/drain-viewer.js +166 -0
  946. package/dist/modes/interactive/drain-viewer.js.map +1 -0
  947. package/dist/modes/interactive/interactive-mode.d.ts +502 -0
  948. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
  949. package/dist/modes/interactive/interactive-mode.js +7007 -0
  950. package/dist/modes/interactive/interactive-mode.js.map +1 -0
  951. package/dist/modes/interactive/prompt-image-attachments.d.ts +34 -0
  952. package/dist/modes/interactive/prompt-image-attachments.d.ts.map +1 -0
  953. package/dist/modes/interactive/prompt-image-attachments.js +120 -0
  954. package/dist/modes/interactive/prompt-image-attachments.js.map +1 -0
  955. package/dist/modes/interactive/relay-stream-adapter.d.ts +19 -0
  956. package/dist/modes/interactive/relay-stream-adapter.d.ts.map +1 -0
  957. package/dist/modes/interactive/relay-stream-adapter.js +107 -0
  958. package/dist/modes/interactive/relay-stream-adapter.js.map +1 -0
  959. package/dist/modes/print-mode.d.ts +28 -0
  960. package/dist/modes/print-mode.d.ts.map +1 -0
  961. package/dist/modes/print-mode.js +132 -0
  962. package/dist/modes/print-mode.js.map +1 -0
  963. package/dist/modes/rpc/in-process-rpc-client.d.ts +31 -0
  964. package/dist/modes/rpc/in-process-rpc-client.d.ts.map +1 -0
  965. package/dist/modes/rpc/in-process-rpc-client.js +63 -0
  966. package/dist/modes/rpc/in-process-rpc-client.js.map +1 -0
  967. package/dist/modes/rpc/iroh-remote-agent-runtime.d.ts +61 -0
  968. package/dist/modes/rpc/iroh-remote-agent-runtime.d.ts.map +1 -0
  969. package/dist/modes/rpc/iroh-remote-agent-runtime.js +155 -0
  970. package/dist/modes/rpc/iroh-remote-agent-runtime.js.map +1 -0
  971. package/dist/modes/rpc/iroh-remote-rpc-mode.d.ts +69 -0
  972. package/dist/modes/rpc/iroh-remote-rpc-mode.d.ts.map +1 -0
  973. package/dist/modes/rpc/iroh-remote-rpc-mode.js +1223 -0
  974. package/dist/modes/rpc/iroh-remote-rpc-mode.js.map +1 -0
  975. package/dist/modes/rpc/jsonl.d.ts +2 -0
  976. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  977. package/dist/modes/rpc/jsonl.js +2 -0
  978. package/dist/modes/rpc/jsonl.js.map +1 -0
  979. package/dist/modes/rpc/rpc-client-base.d.ts +209 -0
  980. package/dist/modes/rpc/rpc-client-base.d.ts.map +1 -0
  981. package/dist/modes/rpc/rpc-client-base.js +530 -0
  982. package/dist/modes/rpc/rpc-client-base.js.map +1 -0
  983. package/dist/modes/rpc/rpc-client.d.ts +50 -0
  984. package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
  985. package/dist/modes/rpc/rpc-client.js +190 -0
  986. package/dist/modes/rpc/rpc-client.js.map +1 -0
  987. package/dist/modes/rpc/rpc-command-dispatcher.d.ts +43 -0
  988. package/dist/modes/rpc/rpc-command-dispatcher.d.ts.map +1 -0
  989. package/dist/modes/rpc/rpc-command-dispatcher.js +613 -0
  990. package/dist/modes/rpc/rpc-command-dispatcher.js.map +1 -0
  991. package/dist/modes/rpc/rpc-command-validation.d.ts +2 -0
  992. package/dist/modes/rpc/rpc-command-validation.d.ts.map +1 -0
  993. package/dist/modes/rpc/rpc-command-validation.js +207 -0
  994. package/dist/modes/rpc/rpc-command-validation.js.map +1 -0
  995. package/dist/modes/rpc/rpc-mode.d.ts +48 -0
  996. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  997. package/dist/modes/rpc/rpc-mode.js +1089 -0
  998. package/dist/modes/rpc/rpc-mode.js.map +1 -0
  999. package/dist/modes/rpc/rpc-transport-client.d.ts +26 -0
  1000. package/dist/modes/rpc/rpc-transport-client.d.ts.map +1 -0
  1001. package/dist/modes/rpc/rpc-transport-client.js +76 -0
  1002. package/dist/modes/rpc/rpc-transport-client.js.map +1 -0
  1003. package/dist/modes/rpc/rpc-types.d.ts +2 -0
  1004. package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
  1005. package/dist/modes/rpc/rpc-types.js +2 -0
  1006. package/dist/modes/rpc/rpc-types.js.map +1 -0
  1007. package/dist/package-manager-cli.d.ts +28 -0
  1008. package/dist/package-manager-cli.d.ts.map +1 -0
  1009. package/dist/package-manager-cli.js +667 -0
  1010. package/dist/package-manager-cli.js.map +1 -0
  1011. package/dist/remote/integrated-runtime-retention.d.ts +17 -0
  1012. package/dist/remote/integrated-runtime-retention.d.ts.map +1 -0
  1013. package/dist/remote/integrated-runtime-retention.js +62 -0
  1014. package/dist/remote/integrated-runtime-retention.js.map +1 -0
  1015. package/dist/remote/iroh-native-adapter.cjs +19 -0
  1016. package/dist/sea/cli.d.ts +4 -0
  1017. package/dist/sea/cli.d.ts.map +1 -0
  1018. package/dist/sea/cli.js +4 -0
  1019. package/dist/sea/cli.js.map +1 -0
  1020. package/dist/sea/register-bedrock.d.ts +2 -0
  1021. package/dist/sea/register-bedrock.d.ts.map +1 -0
  1022. package/dist/sea/register-bedrock.js +4 -0
  1023. package/dist/sea/register-bedrock.js.map +1 -0
  1024. package/dist/store/catalog.d.ts +57 -0
  1025. package/dist/store/catalog.d.ts.map +1 -0
  1026. package/dist/store/catalog.js +331 -0
  1027. package/dist/store/catalog.js.map +1 -0
  1028. package/dist/store/inspector.d.ts +36 -0
  1029. package/dist/store/inspector.d.ts.map +1 -0
  1030. package/dist/store/inspector.js +578 -0
  1031. package/dist/store/inspector.js.map +1 -0
  1032. package/dist/store/install-plan.d.ts +26 -0
  1033. package/dist/store/install-plan.d.ts.map +1 -0
  1034. package/dist/store/install-plan.js +80 -0
  1035. package/dist/store/install-plan.js.map +1 -0
  1036. package/dist/store/render.d.ts +11 -0
  1037. package/dist/store/render.d.ts.map +1 -0
  1038. package/dist/store/render.js +153 -0
  1039. package/dist/store/render.js.map +1 -0
  1040. package/dist/store/resolver.d.ts +22 -0
  1041. package/dist/store/resolver.d.ts.map +1 -0
  1042. package/dist/store/resolver.js +207 -0
  1043. package/dist/store/resolver.js.map +1 -0
  1044. package/dist/store/store-cli.d.ts +7 -0
  1045. package/dist/store/store-cli.d.ts.map +1 -0
  1046. package/dist/store/store-cli.js +636 -0
  1047. package/dist/store/store-cli.js.map +1 -0
  1048. package/dist/store/targets.d.ts +21 -0
  1049. package/dist/store/targets.d.ts.map +1 -0
  1050. package/dist/store/targets.js +55 -0
  1051. package/dist/store/targets.js.map +1 -0
  1052. package/dist/utils/ansi.d.ts +2 -0
  1053. package/dist/utils/ansi.d.ts.map +1 -0
  1054. package/dist/utils/ansi.js +52 -0
  1055. package/dist/utils/ansi.js.map +1 -0
  1056. package/dist/utils/changelog.d.ts +22 -0
  1057. package/dist/utils/changelog.d.ts.map +1 -0
  1058. package/dist/utils/changelog.js +165 -0
  1059. package/dist/utils/changelog.js.map +1 -0
  1060. package/dist/utils/child-process.d.ts +18 -0
  1061. package/dist/utils/child-process.d.ts.map +1 -0
  1062. package/dist/utils/child-process.js +106 -0
  1063. package/dist/utils/child-process.js.map +1 -0
  1064. package/dist/utils/clipboard-image.d.ts +11 -0
  1065. package/dist/utils/clipboard-image.d.ts.map +1 -0
  1066. package/dist/utils/clipboard-image.js +245 -0
  1067. package/dist/utils/clipboard-image.js.map +1 -0
  1068. package/dist/utils/clipboard.d.ts +2 -0
  1069. package/dist/utils/clipboard.d.ts.map +1 -0
  1070. package/dist/utils/clipboard.js +94 -0
  1071. package/dist/utils/clipboard.js.map +1 -0
  1072. package/dist/utils/deprecation.d.ts +4 -0
  1073. package/dist/utils/deprecation.d.ts.map +1 -0
  1074. package/dist/utils/deprecation.js +13 -0
  1075. package/dist/utils/deprecation.js.map +1 -0
  1076. package/dist/utils/durable-atomic-write.d.ts +46 -0
  1077. package/dist/utils/durable-atomic-write.d.ts.map +1 -0
  1078. package/dist/utils/durable-atomic-write.js +104 -0
  1079. package/dist/utils/durable-atomic-write.js.map +1 -0
  1080. package/dist/utils/frontmatter.d.ts +8 -0
  1081. package/dist/utils/frontmatter.d.ts.map +1 -0
  1082. package/dist/utils/frontmatter.js +26 -0
  1083. package/dist/utils/frontmatter.js.map +1 -0
  1084. package/dist/utils/fs-watch.d.ts +5 -0
  1085. package/dist/utils/fs-watch.d.ts.map +1 -0
  1086. package/dist/utils/fs-watch.js +25 -0
  1087. package/dist/utils/fs-watch.js.map +1 -0
  1088. package/dist/utils/git.d.ts +26 -0
  1089. package/dist/utils/git.d.ts.map +1 -0
  1090. package/dist/utils/git.js +195 -0
  1091. package/dist/utils/git.js.map +1 -0
  1092. package/dist/utils/html.d.ts +7 -0
  1093. package/dist/utils/html.d.ts.map +1 -0
  1094. package/dist/utils/html.js +40 -0
  1095. package/dist/utils/html.js.map +1 -0
  1096. package/dist/utils/ignore-files.d.ts +5 -0
  1097. package/dist/utils/ignore-files.d.ts.map +1 -0
  1098. package/dist/utils/ignore-files.js +58 -0
  1099. package/dist/utils/ignore-files.js.map +1 -0
  1100. package/dist/utils/image-codec.d.ts +22 -0
  1101. package/dist/utils/image-codec.d.ts.map +1 -0
  1102. package/dist/utils/image-codec.js +35 -0
  1103. package/dist/utils/image-codec.js.map +1 -0
  1104. package/dist/utils/image-convert.d.ts +9 -0
  1105. package/dist/utils/image-convert.d.ts.map +1 -0
  1106. package/dist/utils/image-convert.js +26 -0
  1107. package/dist/utils/image-convert.js.map +1 -0
  1108. package/dist/utils/image-resize-core.d.ts +31 -0
  1109. package/dist/utils/image-resize-core.d.ts.map +1 -0
  1110. package/dist/utils/image-resize-core.js +181 -0
  1111. package/dist/utils/image-resize-core.js.map +1 -0
  1112. package/dist/utils/image-resize-worker.d.ts +2 -0
  1113. package/dist/utils/image-resize-worker.d.ts.map +1 -0
  1114. package/dist/utils/image-resize-worker.js +31 -0
  1115. package/dist/utils/image-resize-worker.js.map +1 -0
  1116. package/dist/utils/image-resize.d.ts +15 -0
  1117. package/dist/utils/image-resize.d.ts.map +1 -0
  1118. package/dist/utils/image-resize.js +92 -0
  1119. package/dist/utils/image-resize.js.map +1 -0
  1120. package/dist/utils/json.d.ts +3 -0
  1121. package/dist/utils/json.d.ts.map +1 -0
  1122. package/dist/utils/json.js +7 -0
  1123. package/dist/utils/json.js.map +1 -0
  1124. package/dist/utils/mime.d.ts +3 -0
  1125. package/dist/utils/mime.d.ts.map +1 -0
  1126. package/dist/utils/mime.js +69 -0
  1127. package/dist/utils/mime.js.map +1 -0
  1128. package/dist/utils/npm-spec.d.ts +9 -0
  1129. package/dist/utils/npm-spec.d.ts.map +1 -0
  1130. package/dist/utils/npm-spec.js +15 -0
  1131. package/dist/utils/npm-spec.js.map +1 -0
  1132. package/dist/utils/open-browser.d.ts +9 -0
  1133. package/dist/utils/open-browser.d.ts.map +1 -0
  1134. package/dist/utils/open-browser.js +22 -0
  1135. package/dist/utils/open-browser.js.map +1 -0
  1136. package/dist/utils/paths.d.ts +31 -0
  1137. package/dist/utils/paths.d.ts.map +1 -0
  1138. package/dist/utils/paths.js +92 -0
  1139. package/dist/utils/paths.js.map +1 -0
  1140. package/dist/utils/private-files.d.ts +21 -0
  1141. package/dist/utils/private-files.d.ts.map +1 -0
  1142. package/dist/utils/private-files.js +103 -0
  1143. package/dist/utils/private-files.js.map +1 -0
  1144. package/dist/utils/process-env.d.ts +2 -0
  1145. package/dist/utils/process-env.d.ts.map +1 -0
  1146. package/dist/utils/process-env.js +31 -0
  1147. package/dist/utils/process-env.js.map +1 -0
  1148. package/dist/utils/shell.d.ts +30 -0
  1149. package/dist/utils/shell.d.ts.map +1 -0
  1150. package/dist/utils/shell.js +195 -0
  1151. package/dist/utils/shell.js.map +1 -0
  1152. package/dist/utils/sleep.d.ts +5 -0
  1153. package/dist/utils/sleep.d.ts.map +1 -0
  1154. package/dist/utils/sleep.js +17 -0
  1155. package/dist/utils/sleep.js.map +1 -0
  1156. package/dist/utils/syntax-highlight.d.ts +18 -0
  1157. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  1158. package/dist/utils/syntax-highlight.js +139 -0
  1159. package/dist/utils/syntax-highlight.js.map +1 -0
  1160. package/dist/utils/tools-manager.d.ts +3 -0
  1161. package/dist/utils/tools-manager.d.ts.map +1 -0
  1162. package/dist/utils/tools-manager.js +328 -0
  1163. package/dist/utils/tools-manager.js.map +1 -0
  1164. package/dist/utils/version-check.d.ts +15 -0
  1165. package/dist/utils/version-check.d.ts.map +1 -0
  1166. package/dist/utils/version-check.js +61 -0
  1167. package/dist/utils/version-check.js.map +1 -0
  1168. package/dist/utils/volt-user-agent.d.ts +2 -0
  1169. package/dist/utils/volt-user-agent.d.ts.map +1 -0
  1170. package/dist/utils/volt-user-agent.js +5 -0
  1171. package/dist/utils/volt-user-agent.js.map +1 -0
  1172. package/dist/utils/windows-self-update.d.ts +3 -0
  1173. package/dist/utils/windows-self-update.d.ts.map +1 -0
  1174. package/dist/utils/windows-self-update.js +77 -0
  1175. package/dist/utils/windows-self-update.js.map +1 -0
  1176. package/docs/compaction.md +394 -0
  1177. package/docs/containerization.md +111 -0
  1178. package/docs/custom-provider.md +737 -0
  1179. package/docs/daemon.md +190 -0
  1180. package/docs/development.md +70 -0
  1181. package/docs/docs.json +176 -0
  1182. package/docs/extensions.md +2692 -0
  1183. package/docs/images/exy.png +0 -0
  1184. package/docs/images/interactive-mode.png +0 -0
  1185. package/docs/images/tree-view.png +0 -0
  1186. package/docs/index.md +82 -0
  1187. package/docs/iroh-remote-access-design.md +340 -0
  1188. package/docs/iroh-remote-protocol.md +431 -0
  1189. package/docs/json.md +87 -0
  1190. package/docs/keybindings.md +200 -0
  1191. package/docs/live-shared-session-daemon-design.md +1090 -0
  1192. package/docs/lsp.md +143 -0
  1193. package/docs/mcp.md +1044 -0
  1194. package/docs/models.md +495 -0
  1195. package/docs/packages.md +230 -0
  1196. package/docs/prompt-templates.md +108 -0
  1197. package/docs/providers.md +274 -0
  1198. package/docs/quickstart.md +165 -0
  1199. package/docs/rpc.md +2050 -0
  1200. package/docs/sdk.md +1267 -0
  1201. package/docs/security.md +139 -0
  1202. package/docs/session-format.md +412 -0
  1203. package/docs/sessions.md +145 -0
  1204. package/docs/settings.md +371 -0
  1205. package/docs/shell-aliases.md +13 -0
  1206. package/docs/skills.md +243 -0
  1207. package/docs/subagents-design.md +419 -0
  1208. package/docs/terminal-setup.md +148 -0
  1209. package/docs/termux.md +127 -0
  1210. package/docs/themes.md +295 -0
  1211. package/docs/tla/ClientAuth.cfg +16 -0
  1212. package/docs/tla/ClientAuth.tla +205 -0
  1213. package/docs/tla/ClientConn.cfg +16 -0
  1214. package/docs/tla/ClientConn.tla +265 -0
  1215. package/docs/tla/LeaseBroker.cfg +43 -0
  1216. package/docs/tla/LeaseBroker.tla +563 -0
  1217. package/docs/tla/PLAN.md +240 -0
  1218. package/docs/tla/PushOrdering.cfg +17 -0
  1219. package/docs/tla/PushOrdering.tla +183 -0
  1220. package/docs/tla/README.md +205 -0
  1221. package/docs/tla/RelayViewer.cfg +26 -0
  1222. package/docs/tla/RelayViewer.tla +299 -0
  1223. package/docs/tla/SessionTarget.cfg +18 -0
  1224. package/docs/tla/SessionTarget.tla +192 -0
  1225. package/docs/tla/check.sh +34 -0
  1226. package/docs/tmux.md +63 -0
  1227. package/docs/tui.md +949 -0
  1228. package/docs/usage.md +482 -0
  1229. package/docs/windows.md +17 -0
  1230. package/docs/worktrees-design.md +605 -0
  1231. package/examples/README.md +28 -0
  1232. package/examples/extensions/README.md +211 -0
  1233. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  1234. package/examples/extensions/bash-spawn-hook.ts +30 -0
  1235. package/examples/extensions/bookmark.ts +50 -0
  1236. package/examples/extensions/border-status-editor.ts +150 -0
  1237. package/examples/extensions/built-in-tool-renderer.ts +249 -0
  1238. package/examples/extensions/claude-rules.ts +86 -0
  1239. package/examples/extensions/commands.ts +72 -0
  1240. package/examples/extensions/confirm-destructive.ts +59 -0
  1241. package/examples/extensions/custom-compaction.ts +127 -0
  1242. package/examples/extensions/custom-footer.ts +64 -0
  1243. package/examples/extensions/custom-header.ts +72 -0
  1244. package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
  1245. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  1246. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  1247. package/examples/extensions/custom-provider-gitlab-duo/index.ts +400 -0
  1248. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  1249. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  1250. package/examples/extensions/dirty-repo-guard.ts +56 -0
  1251. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  1252. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  1253. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  1254. package/examples/extensions/dynamic-resources/index.ts +15 -0
  1255. package/examples/extensions/dynamic-tools.ts +74 -0
  1256. package/examples/extensions/event-bus.ts +43 -0
  1257. package/examples/extensions/file-trigger.ts +41 -0
  1258. package/examples/extensions/git-checkpoint.ts +53 -0
  1259. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  1260. package/examples/extensions/github-issue-autocomplete.ts +185 -0
  1261. package/examples/extensions/gondolin/index.ts +531 -0
  1262. package/examples/extensions/gondolin/package-lock.json +185 -0
  1263. package/examples/extensions/gondolin/package.json +19 -0
  1264. package/examples/extensions/handoff.ts +191 -0
  1265. package/examples/extensions/hello.ts +26 -0
  1266. package/examples/extensions/hidden-thinking-label.ts +53 -0
  1267. package/examples/extensions/inline-bash.ts +94 -0
  1268. package/examples/extensions/input-transform-streaming.ts +39 -0
  1269. package/examples/extensions/input-transform.ts +43 -0
  1270. package/examples/extensions/interactive-shell.ts +196 -0
  1271. package/examples/extensions/mac-system-theme.ts +47 -0
  1272. package/examples/extensions/message-renderer.ts +59 -0
  1273. package/examples/extensions/minimal-mode.ts +426 -0
  1274. package/examples/extensions/modal-editor.ts +85 -0
  1275. package/examples/extensions/model-status.ts +31 -0
  1276. package/examples/extensions/notify.ts +55 -0
  1277. package/examples/extensions/overlay-qa-tests.ts +1450 -0
  1278. package/examples/extensions/overlay-test.ts +153 -0
  1279. package/examples/extensions/permission-gate.ts +34 -0
  1280. package/examples/extensions/pirate.ts +47 -0
  1281. package/examples/extensions/plan-mode/README.md +65 -0
  1282. package/examples/extensions/plan-mode/index.ts +340 -0
  1283. package/examples/extensions/plan-mode/utils.ts +168 -0
  1284. package/examples/extensions/preset.ts +436 -0
  1285. package/examples/extensions/project-trust.ts +64 -0
  1286. package/examples/extensions/prompt-customizer.ts +97 -0
  1287. package/examples/extensions/protected-paths.ts +30 -0
  1288. package/examples/extensions/provider-payload.ts +18 -0
  1289. package/examples/extensions/qna.ts +122 -0
  1290. package/examples/extensions/question.ts +277 -0
  1291. package/examples/extensions/questionnaire.ts +440 -0
  1292. package/examples/extensions/rainbow-editor.ts +88 -0
  1293. package/examples/extensions/reload-runtime.ts +37 -0
  1294. package/examples/extensions/rpc-demo.ts +118 -0
  1295. package/examples/extensions/sandbox/index.ts +321 -0
  1296. package/examples/extensions/sandbox/package-lock.json +92 -0
  1297. package/examples/extensions/sandbox/package.json +19 -0
  1298. package/examples/extensions/send-user-message.ts +97 -0
  1299. package/examples/extensions/session-name.ts +27 -0
  1300. package/examples/extensions/shutdown-command.ts +63 -0
  1301. package/examples/extensions/snake.ts +343 -0
  1302. package/examples/extensions/space-invaders.ts +560 -0
  1303. package/examples/extensions/ssh.ts +220 -0
  1304. package/examples/extensions/status-line.ts +32 -0
  1305. package/examples/extensions/structured-output.ts +65 -0
  1306. package/examples/extensions/subagent/README.md +175 -0
  1307. package/examples/extensions/subagent/agents/planner.md +37 -0
  1308. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  1309. package/examples/extensions/subagent/agents/scout.md +50 -0
  1310. package/examples/extensions/subagent/agents/worker.md +24 -0
  1311. package/examples/extensions/subagent/agents.ts +126 -0
  1312. package/examples/extensions/subagent/index.ts +1009 -0
  1313. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  1314. package/examples/extensions/subagent/prompts/implement.md +10 -0
  1315. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  1316. package/examples/extensions/summarize.ts +206 -0
  1317. package/examples/extensions/system-prompt-header.ts +17 -0
  1318. package/examples/extensions/tic-tac-toe.ts +1008 -0
  1319. package/examples/extensions/timed-confirm.ts +70 -0
  1320. package/examples/extensions/titlebar-spinner.ts +58 -0
  1321. package/examples/extensions/todo.ts +297 -0
  1322. package/examples/extensions/tool-override.ts +144 -0
  1323. package/examples/extensions/tools.ts +146 -0
  1324. package/examples/extensions/trigger-compact.ts +50 -0
  1325. package/examples/extensions/truncated-tool.ts +195 -0
  1326. package/examples/extensions/widget-placement.ts +9 -0
  1327. package/examples/extensions/with-deps/index.ts +32 -0
  1328. package/examples/extensions/with-deps/package-lock.json +31 -0
  1329. package/examples/extensions/with-deps/package.json +22 -0
  1330. package/examples/extensions/working-indicator.ts +123 -0
  1331. package/examples/extensions/working-message-test.ts +25 -0
  1332. package/examples/remote/firebase-push-relay/README.md +79 -0
  1333. package/examples/remote/firebase-push-relay/firebase.json +12 -0
  1334. package/examples/remote/firebase-push-relay/firestore.indexes.json +11 -0
  1335. package/examples/remote/firebase-push-relay/firestore.rules +9 -0
  1336. package/examples/remote/firebase-push-relay/functions/core.js +511 -0
  1337. package/examples/remote/firebase-push-relay/functions/core.test.js +273 -0
  1338. package/examples/remote/firebase-push-relay/functions/index.js +413 -0
  1339. package/examples/remote/firebase-push-relay/functions/package-lock.json +2974 -0
  1340. package/examples/remote/firebase-push-relay/functions/package.json +18 -0
  1341. package/examples/remote/iroh-sidecar/README.md +211 -0
  1342. package/examples/remote/iroh-sidecar/client.mjs +554 -0
  1343. package/examples/remote/iroh-sidecar/common.mjs +195 -0
  1344. package/examples/remote/iroh-sidecar/host.mjs +4 -0
  1345. package/examples/remote/iroh-sidecar/package-lock.json +213 -0
  1346. package/examples/remote/iroh-sidecar/package.json +13 -0
  1347. package/examples/remote/iroh-sidecar/source-aware-run.mjs +41 -0
  1348. package/examples/rpc-extension-ui.ts +632 -0
  1349. package/examples/sdk/01-minimal.ts +26 -0
  1350. package/examples/sdk/02-custom-model.ts +53 -0
  1351. package/examples/sdk/03-custom-prompt.ts +70 -0
  1352. package/examples/sdk/04-skills.ts +55 -0
  1353. package/examples/sdk/05-tools.ts +48 -0
  1354. package/examples/sdk/06-extensions.ts +94 -0
  1355. package/examples/sdk/07-context-files.ts +42 -0
  1356. package/examples/sdk/08-prompt-templates.ts +51 -0
  1357. package/examples/sdk/09-api-keys-and-oauth.ts +52 -0
  1358. package/examples/sdk/10-settings.ts +53 -0
  1359. package/examples/sdk/11-sessions.ts +52 -0
  1360. package/examples/sdk/12-full-control.ts +78 -0
  1361. package/examples/sdk/13-session-runtime.ts +67 -0
  1362. package/examples/sdk/README.md +147 -0
  1363. package/npm-shrinkwrap.json +3442 -0
  1364. package/package.json +125 -19
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iroh-service.d.ts","sourceRoot":"","sources":["../../src/daemon/iroh-service.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AACjG,OAAO,EAAE,8BAA8B,EAAE,MAAM,+CAA+C,CAAC;AAsE/F,OAAO,EAGN,KAAK,sBAAsB,EAG3B,MAAM,0BAA0B,CAAC;AAUlC,OAAO,KAAK,EAAwB,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAyC7E,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAAC,sBAAsB,EAAE,cAAc,GAAG,eAAe,GAAG,WAAW,CAAC,CAAC;AAExH,wBAAgB,0CAA0C,CAAC,CAAC,SAAS,4BAA4B,EAChG,aAAa,EAAE,8BAA8B,EAC7C,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,YAAY,EAAE,MAAM,GAClB,GAAG,CAAC,CAAC,CAAC,CAWR;AAED,wBAAgB,yCAAyC,CACxD,aAAa,EAAE,8BAA8B,EAC7C,QAAQ,EAAE,QAAQ,CAAC,4BAA4B,CAAC,EAChD,YAAY,EAAE,MAAM,GAClB,GAAG,CAAC,2BAA2B,CAAC,CASlC;AAwBD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,aAAa,GAAG,YAAY,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,UAAiD,CAAC;AAEzF,MAAM,WAAW,uBAAuB;IACvC,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACvC,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,WAAW,GAAG,WAAW,CAAC,EAChE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACnD,uBAAuB,CAiBzB;AA6FD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,GAAE,uBAA4B,GAAG,qBAAqB,CAmCnG","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport type { Socket } from \"node:net\";\nimport { relative, resolve, sep } from \"node:path\";\nimport {\n\tcreateIrohRemoteExplicitAccess,\n\tcreateIrohRemotePresetAccess,\n\tgetIrohRemoteRpcCommandCapabilities,\n\tgetIrohRemoteStreamCapability,\n\tgetMissingIrohRemoteRpcCapability,\n\thasIrohRemoteRpcCapability,\n\tparseIrohRemoteRpcCapabilities,\n\tparseIrohRemoteRpcGrant,\n} from \"../core/remote/iroh/access-grant.ts\";\nimport type { IrohRemoteActiveStreamEntry } from \"../core/remote/iroh/active-stream-registry.ts\";\nimport { IrohRemoteActiveStreamRegistry } from \"../core/remote/iroh/active-stream-registry.ts\";\nimport type { IrohRemoteClientAuthorizationSuccess } from \"../core/remote/iroh/authorization.ts\";\nimport { hashIrohRemotePairingSecret } from \"../core/remote/iroh/authorization.ts\";\nimport {\n\tDEFAULT_IROH_REMOTE_PAIRING_TICKET_TTL_MS,\n\tIrohRemoteHostEngine,\n\ttype IrohRemoteHostHandshakeResult,\n} from \"../core/remote/iroh/engine.ts\";\nimport { createIrohRemoteHandshakeFailure, type IrohRemoteHello } from \"../core/remote/iroh/handshake.ts\";\nimport {\n\tDEFAULT_IROH_REMOTE_HANDSHAKE_MAX_LINE_BYTES,\n\tDEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS,\n\twriteIrohRemoteHandshakeResponse,\n} from \"../core/remote/iroh/handshake-reader.ts\";\nimport { resolveIrohRemoteWorkspaceProjectTrusted } from \"../core/remote/iroh/host-policy.ts\";\nimport { IROH_REMOTE_ALPN, resolveIrohRemoteRuntimeToolPolicy } from \"../core/remote/iroh/protocol.ts\";\nimport {\n\tIrohRemoteInMemoryPushNotificationDeduper,\n\ttype IrohRemoteLiveActivityUpdateIntent,\n\ttype IrohRemotePushNotificationDeliveryStatus,\n\tIrohRemotePushNotificationDispatcher,\n\ttype IrohRemotePushNotificationIntent,\n\tIrohRemotePushRelayHttpClient,\n\trevokeIrohRemoteClientPushTargets,\n} from \"../core/remote/iroh/push.ts\";\nimport {\n\tcreateIrohRemoteRpcCapabilityDeniedResponse,\n\tcreateIrohRemoteRpcErrorResponse,\n} from \"../core/remote/iroh/rpc-command-filter.ts\";\nimport type { IrohRemoteClient, IrohRemoteWorkspace, IrohRemoteWorkspaceWorktree } from \"../core/remote/iroh/state.ts\";\nimport {\n\tIROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,\n\ttype IrohRemoteHostStateManager,\n\tisIrohRemoteWorkspaceHasWorktreesError,\n} from \"../core/remote/iroh/state-manager.ts\";\nimport { getIrohRemoteWorkspaceAvailabilityStatus } from \"../core/remote/iroh/workspace.ts\";\nimport type { IrohRemoteWorktreeRpcBackend } from \"../core/remote/iroh/worktree-rpc.ts\";\nimport type { IrohBiStreamLike } from \"../core/rpc/iroh-transport.ts\";\nimport { getDefaultSessionDir } from \"../core/session-manager.ts\";\nimport { getCurrentThemeName, getResolvedThemeColors } from \"../core/theme/runtime.ts\";\nimport { ProjectTrustStore } from \"../core/trust-manager.ts\";\nimport { runIrohRemoteRpcMode } from \"../modes/rpc/iroh-remote-rpc-mode.ts\";\nimport {\n\tCONTROL_RPC_GRANTS_CAPABILITY,\n\tCONTROL_WORKTREES_CAPABILITY,\n\ttype ControlLeaseStatus,\n\ttype ControlRequest,\n\tRELAY_RPC_COMMAND_TYPES,\n\ttype RelayCloseReason,\n} from \"./control-protocol.ts\";\nimport type { ControlConnection } from \"./control-server.ts\";\nimport {\n\ttype ConversationCommandContext,\n\tcreateKeepAwakeRpcResponse,\n\tcreateRemoteRegisterLiveActivityRpcResponse,\n\tcreateRemoteUnregisterLiveActivityRpcResponse,\n\tcreateRpcSuccessResponse,\n\tcreateWebSearchKeyRpcResponse,\n\tgetRpcResponseId,\n\thandleIntegratedConversationRpcCommand,\n\thandleRemoteHostRpcCommand,\n\tREMOTE_SESSION_LIST_CURSOR_TTL_MS,\n\ttype RemoteSessionListCursorEntry,\n\ttoRpcKeepAwakeStatus,\n} from \"./conversation-commands.ts\";\nimport {\n\tcreateIntegratedConversationHandshakeResponse,\n\tdecorateRemoteHostState,\n\ttype RemoteHostResponseContext,\n} from \"./handshake-responses.ts\";\nimport {\n\tcreateConversationOpenError,\n\tgetResolvedTargetSessionId,\n\ttype IntegratedRuntimeEntry,\n\tIntegratedRuntimeRegistry,\n\ttype IntegratedRuntimeSubscriber,\n} from \"./integrated-runtimes.ts\";\nimport {\n\tformatIrohLoadError,\n\ttype IrohConnectionLike,\n\ttype IrohEndpointLike,\n\ttype IrohModuleLike,\n\tloadIrohModule,\n} from \"./iroh-native.ts\";\nimport { IrohRemoteResourceGuard } from \"./iroh-resource-guard.ts\";\nimport { type DaemonAttachClaim, LeaseBroker, type LeaseState } from \"./lease-broker.ts\";\nimport type { VoltdRuntimeServices, VoltdServiceExtension } from \"./main.ts\";\nimport { RELAY_TOKEN_TTL_MS, RelayRegistry } from \"./relay-stream.ts\";\nimport {\n\tcreateSessionManagerTargetStore,\n\ttype IrohRemoteSessionTarget,\n\tresolveIrohRemoteSessionTarget,\n} from \"./session-target.ts\";\nimport { resolveWorktreeCleanupPolicy } from \"./state.ts\";\nimport { createHostThemeTokensFrame, HOST_THEME_TOKENS_FEATURE } from \"./theme-push.ts\";\nimport { ViewerFeedRegistry } from \"./viewer-feed.ts\";\nimport { isPathInside, type WorkspaceDirectoryResolution } from \"./workspace-directory.ts\";\nimport {\n\ttype RemoteSanitizerOverrides,\n\trunWorkspaceDiscoveryStream,\n\trunWorkspaceManagementStream,\n\trunWorktreeManagementStream,\n\tWORKSPACE_UNREGISTERED_CLOSE_REASON,\n\twriteIrohRemoteJsonLine,\n} from \"./workspace-streams.ts\";\nimport {\n\tevaluateWorktreeRelayGate,\n\tgetRegisteredWorkingDirectoryForWorktree,\n\tgetWorkspaceWorktreesDir,\n\tgetWorktreesRoot,\n\thandleWorktreeControlRequest,\n\tisWorktreeControlRequest,\n\tWorktreeManager,\n\ttype WorktreeResult,\n\tWorktreeRetentionSweeper,\n} from \"./worktree-manager.ts\";\n\nconst ACTIVE_REVOKE_CLOSE_REASON = \"revoked\";\nconst ACTIVE_REPLACE_CLOSE_REASON = \"replaced\";\nconst DUPLICATE_CONVERSATION_RETRY_AFTER_MS = 500;\nconst RELAY_OFFER_RETRY_AFTER_MS = 1000;\nconst WORKSPACE_DISCOVERY_STREAM_SESSION_ID = \"$workspace-discovery\";\nconst WORKSPACE_MANAGEMENT_STREAM_SESSION_ID = \"$workspace-management\";\nconst IROH_ENDPOINT_READY_TIMEOUT_MS = 15_000;\nconst IROH_UNAUTHENTICATED_CONNECTION_TIMEOUT_MS = 15_000;\nconst SHUTDOWN_RUNTIME_IDLE_CAP_MS = 60_000;\n\nexport type AuthorityInvalidationRuntime = Pick<IntegratedRuntimeEntry, \"clientNodeId\" | \"workspaceName\" | \"sessionId\">;\n\nexport function collectClientAuthorityInvalidationRuntimes<T extends AuthorityInvalidationRuntime>(\n\tactiveStreams: IrohRemoteActiveStreamRegistry,\n\truntimes: Iterable<T>,\n\tclientNodeId: string,\n): Set<T> {\n\tconst clientEntries = activeStreams.entriesForClientNodeId(clientNodeId);\n\treturn new Set(\n\t\tArray.from(runtimes).filter(\n\t\t\t(runtime) =>\n\t\t\t\truntime.clientNodeId === clientNodeId ||\n\t\t\t\tclientEntries.some(\n\t\t\t\t\t(entry) => entry.workspaceName === runtime.workspaceName && entry.sessionId === runtime.sessionId,\n\t\t\t\t),\n\t\t),\n\t);\n}\n\nexport function collectClientAuthorityInvalidationStreams(\n\tactiveStreams: IrohRemoteActiveStreamRegistry,\n\truntimes: Iterable<AuthorityInvalidationRuntime>,\n\tclientNodeId: string,\n): Set<IrohRemoteActiveStreamEntry> {\n\tconst runtimeList = Array.from(runtimes);\n\tconst entries = new Set(activeStreams.entriesForClientNodeId(clientNodeId));\n\tfor (const runtime of collectClientAuthorityInvalidationRuntimes(activeStreams, runtimeList, clientNodeId)) {\n\t\tfor (const entry of activeStreams.entriesForConversationKey(runtime.workspaceName, runtime.sessionId)) {\n\t\t\tentries.add(entry);\n\t\t}\n\t}\n\treturn entries;\n}\n\nfunction getRelativeWorkingDirectoryForRoot(rootPath: string, cwd: string): string | null | undefined {\n\tconst root = resolve(rootPath);\n\tconst child = resolve(cwd);\n\tif (!isPathInside(root, child)) {\n\t\treturn null;\n\t}\n\tconst relativePath = relative(root, child);\n\treturn relativePath.length === 0 || relativePath === \".\" ? undefined : relativePath.split(sep).join(\"/\");\n}\n\n/**\n * Defensive cap on concurrent in-flight bi-streams per client connection. A\n * well-behaved client keeps only a handful open (one conversation + a few\n * utility streams); an authenticated-but-misbehaving client could otherwise open\n * unbounded concurrent streams, each spawning a runtime attach, and exhaust\n * daemon resources. Hitting the cap closes the connection.\n */\nconst MAX_CONCURRENT_STREAMS_PER_CONNECTION = 64;\n\nlet activeConnectionSequence = 0;\nlet activeStreamSequence = 0;\n\nexport type IrohRelayMode = \"disabled\" | \"development\" | \"production\";\n\n/**\n * The Volt-operated relay fleet. Endpoints bind against these by default\n * (\"production\" mode); the n0 public relays (\"development\" mode) are for\n * development only and must be opted into via VOLT_IROH_RELAY_MODE=development.\n */\nexport const VOLT_PRODUCTION_RELAY_URLS = [\"https://iroh-relay-us-central.volt-cli.dev\"];\n\nexport interface IrohDaemonServiceConfig {\n\trelayMode?: IrohRelayMode;\n\t/**\n\t * Relay server URLs (e.g. \"https://relay.example.com\"). When set (or via\n\t * VOLT_IROH_RELAY_URLS, comma-separated), production mode binds against\n\t * these instead of the built-in Volt fleet, and pairing tickets carry the\n\t * URLs so clients bind against the same relays.\n\t */\n\trelayUrls?: string[];\n\t/**\n\t * Bearer token presented to relay servers configured with\n\t * access.shared_token. Falls back to VOLT_IROH_RELAY_AUTH_TOKEN, then the\n\t * token persisted in daemon state from a previous start.\n\t */\n\trelayAuthToken?: string;\n\tpushRelayUrl?: string;\n\tpushRelayAuthToken?: string;\n\tprofile?: string;\n}\n\nexport interface ResolvedIrohRelayConfig {\n\trelayMode: IrohRelayMode;\n\trelayUrls: string[];\n\twarning?: string;\n}\n\n/**\n * Resolves the effective relay configuration. Precedence: explicit service\n * config, then VOLT_IROH_RELAY_MODE / VOLT_IROH_RELAY_URLS, then the Volt\n * production relay fleet.\n */\nexport function resolveIrohRelayConfig(\n\tconfig: Pick<IrohDaemonServiceConfig, \"relayMode\" | \"relayUrls\">,\n\tenv: Record<string, string | undefined> = process.env,\n): ResolvedIrohRelayConfig {\n\tconst envUrls = parseRelayUrlsEnv(env.VOLT_IROH_RELAY_URLS);\n\tconst envModeValue = env.VOLT_IROH_RELAY_MODE?.trim();\n\tlet envMode: IrohRelayMode | undefined;\n\tlet warning: string | undefined;\n\tif (envModeValue !== undefined && envModeValue !== \"\") {\n\t\tif (envModeValue === \"disabled\" || envModeValue === \"development\" || envModeValue === \"production\") {\n\t\t\tenvMode = envModeValue;\n\t\t} else {\n\t\t\twarning = `ignoring invalid VOLT_IROH_RELAY_MODE \"${envModeValue}\" (expected disabled, development, or production)`;\n\t\t}\n\t}\n\tconst relayMode = config.relayMode ?? envMode ?? \"production\";\n\tconst configuredUrls = config.relayUrls ?? envUrls;\n\tconst relayUrls =\n\t\trelayMode === \"production\" ? (configuredUrls ?? VOLT_PRODUCTION_RELAY_URLS) : (configuredUrls ?? []);\n\treturn { relayMode, relayUrls, ...(warning === undefined ? {} : { warning }) };\n}\n\nfunction parseRelayUrlsEnv(value: string | undefined): string[] | undefined {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\tconst urls = value\n\t\t.split(\",\")\n\t\t.map((url) => url.trim())\n\t\t.filter((url) => url.length > 0);\n\treturn urls.length > 0 ? urls : undefined;\n}\n\ninterface PendingPairRequest {\n\trequestId: string;\n\tconnectionId: string;\n\tsecretHash: string;\n\texpiresAt: number;\n\ttimer: NodeJS.Timeout;\n}\n\ninterface ClientConnectionRecord {\n\tconnectionId: string;\n\tclose(reason: string): void;\n}\n\ntype RelayPushDeliveryResult =\n\t| { ok: true; status: IrohRemotePushNotificationDeliveryStatus }\n\t| { ok: false; code: string; message: string };\n\nfunction isExpectedApplicationClose(error: unknown): boolean {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn (\n\t\tmessage.includes(\"ConnectionLost(ApplicationClosed\") &&\n\t\tmessage.includes(\"error_code: 0\") &&\n\t\t(message.includes('reason: b\"done\"') ||\n\t\t\tmessage.includes(`reason: b\"${ACTIVE_REVOKE_CLOSE_REASON}\"`) ||\n\t\t\tmessage.includes(`reason: b\"${ACTIVE_REPLACE_CLOSE_REASON}\"`) ||\n\t\t\tmessage.includes(`reason: b\"${WORKSPACE_UNREGISTERED_CLOSE_REASON}\"`))\n\t);\n}\n\nfunction closeConnection(connection: IrohConnectionLike, reason: string): void {\n\ttry {\n\t\tconnection.close(0n, Array.from(Buffer.from(reason, \"utf8\")));\n\t} catch {\n\t\t// Transport closure is best-effort; registry/task cleanup must still run.\n\t}\n}\n\nasync function waitForConnectionClose(connection: IrohConnectionLike): Promise<void> {\n\tawait Promise.race([\n\t\tconnection.closed().catch(() => {}),\n\t\tnew Promise((resolveDelay) => {\n\t\t\tsetTimeout(resolveDelay, 500);\n\t\t}),\n\t]);\n}\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message: string): Promise<T> {\n\tlet timeoutId: NodeJS.Timeout | undefined;\n\ttry {\n\t\treturn await Promise.race([\n\t\t\tpromise,\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\ttimeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t}\n}\n\nfunction closeIrohRemoteStream(stream: IrohBiStreamLike, _reason?: string): void {\n\tvoid Promise.resolve(stream.send.finish?.()).catch(() => {});\n\tvoid Promise.resolve(stream.recv.stop?.(0n)).catch(() => {});\n}\n\nfunction getRemoteTerminalReason(reason: string): string | undefined {\n\tif (reason === ACTIVE_REVOKE_CLOSE_REASON) {\n\t\treturn \"client_revoked\";\n\t}\n\tif (\n\t\treason === WORKSPACE_UNREGISTERED_CLOSE_REASON ||\n\t\treason === \"workspace_authorization_removed\" ||\n\t\treason === \"lease_transferred\" ||\n\t\treason === \"session_rekeyed_reconnect\"\n\t) {\n\t\treturn reason;\n\t}\n\treturn undefined;\n}\n\n/**\n * The daemon's Iroh host: owns the endpoint identity, pairing, revocation,\n * headless integrated runtimes, workspace/device streams, push dispatch, and\n * the accept loop. Ported from the dissolved src/remote/iroh-host.mjs.\n */\nexport function createIrohDaemonService(config: IrohDaemonServiceConfig = {}): VoltdServiceExtension {\n\treturn (services: VoltdRuntimeServices) => {\n\t\tconst log = services.logger.child(\"iroh\");\n\t\tconst loaded = loadIrohModule();\n\t\tif (!loaded.iroh) {\n\t\t\tlog(\"warn\", formatIrohLoadError(loaded.error));\n\t\t\treturn {\n\t\t\t\tasync handleRequest(connection, request) {\n\t\t\t\t\tif (request.type === \"pair_request\") {\n\t\t\t\t\t\tconnection.send({\n\t\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\t\tcode: \"iroh_unavailable\",\n\t\t\t\t\t\t\tmessage: formatIrohLoadError(loaded.error),\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst service = new IrohDaemonService(loaded.iroh, services, config);\n\t\tvoid service.start();\n\t\treturn {\n\t\t\thandleRequest: (connection, request) => service.handleRequest(connection, request),\n\t\t\tonConnectionClosed: (connection) => service.onControlConnectionClosed(connection),\n\t\t\tonThemeChanged: () => service.onThemeChanged(),\n\t\t\tonKeepAwakeChanged: () => service.onKeepAwakeChanged(),\n\t\t\tstatusExtras: () => service.statusExtras(),\n\t\t\tadmitRelay: (relayId, relayToken, socket, bufferedRemainder) =>\n\t\t\t\tservice.admitRelay(relayId, relayToken, socket, bufferedRemainder),\n\t\t\tshutdown: () => service.shutdown(),\n\t\t};\n\t};\n}\n\nclass IrohDaemonService {\n\tprivate readonly iroh: IrohModuleLike;\n\tprivate readonly services: VoltdRuntimeServices;\n\tprivate readonly relayMode: IrohRelayMode;\n\tprivate readonly relayUrls: string[];\n\tprivate readonly relayAuthToken: string | undefined;\n\tprivate readonly relayConfigWarning: string | undefined;\n\tprivate readonly log: ReturnType<VoltdRuntimeServices[\"logger\"][\"child\"]>;\n\tprivate readonly stateManager: IrohRemoteHostStateManager;\n\tprivate readonly activeStreams = new IrohRemoteActiveStreamRegistry();\n\tprivate readonly clientConnections = new Map<string, Set<ClientConnectionRecord>>();\n\tprivate readonly connectionTasks = new Set<Promise<void>>();\n\tprivate readonly resourceGuard = new IrohRemoteResourceGuard();\n\tprivate readonly pendingPairRequests = new Map<string, PendingPairRequest>();\n\tprivate readonly sessionListCursors = new Map<string, RemoteSessionListCursorEntry>();\n\tprivate readonly pushRelayClient: IrohRemotePushRelayHttpClient;\n\tprivate readonly pushNotificationDeduper = new IrohRemoteInMemoryPushNotificationDeduper();\n\tprivate readonly trustStore: ProjectTrustStore;\n\tprivate readonly runtimes: IntegratedRuntimeRegistry;\n\tprivate readonly worktrees: WorktreeManager;\n\tprivate readonly worktreeRetention: WorktreeRetentionSweeper;\n\tprivate readonly leaseBroker: LeaseBroker;\n\tprivate readonly viewerFeeds: ViewerFeedRegistry;\n\tprivate readonly relays = new RelayRegistry();\n\tprivate endpoint: IrohEndpointLike | undefined;\n\tprivate engine: IrohRemoteHostEngine | undefined;\n\tprivate hostNodeId: string | undefined;\n\tprivate endpointTicket: string | undefined;\n\tprivate shuttingDown = false;\n\tprivate readonly ready: { promise: Promise<void>; resolve: () => void; reject: (error: unknown) => void };\n\n\tconstructor(iroh: IrohModuleLike, services: VoltdRuntimeServices, config: IrohDaemonServiceConfig) {\n\t\tthis.iroh = iroh;\n\t\tthis.services = services;\n\t\tconst relayConfig = resolveIrohRelayConfig(config);\n\t\tthis.relayMode = relayConfig.relayMode;\n\t\tthis.relayUrls = relayConfig.relayUrls;\n\t\tthis.relayConfigWarning = relayConfig.warning;\n\t\tconst envRelayAuthToken = process.env.VOLT_IROH_RELAY_AUTH_TOKEN?.trim();\n\t\tthis.relayAuthToken =\n\t\t\tconfig.relayAuthToken ??\n\t\t\t(envRelayAuthToken !== undefined && envRelayAuthToken !== \"\" ? envRelayAuthToken : undefined) ??\n\t\t\tservices.state.state.settings.relayAuthToken;\n\t\t// Persist a newly seen token so bare restarts keep authenticating against\n\t\t// the relay without re-exporting the env var.\n\t\tif (this.relayAuthToken !== undefined && this.relayAuthToken !== services.state.state.settings.relayAuthToken) {\n\t\t\tservices.state.updateSettings({ relayAuthToken: this.relayAuthToken });\n\t\t}\n\t\tthis.log = services.logger.child(\"iroh\");\n\t\tthis.stateManager = services.stateManager;\n\t\tthis.trustStore = new ProjectTrustStore(services.agentDir);\n\t\tthis.pushRelayClient = new IrohRemotePushRelayHttpClient({\n\t\t\tauthToken: config.pushRelayAuthToken ?? process.env.VOLT_PUSH_RELAY_AUTH_TOKEN,\n\t\t\tbaseUrl: config.pushRelayUrl ?? process.env.VOLT_PUSH_RELAY_URL,\n\t\t});\n\t\tthis.runtimes = new IntegratedRuntimeRegistry({\n\t\t\tagentDir: services.agentDir,\n\t\t\tprofile: config.profile,\n\t\t\tauditLogger: services.auditLogger,\n\t\t\tstateManager: this.stateManager,\n\t\t\tactiveStreams: this.activeStreams,\n\t\t\tdetachedRuntimeTtlMs: () => services.state.state.settings.detachedRuntimeTtlMs,\n\t\t\tgetToolPolicy: (workspace, clientAllowTools) =>\n\t\t\t\tresolveIrohRemoteRuntimeToolPolicy({\n\t\t\t\t\tclientAllowTools,\n\t\t\t\t\tworkspaceAllowTools: workspace.allowedTools,\n\t\t\t\t\tdaemonAllowTools: services.state.state.settings.allowTools,\n\t\t\t\t}),\n\t\t\tgetProjectTrustedForWorkspace: (workspace) =>\n\t\t\t\tresolveIrohRemoteWorkspaceProjectTrusted(workspace, { trustStore: this.trustStore }),\n\t\t\tsetClientLastSessionId: (nodeId, workspace, sessionId) =>\n\t\t\t\tthis.requireEngine().setClientLastSessionId(nodeId, workspace, sessionId),\n\t\t\tresolveWorktree: (workspaceName, hello, targetSessionId) =>\n\t\t\t\tthis.resolveConversationWorktree(workspaceName, hello, targetSessionId),\n\t\t\tresolveWorkingDirectory: (options) => this.resolveConversationWorkingDirectory(options),\n\t\t\tbindWorktreeSession: (workspaceName, worktreeId, sessionId) =>\n\t\t\t\tthis.worktrees.bindSession(workspaceName, worktreeId, sessionId),\n\t\t\tonRuntimeRekeyed: (workspaceName, previousSessionId, sessionId) =>\n\t\t\t\tthis.leaseBroker.rekey(workspaceName, previousSessionId, sessionId),\n\t\t\tonRuntimeDisposed: (entry, reason) => {\n\t\t\t\tthis.leaseBroker.onDaemonRuntimeDisposed(entry.workspaceName, entry.sessionId, reason);\n\t\t\t\tif (entry.worktreeId !== undefined) {\n\t\t\t\t\tthis.worktreeRetention.onRuntimeDisposed(entry.workspaceName, entry.worktreeId);\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t\tthis.worktrees = new WorktreeManager({\n\t\t\tagentDir: services.agentDir,\n\t\t\tstateManager: this.stateManager,\n\t\t\tauditLogger: services.auditLogger,\n\t\t\thasActiveRuntimeForSession: (workspaceName, sessionId) =>\n\t\t\t\tthis.runtimes.findOwner(workspaceName, sessionId) !== undefined,\n\t\t\tflushState: () => services.state.flush(),\n\t\t});\n\t\tthis.worktreeRetention = new WorktreeRetentionSweeper({\n\t\t\tmanager: this.worktrees,\n\t\t\tstateManager: this.stateManager,\n\t\t\tauditLogger: services.auditLogger,\n\t\t\tgetRetentionPolicy: () => resolveWorktreeCleanupPolicy(services.state.state.settings).retention,\n\t\t});\n\t\tthis.viewerFeeds = new ViewerFeedRegistry({\n\t\t\tsendTo: (connectionId, event) => services.controlServer.sendTo(connectionId, event),\n\t\t});\n\t\tthis.leaseBroker = new LeaseBroker({\n\t\t\tisRuntimeStreaming: (workspaceName, sessionId) =>\n\t\t\t\tthis.runtimes.findOwner(workspaceName, sessionId)?.runtime.session.isBusy ?? false,\n\t\t\twaitForRuntimeIdle: async (workspaceName, sessionId) => {\n\t\t\t\tawait this.runtimes.findOwner(workspaceName, sessionId)?.runtime.session.waitForIdle();\n\t\t\t},\n\t\t\tdisposeRuntime: async (workspaceName, sessionId, reason) => {\n\t\t\t\tconst owner = this.runtimes.findOwner(workspaceName, sessionId);\n\t\t\t\tif (owner) {\n\t\t\t\t\tawait this.runtimes.stopEntry(owner, reason);\n\t\t\t\t}\n\t\t\t},\n\t\t\tclosePhoneStreams: async (workspaceName, sessionId, reason) => {\n\t\t\t\tawait this.closeActiveStreamsForConversationKey(workspaceName, sessionId, reason);\n\t\t\t},\n\t\t\tcloseRelays: (record, reason) => {\n\t\t\t\tfor (const relayId of Array.from(record.relayIds)) {\n\t\t\t\t\tthis.relays.closeActive(relayId, reason);\n\t\t\t\t\t// Unredeemed offers must not linger until the 10s token expiry:\n\t\t\t\t\t// fail the phone's deferred handshake immediately so it retries\n\t\t\t\t\t// against the new lease owner.\n\t\t\t\t\tthis.abortPendingRelay(relayId, reason, \"relay offer cancelled; retry\", RELAY_OFFER_RETRY_AFTER_MS);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDrainStarted: (record, viewerFeedId) => {\n\t\t\t\tconst owner = this.runtimes.findOwner(record.workspaceName, record.sessionId);\n\t\t\t\tif (owner && record.tuiConnectionId) {\n\t\t\t\t\tthis.viewerFeeds.start(viewerFeedId, record.tuiConnectionId, owner.runtime.session);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonDrainEnded: (_record, viewerFeedId, reason) => {\n\t\t\t\tthis.viewerFeeds.end(viewerFeedId, reason);\n\t\t\t},\n\t\t\taudit: (event) => {\n\t\t\t\tvoid this.logAudit({\n\t\t\t\t\ttype: event.type,\n\t\t\t\t\tworkspace: event.workspaceName,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tdetails: { sessionId: event.sessionId, ...event.details },\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t\tlet readyResolve: () => void = () => {};\n\t\tlet readyReject: (error: unknown) => void = () => {};\n\t\tconst readyPromise = new Promise<void>((resolve, reject) => {\n\t\t\treadyResolve = resolve;\n\t\t\treadyReject = reject;\n\t\t});\n\t\treadyPromise.catch(() => {});\n\t\tthis.ready = { promise: readyPromise, resolve: readyResolve, reject: readyReject };\n\t}\n\n\tprivate requireEngine(): IrohRemoteHostEngine {\n\t\tif (!this.engine) {\n\t\t\tthrow new Error(\"iroh host engine is not ready\");\n\t\t}\n\t\treturn this.engine;\n\t}\n\n\tprivate async pruneWorktreesOnStart(): Promise<void> {\n\t\tif (!resolveWorktreeCleanupPolicy(this.services.state.state.settings).pruneOnStart) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst state = await this.stateManager.getState();\n\t\t\tconst workspacesWithRecords = new Set((state.worktrees ?? []).map((worktree) => worktree.workspaceName));\n\t\t\tfor (const workspace of state.workspaces) {\n\t\t\t\t// Skip workspaces with neither records nor checkout directories: no git\n\t\t\t\t// subprocesses or audit noise on the common no-worktrees start.\n\t\t\t\tif (\n\t\t\t\t\t!workspacesWithRecords.has(workspace.name) &&\n\t\t\t\t\t!existsSync(getWorkspaceWorktreesDir(this.services.agentDir, workspace.path))\n\t\t\t\t) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.worktrees.prune(workspace);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.log(\"warn\", \"worktree prune failed on start\", {\n\t\t\t\t\t\tworkspace: workspace.name,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Startup prune is best-effort; a manual `volt remote worktree prune` covers it.\n\t\t}\n\t}\n\n\tprivate getResponseContext(): RemoteHostResponseContext {\n\t\treturn {\n\t\t\thostNodeId: this.hostNodeId,\n\t\t\trelayMode: this.relayMode,\n\t\t\t...(this.relayMode === \"production\" ? { relayUrls: this.relayUrls } : {}),\n\t\t};\n\t}\n\n\tprivate async isAuthorizationGrantCurrent(authorization: IrohRemoteClientAuthorizationSuccess): Promise<boolean> {\n\t\tconst client = await this.stateManager.getClient(authorization.client.nodeId);\n\t\treturn client?.rpcGrant.revision === authorization.client.rpcGrant.revision;\n\t}\n\n\tprivate getCommandContext(conversation?: {\n\t\tworkspaceName: string;\n\t\tworkspacePath?: string;\n\t\tentry: IntegratedRuntimeEntry;\n\t\tstreamEntry?: IrohRemoteActiveStreamEntry;\n\t}): ConversationCommandContext {\n\t\treturn {\n\t\t\tagentDir: this.services.agentDir,\n\t\t\tauditLogger: this.services.auditLogger,\n\t\t\thostEngine: this.engine,\n\t\t\tstateManager: this.stateManager,\n\t\t\tsessionListCursors: this.sessionListCursors,\n\t\t\tsessionListCursorTtlMs: REMOTE_SESSION_LIST_CURSOR_TTL_MS,\n\t\t\tlistRuntimeStates: (workspaceName) => {\n\t\t\t\tconst states = new Map<string, Exclude<LeaseState, \"unowned\">>();\n\t\t\t\tfor (const record of this.leaseBroker.list()) {\n\t\t\t\t\tif (record.workspaceName === workspaceName && record.state !== \"unowned\") {\n\t\t\t\t\t\tstates.set(record.sessionId, record.state);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn states;\n\t\t\t},\n\t\t\tkeepAwake: this.services.keepAwake,\n\t\t\tonKeepAwakeSetting: (enabled) => this.services.state.updateSettings({ keepAwakeEnabled: enabled }),\n\t\t\twebSearchKey: this.services.webSearchKey,\n\t\t\tcreateWorktreeBackend: (workspace) => this.createWorktreeRpcBackend(workspace),\n\t\t\tonWorkspaceUnregistered: async (workspaceName) => {\n\t\t\t\t// Unregistering the conversation's own workspace keeps the requesting\n\t\t\t\t// stream and runtime alive so the response can still be delivered\n\t\t\t\t// (mirrors the workspace-management stream path).\n\t\t\t\tconst excludeOwn = conversation !== undefined && workspaceName === conversation.workspaceName;\n\t\t\t\tconst workspacePath = excludeOwn ? conversation.workspacePath : undefined;\n\t\t\t\tawait this.cleanupUnregisteredWorkspace(\n\t\t\t\t\tworkspaceName,\n\t\t\t\t\texcludeOwn\n\t\t\t\t\t\t? { streamEntry: conversation.streamEntry, runtimeEntry: conversation.entry, workspacePath }\n\t\t\t\t\t\t: {},\n\t\t\t\t);\n\t\t\t},\n\t\t\t...(conversation === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tisDraining: () =>\n\t\t\t\t\t\t\tthis.leaseBroker.isDraining(conversation.workspaceName, conversation.entry.sessionId),\n\t\t\t\t\t\tisSubagentSession: () =>\n\t\t\t\t\t\t\tconversation.entry.subagentId !== undefined || conversation.entry.parentSessionId !== undefined,\n\t\t\t\t\t}),\n\t\t};\n\t}\n\n\tasync start(): Promise<void> {\n\t\tlet endpoint: IrohEndpointLike | undefined;\n\t\tif (this.relayConfigWarning !== undefined) {\n\t\t\tthis.log(\"warn\", this.relayConfigWarning);\n\t\t}\n\t\t// Reconcile worktree records/checkouts before the endpoint starts taking\n\t\t// conversations (design §5.3 pruneOnStart; default on, quarantine-only).\n\t\tawait this.pruneWorktreesOnStart();\n\t\ttry {\n\t\t\tconst builder = this.iroh.Endpoint.builder();\n\t\t\tif (this.relayMode === \"development\") {\n\t\t\t\tthis.log(\n\t\t\t\t\t\"warn\",\n\t\t\t\t\t\"using public n0 relays (development only; unset VOLT_IROH_RELAY_MODE for the Volt relays)\",\n\t\t\t\t);\n\t\t\t\tthis.iroh.presetN0(builder);\n\t\t\t} else if (this.relayMode === \"production\") {\n\t\t\t\tif (this.relayUrls.length === 0) {\n\t\t\t\t\tthrow new Error(\"relayMode production requires relay URLs (config.relayUrls or VOLT_IROH_RELAY_URLS)\");\n\t\t\t\t}\n\t\t\t\tthis.iroh.presetN0DisableRelay(builder);\n\t\t\t\tif (this.relayAuthToken !== undefined) {\n\t\t\t\t\tconst relayMap = this.iroh.RelayMap.empty();\n\t\t\t\t\tfor (const url of this.relayUrls) {\n\t\t\t\t\t\trelayMap.insert({ url, authToken: this.relayAuthToken });\n\t\t\t\t\t}\n\t\t\t\t\tbuilder.relayMode(this.iroh.RelayMode.custom(relayMap));\n\t\t\t\t} else {\n\t\t\t\t\tbuilder.relayMode(this.iroh.RelayMode.customFromUrls(this.relayUrls));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.iroh.presetMinimal(builder);\n\t\t\t\tbuilder.relayMode(this.iroh.RelayMode.disabled());\n\t\t\t}\n\t\t\tconst secretKey = this.services.state.state.irohSecretKey;\n\t\t\tif (secretKey) {\n\t\t\t\tbuilder.secretKey(secretKey);\n\t\t\t}\n\t\t\tbuilder.alpns([Array.from(Buffer.from(IROH_REMOTE_ALPN, \"utf8\"))]);\n\t\t\tendpoint = await builder.bind();\n\t\t\tif (!secretKey) {\n\t\t\t\tconst boundKey = endpoint.secretKey().toBytes();\n\t\t\t\tthis.services.state.setHostState({\n\t\t\t\t\t...this.services.state.getHostState(),\n\t\t\t\t\thostSecretKey: boundKey,\n\t\t\t\t});\n\t\t\t\t// Persist the freshly minted identity synchronously before the accept\n\t\t\t\t// loop starts taking pairings. A crash/SIGKILL inside the 250ms debounce\n\t\t\t\t// window would otherwise lose the key, and every phone paired against\n\t\t\t\t// this endpoint would be talking to a node id the daemon can never\n\t\t\t\t// reproduce on restart.\n\t\t\t\tawait this.services.state.flush();\n\t\t\t}\n\t\t\tif (this.relayMode !== \"disabled\") {\n\t\t\t\tawait endpoint.online();\n\t\t\t}\n\t\t\tthis.endpoint = endpoint;\n\t\t\tthis.hostNodeId = endpoint.id().toString();\n\t\t\tthis.endpointTicket = this.iroh.EndpointTicket.fromAddr(endpoint.addr()).toString();\n\t\t\tthis.engine = new IrohRemoteHostEngine({\n\t\t\t\tauditLogger: this.services.auditLogger,\n\t\t\t\tclassifyWorkspaceAvailability: getIrohRemoteWorkspaceAvailabilityStatus,\n\t\t\t\thostNodeId: this.hostNodeId,\n\t\t\t\trelayMode: this.relayMode,\n\t\t\t\t...(this.relayMode === \"production\" ? { relayUrls: this.relayUrls } : {}),\n\t\t\t\tstateManager: this.stateManager,\n\t\t\t\tvalidateWorkspace: async (workspace) =>\n\t\t\t\t\t(await getIrohRemoteWorkspaceAvailabilityStatus(workspace)) === \"available\",\n\t\t\t\tworkspace: { name: \"voltd\", path: this.services.agentDir },\n\t\t\t});\n\t\t\tthis.ready.resolve();\n\t\t\tthis.log(\"info\", `iroh endpoint online`, {\n\t\t\t\thostNodeId: this.hostNodeId,\n\t\t\t\trelayMode: this.relayMode,\n\t\t\t\t...(this.relayMode === \"production\" ? { relayUrls: this.relayUrls } : {}),\n\t\t\t});\n\t\t\tvoid this.acceptLoop(endpoint);\n\t\t} catch (error) {\n\t\t\tif (endpoint) {\n\t\t\t\t// bind() succeeded but a later start step (e.g. online()) failed before\n\t\t\t\t// the endpoint was adopted; close it so its QUIC socket is not leaked.\n\t\t\t\ttry {\n\t\t\t\t\tawait endpoint.close();\n\t\t\t\t} catch {}\n\t\t\t\tif (this.endpoint === endpoint) {\n\t\t\t\t\tthis.endpoint = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.ready.reject(error);\n\t\t\tthis.log(\"error\", `failed to start iroh endpoint: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\t}\n\n\tprivate async acceptLoop(endpoint: IrohEndpointLike): Promise<void> {\n\t\twhile (!this.shuttingDown) {\n\t\t\tlet incoming: Awaited<ReturnType<IrohEndpointLike[\"acceptNext\"]>>;\n\t\t\ttry {\n\t\t\t\tincoming = await endpoint.acceptNext();\n\t\t\t} catch (error) {\n\t\t\t\tif (this.shuttingDown) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tthis.log(\"error\", `accept failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!incoming) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst connectionAdmission = this.resourceGuard.tryAcquireConnectionTask();\n\t\t\tif (!connectionAdmission.ok) {\n\t\t\t\tlet refused = true;\n\t\t\t\ttry {\n\t\t\t\t\tawait incoming.refuse();\n\t\t\t\t} catch {\n\t\t\t\t\trefused = false;\n\t\t\t\t}\n\t\t\t\tawait this.logAudit({\n\t\t\t\t\ttype: \"iroh_security_connection_limit\",\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"incoming connection refused at daemon connection-task limit\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tlimit: connectionAdmission.limit,\n\t\t\t\t\t\trefused,\n\t\t\t\t\t\tscope: connectionAdmission.scope,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst task = this.handleConnection(incoming)\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tif (!isExpectedApplicationClose(error)) {\n\t\t\t\t\t\tthis.log(\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t`connection error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tthis.connectionTasks.delete(task);\n\t\t\t\t\tconnectionAdmission.lease.release();\n\t\t\t\t});\n\t\t\tthis.connectionTasks.add(task);\n\t\t}\n\t}\n\n\tprivate async handleConnection(\n\t\tincoming: NonNullable<Awaited<ReturnType<IrohEndpointLike[\"acceptNext\"]>>>,\n\t): Promise<void> {\n\t\tlet connection: IrohConnectionLike;\n\t\ttry {\n\t\t\tconst accepting = await incoming.accept();\n\t\t\tconnection = await accepting.connect();\n\t\t} catch {\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"iroh_security_transport_rejected\",\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"incoming transport handshake failed\",\n\t\t\t\tdetails: { phase: \"transport_connect\" },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconnection.setMaxConcurrentBiStreams(BigInt(MAX_CONCURRENT_STREAMS_PER_CONNECTION));\n\t\t} catch {\n\t\t\tcloseConnection(connection, \"stream_limit_configuration_failed\");\n\t\t\tawait waitForConnectionClose(connection);\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"iroh_security_transport_rejected\",\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"connected transport could not enforce the inbound stream limit\",\n\t\t\t\tdetails: { phase: \"stream_limit_configuration\" },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet remoteId: string;\n\t\ttry {\n\t\t\tremoteId = connection.remoteId().toString();\n\t\t} catch {\n\t\t\tcloseConnection(connection, \"invalid_remote_identity\");\n\t\t\tawait waitForConnectionClose(connection);\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"iroh_security_transport_rejected\",\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"connected transport did not expose a valid remote identity\",\n\t\t\t\tdetails: { phase: \"remote_identity\" },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst nodeConnectionAdmission = this.resourceGuard.tryAcquireNodeConnection(remoteId);\n\t\tif (!nodeConnectionAdmission.ok) {\n\t\t\tcloseConnection(connection, \"node_connection_limit\");\n\t\t\tawait waitForConnectionClose(connection);\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"iroh_security_connection_limit\",\n\t\t\t\tclientNodeId: remoteId,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"connection refused at per-node connection limit\",\n\t\t\t\tdetails: { limit: nodeConnectionAdmission.limit, scope: nodeConnectionAdmission.scope },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst unauthenticatedAdmission = this.resourceGuard.tryAcquireUnauthenticatedConnection(remoteId);\n\t\tif (!unauthenticatedAdmission.ok) {\n\t\t\tnodeConnectionAdmission.lease.release();\n\t\t\tcloseConnection(connection, \"unauthenticated_connection_limit\");\n\t\t\tawait waitForConnectionClose(connection);\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"iroh_security_unauthenticated_connection_limit\",\n\t\t\t\tclientNodeId: remoteId,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"unauthenticated connection refused at admission limit\",\n\t\t\t\tdetails: { limit: unauthenticatedAdmission.limit, scope: unauthenticatedAdmission.scope },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst connectionId = `conn-${++activeConnectionSequence}`;\n\t\tconst removeClientConnection = this.registerClientConnection(remoteId, connection, connectionId);\n\t\tconst streamTasks = new Set<Promise<void>>();\n\t\tlet acceptedStreamCount = 0;\n\t\tlet closeRequested = false;\n\t\tlet authenticated = false;\n\t\tconst unauthenticatedTimer = setTimeout(() => {\n\t\t\tif (authenticated || closeRequested) return;\n\t\t\tcloseRequested = true;\n\t\t\tcloseConnection(connection, \"handshake_timeout\");\n\t\t\tvoid this.logAudit({\n\t\t\t\ttype: \"iroh_security_handshake_timeout\",\n\t\t\t\tclientNodeId: remoteId,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"connection did not authenticate before the handshake deadline\",\n\t\t\t\tdetails: { connectionId, timeoutMs: IROH_UNAUTHENTICATED_CONNECTION_TIMEOUT_MS },\n\t\t\t});\n\t\t}, IROH_UNAUTHENTICATED_CONNECTION_TIMEOUT_MS);\n\t\tunauthenticatedTimer.unref?.();\n\n\t\tconst markAuthenticated = async (): Promise<boolean> => {\n\t\t\tif (authenticated) return true;\n\t\t\tif (closeRequested) return false;\n\t\t\tauthenticated = true;\n\t\t\tclearTimeout(unauthenticatedTimer);\n\t\t\tunauthenticatedAdmission.lease.release();\n\t\t\tthis.log(\"info\", `client connection opened: ${remoteId} (${connectionId})`);\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"client_connected\",\n\t\t\t\tclientNodeId: remoteId,\n\t\t\t\tsuccess: true,\n\t\t\t\tdetails: { connectionId },\n\t\t\t});\n\t\t\treturn true;\n\t\t};\n\n\t\tconst requestCloseWhenIdle = () => {\n\t\t\tif (closeRequested || acceptedStreamCount === 0 || streamTasks.size > 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcloseRequested = true;\n\t\t\tcloseConnection(connection, \"done\");\n\t\t};\n\n\t\ttry {\n\t\t\twhile (!closeRequested) {\n\t\t\t\tconst stream = await (!authenticated\n\t\t\t\t\t? withTimeout(connection.acceptBi(), DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS, \"handshake timed out\")\n\t\t\t\t\t: connection.acceptBi());\n\t\t\t\tacceptedStreamCount++;\n\t\t\t\tif (streamTasks.size >= MAX_CONCURRENT_STREAMS_PER_CONNECTION) {\n\t\t\t\t\t// One connection is holding too many concurrent streams open. Refuse\n\t\t\t\t\t// further work and close the connection rather than let\n\t\t\t\t\t// it exhaust daemon resources; the just-accepted stream is torn down\n\t\t\t\t\t// with the connection. A legitimate client never reaches this.\n\t\t\t\t\tcloseRequested = true;\n\t\t\t\t\tcloseConnection(connection, \"stream_limit_exceeded\");\n\t\t\t\t\tawait this.logAudit({\n\t\t\t\t\t\ttype: \"iroh_security_stream_limit\",\n\t\t\t\t\t\tclientNodeId: remoteId,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: \"connection exceeded concurrent stream limit\",\n\t\t\t\t\t\tdetails: { connectionId, limit: MAX_CONCURRENT_STREAMS_PER_CONNECTION, scope: \"connection\" },\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst streamAdmission = this.resourceGuard.tryAcquireActiveStream(remoteId);\n\t\t\t\tif (!streamAdmission.ok) {\n\t\t\t\t\tcloseIrohRemoteStream(stream, \"stream_limit_exceeded\");\n\t\t\t\t\tawait this.logAudit({\n\t\t\t\t\t\ttype: \"iroh_security_stream_limit\",\n\t\t\t\t\t\tclientNodeId: remoteId,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: \"stream refused at daemon active-stream limit\",\n\t\t\t\t\t\tdetails: { connectionId, limit: streamAdmission.limit, scope: streamAdmission.scope },\n\t\t\t\t\t});\n\t\t\t\t\trequestCloseWhenIdle();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst streamId = `stream-${++activeStreamSequence}`;\n\t\t\t\tconst task = this.handleConnectionStream(\n\t\t\t\t\tstream,\n\t\t\t\t\tconnection,\n\t\t\t\t\tremoteId,\n\t\t\t\t\tconnectionId,\n\t\t\t\t\tstreamId,\n\t\t\t\t\tmarkAuthenticated,\n\t\t\t\t)\n\t\t\t\t\t.catch(async (error) => {\n\t\t\t\t\t\tif (!isExpectedApplicationClose(error)) {\n\t\t\t\t\t\t\tif (authenticated) {\n\t\t\t\t\t\t\t\tthis.log(\n\t\t\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t\t\t`stream error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tawait this.logAudit({\n\t\t\t\t\t\t\t\t\ttype: \"iroh_security_transport_rejected\",\n\t\t\t\t\t\t\t\t\tclientNodeId: remoteId,\n\t\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\t\terror: \"unauthenticated stream failed\",\n\t\t\t\t\t\t\t\t\tdetails: { connectionId, phase: \"stream_handshake\" },\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tstreamAdmission.lease.release();\n\t\t\t\t\t\tstreamTasks.delete(task);\n\t\t\t\t\t\trequestCloseWhenIdle();\n\t\t\t\t\t});\n\t\t\t\tstreamTasks.add(task);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (acceptedStreamCount === 0 && authenticated) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (acceptedStreamCount === 0 && !closeRequested) {\n\t\t\t\tawait this.logAudit({\n\t\t\t\t\ttype: \"iroh_security_handshake_timeout\",\n\t\t\t\t\tclientNodeId: remoteId,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"connection closed or timed out before opening a handshake stream\",\n\t\t\t\t\tdetails: { connectionId, timeoutMs: DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS },\n\t\t\t\t});\n\t\t\t}\n\t\t} finally {\n\t\t\tclearTimeout(unauthenticatedTimer);\n\t\t\tunauthenticatedAdmission.lease.release();\n\t\t\tnodeConnectionAdmission.lease.release();\n\t\t\tawait this.closeActiveStreamsForConnection(connectionId, \"connection_closed\");\n\t\t\tawait Promise.allSettled(streamTasks);\n\t\t\tremoveClientConnection();\n\t\t\tif (!closeRequested) {\n\t\t\t\tcloseConnection(connection, \"done\");\n\t\t\t}\n\t\t\tawait waitForConnectionClose(connection);\n\t\t\tif (authenticated) {\n\t\t\t\tthis.log(\"info\", `client connection closed: ${remoteId} (${connectionId})`);\n\t\t\t\tawait this.logAudit({\n\t\t\t\t\ttype: \"client_disconnected\",\n\t\t\t\t\tclientNodeId: remoteId,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tdetails: { connectionId },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleConnectionStream(\n\t\tstream: IrohBiStreamLike,\n\t\tconnection: IrohConnectionLike,\n\t\tremoteId: string,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t\tmarkAuthenticated: () => Promise<boolean>,\n\t): Promise<void> {\n\t\tconst engine = this.requireEngine();\n\t\tconst handshakeAdmission = this.resourceGuard.tryAcquireHandshake(remoteId);\n\t\tif (!handshakeAdmission.ok) {\n\t\t\tcloseIrohRemoteStream(stream, \"handshake_limit_exceeded\");\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"iroh_security_handshake_limit\",\n\t\t\t\tclientNodeId: remoteId,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"stream refused at concurrent handshake limit\",\n\t\t\t\tdetails: { connectionId, limit: handshakeAdmission.limit, scope: handshakeAdmission.scope },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet handshake: IrohRemoteHostHandshakeResult;\n\t\ttry {\n\t\t\thandshake = await engine.readHandshake(stream, remoteId, {\n\t\t\t\tchild: \"volt\",\n\t\t\t\tmaxLineBytes: DEFAULT_IROH_REMOTE_HANDSHAKE_MAX_LINE_BYTES,\n\t\t\t\ttimeoutMs: DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS,\n\t\t\t\twriteSuccessResponse: false,\n\t\t\t});\n\t\t} finally {\n\t\t\thandshakeAdmission.lease.release();\n\t\t}\n\t\tif (!handshake.ok) {\n\t\t\tif (\n\t\t\t\thandshake.response.outcome === \"workspace_authorization_removed\" &&\n\t\t\t\ttypeof handshake.response.workspace === \"string\"\n\t\t\t) {\n\t\t\t\tawait this.closeWorkspaceAuthorizationRemovedStreams(remoteId, handshake.response.workspace);\n\t\t\t}\n\t\t\tawait Promise.resolve(stream.send.finish?.()).catch(() => {});\n\t\t\tawait Promise.resolve(stream.recv.stop?.(0n)).catch(() => {});\n\t\t\treturn;\n\t\t}\n\t\tif (!(await markAuthenticated())) {\n\t\t\tcloseIrohRemoteStream(stream, \"handshake_timeout\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst streamCapability = getIrohRemoteStreamCapability({\n\t\t\tmode: handshake.hello.mode,\n\t\t\t...(handshake.hello.mode === \"workspaceManagement\"\n\t\t\t\t? { purpose: handshake.hello.workspaceManagement.purpose }\n\t\t\t\t: {}),\n\t\t});\n\t\tif (\n\t\t\tstreamCapability !== undefined &&\n\t\t\t!hasIrohRemoteRpcCapability(\n\t\t\t\tparseIrohRemoteRpcGrant(handshake.authorization.client.rpcGrant, \"client rpcGrant\"),\n\t\t\t\tstreamCapability,\n\t\t\t)\n\t\t) {\n\t\t\tawait writeIrohRemoteHandshakeResponse(\n\t\t\t\tstream.send,\n\t\t\t\tcreateIrohRemoteHandshakeFailure(`rpc_capability_denied: ${streamCapability}`, {\n\t\t\t\t\thostNodeId: this.hostNodeId,\n\t\t\t\t\tworkspace: handshake.authorization.workspace.name,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tcloseIrohRemoteStream(stream, \"rpc_capability_denied\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.notifyPairingConsumed(handshake, remoteId);\n\n\t\tif (handshake.authorization.paired) {\n\t\t\tthis.log(\"info\", `paired client stream: ${handshake.authorization.client.label} (${remoteId}, ${streamId})`);\n\t\t}\n\n\t\tif (handshake.hello.mode === \"workspaceDiscovery\") {\n\t\t\tawait this.runWorkspaceDiscovery(stream, handshake, connection, connectionId, streamId);\n\t\t\treturn;\n\t\t}\n\t\tif (handshake.hello.mode === \"workspaceManagement\") {\n\t\t\tif (handshake.hello.workspaceManagement.purpose === \"manage_worktrees\") {\n\t\t\t\tawait this.runWorktreeManagement(stream, handshake, connection, connectionId, streamId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.runWorkspaceManagement(stream, handshake, connection, connectionId, streamId);\n\t\t\treturn;\n\t\t}\n\t\tawait this.runIntegratedConversation(stream, handshake, connection, connectionId, streamId);\n\t}\n\n\t// ==========================================================================\n\t// Workspace streams\n\t// ==========================================================================\n\n\tprivate registerActiveStream(\n\t\tauthorization: IrohRemoteClientAuthorizationSuccess,\n\t\tsessionId: string,\n\t\tstream: IrohBiStreamLike,\n\t\tconnection: IrohConnectionLike,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t\tdetails: { terminalSessionId?: string | undefined; sanitizerOverrides?: RemoteSanitizerOverrides } = {},\n\t): { entry: IrohRemoteActiveStreamEntry; remove: () => void } {\n\t\tconst entry: IrohRemoteActiveStreamEntry = {\n\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\tconnectionId,\n\t\t\tsessionId,\n\t\t\tstreamId,\n\t\t\tworkspaceName: authorization.workspace.name,\n\t\t\tclose: (reason: string) =>\n\t\t\t\tthis.closeStreamWithTerminal(stream, reason, {\n\t\t\t\t\tauthorization,\n\t\t\t\t\tsessionId: Object.hasOwn(details, \"terminalSessionId\") ? details.terminalSessionId : entry.sessionId,\n\t\t\t\t}),\n\t\t\tcloseConnection: (reason: string) => closeConnection(connection, reason),\n\t\t\twrite: (value: object) =>\n\t\t\t\twriteIrohRemoteJsonLine(stream.send, value, authorization, details.sanitizerOverrides ?? {}),\n\t\t};\n\t\tconst remove = this.activeStreams.register(entry);\n\t\treturn { entry, remove };\n\t}\n\n\t// ==========================================================================\n\t// iOS theme token push (§9.5) — flag off by default, capability gated\n\t// ==========================================================================\n\n\tprivate isThemeTokenPushEnabled(): boolean {\n\t\treturn this.services.state.state.settings.themeTokenPush === true || process.env.VOLT_HOST_THEME_TOKENS === \"1\";\n\t}\n\n\t/** Send the current sanitized theme tokens to one capable stream. */\n\tprivate pushThemeTokensToStream(entry: IrohRemoteActiveStreamEntry): void {\n\t\tif (!this.isThemeTokenPushEnabled() || !entry.capabilities?.has(HOST_THEME_TOKENS_FEATURE)) {\n\t\t\treturn;\n\t\t}\n\t\tconst frame = createHostThemeTokensFrame(getCurrentThemeName() ?? \"dark\", getResolvedThemeColors());\n\t\tvoid Promise.resolve(entry.write?.(frame)).catch(() => {});\n\t}\n\n\t/** Theme changed: fan the new tokens out to every capable phone stream. */\n\tonThemeChanged(): void {\n\t\tif (!this.isThemeTokenPushEnabled()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of this.activeStreams.allEntries()) {\n\t\t\tthis.pushThemeTokensToStream(entry);\n\t\t}\n\t}\n\n\t/**\n\t * Keep-awake status changed (control toggle, phone toggle, or degradation):\n\t * fan the new state to every phone stream. Clients that ignore the frame are\n\t * fully supported, so no capability gating.\n\t */\n\tonKeepAwakeChanged(): void {\n\t\tconst frame = {\n\t\t\ttype: \"keep_awake_changed\",\n\t\t\tdata: { keepAwake: toRpcKeepAwakeStatus(this.services.keepAwake.status) },\n\t\t};\n\t\tfor (const entry of this.activeStreams.allEntries()) {\n\t\t\tvoid Promise.resolve(entry.write?.(frame)).catch(() => {});\n\t\t}\n\t}\n\n\tprivate async closeStreamWithTerminal(\n\t\tstream: IrohBiStreamLike,\n\t\treason: string,\n\t\tterminal: { authorization: IrohRemoteClientAuthorizationSuccess; sessionId: string | undefined },\n\t): Promise<void> {\n\t\tconst terminalReason = getRemoteTerminalReason(reason);\n\t\tif (terminalReason) {\n\t\t\tawait writeIrohRemoteJsonLine(\n\t\t\t\tstream.send,\n\t\t\t\t{\n\t\t\t\t\ttype: \"remote_terminal\",\n\t\t\t\t\treason: terminalReason,\n\t\t\t\t\tworkspace: terminal.authorization.workspace.name,\n\t\t\t\t\t...(terminal.sessionId === undefined ? {} : { sessionId: terminal.sessionId }),\n\t\t\t\t\thostNodeId: this.hostNodeId,\n\t\t\t\t},\n\t\t\t\tterminal.authorization,\n\t\t\t).catch(() => {});\n\t\t}\n\t\tcloseIrohRemoteStream(stream, reason);\n\t}\n\n\tprivate async runWorkspaceDiscovery(\n\t\tstream: IrohBiStreamLike,\n\t\thandshake: Extract<IrohRemoteHostHandshakeResult, { ok: true }>,\n\t\tconnection: IrohConnectionLike,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t): Promise<void> {\n\t\tawait writeIrohRemoteHandshakeResponse(stream.send, handshake.response);\n\t\tconst activeStream = this.registerActiveStream(\n\t\t\thandshake.authorization,\n\t\t\tWORKSPACE_DISCOVERY_STREAM_SESSION_ID,\n\t\t\tstream,\n\t\t\tconnection,\n\t\t\tconnectionId,\n\t\t\tstreamId,\n\t\t\t{ terminalSessionId: undefined },\n\t\t);\n\t\ttry {\n\t\t\tawait runWorkspaceDiscoveryStream(\n\t\t\t\t{\n\t\t\t\t\tstream,\n\t\t\t\t\tinitialInput: handshake.initialInput,\n\t\t\t\t\tauthorization: handshake.authorization,\n\t\t\t\t\tisRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(handshake.authorization),\n\t\t\t\t\tcloseStream: (reason) => closeIrohRemoteStream(stream, reason),\n\t\t\t\t},\n\t\t\t\t{ commandContext: this.getCommandContext() },\n\t\t\t);\n\t\t} finally {\n\t\t\tactiveStream.remove();\n\t\t}\n\t}\n\n\tprivate async runWorkspaceManagement(\n\t\tstream: IrohBiStreamLike,\n\t\thandshake: Extract<IrohRemoteHostHandshakeResult, { ok: true }>,\n\t\tconnection: IrohConnectionLike,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t): Promise<void> {\n\t\tawait writeIrohRemoteHandshakeResponse(stream.send, handshake.response);\n\t\tconst activeStream = this.registerActiveStream(\n\t\t\thandshake.authorization,\n\t\t\tWORKSPACE_MANAGEMENT_STREAM_SESSION_ID,\n\t\t\tstream,\n\t\t\tconnection,\n\t\t\tconnectionId,\n\t\t\tstreamId,\n\t\t\t{ terminalSessionId: undefined },\n\t\t);\n\t\ttry {\n\t\t\tawait runWorkspaceManagementStream(\n\t\t\t\t{\n\t\t\t\t\tstream,\n\t\t\t\t\tinitialInput: handshake.initialInput,\n\t\t\t\t\tauthorization: handshake.authorization,\n\t\t\t\t\tisRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(handshake.authorization),\n\t\t\t\t\tcloseStream: (reason) => {\n\t\t\t\t\t\tactiveStream.remove();\n\t\t\t\t\t\tcloseIrohRemoteStream(stream, reason);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tauditLogger: this.services.auditLogger,\n\t\t\t\t\tcommandContext: this.getCommandContext(),\n\t\t\t\t\tunregisterWorkspace: async (workspaceName) => {\n\t\t\t\t\t\tlet removedWorkspace: Awaited<ReturnType<IrohRemoteHostStateManager[\"unregisterWorkspace\"]>>;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tremovedWorkspace = await this.stateManager.unregisterWorkspace(workspaceName);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tif (!isIrohRemoteWorkspaceHasWorktreesError(error)) {\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\t\terror: IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,\n\t\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\t\tworktreeCount: error.worktreeIds.length,\n\t\t\t\t\t\t\t\t\tworktreeIds: error.worktreeIds,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!removedWorkspace) {\n\t\t\t\t\t\t\treturn { ok: false, error: \"workspace_unregistered\" };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.engine?.clearPairingSecretForWorkspace(workspaceName);\n\t\t\t\t\t\tconst { closedStreamCount, stoppedRuntimeCount } = await this.cleanupUnregisteredWorkspace(\n\t\t\t\t\t\t\tworkspaceName,\n\t\t\t\t\t\t\t{ streamEntry: activeStream.entry, workspacePath: removedWorkspace.path },\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn { ok: true, closedStreamCount, stoppedRuntimeCount };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} finally {\n\t\t\tactiveStream.remove();\n\t\t}\n\t}\n\n\t/** Serve a manage_worktrees workspaceManagement stream (worktrees.v1). */\n\tprivate async runWorktreeManagement(\n\t\tstream: IrohBiStreamLike,\n\t\thandshake: Extract<IrohRemoteHostHandshakeResult, { ok: true }>,\n\t\tconnection: IrohConnectionLike,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t): Promise<void> {\n\t\tawait writeIrohRemoteHandshakeResponse(stream.send, handshake.response);\n\t\tconst sanitizerOverrides: RemoteSanitizerOverrides = {\n\t\t\tadditionalRedactedPaths: [getWorktreesRoot(this.services.agentDir)],\n\t\t};\n\t\tconst activeStream = this.registerActiveStream(\n\t\t\thandshake.authorization,\n\t\t\tWORKSPACE_MANAGEMENT_STREAM_SESSION_ID,\n\t\t\tstream,\n\t\t\tconnection,\n\t\t\tconnectionId,\n\t\t\tstreamId,\n\t\t\t{ terminalSessionId: undefined, sanitizerOverrides },\n\t\t);\n\t\ttry {\n\t\t\tawait runWorktreeManagementStream(\n\t\t\t\t{\n\t\t\t\t\tstream,\n\t\t\t\t\tinitialInput: handshake.initialInput,\n\t\t\t\t\tauthorization: handshake.authorization,\n\t\t\t\t\tisRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(handshake.authorization),\n\t\t\t\t\tcloseStream: (reason) => closeIrohRemoteStream(stream, reason),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tauditLogger: this.services.auditLogger,\n\t\t\t\t\tadditionalRedactedPaths: sanitizerOverrides.additionalRedactedPaths,\n\t\t\t\t\tworktrees: this.createWorktreeRpcBackend(handshake.authorization.workspace),\n\t\t\t\t},\n\t\t\t);\n\t\t} finally {\n\t\t\tactiveStream.remove();\n\t\t}\n\t}\n\n\t/** Backend for the worktree RPC helpers, bound to the stream's authorized workspace. */\n\tprivate createWorktreeRpcBackend(workspace: IrohRemoteWorkspace): IrohRemoteWorktreeRpcBackend {\n\t\treturn {\n\t\t\tcreateWorktree: async (_workspaceName, options) => {\n\t\t\t\tconst created = await this.worktrees.create(workspace, options);\n\t\t\t\tif (!created.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terror: created.error,\n\t\t\t\t\t\t...(created.detail === undefined ? {} : { detail: created.detail }),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn { ok: true, worktree: created.worktree };\n\t\t\t},\n\t\t\tlistWorktrees: async () => ({ ok: true, worktrees: await this.worktrees.list(workspace) }),\n\t\t\tremoveWorktree: async (_workspaceName, worktreeId, force) =>\n\t\t\t\tthis.removeWorkspaceWorktree(workspace, worktreeId, force),\n\t\t};\n\t}\n\n\t/**\n\t * Runtime-aware worktree removal: refuses busy worktrees without force; with\n\t * force, closes bound phone streams and stops bound runtimes first.\n\t */\n\tprivate async removeWorkspaceWorktree(\n\t\tworkspace: IrohRemoteWorkspace,\n\t\tworktreeId: string,\n\t\tforce: boolean,\n\t): Promise<WorktreeResult<{ stoppedRuntimeCount: number; closedStreamCount: number }>> {\n\t\tconst record = await this.worktrees.findWorktree(workspace.name, worktreeId);\n\t\tif (!record) {\n\t\t\treturn { ok: false, error: \"worktree_not_found\" };\n\t\t}\n\t\tlet stoppedRuntimeCount = 0;\n\t\tlet closedStreamCount = 0;\n\t\tconst boundEntries = record.sessionIds\n\t\t\t.map((sessionId) => this.runtimes.findOwner(workspace.name, sessionId))\n\t\t\t.filter((entry): entry is IntegratedRuntimeEntry => entry !== undefined);\n\t\tif (boundEntries.length > 0) {\n\t\t\tif (!force) {\n\t\t\t\treturn { ok: false, error: \"worktree_busy\" };\n\t\t\t}\n\t\t\tfor (const entry of boundEntries) {\n\t\t\t\tclosedStreamCount += await this.closeActiveStreamsForConversationKey(\n\t\t\t\t\tworkspace.name,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\t\"worktree_removed\",\n\t\t\t\t);\n\t\t\t\tawait this.runtimes.stopEntry(entry, \"worktree_removed\");\n\t\t\t\tstoppedRuntimeCount++;\n\t\t\t}\n\t\t}\n\t\tconst removed = await this.worktrees.remove(workspace, worktreeId, { force });\n\t\tif (!removed.ok) {\n\t\t\treturn removed;\n\t\t}\n\t\treturn { ok: true, stoppedRuntimeCount, closedStreamCount };\n\t}\n\n\t/**\n\t * Worktree resolution for conversation opens: explicit worktreeId on \"new\"\n\t * (must exist AND be on disk), persisted binding on resume (missing checkout\n\t * fails with session_unavailable). Availability is an open-time failure, not\n\t * an authorization failure.\n\t */\n\tprivate async resolveConversationWorktree(\n\t\tworkspaceName: string,\n\t\thello: IrohRemoteHello,\n\t\ttargetSessionId: string | undefined,\n\t): Promise<IrohRemoteWorkspaceWorktree | undefined> {\n\t\tif (hello.mode !== \"conversation\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (hello.conversation.target === \"new\") {\n\t\t\tconst worktreeId = hello.conversation.worktreeId;\n\t\t\tif (worktreeId === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tconst worktree = await this.worktrees.findWorktree(workspaceName, worktreeId);\n\t\t\tif (!worktree || !existsSync(worktree.path)) {\n\t\t\t\tthrow createConversationOpenError(\"invalid_conversation_target\", \"unknown or unavailable worktree\", {\n\t\t\t\t\tworkspace: workspaceName,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn worktree;\n\t\t}\n\t\tif (targetSessionId === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst worktree = await this.worktrees.resolveSessionWorktree(workspaceName, targetSessionId);\n\t\tif (worktree === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (!existsSync(worktree.path)) {\n\t\t\tthrow createConversationOpenError(\"session_unavailable\", \"worktree checkout is unavailable\", {\n\t\t\t\tworkspace: workspaceName,\n\t\t\t\tsessionId: targetSessionId,\n\t\t\t});\n\t\t}\n\t\treturn worktree;\n\t}\n\n\tprivate async resolveConversationWorkingDirectory(options: {\n\t\tworkspace: IrohRemoteWorkspace;\n\t\trootPath: string;\n\t\tworkingDirectory?: string;\n\t\tworktree?: IrohRemoteWorkspaceWorktree;\n\t}): Promise<WorkspaceDirectoryResolution> {\n\t\tif (options.worktree === undefined) {\n\t\t\tconst parentDirectory = await this.worktrees.validateWorkingDirectory(\n\t\t\t\toptions.workspace,\n\t\t\t\toptions.workingDirectory,\n\t\t\t);\n\t\t\tif (!parentDirectory.ok) {\n\t\t\t\tconst message = parentDirectory.detail ?? parentDirectory.error;\n\t\t\t\tthrow createConversationOpenError(\"invalid_conversation_target\", message, {\n\t\t\t\t\tworkspace: options.workspace.name,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn parentDirectory.directory;\n\t\t}\n\t\tconst worktreeDirectory = await this.worktrees.resolveWorktreeWorkingDirectory(\n\t\t\toptions.workspace,\n\t\t\toptions.worktree,\n\t\t\toptions.workingDirectory,\n\t\t);\n\t\tif (!worktreeDirectory.ok) {\n\t\t\tthrow createConversationOpenError(\n\t\t\t\t\"invalid_conversation_target\",\n\t\t\t\tworktreeDirectory.detail ?? worktreeDirectory.error,\n\t\t\t\t{\n\t\t\t\t\tworkspace: options.workspace.name,\n\t\t\t\t\tworktreeId: options.worktree.id,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn worktreeDirectory.directory;\n\t}\n\n\t// ==========================================================================\n\t// Integrated conversation serving\n\t// ==========================================================================\n\n\tprivate createPushNotificationDispatcher(\n\t\tauthorization: IrohRemoteClientAuthorizationSuccess,\n\t): IrohRemotePushNotificationDispatcher {\n\t\treturn new IrohRemotePushNotificationDispatcher({\n\t\t\tauditLogger: this.services.auditLogger,\n\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\tdeduper: this.pushNotificationDeduper,\n\t\t\trelayClient: this.pushRelayClient,\n\t\t\tstateManager: this.stateManager,\n\t\t\tworkspace: authorization.workspace.name,\n\t\t});\n\t}\n\n\tprivate async revokeClientPushTargets(client: IrohRemoteClient | undefined): Promise<void> {\n\t\tif ((client?.pushTargets?.length ?? 0) === 0) return;\n\t\ttry {\n\t\t\tconst summary = await revokeIrohRemoteClientPushTargets(client, this.pushRelayClient);\n\t\t\tconst complete = summary.failed === 0 && summary.skipped === 0;\n\t\t\tif (!complete) {\n\t\t\t\tthis.log(\"warn\", \"remote push-target cleanup incomplete after client revoke\", { ...summary });\n\t\t\t}\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"push_targets_revoked\",\n\t\t\t\tclientNodeId: client?.nodeId,\n\t\t\t\tsuccess: complete,\n\t\t\t\terror: complete ? undefined : \"remote push-target cleanup incomplete; relay TTL remains the lifetime bound\",\n\t\t\t\tdetails: { ...summary, remainingLifetimeBound: \"relay_target_ttl\" },\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.log(\"warn\", \"remote push-target cleanup failed after client revoke\", { error: message });\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"push_targets_revoked\",\n\t\t\t\tclientNodeId: client?.nodeId,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"remote push-target cleanup failed; relay TTL remains the lifetime bound\",\n\t\t\t\tdetails: { remainingLifetimeBound: \"relay_target_ttl\" },\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async sendHandshakeError(stream: IrohBiStreamLike, error: unknown): Promise<void> {\n\t\tconst record = (error ?? {}) as Record<string, unknown>;\n\t\t// Plain {message, ...} records (abortPendingRelay, lease re-check) must not\n\t\t// stringify to \"[object Object]\".\n\t\tconst message =\n\t\t\terror instanceof Error ? error.message : typeof record.message === \"string\" ? record.message : String(error);\n\t\tconst outcome = typeof record.outcome === \"string\" ? record.outcome : undefined;\n\t\tconst workspace = typeof record.workspace === \"string\" ? record.workspace : undefined;\n\t\tconst sessionId = typeof record.sessionId === \"string\" ? record.sessionId : undefined;\n\t\tconst retryAfterMs = typeof record.retryAfterMs === \"number\" ? record.retryAfterMs : undefined;\n\t\tawait writeIrohRemoteHandshakeResponse(\n\t\t\tstream.send,\n\t\t\tcreateIrohRemoteHandshakeFailure(message, {\n\t\t\t\thostNodeId: this.hostNodeId,\n\t\t\t\t...(outcome === undefined ? {} : { outcome: outcome as never }),\n\t\t\t\t...(workspace === undefined ? {} : { workspace }),\n\t\t\t\t...(sessionId === undefined ? {} : { sessionId }),\n\t\t\t\t...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n\t\t\t}),\n\t\t);\n\t\tawait Promise.resolve(stream.send.finish?.()).catch(() => {});\n\t\tawait Promise.resolve(stream.recv.stop?.(0n)).catch(() => {});\n\t}\n\n\tprivate async rejectDuplicateActiveConnection(\n\t\tstream: IrohBiStreamLike,\n\t\tauthorization: IrohRemoteClientAuthorizationSuccess,\n\t\tsessionId: string,\n\t\tsource = \"active_stream_registry\",\n\t): Promise<void> {\n\t\tconst error = \"duplicate conversation connection\";\n\t\tawait this.logAudit({\n\t\t\ttype: \"duplicate_connection_rejected\",\n\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\tworkspace: authorization.workspace.name,\n\t\t\tsuccess: false,\n\t\t\terror,\n\t\t\tdetails: {\n\t\t\t\tretryAfterMs: DUPLICATE_CONVERSATION_RETRY_AFTER_MS,\n\t\t\t\tsessionId,\n\t\t\t\tsource,\n\t\t\t},\n\t\t});\n\t\tawait writeIrohRemoteHandshakeResponse(\n\t\t\tstream.send,\n\t\t\tcreateIrohRemoteHandshakeFailure(error, {\n\t\t\t\thostNodeId: this.hostNodeId,\n\t\t\t\toutcome: \"duplicate_conversation_connection\",\n\t\t\t\tworkspace: authorization.workspace.name,\n\t\t\t\tsessionId,\n\t\t\t\tretryAfterMs: DUPLICATE_CONVERSATION_RETRY_AFTER_MS,\n\t\t\t}),\n\t\t);\n\t\tawait Promise.resolve(stream.send.finish?.()).catch(() => {});\n\t\tawait Promise.resolve(stream.recv.stop?.(0n)).catch(() => {});\n\t}\n\n\tprivate async closeReplacedActiveStreams(\n\t\tauthorization: IrohRemoteClientAuthorizationSuccess,\n\t\treplacementStreamId: string,\n\t\treplacedEntries: IrohRemoteActiveStreamEntry[],\n\t): Promise<void> {\n\t\tif (replacedEntries.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst replacedStreamIds = replacedEntries.map((entry) => entry.streamId);\n\t\tfor (const entry of replacedEntries) {\n\t\t\tawait Promise.resolve(entry.close(ACTIVE_REPLACE_CLOSE_REASON)).catch(() => {});\n\t\t}\n\t\tawait this.closeIdleConnectionsForEntries(replacedEntries, ACTIVE_REPLACE_CLOSE_REASON);\n\t\tthis.log(\n\t\t\t\"info\",\n\t\t\t`client stream replaced: ${authorization.client.nodeId}/${authorization.workspace.name} (${replacedStreamIds.join(\", \")} -> ${replacementStreamId})`,\n\t\t);\n\t\tawait this.logAudit({\n\t\t\ttype: \"duplicate_connection_replaced\",\n\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\tworkspace: authorization.workspace.name,\n\t\t\tsuccess: true,\n\t\t\tdetails: {\n\t\t\t\tcloseReason: ACTIVE_REPLACE_CLOSE_REASON,\n\t\t\t\tclosedCount: replacedEntries.length,\n\t\t\t\treplacedStreamIds,\n\t\t\t\treplacementStreamId,\n\t\t\t\tsource: \"active_stream_registry\",\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Relay a phone conversation stream to the owning TUI (§5.6): the daemon\n\t * has already authenticated the phone; the TUI serves the framed RPC from\n\t * its in-process runtime over a dedicated relay unix connection.\n\t */\n\tprivate async relayConversationToTui(\n\t\tstream: IrohBiStreamLike,\n\t\thandshake: Extract<IrohRemoteHostHandshakeResult, { ok: true }>,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t\ttargetSessionId: string,\n\t\ttuiConnectionId: string,\n\t): Promise<void> {\n\t\tconst authorization = handshake.authorization;\n\t\tconst workspaceName = authorization.workspace.name;\n\n\t\t// Duplicate handling per clientNodeId + key: duplicates already on this\n\t\t// Iroh connection are real duplicates; entries on older connections are\n\t\t// stale for this conversation and may be replaced independently of any\n\t\t// sibling subagent streams that opened first on the new connection.\n\t\tconst liveRelays = this.relays.activeForConversation(authorization.client.nodeId, workspaceName, targetSessionId);\n\t\tconst pendingRelays = this.relays.pendingForConversation(\n\t\t\tauthorization.client.nodeId,\n\t\t\tworkspaceName,\n\t\t\ttargetSessionId,\n\t\t);\n\t\tif (\n\t\t\tliveRelays.some((relay) => relay.connectionId === connectionId) ||\n\t\t\tpendingRelays.some((pending) => pending.connectionId === connectionId)\n\t\t) {\n\t\t\tawait this.rejectDuplicateActiveConnection(stream, authorization, targetSessionId, \"relay_registry\");\n\t\t\treturn;\n\t\t}\n\t\tfor (const relay of liveRelays) {\n\t\t\trelay.close(\"error\");\n\t\t}\n\t\t// Unredeemed offers for the same conversation on older connections are\n\t\t// superseded by this one: fail their deferred handshakes and settle them\n\t\t// (relay_closed to the TUI, lease bookkeeping) instead of leaking tasks.\n\t\tfor (const pending of pendingRelays) {\n\t\t\tthis.abortPendingRelay(pending.relayId, \"error\", \"relay offer superseded; retry\", RELAY_OFFER_RETRY_AFTER_MS);\n\t\t}\n\n\t\t// Resolve the concrete session target for the preamble (§3.7).\n\t\tconst target: IrohRemoteSessionTarget =\n\t\t\thandshake.hello.mode === \"conversation\" && handshake.hello.conversation.target === \"session\"\n\t\t\t\t? { kind: \"session\", sessionId: handshake.hello.conversation.sessionId }\n\t\t\t\t: { kind: \"last\", resumeSessionId: targetSessionId };\n\t\t// A worktree-bound session must resolve against the worktree cwd (the\n\t\t// parent-keyed session dir plus a non-matching cwd makes SessionManager.list\n\t\t// filter by header cwd, restricting resolution to that worktree's sessions).\n\t\tconst boundWorktree = await this.stateManager.findWorktreeForSession(workspaceName, targetSessionId);\n\t\tconst relayOwnerCapabilities = this.services.controlServer\n\t\t\t.connections()\n\t\t\t.find((controlConnection) => controlConnection.connectionId === tuiConnectionId)?.capabilities;\n\t\tif (!relayOwnerCapabilities?.has(CONTROL_RPC_GRANTS_CAPABILITY)) {\n\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\tmessage: \"conversation owner is not grant-aware; retry\",\n\t\t\t\tretryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// Worktree-bound conversations are only relayed to TUIs that advertised the\n\t\t// worktrees control capability (an old TUI would sanitize with the parent\n\t\t// root and leak host paths), and never when the checkout has vanished.\n\t\tconst relayGate = evaluateWorktreeRelayGate(boundWorktree, relayOwnerCapabilities, CONTROL_WORKTREES_CAPABILITY);\n\t\tif (!relayGate.ok) {\n\t\t\tif (relayGate.reason === \"checkout_missing\") {\n\t\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\t\tmessage: \"worktree checkout is unavailable\",\n\t\t\t\t\toutcome: \"session_unavailable\",\n\t\t\t\t\tworkspace: workspaceName,\n\t\t\t\t\tsessionId: targetSessionId,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\tmessage: \"conversation owner cannot serve worktree sessions; retry\",\n\t\t\t\tretryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tlet resolvedTarget: Awaited<ReturnType<typeof resolveIrohRemoteSessionTarget>>;\n\t\ttry {\n\t\t\tresolvedTarget = await resolveIrohRemoteSessionTarget(\n\t\t\t\ttarget,\n\t\t\t\t{ name: workspaceName, path: authorization.workspace.path },\n\t\t\t\tcreateSessionManagerTargetStore(\n\t\t\t\t\tboundWorktree?.path ?? authorization.workspace.path,\n\t\t\t\t\tgetDefaultSessionDir(authorization.workspace.path, this.services.agentDir),\n\t\t\t\t\t{ listAll: true, preserveSessionCwd: true },\n\t\t\t\t),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tawait this.sendHandshakeError(stream, error);\n\t\t\treturn;\n\t\t}\n\t\tconst resolvedSessionManager = resolvedTarget.sessionManager as { getCwd?: () => string };\n\t\tconst resolvedSessionCwd =\n\t\t\tresolvedSessionManager.getCwd?.() ?? boundWorktree?.path ?? authorization.workspace.path;\n\t\tconst relayWorkingDirectoryRelativeToRoot = getRelativeWorkingDirectoryForRoot(\n\t\t\tboundWorktree?.path ?? authorization.workspace.path,\n\t\t\tresolvedSessionCwd,\n\t\t);\n\t\tif (relayWorkingDirectoryRelativeToRoot === null) {\n\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\tmessage: \"stored session working directory is outside the authorized workspace\",\n\t\t\t\toutcome: \"session_unavailable\",\n\t\t\t\tworkspace: workspaceName,\n\t\t\t\tsessionId: targetSessionId,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst relayWorkingDirectory =\n\t\t\tboundWorktree === undefined\n\t\t\t\t? relayWorkingDirectoryRelativeToRoot\n\t\t\t\t: getRegisteredWorkingDirectoryForWorktree(boundWorktree, relayWorkingDirectoryRelativeToRoot);\n\n\t\t// Session-target resolution awaited; the lease can have moved (release,\n\t\t// rekey, connection loss) in the meantime. Re-check before minting so the\n\t\t// offer cannot go to a stale or dead owner.\n\t\tconst lease = this.leaseBroker.lookup(workspaceName, targetSessionId);\n\t\tif (lease?.state !== \"tui-owned\" || lease.tuiConnectionId !== tuiConnectionId) {\n\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\tmessage: \"conversation lease owner changed; retry\",\n\t\t\t\tretryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\t// The target-resolution awaits above can race an access update or revoke.\n\t\t// Recheck immediately before the synchronous mint so stale authorization\n\t\t// cannot create a new pending offer after control-plane invalidation acks.\n\t\tif (!(await this.isAuthorizationGrantCurrent(authorization))) {\n\t\t\tawait this.sendHandshakeError(stream, { message: \"client access changed; reconnect\" });\n\t\t\treturn;\n\t\t}\n\n\t\t// A sibling stream can resolve/redeem while this stream awaits target\n\t\t// resolution. Re-check immediately before minting the offer.\n\t\tconst currentLiveRelays = this.relays.activeForConversation(\n\t\t\tauthorization.client.nodeId,\n\t\t\tworkspaceName,\n\t\t\ttargetSessionId,\n\t\t);\n\t\tconst currentPendingRelays = this.relays.pendingForConversation(\n\t\t\tauthorization.client.nodeId,\n\t\t\tworkspaceName,\n\t\t\ttargetSessionId,\n\t\t);\n\t\tif (\n\t\t\tcurrentLiveRelays.some((relay) => relay.connectionId === connectionId) ||\n\t\t\tcurrentPendingRelays.some((pending) => pending.connectionId === connectionId)\n\t\t) {\n\t\t\tawait this.rejectDuplicateActiveConnection(stream, authorization, targetSessionId, \"relay_registry\");\n\t\t\treturn;\n\t\t}\n\t\tfor (const relay of currentLiveRelays) {\n\t\t\trelay.close(\"error\");\n\t\t}\n\t\tfor (const pending of currentPendingRelays) {\n\t\t\tthis.abortPendingRelay(pending.relayId, \"error\", \"relay offer superseded; retry\", RELAY_OFFER_RETRY_AFTER_MS);\n\t\t}\n\n\t\tconst settled = new Promise<void>((resolveSettled) => {\n\t\t\tconst relay = this.relays.mint({\n\t\t\t\tworkspaceName,\n\t\t\t\tsessionId: targetSessionId,\n\t\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\t\tconnectionId,\n\t\t\t\townerControlConnectionId: tuiConnectionId,\n\t\t\t\tstreamId,\n\t\t\t\tstream,\n\t\t\t\tpreamble: {\n\t\t\t\t\thandshake: {\n\t\t\t\t\t\thello: handshake.hello,\n\t\t\t\t\t\tresponse: handshake.response,\n\t\t\t\t\t\tinitialInput: Array.from(handshake.initialInput),\n\t\t\t\t\t},\n\t\t\t\t\tauthorization: {\n\t\t\t\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\t\t\t\tallowedTools: authorization.client.allowedTools,\n\t\t\t\t\t\trpcGrant: authorization.client.rpcGrant,\n\t\t\t\t\t\tworkspaceName,\n\t\t\t\t\t\tworkspacePath: authorization.workspace.path,\n\t\t\t\t\t\t...(boundWorktree === undefined\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\tworktreeId: boundWorktree.id,\n\t\t\t\t\t\t\t\t\tworktreePath: boundWorktree.path,\n\t\t\t\t\t\t\t\t\t...(boundWorktree.sourceRootRelativePath === undefined\n\t\t\t\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t\t\t\t: { worktreeSourceRootRelativePath: boundWorktree.sourceRootRelativePath }),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\t// The phone verifies the saved host's node id in the handshake\n\t\t\t\t\t// response the TUI writes; without this the relay path fails the\n\t\t\t\t\t// client's identity check.\n\t\t\t\t\t...(this.hostNodeId === undefined ? {} : { hostNodeId: this.hostNodeId }),\n\t\t\t\t\trelayMode: this.relayMode,\n\t\t\t\t\t...(this.relayMode === \"production\" ? { relayUrls: this.relayUrls } : {}),\n\t\t\t\t\tconnectionId,\n\t\t\t\t\tstreamId,\n\t\t\t\t\tresolvedTarget: {\n\t\t\t\t\t\tsessionId: resolvedTarget.sessionId,\n\t\t\t\t\t\t...(resolvedTarget.sessionFilePath === undefined\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: { sessionFilePath: resolvedTarget.sessionFilePath }),\n\t\t\t\t\t\tselection: resolvedTarget.selection,\n\t\t\t\t\t\t...(resolvedTarget.requestedSessionId === undefined\n\t\t\t\t\t\t\t? {}\n\t\t\t\t\t\t\t: { requestedSessionId: resolvedTarget.requestedSessionId }),\n\t\t\t\t\t\tworkspaceName: resolvedTarget.workspaceName,\n\t\t\t\t\t\tworkspacePath: resolvedTarget.workspacePath,\n\t\t\t\t\t\t...(boundWorktree === undefined ? {} : { worktreeId: boundWorktree.id }),\n\t\t\t\t\t\t...(relayWorkingDirectory === undefined ? {} : { workingDirectory: relayWorkingDirectory }),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tsettle: (outcome) => {\n\t\t\t\t\tthis.leaseBroker.unregisterRelay(workspaceName, targetSessionId, relay.relayId);\n\t\t\t\t\tthis.services.controlServer.sendTo(tuiConnectionId, {\n\t\t\t\t\t\ttype: \"relay_closed\",\n\t\t\t\t\t\trelayId: relay.relayId,\n\t\t\t\t\t\treason: outcome.reason,\n\t\t\t\t\t});\n\t\t\t\t\tvoid this.logAudit({\n\t\t\t\t\t\ttype: \"relay_closed\",\n\t\t\t\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\t\t\t\tworkspace: workspaceName,\n\t\t\t\t\t\tsuccess: outcome.error === undefined,\n\t\t\t\t\t\terror: outcome.error,\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\trelayId: relay.relayId,\n\t\t\t\t\t\t\treason: outcome.reason,\n\t\t\t\t\t\t\tbytesUp: outcome.bytesUp,\n\t\t\t\t\t\t\tbytesDown: outcome.bytesDown,\n\t\t\t\t\t\t\tdurationMs: outcome.durationMs,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tresolveSettled();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\t// The offer is single-use with a 10s expiry; the phone's handshake\n\t\t\t// response is deferred until the TUI redeems the token.\n\t\t\tconst expiryTimer = setTimeout(() => {\n\t\t\t\tthis.abortPendingRelay(relay.relayId, \"error\", \"relay offer expired; retry\", RELAY_OFFER_RETRY_AFTER_MS);\n\t\t\t}, RELAY_TOKEN_TTL_MS);\n\t\t\texpiryTimer.unref?.();\n\n\t\t\tthis.leaseBroker.registerRelay(workspaceName, targetSessionId, relay.relayId);\n\t\t\tvoid this.logAudit({\n\t\t\t\ttype: \"relay_opened\",\n\t\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\t\tworkspace: workspaceName,\n\t\t\t\tsuccess: true,\n\t\t\t\tdetails: {\n\t\t\t\t\trelayId: relay.relayId,\n\t\t\t\t\tworkspaceName,\n\t\t\t\t\tsessionId: targetSessionId,\n\t\t\t\t\tconnectionId,\n\t\t\t\t\tstreamId,\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst delivered = this.services.controlServer.sendTo(tuiConnectionId, {\n\t\t\t\ttype: \"relay_offer\",\n\t\t\t\trelayId: relay.relayId,\n\t\t\t\trelayToken: relay.relayToken,\n\t\t\t\tworkspaceName,\n\t\t\t\tsessionId: targetSessionId,\n\t\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\t\tconnectionId,\n\t\t\t\tstreamId,\n\t\t\t});\n\t\t\tif (!delivered) {\n\t\t\t\t// The TUI connection vanished between the lease check and the offer;\n\t\t\t\t// fail the phone's deferred handshake now instead of after the TTL.\n\t\t\t\tthis.abortPendingRelay(\n\t\t\t\t\trelay.relayId,\n\t\t\t\t\t\"error\",\n\t\t\t\t\t\"relay offer undeliverable; retry\",\n\t\t\t\t\tRELAY_OFFER_RETRY_AFTER_MS,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tawait settled;\n\t}\n\n\tprivate async runIntegratedConversation(\n\t\tstream: IrohBiStreamLike,\n\t\thandshake: Extract<IrohRemoteHostHandshakeResult, { ok: true }>,\n\t\tconnection: IrohConnectionLike,\n\t\tconnectionId: string,\n\t\tstreamId: string,\n\t): Promise<void> {\n\t\tconst authorization = handshake.authorization;\n\t\tconst targetSessionId = getResolvedTargetSessionId(handshake.hello, authorization);\n\t\tconst daemonAttach = this.leaseBroker.beginDaemonAttach(authorization.workspace.name, targetSessionId);\n\t\tif (daemonAttach.kind === \"relay\") {\n\t\t\tif (!targetSessionId) {\n\t\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\t\tmessage: \"conversation lease owner changed; retry\",\n\t\t\t\t\tretryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.relayConversationToTui(\n\t\t\t\tstream,\n\t\t\t\thandshake,\n\t\t\t\tconnectionId,\n\t\t\t\tstreamId,\n\t\t\t\ttargetSessionId,\n\t\t\t\tdaemonAttach.tuiConnectionId,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (daemonAttach.kind === \"retry\") {\n\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\tmessage: \"conversation lease is draining; retry\",\n\t\t\t\tretryAfterMs: daemonAttach.retryAfterMs,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst daemonAttachClaim: DaemonAttachClaim = daemonAttach.claim;\n\t\tlet entry: IntegratedRuntimeEntry;\n\t\tlet sessionSelection: Awaited<ReturnType<IntegratedRuntimeRegistry[\"getOrCreateEntry\"]>>[\"sessionSelection\"];\n\t\tlet createdRuntime = false;\n\t\ttry {\n\t\t\t({\n\t\t\t\tentry,\n\t\t\t\tsessionSelection,\n\t\t\t\tcreated: createdRuntime,\n\t\t\t} = await this.runtimes.getOrCreateEntry(\n\t\t\t\t{ hello: handshake.hello, response: handshake.response },\n\t\t\t\tauthorization,\n\t\t\t));\n\t\t} catch (error) {\n\t\t\tthis.leaseBroker.abortDaemonAttach(daemonAttachClaim);\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"runtime_failure\",\n\t\t\t\tclientNodeId: authorization.client.nodeId,\n\t\t\t\tworkspace: authorization.workspace.name,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\tdetails: { runtime: \"integrated-volt\" },\n\t\t\t});\n\t\t\tawait this.sendHandshakeError(stream, error);\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tthis.activeStreams.hasConversationOnConnection(\n\t\t\t\tauthorization.client.nodeId,\n\t\t\t\tauthorization.workspace.name,\n\t\t\t\tentry.sessionId,\n\t\t\t\tconnectionId,\n\t\t\t)\n\t\t) {\n\t\t\tthis.leaseBroker.abortDaemonAttach(daemonAttachClaim);\n\t\t\tif (createdRuntime) {\n\t\t\t\tawait this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);\n\t\t\t} else {\n\t\t\t\t// Reattach: getOrCreateEntry cancelled the detached-runtime retention\n\t\t\t\t// timer up front. Re-arm it (no-op unless the entry is still detached\n\t\t\t\t// with no timer) so aborting here never leaves the runtime unswept.\n\t\t\t\tawait this.runtimes.detachWithoutSubscriber(entry, \"reattach_superseded\");\n\t\t\t}\n\t\t\tawait this.rejectDuplicateActiveConnection(stream, authorization, entry.sessionId);\n\t\t\treturn;\n\t\t}\n\n\t\tlet activeStream: { entry: IrohRemoteActiveStreamEntry; remove: () => void } | undefined;\n\t\tlet subscriber: IntegratedRuntimeSubscriber | undefined;\n\t\tlet subscriberError: unknown;\n\t\tlet handshakeCommitted = false;\n\t\ttry {\n\t\t\tconst brokerCommit = this.leaseBroker.commitDaemonRuntime(\n\t\t\t\tdaemonAttachClaim,\n\t\t\t\tauthorization.workspace.name,\n\t\t\t\tentry.sessionId,\n\t\t\t);\n\t\t\tif (!brokerCommit.ok) {\n\t\t\t\tif (createdRuntime) {\n\t\t\t\t\tawait this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\tbrokerCommit.reason === \"tui_owned\" &&\n\t\t\t\t\tbrokerCommit.tuiConnectionId &&\n\t\t\t\t\ttargetSessionId === entry.sessionId\n\t\t\t\t) {\n\t\t\t\t\tawait this.relayConversationToTui(\n\t\t\t\t\t\tstream,\n\t\t\t\t\t\thandshake,\n\t\t\t\t\t\tconnectionId,\n\t\t\t\t\t\tstreamId,\n\t\t\t\t\t\tentry.sessionId,\n\t\t\t\t\t\tbrokerCommit.tuiConnectionId,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait this.sendHandshakeError(stream, {\n\t\t\t\t\tmessage: \"conversation lease owner changed; retry\",\n\t\t\t\t\tretryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait this.runtimes.commitEntry(entry, sessionSelection, authorization);\n\t\t\t} catch (error) {\n\t\t\t\tthis.leaseBroker.rollbackDaemonRuntimeCommit(\n\t\t\t\t\tauthorization.workspace.name,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\tbrokerCommit.previousState,\n\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tthis.activeStreams.hasConversationOnConnection(\n\t\t\t\t\tauthorization.client.nodeId,\n\t\t\t\t\tauthorization.workspace.name,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\tconnectionId,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tthis.leaseBroker.rollbackDaemonRuntimeCommit(\n\t\t\t\t\tauthorization.workspace.name,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\tbrokerCommit.previousState,\n\t\t\t\t);\n\t\t\t\tif (createdRuntime) {\n\t\t\t\t\tawait this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);\n\t\t\t\t} else {\n\t\t\t\t\tawait this.runtimes.detachWithoutSubscriber(entry, \"reattach_superseded\");\n\t\t\t\t}\n\t\t\t\tawait this.rejectDuplicateActiveConnection(stream, authorization, entry.sessionId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thandshakeCommitted = true;\n\t\t\t// Worktree-bound conversations sanitize with the worktree checkout as the\n\t\t\t// root; the parent checkout and the worktrees root must ALSO redact (bash\n\t\t\t// output like `git worktree list` prints both).\n\t\t\tconst worktreeSanitizerOverrides: RemoteSanitizerOverrides | undefined =\n\t\t\t\tentry.worktreePath === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tremoteWorkspacePath:\n\t\t\t\t\t\t\t\tentry.worktreeSourceRootRelativePath === undefined\n\t\t\t\t\t\t\t\t\t? \"/workspace\"\n\t\t\t\t\t\t\t\t\t: `/workspace/${entry.worktreeSourceRootRelativePath}`,\n\t\t\t\t\t\t\tworkspacePath: entry.worktreePath,\n\t\t\t\t\t\t\tadditionalRedactedPaths: [authorization.workspace.path, getWorktreesRoot(this.services.agentDir)],\n\t\t\t\t\t\t};\n\t\t\tif (!(await this.isAuthorizationGrantCurrent(authorization))) {\n\t\t\t\tawait this.runtimes.stopEntry(entry, \"access_updated_during_attach\");\n\t\t\t\tthrow new Error(\"client access changed during conversation attach; reconnect\");\n\t\t\t}\n\t\t\tconst replacedEntries = this.activeStreams.takeEntriesForConversationOnOtherConnections(\n\t\t\t\tauthorization.client.nodeId,\n\t\t\t\tauthorization.workspace.name,\n\t\t\t\tentry.sessionId,\n\t\t\t\tconnectionId,\n\t\t\t);\n\t\t\tactiveStream = this.registerActiveStream(\n\t\t\t\tauthorization,\n\t\t\t\tentry.sessionId,\n\t\t\t\tstream,\n\t\t\t\tconnection,\n\t\t\t\tconnectionId,\n\t\t\t\tstreamId,\n\t\t\t\tworktreeSanitizerOverrides === undefined ? {} : { sanitizerOverrides: worktreeSanitizerOverrides },\n\t\t\t);\n\t\t\tawait this.closeReplacedActiveStreams(authorization, streamId, replacedEntries);\n\t\t\tawait writeIrohRemoteHandshakeResponse(\n\t\t\t\tstream.send,\n\t\t\t\tcreateIntegratedConversationHandshakeResponse(\n\t\t\t\t\t{ hello: handshake.hello, response: handshake.response },\n\t\t\t\t\tauthorization,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\tsessionSelection,\n\t\t\t\t\tthis.getResponseContext(),\n\t\t\t\t\tentry.worktreeId,\n\t\t\t\t\tentry.workingDirectory,\n\t\t\t\t),\n\t\t\t);\n\t\t\tsubscriber = await this.runtimes.attachSubscriber(entry);\n\t\t\tthis.leaseBroker.onDaemonRuntimeStreamCountChanged(\n\t\t\t\tauthorization.workspace.name,\n\t\t\t\tentry.sessionId,\n\t\t\t\tentry.subscribers.size,\n\t\t\t);\n\t\t\tawait this.runtimes.replayWorkflowEvents(activeStream.entry, entry);\n\t\t\tconst pushDispatcher = this.createPushNotificationDispatcher(authorization);\n\t\t\tawait runIrohRemoteRpcMode(entry.runtime, {\n\t\t\t\trpcGrant: authorization.client.rpcGrant,\n\t\t\t\tisRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(authorization),\n\t\t\t\tdecorateOutbound: (value) => decorateRemoteHostState(value, authorization, this.getResponseContext()),\n\t\t\t\tdisposeRuntimeOnClose: false,\n\t\t\t\tnotificationDelivery: pushDispatcher,\n\t\t\t\tonClientCapabilitiesChanged: (features) => {\n\t\t\t\t\tconst streamEntry = activeStream?.entry;\n\t\t\t\t\tif (streamEntry) {\n\t\t\t\t\t\tstreamEntry.capabilities = new Set(features);\n\t\t\t\t\t\tthis.pushThemeTokensToStream(streamEntry);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonSessionChanged: async (session) => {\n\t\t\t\t\tawait this.runtimes.handleSessionChanged(entry, activeStream?.entry, session, authorization);\n\t\t\t\t},\n\t\t\t\tonWorkflowEvent: async (event) => {\n\t\t\t\t\tawait this.runtimes.handleWorkflowEvent(\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\tevent as unknown as Record<string, unknown>,\n\t\t\t\t\t\tactiveStream?.entry,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tregisterPushTarget: (args) => pushDispatcher.registerPushTarget(args),\n\t\t\t\tremoteCommandHandler: (command) =>\n\t\t\t\t\thandleIntegratedConversationRpcCommand(\n\t\t\t\t\t\tcommand as { type: string } & Record<string, unknown>,\n\t\t\t\t\t\tauthorization,\n\t\t\t\t\t\tthis.getCommandContext({\n\t\t\t\t\t\t\tworkspaceName: authorization.workspace.name,\n\t\t\t\t\t\t\tworkspacePath: authorization.workspace.path,\n\t\t\t\t\t\t\tentry,\n\t\t\t\t\t\t\tstreamEntry: activeStream?.entry,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tentry.runtime,\n\t\t\t\t\t),\n\t\t\t\tstream,\n\t\t\t\tinitialInput: handshake.initialInput,\n\t\t\t\tworkspaceName: authorization.workspace.name,\n\t\t\t\tworkspacePath: entry.worktreePath ?? authorization.workspace.path,\n\t\t\t\t...(worktreeSanitizerOverrides?.remoteWorkspacePath === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { remoteWorkspacePath: worktreeSanitizerOverrides.remoteWorkspacePath }),\n\t\t\t\t...(worktreeSanitizerOverrides?.additionalRedactedPaths === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { additionalRedactedPaths: worktreeSanitizerOverrides.additionalRedactedPaths }),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tsubscriberError = error;\n\t\t\tif (!handshakeCommitted) {\n\t\t\t\tif (createdRuntime) {\n\t\t\t\t\tawait this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);\n\t\t\t\t}\n\t\t\t\tawait this.sendHandshakeError(stream, error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} finally {\n\t\t\tif (subscriber) {\n\t\t\t\tawait this.runtimes.detachSubscriber(\n\t\t\t\t\tentry,\n\t\t\t\t\tsubscriber,\n\t\t\t\t\tsubscriberError ? \"transport_error\" : \"transport_closed\",\n\t\t\t\t\tsubscriberError,\n\t\t\t\t);\n\t\t\t\tthis.leaseBroker.onDaemonRuntimeStreamCountChanged(\n\t\t\t\t\tauthorization.workspace.name,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\tentry.subscribers.size,\n\t\t\t\t);\n\t\t\t} else if (handshakeCommitted || !createdRuntime) {\n\t\t\t\t// handshakeCommitted: normal detach after the runtime ran. !createdRuntime:\n\t\t\t\t// a reattach that failed before attachSubscriber, whose retention timer\n\t\t\t\t// getOrCreateEntry cancelled up front — re-arm it so the runtime is still\n\t\t\t\t// swept at TTL instead of leaking forever. detachWithoutSubscriber no-ops\n\t\t\t\t// when the entry was replaced or still has other subscribers.\n\t\t\t\tawait this.runtimes.detachWithoutSubscriber(\n\t\t\t\t\tentry,\n\t\t\t\t\tsubscriberError ? \"transport_error\" : \"transport_closed\",\n\t\t\t\t);\n\t\t\t\t// Sync the lease's stream count to reality. Without this, a handshake\n\t\t\t\t// write that failed after commitDaemonRuntime but before attachSubscriber\n\t\t\t\t// leaves the lease stuck at daemon-active with no live stream until the\n\t\t\t\t// detached-runtime retention TTL expires.\n\t\t\t\tthis.leaseBroker.onDaemonRuntimeStreamCountChanged(\n\t\t\t\t\tauthorization.workspace.name,\n\t\t\t\t\tentry.sessionId,\n\t\t\t\t\tentry.subscribers.size,\n\t\t\t\t);\n\t\t\t}\n\t\t\tactiveStream?.remove();\n\t\t}\n\t}\n\n\t// ==========================================================================\n\t// Stream/connection registries\n\t// ==========================================================================\n\n\tprivate registerClientConnection(nodeId: string, connection: IrohConnectionLike, connectionId: string): () => void {\n\t\tconst record: ClientConnectionRecord = {\n\t\t\tconnectionId,\n\t\t\tclose: (reason: string) => closeConnection(connection, reason),\n\t\t};\n\t\tlet records = this.clientConnections.get(nodeId);\n\t\tif (!records) {\n\t\t\trecords = new Set();\n\t\t\tthis.clientConnections.set(nodeId, records);\n\t\t}\n\t\trecords.add(record);\n\t\tlet removed = false;\n\t\treturn () => {\n\t\t\tif (removed) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tremoved = true;\n\t\t\trecords.delete(record);\n\t\t\tif (records.size === 0 && this.clientConnections.get(nodeId) === records) {\n\t\t\t\tthis.clientConnections.delete(nodeId);\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate closeClientConnectionsForClient(nodeId: string, reason: string): number {\n\t\tconst records = Array.from(this.clientConnections.get(nodeId) ?? []);\n\t\tif (records.length === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tthis.clientConnections.delete(nodeId);\n\t\tfor (const record of records) {\n\t\t\ttry {\n\t\t\t\trecord.close(reason);\n\t\t\t} catch {\n\t\t\t\t// Connection closure is best-effort; the transport may already be closing.\n\t\t\t}\n\t\t}\n\t\treturn records.length;\n\t}\n\n\tprivate async closeEntryConnection(entry: IrohRemoteActiveStreamEntry, reason: string): Promise<void> {\n\t\ttry {\n\t\t\tawait Promise.resolve(entry.closeConnection?.(reason));\n\t\t} catch {\n\t\t\t// Connection closure is best-effort. Stream teardown still drives task cleanup.\n\t\t}\n\t}\n\n\tprivate async closeIdleConnectionsForEntries(entries: IrohRemoteActiveStreamEntry[], reason: string): Promise<void> {\n\t\tconst closedConnectionIds = new Set<string>();\n\t\tfor (const entry of entries) {\n\t\t\tif (closedConnectionIds.has(entry.connectionId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (this.activeStreams.entriesForConnection(entry.connectionId).length > 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tclosedConnectionIds.add(entry.connectionId);\n\t\t\tawait this.closeEntryConnection(entry, reason);\n\t\t}\n\t}\n\n\tprivate async closeActiveStreamsForConnection(connectionId: string, reason: string): Promise<void> {\n\t\tconst entries = this.activeStreams.entriesForConnection(connectionId);\n\t\tfor (const entry of entries) {\n\t\t\tthis.activeStreams.unregister(entry);\n\t\t\tawait Promise.resolve(entry.close(reason)).catch(() => {});\n\t\t}\n\t}\n\n\tprivate async closeActiveStreamsForConversationKey(\n\t\tworkspaceName: string,\n\t\tsessionId: string,\n\t\treason: string,\n\t): Promise<number> {\n\t\tconst entries = this.activeStreams.entriesForConversationKey(workspaceName, sessionId);\n\t\tfor (const entry of entries) {\n\t\t\tthis.activeStreams.unregister(entry);\n\t\t\tawait Promise.resolve(entry.close(reason)).catch(() => {});\n\t\t}\n\t\tawait this.closeIdleConnectionsForEntries(entries, reason);\n\t\treturn entries.length;\n\t}\n\n\tprivate async closeActiveStreamsForWorkspace(\n\t\tworkspaceName: string,\n\t\treason: string,\n\t\texcludedEntry?: IrohRemoteActiveStreamEntry,\n\t): Promise<number> {\n\t\tconst entries = this.activeStreams\n\t\t\t.entriesForWorkspaceName(workspaceName)\n\t\t\t.filter((entry) => entry !== excludedEntry);\n\t\tif (entries.length === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tthis.activeStreams.unregister(entry);\n\t\t\tawait Promise.resolve(entry.close(reason)).catch(() => {});\n\t\t}\n\t\tawait this.closeIdleConnectionsForEntries(entries, reason);\n\t\treturn entries.length;\n\t}\n\n\t/**\n\t * Invalidate an unredeemed relay offer: fail the phone's deferred handshake\n\t * immediately and settle the relay (lease bookkeeping, relay_closed to the\n\t * TUI, audit).\n\t */\n\tprivate abortPendingRelay(relayId: string, reason: RelayCloseReason, message: string, retryAfterMs?: number): void {\n\t\tconst pending = this.relays.invalidatePending(relayId);\n\t\tif (!pending) {\n\t\t\treturn;\n\t\t}\n\t\tvoid this.sendHandshakeError(pending.stream, {\n\t\t\tmessage,\n\t\t\t...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n\t\t}).finally(() => pending.settle({ reason, bytesUp: 0, bytesDown: 0, durationMs: 0 }));\n\t}\n\n\tprivate closeRelaysForWorkspace(workspaceName: string, excludeRelayIds?: ReadonlySet<string>): void {\n\t\tfor (const relay of this.relays.activeRelays()) {\n\t\t\tif (relay.workspaceName === workspaceName && !excludeRelayIds?.has(relay.relayId)) {\n\t\t\t\trelay.close(\"workspace_unregistered\");\n\t\t\t}\n\t\t}\n\t\tfor (const pending of this.relays.pendingRelays()) {\n\t\t\tif (pending.workspaceName === workspaceName && !excludeRelayIds?.has(pending.relayId)) {\n\t\t\t\tthis.abortPendingRelay(pending.relayId, \"workspace_unregistered\", \"workspace unregistered\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Post-unregister host cleanup shared by the control, workspace-management,\n\t * and conversation RPC unregister paths: closes phone streams, stops\n\t * runtimes, drops live activities, and closes TUI relays for the workspace.\n\t * Exclusions keep the requesting stream/runtime/relays alive so the\n\t * unregister response can still be delivered.\n\t */\n\tprivate async cleanupUnregisteredWorkspace(\n\t\tworkspaceName: string,\n\t\texclusions: {\n\t\t\tstreamEntry?: IrohRemoteActiveStreamEntry;\n\t\t\truntimeEntry?: IntegratedRuntimeEntry;\n\t\t\trelayIds?: ReadonlySet<string>;\n\t\t\t/** Enables a non-destructive audit of preserved checkout directories. */\n\t\t\tworkspacePath?: string;\n\t\t} = {},\n\t): Promise<{ closedStreamCount: number; stoppedRuntimeCount: number }> {\n\t\tconst closedStreamCount = await this.closeActiveStreamsForWorkspace(\n\t\t\tworkspaceName,\n\t\t\tWORKSPACE_UNREGISTERED_CLOSE_REASON,\n\t\t\texclusions.streamEntry,\n\t\t);\n\t\tconst stoppedRuntimeCount = await this.runtimes.stopForWorkspace(\n\t\t\tworkspaceName,\n\t\t\tWORKSPACE_UNREGISTERED_CLOSE_REASON,\n\t\t\texclusions.runtimeEntry,\n\t\t);\n\t\tawait this.stateManager.removeLiveActivitiesForWorkspace(workspaceName);\n\t\tthis.closeRelaysForWorkspace(workspaceName, exclusions.relayIds);\n\t\tif (exclusions.workspacePath !== undefined) {\n\t\t\tawait this.worktrees\n\t\t\t\t.cleanupUnregisteredWorkspace({ name: workspaceName, path: exclusions.workspacePath })\n\t\t\t\t.catch(() => {});\n\t\t}\n\t\treturn { closedStreamCount, stoppedRuntimeCount };\n\t}\n\n\tprivate async closeActiveStreamsForClientWorkspace(\n\t\tnodeId: string,\n\t\tworkspaceName: string,\n\t\treason: string,\n\t): Promise<number> {\n\t\tconst entries = this.activeStreams\n\t\t\t.entriesForClientNodeId(nodeId)\n\t\t\t.filter((entry) => entry.workspaceName === workspaceName);\n\t\tif (entries.length === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tthis.activeStreams.unregister(entry);\n\t\t\tawait Promise.resolve(entry.close(reason)).catch(() => {});\n\t\t}\n\t\tawait this.closeIdleConnectionsForEntries(entries, reason);\n\t\treturn entries.length;\n\t}\n\n\tprivate async closeClientForAccessUpdate(nodeId: string): Promise<void> {\n\t\tconst runtimeEntries = collectClientAuthorityInvalidationRuntimes(\n\t\t\tthis.activeStreams,\n\t\t\tthis.runtimes.values(),\n\t\t\tnodeId,\n\t\t);\n\t\tconst entries = collectClientAuthorityInvalidationStreams(this.activeStreams, runtimeEntries, nodeId);\n\t\tfor (const entry of entries) {\n\t\t\tthis.activeStreams.unregister(entry);\n\t\t}\n\t\t// Invalidate transport and relay authority synchronously. Terminal writes\n\t\t// below are best-effort and must never keep old commands or buffered prompts\n\t\t// alive behind backpressure.\n\t\tthis.closeClientConnectionsForClient(nodeId, \"access_updated\");\n\t\tfor (const entry of entries) {\n\t\t\ttry {\n\t\t\t\tentry.closeConnection?.(\"access_updated\");\n\t\t\t} catch {}\n\t\t}\n\t\tfor (const relay of this.relays.activeRelays()) {\n\t\t\tif (relay.clientNodeId === nodeId) relay.close(\"error\");\n\t\t}\n\t\tfor (const pending of this.relays.pendingRelays()) {\n\t\t\tif (pending.clientNodeId === nodeId) {\n\t\t\t\tthis.abortPendingRelay(pending.relayId, \"error\", \"client access updated; reconnect\");\n\t\t\t}\n\t\t}\n\t\tconst streamClosures = Promise.allSettled(\n\t\t\tArray.from(entries, (entry) => Promise.resolve(entry.close(\"access_updated\"))),\n\t\t);\n\t\tawait Promise.allSettled(\n\t\t\tArray.from(runtimeEntries, (runtimeEntry) => this.runtimes.stopEntry(runtimeEntry, \"access_updated\")),\n\t\t);\n\t\tawait streamClosures;\n\t}\n\n\tprivate async closeWorkspaceAuthorizationRemovedStreams(nodeId: string, workspaceName: string): Promise<void> {\n\t\tconst reason = \"workspace_authorization_removed\";\n\t\tconst closedStreamCount = await this.closeActiveStreamsForClientWorkspace(nodeId, workspaceName, reason);\n\t\tconst stoppedRuntimeCount = await this.runtimes.stopForClientWorkspace(nodeId, workspaceName, reason);\n\t\tconst removedLiveActivityCount = await this.stateManager.removeClientLiveActivitiesForWorkspace(\n\t\t\tnodeId,\n\t\t\tworkspaceName,\n\t\t);\n\t\tawait this.logAudit({\n\t\t\ttype: \"workspace_authorization_removed\",\n\t\t\tclientNodeId: nodeId,\n\t\t\tworkspace: workspaceName,\n\t\t\tsuccess: closedStreamCount > 0 || stoppedRuntimeCount > 0 || removedLiveActivityCount > 0,\n\t\t\tdetails: {\n\t\t\t\tclosedStreamCount,\n\t\t\t\tremovedLiveActivityCount,\n\t\t\t\tsource: \"authorization_failure\",\n\t\t\t\tstoppedRuntimeCount,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync closeActiveStreamsForClient(nodeId: string): Promise<{ closed: boolean; closedCount: number }> {\n\t\tconst runtimeEntries = collectClientAuthorityInvalidationRuntimes(\n\t\t\tthis.activeStreams,\n\t\t\tthis.runtimes.values(),\n\t\t\tnodeId,\n\t\t);\n\t\tconst entries = collectClientAuthorityInvalidationStreams(this.activeStreams, runtimeEntries, nodeId);\n\t\tfor (const entry of entries) {\n\t\t\tthis.activeStreams.unregister(entry);\n\t\t}\n\n\t\t// Match access-update ordering: synchronously make active and unredeemed TUI\n\t\t// relays unusable before any terminal write, runtime disposal, or control ack.\n\t\tconst activeRelays = this.relays.activeRelays().filter((relay) => relay.clientNodeId === nodeId);\n\t\tconst pendingRelays = this.relays.pendingRelays().filter((relay) => relay.clientNodeId === nodeId);\n\t\tfor (const relay of activeRelays) {\n\t\t\trelay.close(\"error\");\n\t\t}\n\t\tfor (const pending of pendingRelays) {\n\t\t\tthis.abortPendingRelay(pending.relayId, \"error\", \"client revoked; re-pair required\");\n\t\t}\n\n\t\tconst closedConnectionCount = this.closeClientConnectionsForClient(nodeId, ACTIVE_REVOKE_CLOSE_REASON);\n\t\tfor (const entry of entries) {\n\t\t\ttry {\n\t\t\t\tentry.closeConnection?.(ACTIVE_REVOKE_CLOSE_REASON);\n\t\t\t} catch {}\n\t\t}\n\t\tconst streamClosures = Promise.allSettled(\n\t\t\tArray.from(entries, (entry) => Promise.resolve(entry.close(ACTIVE_REVOKE_CLOSE_REASON))),\n\t\t);\n\t\tawait Promise.allSettled(\n\t\t\tArray.from(runtimeEntries, (runtimeEntry) => this.runtimes.stopEntry(runtimeEntry, \"client_revoked\")),\n\t\t);\n\t\tawait streamClosures;\n\t\tconst stoppedRuntimeCount = runtimeEntries.size;\n\t\tconst closed =\n\t\t\tentries.size > 0 || closedConnectionCount > 0 || activeRelays.length > 0 || pendingRelays.length > 0;\n\t\tif (entries.size === 0) {\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"active_connection_revoked\",\n\t\t\t\tclientNodeId: nodeId,\n\t\t\t\tsuccess: closed || stoppedRuntimeCount > 0,\n\t\t\t\terror: closed || stoppedRuntimeCount > 0 ? undefined : \"no active connection found\",\n\t\t\t\tdetails: {\n\t\t\t\t\tactiveRelayCount: activeRelays.length,\n\t\t\t\t\tcloseReason: ACTIVE_REVOKE_CLOSE_REASON,\n\t\t\t\t\tclosedConnectionCount,\n\t\t\t\t\tpendingRelayCount: pendingRelays.length,\n\t\t\t\t\tsource: \"control_channel\",\n\t\t\t\t\tstoppedRuntimeCount,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn { closed, closedCount: closedConnectionCount + activeRelays.length + pendingRelays.length };\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tawait this.logAudit({\n\t\t\t\ttype: \"active_connection_revoked\",\n\t\t\t\tclientNodeId: nodeId,\n\t\t\t\tworkspace: entry.workspaceName,\n\t\t\t\tsuccess: true,\n\t\t\t\tdetails: {\n\t\t\t\t\tactiveRelayCount: activeRelays.length,\n\t\t\t\t\tcloseReason: ACTIVE_REVOKE_CLOSE_REASON,\n\t\t\t\t\tclosedConnectionCount,\n\t\t\t\t\tpendingRelayCount: pendingRelays.length,\n\t\t\t\t\tsource: \"control_channel\",\n\t\t\t\t\tstreamId: entry.streamId,\n\t\t\t\t\tstoppedRuntimeCount,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t\treturn { closed: true, closedCount: entries.size + activeRelays.length + pendingRelays.length };\n\t}\n\n\t// ==========================================================================\n\t// Pairing over the control plane\n\t// ==========================================================================\n\n\tprivate notifyPairingConsumed(\n\t\thandshake: { ok: true; authorization: IrohRemoteClientAuthorizationSuccess },\n\t\tremoteId: string,\n\t): void {\n\t\tconst consumed = handshake.authorization.consumedPairingTicket;\n\t\tif (!consumed) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const [requestId, pending] of this.pendingPairRequests) {\n\t\t\tif (pending.secretHash !== consumed.secretHash) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tclearTimeout(pending.timer);\n\t\t\tthis.pendingPairRequests.delete(requestId);\n\t\t\tthis.services.controlServer.sendTo(pending.connectionId, {\n\t\t\t\ttype: \"pairing_progress\",\n\t\t\t\trequestId,\n\t\t\t\tphase: \"completed\",\n\t\t\t\tclientNodeId: remoteId,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async handlePairRequest(\n\t\tconnection: ControlConnection,\n\t\trequest: ControlRequest & { type: \"pair_request\" },\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait withTimeout(\n\t\t\t\tthis.ready.promise,\n\t\t\t\tIROH_ENDPOINT_READY_TIMEOUT_MS,\n\t\t\t\t\"Iroh endpoint did not become ready within 15s\",\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconnection.send({\n\t\t\t\ttype: \"error\",\n\t\t\t\tid: request.id,\n\t\t\t\tcode: \"iroh_unavailable\",\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst engine = this.requireEngine();\n\t\tconst endpoint = this.endpoint;\n\t\tif (!endpoint || !this.endpointTicket) {\n\t\t\tconnection.send({ type: \"error\", id: request.id, code: \"iroh_unavailable\", message: \"endpoint not ready\" });\n\t\t\treturn;\n\t\t}\n\t\tconst workspaceName =\n\t\t\ttypeof (request as Record<string, unknown>).workspaceName === \"string\"\n\t\t\t\t? ((request as Record<string, unknown>).workspaceName as string)\n\t\t\t\t: undefined;\n\t\tconst requestId = randomUUID();\n\t\ttry {\n\t\t\tconst access =\n\t\t\t\trequest.access !== undefined\n\t\t\t\t\t? createIrohRemotePresetAccess(request.access)\n\t\t\t\t\t: request.allowedTools !== undefined && request.rpcCapabilities !== undefined\n\t\t\t\t\t\t? createIrohRemoteExplicitAccess(\n\t\t\t\t\t\t\t\trequest.allowedTools,\n\t\t\t\t\t\t\t\tparseIrohRemoteRpcCapabilities(request.rpcCapabilities),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: createIrohRemotePresetAccess(\"coding\");\n\t\t\tconst pairing = await engine.pair({\n\t\t\t\tallowTools: access.allowedTools,\n\t\t\t\trpcGrant: access.rpcGrant,\n\t\t\t\tirohTicket: this.endpointTicket,\n\t\t\t\tnodeId: this.hostNodeId,\n\t\t\t\trelayMode: this.relayMode,\n\t\t\t\t...(this.relayMode === \"production\" ? { relayUrls: this.relayUrls } : {}),\n\t\t\t\t...(this.relayMode === \"production\" && this.relayAuthToken !== undefined\n\t\t\t\t\t? { relayAuthToken: this.relayAuthToken }\n\t\t\t\t\t: {}),\n\t\t\t\t...(workspaceName === undefined ? {} : { workspace: workspaceName }),\n\t\t\t});\n\t\t\tconnection.send({ type: \"pair_started\", id: request.id, requestId });\n\t\t\tconnection.send({\n\t\t\t\ttype: \"pairing_progress\",\n\t\t\t\trequestId,\n\t\t\t\tphase: \"ticket\",\n\t\t\t\tticket: pairing.ticket,\n\t\t\t});\n\t\t\tconnection.send({ type: \"pairing_progress\", requestId, phase: \"waiting\" });\n\t\t\tconst ttlMs = Math.max(0, pairing.expiresAt - Date.now());\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => {\n\t\t\t\t\tif (!this.pendingPairRequests.delete(requestId)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.services.controlServer.sendTo(connection.connectionId, {\n\t\t\t\t\t\ttype: \"pairing_progress\",\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\tphase: \"failed\",\n\t\t\t\t\t\terror: \"pairing ticket expired\",\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tttlMs > 0 ? ttlMs : DEFAULT_IROH_REMOTE_PAIRING_TICKET_TTL_MS,\n\t\t\t);\n\t\t\ttimer.unref?.();\n\t\t\tthis.pendingPairRequests.set(requestId, {\n\t\t\t\trequestId,\n\t\t\t\tconnectionId: connection.connectionId,\n\t\t\t\tsecretHash: hashIrohRemotePairingSecret(pairing.secret),\n\t\t\t\texpiresAt: pairing.expiresAt,\n\t\t\t\ttimer,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconnection.send({\n\t\t\t\ttype: \"error\",\n\t\t\t\tid: request.id,\n\t\t\t\tcode: \"pair_failed\",\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\n\t// ==========================================================================\n\t// Control plane integration\n\t// ==========================================================================\n\n\tasync handleRequest(connection: ControlConnection, request: ControlRequest): Promise<boolean> {\n\t\tswitch (request.type) {\n\t\t\tcase \"lease_acquire\": {\n\t\t\t\tconst outcome = await this.leaseBroker.acquireForTui({\n\t\t\t\t\tconnectionId: connection.connectionId,\n\t\t\t\t\tworkspaceName: request.workspaceName,\n\t\t\t\t\tsessionId: request.sessionId,\n\t\t\t\t\tforce: request.force,\n\t\t\t\t});\n\t\t\t\tif (outcome.kind === \"granted\") {\n\t\t\t\t\tconnection.send({\n\t\t\t\t\t\ttype: \"lease_granted\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tworkspaceName: request.workspaceName,\n\t\t\t\t\t\tsessionId: request.sessionId,\n\t\t\t\t\t\thandoff: outcome.handoff,\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (outcome.kind === \"denied\") {\n\t\t\t\t\tconnection.send({ type: \"lease_denied\", id: request.id, reason: outcome.reason });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"lease_pending\", id: request.id, viewerFeedId: outcome.viewerFeedId });\n\t\t\t\toutcome.granted.then(\n\t\t\t\t\t(granted) => {\n\t\t\t\t\t\tconnection.send({\n\t\t\t\t\t\t\ttype: \"lease_granted\",\n\t\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\t\tworkspaceName: request.workspaceName,\n\t\t\t\t\t\t\tsessionId: request.sessionId,\n\t\t\t\t\t\t\thandoff: granted.handoff,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\tconnection.send({\n\t\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\t\tcode: \"drain_failed\",\n\t\t\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"lease_release\": {\n\t\t\t\tconst result = this.leaseBroker.releaseFromTui(\n\t\t\t\t\tconnection.connectionId,\n\t\t\t\t\trequest.workspaceName,\n\t\t\t\t\trequest.sessionId,\n\t\t\t\t);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: result.code, message: \"lease not held\" });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"lease_rekey\": {\n\t\t\t\tthis.leaseBroker.rekey(request.workspaceName, request.oldSessionId, request.newSessionId);\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"viewer_subscribe\": {\n\t\t\t\tif (!this.viewerFeeds.subscribe(request.viewerFeedId, connection.connectionId)) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: \"not_found\", message: \"unknown viewer feed\" });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"viewer_unsubscribe\": {\n\t\t\t\tif (!this.viewerFeeds.unsubscribe(request.viewerFeedId, connection.connectionId)) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: \"not_found\", message: \"unknown viewer feed\" });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"viewer_abort\": {\n\t\t\t\tif (!(await this.viewerFeeds.abort(request.viewerFeedId, connection.connectionId))) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: \"not_found\", message: \"unknown viewer feed\" });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"pair_request\":\n\t\t\t\tawait this.handlePairRequest(connection, request);\n\t\t\t\treturn true;\n\t\t\tcase \"pair_cancel\": {\n\t\t\t\tconst pending = this.pendingPairRequests.get(request.requestId);\n\t\t\t\tif (!pending || pending.connectionId !== connection.connectionId) {\n\t\t\t\t\tconnection.send({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tcode: \"not_found\",\n\t\t\t\t\t\tmessage: \"pairing request not found\",\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.cancelPendingPairing(request.requestId, pending);\n\t\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconnection.send({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tcode: \"cancel_failed\",\n\t\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"relay_rpc\": {\n\t\t\t\tconst result = await this.handleRelayRpc(connection, request);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: result.code, message: result.message });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({\n\t\t\t\t\ttype: \"relay_rpc_result\",\n\t\t\t\t\tid: request.id,\n\t\t\t\t\tresponse: result.response,\n\t\t\t\t\t...(result.workspaceMetadata === undefined ? {} : { workspaceMetadata: result.workspaceMetadata }),\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"relay_notification_delivery\": {\n\t\t\t\tconst result = await this.handleRelayNotificationDelivery(connection, request);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: result.code, message: result.message });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"relay_push_delivery_result\", id: request.id, status: result.status });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"relay_live_activity_delivery\": {\n\t\t\t\tconst result = await this.handleRelayLiveActivityDelivery(connection, request);\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: result.code, message: result.message });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconnection.send({ type: \"relay_push_delivery_result\", id: request.id, status: result.status });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"client_access_update\": {\n\t\t\t\tconst access =\n\t\t\t\t\trequest.access !== undefined\n\t\t\t\t\t\t? createIrohRemotePresetAccess(request.access)\n\t\t\t\t\t\t: createIrohRemoteExplicitAccess(\n\t\t\t\t\t\t\t\trequest.allowedTools ?? [],\n\t\t\t\t\t\t\t\tparseIrohRemoteRpcCapabilities(request.rpcCapabilities),\n\t\t\t\t\t\t\t);\n\t\t\t\tconst engine = this.engine;\n\t\t\t\tconst updated = engine\n\t\t\t\t\t? await engine.updateClientAccess(request.clientNodeId, request.expectedRevision, access)\n\t\t\t\t\t: await this.stateManager.updateClientAccess(request.clientNodeId, request.expectedRevision, access);\n\t\t\t\tif (!engine) {\n\t\t\t\t\tawait this.logAudit({\n\t\t\t\t\t\ttype: \"client_access_updated\",\n\t\t\t\t\t\tclientNodeId: request.clientNodeId,\n\t\t\t\t\t\tsuccess: updated.ok,\n\t\t\t\t\t\terror: updated.ok ? undefined : updated.reason,\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\texpectedRevision: request.expectedRevision,\n\t\t\t\t\t\t\t...(updated.ok\n\t\t\t\t\t\t\t\t? { revision: updated.client.rpcGrant.revision }\n\t\t\t\t\t\t\t\t: { currentRevision: updated.currentRevision }),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (!updated.ok) {\n\t\t\t\t\tconnection.send({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tcode: updated.reason,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\tupdated.reason === \"revision_conflict\"\n\t\t\t\t\t\t\t\t? `RPC grant revision conflict (current ${updated.currentRevision ?? \"unknown\"})`\n\t\t\t\t\t\t\t\t: updated.reason === \"revision_exhausted\"\n\t\t\t\t\t\t\t\t\t? \"RPC grant revision is exhausted; revoke and re-pair the client\"\n\t\t\t\t\t\t\t\t\t: \"client not found\",\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tawait this.services.state.flush();\n\t\t\t\tawait this.closeClientForAccessUpdate(request.clientNodeId);\n\t\t\t\tconnection.send({\n\t\t\t\t\ttype: \"client_access_updated\",\n\t\t\t\t\tid: request.id,\n\t\t\t\t\tclient: {\n\t\t\t\t\t\tclientNodeId: updated.client.nodeId,\n\t\t\t\t\t\tlabel: updated.client.label,\n\t\t\t\t\t\tpairedAtMs: updated.client.pairedAt,\n\t\t\t\t\t\tlastSeenAtMs: updated.client.lastSeenAt,\n\t\t\t\t\t\tallowedTools: updated.client.allowedTools.length === 0 ? [] : updated.client.allowedTools.split(\",\"),\n\t\t\t\t\t\trpcGrant: updated.client.rpcGrant,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"client_revoke\": {\n\t\t\t\tconst result = await this.requireEngineSafe();\n\t\t\t\tif (!result.ok) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: \"iroh_unavailable\", message: result.error });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconst revocation = await result.engine.revokeClient(request.clientNodeId);\n\t\t\t\tif (!revocation.revoked) {\n\t\t\t\t\tconnection.send({ type: \"error\", id: request.id, code: \"not_found\", message: \"client not found\" });\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tawait this.closeActiveStreamsForClient(request.clientNodeId);\n\t\t\t\tawait this.revokeClientPushTargets(revocation.client);\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcase \"workspace_unregister\": {\n\t\t\t\tlet removedWorkspace: Awaited<ReturnType<IrohRemoteHostStateManager[\"unregisterWorkspace\"]>>;\n\t\t\t\ttry {\n\t\t\t\t\tremovedWorkspace = await this.stateManager.unregisterWorkspace(request.name);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isIrohRemoteWorkspaceHasWorktreesError(error)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t\tawait this.logAudit({\n\t\t\t\t\t\ttype: \"workspace_unregistered\",\n\t\t\t\t\t\tworkspace: request.name,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,\n\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\tsource: \"control\",\n\t\t\t\t\t\t\tworktreeCount: error.worktreeIds.length,\n\t\t\t\t\t\t\tworktreeIds: error.worktreeIds,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tconnection.send({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tcode: IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,\n\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (!removedWorkspace) {\n\t\t\t\t\tconnection.send({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\tcode: \"not_found\",\n\t\t\t\t\t\tmessage: `No registered workspace named ${request.name}`,\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tthis.engine?.clearPairingSecretForWorkspace(request.name);\n\t\t\t\tawait this.cleanupUnregisteredWorkspace(request.name, { workspacePath: removedWorkspace.path });\n\t\t\t\tawait this.logAudit({\n\t\t\t\t\ttype: \"workspace_unregistered\",\n\t\t\t\t\tworkspace: request.name,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tdetails: { source: \"control\" },\n\t\t\t\t});\n\t\t\t\tconnection.send({ type: \"ok\", id: request.id });\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tif (isWorktreeControlRequest(request)) {\n\t\t\t\t\tawait handleWorktreeControlRequest(connection, request, {\n\t\t\t\t\t\tmanager: this.worktrees,\n\t\t\t\t\t\tstateManager: this.stateManager,\n\t\t\t\t\t\tremoveWorktree: (workspace, worktreeId, force) =>\n\t\t\t\t\t\t\tthis.removeWorkspaceWorktree(workspace, worktreeId, force),\n\t\t\t\t\t});\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async createRelayDeliveryAuthorization(\n\t\tconnection: ControlConnection,\n\t\trequest: { clientNodeId: string; workspaceName: string; sessionId: string },\n\t): Promise<\n\t\t{ ok: true; authorization: IrohRemoteClientAuthorizationSuccess } | { ok: false; code: string; message: string }\n\t> {\n\t\tconst lease = this.leaseBroker.lookup(request.workspaceName, request.sessionId);\n\t\tif (!lease || lease.state !== \"tui-owned\" || lease.tuiConnectionId !== connection.connectionId) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tcode: \"not_held\",\n\t\t\t\tmessage: \"relay lease is not held by this control connection\",\n\t\t\t};\n\t\t}\n\t\tconst client = await this.stateManager.getClient(request.clientNodeId);\n\t\tif (!client) {\n\t\t\treturn { ok: false, code: \"not_found\", message: \"paired client not found\" };\n\t\t}\n\t\tconst workspace = (await this.stateManager.getState()).workspaces.find(\n\t\t\t(candidate) => candidate.name === request.workspaceName,\n\t\t);\n\t\tif (!workspace) {\n\t\t\treturn { ok: false, code: \"not_found\", message: `no registered workspace named ${request.workspaceName}` };\n\t\t}\n\t\treturn {\n\t\t\tok: true,\n\t\t\tauthorization: {\n\t\t\t\tok: true,\n\t\t\t\tallowTools: client.allowedTools,\n\t\t\t\tclient,\n\t\t\t\tpaired: true,\n\t\t\t\tpairingSecretConsumed: false,\n\t\t\t\tworkspace,\n\t\t\t\tworkspaceNames: [workspace.name],\n\t\t\t\tworkspaces: [{ name: workspace.name, status: \"available\" }],\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async handleRelayNotificationDelivery(\n\t\tconnection: ControlConnection,\n\t\trequest: Extract<ControlRequest, { type: \"relay_notification_delivery\" }>,\n\t): Promise<RelayPushDeliveryResult> {\n\t\tconst notification = request.notification;\n\t\tif (notification.sessionId !== undefined && notification.sessionId !== request.sessionId) {\n\t\t\treturn { ok: false, code: \"session_mismatch\", message: \"notification session does not match relay session\" };\n\t\t}\n\t\tif (notification.workspace !== undefined && notification.workspace !== request.workspaceName) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tcode: \"workspace_mismatch\",\n\t\t\t\tmessage: \"notification workspace does not match relay workspace\",\n\t\t\t};\n\t\t}\n\t\tconst authorization = await this.createRelayDeliveryAuthorization(connection, request);\n\t\tif (!authorization.ok) {\n\t\t\treturn authorization;\n\t\t}\n\t\tconst scopedNotification: IrohRemotePushNotificationIntent = {\n\t\t\t...notification,\n\t\t\tsessionId: notification.sessionId ?? request.sessionId,\n\t\t\tworkspace: notification.workspace ?? request.workspaceName,\n\t\t};\n\t\ttry {\n\t\t\tconst status = await this.createPushNotificationDispatcher(authorization.authorization).deliverNotification(\n\t\t\t\tscopedNotification,\n\t\t\t);\n\t\t\treturn { ok: true, status };\n\t\t} catch {\n\t\t\treturn { ok: true, status: \"failed\" };\n\t\t}\n\t}\n\n\tprivate async handleRelayLiveActivityDelivery(\n\t\tconnection: ControlConnection,\n\t\trequest: Extract<ControlRequest, { type: \"relay_live_activity_delivery\" }>,\n\t): Promise<RelayPushDeliveryResult> {\n\t\tconst contentState = request.update.contentState;\n\t\tif (contentState.sessionID !== undefined && contentState.sessionID !== request.sessionId) {\n\t\t\treturn { ok: false, code: \"session_mismatch\", message: \"Live Activity session does not match relay session\" };\n\t\t}\n\t\tif (contentState.workspaceName !== undefined && contentState.workspaceName !== request.workspaceName) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tcode: \"workspace_mismatch\",\n\t\t\t\tmessage: \"Live Activity workspace does not match relay workspace\",\n\t\t\t};\n\t\t}\n\t\tconst authorization = await this.createRelayDeliveryAuthorization(connection, request);\n\t\tif (!authorization.ok) {\n\t\t\treturn authorization;\n\t\t}\n\t\tconst scopedUpdate: IrohRemoteLiveActivityUpdateIntent = {\n\t\t\t...request.update,\n\t\t\tcontentState: {\n\t\t\t\t...contentState,\n\t\t\t\tsessionID: contentState.sessionID ?? request.sessionId,\n\t\t\t\tworkspaceName: contentState.workspaceName ?? request.workspaceName,\n\t\t\t},\n\t\t};\n\t\ttry {\n\t\t\tconst status = await this.createPushNotificationDispatcher(\n\t\t\t\tauthorization.authorization,\n\t\t\t).deliverLiveActivityUpdate(scopedUpdate);\n\t\t\treturn { ok: true, status };\n\t\t} catch {\n\t\t\treturn { ok: true, status: \"failed\" };\n\t\t}\n\t}\n\n\t/**\n\t * Execute a state-touching RPC command forwarded from a TUI-owned relay\n\t * against the daemon's real state (§5.6): push targets, live activities,\n\t * and workspace unregistration must land here, not in the TUI's in-memory\n\t * state copy.\n\t */\n\tprivate async handleRelayRpc(\n\t\tconnection: ControlConnection,\n\t\trequest: Extract<ControlRequest, { type: \"relay_rpc\" }>,\n\t): Promise<\n\t\t| {\n\t\t\t\tok: true;\n\t\t\t\tresponse: Record<string, unknown>;\n\t\t\t\tworkspaceMetadata?: { workspaceNames: string[]; workspaces: Array<{ name: string; status: string }> };\n\t\t }\n\t\t| { ok: false; code: string; message: string }\n\t> {\n\t\tconst relayAuthorization = this.relays.authorizeRpc(request.relayId, connection.connectionId, request);\n\t\tif (!relayAuthorization.ok) {\n\t\t\treturn relayAuthorization;\n\t\t}\n\t\tconst command = request.command;\n\t\tif (!RELAY_RPC_COMMAND_TYPES.has(command.type)) {\n\t\t\treturn { ok: false, code: \"unsupported\", message: `unsupported relay rpc command: ${command.type}` };\n\t\t}\n\t\tconst client = await this.stateManager.getClient(request.clientNodeId);\n\t\tif (!client) {\n\t\t\treturn { ok: false, code: \"not_found\", message: \"paired client not found\" };\n\t\t}\n\t\tconst requiredCapabilities = getIrohRemoteRpcCommandCapabilities(command);\n\t\tif (requiredCapabilities === undefined) {\n\t\t\treturn { ok: false, code: \"unsupported\", message: `unsupported relay rpc command: ${command.type}` };\n\t\t}\n\t\tconst missingCapability = getMissingIrohRemoteRpcCapability(client.rpcGrant, requiredCapabilities);\n\t\tif (missingCapability !== undefined) {\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tresponse: {\n\t\t\t\t\t...createIrohRemoteRpcCapabilityDeniedResponse(\n\t\t\t\t\t\tgetRpcResponseId(command),\n\t\t\t\t\t\tcommand.type,\n\t\t\t\t\t\tmissingCapability,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst workspace = (await this.stateManager.getState()).workspaces.find(\n\t\t\t(candidate) => candidate.name === request.workspaceName,\n\t\t);\n\t\tif (!workspace) {\n\t\t\treturn { ok: false, code: \"not_found\", message: `no registered workspace named ${request.workspaceName}` };\n\t\t}\n\t\tconst authorization: IrohRemoteClientAuthorizationSuccess = {\n\t\t\tok: true,\n\t\t\tallowTools: client.allowedTools,\n\t\t\tclient,\n\t\t\tpaired: true,\n\t\t\tpairingSecretConsumed: false,\n\t\t\tworkspace,\n\t\t\tworkspaceNames: [workspace.name],\n\t\t\tworkspaces: [{ name: workspace.name, status: \"available\" }],\n\t\t};\n\t\tconst responseId = getRpcResponseId(command);\n\t\tif (command.type === \"set_keep_awake\" || command.type === \"get_keep_awake\") {\n\t\t\tconst response = createKeepAwakeRpcResponse(command, this.getCommandContext());\n\t\t\treturn { ok: true, response: response as Record<string, unknown> };\n\t\t}\n\t\tif (command.type === \"set_web_search_key\" || command.type === \"get_web_search_status\") {\n\t\t\tconst response = createWebSearchKeyRpcResponse(command, this.getCommandContext());\n\t\t\treturn { ok: true, response: response as Record<string, unknown> };\n\t\t}\n\t\tif (command.type === \"register_push_target\") {\n\t\t\ttry {\n\t\t\t\tconst data = await this.createPushNotificationDispatcher(authorization).registerPushTarget(command.args);\n\t\t\t\treturn { ok: true, response: createRpcSuccessResponse(responseId, command.type, { ...data }) };\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\tok: true,\n\t\t\t\t\tresponse: {\n\t\t\t\t\t\t...createIrohRemoteRpcErrorResponse(\n\t\t\t\t\t\t\tresponseId,\n\t\t\t\t\t\t\tcommand.type,\n\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tif (command.type === \"register_live_activity\") {\n\t\t\tconst response = await createRemoteRegisterLiveActivityRpcResponse(\n\t\t\t\tcommand,\n\t\t\t\tauthorization,\n\t\t\t\tthis.getCommandContext(),\n\t\t\t\trequest.sessionId,\n\t\t\t);\n\t\t\treturn { ok: true, response: response as Record<string, unknown> };\n\t\t}\n\t\tif (command.type === \"unregister_live_activity\") {\n\t\t\tconst response = await createRemoteUnregisterLiveActivityRpcResponse(\n\t\t\t\tcommand,\n\t\t\t\tauthorization,\n\t\t\t\tthis.getCommandContext(),\n\t\t\t\trequest.sessionId,\n\t\t\t);\n\t\t\treturn { ok: true, response: response as Record<string, unknown> };\n\t\t}\n\t\t// unregister_workspace: run against the daemon state with the shared host\n\t\t// cleanup, keeping the requesting conversation's own relays open so the\n\t\t// response can still be delivered over them.\n\t\tconst excludeRelayIds = new Set(\n\t\t\tthis.relays\n\t\t\t\t.activeForConversation(request.clientNodeId, request.workspaceName, request.sessionId)\n\t\t\t\t.map((relay) => relay.relayId),\n\t\t);\n\t\tconst context: ConversationCommandContext = {\n\t\t\t...this.getCommandContext(),\n\t\t\tonWorkspaceUnregistered: async (workspaceName) => {\n\t\t\t\tawait this.cleanupUnregisteredWorkspace(workspaceName, {\n\t\t\t\t\trelayIds: excludeRelayIds,\n\t\t\t\t\tworkspacePath: workspace.path,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t\tconst response = await handleRemoteHostRpcCommand(command, authorization, context);\n\t\tif (!response) {\n\t\t\treturn { ok: false, code: \"unsupported\", message: `unsupported relay rpc command: ${command.type}` };\n\t\t}\n\t\treturn {\n\t\t\tok: true,\n\t\t\tresponse: response as Record<string, unknown>,\n\t\t\tworkspaceMetadata: {\n\t\t\t\tworkspaceNames: [...authorization.workspaceNames],\n\t\t\t\tworkspaces: authorization.workspaces.map((entry) => ({ name: entry.name, status: entry.status })),\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate async requireEngineSafe(): Promise<\n\t\t{ ok: true; engine: IrohRemoteHostEngine } | { ok: false; error: string }\n\t> {\n\t\ttry {\n\t\t\tawait withTimeout(\n\t\t\t\tthis.ready.promise,\n\t\t\t\tIROH_ENDPOINT_READY_TIMEOUT_MS,\n\t\t\t\t\"Iroh endpoint did not become ready within 15s\",\n\t\t\t);\n\t\t} catch (error) {\n\t\t\treturn { ok: false, error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t\tif (!this.engine) {\n\t\t\treturn { ok: false, error: \"iroh host engine is not ready\" };\n\t\t}\n\t\treturn { ok: true, engine: this.engine };\n\t}\n\n\tprivate async cancelPendingPairing(\n\t\trequestId: string,\n\t\tpending: { secretHash: string; timer: NodeJS.Timeout },\n\t): Promise<void> {\n\t\tclearTimeout(pending.timer);\n\t\tif (this.engine) {\n\t\t\tawait this.engine.cancelPairingSecretByHash(pending.secretHash);\n\t\t} else {\n\t\t\tawait this.stateManager.removePendingPairingTicket(pending.secretHash);\n\t\t}\n\t\tawait this.services.state.flush();\n\t\tthis.pendingPairRequests.delete(requestId);\n\t}\n\n\tonControlConnectionClosed(connection: ControlConnection): void {\n\t\tthis.leaseBroker.releaseAllForConnection(connection.connectionId);\n\t\tfor (const [requestId, pending] of this.pendingPairRequests) {\n\t\t\tif (pending.connectionId !== connection.connectionId) continue;\n\t\t\tvoid this.cancelPendingPairing(requestId, pending).catch((error) => {\n\t\t\t\tthis.log(\"warn\", \"failed to cancel pairing after control disconnect\", {\n\t\t\t\t\trequestId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\tadmitRelay(relayId: string, relayToken: string, socket: Socket, bufferedRemainder: Buffer): boolean {\n\t\treturn this.relays.admit(relayId, relayToken, socket, bufferedRemainder);\n\t}\n\n\tstatusExtras(): { leases: ControlLeaseStatus[]; phoneConnections: number; relayCount: number } {\n\t\tconst leases: ControlLeaseStatus[] = this.leaseBroker.list().map((record) => ({\n\t\t\tworkspaceName: record.workspaceName,\n\t\t\tsessionId: record.sessionId,\n\t\t\tstate: record.state,\n\t\t\trelayCount: record.relayIds.size,\n\t\t\tstreamCount: record.streamCount,\n\t\t}));\n\t\treturn { leases, phoneConnections: this.clientConnections.size, relayCount: this.relays.activeCount() };\n\t}\n\n\tasync shutdown(): Promise<void> {\n\t\tthis.shuttingDown = true;\n\t\tthis.worktreeRetention.dispose();\n\t\t// 1. Stop accepting: close the endpoint accept loop lazily; new hellos are\n\t\t// rejected by the control server's shutting-down gate. Close relays and\n\t\t// unredeemed offers up front with the dedicated host_shutdown reason —\n\t\t// otherwise they die with the endpoint and TUIs see a misleading\n\t\t// relay_closed{error}.\n\t\tfor (const relay of this.relays.activeRelays()) {\n\t\t\trelay.close(\"host_shutdown\");\n\t\t}\n\t\tfor (const pending of this.relays.pendingRelays()) {\n\t\t\tthis.abortPendingRelay(pending.relayId, \"host_shutdown\", \"daemon shutting down\");\n\t\t}\n\t\t// 2. Wait for busy runtimes to go idle (60s cap each, concurrently);\n\t\t// never abort prompt preflight or a turn from shutdown.\n\t\tconst drainResults = await Promise.allSettled(\n\t\t\tthis.runtimes\n\t\t\t\t.values()\n\t\t\t\t.filter((entry) => entry.runtime.session.isBusy)\n\t\t\t\t.map((entry) =>\n\t\t\t\t\twithTimeout(entry.runtime.session.waitForIdle(), SHUTDOWN_RUNTIME_IDLE_CAP_MS, \"drain cap\"),\n\t\t\t\t),\n\t\t);\n\t\tconst cappedRuntimes = drainResults.filter((result) => result.status === \"rejected\").length;\n\t\t// 3. Flush + dispose runtimes through the normal dispose path.\n\t\tawait this.runtimes.stopAll(\"host_shutdown\");\n\t\t// 4. Close all phone streams and connections with reason host_shutdown.\n\t\tfor (const nodeId of Array.from(this.clientConnections.keys())) {\n\t\t\tfor (const entry of this.activeStreams.entriesForClientNodeId(nodeId)) {\n\t\t\t\tthis.activeStreams.unregister(entry);\n\t\t\t\tawait Promise.resolve(entry.close(\"host_shutdown\")).catch(() => {});\n\t\t\t}\n\t\t\tawait this.closeClientConnectionsForClient(nodeId, \"host_shutdown\");\n\t\t}\n\t\ttry {\n\t\t\tawait this.endpoint?.close();\n\t\t} catch {\n\t\t\t// Endpoint shutdown is best-effort.\n\t\t}\n\t\tawait Promise.allSettled(this.connectionTasks);\n\t\tawait this.services.auditLogger.flush().catch(() => {});\n\t\tthis.log(\"info\", \"iroh service stopped\", { cappedRuntimes });\n\t}\n\n\tprivate async logAudit(event: Parameters<VoltdRuntimeServices[\"auditLogger\"][\"log\"]>[0]): Promise<void> {\n\t\ttry {\n\t\t\tawait this.services.auditLogger.log(event);\n\t\t} catch {\n\t\t\t// Audit logging is best-effort.\n\t\t}\n\t}\n}\n"]}