@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,2604 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { randomUUID } from "node:crypto";
3
+ import { existsSync } from "node:fs";
4
+ import { relative, resolve, sep } from "node:path";
5
+ import { createIrohRemoteExplicitAccess, createIrohRemotePresetAccess, getIrohRemoteRpcCommandCapabilities, getIrohRemoteStreamCapability, getMissingIrohRemoteRpcCapability, hasIrohRemoteRpcCapability, parseIrohRemoteRpcCapabilities, parseIrohRemoteRpcGrant, } from "../core/remote/iroh/access-grant.js";
6
+ import { IrohRemoteActiveStreamRegistry } from "../core/remote/iroh/active-stream-registry.js";
7
+ import { hashIrohRemotePairingSecret } from "../core/remote/iroh/authorization.js";
8
+ import { DEFAULT_IROH_REMOTE_PAIRING_TICKET_TTL_MS, IrohRemoteHostEngine, } from "../core/remote/iroh/engine.js";
9
+ import { createIrohRemoteHandshakeFailure } from "../core/remote/iroh/handshake.js";
10
+ import { DEFAULT_IROH_REMOTE_HANDSHAKE_MAX_LINE_BYTES, DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS, writeIrohRemoteHandshakeResponse, } from "../core/remote/iroh/handshake-reader.js";
11
+ import { resolveIrohRemoteWorkspaceProjectTrusted } from "../core/remote/iroh/host-policy.js";
12
+ import { IROH_REMOTE_ALPN, resolveIrohRemoteRuntimeToolPolicy } from "../core/remote/iroh/protocol.js";
13
+ import { IrohRemoteInMemoryPushNotificationDeduper, IrohRemotePushNotificationDispatcher, IrohRemotePushRelayHttpClient, revokeIrohRemoteClientPushTargets, } from "../core/remote/iroh/push.js";
14
+ import { createIrohRemoteRpcCapabilityDeniedResponse, createIrohRemoteRpcErrorResponse, } from "../core/remote/iroh/rpc-command-filter.js";
15
+ import { IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR, isIrohRemoteWorkspaceHasWorktreesError, } from "../core/remote/iroh/state-manager.js";
16
+ import { getIrohRemoteWorkspaceAvailabilityStatus } from "../core/remote/iroh/workspace.js";
17
+ import { getDefaultSessionDir } from "../core/session-manager.js";
18
+ import { getCurrentThemeName, getResolvedThemeColors } from "../core/theme/runtime.js";
19
+ import { ProjectTrustStore } from "../core/trust-manager.js";
20
+ import { runIrohRemoteRpcMode } from "../modes/rpc/iroh-remote-rpc-mode.js";
21
+ import { CONTROL_RPC_GRANTS_CAPABILITY, CONTROL_WORKTREES_CAPABILITY, RELAY_RPC_COMMAND_TYPES, } from "./control-protocol.js";
22
+ import { createKeepAwakeRpcResponse, createRemoteRegisterLiveActivityRpcResponse, createRemoteUnregisterLiveActivityRpcResponse, createRpcSuccessResponse, createWebSearchKeyRpcResponse, getRpcResponseId, handleIntegratedConversationRpcCommand, handleRemoteHostRpcCommand, REMOTE_SESSION_LIST_CURSOR_TTL_MS, toRpcKeepAwakeStatus, } from "./conversation-commands.js";
23
+ import { createIntegratedConversationHandshakeResponse, decorateRemoteHostState, } from "./handshake-responses.js";
24
+ import { createConversationOpenError, getResolvedTargetSessionId, IntegratedRuntimeRegistry, } from "./integrated-runtimes.js";
25
+ import { formatIrohLoadError, loadIrohModule, } from "./iroh-native.js";
26
+ import { IrohRemoteResourceGuard } from "./iroh-resource-guard.js";
27
+ import { LeaseBroker } from "./lease-broker.js";
28
+ import { RELAY_TOKEN_TTL_MS, RelayRegistry } from "./relay-stream.js";
29
+ import { createSessionManagerTargetStore, resolveIrohRemoteSessionTarget, } from "./session-target.js";
30
+ import { resolveWorktreeCleanupPolicy } from "./state.js";
31
+ import { createHostThemeTokensFrame, HOST_THEME_TOKENS_FEATURE } from "./theme-push.js";
32
+ import { ViewerFeedRegistry } from "./viewer-feed.js";
33
+ import { isPathInside } from "./workspace-directory.js";
34
+ import { runWorkspaceDiscoveryStream, runWorkspaceManagementStream, runWorktreeManagementStream, WORKSPACE_UNREGISTERED_CLOSE_REASON, writeIrohRemoteJsonLine, } from "./workspace-streams.js";
35
+ import { evaluateWorktreeRelayGate, getRegisteredWorkingDirectoryForWorktree, getWorkspaceWorktreesDir, getWorktreesRoot, handleWorktreeControlRequest, isWorktreeControlRequest, WorktreeManager, WorktreeRetentionSweeper, } from "./worktree-manager.js";
36
+ const ACTIVE_REVOKE_CLOSE_REASON = "revoked";
37
+ const ACTIVE_REPLACE_CLOSE_REASON = "replaced";
38
+ const DUPLICATE_CONVERSATION_RETRY_AFTER_MS = 500;
39
+ const RELAY_OFFER_RETRY_AFTER_MS = 1000;
40
+ const WORKSPACE_DISCOVERY_STREAM_SESSION_ID = "$workspace-discovery";
41
+ const WORKSPACE_MANAGEMENT_STREAM_SESSION_ID = "$workspace-management";
42
+ const IROH_ENDPOINT_READY_TIMEOUT_MS = 15_000;
43
+ const IROH_UNAUTHENTICATED_CONNECTION_TIMEOUT_MS = 15_000;
44
+ const SHUTDOWN_RUNTIME_IDLE_CAP_MS = 60_000;
45
+ export function collectClientAuthorityInvalidationRuntimes(activeStreams, runtimes, clientNodeId) {
46
+ const clientEntries = activeStreams.entriesForClientNodeId(clientNodeId);
47
+ return new Set(Array.from(runtimes).filter((runtime) => runtime.clientNodeId === clientNodeId ||
48
+ clientEntries.some((entry) => entry.workspaceName === runtime.workspaceName && entry.sessionId === runtime.sessionId)));
49
+ }
50
+ export function collectClientAuthorityInvalidationStreams(activeStreams, runtimes, clientNodeId) {
51
+ const runtimeList = Array.from(runtimes);
52
+ const entries = new Set(activeStreams.entriesForClientNodeId(clientNodeId));
53
+ for (const runtime of collectClientAuthorityInvalidationRuntimes(activeStreams, runtimeList, clientNodeId)) {
54
+ for (const entry of activeStreams.entriesForConversationKey(runtime.workspaceName, runtime.sessionId)) {
55
+ entries.add(entry);
56
+ }
57
+ }
58
+ return entries;
59
+ }
60
+ function getRelativeWorkingDirectoryForRoot(rootPath, cwd) {
61
+ const root = resolve(rootPath);
62
+ const child = resolve(cwd);
63
+ if (!isPathInside(root, child)) {
64
+ return null;
65
+ }
66
+ const relativePath = relative(root, child);
67
+ return relativePath.length === 0 || relativePath === "." ? undefined : relativePath.split(sep).join("/");
68
+ }
69
+ /**
70
+ * Defensive cap on concurrent in-flight bi-streams per client connection. A
71
+ * well-behaved client keeps only a handful open (one conversation + a few
72
+ * utility streams); an authenticated-but-misbehaving client could otherwise open
73
+ * unbounded concurrent streams, each spawning a runtime attach, and exhaust
74
+ * daemon resources. Hitting the cap closes the connection.
75
+ */
76
+ const MAX_CONCURRENT_STREAMS_PER_CONNECTION = 64;
77
+ let activeConnectionSequence = 0;
78
+ let activeStreamSequence = 0;
79
+ /**
80
+ * The Volt-operated relay fleet. Endpoints bind against these by default
81
+ * ("production" mode); the n0 public relays ("development" mode) are for
82
+ * development only and must be opted into via VOLT_IROH_RELAY_MODE=development.
83
+ */
84
+ export const VOLT_PRODUCTION_RELAY_URLS = ["https://iroh-relay-us-central.volt-cli.dev"];
85
+ /**
86
+ * Resolves the effective relay configuration. Precedence: explicit service
87
+ * config, then VOLT_IROH_RELAY_MODE / VOLT_IROH_RELAY_URLS, then the Volt
88
+ * production relay fleet.
89
+ */
90
+ export function resolveIrohRelayConfig(config, env = process.env) {
91
+ const envUrls = parseRelayUrlsEnv(env.VOLT_IROH_RELAY_URLS);
92
+ const envModeValue = env.VOLT_IROH_RELAY_MODE?.trim();
93
+ let envMode;
94
+ let warning;
95
+ if (envModeValue !== undefined && envModeValue !== "") {
96
+ if (envModeValue === "disabled" || envModeValue === "development" || envModeValue === "production") {
97
+ envMode = envModeValue;
98
+ }
99
+ else {
100
+ warning = `ignoring invalid VOLT_IROH_RELAY_MODE "${envModeValue}" (expected disabled, development, or production)`;
101
+ }
102
+ }
103
+ const relayMode = config.relayMode ?? envMode ?? "production";
104
+ const configuredUrls = config.relayUrls ?? envUrls;
105
+ const relayUrls = relayMode === "production" ? (configuredUrls ?? VOLT_PRODUCTION_RELAY_URLS) : (configuredUrls ?? []);
106
+ return { relayMode, relayUrls, ...(warning === undefined ? {} : { warning }) };
107
+ }
108
+ function parseRelayUrlsEnv(value) {
109
+ if (value === undefined) {
110
+ return undefined;
111
+ }
112
+ const urls = value
113
+ .split(",")
114
+ .map((url) => url.trim())
115
+ .filter((url) => url.length > 0);
116
+ return urls.length > 0 ? urls : undefined;
117
+ }
118
+ function isExpectedApplicationClose(error) {
119
+ const message = error instanceof Error ? error.message : String(error);
120
+ return (message.includes("ConnectionLost(ApplicationClosed") &&
121
+ message.includes("error_code: 0") &&
122
+ (message.includes('reason: b"done"') ||
123
+ message.includes(`reason: b"${ACTIVE_REVOKE_CLOSE_REASON}"`) ||
124
+ message.includes(`reason: b"${ACTIVE_REPLACE_CLOSE_REASON}"`) ||
125
+ message.includes(`reason: b"${WORKSPACE_UNREGISTERED_CLOSE_REASON}"`)));
126
+ }
127
+ function closeConnection(connection, reason) {
128
+ try {
129
+ connection.close(0n, Array.from(Buffer.from(reason, "utf8")));
130
+ }
131
+ catch {
132
+ // Transport closure is best-effort; registry/task cleanup must still run.
133
+ }
134
+ }
135
+ async function waitForConnectionClose(connection) {
136
+ await Promise.race([
137
+ connection.closed().catch(() => { }),
138
+ new Promise((resolveDelay) => {
139
+ setTimeout(resolveDelay, 500);
140
+ }),
141
+ ]);
142
+ }
143
+ async function withTimeout(promise, timeoutMs, message) {
144
+ let timeoutId;
145
+ try {
146
+ return await Promise.race([
147
+ promise,
148
+ new Promise((_, reject) => {
149
+ timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);
150
+ }),
151
+ ]);
152
+ }
153
+ finally {
154
+ clearTimeout(timeoutId);
155
+ }
156
+ }
157
+ function closeIrohRemoteStream(stream, _reason) {
158
+ void Promise.resolve(stream.send.finish?.()).catch(() => { });
159
+ void Promise.resolve(stream.recv.stop?.(0n)).catch(() => { });
160
+ }
161
+ function getRemoteTerminalReason(reason) {
162
+ if (reason === ACTIVE_REVOKE_CLOSE_REASON) {
163
+ return "client_revoked";
164
+ }
165
+ if (reason === WORKSPACE_UNREGISTERED_CLOSE_REASON ||
166
+ reason === "workspace_authorization_removed" ||
167
+ reason === "lease_transferred" ||
168
+ reason === "session_rekeyed_reconnect") {
169
+ return reason;
170
+ }
171
+ return undefined;
172
+ }
173
+ /**
174
+ * The daemon's Iroh host: owns the endpoint identity, pairing, revocation,
175
+ * headless integrated runtimes, workspace/device streams, push dispatch, and
176
+ * the accept loop. Ported from the dissolved src/remote/iroh-host.mjs.
177
+ */
178
+ export function createIrohDaemonService(config = {}) {
179
+ return (services) => {
180
+ const log = services.logger.child("iroh");
181
+ const loaded = loadIrohModule();
182
+ if (!loaded.iroh) {
183
+ log("warn", formatIrohLoadError(loaded.error));
184
+ return {
185
+ async handleRequest(connection, request) {
186
+ if (request.type === "pair_request") {
187
+ connection.send({
188
+ type: "error",
189
+ id: request.id,
190
+ code: "iroh_unavailable",
191
+ message: formatIrohLoadError(loaded.error),
192
+ });
193
+ return true;
194
+ }
195
+ return false;
196
+ },
197
+ };
198
+ }
199
+ const service = new IrohDaemonService(loaded.iroh, services, config);
200
+ void service.start();
201
+ return {
202
+ handleRequest: (connection, request) => service.handleRequest(connection, request),
203
+ onConnectionClosed: (connection) => service.onControlConnectionClosed(connection),
204
+ onThemeChanged: () => service.onThemeChanged(),
205
+ onKeepAwakeChanged: () => service.onKeepAwakeChanged(),
206
+ statusExtras: () => service.statusExtras(),
207
+ admitRelay: (relayId, relayToken, socket, bufferedRemainder) => service.admitRelay(relayId, relayToken, socket, bufferedRemainder),
208
+ shutdown: () => service.shutdown(),
209
+ };
210
+ };
211
+ }
212
+ class IrohDaemonService {
213
+ iroh;
214
+ services;
215
+ relayMode;
216
+ relayUrls;
217
+ relayAuthToken;
218
+ relayConfigWarning;
219
+ log;
220
+ stateManager;
221
+ activeStreams = new IrohRemoteActiveStreamRegistry();
222
+ clientConnections = new Map();
223
+ connectionTasks = new Set();
224
+ resourceGuard = new IrohRemoteResourceGuard();
225
+ pendingPairRequests = new Map();
226
+ sessionListCursors = new Map();
227
+ pushRelayClient;
228
+ pushNotificationDeduper = new IrohRemoteInMemoryPushNotificationDeduper();
229
+ trustStore;
230
+ runtimes;
231
+ worktrees;
232
+ worktreeRetention;
233
+ leaseBroker;
234
+ viewerFeeds;
235
+ relays = new RelayRegistry();
236
+ endpoint;
237
+ engine;
238
+ hostNodeId;
239
+ endpointTicket;
240
+ shuttingDown = false;
241
+ ready;
242
+ constructor(iroh, services, config) {
243
+ this.iroh = iroh;
244
+ this.services = services;
245
+ const relayConfig = resolveIrohRelayConfig(config);
246
+ this.relayMode = relayConfig.relayMode;
247
+ this.relayUrls = relayConfig.relayUrls;
248
+ this.relayConfigWarning = relayConfig.warning;
249
+ const envRelayAuthToken = process.env.VOLT_IROH_RELAY_AUTH_TOKEN?.trim();
250
+ this.relayAuthToken =
251
+ config.relayAuthToken ??
252
+ (envRelayAuthToken !== undefined && envRelayAuthToken !== "" ? envRelayAuthToken : undefined) ??
253
+ services.state.state.settings.relayAuthToken;
254
+ // Persist a newly seen token so bare restarts keep authenticating against
255
+ // the relay without re-exporting the env var.
256
+ if (this.relayAuthToken !== undefined && this.relayAuthToken !== services.state.state.settings.relayAuthToken) {
257
+ services.state.updateSettings({ relayAuthToken: this.relayAuthToken });
258
+ }
259
+ this.log = services.logger.child("iroh");
260
+ this.stateManager = services.stateManager;
261
+ this.trustStore = new ProjectTrustStore(services.agentDir);
262
+ this.pushRelayClient = new IrohRemotePushRelayHttpClient({
263
+ authToken: config.pushRelayAuthToken ?? process.env.VOLT_PUSH_RELAY_AUTH_TOKEN,
264
+ baseUrl: config.pushRelayUrl ?? process.env.VOLT_PUSH_RELAY_URL,
265
+ });
266
+ this.runtimes = new IntegratedRuntimeRegistry({
267
+ agentDir: services.agentDir,
268
+ profile: config.profile,
269
+ auditLogger: services.auditLogger,
270
+ stateManager: this.stateManager,
271
+ activeStreams: this.activeStreams,
272
+ detachedRuntimeTtlMs: () => services.state.state.settings.detachedRuntimeTtlMs,
273
+ getToolPolicy: (workspace, clientAllowTools) => resolveIrohRemoteRuntimeToolPolicy({
274
+ clientAllowTools,
275
+ workspaceAllowTools: workspace.allowedTools,
276
+ daemonAllowTools: services.state.state.settings.allowTools,
277
+ }),
278
+ getProjectTrustedForWorkspace: (workspace) => resolveIrohRemoteWorkspaceProjectTrusted(workspace, { trustStore: this.trustStore }),
279
+ setClientLastSessionId: (nodeId, workspace, sessionId) => this.requireEngine().setClientLastSessionId(nodeId, workspace, sessionId),
280
+ resolveWorktree: (workspaceName, hello, targetSessionId) => this.resolveConversationWorktree(workspaceName, hello, targetSessionId),
281
+ resolveWorkingDirectory: (options) => this.resolveConversationWorkingDirectory(options),
282
+ bindWorktreeSession: (workspaceName, worktreeId, sessionId) => this.worktrees.bindSession(workspaceName, worktreeId, sessionId),
283
+ onRuntimeRekeyed: (workspaceName, previousSessionId, sessionId) => this.leaseBroker.rekey(workspaceName, previousSessionId, sessionId),
284
+ onRuntimeDisposed: (entry, reason) => {
285
+ this.leaseBroker.onDaemonRuntimeDisposed(entry.workspaceName, entry.sessionId, reason);
286
+ if (entry.worktreeId !== undefined) {
287
+ this.worktreeRetention.onRuntimeDisposed(entry.workspaceName, entry.worktreeId);
288
+ }
289
+ },
290
+ });
291
+ this.worktrees = new WorktreeManager({
292
+ agentDir: services.agentDir,
293
+ stateManager: this.stateManager,
294
+ auditLogger: services.auditLogger,
295
+ hasActiveRuntimeForSession: (workspaceName, sessionId) => this.runtimes.findOwner(workspaceName, sessionId) !== undefined,
296
+ flushState: () => services.state.flush(),
297
+ });
298
+ this.worktreeRetention = new WorktreeRetentionSweeper({
299
+ manager: this.worktrees,
300
+ stateManager: this.stateManager,
301
+ auditLogger: services.auditLogger,
302
+ getRetentionPolicy: () => resolveWorktreeCleanupPolicy(services.state.state.settings).retention,
303
+ });
304
+ this.viewerFeeds = new ViewerFeedRegistry({
305
+ sendTo: (connectionId, event) => services.controlServer.sendTo(connectionId, event),
306
+ });
307
+ this.leaseBroker = new LeaseBroker({
308
+ isRuntimeStreaming: (workspaceName, sessionId) => this.runtimes.findOwner(workspaceName, sessionId)?.runtime.session.isBusy ?? false,
309
+ waitForRuntimeIdle: async (workspaceName, sessionId) => {
310
+ await this.runtimes.findOwner(workspaceName, sessionId)?.runtime.session.waitForIdle();
311
+ },
312
+ disposeRuntime: async (workspaceName, sessionId, reason) => {
313
+ const owner = this.runtimes.findOwner(workspaceName, sessionId);
314
+ if (owner) {
315
+ await this.runtimes.stopEntry(owner, reason);
316
+ }
317
+ },
318
+ closePhoneStreams: async (workspaceName, sessionId, reason) => {
319
+ await this.closeActiveStreamsForConversationKey(workspaceName, sessionId, reason);
320
+ },
321
+ closeRelays: (record, reason) => {
322
+ for (const relayId of Array.from(record.relayIds)) {
323
+ this.relays.closeActive(relayId, reason);
324
+ // Unredeemed offers must not linger until the 10s token expiry:
325
+ // fail the phone's deferred handshake immediately so it retries
326
+ // against the new lease owner.
327
+ this.abortPendingRelay(relayId, reason, "relay offer cancelled; retry", RELAY_OFFER_RETRY_AFTER_MS);
328
+ }
329
+ },
330
+ onDrainStarted: (record, viewerFeedId) => {
331
+ const owner = this.runtimes.findOwner(record.workspaceName, record.sessionId);
332
+ if (owner && record.tuiConnectionId) {
333
+ this.viewerFeeds.start(viewerFeedId, record.tuiConnectionId, owner.runtime.session);
334
+ }
335
+ },
336
+ onDrainEnded: (_record, viewerFeedId, reason) => {
337
+ this.viewerFeeds.end(viewerFeedId, reason);
338
+ },
339
+ audit: (event) => {
340
+ void this.logAudit({
341
+ type: event.type,
342
+ workspace: event.workspaceName,
343
+ success: true,
344
+ details: { sessionId: event.sessionId, ...event.details },
345
+ });
346
+ },
347
+ });
348
+ let readyResolve = () => { };
349
+ let readyReject = () => { };
350
+ const readyPromise = new Promise((resolve, reject) => {
351
+ readyResolve = resolve;
352
+ readyReject = reject;
353
+ });
354
+ readyPromise.catch(() => { });
355
+ this.ready = { promise: readyPromise, resolve: readyResolve, reject: readyReject };
356
+ }
357
+ requireEngine() {
358
+ if (!this.engine) {
359
+ throw new Error("iroh host engine is not ready");
360
+ }
361
+ return this.engine;
362
+ }
363
+ async pruneWorktreesOnStart() {
364
+ if (!resolveWorktreeCleanupPolicy(this.services.state.state.settings).pruneOnStart) {
365
+ return;
366
+ }
367
+ try {
368
+ const state = await this.stateManager.getState();
369
+ const workspacesWithRecords = new Set((state.worktrees ?? []).map((worktree) => worktree.workspaceName));
370
+ for (const workspace of state.workspaces) {
371
+ // Skip workspaces with neither records nor checkout directories: no git
372
+ // subprocesses or audit noise on the common no-worktrees start.
373
+ if (!workspacesWithRecords.has(workspace.name) &&
374
+ !existsSync(getWorkspaceWorktreesDir(this.services.agentDir, workspace.path))) {
375
+ continue;
376
+ }
377
+ try {
378
+ await this.worktrees.prune(workspace);
379
+ }
380
+ catch (error) {
381
+ this.log("warn", "worktree prune failed on start", {
382
+ workspace: workspace.name,
383
+ error: error instanceof Error ? error.message : String(error),
384
+ });
385
+ }
386
+ }
387
+ }
388
+ catch {
389
+ // Startup prune is best-effort; a manual `volt remote worktree prune` covers it.
390
+ }
391
+ }
392
+ getResponseContext() {
393
+ return {
394
+ hostNodeId: this.hostNodeId,
395
+ relayMode: this.relayMode,
396
+ ...(this.relayMode === "production" ? { relayUrls: this.relayUrls } : {}),
397
+ };
398
+ }
399
+ async isAuthorizationGrantCurrent(authorization) {
400
+ const client = await this.stateManager.getClient(authorization.client.nodeId);
401
+ return client?.rpcGrant.revision === authorization.client.rpcGrant.revision;
402
+ }
403
+ getCommandContext(conversation) {
404
+ return {
405
+ agentDir: this.services.agentDir,
406
+ auditLogger: this.services.auditLogger,
407
+ hostEngine: this.engine,
408
+ stateManager: this.stateManager,
409
+ sessionListCursors: this.sessionListCursors,
410
+ sessionListCursorTtlMs: REMOTE_SESSION_LIST_CURSOR_TTL_MS,
411
+ listRuntimeStates: (workspaceName) => {
412
+ const states = new Map();
413
+ for (const record of this.leaseBroker.list()) {
414
+ if (record.workspaceName === workspaceName && record.state !== "unowned") {
415
+ states.set(record.sessionId, record.state);
416
+ }
417
+ }
418
+ return states;
419
+ },
420
+ keepAwake: this.services.keepAwake,
421
+ onKeepAwakeSetting: (enabled) => this.services.state.updateSettings({ keepAwakeEnabled: enabled }),
422
+ webSearchKey: this.services.webSearchKey,
423
+ createWorktreeBackend: (workspace) => this.createWorktreeRpcBackend(workspace),
424
+ onWorkspaceUnregistered: async (workspaceName) => {
425
+ // Unregistering the conversation's own workspace keeps the requesting
426
+ // stream and runtime alive so the response can still be delivered
427
+ // (mirrors the workspace-management stream path).
428
+ const excludeOwn = conversation !== undefined && workspaceName === conversation.workspaceName;
429
+ const workspacePath = excludeOwn ? conversation.workspacePath : undefined;
430
+ await this.cleanupUnregisteredWorkspace(workspaceName, excludeOwn
431
+ ? { streamEntry: conversation.streamEntry, runtimeEntry: conversation.entry, workspacePath }
432
+ : {});
433
+ },
434
+ ...(conversation === undefined
435
+ ? {}
436
+ : {
437
+ isDraining: () => this.leaseBroker.isDraining(conversation.workspaceName, conversation.entry.sessionId),
438
+ isSubagentSession: () => conversation.entry.subagentId !== undefined || conversation.entry.parentSessionId !== undefined,
439
+ }),
440
+ };
441
+ }
442
+ async start() {
443
+ let endpoint;
444
+ if (this.relayConfigWarning !== undefined) {
445
+ this.log("warn", this.relayConfigWarning);
446
+ }
447
+ // Reconcile worktree records/checkouts before the endpoint starts taking
448
+ // conversations (design §5.3 pruneOnStart; default on, quarantine-only).
449
+ await this.pruneWorktreesOnStart();
450
+ try {
451
+ const builder = this.iroh.Endpoint.builder();
452
+ if (this.relayMode === "development") {
453
+ this.log("warn", "using public n0 relays (development only; unset VOLT_IROH_RELAY_MODE for the Volt relays)");
454
+ this.iroh.presetN0(builder);
455
+ }
456
+ else if (this.relayMode === "production") {
457
+ if (this.relayUrls.length === 0) {
458
+ throw new Error("relayMode production requires relay URLs (config.relayUrls or VOLT_IROH_RELAY_URLS)");
459
+ }
460
+ this.iroh.presetN0DisableRelay(builder);
461
+ if (this.relayAuthToken !== undefined) {
462
+ const relayMap = this.iroh.RelayMap.empty();
463
+ for (const url of this.relayUrls) {
464
+ relayMap.insert({ url, authToken: this.relayAuthToken });
465
+ }
466
+ builder.relayMode(this.iroh.RelayMode.custom(relayMap));
467
+ }
468
+ else {
469
+ builder.relayMode(this.iroh.RelayMode.customFromUrls(this.relayUrls));
470
+ }
471
+ }
472
+ else {
473
+ this.iroh.presetMinimal(builder);
474
+ builder.relayMode(this.iroh.RelayMode.disabled());
475
+ }
476
+ const secretKey = this.services.state.state.irohSecretKey;
477
+ if (secretKey) {
478
+ builder.secretKey(secretKey);
479
+ }
480
+ builder.alpns([Array.from(Buffer.from(IROH_REMOTE_ALPN, "utf8"))]);
481
+ endpoint = await builder.bind();
482
+ if (!secretKey) {
483
+ const boundKey = endpoint.secretKey().toBytes();
484
+ this.services.state.setHostState({
485
+ ...this.services.state.getHostState(),
486
+ hostSecretKey: boundKey,
487
+ });
488
+ // Persist the freshly minted identity synchronously before the accept
489
+ // loop starts taking pairings. A crash/SIGKILL inside the 250ms debounce
490
+ // window would otherwise lose the key, and every phone paired against
491
+ // this endpoint would be talking to a node id the daemon can never
492
+ // reproduce on restart.
493
+ await this.services.state.flush();
494
+ }
495
+ if (this.relayMode !== "disabled") {
496
+ await endpoint.online();
497
+ }
498
+ this.endpoint = endpoint;
499
+ this.hostNodeId = endpoint.id().toString();
500
+ this.endpointTicket = this.iroh.EndpointTicket.fromAddr(endpoint.addr()).toString();
501
+ this.engine = new IrohRemoteHostEngine({
502
+ auditLogger: this.services.auditLogger,
503
+ classifyWorkspaceAvailability: getIrohRemoteWorkspaceAvailabilityStatus,
504
+ hostNodeId: this.hostNodeId,
505
+ relayMode: this.relayMode,
506
+ ...(this.relayMode === "production" ? { relayUrls: this.relayUrls } : {}),
507
+ stateManager: this.stateManager,
508
+ validateWorkspace: async (workspace) => (await getIrohRemoteWorkspaceAvailabilityStatus(workspace)) === "available",
509
+ workspace: { name: "voltd", path: this.services.agentDir },
510
+ });
511
+ this.ready.resolve();
512
+ this.log("info", `iroh endpoint online`, {
513
+ hostNodeId: this.hostNodeId,
514
+ relayMode: this.relayMode,
515
+ ...(this.relayMode === "production" ? { relayUrls: this.relayUrls } : {}),
516
+ });
517
+ void this.acceptLoop(endpoint);
518
+ }
519
+ catch (error) {
520
+ if (endpoint) {
521
+ // bind() succeeded but a later start step (e.g. online()) failed before
522
+ // the endpoint was adopted; close it so its QUIC socket is not leaked.
523
+ try {
524
+ await endpoint.close();
525
+ }
526
+ catch { }
527
+ if (this.endpoint === endpoint) {
528
+ this.endpoint = undefined;
529
+ }
530
+ }
531
+ this.ready.reject(error);
532
+ this.log("error", `failed to start iroh endpoint: ${error instanceof Error ? error.message : String(error)}`);
533
+ }
534
+ }
535
+ async acceptLoop(endpoint) {
536
+ while (!this.shuttingDown) {
537
+ let incoming;
538
+ try {
539
+ incoming = await endpoint.acceptNext();
540
+ }
541
+ catch (error) {
542
+ if (this.shuttingDown) {
543
+ break;
544
+ }
545
+ this.log("error", `accept failed: ${error instanceof Error ? error.message : String(error)}`);
546
+ continue;
547
+ }
548
+ if (!incoming) {
549
+ break;
550
+ }
551
+ const connectionAdmission = this.resourceGuard.tryAcquireConnectionTask();
552
+ if (!connectionAdmission.ok) {
553
+ let refused = true;
554
+ try {
555
+ await incoming.refuse();
556
+ }
557
+ catch {
558
+ refused = false;
559
+ }
560
+ await this.logAudit({
561
+ type: "iroh_security_connection_limit",
562
+ success: false,
563
+ error: "incoming connection refused at daemon connection-task limit",
564
+ details: {
565
+ limit: connectionAdmission.limit,
566
+ refused,
567
+ scope: connectionAdmission.scope,
568
+ },
569
+ });
570
+ continue;
571
+ }
572
+ const task = this.handleConnection(incoming)
573
+ .catch((error) => {
574
+ if (!isExpectedApplicationClose(error)) {
575
+ this.log("error", `connection error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
576
+ }
577
+ })
578
+ .finally(() => {
579
+ this.connectionTasks.delete(task);
580
+ connectionAdmission.lease.release();
581
+ });
582
+ this.connectionTasks.add(task);
583
+ }
584
+ }
585
+ async handleConnection(incoming) {
586
+ let connection;
587
+ try {
588
+ const accepting = await incoming.accept();
589
+ connection = await accepting.connect();
590
+ }
591
+ catch {
592
+ await this.logAudit({
593
+ type: "iroh_security_transport_rejected",
594
+ success: false,
595
+ error: "incoming transport handshake failed",
596
+ details: { phase: "transport_connect" },
597
+ });
598
+ return;
599
+ }
600
+ try {
601
+ connection.setMaxConcurrentBiStreams(BigInt(MAX_CONCURRENT_STREAMS_PER_CONNECTION));
602
+ }
603
+ catch {
604
+ closeConnection(connection, "stream_limit_configuration_failed");
605
+ await waitForConnectionClose(connection);
606
+ await this.logAudit({
607
+ type: "iroh_security_transport_rejected",
608
+ success: false,
609
+ error: "connected transport could not enforce the inbound stream limit",
610
+ details: { phase: "stream_limit_configuration" },
611
+ });
612
+ return;
613
+ }
614
+ let remoteId;
615
+ try {
616
+ remoteId = connection.remoteId().toString();
617
+ }
618
+ catch {
619
+ closeConnection(connection, "invalid_remote_identity");
620
+ await waitForConnectionClose(connection);
621
+ await this.logAudit({
622
+ type: "iroh_security_transport_rejected",
623
+ success: false,
624
+ error: "connected transport did not expose a valid remote identity",
625
+ details: { phase: "remote_identity" },
626
+ });
627
+ return;
628
+ }
629
+ const nodeConnectionAdmission = this.resourceGuard.tryAcquireNodeConnection(remoteId);
630
+ if (!nodeConnectionAdmission.ok) {
631
+ closeConnection(connection, "node_connection_limit");
632
+ await waitForConnectionClose(connection);
633
+ await this.logAudit({
634
+ type: "iroh_security_connection_limit",
635
+ clientNodeId: remoteId,
636
+ success: false,
637
+ error: "connection refused at per-node connection limit",
638
+ details: { limit: nodeConnectionAdmission.limit, scope: nodeConnectionAdmission.scope },
639
+ });
640
+ return;
641
+ }
642
+ const unauthenticatedAdmission = this.resourceGuard.tryAcquireUnauthenticatedConnection(remoteId);
643
+ if (!unauthenticatedAdmission.ok) {
644
+ nodeConnectionAdmission.lease.release();
645
+ closeConnection(connection, "unauthenticated_connection_limit");
646
+ await waitForConnectionClose(connection);
647
+ await this.logAudit({
648
+ type: "iroh_security_unauthenticated_connection_limit",
649
+ clientNodeId: remoteId,
650
+ success: false,
651
+ error: "unauthenticated connection refused at admission limit",
652
+ details: { limit: unauthenticatedAdmission.limit, scope: unauthenticatedAdmission.scope },
653
+ });
654
+ return;
655
+ }
656
+ const connectionId = `conn-${++activeConnectionSequence}`;
657
+ const removeClientConnection = this.registerClientConnection(remoteId, connection, connectionId);
658
+ const streamTasks = new Set();
659
+ let acceptedStreamCount = 0;
660
+ let closeRequested = false;
661
+ let authenticated = false;
662
+ const unauthenticatedTimer = setTimeout(() => {
663
+ if (authenticated || closeRequested)
664
+ return;
665
+ closeRequested = true;
666
+ closeConnection(connection, "handshake_timeout");
667
+ void this.logAudit({
668
+ type: "iroh_security_handshake_timeout",
669
+ clientNodeId: remoteId,
670
+ success: false,
671
+ error: "connection did not authenticate before the handshake deadline",
672
+ details: { connectionId, timeoutMs: IROH_UNAUTHENTICATED_CONNECTION_TIMEOUT_MS },
673
+ });
674
+ }, IROH_UNAUTHENTICATED_CONNECTION_TIMEOUT_MS);
675
+ unauthenticatedTimer.unref?.();
676
+ const markAuthenticated = async () => {
677
+ if (authenticated)
678
+ return true;
679
+ if (closeRequested)
680
+ return false;
681
+ authenticated = true;
682
+ clearTimeout(unauthenticatedTimer);
683
+ unauthenticatedAdmission.lease.release();
684
+ this.log("info", `client connection opened: ${remoteId} (${connectionId})`);
685
+ await this.logAudit({
686
+ type: "client_connected",
687
+ clientNodeId: remoteId,
688
+ success: true,
689
+ details: { connectionId },
690
+ });
691
+ return true;
692
+ };
693
+ const requestCloseWhenIdle = () => {
694
+ if (closeRequested || acceptedStreamCount === 0 || streamTasks.size > 0) {
695
+ return;
696
+ }
697
+ closeRequested = true;
698
+ closeConnection(connection, "done");
699
+ };
700
+ try {
701
+ while (!closeRequested) {
702
+ const stream = await (!authenticated
703
+ ? withTimeout(connection.acceptBi(), DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS, "handshake timed out")
704
+ : connection.acceptBi());
705
+ acceptedStreamCount++;
706
+ if (streamTasks.size >= MAX_CONCURRENT_STREAMS_PER_CONNECTION) {
707
+ // One connection is holding too many concurrent streams open. Refuse
708
+ // further work and close the connection rather than let
709
+ // it exhaust daemon resources; the just-accepted stream is torn down
710
+ // with the connection. A legitimate client never reaches this.
711
+ closeRequested = true;
712
+ closeConnection(connection, "stream_limit_exceeded");
713
+ await this.logAudit({
714
+ type: "iroh_security_stream_limit",
715
+ clientNodeId: remoteId,
716
+ success: false,
717
+ error: "connection exceeded concurrent stream limit",
718
+ details: { connectionId, limit: MAX_CONCURRENT_STREAMS_PER_CONNECTION, scope: "connection" },
719
+ });
720
+ break;
721
+ }
722
+ const streamAdmission = this.resourceGuard.tryAcquireActiveStream(remoteId);
723
+ if (!streamAdmission.ok) {
724
+ closeIrohRemoteStream(stream, "stream_limit_exceeded");
725
+ await this.logAudit({
726
+ type: "iroh_security_stream_limit",
727
+ clientNodeId: remoteId,
728
+ success: false,
729
+ error: "stream refused at daemon active-stream limit",
730
+ details: { connectionId, limit: streamAdmission.limit, scope: streamAdmission.scope },
731
+ });
732
+ requestCloseWhenIdle();
733
+ continue;
734
+ }
735
+ const streamId = `stream-${++activeStreamSequence}`;
736
+ const task = this.handleConnectionStream(stream, connection, remoteId, connectionId, streamId, markAuthenticated)
737
+ .catch(async (error) => {
738
+ if (!isExpectedApplicationClose(error)) {
739
+ if (authenticated) {
740
+ this.log("error", `stream error: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
741
+ }
742
+ else {
743
+ await this.logAudit({
744
+ type: "iroh_security_transport_rejected",
745
+ clientNodeId: remoteId,
746
+ success: false,
747
+ error: "unauthenticated stream failed",
748
+ details: { connectionId, phase: "stream_handshake" },
749
+ });
750
+ }
751
+ }
752
+ })
753
+ .finally(() => {
754
+ streamAdmission.lease.release();
755
+ streamTasks.delete(task);
756
+ requestCloseWhenIdle();
757
+ });
758
+ streamTasks.add(task);
759
+ }
760
+ }
761
+ catch (error) {
762
+ if (acceptedStreamCount === 0 && authenticated) {
763
+ throw error;
764
+ }
765
+ if (acceptedStreamCount === 0 && !closeRequested) {
766
+ await this.logAudit({
767
+ type: "iroh_security_handshake_timeout",
768
+ clientNodeId: remoteId,
769
+ success: false,
770
+ error: "connection closed or timed out before opening a handshake stream",
771
+ details: { connectionId, timeoutMs: DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS },
772
+ });
773
+ }
774
+ }
775
+ finally {
776
+ clearTimeout(unauthenticatedTimer);
777
+ unauthenticatedAdmission.lease.release();
778
+ nodeConnectionAdmission.lease.release();
779
+ await this.closeActiveStreamsForConnection(connectionId, "connection_closed");
780
+ await Promise.allSettled(streamTasks);
781
+ removeClientConnection();
782
+ if (!closeRequested) {
783
+ closeConnection(connection, "done");
784
+ }
785
+ await waitForConnectionClose(connection);
786
+ if (authenticated) {
787
+ this.log("info", `client connection closed: ${remoteId} (${connectionId})`);
788
+ await this.logAudit({
789
+ type: "client_disconnected",
790
+ clientNodeId: remoteId,
791
+ success: true,
792
+ details: { connectionId },
793
+ });
794
+ }
795
+ }
796
+ }
797
+ async handleConnectionStream(stream, connection, remoteId, connectionId, streamId, markAuthenticated) {
798
+ const engine = this.requireEngine();
799
+ const handshakeAdmission = this.resourceGuard.tryAcquireHandshake(remoteId);
800
+ if (!handshakeAdmission.ok) {
801
+ closeIrohRemoteStream(stream, "handshake_limit_exceeded");
802
+ await this.logAudit({
803
+ type: "iroh_security_handshake_limit",
804
+ clientNodeId: remoteId,
805
+ success: false,
806
+ error: "stream refused at concurrent handshake limit",
807
+ details: { connectionId, limit: handshakeAdmission.limit, scope: handshakeAdmission.scope },
808
+ });
809
+ return;
810
+ }
811
+ let handshake;
812
+ try {
813
+ handshake = await engine.readHandshake(stream, remoteId, {
814
+ child: "volt",
815
+ maxLineBytes: DEFAULT_IROH_REMOTE_HANDSHAKE_MAX_LINE_BYTES,
816
+ timeoutMs: DEFAULT_IROH_REMOTE_HANDSHAKE_TIMEOUT_MS,
817
+ writeSuccessResponse: false,
818
+ });
819
+ }
820
+ finally {
821
+ handshakeAdmission.lease.release();
822
+ }
823
+ if (!handshake.ok) {
824
+ if (handshake.response.outcome === "workspace_authorization_removed" &&
825
+ typeof handshake.response.workspace === "string") {
826
+ await this.closeWorkspaceAuthorizationRemovedStreams(remoteId, handshake.response.workspace);
827
+ }
828
+ await Promise.resolve(stream.send.finish?.()).catch(() => { });
829
+ await Promise.resolve(stream.recv.stop?.(0n)).catch(() => { });
830
+ return;
831
+ }
832
+ if (!(await markAuthenticated())) {
833
+ closeIrohRemoteStream(stream, "handshake_timeout");
834
+ return;
835
+ }
836
+ const streamCapability = getIrohRemoteStreamCapability({
837
+ mode: handshake.hello.mode,
838
+ ...(handshake.hello.mode === "workspaceManagement"
839
+ ? { purpose: handshake.hello.workspaceManagement.purpose }
840
+ : {}),
841
+ });
842
+ if (streamCapability !== undefined &&
843
+ !hasIrohRemoteRpcCapability(parseIrohRemoteRpcGrant(handshake.authorization.client.rpcGrant, "client rpcGrant"), streamCapability)) {
844
+ await writeIrohRemoteHandshakeResponse(stream.send, createIrohRemoteHandshakeFailure(`rpc_capability_denied: ${streamCapability}`, {
845
+ hostNodeId: this.hostNodeId,
846
+ workspace: handshake.authorization.workspace.name,
847
+ }));
848
+ closeIrohRemoteStream(stream, "rpc_capability_denied");
849
+ return;
850
+ }
851
+ this.notifyPairingConsumed(handshake, remoteId);
852
+ if (handshake.authorization.paired) {
853
+ this.log("info", `paired client stream: ${handshake.authorization.client.label} (${remoteId}, ${streamId})`);
854
+ }
855
+ if (handshake.hello.mode === "workspaceDiscovery") {
856
+ await this.runWorkspaceDiscovery(stream, handshake, connection, connectionId, streamId);
857
+ return;
858
+ }
859
+ if (handshake.hello.mode === "workspaceManagement") {
860
+ if (handshake.hello.workspaceManagement.purpose === "manage_worktrees") {
861
+ await this.runWorktreeManagement(stream, handshake, connection, connectionId, streamId);
862
+ return;
863
+ }
864
+ await this.runWorkspaceManagement(stream, handshake, connection, connectionId, streamId);
865
+ return;
866
+ }
867
+ await this.runIntegratedConversation(stream, handshake, connection, connectionId, streamId);
868
+ }
869
+ // ==========================================================================
870
+ // Workspace streams
871
+ // ==========================================================================
872
+ registerActiveStream(authorization, sessionId, stream, connection, connectionId, streamId, details = {}) {
873
+ const entry = {
874
+ clientNodeId: authorization.client.nodeId,
875
+ connectionId,
876
+ sessionId,
877
+ streamId,
878
+ workspaceName: authorization.workspace.name,
879
+ close: (reason) => this.closeStreamWithTerminal(stream, reason, {
880
+ authorization,
881
+ sessionId: Object.hasOwn(details, "terminalSessionId") ? details.terminalSessionId : entry.sessionId,
882
+ }),
883
+ closeConnection: (reason) => closeConnection(connection, reason),
884
+ write: (value) => writeIrohRemoteJsonLine(stream.send, value, authorization, details.sanitizerOverrides ?? {}),
885
+ };
886
+ const remove = this.activeStreams.register(entry);
887
+ return { entry, remove };
888
+ }
889
+ // ==========================================================================
890
+ // iOS theme token push (§9.5) — flag off by default, capability gated
891
+ // ==========================================================================
892
+ isThemeTokenPushEnabled() {
893
+ return this.services.state.state.settings.themeTokenPush === true || process.env.VOLT_HOST_THEME_TOKENS === "1";
894
+ }
895
+ /** Send the current sanitized theme tokens to one capable stream. */
896
+ pushThemeTokensToStream(entry) {
897
+ if (!this.isThemeTokenPushEnabled() || !entry.capabilities?.has(HOST_THEME_TOKENS_FEATURE)) {
898
+ return;
899
+ }
900
+ const frame = createHostThemeTokensFrame(getCurrentThemeName() ?? "dark", getResolvedThemeColors());
901
+ void Promise.resolve(entry.write?.(frame)).catch(() => { });
902
+ }
903
+ /** Theme changed: fan the new tokens out to every capable phone stream. */
904
+ onThemeChanged() {
905
+ if (!this.isThemeTokenPushEnabled()) {
906
+ return;
907
+ }
908
+ for (const entry of this.activeStreams.allEntries()) {
909
+ this.pushThemeTokensToStream(entry);
910
+ }
911
+ }
912
+ /**
913
+ * Keep-awake status changed (control toggle, phone toggle, or degradation):
914
+ * fan the new state to every phone stream. Clients that ignore the frame are
915
+ * fully supported, so no capability gating.
916
+ */
917
+ onKeepAwakeChanged() {
918
+ const frame = {
919
+ type: "keep_awake_changed",
920
+ data: { keepAwake: toRpcKeepAwakeStatus(this.services.keepAwake.status) },
921
+ };
922
+ for (const entry of this.activeStreams.allEntries()) {
923
+ void Promise.resolve(entry.write?.(frame)).catch(() => { });
924
+ }
925
+ }
926
+ async closeStreamWithTerminal(stream, reason, terminal) {
927
+ const terminalReason = getRemoteTerminalReason(reason);
928
+ if (terminalReason) {
929
+ await writeIrohRemoteJsonLine(stream.send, {
930
+ type: "remote_terminal",
931
+ reason: terminalReason,
932
+ workspace: terminal.authorization.workspace.name,
933
+ ...(terminal.sessionId === undefined ? {} : { sessionId: terminal.sessionId }),
934
+ hostNodeId: this.hostNodeId,
935
+ }, terminal.authorization).catch(() => { });
936
+ }
937
+ closeIrohRemoteStream(stream, reason);
938
+ }
939
+ async runWorkspaceDiscovery(stream, handshake, connection, connectionId, streamId) {
940
+ await writeIrohRemoteHandshakeResponse(stream.send, handshake.response);
941
+ const activeStream = this.registerActiveStream(handshake.authorization, WORKSPACE_DISCOVERY_STREAM_SESSION_ID, stream, connection, connectionId, streamId, { terminalSessionId: undefined });
942
+ try {
943
+ await runWorkspaceDiscoveryStream({
944
+ stream,
945
+ initialInput: handshake.initialInput,
946
+ authorization: handshake.authorization,
947
+ isRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(handshake.authorization),
948
+ closeStream: (reason) => closeIrohRemoteStream(stream, reason),
949
+ }, { commandContext: this.getCommandContext() });
950
+ }
951
+ finally {
952
+ activeStream.remove();
953
+ }
954
+ }
955
+ async runWorkspaceManagement(stream, handshake, connection, connectionId, streamId) {
956
+ await writeIrohRemoteHandshakeResponse(stream.send, handshake.response);
957
+ const activeStream = this.registerActiveStream(handshake.authorization, WORKSPACE_MANAGEMENT_STREAM_SESSION_ID, stream, connection, connectionId, streamId, { terminalSessionId: undefined });
958
+ try {
959
+ await runWorkspaceManagementStream({
960
+ stream,
961
+ initialInput: handshake.initialInput,
962
+ authorization: handshake.authorization,
963
+ isRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(handshake.authorization),
964
+ closeStream: (reason) => {
965
+ activeStream.remove();
966
+ closeIrohRemoteStream(stream, reason);
967
+ },
968
+ }, {
969
+ auditLogger: this.services.auditLogger,
970
+ commandContext: this.getCommandContext(),
971
+ unregisterWorkspace: async (workspaceName) => {
972
+ let removedWorkspace;
973
+ try {
974
+ removedWorkspace = await this.stateManager.unregisterWorkspace(workspaceName);
975
+ }
976
+ catch (error) {
977
+ if (!isIrohRemoteWorkspaceHasWorktreesError(error)) {
978
+ throw error;
979
+ }
980
+ return {
981
+ ok: false,
982
+ error: IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,
983
+ details: {
984
+ worktreeCount: error.worktreeIds.length,
985
+ worktreeIds: error.worktreeIds,
986
+ },
987
+ };
988
+ }
989
+ if (!removedWorkspace) {
990
+ return { ok: false, error: "workspace_unregistered" };
991
+ }
992
+ this.engine?.clearPairingSecretForWorkspace(workspaceName);
993
+ const { closedStreamCount, stoppedRuntimeCount } = await this.cleanupUnregisteredWorkspace(workspaceName, { streamEntry: activeStream.entry, workspacePath: removedWorkspace.path });
994
+ return { ok: true, closedStreamCount, stoppedRuntimeCount };
995
+ },
996
+ });
997
+ }
998
+ finally {
999
+ activeStream.remove();
1000
+ }
1001
+ }
1002
+ /** Serve a manage_worktrees workspaceManagement stream (worktrees.v1). */
1003
+ async runWorktreeManagement(stream, handshake, connection, connectionId, streamId) {
1004
+ await writeIrohRemoteHandshakeResponse(stream.send, handshake.response);
1005
+ const sanitizerOverrides = {
1006
+ additionalRedactedPaths: [getWorktreesRoot(this.services.agentDir)],
1007
+ };
1008
+ const activeStream = this.registerActiveStream(handshake.authorization, WORKSPACE_MANAGEMENT_STREAM_SESSION_ID, stream, connection, connectionId, streamId, { terminalSessionId: undefined, sanitizerOverrides });
1009
+ try {
1010
+ await runWorktreeManagementStream({
1011
+ stream,
1012
+ initialInput: handshake.initialInput,
1013
+ authorization: handshake.authorization,
1014
+ isRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(handshake.authorization),
1015
+ closeStream: (reason) => closeIrohRemoteStream(stream, reason),
1016
+ }, {
1017
+ auditLogger: this.services.auditLogger,
1018
+ additionalRedactedPaths: sanitizerOverrides.additionalRedactedPaths,
1019
+ worktrees: this.createWorktreeRpcBackend(handshake.authorization.workspace),
1020
+ });
1021
+ }
1022
+ finally {
1023
+ activeStream.remove();
1024
+ }
1025
+ }
1026
+ /** Backend for the worktree RPC helpers, bound to the stream's authorized workspace. */
1027
+ createWorktreeRpcBackend(workspace) {
1028
+ return {
1029
+ createWorktree: async (_workspaceName, options) => {
1030
+ const created = await this.worktrees.create(workspace, options);
1031
+ if (!created.ok) {
1032
+ return {
1033
+ ok: false,
1034
+ error: created.error,
1035
+ ...(created.detail === undefined ? {} : { detail: created.detail }),
1036
+ };
1037
+ }
1038
+ return { ok: true, worktree: created.worktree };
1039
+ },
1040
+ listWorktrees: async () => ({ ok: true, worktrees: await this.worktrees.list(workspace) }),
1041
+ removeWorktree: async (_workspaceName, worktreeId, force) => this.removeWorkspaceWorktree(workspace, worktreeId, force),
1042
+ };
1043
+ }
1044
+ /**
1045
+ * Runtime-aware worktree removal: refuses busy worktrees without force; with
1046
+ * force, closes bound phone streams and stops bound runtimes first.
1047
+ */
1048
+ async removeWorkspaceWorktree(workspace, worktreeId, force) {
1049
+ const record = await this.worktrees.findWorktree(workspace.name, worktreeId);
1050
+ if (!record) {
1051
+ return { ok: false, error: "worktree_not_found" };
1052
+ }
1053
+ let stoppedRuntimeCount = 0;
1054
+ let closedStreamCount = 0;
1055
+ const boundEntries = record.sessionIds
1056
+ .map((sessionId) => this.runtimes.findOwner(workspace.name, sessionId))
1057
+ .filter((entry) => entry !== undefined);
1058
+ if (boundEntries.length > 0) {
1059
+ if (!force) {
1060
+ return { ok: false, error: "worktree_busy" };
1061
+ }
1062
+ for (const entry of boundEntries) {
1063
+ closedStreamCount += await this.closeActiveStreamsForConversationKey(workspace.name, entry.sessionId, "worktree_removed");
1064
+ await this.runtimes.stopEntry(entry, "worktree_removed");
1065
+ stoppedRuntimeCount++;
1066
+ }
1067
+ }
1068
+ const removed = await this.worktrees.remove(workspace, worktreeId, { force });
1069
+ if (!removed.ok) {
1070
+ return removed;
1071
+ }
1072
+ return { ok: true, stoppedRuntimeCount, closedStreamCount };
1073
+ }
1074
+ /**
1075
+ * Worktree resolution for conversation opens: explicit worktreeId on "new"
1076
+ * (must exist AND be on disk), persisted binding on resume (missing checkout
1077
+ * fails with session_unavailable). Availability is an open-time failure, not
1078
+ * an authorization failure.
1079
+ */
1080
+ async resolveConversationWorktree(workspaceName, hello, targetSessionId) {
1081
+ if (hello.mode !== "conversation") {
1082
+ return undefined;
1083
+ }
1084
+ if (hello.conversation.target === "new") {
1085
+ const worktreeId = hello.conversation.worktreeId;
1086
+ if (worktreeId === undefined) {
1087
+ return undefined;
1088
+ }
1089
+ const worktree = await this.worktrees.findWorktree(workspaceName, worktreeId);
1090
+ if (!worktree || !existsSync(worktree.path)) {
1091
+ throw createConversationOpenError("invalid_conversation_target", "unknown or unavailable worktree", {
1092
+ workspace: workspaceName,
1093
+ });
1094
+ }
1095
+ return worktree;
1096
+ }
1097
+ if (targetSessionId === undefined) {
1098
+ return undefined;
1099
+ }
1100
+ const worktree = await this.worktrees.resolveSessionWorktree(workspaceName, targetSessionId);
1101
+ if (worktree === undefined) {
1102
+ return undefined;
1103
+ }
1104
+ if (!existsSync(worktree.path)) {
1105
+ throw createConversationOpenError("session_unavailable", "worktree checkout is unavailable", {
1106
+ workspace: workspaceName,
1107
+ sessionId: targetSessionId,
1108
+ });
1109
+ }
1110
+ return worktree;
1111
+ }
1112
+ async resolveConversationWorkingDirectory(options) {
1113
+ if (options.worktree === undefined) {
1114
+ const parentDirectory = await this.worktrees.validateWorkingDirectory(options.workspace, options.workingDirectory);
1115
+ if (!parentDirectory.ok) {
1116
+ const message = parentDirectory.detail ?? parentDirectory.error;
1117
+ throw createConversationOpenError("invalid_conversation_target", message, {
1118
+ workspace: options.workspace.name,
1119
+ });
1120
+ }
1121
+ return parentDirectory.directory;
1122
+ }
1123
+ const worktreeDirectory = await this.worktrees.resolveWorktreeWorkingDirectory(options.workspace, options.worktree, options.workingDirectory);
1124
+ if (!worktreeDirectory.ok) {
1125
+ throw createConversationOpenError("invalid_conversation_target", worktreeDirectory.detail ?? worktreeDirectory.error, {
1126
+ workspace: options.workspace.name,
1127
+ worktreeId: options.worktree.id,
1128
+ });
1129
+ }
1130
+ return worktreeDirectory.directory;
1131
+ }
1132
+ // ==========================================================================
1133
+ // Integrated conversation serving
1134
+ // ==========================================================================
1135
+ createPushNotificationDispatcher(authorization) {
1136
+ return new IrohRemotePushNotificationDispatcher({
1137
+ auditLogger: this.services.auditLogger,
1138
+ clientNodeId: authorization.client.nodeId,
1139
+ deduper: this.pushNotificationDeduper,
1140
+ relayClient: this.pushRelayClient,
1141
+ stateManager: this.stateManager,
1142
+ workspace: authorization.workspace.name,
1143
+ });
1144
+ }
1145
+ async revokeClientPushTargets(client) {
1146
+ if ((client?.pushTargets?.length ?? 0) === 0)
1147
+ return;
1148
+ try {
1149
+ const summary = await revokeIrohRemoteClientPushTargets(client, this.pushRelayClient);
1150
+ const complete = summary.failed === 0 && summary.skipped === 0;
1151
+ if (!complete) {
1152
+ this.log("warn", "remote push-target cleanup incomplete after client revoke", { ...summary });
1153
+ }
1154
+ await this.logAudit({
1155
+ type: "push_targets_revoked",
1156
+ clientNodeId: client?.nodeId,
1157
+ success: complete,
1158
+ error: complete ? undefined : "remote push-target cleanup incomplete; relay TTL remains the lifetime bound",
1159
+ details: { ...summary, remainingLifetimeBound: "relay_target_ttl" },
1160
+ });
1161
+ }
1162
+ catch (error) {
1163
+ const message = error instanceof Error ? error.message : String(error);
1164
+ this.log("warn", "remote push-target cleanup failed after client revoke", { error: message });
1165
+ await this.logAudit({
1166
+ type: "push_targets_revoked",
1167
+ clientNodeId: client?.nodeId,
1168
+ success: false,
1169
+ error: "remote push-target cleanup failed; relay TTL remains the lifetime bound",
1170
+ details: { remainingLifetimeBound: "relay_target_ttl" },
1171
+ });
1172
+ }
1173
+ }
1174
+ async sendHandshakeError(stream, error) {
1175
+ const record = (error ?? {});
1176
+ // Plain {message, ...} records (abortPendingRelay, lease re-check) must not
1177
+ // stringify to "[object Object]".
1178
+ const message = error instanceof Error ? error.message : typeof record.message === "string" ? record.message : String(error);
1179
+ const outcome = typeof record.outcome === "string" ? record.outcome : undefined;
1180
+ const workspace = typeof record.workspace === "string" ? record.workspace : undefined;
1181
+ const sessionId = typeof record.sessionId === "string" ? record.sessionId : undefined;
1182
+ const retryAfterMs = typeof record.retryAfterMs === "number" ? record.retryAfterMs : undefined;
1183
+ await writeIrohRemoteHandshakeResponse(stream.send, createIrohRemoteHandshakeFailure(message, {
1184
+ hostNodeId: this.hostNodeId,
1185
+ ...(outcome === undefined ? {} : { outcome: outcome }),
1186
+ ...(workspace === undefined ? {} : { workspace }),
1187
+ ...(sessionId === undefined ? {} : { sessionId }),
1188
+ ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
1189
+ }));
1190
+ await Promise.resolve(stream.send.finish?.()).catch(() => { });
1191
+ await Promise.resolve(stream.recv.stop?.(0n)).catch(() => { });
1192
+ }
1193
+ async rejectDuplicateActiveConnection(stream, authorization, sessionId, source = "active_stream_registry") {
1194
+ const error = "duplicate conversation connection";
1195
+ await this.logAudit({
1196
+ type: "duplicate_connection_rejected",
1197
+ clientNodeId: authorization.client.nodeId,
1198
+ workspace: authorization.workspace.name,
1199
+ success: false,
1200
+ error,
1201
+ details: {
1202
+ retryAfterMs: DUPLICATE_CONVERSATION_RETRY_AFTER_MS,
1203
+ sessionId,
1204
+ source,
1205
+ },
1206
+ });
1207
+ await writeIrohRemoteHandshakeResponse(stream.send, createIrohRemoteHandshakeFailure(error, {
1208
+ hostNodeId: this.hostNodeId,
1209
+ outcome: "duplicate_conversation_connection",
1210
+ workspace: authorization.workspace.name,
1211
+ sessionId,
1212
+ retryAfterMs: DUPLICATE_CONVERSATION_RETRY_AFTER_MS,
1213
+ }));
1214
+ await Promise.resolve(stream.send.finish?.()).catch(() => { });
1215
+ await Promise.resolve(stream.recv.stop?.(0n)).catch(() => { });
1216
+ }
1217
+ async closeReplacedActiveStreams(authorization, replacementStreamId, replacedEntries) {
1218
+ if (replacedEntries.length === 0) {
1219
+ return;
1220
+ }
1221
+ const replacedStreamIds = replacedEntries.map((entry) => entry.streamId);
1222
+ for (const entry of replacedEntries) {
1223
+ await Promise.resolve(entry.close(ACTIVE_REPLACE_CLOSE_REASON)).catch(() => { });
1224
+ }
1225
+ await this.closeIdleConnectionsForEntries(replacedEntries, ACTIVE_REPLACE_CLOSE_REASON);
1226
+ this.log("info", `client stream replaced: ${authorization.client.nodeId}/${authorization.workspace.name} (${replacedStreamIds.join(", ")} -> ${replacementStreamId})`);
1227
+ await this.logAudit({
1228
+ type: "duplicate_connection_replaced",
1229
+ clientNodeId: authorization.client.nodeId,
1230
+ workspace: authorization.workspace.name,
1231
+ success: true,
1232
+ details: {
1233
+ closeReason: ACTIVE_REPLACE_CLOSE_REASON,
1234
+ closedCount: replacedEntries.length,
1235
+ replacedStreamIds,
1236
+ replacementStreamId,
1237
+ source: "active_stream_registry",
1238
+ },
1239
+ });
1240
+ }
1241
+ /**
1242
+ * Relay a phone conversation stream to the owning TUI (§5.6): the daemon
1243
+ * has already authenticated the phone; the TUI serves the framed RPC from
1244
+ * its in-process runtime over a dedicated relay unix connection.
1245
+ */
1246
+ async relayConversationToTui(stream, handshake, connectionId, streamId, targetSessionId, tuiConnectionId) {
1247
+ const authorization = handshake.authorization;
1248
+ const workspaceName = authorization.workspace.name;
1249
+ // Duplicate handling per clientNodeId + key: duplicates already on this
1250
+ // Iroh connection are real duplicates; entries on older connections are
1251
+ // stale for this conversation and may be replaced independently of any
1252
+ // sibling subagent streams that opened first on the new connection.
1253
+ const liveRelays = this.relays.activeForConversation(authorization.client.nodeId, workspaceName, targetSessionId);
1254
+ const pendingRelays = this.relays.pendingForConversation(authorization.client.nodeId, workspaceName, targetSessionId);
1255
+ if (liveRelays.some((relay) => relay.connectionId === connectionId) ||
1256
+ pendingRelays.some((pending) => pending.connectionId === connectionId)) {
1257
+ await this.rejectDuplicateActiveConnection(stream, authorization, targetSessionId, "relay_registry");
1258
+ return;
1259
+ }
1260
+ for (const relay of liveRelays) {
1261
+ relay.close("error");
1262
+ }
1263
+ // Unredeemed offers for the same conversation on older connections are
1264
+ // superseded by this one: fail their deferred handshakes and settle them
1265
+ // (relay_closed to the TUI, lease bookkeeping) instead of leaking tasks.
1266
+ for (const pending of pendingRelays) {
1267
+ this.abortPendingRelay(pending.relayId, "error", "relay offer superseded; retry", RELAY_OFFER_RETRY_AFTER_MS);
1268
+ }
1269
+ // Resolve the concrete session target for the preamble (§3.7).
1270
+ const target = handshake.hello.mode === "conversation" && handshake.hello.conversation.target === "session"
1271
+ ? { kind: "session", sessionId: handshake.hello.conversation.sessionId }
1272
+ : { kind: "last", resumeSessionId: targetSessionId };
1273
+ // A worktree-bound session must resolve against the worktree cwd (the
1274
+ // parent-keyed session dir plus a non-matching cwd makes SessionManager.list
1275
+ // filter by header cwd, restricting resolution to that worktree's sessions).
1276
+ const boundWorktree = await this.stateManager.findWorktreeForSession(workspaceName, targetSessionId);
1277
+ const relayOwnerCapabilities = this.services.controlServer
1278
+ .connections()
1279
+ .find((controlConnection) => controlConnection.connectionId === tuiConnectionId)?.capabilities;
1280
+ if (!relayOwnerCapabilities?.has(CONTROL_RPC_GRANTS_CAPABILITY)) {
1281
+ await this.sendHandshakeError(stream, {
1282
+ message: "conversation owner is not grant-aware; retry",
1283
+ retryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,
1284
+ });
1285
+ return;
1286
+ }
1287
+ // Worktree-bound conversations are only relayed to TUIs that advertised the
1288
+ // worktrees control capability (an old TUI would sanitize with the parent
1289
+ // root and leak host paths), and never when the checkout has vanished.
1290
+ const relayGate = evaluateWorktreeRelayGate(boundWorktree, relayOwnerCapabilities, CONTROL_WORKTREES_CAPABILITY);
1291
+ if (!relayGate.ok) {
1292
+ if (relayGate.reason === "checkout_missing") {
1293
+ await this.sendHandshakeError(stream, {
1294
+ message: "worktree checkout is unavailable",
1295
+ outcome: "session_unavailable",
1296
+ workspace: workspaceName,
1297
+ sessionId: targetSessionId,
1298
+ });
1299
+ return;
1300
+ }
1301
+ await this.sendHandshakeError(stream, {
1302
+ message: "conversation owner cannot serve worktree sessions; retry",
1303
+ retryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,
1304
+ });
1305
+ return;
1306
+ }
1307
+ let resolvedTarget;
1308
+ try {
1309
+ resolvedTarget = await resolveIrohRemoteSessionTarget(target, { name: workspaceName, path: authorization.workspace.path }, createSessionManagerTargetStore(boundWorktree?.path ?? authorization.workspace.path, getDefaultSessionDir(authorization.workspace.path, this.services.agentDir), { listAll: true, preserveSessionCwd: true }));
1310
+ }
1311
+ catch (error) {
1312
+ await this.sendHandshakeError(stream, error);
1313
+ return;
1314
+ }
1315
+ const resolvedSessionManager = resolvedTarget.sessionManager;
1316
+ const resolvedSessionCwd = resolvedSessionManager.getCwd?.() ?? boundWorktree?.path ?? authorization.workspace.path;
1317
+ const relayWorkingDirectoryRelativeToRoot = getRelativeWorkingDirectoryForRoot(boundWorktree?.path ?? authorization.workspace.path, resolvedSessionCwd);
1318
+ if (relayWorkingDirectoryRelativeToRoot === null) {
1319
+ await this.sendHandshakeError(stream, {
1320
+ message: "stored session working directory is outside the authorized workspace",
1321
+ outcome: "session_unavailable",
1322
+ workspace: workspaceName,
1323
+ sessionId: targetSessionId,
1324
+ });
1325
+ return;
1326
+ }
1327
+ const relayWorkingDirectory = boundWorktree === undefined
1328
+ ? relayWorkingDirectoryRelativeToRoot
1329
+ : getRegisteredWorkingDirectoryForWorktree(boundWorktree, relayWorkingDirectoryRelativeToRoot);
1330
+ // Session-target resolution awaited; the lease can have moved (release,
1331
+ // rekey, connection loss) in the meantime. Re-check before minting so the
1332
+ // offer cannot go to a stale or dead owner.
1333
+ const lease = this.leaseBroker.lookup(workspaceName, targetSessionId);
1334
+ if (lease?.state !== "tui-owned" || lease.tuiConnectionId !== tuiConnectionId) {
1335
+ await this.sendHandshakeError(stream, {
1336
+ message: "conversation lease owner changed; retry",
1337
+ retryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,
1338
+ });
1339
+ return;
1340
+ }
1341
+ // The target-resolution awaits above can race an access update or revoke.
1342
+ // Recheck immediately before the synchronous mint so stale authorization
1343
+ // cannot create a new pending offer after control-plane invalidation acks.
1344
+ if (!(await this.isAuthorizationGrantCurrent(authorization))) {
1345
+ await this.sendHandshakeError(stream, { message: "client access changed; reconnect" });
1346
+ return;
1347
+ }
1348
+ // A sibling stream can resolve/redeem while this stream awaits target
1349
+ // resolution. Re-check immediately before minting the offer.
1350
+ const currentLiveRelays = this.relays.activeForConversation(authorization.client.nodeId, workspaceName, targetSessionId);
1351
+ const currentPendingRelays = this.relays.pendingForConversation(authorization.client.nodeId, workspaceName, targetSessionId);
1352
+ if (currentLiveRelays.some((relay) => relay.connectionId === connectionId) ||
1353
+ currentPendingRelays.some((pending) => pending.connectionId === connectionId)) {
1354
+ await this.rejectDuplicateActiveConnection(stream, authorization, targetSessionId, "relay_registry");
1355
+ return;
1356
+ }
1357
+ for (const relay of currentLiveRelays) {
1358
+ relay.close("error");
1359
+ }
1360
+ for (const pending of currentPendingRelays) {
1361
+ this.abortPendingRelay(pending.relayId, "error", "relay offer superseded; retry", RELAY_OFFER_RETRY_AFTER_MS);
1362
+ }
1363
+ const settled = new Promise((resolveSettled) => {
1364
+ const relay = this.relays.mint({
1365
+ workspaceName,
1366
+ sessionId: targetSessionId,
1367
+ clientNodeId: authorization.client.nodeId,
1368
+ connectionId,
1369
+ ownerControlConnectionId: tuiConnectionId,
1370
+ streamId,
1371
+ stream,
1372
+ preamble: {
1373
+ handshake: {
1374
+ hello: handshake.hello,
1375
+ response: handshake.response,
1376
+ initialInput: Array.from(handshake.initialInput),
1377
+ },
1378
+ authorization: {
1379
+ clientNodeId: authorization.client.nodeId,
1380
+ allowedTools: authorization.client.allowedTools,
1381
+ rpcGrant: authorization.client.rpcGrant,
1382
+ workspaceName,
1383
+ workspacePath: authorization.workspace.path,
1384
+ ...(boundWorktree === undefined
1385
+ ? {}
1386
+ : {
1387
+ worktreeId: boundWorktree.id,
1388
+ worktreePath: boundWorktree.path,
1389
+ ...(boundWorktree.sourceRootRelativePath === undefined
1390
+ ? {}
1391
+ : { worktreeSourceRootRelativePath: boundWorktree.sourceRootRelativePath }),
1392
+ }),
1393
+ },
1394
+ // The phone verifies the saved host's node id in the handshake
1395
+ // response the TUI writes; without this the relay path fails the
1396
+ // client's identity check.
1397
+ ...(this.hostNodeId === undefined ? {} : { hostNodeId: this.hostNodeId }),
1398
+ relayMode: this.relayMode,
1399
+ ...(this.relayMode === "production" ? { relayUrls: this.relayUrls } : {}),
1400
+ connectionId,
1401
+ streamId,
1402
+ resolvedTarget: {
1403
+ sessionId: resolvedTarget.sessionId,
1404
+ ...(resolvedTarget.sessionFilePath === undefined
1405
+ ? {}
1406
+ : { sessionFilePath: resolvedTarget.sessionFilePath }),
1407
+ selection: resolvedTarget.selection,
1408
+ ...(resolvedTarget.requestedSessionId === undefined
1409
+ ? {}
1410
+ : { requestedSessionId: resolvedTarget.requestedSessionId }),
1411
+ workspaceName: resolvedTarget.workspaceName,
1412
+ workspacePath: resolvedTarget.workspacePath,
1413
+ ...(boundWorktree === undefined ? {} : { worktreeId: boundWorktree.id }),
1414
+ ...(relayWorkingDirectory === undefined ? {} : { workingDirectory: relayWorkingDirectory }),
1415
+ },
1416
+ },
1417
+ settle: (outcome) => {
1418
+ this.leaseBroker.unregisterRelay(workspaceName, targetSessionId, relay.relayId);
1419
+ this.services.controlServer.sendTo(tuiConnectionId, {
1420
+ type: "relay_closed",
1421
+ relayId: relay.relayId,
1422
+ reason: outcome.reason,
1423
+ });
1424
+ void this.logAudit({
1425
+ type: "relay_closed",
1426
+ clientNodeId: authorization.client.nodeId,
1427
+ workspace: workspaceName,
1428
+ success: outcome.error === undefined,
1429
+ error: outcome.error,
1430
+ details: {
1431
+ relayId: relay.relayId,
1432
+ reason: outcome.reason,
1433
+ bytesUp: outcome.bytesUp,
1434
+ bytesDown: outcome.bytesDown,
1435
+ durationMs: outcome.durationMs,
1436
+ },
1437
+ });
1438
+ resolveSettled();
1439
+ },
1440
+ });
1441
+ // The offer is single-use with a 10s expiry; the phone's handshake
1442
+ // response is deferred until the TUI redeems the token.
1443
+ const expiryTimer = setTimeout(() => {
1444
+ this.abortPendingRelay(relay.relayId, "error", "relay offer expired; retry", RELAY_OFFER_RETRY_AFTER_MS);
1445
+ }, RELAY_TOKEN_TTL_MS);
1446
+ expiryTimer.unref?.();
1447
+ this.leaseBroker.registerRelay(workspaceName, targetSessionId, relay.relayId);
1448
+ void this.logAudit({
1449
+ type: "relay_opened",
1450
+ clientNodeId: authorization.client.nodeId,
1451
+ workspace: workspaceName,
1452
+ success: true,
1453
+ details: {
1454
+ relayId: relay.relayId,
1455
+ workspaceName,
1456
+ sessionId: targetSessionId,
1457
+ connectionId,
1458
+ streamId,
1459
+ },
1460
+ });
1461
+ const delivered = this.services.controlServer.sendTo(tuiConnectionId, {
1462
+ type: "relay_offer",
1463
+ relayId: relay.relayId,
1464
+ relayToken: relay.relayToken,
1465
+ workspaceName,
1466
+ sessionId: targetSessionId,
1467
+ clientNodeId: authorization.client.nodeId,
1468
+ connectionId,
1469
+ streamId,
1470
+ });
1471
+ if (!delivered) {
1472
+ // The TUI connection vanished between the lease check and the offer;
1473
+ // fail the phone's deferred handshake now instead of after the TTL.
1474
+ this.abortPendingRelay(relay.relayId, "error", "relay offer undeliverable; retry", RELAY_OFFER_RETRY_AFTER_MS);
1475
+ }
1476
+ });
1477
+ await settled;
1478
+ }
1479
+ async runIntegratedConversation(stream, handshake, connection, connectionId, streamId) {
1480
+ const authorization = handshake.authorization;
1481
+ const targetSessionId = getResolvedTargetSessionId(handshake.hello, authorization);
1482
+ const daemonAttach = this.leaseBroker.beginDaemonAttach(authorization.workspace.name, targetSessionId);
1483
+ if (daemonAttach.kind === "relay") {
1484
+ if (!targetSessionId) {
1485
+ await this.sendHandshakeError(stream, {
1486
+ message: "conversation lease owner changed; retry",
1487
+ retryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,
1488
+ });
1489
+ return;
1490
+ }
1491
+ await this.relayConversationToTui(stream, handshake, connectionId, streamId, targetSessionId, daemonAttach.tuiConnectionId);
1492
+ return;
1493
+ }
1494
+ if (daemonAttach.kind === "retry") {
1495
+ await this.sendHandshakeError(stream, {
1496
+ message: "conversation lease is draining; retry",
1497
+ retryAfterMs: daemonAttach.retryAfterMs,
1498
+ });
1499
+ return;
1500
+ }
1501
+ const daemonAttachClaim = daemonAttach.claim;
1502
+ let entry;
1503
+ let sessionSelection;
1504
+ let createdRuntime = false;
1505
+ try {
1506
+ ({
1507
+ entry,
1508
+ sessionSelection,
1509
+ created: createdRuntime,
1510
+ } = await this.runtimes.getOrCreateEntry({ hello: handshake.hello, response: handshake.response }, authorization));
1511
+ }
1512
+ catch (error) {
1513
+ this.leaseBroker.abortDaemonAttach(daemonAttachClaim);
1514
+ await this.logAudit({
1515
+ type: "runtime_failure",
1516
+ clientNodeId: authorization.client.nodeId,
1517
+ workspace: authorization.workspace.name,
1518
+ success: false,
1519
+ error: error instanceof Error ? error.message : String(error),
1520
+ details: { runtime: "integrated-volt" },
1521
+ });
1522
+ await this.sendHandshakeError(stream, error);
1523
+ return;
1524
+ }
1525
+ if (this.activeStreams.hasConversationOnConnection(authorization.client.nodeId, authorization.workspace.name, entry.sessionId, connectionId)) {
1526
+ this.leaseBroker.abortDaemonAttach(daemonAttachClaim);
1527
+ if (createdRuntime) {
1528
+ await this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);
1529
+ }
1530
+ else {
1531
+ // Reattach: getOrCreateEntry cancelled the detached-runtime retention
1532
+ // timer up front. Re-arm it (no-op unless the entry is still detached
1533
+ // with no timer) so aborting here never leaves the runtime unswept.
1534
+ await this.runtimes.detachWithoutSubscriber(entry, "reattach_superseded");
1535
+ }
1536
+ await this.rejectDuplicateActiveConnection(stream, authorization, entry.sessionId);
1537
+ return;
1538
+ }
1539
+ let activeStream;
1540
+ let subscriber;
1541
+ let subscriberError;
1542
+ let handshakeCommitted = false;
1543
+ try {
1544
+ const brokerCommit = this.leaseBroker.commitDaemonRuntime(daemonAttachClaim, authorization.workspace.name, entry.sessionId);
1545
+ if (!brokerCommit.ok) {
1546
+ if (createdRuntime) {
1547
+ await this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);
1548
+ }
1549
+ if (brokerCommit.reason === "tui_owned" &&
1550
+ brokerCommit.tuiConnectionId &&
1551
+ targetSessionId === entry.sessionId) {
1552
+ await this.relayConversationToTui(stream, handshake, connectionId, streamId, entry.sessionId, brokerCommit.tuiConnectionId);
1553
+ return;
1554
+ }
1555
+ await this.sendHandshakeError(stream, {
1556
+ message: "conversation lease owner changed; retry",
1557
+ retryAfterMs: RELAY_OFFER_RETRY_AFTER_MS,
1558
+ });
1559
+ return;
1560
+ }
1561
+ try {
1562
+ await this.runtimes.commitEntry(entry, sessionSelection, authorization);
1563
+ }
1564
+ catch (error) {
1565
+ this.leaseBroker.rollbackDaemonRuntimeCommit(authorization.workspace.name, entry.sessionId, brokerCommit.previousState);
1566
+ throw error;
1567
+ }
1568
+ if (this.activeStreams.hasConversationOnConnection(authorization.client.nodeId, authorization.workspace.name, entry.sessionId, connectionId)) {
1569
+ this.leaseBroker.rollbackDaemonRuntimeCommit(authorization.workspace.name, entry.sessionId, brokerCommit.previousState);
1570
+ if (createdRuntime) {
1571
+ await this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);
1572
+ }
1573
+ else {
1574
+ await this.runtimes.detachWithoutSubscriber(entry, "reattach_superseded");
1575
+ }
1576
+ await this.rejectDuplicateActiveConnection(stream, authorization, entry.sessionId);
1577
+ return;
1578
+ }
1579
+ handshakeCommitted = true;
1580
+ // Worktree-bound conversations sanitize with the worktree checkout as the
1581
+ // root; the parent checkout and the worktrees root must ALSO redact (bash
1582
+ // output like `git worktree list` prints both).
1583
+ const worktreeSanitizerOverrides = entry.worktreePath === undefined
1584
+ ? undefined
1585
+ : {
1586
+ remoteWorkspacePath: entry.worktreeSourceRootRelativePath === undefined
1587
+ ? "/workspace"
1588
+ : `/workspace/${entry.worktreeSourceRootRelativePath}`,
1589
+ workspacePath: entry.worktreePath,
1590
+ additionalRedactedPaths: [authorization.workspace.path, getWorktreesRoot(this.services.agentDir)],
1591
+ };
1592
+ if (!(await this.isAuthorizationGrantCurrent(authorization))) {
1593
+ await this.runtimes.stopEntry(entry, "access_updated_during_attach");
1594
+ throw new Error("client access changed during conversation attach; reconnect");
1595
+ }
1596
+ const replacedEntries = this.activeStreams.takeEntriesForConversationOnOtherConnections(authorization.client.nodeId, authorization.workspace.name, entry.sessionId, connectionId);
1597
+ activeStream = this.registerActiveStream(authorization, entry.sessionId, stream, connection, connectionId, streamId, worktreeSanitizerOverrides === undefined ? {} : { sanitizerOverrides: worktreeSanitizerOverrides });
1598
+ await this.closeReplacedActiveStreams(authorization, streamId, replacedEntries);
1599
+ await writeIrohRemoteHandshakeResponse(stream.send, createIntegratedConversationHandshakeResponse({ hello: handshake.hello, response: handshake.response }, authorization, entry.sessionId, sessionSelection, this.getResponseContext(), entry.worktreeId, entry.workingDirectory));
1600
+ subscriber = await this.runtimes.attachSubscriber(entry);
1601
+ this.leaseBroker.onDaemonRuntimeStreamCountChanged(authorization.workspace.name, entry.sessionId, entry.subscribers.size);
1602
+ await this.runtimes.replayWorkflowEvents(activeStream.entry, entry);
1603
+ const pushDispatcher = this.createPushNotificationDispatcher(authorization);
1604
+ await runIrohRemoteRpcMode(entry.runtime, {
1605
+ rpcGrant: authorization.client.rpcGrant,
1606
+ isRpcGrantCurrent: () => this.isAuthorizationGrantCurrent(authorization),
1607
+ decorateOutbound: (value) => decorateRemoteHostState(value, authorization, this.getResponseContext()),
1608
+ disposeRuntimeOnClose: false,
1609
+ notificationDelivery: pushDispatcher,
1610
+ onClientCapabilitiesChanged: (features) => {
1611
+ const streamEntry = activeStream?.entry;
1612
+ if (streamEntry) {
1613
+ streamEntry.capabilities = new Set(features);
1614
+ this.pushThemeTokensToStream(streamEntry);
1615
+ }
1616
+ },
1617
+ onSessionChanged: async (session) => {
1618
+ await this.runtimes.handleSessionChanged(entry, activeStream?.entry, session, authorization);
1619
+ },
1620
+ onWorkflowEvent: async (event) => {
1621
+ await this.runtimes.handleWorkflowEvent(entry, event, activeStream?.entry);
1622
+ },
1623
+ registerPushTarget: (args) => pushDispatcher.registerPushTarget(args),
1624
+ remoteCommandHandler: (command) => handleIntegratedConversationRpcCommand(command, authorization, this.getCommandContext({
1625
+ workspaceName: authorization.workspace.name,
1626
+ workspacePath: authorization.workspace.path,
1627
+ entry,
1628
+ streamEntry: activeStream?.entry,
1629
+ }), entry.runtime),
1630
+ stream,
1631
+ initialInput: handshake.initialInput,
1632
+ workspaceName: authorization.workspace.name,
1633
+ workspacePath: entry.worktreePath ?? authorization.workspace.path,
1634
+ ...(worktreeSanitizerOverrides?.remoteWorkspacePath === undefined
1635
+ ? {}
1636
+ : { remoteWorkspacePath: worktreeSanitizerOverrides.remoteWorkspacePath }),
1637
+ ...(worktreeSanitizerOverrides?.additionalRedactedPaths === undefined
1638
+ ? {}
1639
+ : { additionalRedactedPaths: worktreeSanitizerOverrides.additionalRedactedPaths }),
1640
+ });
1641
+ }
1642
+ catch (error) {
1643
+ subscriberError = error;
1644
+ if (!handshakeCommitted) {
1645
+ if (createdRuntime) {
1646
+ await this.runtimes.cleanupUncommittedEntry(entry, sessionSelection);
1647
+ }
1648
+ await this.sendHandshakeError(stream, error);
1649
+ return;
1650
+ }
1651
+ }
1652
+ finally {
1653
+ if (subscriber) {
1654
+ await this.runtimes.detachSubscriber(entry, subscriber, subscriberError ? "transport_error" : "transport_closed", subscriberError);
1655
+ this.leaseBroker.onDaemonRuntimeStreamCountChanged(authorization.workspace.name, entry.sessionId, entry.subscribers.size);
1656
+ }
1657
+ else if (handshakeCommitted || !createdRuntime) {
1658
+ // handshakeCommitted: normal detach after the runtime ran. !createdRuntime:
1659
+ // a reattach that failed before attachSubscriber, whose retention timer
1660
+ // getOrCreateEntry cancelled up front — re-arm it so the runtime is still
1661
+ // swept at TTL instead of leaking forever. detachWithoutSubscriber no-ops
1662
+ // when the entry was replaced or still has other subscribers.
1663
+ await this.runtimes.detachWithoutSubscriber(entry, subscriberError ? "transport_error" : "transport_closed");
1664
+ // Sync the lease's stream count to reality. Without this, a handshake
1665
+ // write that failed after commitDaemonRuntime but before attachSubscriber
1666
+ // leaves the lease stuck at daemon-active with no live stream until the
1667
+ // detached-runtime retention TTL expires.
1668
+ this.leaseBroker.onDaemonRuntimeStreamCountChanged(authorization.workspace.name, entry.sessionId, entry.subscribers.size);
1669
+ }
1670
+ activeStream?.remove();
1671
+ }
1672
+ }
1673
+ // ==========================================================================
1674
+ // Stream/connection registries
1675
+ // ==========================================================================
1676
+ registerClientConnection(nodeId, connection, connectionId) {
1677
+ const record = {
1678
+ connectionId,
1679
+ close: (reason) => closeConnection(connection, reason),
1680
+ };
1681
+ let records = this.clientConnections.get(nodeId);
1682
+ if (!records) {
1683
+ records = new Set();
1684
+ this.clientConnections.set(nodeId, records);
1685
+ }
1686
+ records.add(record);
1687
+ let removed = false;
1688
+ return () => {
1689
+ if (removed) {
1690
+ return;
1691
+ }
1692
+ removed = true;
1693
+ records.delete(record);
1694
+ if (records.size === 0 && this.clientConnections.get(nodeId) === records) {
1695
+ this.clientConnections.delete(nodeId);
1696
+ }
1697
+ };
1698
+ }
1699
+ closeClientConnectionsForClient(nodeId, reason) {
1700
+ const records = Array.from(this.clientConnections.get(nodeId) ?? []);
1701
+ if (records.length === 0) {
1702
+ return 0;
1703
+ }
1704
+ this.clientConnections.delete(nodeId);
1705
+ for (const record of records) {
1706
+ try {
1707
+ record.close(reason);
1708
+ }
1709
+ catch {
1710
+ // Connection closure is best-effort; the transport may already be closing.
1711
+ }
1712
+ }
1713
+ return records.length;
1714
+ }
1715
+ async closeEntryConnection(entry, reason) {
1716
+ try {
1717
+ await Promise.resolve(entry.closeConnection?.(reason));
1718
+ }
1719
+ catch {
1720
+ // Connection closure is best-effort. Stream teardown still drives task cleanup.
1721
+ }
1722
+ }
1723
+ async closeIdleConnectionsForEntries(entries, reason) {
1724
+ const closedConnectionIds = new Set();
1725
+ for (const entry of entries) {
1726
+ if (closedConnectionIds.has(entry.connectionId)) {
1727
+ continue;
1728
+ }
1729
+ if (this.activeStreams.entriesForConnection(entry.connectionId).length > 0) {
1730
+ continue;
1731
+ }
1732
+ closedConnectionIds.add(entry.connectionId);
1733
+ await this.closeEntryConnection(entry, reason);
1734
+ }
1735
+ }
1736
+ async closeActiveStreamsForConnection(connectionId, reason) {
1737
+ const entries = this.activeStreams.entriesForConnection(connectionId);
1738
+ for (const entry of entries) {
1739
+ this.activeStreams.unregister(entry);
1740
+ await Promise.resolve(entry.close(reason)).catch(() => { });
1741
+ }
1742
+ }
1743
+ async closeActiveStreamsForConversationKey(workspaceName, sessionId, reason) {
1744
+ const entries = this.activeStreams.entriesForConversationKey(workspaceName, sessionId);
1745
+ for (const entry of entries) {
1746
+ this.activeStreams.unregister(entry);
1747
+ await Promise.resolve(entry.close(reason)).catch(() => { });
1748
+ }
1749
+ await this.closeIdleConnectionsForEntries(entries, reason);
1750
+ return entries.length;
1751
+ }
1752
+ async closeActiveStreamsForWorkspace(workspaceName, reason, excludedEntry) {
1753
+ const entries = this.activeStreams
1754
+ .entriesForWorkspaceName(workspaceName)
1755
+ .filter((entry) => entry !== excludedEntry);
1756
+ if (entries.length === 0) {
1757
+ return 0;
1758
+ }
1759
+ for (const entry of entries) {
1760
+ this.activeStreams.unregister(entry);
1761
+ await Promise.resolve(entry.close(reason)).catch(() => { });
1762
+ }
1763
+ await this.closeIdleConnectionsForEntries(entries, reason);
1764
+ return entries.length;
1765
+ }
1766
+ /**
1767
+ * Invalidate an unredeemed relay offer: fail the phone's deferred handshake
1768
+ * immediately and settle the relay (lease bookkeeping, relay_closed to the
1769
+ * TUI, audit).
1770
+ */
1771
+ abortPendingRelay(relayId, reason, message, retryAfterMs) {
1772
+ const pending = this.relays.invalidatePending(relayId);
1773
+ if (!pending) {
1774
+ return;
1775
+ }
1776
+ void this.sendHandshakeError(pending.stream, {
1777
+ message,
1778
+ ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
1779
+ }).finally(() => pending.settle({ reason, bytesUp: 0, bytesDown: 0, durationMs: 0 }));
1780
+ }
1781
+ closeRelaysForWorkspace(workspaceName, excludeRelayIds) {
1782
+ for (const relay of this.relays.activeRelays()) {
1783
+ if (relay.workspaceName === workspaceName && !excludeRelayIds?.has(relay.relayId)) {
1784
+ relay.close("workspace_unregistered");
1785
+ }
1786
+ }
1787
+ for (const pending of this.relays.pendingRelays()) {
1788
+ if (pending.workspaceName === workspaceName && !excludeRelayIds?.has(pending.relayId)) {
1789
+ this.abortPendingRelay(pending.relayId, "workspace_unregistered", "workspace unregistered");
1790
+ }
1791
+ }
1792
+ }
1793
+ /**
1794
+ * Post-unregister host cleanup shared by the control, workspace-management,
1795
+ * and conversation RPC unregister paths: closes phone streams, stops
1796
+ * runtimes, drops live activities, and closes TUI relays for the workspace.
1797
+ * Exclusions keep the requesting stream/runtime/relays alive so the
1798
+ * unregister response can still be delivered.
1799
+ */
1800
+ async cleanupUnregisteredWorkspace(workspaceName, exclusions = {}) {
1801
+ const closedStreamCount = await this.closeActiveStreamsForWorkspace(workspaceName, WORKSPACE_UNREGISTERED_CLOSE_REASON, exclusions.streamEntry);
1802
+ const stoppedRuntimeCount = await this.runtimes.stopForWorkspace(workspaceName, WORKSPACE_UNREGISTERED_CLOSE_REASON, exclusions.runtimeEntry);
1803
+ await this.stateManager.removeLiveActivitiesForWorkspace(workspaceName);
1804
+ this.closeRelaysForWorkspace(workspaceName, exclusions.relayIds);
1805
+ if (exclusions.workspacePath !== undefined) {
1806
+ await this.worktrees
1807
+ .cleanupUnregisteredWorkspace({ name: workspaceName, path: exclusions.workspacePath })
1808
+ .catch(() => { });
1809
+ }
1810
+ return { closedStreamCount, stoppedRuntimeCount };
1811
+ }
1812
+ async closeActiveStreamsForClientWorkspace(nodeId, workspaceName, reason) {
1813
+ const entries = this.activeStreams
1814
+ .entriesForClientNodeId(nodeId)
1815
+ .filter((entry) => entry.workspaceName === workspaceName);
1816
+ if (entries.length === 0) {
1817
+ return 0;
1818
+ }
1819
+ for (const entry of entries) {
1820
+ this.activeStreams.unregister(entry);
1821
+ await Promise.resolve(entry.close(reason)).catch(() => { });
1822
+ }
1823
+ await this.closeIdleConnectionsForEntries(entries, reason);
1824
+ return entries.length;
1825
+ }
1826
+ async closeClientForAccessUpdate(nodeId) {
1827
+ const runtimeEntries = collectClientAuthorityInvalidationRuntimes(this.activeStreams, this.runtimes.values(), nodeId);
1828
+ const entries = collectClientAuthorityInvalidationStreams(this.activeStreams, runtimeEntries, nodeId);
1829
+ for (const entry of entries) {
1830
+ this.activeStreams.unregister(entry);
1831
+ }
1832
+ // Invalidate transport and relay authority synchronously. Terminal writes
1833
+ // below are best-effort and must never keep old commands or buffered prompts
1834
+ // alive behind backpressure.
1835
+ this.closeClientConnectionsForClient(nodeId, "access_updated");
1836
+ for (const entry of entries) {
1837
+ try {
1838
+ entry.closeConnection?.("access_updated");
1839
+ }
1840
+ catch { }
1841
+ }
1842
+ for (const relay of this.relays.activeRelays()) {
1843
+ if (relay.clientNodeId === nodeId)
1844
+ relay.close("error");
1845
+ }
1846
+ for (const pending of this.relays.pendingRelays()) {
1847
+ if (pending.clientNodeId === nodeId) {
1848
+ this.abortPendingRelay(pending.relayId, "error", "client access updated; reconnect");
1849
+ }
1850
+ }
1851
+ const streamClosures = Promise.allSettled(Array.from(entries, (entry) => Promise.resolve(entry.close("access_updated"))));
1852
+ await Promise.allSettled(Array.from(runtimeEntries, (runtimeEntry) => this.runtimes.stopEntry(runtimeEntry, "access_updated")));
1853
+ await streamClosures;
1854
+ }
1855
+ async closeWorkspaceAuthorizationRemovedStreams(nodeId, workspaceName) {
1856
+ const reason = "workspace_authorization_removed";
1857
+ const closedStreamCount = await this.closeActiveStreamsForClientWorkspace(nodeId, workspaceName, reason);
1858
+ const stoppedRuntimeCount = await this.runtimes.stopForClientWorkspace(nodeId, workspaceName, reason);
1859
+ const removedLiveActivityCount = await this.stateManager.removeClientLiveActivitiesForWorkspace(nodeId, workspaceName);
1860
+ await this.logAudit({
1861
+ type: "workspace_authorization_removed",
1862
+ clientNodeId: nodeId,
1863
+ workspace: workspaceName,
1864
+ success: closedStreamCount > 0 || stoppedRuntimeCount > 0 || removedLiveActivityCount > 0,
1865
+ details: {
1866
+ closedStreamCount,
1867
+ removedLiveActivityCount,
1868
+ source: "authorization_failure",
1869
+ stoppedRuntimeCount,
1870
+ },
1871
+ });
1872
+ }
1873
+ async closeActiveStreamsForClient(nodeId) {
1874
+ const runtimeEntries = collectClientAuthorityInvalidationRuntimes(this.activeStreams, this.runtimes.values(), nodeId);
1875
+ const entries = collectClientAuthorityInvalidationStreams(this.activeStreams, runtimeEntries, nodeId);
1876
+ for (const entry of entries) {
1877
+ this.activeStreams.unregister(entry);
1878
+ }
1879
+ // Match access-update ordering: synchronously make active and unredeemed TUI
1880
+ // relays unusable before any terminal write, runtime disposal, or control ack.
1881
+ const activeRelays = this.relays.activeRelays().filter((relay) => relay.clientNodeId === nodeId);
1882
+ const pendingRelays = this.relays.pendingRelays().filter((relay) => relay.clientNodeId === nodeId);
1883
+ for (const relay of activeRelays) {
1884
+ relay.close("error");
1885
+ }
1886
+ for (const pending of pendingRelays) {
1887
+ this.abortPendingRelay(pending.relayId, "error", "client revoked; re-pair required");
1888
+ }
1889
+ const closedConnectionCount = this.closeClientConnectionsForClient(nodeId, ACTIVE_REVOKE_CLOSE_REASON);
1890
+ for (const entry of entries) {
1891
+ try {
1892
+ entry.closeConnection?.(ACTIVE_REVOKE_CLOSE_REASON);
1893
+ }
1894
+ catch { }
1895
+ }
1896
+ const streamClosures = Promise.allSettled(Array.from(entries, (entry) => Promise.resolve(entry.close(ACTIVE_REVOKE_CLOSE_REASON))));
1897
+ await Promise.allSettled(Array.from(runtimeEntries, (runtimeEntry) => this.runtimes.stopEntry(runtimeEntry, "client_revoked")));
1898
+ await streamClosures;
1899
+ const stoppedRuntimeCount = runtimeEntries.size;
1900
+ const closed = entries.size > 0 || closedConnectionCount > 0 || activeRelays.length > 0 || pendingRelays.length > 0;
1901
+ if (entries.size === 0) {
1902
+ await this.logAudit({
1903
+ type: "active_connection_revoked",
1904
+ clientNodeId: nodeId,
1905
+ success: closed || stoppedRuntimeCount > 0,
1906
+ error: closed || stoppedRuntimeCount > 0 ? undefined : "no active connection found",
1907
+ details: {
1908
+ activeRelayCount: activeRelays.length,
1909
+ closeReason: ACTIVE_REVOKE_CLOSE_REASON,
1910
+ closedConnectionCount,
1911
+ pendingRelayCount: pendingRelays.length,
1912
+ source: "control_channel",
1913
+ stoppedRuntimeCount,
1914
+ },
1915
+ });
1916
+ return { closed, closedCount: closedConnectionCount + activeRelays.length + pendingRelays.length };
1917
+ }
1918
+ for (const entry of entries) {
1919
+ await this.logAudit({
1920
+ type: "active_connection_revoked",
1921
+ clientNodeId: nodeId,
1922
+ workspace: entry.workspaceName,
1923
+ success: true,
1924
+ details: {
1925
+ activeRelayCount: activeRelays.length,
1926
+ closeReason: ACTIVE_REVOKE_CLOSE_REASON,
1927
+ closedConnectionCount,
1928
+ pendingRelayCount: pendingRelays.length,
1929
+ source: "control_channel",
1930
+ streamId: entry.streamId,
1931
+ stoppedRuntimeCount,
1932
+ },
1933
+ });
1934
+ }
1935
+ return { closed: true, closedCount: entries.size + activeRelays.length + pendingRelays.length };
1936
+ }
1937
+ // ==========================================================================
1938
+ // Pairing over the control plane
1939
+ // ==========================================================================
1940
+ notifyPairingConsumed(handshake, remoteId) {
1941
+ const consumed = handshake.authorization.consumedPairingTicket;
1942
+ if (!consumed) {
1943
+ return;
1944
+ }
1945
+ for (const [requestId, pending] of this.pendingPairRequests) {
1946
+ if (pending.secretHash !== consumed.secretHash) {
1947
+ continue;
1948
+ }
1949
+ clearTimeout(pending.timer);
1950
+ this.pendingPairRequests.delete(requestId);
1951
+ this.services.controlServer.sendTo(pending.connectionId, {
1952
+ type: "pairing_progress",
1953
+ requestId,
1954
+ phase: "completed",
1955
+ clientNodeId: remoteId,
1956
+ });
1957
+ }
1958
+ }
1959
+ async handlePairRequest(connection, request) {
1960
+ try {
1961
+ await withTimeout(this.ready.promise, IROH_ENDPOINT_READY_TIMEOUT_MS, "Iroh endpoint did not become ready within 15s");
1962
+ }
1963
+ catch (error) {
1964
+ connection.send({
1965
+ type: "error",
1966
+ id: request.id,
1967
+ code: "iroh_unavailable",
1968
+ message: error instanceof Error ? error.message : String(error),
1969
+ });
1970
+ return;
1971
+ }
1972
+ const engine = this.requireEngine();
1973
+ const endpoint = this.endpoint;
1974
+ if (!endpoint || !this.endpointTicket) {
1975
+ connection.send({ type: "error", id: request.id, code: "iroh_unavailable", message: "endpoint not ready" });
1976
+ return;
1977
+ }
1978
+ const workspaceName = typeof request.workspaceName === "string"
1979
+ ? request.workspaceName
1980
+ : undefined;
1981
+ const requestId = randomUUID();
1982
+ try {
1983
+ const access = request.access !== undefined
1984
+ ? createIrohRemotePresetAccess(request.access)
1985
+ : request.allowedTools !== undefined && request.rpcCapabilities !== undefined
1986
+ ? createIrohRemoteExplicitAccess(request.allowedTools, parseIrohRemoteRpcCapabilities(request.rpcCapabilities))
1987
+ : createIrohRemotePresetAccess("coding");
1988
+ const pairing = await engine.pair({
1989
+ allowTools: access.allowedTools,
1990
+ rpcGrant: access.rpcGrant,
1991
+ irohTicket: this.endpointTicket,
1992
+ nodeId: this.hostNodeId,
1993
+ relayMode: this.relayMode,
1994
+ ...(this.relayMode === "production" ? { relayUrls: this.relayUrls } : {}),
1995
+ ...(this.relayMode === "production" && this.relayAuthToken !== undefined
1996
+ ? { relayAuthToken: this.relayAuthToken }
1997
+ : {}),
1998
+ ...(workspaceName === undefined ? {} : { workspace: workspaceName }),
1999
+ });
2000
+ connection.send({ type: "pair_started", id: request.id, requestId });
2001
+ connection.send({
2002
+ type: "pairing_progress",
2003
+ requestId,
2004
+ phase: "ticket",
2005
+ ticket: pairing.ticket,
2006
+ });
2007
+ connection.send({ type: "pairing_progress", requestId, phase: "waiting" });
2008
+ const ttlMs = Math.max(0, pairing.expiresAt - Date.now());
2009
+ const timer = setTimeout(() => {
2010
+ if (!this.pendingPairRequests.delete(requestId)) {
2011
+ return;
2012
+ }
2013
+ this.services.controlServer.sendTo(connection.connectionId, {
2014
+ type: "pairing_progress",
2015
+ requestId,
2016
+ phase: "failed",
2017
+ error: "pairing ticket expired",
2018
+ });
2019
+ }, ttlMs > 0 ? ttlMs : DEFAULT_IROH_REMOTE_PAIRING_TICKET_TTL_MS);
2020
+ timer.unref?.();
2021
+ this.pendingPairRequests.set(requestId, {
2022
+ requestId,
2023
+ connectionId: connection.connectionId,
2024
+ secretHash: hashIrohRemotePairingSecret(pairing.secret),
2025
+ expiresAt: pairing.expiresAt,
2026
+ timer,
2027
+ });
2028
+ }
2029
+ catch (error) {
2030
+ connection.send({
2031
+ type: "error",
2032
+ id: request.id,
2033
+ code: "pair_failed",
2034
+ message: error instanceof Error ? error.message : String(error),
2035
+ });
2036
+ }
2037
+ }
2038
+ // ==========================================================================
2039
+ // Control plane integration
2040
+ // ==========================================================================
2041
+ async handleRequest(connection, request) {
2042
+ switch (request.type) {
2043
+ case "lease_acquire": {
2044
+ const outcome = await this.leaseBroker.acquireForTui({
2045
+ connectionId: connection.connectionId,
2046
+ workspaceName: request.workspaceName,
2047
+ sessionId: request.sessionId,
2048
+ force: request.force,
2049
+ });
2050
+ if (outcome.kind === "granted") {
2051
+ connection.send({
2052
+ type: "lease_granted",
2053
+ id: request.id,
2054
+ workspaceName: request.workspaceName,
2055
+ sessionId: request.sessionId,
2056
+ handoff: outcome.handoff,
2057
+ });
2058
+ return true;
2059
+ }
2060
+ if (outcome.kind === "denied") {
2061
+ connection.send({ type: "lease_denied", id: request.id, reason: outcome.reason });
2062
+ return true;
2063
+ }
2064
+ connection.send({ type: "lease_pending", id: request.id, viewerFeedId: outcome.viewerFeedId });
2065
+ outcome.granted.then((granted) => {
2066
+ connection.send({
2067
+ type: "lease_granted",
2068
+ id: request.id,
2069
+ workspaceName: request.workspaceName,
2070
+ sessionId: request.sessionId,
2071
+ handoff: granted.handoff,
2072
+ });
2073
+ }, (error) => {
2074
+ connection.send({
2075
+ type: "error",
2076
+ id: request.id,
2077
+ code: "drain_failed",
2078
+ message: error instanceof Error ? error.message : String(error),
2079
+ });
2080
+ });
2081
+ return true;
2082
+ }
2083
+ case "lease_release": {
2084
+ const result = this.leaseBroker.releaseFromTui(connection.connectionId, request.workspaceName, request.sessionId);
2085
+ if (!result.ok) {
2086
+ connection.send({ type: "error", id: request.id, code: result.code, message: "lease not held" });
2087
+ return true;
2088
+ }
2089
+ connection.send({ type: "ok", id: request.id });
2090
+ return true;
2091
+ }
2092
+ case "lease_rekey": {
2093
+ this.leaseBroker.rekey(request.workspaceName, request.oldSessionId, request.newSessionId);
2094
+ connection.send({ type: "ok", id: request.id });
2095
+ return true;
2096
+ }
2097
+ case "viewer_subscribe": {
2098
+ if (!this.viewerFeeds.subscribe(request.viewerFeedId, connection.connectionId)) {
2099
+ connection.send({ type: "error", id: request.id, code: "not_found", message: "unknown viewer feed" });
2100
+ return true;
2101
+ }
2102
+ connection.send({ type: "ok", id: request.id });
2103
+ return true;
2104
+ }
2105
+ case "viewer_unsubscribe": {
2106
+ if (!this.viewerFeeds.unsubscribe(request.viewerFeedId, connection.connectionId)) {
2107
+ connection.send({ type: "error", id: request.id, code: "not_found", message: "unknown viewer feed" });
2108
+ return true;
2109
+ }
2110
+ connection.send({ type: "ok", id: request.id });
2111
+ return true;
2112
+ }
2113
+ case "viewer_abort": {
2114
+ if (!(await this.viewerFeeds.abort(request.viewerFeedId, connection.connectionId))) {
2115
+ connection.send({ type: "error", id: request.id, code: "not_found", message: "unknown viewer feed" });
2116
+ return true;
2117
+ }
2118
+ connection.send({ type: "ok", id: request.id });
2119
+ return true;
2120
+ }
2121
+ case "pair_request":
2122
+ await this.handlePairRequest(connection, request);
2123
+ return true;
2124
+ case "pair_cancel": {
2125
+ const pending = this.pendingPairRequests.get(request.requestId);
2126
+ if (!pending || pending.connectionId !== connection.connectionId) {
2127
+ connection.send({
2128
+ type: "error",
2129
+ id: request.id,
2130
+ code: "not_found",
2131
+ message: "pairing request not found",
2132
+ });
2133
+ return true;
2134
+ }
2135
+ try {
2136
+ await this.cancelPendingPairing(request.requestId, pending);
2137
+ connection.send({ type: "ok", id: request.id });
2138
+ }
2139
+ catch (error) {
2140
+ connection.send({
2141
+ type: "error",
2142
+ id: request.id,
2143
+ code: "cancel_failed",
2144
+ message: error instanceof Error ? error.message : String(error),
2145
+ });
2146
+ }
2147
+ return true;
2148
+ }
2149
+ case "relay_rpc": {
2150
+ const result = await this.handleRelayRpc(connection, request);
2151
+ if (!result.ok) {
2152
+ connection.send({ type: "error", id: request.id, code: result.code, message: result.message });
2153
+ return true;
2154
+ }
2155
+ connection.send({
2156
+ type: "relay_rpc_result",
2157
+ id: request.id,
2158
+ response: result.response,
2159
+ ...(result.workspaceMetadata === undefined ? {} : { workspaceMetadata: result.workspaceMetadata }),
2160
+ });
2161
+ return true;
2162
+ }
2163
+ case "relay_notification_delivery": {
2164
+ const result = await this.handleRelayNotificationDelivery(connection, request);
2165
+ if (!result.ok) {
2166
+ connection.send({ type: "error", id: request.id, code: result.code, message: result.message });
2167
+ return true;
2168
+ }
2169
+ connection.send({ type: "relay_push_delivery_result", id: request.id, status: result.status });
2170
+ return true;
2171
+ }
2172
+ case "relay_live_activity_delivery": {
2173
+ const result = await this.handleRelayLiveActivityDelivery(connection, request);
2174
+ if (!result.ok) {
2175
+ connection.send({ type: "error", id: request.id, code: result.code, message: result.message });
2176
+ return true;
2177
+ }
2178
+ connection.send({ type: "relay_push_delivery_result", id: request.id, status: result.status });
2179
+ return true;
2180
+ }
2181
+ case "client_access_update": {
2182
+ const access = request.access !== undefined
2183
+ ? createIrohRemotePresetAccess(request.access)
2184
+ : createIrohRemoteExplicitAccess(request.allowedTools ?? [], parseIrohRemoteRpcCapabilities(request.rpcCapabilities));
2185
+ const engine = this.engine;
2186
+ const updated = engine
2187
+ ? await engine.updateClientAccess(request.clientNodeId, request.expectedRevision, access)
2188
+ : await this.stateManager.updateClientAccess(request.clientNodeId, request.expectedRevision, access);
2189
+ if (!engine) {
2190
+ await this.logAudit({
2191
+ type: "client_access_updated",
2192
+ clientNodeId: request.clientNodeId,
2193
+ success: updated.ok,
2194
+ error: updated.ok ? undefined : updated.reason,
2195
+ details: {
2196
+ expectedRevision: request.expectedRevision,
2197
+ ...(updated.ok
2198
+ ? { revision: updated.client.rpcGrant.revision }
2199
+ : { currentRevision: updated.currentRevision }),
2200
+ },
2201
+ });
2202
+ }
2203
+ if (!updated.ok) {
2204
+ connection.send({
2205
+ type: "error",
2206
+ id: request.id,
2207
+ code: updated.reason,
2208
+ message: updated.reason === "revision_conflict"
2209
+ ? `RPC grant revision conflict (current ${updated.currentRevision ?? "unknown"})`
2210
+ : updated.reason === "revision_exhausted"
2211
+ ? "RPC grant revision is exhausted; revoke and re-pair the client"
2212
+ : "client not found",
2213
+ });
2214
+ return true;
2215
+ }
2216
+ await this.services.state.flush();
2217
+ await this.closeClientForAccessUpdate(request.clientNodeId);
2218
+ connection.send({
2219
+ type: "client_access_updated",
2220
+ id: request.id,
2221
+ client: {
2222
+ clientNodeId: updated.client.nodeId,
2223
+ label: updated.client.label,
2224
+ pairedAtMs: updated.client.pairedAt,
2225
+ lastSeenAtMs: updated.client.lastSeenAt,
2226
+ allowedTools: updated.client.allowedTools.length === 0 ? [] : updated.client.allowedTools.split(","),
2227
+ rpcGrant: updated.client.rpcGrant,
2228
+ },
2229
+ });
2230
+ return true;
2231
+ }
2232
+ case "client_revoke": {
2233
+ const result = await this.requireEngineSafe();
2234
+ if (!result.ok) {
2235
+ connection.send({ type: "error", id: request.id, code: "iroh_unavailable", message: result.error });
2236
+ return true;
2237
+ }
2238
+ const revocation = await result.engine.revokeClient(request.clientNodeId);
2239
+ if (!revocation.revoked) {
2240
+ connection.send({ type: "error", id: request.id, code: "not_found", message: "client not found" });
2241
+ return true;
2242
+ }
2243
+ await this.closeActiveStreamsForClient(request.clientNodeId);
2244
+ await this.revokeClientPushTargets(revocation.client);
2245
+ connection.send({ type: "ok", id: request.id });
2246
+ return true;
2247
+ }
2248
+ case "workspace_unregister": {
2249
+ let removedWorkspace;
2250
+ try {
2251
+ removedWorkspace = await this.stateManager.unregisterWorkspace(request.name);
2252
+ }
2253
+ catch (error) {
2254
+ if (!isIrohRemoteWorkspaceHasWorktreesError(error)) {
2255
+ throw error;
2256
+ }
2257
+ await this.logAudit({
2258
+ type: "workspace_unregistered",
2259
+ workspace: request.name,
2260
+ success: false,
2261
+ error: IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,
2262
+ details: {
2263
+ source: "control",
2264
+ worktreeCount: error.worktreeIds.length,
2265
+ worktreeIds: error.worktreeIds,
2266
+ },
2267
+ });
2268
+ connection.send({
2269
+ type: "error",
2270
+ id: request.id,
2271
+ code: IROH_REMOTE_WORKSPACE_HAS_WORKTREES_ERROR,
2272
+ message: error.message,
2273
+ });
2274
+ return true;
2275
+ }
2276
+ if (!removedWorkspace) {
2277
+ connection.send({
2278
+ type: "error",
2279
+ id: request.id,
2280
+ code: "not_found",
2281
+ message: `No registered workspace named ${request.name}`,
2282
+ });
2283
+ return true;
2284
+ }
2285
+ this.engine?.clearPairingSecretForWorkspace(request.name);
2286
+ await this.cleanupUnregisteredWorkspace(request.name, { workspacePath: removedWorkspace.path });
2287
+ await this.logAudit({
2288
+ type: "workspace_unregistered",
2289
+ workspace: request.name,
2290
+ success: true,
2291
+ details: { source: "control" },
2292
+ });
2293
+ connection.send({ type: "ok", id: request.id });
2294
+ return true;
2295
+ }
2296
+ default:
2297
+ if (isWorktreeControlRequest(request)) {
2298
+ await handleWorktreeControlRequest(connection, request, {
2299
+ manager: this.worktrees,
2300
+ stateManager: this.stateManager,
2301
+ removeWorktree: (workspace, worktreeId, force) => this.removeWorkspaceWorktree(workspace, worktreeId, force),
2302
+ });
2303
+ return true;
2304
+ }
2305
+ return false;
2306
+ }
2307
+ }
2308
+ async createRelayDeliveryAuthorization(connection, request) {
2309
+ const lease = this.leaseBroker.lookup(request.workspaceName, request.sessionId);
2310
+ if (!lease || lease.state !== "tui-owned" || lease.tuiConnectionId !== connection.connectionId) {
2311
+ return {
2312
+ ok: false,
2313
+ code: "not_held",
2314
+ message: "relay lease is not held by this control connection",
2315
+ };
2316
+ }
2317
+ const client = await this.stateManager.getClient(request.clientNodeId);
2318
+ if (!client) {
2319
+ return { ok: false, code: "not_found", message: "paired client not found" };
2320
+ }
2321
+ const workspace = (await this.stateManager.getState()).workspaces.find((candidate) => candidate.name === request.workspaceName);
2322
+ if (!workspace) {
2323
+ return { ok: false, code: "not_found", message: `no registered workspace named ${request.workspaceName}` };
2324
+ }
2325
+ return {
2326
+ ok: true,
2327
+ authorization: {
2328
+ ok: true,
2329
+ allowTools: client.allowedTools,
2330
+ client,
2331
+ paired: true,
2332
+ pairingSecretConsumed: false,
2333
+ workspace,
2334
+ workspaceNames: [workspace.name],
2335
+ workspaces: [{ name: workspace.name, status: "available" }],
2336
+ },
2337
+ };
2338
+ }
2339
+ async handleRelayNotificationDelivery(connection, request) {
2340
+ const notification = request.notification;
2341
+ if (notification.sessionId !== undefined && notification.sessionId !== request.sessionId) {
2342
+ return { ok: false, code: "session_mismatch", message: "notification session does not match relay session" };
2343
+ }
2344
+ if (notification.workspace !== undefined && notification.workspace !== request.workspaceName) {
2345
+ return {
2346
+ ok: false,
2347
+ code: "workspace_mismatch",
2348
+ message: "notification workspace does not match relay workspace",
2349
+ };
2350
+ }
2351
+ const authorization = await this.createRelayDeliveryAuthorization(connection, request);
2352
+ if (!authorization.ok) {
2353
+ return authorization;
2354
+ }
2355
+ const scopedNotification = {
2356
+ ...notification,
2357
+ sessionId: notification.sessionId ?? request.sessionId,
2358
+ workspace: notification.workspace ?? request.workspaceName,
2359
+ };
2360
+ try {
2361
+ const status = await this.createPushNotificationDispatcher(authorization.authorization).deliverNotification(scopedNotification);
2362
+ return { ok: true, status };
2363
+ }
2364
+ catch {
2365
+ return { ok: true, status: "failed" };
2366
+ }
2367
+ }
2368
+ async handleRelayLiveActivityDelivery(connection, request) {
2369
+ const contentState = request.update.contentState;
2370
+ if (contentState.sessionID !== undefined && contentState.sessionID !== request.sessionId) {
2371
+ return { ok: false, code: "session_mismatch", message: "Live Activity session does not match relay session" };
2372
+ }
2373
+ if (contentState.workspaceName !== undefined && contentState.workspaceName !== request.workspaceName) {
2374
+ return {
2375
+ ok: false,
2376
+ code: "workspace_mismatch",
2377
+ message: "Live Activity workspace does not match relay workspace",
2378
+ };
2379
+ }
2380
+ const authorization = await this.createRelayDeliveryAuthorization(connection, request);
2381
+ if (!authorization.ok) {
2382
+ return authorization;
2383
+ }
2384
+ const scopedUpdate = {
2385
+ ...request.update,
2386
+ contentState: {
2387
+ ...contentState,
2388
+ sessionID: contentState.sessionID ?? request.sessionId,
2389
+ workspaceName: contentState.workspaceName ?? request.workspaceName,
2390
+ },
2391
+ };
2392
+ try {
2393
+ const status = await this.createPushNotificationDispatcher(authorization.authorization).deliverLiveActivityUpdate(scopedUpdate);
2394
+ return { ok: true, status };
2395
+ }
2396
+ catch {
2397
+ return { ok: true, status: "failed" };
2398
+ }
2399
+ }
2400
+ /**
2401
+ * Execute a state-touching RPC command forwarded from a TUI-owned relay
2402
+ * against the daemon's real state (§5.6): push targets, live activities,
2403
+ * and workspace unregistration must land here, not in the TUI's in-memory
2404
+ * state copy.
2405
+ */
2406
+ async handleRelayRpc(connection, request) {
2407
+ const relayAuthorization = this.relays.authorizeRpc(request.relayId, connection.connectionId, request);
2408
+ if (!relayAuthorization.ok) {
2409
+ return relayAuthorization;
2410
+ }
2411
+ const command = request.command;
2412
+ if (!RELAY_RPC_COMMAND_TYPES.has(command.type)) {
2413
+ return { ok: false, code: "unsupported", message: `unsupported relay rpc command: ${command.type}` };
2414
+ }
2415
+ const client = await this.stateManager.getClient(request.clientNodeId);
2416
+ if (!client) {
2417
+ return { ok: false, code: "not_found", message: "paired client not found" };
2418
+ }
2419
+ const requiredCapabilities = getIrohRemoteRpcCommandCapabilities(command);
2420
+ if (requiredCapabilities === undefined) {
2421
+ return { ok: false, code: "unsupported", message: `unsupported relay rpc command: ${command.type}` };
2422
+ }
2423
+ const missingCapability = getMissingIrohRemoteRpcCapability(client.rpcGrant, requiredCapabilities);
2424
+ if (missingCapability !== undefined) {
2425
+ return {
2426
+ ok: true,
2427
+ response: {
2428
+ ...createIrohRemoteRpcCapabilityDeniedResponse(getRpcResponseId(command), command.type, missingCapability),
2429
+ },
2430
+ };
2431
+ }
2432
+ const workspace = (await this.stateManager.getState()).workspaces.find((candidate) => candidate.name === request.workspaceName);
2433
+ if (!workspace) {
2434
+ return { ok: false, code: "not_found", message: `no registered workspace named ${request.workspaceName}` };
2435
+ }
2436
+ const authorization = {
2437
+ ok: true,
2438
+ allowTools: client.allowedTools,
2439
+ client,
2440
+ paired: true,
2441
+ pairingSecretConsumed: false,
2442
+ workspace,
2443
+ workspaceNames: [workspace.name],
2444
+ workspaces: [{ name: workspace.name, status: "available" }],
2445
+ };
2446
+ const responseId = getRpcResponseId(command);
2447
+ if (command.type === "set_keep_awake" || command.type === "get_keep_awake") {
2448
+ const response = createKeepAwakeRpcResponse(command, this.getCommandContext());
2449
+ return { ok: true, response: response };
2450
+ }
2451
+ if (command.type === "set_web_search_key" || command.type === "get_web_search_status") {
2452
+ const response = createWebSearchKeyRpcResponse(command, this.getCommandContext());
2453
+ return { ok: true, response: response };
2454
+ }
2455
+ if (command.type === "register_push_target") {
2456
+ try {
2457
+ const data = await this.createPushNotificationDispatcher(authorization).registerPushTarget(command.args);
2458
+ return { ok: true, response: createRpcSuccessResponse(responseId, command.type, { ...data }) };
2459
+ }
2460
+ catch (error) {
2461
+ return {
2462
+ ok: true,
2463
+ response: {
2464
+ ...createIrohRemoteRpcErrorResponse(responseId, command.type, error instanceof Error ? error.message : String(error)),
2465
+ },
2466
+ };
2467
+ }
2468
+ }
2469
+ if (command.type === "register_live_activity") {
2470
+ const response = await createRemoteRegisterLiveActivityRpcResponse(command, authorization, this.getCommandContext(), request.sessionId);
2471
+ return { ok: true, response: response };
2472
+ }
2473
+ if (command.type === "unregister_live_activity") {
2474
+ const response = await createRemoteUnregisterLiveActivityRpcResponse(command, authorization, this.getCommandContext(), request.sessionId);
2475
+ return { ok: true, response: response };
2476
+ }
2477
+ // unregister_workspace: run against the daemon state with the shared host
2478
+ // cleanup, keeping the requesting conversation's own relays open so the
2479
+ // response can still be delivered over them.
2480
+ const excludeRelayIds = new Set(this.relays
2481
+ .activeForConversation(request.clientNodeId, request.workspaceName, request.sessionId)
2482
+ .map((relay) => relay.relayId));
2483
+ const context = {
2484
+ ...this.getCommandContext(),
2485
+ onWorkspaceUnregistered: async (workspaceName) => {
2486
+ await this.cleanupUnregisteredWorkspace(workspaceName, {
2487
+ relayIds: excludeRelayIds,
2488
+ workspacePath: workspace.path,
2489
+ });
2490
+ },
2491
+ };
2492
+ const response = await handleRemoteHostRpcCommand(command, authorization, context);
2493
+ if (!response) {
2494
+ return { ok: false, code: "unsupported", message: `unsupported relay rpc command: ${command.type}` };
2495
+ }
2496
+ return {
2497
+ ok: true,
2498
+ response: response,
2499
+ workspaceMetadata: {
2500
+ workspaceNames: [...authorization.workspaceNames],
2501
+ workspaces: authorization.workspaces.map((entry) => ({ name: entry.name, status: entry.status })),
2502
+ },
2503
+ };
2504
+ }
2505
+ async requireEngineSafe() {
2506
+ try {
2507
+ await withTimeout(this.ready.promise, IROH_ENDPOINT_READY_TIMEOUT_MS, "Iroh endpoint did not become ready within 15s");
2508
+ }
2509
+ catch (error) {
2510
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
2511
+ }
2512
+ if (!this.engine) {
2513
+ return { ok: false, error: "iroh host engine is not ready" };
2514
+ }
2515
+ return { ok: true, engine: this.engine };
2516
+ }
2517
+ async cancelPendingPairing(requestId, pending) {
2518
+ clearTimeout(pending.timer);
2519
+ if (this.engine) {
2520
+ await this.engine.cancelPairingSecretByHash(pending.secretHash);
2521
+ }
2522
+ else {
2523
+ await this.stateManager.removePendingPairingTicket(pending.secretHash);
2524
+ }
2525
+ await this.services.state.flush();
2526
+ this.pendingPairRequests.delete(requestId);
2527
+ }
2528
+ onControlConnectionClosed(connection) {
2529
+ this.leaseBroker.releaseAllForConnection(connection.connectionId);
2530
+ for (const [requestId, pending] of this.pendingPairRequests) {
2531
+ if (pending.connectionId !== connection.connectionId)
2532
+ continue;
2533
+ void this.cancelPendingPairing(requestId, pending).catch((error) => {
2534
+ this.log("warn", "failed to cancel pairing after control disconnect", {
2535
+ requestId,
2536
+ error: error instanceof Error ? error.message : String(error),
2537
+ });
2538
+ });
2539
+ }
2540
+ }
2541
+ admitRelay(relayId, relayToken, socket, bufferedRemainder) {
2542
+ return this.relays.admit(relayId, relayToken, socket, bufferedRemainder);
2543
+ }
2544
+ statusExtras() {
2545
+ const leases = this.leaseBroker.list().map((record) => ({
2546
+ workspaceName: record.workspaceName,
2547
+ sessionId: record.sessionId,
2548
+ state: record.state,
2549
+ relayCount: record.relayIds.size,
2550
+ streamCount: record.streamCount,
2551
+ }));
2552
+ return { leases, phoneConnections: this.clientConnections.size, relayCount: this.relays.activeCount() };
2553
+ }
2554
+ async shutdown() {
2555
+ this.shuttingDown = true;
2556
+ this.worktreeRetention.dispose();
2557
+ // 1. Stop accepting: close the endpoint accept loop lazily; new hellos are
2558
+ // rejected by the control server's shutting-down gate. Close relays and
2559
+ // unredeemed offers up front with the dedicated host_shutdown reason —
2560
+ // otherwise they die with the endpoint and TUIs see a misleading
2561
+ // relay_closed{error}.
2562
+ for (const relay of this.relays.activeRelays()) {
2563
+ relay.close("host_shutdown");
2564
+ }
2565
+ for (const pending of this.relays.pendingRelays()) {
2566
+ this.abortPendingRelay(pending.relayId, "host_shutdown", "daemon shutting down");
2567
+ }
2568
+ // 2. Wait for busy runtimes to go idle (60s cap each, concurrently);
2569
+ // never abort prompt preflight or a turn from shutdown.
2570
+ const drainResults = await Promise.allSettled(this.runtimes
2571
+ .values()
2572
+ .filter((entry) => entry.runtime.session.isBusy)
2573
+ .map((entry) => withTimeout(entry.runtime.session.waitForIdle(), SHUTDOWN_RUNTIME_IDLE_CAP_MS, "drain cap")));
2574
+ const cappedRuntimes = drainResults.filter((result) => result.status === "rejected").length;
2575
+ // 3. Flush + dispose runtimes through the normal dispose path.
2576
+ await this.runtimes.stopAll("host_shutdown");
2577
+ // 4. Close all phone streams and connections with reason host_shutdown.
2578
+ for (const nodeId of Array.from(this.clientConnections.keys())) {
2579
+ for (const entry of this.activeStreams.entriesForClientNodeId(nodeId)) {
2580
+ this.activeStreams.unregister(entry);
2581
+ await Promise.resolve(entry.close("host_shutdown")).catch(() => { });
2582
+ }
2583
+ await this.closeClientConnectionsForClient(nodeId, "host_shutdown");
2584
+ }
2585
+ try {
2586
+ await this.endpoint?.close();
2587
+ }
2588
+ catch {
2589
+ // Endpoint shutdown is best-effort.
2590
+ }
2591
+ await Promise.allSettled(this.connectionTasks);
2592
+ await this.services.auditLogger.flush().catch(() => { });
2593
+ this.log("info", "iroh service stopped", { cappedRuntimes });
2594
+ }
2595
+ async logAudit(event) {
2596
+ try {
2597
+ await this.services.auditLogger.log(event);
2598
+ }
2599
+ catch {
2600
+ // Audit logging is best-effort.
2601
+ }
2602
+ }
2603
+ }
2604
+ //# sourceMappingURL=iroh-service.js.map